关于使用Winpcap发送数据包的心得

在对网络数据包发送解析进行编程的时候,人们普遍使用的是Win socket套接字技术,但该技术仅限于网络层及其以上的传输层,对于下层应用的不多,为了了解数据包发送的真正机制,需要使用winpcap来进行数据包发送。下面针对两种协议使用winpcap进行数据包的发送。

winpcap下载地址:http://www.winpcap.org/archive/

使用winpcap发送TCP协议数据包

首先本机上应该安装winpcap以及wpcap,这样才可以调用winpcap中的函数。

下面针对TCP协议进行分析:

由于要发送的数据必须构建以太网数据头,IP数据包头和TCP数据包头,所以待发送的数据应该如下:

以太网数据头 | *.*IP数据头 | TCP数据头 | 数据

  1. 定义以太网数据头结构

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;

  1. 定义IP数据报头

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;

  1. 定义TCP数据报头

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句柄

使用Winpcap发送UDP数据包

类似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发送数据包的过程和原理,相信针对其他协议也是大同小异。

你可能感兴趣的:(程序人生)