[經驗]TCP,UDP完整資料包校驗和通用計算 - 日誌 - redice - 我的空間

chief1985發表於2008-10-11
導讀:

ICMP,IP,UDP,TCP報頭部分都有checksum(檢驗和)欄位。ICMP和IP報頭校驗和的計算都很簡單,使用RFC1071中給出的方法即可完成(如下)。
 

//計算校驗和
USHORT checksum(USHORT *buffer,int size)
{
 unsigned long cksum=0;
 while(size>1)
 {
  cksum+=*buffer++;
  size-=sizeof(USHORT);
 }
 if(size)
 {
  cksum+=*(UCHAR *)buffer;
 }
 //將32位數轉換成16
 while (cksum>>16)
  cksum=(cksum>>16)+(cksum & 0xffff);
 return (USHORT) (~cksum);
}

    UDP/TCP報頭中的校驗和的計算比較複雜的,要用到 UDP/TCP偽首部:先要填充偽首部各個欄位,然後再將UDP/TCP報頭以後(包括報頭)的資料附加到偽首部的後面,再對位首部使用上述校驗和計算,所得到的值才是UDP/TCP報頭部分的校驗和。

 

位首部可以用如下的結構體表示:

typedef struct{
 ULONG  sourceip;    //源IP地址
 ULONG  destip;      //目的IP地址
 BYTE mbz;           //置空(0)
 BYTE ptcl;          //協議型別
 USHORT plen;        //TCP/UDP資料包的長度(即從TCP/UDP報頭算起到資料包結束的長度 單位:位元組)
}Psd_Header;

 

 

這個過程是一個很繁瑣的過程,計算過幾次後再也忍受不了做這樣重複的工作,於是寫了一個通用的計算函式。這個函式使用起來我感覺非常方便:先封裝好你的資料包(完整的,包括以太頭),然後將資料包的首地址作為引數,呼叫該函式即可。函式將幫你完成IP報頭以及UDP/TCP報頭部分校驗和的計算。

 

//-------------------------------------------------------------------------
// PacketCheckSum
// 計算資料包的校驗和
// 引數:packet-待處理資料(將封裝好的資料包的指標)
//-------------------------------------------------------------------------
void PacketCheckSum(unsigned char packet[])
{
 Dlc_Header *pdlc_header=NULL; //以太頭指標
 Ip_Header  *pip_header=NULL;  //IP頭指標
 unsigned short attachsize=0; //傳輸層協議頭以及附加資料的總長度

 pdlc_header=(Dlc_Header *)packet;

 //判斷ethertype,如果不是IP包則不予處理
 if(ntohs(pdlc_header->ethertype)!=0x0800) return;

 pip_header=(Ip_Header  *)(packet+14);
 //TCP包
 if(0x06==pip_header->proto)
 {
  
  Tcp_Header *ptcp_header=NULL; //TCP頭指標
  Tcp_Psd_Header *ptcp_psd_header=NULL;
  
  ptcp_header=(Tcp_Header *)(packet+14+((pip_header->ver_len)&15)*4);

  attachsize=ntohs(pip_header->total_len)-((pip_header->ver_len)&15)*4;
  ptcp_psd_header=(Tcp_Psd_Header *)malloc(attachsize+sizeof(Tcp_Psd_Header));
  if(!ptcp_psd_header) return;
  memset(ptcp_psd_header,0,attachsize+sizeof(Tcp_Psd_Header));

  //填充偽TCP頭
  ptcp_psd_header->destip=pip_header->destIP;
  ptcp_psd_header->sourceip=pip_header->sourceIP;
  ptcp_psd_header->mbz=0;
  ptcp_psd_header->ptcl=0x06;
  ptcp_psd_header->tcpl=htons(attachsize);

  //計算TCP校驗和
  ptcp_header->chksum=0;
  memcpy((unsigned char *)ptcp_psd_header+sizeof(Tcp_Psd_Header),
   (unsigned char *)ptcp_header,attachsize);
  ptcp_header->chksum=checksum((unsigned short *)ptcp_psd_header,
   attachsize+sizeof(Tcp_Psd_Header));
  
  //計算ip頭的校驗和
  pip_header->checksum=0;
  pip_header->checksum=checksum((unsigned short *)pip_header,20);
  return;
 }
 
 //UDP包
 if(0x11==pip_header->proto)
 {
  Udp_Header *pudp_header=NULL; //UDP頭指標
  Udp_Psd_Header *pudp_psd_header=NULL;

  pudp_header=(Udp_Header *)(packet+14+((pip_header->ver_len)&15)*4);

  attachsize=ntohs(pip_header->total_len)-((pip_header->ver_len)&15)*4;
  pudp_psd_header=(Udp_Psd_Header *)malloc(attachsize+sizeof(Udp_Psd_Header));
  if(!pudp_psd_header) return;
        memset(pudp_psd_header,0,attachsize+sizeof(Udp_Psd_Header));

  //填充偽UDP頭
  pudp_psd_header->destip=pip_header->destIP;
  pudp_psd_header->sourceip=pip_header->sourceIP;
  pudp_psd_header->mbz=0;
  pudp_psd_header->ptcl=0x11;
  pudp_psd_header->udpl=htons(attachsize);
  
  //計算UDP校驗和
  pudp_header->chksum=0;
  memcpy((unsigned char *)pudp_psd_header+sizeof(Udp_Psd_Header),
   (unsigned char *)pudp_header,attachsize);
  pudp_header->chksum=checksum((unsigned short *)pudp_psd_header,
   attachsize+sizeof(Udp_Psd_Header));
    
  //計算ip頭的校驗和
  pip_header->checksum=0;
  pip_header->checksum=checksum((unsigned short *)pip_header,20);  
  return;
 }
 return;
}

 

需要幾個標頭檔案,以及庫:

 

#include
#include
#include "packet.h"
#pragma comment(lib,"ws2_32.lib")

 

 

 

最後附上我使用的資料包的結構體(比較多):

 

 

//資料包結構體
#pragma pack(1) 
/*物理幀頭結構*/
typedef struct {
   BYTE  desmac[6];      //目的MAC地址
   BYTE  srcmac[6];      //源MAC地址
   USHORT  ethertype;    //幀型別
}Dlc_Header;

/*Arp幀結構*/
typedef struct {
   USHORT hw_type;       //硬體型別Ethernet:0x1
   USHORT prot_type;     //上層協議型別IP:0x0800
   BYTE hw_addr_len;     //硬體地址長度:6
   BYTE prot_addr_len;   //協議地址(IP地址)的長度:4
   USHORT flag;          //1表示請求,2表示應答
   BYTE send_hw_addr[6]; //源MAC地址
   UINT send_prot_addr;  //源IP地址
   BYTE targ_hw_addr[6]; //目的MAC地址
   UINT targ_prot_addr;  //目的IP地址
   BYTE padding[18];     //填充資料  
}Arp_Frame;

/*ARP包=DLC頭+ARP幀*/
typedef struct {
 Dlc_Header dlcheader;//DLC頭
 Arp_Frame arpframe;  //ARP幀
}ARP_Packet;

/*IP報頭結構*/
typedef struct {
 BYTE  ver_len;       //IP包頭部長度,單位:4位元組
 BYTE  tos;           //服務型別TOS
 USHORT total_len;    //IP包總長度 
 USHORT ident;        //標識
 USHORT frag_and_flags;  //標誌位
 BYTE ttl;           //生存時間
 BYTE proto;         //協議
 USHORT checksum;    //IP首部校驗和
 UINT  sourceIP;  //源IP地址(32位)
 UINT  destIP;    //目的IP地址(32位)
}Ip_Header;

/*TCP報頭結構*/
typedef struct {
 USHORT srcport;   // 源埠
 USHORT dstport;   // 目的埠
 UINT seqnum;      // 順序號
 UINT acknum;      // 確認號
 BYTE dataoff;     // TCP頭長
 BYTE flags;       // 標誌(URG、ACK等)
 USHORT window;    // 視窗大小
 USHORT chksum;    // 校驗和
 USHORT urgptr;    // 緊急指標
}Tcp_Header;

//TCP偽首部 用於進行TCP校驗和的計算,保證TCP效驗的有效性
typedef struct{
 ULONG  sourceip;    //源IP地址
 ULONG  destip;      //目的IP地址
 BYTE mbz;           //置空(0)
 BYTE ptcl;          //協議型別(IPPROTO_TCP)
 USHORT tcpl;        //TCP包的總長度(單位:位元組)
}Tcp_Psd_Header;

/*UDP報頭*/
typedef struct  { 
 USHORT srcport;     // 源埠
 USHORT dstport;     // 目的埠
 USHORT total_len;   // 包括UDP報頭及UDP資料的長度(單位:位元組)
 USHORT chksum;      // 校驗和
}Udp_Header;

/*UDP偽首部-僅用於計算校驗和*/
typedef struct tsd_hdr 

 ULONG  sourceip;    //源IP地址
 ULONG  destip;      //目的IP地址
 BYTE  mbz;           //置空(0)
 BYTE  ptcl;          //協議型別(IPPROTO_UDP)
 USHORT udpl;         //UDP包總長度(單位:位元組) 
}Udp_Psd_Header;

/*ICMP報頭*/
typedef struct{
 BYTE i_type;     //型別 型別是關鍵:0->回送應答(Ping應答) 8->回送請求(Ping請求)
 BYTE i_code;     //程式碼 這個與型別有關 當型別為0或8時這裡都是0
 USHORT i_cksum;  //ICMP包校驗和
 USHORT i_id;     //識別號(一般用程式ID作為標識號)
 USHORT i_seq;    //報文序列號(一般設定為0)
 //UINT timestamp;  //時間戳
 BYTE padding[32];//填充資料
}Icmp_Header;
/*ICMP資料包*/
typedef struct
{
 Dlc_Header dlc_header;  //以太幀
 Ip_Header  ip_header;   //IP頭
 Icmp_Header icmp_header;//ICMP幀
}Icmp_Packet;

/*攻擊資訊*/
typedef struct
{
 unsigned char flag;     //攻擊資料包型別1-arp,2-tcp,3-udp
 unsigned int srcip;     //攻擊者IP
 unsigned char code[33]; //攻擊特徵碼
}Attack_Infor;
#pragma pack() 

本文轉自
http://hi.bccn.net/space-112902-do-blog-id-12121.html

相關文章