Skip to content

Instantly share code, notes, and snippets.

@iordic
Created September 30, 2020 14:50
Show Gist options
  • Save iordic/1752fd3c7ec619dc467efe9ea910585b to your computer and use it in GitHub Desktop.
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.
/*
* 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