Guest gnoo Posted December 29, 2018 at 05:21 PM Share Posted December 29, 2018 at 05:21 PM Saudações, Este conteúdo está sujeito a erros de interpretação por parte da minha pessoa, se vires algum erro ou achas que tens algo a acrescentar deixa nos comentários para ser corrigido/adicionado... Os conceitos de redes aqui apresentados foram retirados do livro: antes de começar a falar sobre pacotes TCP, temos que fazer uma pequena alteração para complemetar o código anterior na função em que tratamos os dados de um pacote Ipv4, com uma linha de código que vai calcular o tamanho do pacote Ipv4 em bytes, valor esse que serve para fazer o fatiamento da carga que vai ser passada como argumento para a função, que trata os dados do pacote TCP, tendo também que retornar o valor da carga com o fatiamento referido. Eu não vou fazer a alteração no código do post anterior, porque se a intenção for apenas analisar um pacote Ipv4 o código anterior faz essa tarefa, mas para analisar o pacote TCP tem que ser feita essa pequena alteração que vamos ver agora… Na linha tamanho_header_bytes = (versao & 15) * 4 e retornando o valor da carga com o devido fatiamento, carga[tamanho_header_bytes:] Alteração da Função trata dados pacote Ipv4 def dados_pacote_ipv4(carga): tupla_dados_ipv4 = struct.unpack("!BBHHHBBH4s4s", carga[:20]) versao = tupla_dados_ipv4[0] header_len = versao >> 4 tipo_servico = tupla_dados_ipv4[1] tamanho_total = tupla_dados_ipv4[2] identificacao = tupla_dados_ipv4[3] offset_fragmento = tupla_dados_ipv4[4] tempo_vida_ttl = tupla_dados_ipv4[5] protocolos = tupla_dados_ipv4[6] checksum_cabecalho = tupla_dados_ipv4[7] ip_origem = inet_ntoa(tupla_dados_ipv4[8]) ip_destino = inet_ntoa(tupla_dados_ipv4[9]) # Esta linha é a alteração efetuada do tópico anterior tamanho_header_bytes = (versao & 15) * 4 # Cálculo Tamanho do header em bytes return versao, header_len, tipo_servico, + \ tamanho_total, identificacao, offset_fragmento, + \ tempo_vida_ttl, protocolos, checksum_cabecalho, ip_origem, ip_destino, carga[tamanho_header_bytes:] Agora segue o script do tópico anterior completo com as devidas alterações para análise do pacote TCP. #coding:utf-8 #!/usr/bin/env python3 from socket import * import struct import binascii # AF_PACKET Familia protocolo (everywere) # ntohs(3) - Permite capturar todos os pacotes com frames ethernet def ethernet_frame(raw_dados): mac_destino, mac_fonte, protocolo = struct.unpack('! 6s 6s H', raw_dados[:14]) return byte_to_hex_mac(mac_destino), byte_to_hex_mac(mac_fonte), htons(protocolo), raw_dados[14:] def byte_to_hex_mac(mac_em_bytes): endereco = binascii.hexlify(mac_em_bytes).decode("ascii") return ":".join([endereco[i:i+2] for i in range(0,12,2)]) def dados_pacote_ipv4(carga): tupla_dados_ipv4 = struct.unpack("!BBHHHBBH4s4s", carga[:20]) versao = tupla_dados_ipv4[0] header_len = versao >> 4 tipo_servico = tupla_dados_ipv4[1] tamanho_total = tupla_dados_ipv4[2] identificacao = tupla_dados_ipv4[3] offset_fragmento = tupla_dados_ipv4[4] tempo_vida_ttl = tupla_dados_ipv4[5] protocolos = tupla_dados_ipv4[6] checksum_cabecalho = tupla_dados_ipv4[7] ip_origem = inet_ntoa(tupla_dados_ipv4[8]) ip_destino = inet_ntoa(tupla_dados_ipv4[9]) # correção tamanho_header_bytes = (versao & 15) * 4 # Cálculo Tamanho do header em bytes return versao, header_len, tipo_servico, + \ tamanho_total, identificacao, offset_fragmento, + \ tempo_vida_ttl, protocolos, checksum_cabecalho, ip_origem, ip_destino, carga[tamanho_header_bytes:] sock = socket(AF_PACKET, SOCK_RAW, ntohs(0x0003)) while True: raw_dados, addr = sock.recvfrom(65536) mac_destino, mac_fonte, protocolo, carga_util = ethernet_frame(raw_dados) print("------- FRAME ETHERNET-----------") print("Endereço MAC destino : {}".format(mac_destino)) print("Endereço MAC fonte : {}".format(mac_fonte)) print("Protocolo : {}\n".format(protocolo)) print("Carga útil -> {}\n".format(carga_util)) # A variavel (protocolo) está a referir-se ao # tipo de frame # Se for 8 vem ai pacote IP \0/ if protocolo == 8: """ Para desempacotar os valores da função dados_pacote_ipv4 com as variáveis aninhadas, necessáriamente elas têm que estar entre parênteses... """ ( versao, header_len, tipo_servico, tamanho_total, identificacao, offset_fragmento, tempo_vida_ttl, protocolos, checksum_cabecalho, ip_origem, ip_destino, carga ) = dados_pacote_ipv4(carga_util) print("--------- HEADER IPV4----------") print("Versão : {}".format(versao)) print("Header_len : {}".format(header_len)) print("Tipo Serviço : {}".format(tipo_servico)) print("Tamanho Total : {}".format(tamanho_total)) print("Identificação : {}".format(identificacao)) print("Offset Fragmento : {}".format(offset_fragmento)) print("Tempo vida TTL : {}".format(tempo_vida_ttl)) print("Protocolo : {}".format(protocolos)) print("checksum Cabeçalho : {}".format(checksum_cabecalho)) print("IP Origem: {}".format(ip_origem)) print("IP Destino: {}\n".format(ip_destino)) print("Carga : {}\n".format(carga)) Agora que já temos as alterações feitas, antes de avançar e fazer a função que trata os dados do pacote TCP, vamos ver algumas caracteristicas desse mesmo pacote. ATENÇÃO: O conteúdo que segue deve ser complementado com a leitura de um livro da especialidade Formato do segmento TCP Citar A unidade de transferência entre o TCP nas duas máquinas é chamada de segmento. Os segmentos são trocados para se estabelecer conexões, transferir dados, enviar confirmações, anunciar tamanhos de janela e fechar conexões. Como o TCP utiliza o piggybacking, uma confirmação trafegando do computador A para o computador B pode trafegar no mesmo segmento dos dados atravessando do computador A para o computador B, embora a confirmação se refira aos dados enviados de B para A.*** Como a maioria dos protocolos, uma mensagem é dividida em duas partes conceituais: um cabeçalho que contém metadados e uma área de carga útil que leva dados. A Figura 11.7 mostra o formato do segmento TCP. NOTA: O campo (bits de código) entenda-se, flags. Citar O cabeçalho, conhecido como cabeçalho TCP, consiste de pelo menos 20 octetos e deve conter mais se o segmento carregar opções. O cabeçalho tem a identificação esperada e informações de controle. Os campos PORTA DE ORIGEM e PORTA DE DESTINO contêm os números de porta TCP que identificam os programas aplicativos nas extremidades da conexão. O campo NÚMERO DE SEQUÊNCIA identifica a posição no fluxo de bytes do emissor dos dados no segmento. O campo NÚMERO DE CONFIRMAÇÃO identifica o número do octeto que a origem espera receber em seguida. Observe que o número de sequência se refere ao fluxo que segue na mesma direção do segmento, enquanto o número de confirmação refere-se ao fluxo que segue na direção oposta do segmento. O campo HLEN* contém um inteiro que especifica o tamanho do cabeçalho do segmento medido em múltiplos de 32 bits. Ele é necessário porque o campo OPÇÕES varia em tamanho, dependendo de quais opções foram incluídas. Assim, o tamanho do cabeçalho TCP varia dependendo das opções selecionadas. O campo de 6 bits marcado como RESERVADO é reservado para uso futuro (outra seção descreve um uso proposto). Alguns segmentos transportam apenas uma confirmação, enquanto outros transportam dados. Já outros, ainda, transportam requisições para estabelecer ou fechar uma conexão. O TCP utiliza um campo de 6 bits rotulado como BITS DE CÓDIGO para determinar a finalidade e o conteúdo do segmento. Os seis bits dizem como interpretar outros campos no cabeçalho, de acordo com a tabela na Figura 11.8. O TCP anuncia quantos dados ele espera aceitar toda vez que envia um segmento, especificando seu tamanho de buffer no campo JANELA. O campo contém um inteiro não sinalizado de 16 bits na ordem de bytes padrão da rede. Anúncios de janela oferecem outro exemplo de piggybacking, pois acompanham todos os segmentos, incluindo aqueles transportando dados e também aqueles carregando apenas uma confirmação. NOTA: de forma a compreender melhor os valores da variável ( flags ), que aparece na função que vamos fazer a seguir deves ler a matéria que segue neste link https://www.manitonetworks.com/flow-management/2016/10/16/decoding-tcp-flags Função para tratar os dados do cabeçalho TCP def dados_pacote_tcp(carga): tupla_dados_tcp = struct.unpack('! HHLLBBHHH', carga[:20]) porta_fonte = tupla_dados_tcp[0] porta_destino = tupla_dados_tcp[1] numero_sequencia = tupla_dados_tcp[2] numero_confirmacao = tupla_dados_tcp[3] # Esta linha recebe dois campos do cabeçalho ( HLEN & RESERVADO ) hlen_e_reservado = tupla_dados_tcp[4] # Esta linha recebe campo HLEN header_len = (hlen_e_reservado >> 4) * 4 # variável flags flags = tupla_dados_tcp[5] flag_FIN = flags & 1 flag_SYN = (flags >> 1) & 1 flag_RST = (flags >> 2) & 1 flag_PSH = (flags >> 3) & 1 flag_ACK = (flags >> 4) & 1 flag_URG = (flags >> 5) & 1 flag_ECE = (flags >> 6) & 1 flag_CWR = (flags >> 7) & 1 window_size = tupla_dados_tcp[6] checksum = tupla_dados_tcp[7] urgent_pointer = tupla_dados_tcp[8] return porta_fonte, porta_destino ,numero_sequencia, + \ numero_confirmacao, hlen_e_reservado, header_len, + \ flags, flag_FIN, flag_SYN, flag_RST, flag_PSH, flag_ACK, + \ flag_URG, flag_ECE, flag_CWR, window_size, checksum, urgent_pointer Script Completo.... #coding:utf-8 #!/usr/bin/env python3 from socket import * import struct import binascii # AF_PACKET Familia protocolo (everywere) # ntohs(3) - Permite capturar todos os pacotes com frames ethernet def ethernet_frame(raw_dados): mac_destino, mac_fonte, protocolo = struct.unpack('! 6s 6s H', raw_dados[:14]) return byte_to_hex_mac(mac_destino), byte_to_hex_mac(mac_fonte), htons(protocolo), raw_dados[14:] def byte_to_hex_mac(mac_em_bytes): endereco = binascii.hexlify(mac_em_bytes).decode("ascii") return ":".join([endereco[i:i+2] for i in range(0,12,2)]) def dados_pacote_ipv4(carga): tupla_dados_ipv4 = struct.unpack("!BBHHHBBH4s4s", carga[:20]) versao = tupla_dados_ipv4[0] header_len = versao >> 4 tipo_servico = tupla_dados_ipv4[1] tamanho_total = tupla_dados_ipv4[2] identificacao = tupla_dados_ipv4[3] offset_fragmento = tupla_dados_ipv4[4] tempo_vida_ttl = tupla_dados_ipv4[5] protocolos = tupla_dados_ipv4[6] checksum_cabecalho = tupla_dados_ipv4[7] ip_origem = inet_ntoa(tupla_dados_ipv4[8]) ip_destino = inet_ntoa(tupla_dados_ipv4[9]) # correção tamanho_header_bytes = (versao & 15) * 4 # Cálculo Tamanho do header em bytes return versao, header_len, tipo_servico, + \ tamanho_total, identificacao, offset_fragmento, + \ tempo_vida_ttl, protocolos, checksum_cabecalho, ip_origem, ip_destino, carga[tamanho_header_bytes:] def dados_pacote_tcp(carga): tupla_dados_tcp = struct.unpack('! HHLLBBHHH', carga[:20]) porta_fonte = tupla_dados_tcp[0] porta_destino = tupla_dados_tcp[1] numero_sequencia = tupla_dados_tcp[2] numero_confirmacao = tupla_dados_tcp[3] # Esta linha recebe dois campos do cabeçalho ( HLEN & RESERVADO ) hlen_e_reservado = tupla_dados_tcp[4] # Esta linha recebe campo HLEN header_len = (hlen_e_reservado >> 4) * 4 flags = tupla_dados_tcp[5] flag_FIN = flags & 1 flag_SYN = (flags >> 1) & 1 flag_RST = (flags >> 2) & 1 flag_PSH = (flags >> 3) & 1 flag_ACK = (flags >> 4) & 1 flag_URG = (flags >> 5) & 1 flag_ECE = (flags >> 6) & 1 flag_CWR = (flags >> 7) & 1 window_size = tupla_dados_tcp[6] checksum = tupla_dados_tcp[7] urgent_pointer = tupla_dados_tcp[8] return porta_fonte, porta_destino ,numero_sequencia, + \ numero_confirmacao, hlen_e_reservado, header_len, + \ flags, flag_FIN, flag_SYN, flag_RST, flag_PSH, flag_ACK, + \ flag_URG, flag_ECE, flag_CWR, window_size, checksum, urgent_pointer sock = socket(AF_PACKET, SOCK_RAW, ntohs(0x0003)) while True: raw_dados, addr = sock.recvfrom(65536) mac_destino, mac_fonte, protocolo, carga_util = ethernet_frame(raw_dados) print("------- FRAME ETHERNET-----------") print("Endereço MAC destino : {}".format(mac_destino)) print("Endereço MAC fonte : {}".format(mac_fonte)) print("Protocolo : {}\n".format(protocolo)) print("Carga útil -> {}\n".format(carga_util)) # A variavel (protocolo) está a referir-se ao # tipo de frame # Se for 8 vem ai pacote IP \0/ if protocolo == 8: """ Para desempacotar os valores da função dados_pacote_ipv4 com as variáveis aninhadas, necessáriamente elas têm que estar entre parênteses... """ ( versao, header_len, tipo_servico, tamanho_total, identificacao, offset_fragmento, tempo_vida_ttl, protocolos, checksum_cabecalho, ip_origem, ip_destino, carga ) = dados_pacote_ipv4(carga_util) print("--------- HEADER IPV4----------") print("Versão : {}".format(versao)) print("Header_len : {}".format(header_len)) print("Tipo Serviço : {}".format(tipo_servico)) print("Tamanho Total : {}".format(tamanho_total)) print("Identificação : {}".format(identificacao)) print("Offset Fragmento : {}".format(offset_fragmento)) print("Tempo vida TTL : {}".format(tempo_vida_ttl)) print("Protocolo : {}".format(protocolos)) print("checksum Cabeçalho : {}".format(checksum_cabecalho)) print("IP Origem: {}".format(ip_origem)) print("IP Destino: {}\n".format(ip_destino)) print("Carga : {}\n".format(carga)) if protocolos == 6: (porta_fonte, porta_destino, numero_sequencia, numero_confirmacao, hlen_e_reservado, header_len, flags, flag_FIN, flag_SYN, flag_RST, flag_PSH, flag_ACK, flag_URG, flag_ECE, flag_CWR, window_size, checksum, urgent_pointer ) = dados_pacote_tcp(carga) print("--------- HEADER TCP----------") print("Porta Fonte : {}".format(porta_fonte)) print("Porta Destino : {}".format(porta_destino)) print("Número Sequência : {}".format(numero_sequencia)) print("Número Confirmação : {}".format(numero_confirmacao)) print("Hlen_e_reservado : {}".format(hlen_e_reservado)) print("Header_len : {}".format(header_len)) print("Flags : {}".format(flags)) print("Flag FIN : {}".format(flag_FIN)) print("Flag SYN : {}".format(flag_SYN)) print("Flag RST : {}".format(flag_RST)) print("Flag PSH : {}".format(flag_PSH)) print("Flag ACK : {}".format(flag_ACK)) print("Flag URG : {}".format(flag_URG)) print("Flag ECE : {}".format(flag_ECE)) print("Flag CWR : {}".format(flag_CWR)) print("window size : {}".format(window_size)) print("checksum : {}".format(checksum)) print("urgent pointer : {}".format(urgent_pointer)) """ socket.ntohs( x ) Converte inteiros positivos de 16 bits da rede para a ordem de bytes do host. Nas máquinas em que a ordem de bytes do host é igual à ordem de bytes da rede, isso é um não operacional; caso contrário, ele executará uma operação de troca de 2 bytes. ------------------------------------------------------------------------------ socket.htons( x ) Converte inteiros positivos de 16 bits da ordem de bytes do host para a rede. Nas máquinas em que a ordem de bytes do host é igual à ordem de bytes da rede, isso é um não operacional; caso contrário, ele executará uma operação de troca de 2 bytes. """ Em seguida vamos fazer o UDP… Abraço. Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.