본문 바로가기

IT/WinpcapProgramming

TCP 패킷 체크섬(Checksum) C언어로 구현하기

/* 
written by kaspy (kaspyx@gmail.com)
*/ 



네트워크 소켓 프로그래밍에서 TCP 및 IP 등에서는 체크섬(checksum)을 통해서 패킷이 변조됬거나 손상됬는지 검사를 해주는 루틴이 있다.


저번장에 이어서, 이번에는 TCP 패킷 체크섬을 구하는 내용을 소개하겠다.


1. 체크섬(Checksum)을 구하는 함수


  1. typedef unsigned char u_char;
  2. typedef short SHORT;
  3.  
  4. unsigned short in_checksum(unsigned short *ptr,int nbytes) {
  5.         register long sum;
  6.         unsigned short oddbyte;
  7.         register short answer;
  8.  
  9.         sum=0;
  10.         while(nbytes>1) {
  11.                 sum+=*ptr++;
  12.                 nbytes-=2;
  13.         }
  14.         if(nbytes==1) {
  15.                 oddbyte=0;
  16.                 *((u_char*)&oddbyte)=*(u_char*)ptr;
  17.                 sum+=oddbyte;
  18.         }
  19.  
  20.         sum = (sum>>16)+(sum & 0xffff);
  21.         sum = sum + (sum>>16);
  22.         answer=(SHORT)~sum;
  23.        
  24.         return(answer);
  25. }


2. TCP 체크섬(Checksum) 구하기

(http://www.networksorcery.com/enp/protocol/tcp.htm#Checksum)


TCP 헤더 패킷은 MAC 헤더뒤에 IP 헤더가 이어져 나오고 바로 그뒤에 붙어있고 그뒤에 Data가 붙어서 보내어진다.



그리고 TCP Header에 대한 필드값 구성은 아래와 같다.



이중에 체크섬(Checksum) 값이 주어져있는데, 이값이 맞지않으면 받은 호스트쪽에서 데이터가 손상됬거나 변조된것으로 간주하고 패킷을 버린다.


TCP 체크섬은 IP 체크섬 구하는 방법보다 조금더 복잡하다. 


TCP 체크섬은 "Pseudo Header + TCP Header + Data" 의 데이터 값을 기준으로 계산되진다.




각각의 데이터에 대한 값을 모두 채워넣고 TCP 체크섬(Checksum) 필드만 0으로 설정하고 체크섬 함수를 위의 세개 데이터와 필드의 총크기를 주면 구할수있다.


Pseudo Header에 대한 구조는 아래와 같다.



C언어로 구현해보자. 기본 TCP 패킷으로 Pseudo Header 사이즈 12 TCP Header 사이즈 20 그리고 Data 사이즈는 0으로 가정하여 구현해보았다.


  1. #include <stdio.h>
  2. #include <Windows.h>
  3.  
  4. #pragma comment (lib, "ws2_32.lib" )
  5.  
  6. struct pseudo_header{
  7.    unsigned int source_address;
  8.    unsigned int dest_address;
  9.    unsigned char placeholder;
  10.    unsigned char protocol;
  11.    unsigned short tcp_length;
  12. };
  13.  
  14. struct tcp_header
  15. {
  16.      unsigned short source_port;
  17.     unsigned short dest_port;
  18.     unsigned int sequence;
  19.     unsigned int acknowledge;
  20.     unsigned char ns:1;
  21.     unsigned char reserved_part1:3;
  22.     unsigned char data_offset:4;
  23.     unsigned char fin:1;
  24.     unsigned char syn:1;
  25.     unsigned char rst:1;
  26.     unsigned char psh:1;
  27.     unsigned char ack:1;
  28.     unsigned char urg:1;
  29.     unsigned char ecn:1;
  30.     unsigned char cwr:1;
  31.     unsigned short window;
  32.     unsigned short checksum;
  33.     unsigned short urgent_pointer;
  34. };
  35.  
  36. typedef unsigned char u_char;
  37. typedef short SHORT;
  38.  
  39. unsigned short in_checksum(unsigned short *ptr,int nbytes) {
  40.         register long sum;
  41.         unsigned short oddbyte;
  42.         register short answer;
  43.  
  44.         sum=0;
  45.         while(nbytes>1) {
  46.                 sum+=*ptr++;
  47.                 nbytes-=2;
  48.         }
  49.         if(nbytes==1) {
  50.                 oddbyte=0;
  51.                 *((u_char*)&oddbyte)=*(u_char*)ptr;
  52.                 sum+=oddbyte;
  53.         }
  54.  
  55.         sum = (sum>>16)+(sum & 0xffff);
  56.         sum = sum + (sum>>16);
  57.         answer=(SHORT)~sum;
  58.        
  59.         return(answer);
  60. }
  61.  
  62. void main()
  63. {
  64.         unsigned int src_port = 46731;
  65.         unsigned int dst_port = 7778;
  66.  
  67.         char *src_ip = "165.246.67.211";
  68.         char *dest_ip = "165.246.12.215";;
  69.         struct pseudo_header  *psh;
  70.         struct tcp_header *myth;
  71.         unsigned short tcpchecksum;
  72.  
  73.         psh = (pseudo_header*) malloc(sizeof(pseudo_header));
  74.         myth = (tcp_header*)malloc(sizeof(tcp_header));
  75.         psh->source_address=inet_addr(src_ip);
  76.         psh->dest_address=inet_addr(dest_ip);
  77.  
  78.         psh->placeholder=0;  // reserved
  79.         psh->protocol=6;  // protocol number for tcp
  80.         psh->tcp_length=htons(20); // tcp header size.
  81.  
  82.         // build tcp header
  83.         myth->source_port = htons(src_port);
  84.         myth->dest_port = htons(dst_port);
  85.         //0xf101c700 => network byte order => 0x00c701f1
  86.         myth->sequence = 0x00c701f1;
  87.         myth->acknowledge = 0;
  88.  
  89.         myth->ns = 0;
  90.         myth->reserved_part1 = 0;
  91.         myth->data_offset = 5;  // tcp length = 5 * 4, 20
  92.         myth->fin = 0;
  93.         myth->syn = 1;
  94.         myth->rst = 0;
  95.         myth->psh = 0;
  96.         myth->ack = 0;
  97.         myth->urg = 0;
  98.         myth->ecn = 0;
  99.         myth->cwr = 0;
  100.         myth->window = htons(0x2000);
  101.         myth->checksum = 0;
  102.         myth->urgent_pointer = 0x0;
  103.  
  104.         unsigned char *seudo;
  105.         unsigned int tcp_data_size;
  106.         tcp_data_size = sizeof(struct pseudo_header)+ sizeof(struct tcp_header);
  107.         seudo = (unsigned char *)malloc(tcp_data_size);
  108.         memcpy(seudo, psh, sizeof(struct pseudo_header));
  109.         memcpy(seudo+sizeof(struct pseudo_header), myth, sizeof(struct tcp_header));
  110.        
  111.         printf("\n");
  112.         printf("tcp pseudo size = %d\n",sizeof(struct pseudo_header));
  113.         printf("tcp hdr size = %d\n",sizeof(struct tcp_header));
  114.         printf("tcp size = %d\n",tcp_data_size);
  115.  
  116.         myth->checksum  = in_checksum((unsigned short*)seudo,tcp_data_size);
  117.  
  118.         printf("\n%x\n",myth->checksum);
  119.  
  120. }



* 참조한 링크 

http://www.tcpipguide.com/free/t_TCPChecksumCalculationandtheTCPPseudoHeader-2.htm#Figure_218