阅读:2877回复:7
passthru下TCP校验和的计算问题 郁闷好几天了
写了一个数据包捕获转发的驱动 计算TCP校验一直不正确 IP校验和没问题 算TCP校验和的时候我已经增加了伪头啊...
源码如下 USHORT checksum(USHORT *buffer, int size) { unsigned long cksum=0; while(size >1) { cksum+=*buffer++; size -=sizeof(USHORT); } if(size) { cksum += *(UCHAR*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (USHORT)~cksum; } 封装TCP包部分 //计算TCP包 nTempPort=TcpHdr.sourcePort; TcpHdr.sourcePort=TcpHdr.destinationPort; TcpHdr.destinationPort=nTempPort; nTempSeq=TcpHdr.sequenceNumber; TcpHdr.sequenceNumber=TcpHdr.acknowledgeNumber;//上次的ACK值作为SEQ TcpHdr.acknowledgeNumber=nTempSeq+htons((nDataLen-sizeof(ETHeader)-sizeof(IPHeader)));//计算ACK内容 TcpHdr.dataoffset=(sizeof(TCPHeader)/4<<4|0); TcpHdr.flags=0x18; TcpHdr.windows=65535; TcpHdr.checksum=0; //填充TCP伪头 PsdHdr.saddr = IpHdr.ipSource; PsdHdr.daddr = IpHdr.ipDestination; PsdHdr.mbz = 0; PsdHdr.ptcl = IPPROTO_TCP; PsdHdr.tcpl = ntohs(sizeof(TCPHeader)); RtlZeroMemory(szSendData,2048);//清零 RtlCopyMemory(szSendData,&PsdHdr,sizeof(PSDHeader)); RtlCopyMemory(szSendData+sizeof(PSDHeader),&TcpHdr,sizeof(TCPHeader)); TempCheckNum=checksum((USHORT*)szSendData,sizeof(PSDHeader)+sizeof(TCPHeader)); //计算TCP校验和 TcpHdr.checksum=TempCheckNum; |
|
沙发#
发布于:2010-12-30 01:01
补充一句 客户端网卡已经收到包了 只不过因为校验和不对被上层丢弃了
|
|
板凳#
发布于:2010-12-30 09:11
校验和不正确 应该是发送不出去的吧。。 你怎么看到的?
|
|
地板#
发布于:2010-12-30 09:21
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 <winsock2.h> #include <windows.h> #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() 网上代码,可以参考 |
|
|
地下室#
发布于:2010-12-30 11:15
没用 我已经加了伪头了
|
|
5楼#
发布于:2010-12-30 18:52
自己解决了 因为不在TCP握手阶段 校验和要校验数据包大小 感谢两位好心的朋友帮忙 困扰了好几天了也
|
|
6楼#
发布于:2010-12-31 09:09
回 5楼(gt2333588) 的帖子
我粘贴的代码可以在不同阶段通用,可以参考一下 |
|
|
7楼#
发布于:2011-10-17 14:49
|
|