Created
September 30, 2020 14:50
-
-
Save iordic/1752fd3c7ec619dc467efe9ea910585b to your computer and use it in GitHub Desktop.
Comprobar un checksum de TCP a partir de un hexstream copiado desde Wireshark.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Comprobador del checksum de TCP a partir de un hexstream copiado de Wireshark. | |
* Copyright (C) 2020 @iordic | |
* | |
* Licencia (GPLv3): | |
* ================= | |
* This program is free software: you can redistribute it and/or modify | |
* it under the terms of the GNU General Public License as published by | |
* the Free Software Foundation, either version 3 of the License, or | |
* (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
* GNU General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <https://www.gnu.org/licenses/>. | |
* | |
* | |
* Descripción: | |
* ============ | |
* Para calcular el checksum de TCP: | |
* Suma de cada 2-bytes de: pseudoheader + header TCP + datos TCP | |
* | |
* Pseudoheader: IP Origen + IP Destino + (Cero + Versión + Longitud segmento) | |
* | |
* Ethernet header son 14 bytes siempre: 6 src, 6 dest, 2 type | |
* IP header, tamaño variable en segundo nibble | |
* | |
* Uso: | |
* ---- | |
* ./<ejecutable> <Hex Stream> | |
* | |
* - Hex Stream: cadena de carácteres hexadecimales entera del paquete TCP. | |
* Sacarlo en wireshark: botón secundario -> Copy -> ...as a Hex Stream | |
*/ | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdint.h> | |
#define MAC_ADDRESS_SIZE 6 | |
#define IPV4_TYPE 8 | |
#define IPV4_ADDRESS_SIZE 4 | |
#define TCP_PROTOCOL 6 | |
/* Pseudoheader: IP Origen + IP Destino + (Cero + Versión + Longitud segmento) */ | |
struct Pseudoheader { | |
uint32_t source_ip; | |
uint32_t dest_ip; | |
uint8_t zero; | |
uint8_t version; | |
uint16_t segment_length; // Longitud del header TCP + Longitud datos (el payload) | |
} pseudoheader; | |
/* Firmas de funciones */ | |
int check_hex_string(char* in); | |
void string_to_hex(char* in, uint8_t *out, int size); | |
uint16_t checksum_calc(uint8_t *buffer, int size); | |
int main(int argc, char* argv[]) { | |
if (argc < 2) { | |
printf("Error: falta el argumento.\n"); | |
printf("Uso: %s <hex stream>", argv[0]); | |
printf(" (cadena de caracteres copiada de wireshark sin espacios).\n"); | |
return 1; | |
} | |
int input_size = strlen(argv[1]); | |
char *input = argv[1]; // Puntero al hex stream de wireshark (primer y único argumento) | |
if (input_size % 2 != 0) { | |
printf("Error: el número de bytes es impar (bytes: %d).\n", input_size); | |
return 1; | |
} | |
if (!check_hex_string(input)) { | |
printf("Error: la cadena es inválida.\n"); | |
return 1; | |
} | |
int buf_size = input_size / 2; | |
uint8_t* raw_stream = (uint8_t *) malloc(buf_size); | |
string_to_hex(input, raw_stream, input_size); | |
// Después de las direcciones viene el tipo de paquete | |
int eth_header_length = (MAC_ADDRESS_SIZE * 2) + 2; | |
int pos = eth_header_length - 2; | |
if (raw_stream[pos] != IPV4_TYPE) { | |
printf("Error: el paquete no pertenece a IPv4.\n"); | |
return 1; | |
} | |
pos = eth_header_length; // Salta al header de IPv4 | |
int ipv4_header_length = (raw_stream[pos] & 0xf) * 4; // Valor: 4 bits (IHL) * 4 | |
pos += 2; // Salta a longitud total del paquete IPv4 | |
// Recomponer el tamaño total del paquete | |
int ipv4_total_length = (raw_stream[pos] & 0xff) << 8; | |
pos++; | |
ipv4_total_length += raw_stream[pos] & 0xff; | |
pos += 1 + 2 + 2 + 1; // Saltar identificación, los flags y el TTL | |
if ((raw_stream[pos] & 0xff) != TCP_PROTOCOL) { | |
printf("Error: el paquete no pertenece al protocolo TCP (protocolo: %d).\n", raw_stream[pos]); | |
return 1; | |
} | |
pseudoheader.zero = 0; | |
pseudoheader.version = raw_stream[pos] & 0xff; | |
pos += 1 + 2; // Saltar checksum | |
// Adaptar a little endian | |
pseudoheader.source_ip = raw_stream[pos] << 24; | |
pseudoheader.source_ip += raw_stream[pos+1] << 16; | |
pseudoheader.source_ip += raw_stream[pos+2] << 8; | |
pseudoheader.source_ip += raw_stream[pos+3]; | |
pos += 4; | |
pseudoheader.dest_ip = raw_stream[pos] << 24; | |
pseudoheader.dest_ip += raw_stream[pos+1] << 16; | |
pseudoheader.dest_ip += raw_stream[pos+2] << 8; | |
pseudoheader.dest_ip += raw_stream[pos+3]; | |
/* Llegamos a TCP */ | |
pos = eth_header_length + ipv4_header_length; | |
pos += 2 + 2 + 4 + 4; // Salta los puertos, seq number, ack number, | |
int tcp_header_length = ((raw_stream[pos] & 0xf0) >> 4) * 4; | |
pos += 2 + 2; // saltamos flags y el windows size | |
uint16_t checksum_recibido = raw_stream[pos] << 8; | |
checksum_recibido += raw_stream[pos+1]; | |
// Aprovechamos para cargarnos el checksum | |
raw_stream[pos] = '\0'; | |
raw_stream[pos+1] = '\0'; | |
pos += 2; | |
// Longitud de segmento = longitud del encabezado TCP + Datos (sumar y restar el TCP Header es inútil pero por legibilidad) | |
pseudoheader.segment_length = tcp_header_length + (ipv4_total_length - ipv4_header_length - tcp_header_length); | |
pos = 0; | |
/* Serializar toda la información */ | |
int buff_size = sizeof(pseudoheader) + pseudoheader.segment_length; | |
uint8_t *buffer = (uint8_t *) malloc(buff_size); // Pseudoheader + TCP header + TCP Datos | |
memset(buffer, 0, buff_size); | |
// Primero hay que serializar el pseudoheader (para ello adaptamos el little endian) | |
buffer[pos] = (pseudoheader.source_ip >> 24) & 0xff; | |
buffer[pos+1] = (pseudoheader.source_ip >> 16) & 0xff; | |
buffer[pos+2] = (pseudoheader.source_ip >> 8) & 0xff; | |
buffer[pos+3] = (pseudoheader.source_ip) & 0xff; | |
pos += 4; | |
buffer[pos] = (pseudoheader.dest_ip >> 24) & 0xff; | |
buffer[pos+1] = (pseudoheader.dest_ip >> 16) & 0xff; | |
buffer[pos+2] = (pseudoheader.dest_ip >> 8) & 0xff; | |
buffer[pos+3] = (pseudoheader.dest_ip) & 0xff; | |
pos += 4; | |
buffer[pos] = pseudoheader.zero; | |
pos++; | |
buffer[pos] = pseudoheader.version; | |
pos++; | |
buffer[pos] = (pseudoheader.segment_length >> 8) & 0xff; | |
buffer[pos+1] = pseudoheader.segment_length & 0xff; | |
// Serializamos el header y los datos (como van seguidos se puede hacer de golpe) | |
memcpy(buffer + sizeof(pseudoheader), raw_stream + eth_header_length + ipv4_header_length, pseudoheader.segment_length); | |
uint16_t checksum_calculado = checksum_calc(buffer, buff_size); | |
if (checksum_recibido == checksum_calculado) | |
printf("- CHECKSUM CORRECTO.\n"); | |
else | |
printf("- CHECKSUM INCORRECTO.\n"); | |
printf("Checksum -> recibido: %x, calculado: %x\n", checksum_recibido, checksum_calculado); | |
// Liberar y salir | |
free(buffer); | |
free(raw_stream); | |
return 0; | |
} | |
void string_to_hex(char* in, uint8_t* out, int size) { | |
uint8_t v, aux; | |
int i; | |
for (i = 0; i < size; i++) { | |
// Cada 2 caracteres se almacenan en una posición del buffer (1 byte = 2 dígitos) | |
aux = !(i % 2) ? 0 : aux << 4; | |
v = in[i]; | |
// Convertir dígito hexadecimal a valor numérico | |
aux += (v >= '0' && v <= '9') ? v - '0' : ((v >= 'a' && v <= 'f') ? (v - 'a') + 10 : v); | |
if ((i % 2)) { | |
out[i / 2] = aux & 0xff; | |
} | |
} | |
} | |
int check_hex_string(char* in) { | |
int i, s = strlen(in); | |
uint8_t *p; | |
for (i = 0; i < s; i++) { | |
p = (uint8_t *) &in[i]; | |
// Comprobación de carácteres válidos | |
if (!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f'))) { | |
printf("%x\n", *p); | |
return 0; | |
} | |
} | |
return 1; | |
} | |
uint16_t checksum_calc(uint8_t *buffer, int size) { | |
uint16_t result = 0; | |
uint16_t *pares = (uint16_t *) buffer; | |
/* Suma de todos los 2-bytes, sumando el acarreo si lo hay. */ | |
for (int i = 0; i < size / sizeof(uint16_t); i++) { | |
result = result + pares[i] + (((result + pares[i]) >> 16) & 0x1); | |
} | |
// Pasar a little endian | |
result = ((result & 0xff) << 8) + ((result & 0xff00) >> 8); | |
result = ~result; // Complemento a 1 | |
return result; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment