在对网络数据包发送解析进行编程的时候,人们普遍使用的是Win socket套接字技术,但该技术仅限于网络层及其以上的传输层,对于下层应用的不多,为了了解数据包发送的真正机制,需要使用winpcap来进行数据包发送。下面针对两种协议使用winpcap进行数据包的发送。
winpcap下载地址:http://www.winpcap.org/archive/
首先本机上应该安装winpcap以及wpcap,这样才可以调用winpcap中的函数。
下面针对TCP协议进行分析:
由于要发送的数据必须构建以太网数据头,IP数据包头和TCP数据包头,所以待发送的数据应该如下:
以太网数据头 | *.*IP数据头 | TCP数据头 | 数据
typedef struct et_header
{
unsigned char eh_dst[6]; //目的地址
unsigned char eh_src[6]; //源地址
unsigned short eh_type; //eh_type的值需要考察上一层的协议,如果为ip则为0×0800
}ET_HEADER;
typedef struct ip_hdr
{
unsigned char h_verlen; //ip头部长度(按4字节对齐)
unsigned char tos; //服务类型
unsigned short total_len; //总长度(包含IP数据头,TCP数据头以及数据)
unsigned char version:4; //一般IP类型为IPv4
unsigned short ident; //ID定义单独IP
unsigned short frag_and_flags;//标志位偏移量
unsigned char ttl; //生存时间
unsigned char proto; //协议类型
unsigned short checksum; //检查和
unsigned int sourceIP; //源IP地址
unsigned int destIP; //目的IP地址
}IP_HEADER;
typedef struct tcp_hdr
{
unsigned short th_sport; //源端口
unsigned short th_dport; //目的端口
unsigned int th_seq; //序列号
unsigned int th_ack; //确认号
unsigned char th_lenres; //4位首部长度
unsigned char th_flag; //标志位
unsigned short th_win; //窗口大小
unsigned short th_sum; //检验和
unsigned short th_urp; //紧急指针
}TCP_HEADER;
上面三个结构类型中构建了数据报头的所有信息
考虑到检验和的问题,需要添加一个伪首部:
typedef struct tsd_hdr
{
unsigned long saddr; //源地址
unsigned long daddr; //目的地址
char mbz; //置空
char ptcl; //协议类型
unsigned short tcpl; //数据包长度
}PSD_HEADER;
以下针对信息进行处理
处理流程:
l 获取本地机器设备列表(调用pcap_findalldevs_ex函数)如果找到网卡设备就把设备存储在interfaceName(可以自定义)变量之中,同时获取本地IP地址(最基本条件下),调用pcap_freealldevs函数释放设备。
l 获取本地网关的MAC地址,通过声明PPACKET_OID_DATA OidData,然后进行MAC地址分配空间
OidData=(PPACKET_OID_DATA)malloc(6+sizeof(PACKET_OID_DATA)),调用OidDATA->Oid=OID_802_3_CURRENT_ADDRESS;ZeroMemory(OidData->Data,6);即可获得MAC地址
l 接下来对数据包的头部进行赋值,尤其要注意针对由于TCP标志位较多,可以使用位运算判断FLAG的具体值。
l pcap_open( InterfaceName, 100, PCAP_OPENFLAG_PROMISCUOUS, 100, NULL, errbuf )该函数打开网卡设备
l pcap_sendpacket( pcap_t *p, Buffer, sizeof(Buffer) 发送数据包,Buffer就是要发送数据包,p表示winpcap句柄
类似TCP所不同的是需要添加UDP头部数据
UDP数据包头
struct udphdr
{
u_int16_t source; /* source port */
u_int16_t dest; /* destination port */
u_int16_t len; /* udp length */
u_int16_t checkl; /* udp checksum */
};
为了避免重复,下面说明数据包头部信息的设置
ether_header* pether_header=(ether_header*)buffer; //以太网首部指针
ip_header* pip_herder=(ip_header*)(buffer + sizeof(ether_header)); //IP数据头指针
udphdr* pudp_herder=(udphdr*)(buffer + sizeof(ether_header) + sizeof(ip_header));//UDP数据头指针
//针对以太网头部源地址进行赋值
pether_header->ether_dhost[0] = 1; //0×0 * 16 + 0×0;;
pether_header->ether_dhost[1] = 1; //0×2 * 16 + 0×1;
pether_header->ether_dhost[2] = 1; //0×2 * 16 + 0×7;
pether_header->ether_dhost[3] = 1; //0×2 * 16 + 0×3;
pether_header->ether_dhost[4] = 1; //0×7 * 16 + 0×2;
pether_header->ether_dhost[5] = 1; //0xf * 16 + 0xe;
//针对以太网头部目的地址进行赋值
pether_header->ether_shost[0] = 1; //0×0 * 16 + 0×0;;
pether_header->ether_shost[1] = 1; //0×1 * 16 + 0xF;
pether_header->ether_shost[2] = 1; //0xD * 16 + 0×0;
pether_header->ether_shost[3] = 1; //0×1 * 16 + 0×6;
pether_header->ether_shost[4] = 1; //0×6 * 16 + 0×3;
pether_header->ether_shost[5] = 1; //0×7 * 16 + 0×1;
//针对以太网协议进行赋值
pether_header->ether_type = htons(ETHERTYPE_IP);
//构建IP数据头
pip_herder->ihl = sizeof(ip_header) / 4; //以4字节为单位
pip_herder->version = 4; //设定版本号
pip_herder->tos = 0; //设定类型
pip_herder->tot_len = htons(sizeof(buffer) – sizeof(ether_header)); //设定长度
pip_herder->id = htons(0×1000);//设定ID
pip_herder->frag_off = htons(0);//设定偏移量
pip_herder->ttl = 0×80;//设定TTL
pip_herder->protocol = IPPROTO_UDP; //设定协议类型
pip_herder->check = 0; //设定检验和
pip_herder->saddr = inet_addr(“192.168.18.*”); //设定源地址
pip_herder->daddr = inet_addr(“122.*.*.*”);//设定目的地址
pip_herder->check = in_cksum((u_int16_t*)pip_herder, sizeof(ip_header)); //重新设定检验和
//构建UDP数据头;
pudp_herder->dest = htons(7865); //目的端口号
pudp_herder->source = htons(2834);//源端口号
pudp_herder->len = htons(sizeof(buffer) – sizeof(ether_header) – sizeof(ip_header));//设定长度
pudp_herder->checkl = 0;//设定检验和
//构造伪UDP首部
char buffer2[64] = { 0 };
Psd_Header* psd = (Psd_Header*)buffer2;
psd->sourceip = inet_addr(“192.168.18.*”);
psd->destip = inet_addr(“122.*.*.*”);
psd->ptcl = IPPROTO_UDP;
psd->plen = htons(sizeof(buffer) – sizeof(ether_header) – sizeof(ip_header));
psd->mbz = 0;
memcpy(buffer2 + sizeof(Psd_Header), (void*)pudp_herder, sizeof(buffer) – sizeof(ether_header) – sizeof(ip_header));
pudp_herder->checkl = in_cksum((u_int16_t *)buffer2,sizeof(buffer) – sizeof(ether_header) – sizeof(ip_header) + sizeof(Psd_Header));
接下来调用pcap_open,pcap_sendpacket即可发送数据包
小结:通过研究这两个协议了解了winpcap发送数据包的过程和原理,相信针对其他协议也是大同小异。