天天看點

用winpcap發送UDP資料包

今天嘗試用winpcap提供的pcap_sendpacket函數成功發送一個udp資料包到目的位址。 下面就來回顧一下過程。

首先用pcap_findalldevs得到網絡接口,比如在我的機器上得到這樣的結果:

1: /Device/NPF_GenericDialupAdapter

2: /Device/NPF_{06864041-9387-44DC-AF44-37779B0F2E9E}

現在我們及應用"/Device/NPF_{06864041-9387-44DC-AF44-37779B0F2E9E}“對應的接口發送資料。

由于發送資料必須自己建構以太網資料頭, IP資料標頭 和 UDP資料標頭, 資料結構大概是這個樣子的。

" 以太網資料頭 |  IP資料頭 | UDP資料標頭 | 資料"

以太網資料頭結構如下,

struct   ether_header{

 u_char   ether_dhost[ETHER_ADDR_LEN];

 u_char   ether_shost[ETHER_ADDR_LEN];

 u_short   ether_type;  //如果上一層為IP協定。則ether_type的值就是0x0800

};

以太網位址填充什麼資料, 我不是很清楚, 你可以填上實際的位址, 我這裡全部填1.

IP資料頭

struct ip_header  //小端模式__LITTLE_ENDIAN

{

 unsigned   char  ihl:4;    //ip   header   length   

 unsigned   char  version:4;   //version  

 u_char    tos;    //type   of   service  

 u_short    tot_len;   //total   length  

 u_short    id;     //identification  

 u_short    frag_off;   //fragment   offset  

 u_char    ttl;    //time   to   live  

 u_char    protocol;   //protocol   type  

 u_short    check;    //check   sum  

 u_int    saddr;    //source   address  

 u_int    daddr;    //destination   address  

};

tot_len為 整個IP資料包長度 (IP資料頭 | UDP資料標頭 | 資料)

ihl 為IP資料頭的總長度 除以4.  (4位元組對齊)

check 為在資料填好後, check為0 時的校驗和, 是對于IP資料頭。

u_int16_t in_cksum (u_int16_t * addr, int len)

{

int nleft = len;

u_int32_t sum = 0;

u_int16_t *w = addr;

u_int16_t answer = 0;

/*

* Our algorithm is simple, using a 32 bit accumulator (sum), we add

* sequential 16 bit words to it, and at the end, fold back all the

* carry bits from the top 16 bits into the lower 16 bits.

*/

while (nleft > 1) {

sum += *w++;

nleft -= 2;

}

/* mop up an odd byte, if necessary */

if (nleft == 1) {

* (unsigned char *) (&answer) = * (unsigned char *) w;

sum += answer;

}

/* add back carry outs from top 16 bits to low 16 bits */

sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */

sum += (sum >> 16); /* add carry */

answer = ~sum; /* truncate to 16 bits */

return (answer);

}

protocol 為IPPROTO_UDP。

UDP資料標頭

struct udphdr

{

  u_int16_t source;        

  u_int16_t dest;   

  u_int16_t len;           

  u_int16_t checkl;        

};

len 為UDP資料包長度,

checkl  校驗和(IP為首部 | UDP資料標頭 | 資料), 這個比較特殊一點, 要加一個僞首部

struct Psd_Header {

 ULONG sourceip; //源IP位址

 ULONG destip; //目的IP位址

 BYTE mbz; //置空(0)

 BYTE ptcl; //協定類型

 USHORT plen; //TCP/UDP資料包的長度(即從TCP/UDP報頭算起到資料包結束的長度 機關:位元組)

};

plen 和UDP長度是一樣的。

當把這些都填充好了, 就可以用pcap_sendpacket發送出去了

#define HAVE_REMOTE

#include <pcap.h>

#pragma comment(lib, "wpcap.lib")

#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

//typedef void(* pcap_handler)(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)

void my_pcap_handler(u_char *user, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data);

#define ETHER_ADDR_LEN 6

//from linux's ethernet.h

#define ETHERTYPE_PUP 0x0200 /* Xerox PUP */

#define ETHERTYPE_SPRITE 0x0500 /* Sprite */

#define ETHERTYPE_IP 0x0800 /* IP */

#define ETHERTYPE_ARP 0x0806 /* Address resolution */

#define ETHERTYPE_REVARP 0x8035 /* Reverse ARP */

#define ETHERTYPE_AT 0x809B /* AppleTalk protocol */

#define ETHERTYPE_AARP 0x80F3 /* AppleTalk ARP */

#define ETHERTYPE_VLAN 0x8100 /* IEEE 802.1Q VLAN tagging */

#define ETHERTYPE_IPX 0x8137 /* IPX */

#define ETHERTYPE_IPV6 0x86dd /* IP protocol version 6 */

#define ETHERTYPE_LOOPBACK 0x9000 /* used to test interfaces */

struct ether_header{

u_char ether_dhost[ETHER_ADDR_LEN];

u_char ether_shost[ETHER_ADDR_LEN];

u_short ether_type; //如果上一層為IP協定。則ether_type的值就是0x0800

};

char* prase_ether_host(u_char ether_host[ETHER_ADDR_LEN], char* buffer);

struct ip_header //小端模式__LITTLE_ENDIAN

{

unsigned char ihl:4; //ip header length

unsigned char version:4; //version

u_char tos; //type of service

u_short tot_len; //total length

u_short id; //identification

u_short frag_off; //fragment offset

u_char ttl; //time to live

u_char protocol; //protocol type

u_short check; //check sum

u_int saddr; //source address

u_int daddr; //destination address

};

struct tcphdr //小端模式__LITTLE_ENDIAN

{

u_int16_t source;

u_int16_t dest;

u_int32_t seq;

u_int32_t ack_seq;

u_int16_t res1:4;

u_int16_t doff:4;

u_int16_t fin:1;

u_int16_t syn:1;

u_int16_t rst:1;

u_int16_t psh:1;

u_int16_t ack:1;

u_int16_t urg:1;

u_int16_t res2:2;

u_int16_t window;

u_int16_t check;

u_int16_t urg_ptr;

};

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 */

};

char* uint_to_addr(u_int addr);

u_int16_t in_cksum (u_int16_t * addr, int len)

{

int nleft = len;

u_int32_t sum = 0;

u_int16_t *w = addr;

u_int16_t answer = 0;

/*

* Our algorithm is simple, using a 32 bit accumulator (sum), we add

* sequential 16 bit words to it, and at the end, fold back all the

* carry bits from the top 16 bits into the lower 16 bits.

*/

while (nleft > 1) {

sum += *w++;

nleft -= 2;

}

/* mop up an odd byte, if necessary */

if (nleft == 1) {

* (unsigned char *) (&answer) = * (unsigned char *) w;

sum += answer;

}

/* add back carry outs from top 16 bits to low 16 bits */

sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */

sum += (sum >> 16); /* add carry */

answer = ~sum; /* truncate to 16 bits */

return (answer);

}

struct Psd_Header {

ULONG sourceip; //源IP位址

ULONG destip; //目的IP位址

BYTE mbz; //置空(0)

BYTE ptcl; //協定類型

USHORT plen; //TCP/UDP資料包的長度(即從TCP/UDP報頭算起到資料包結束的長度 機關:位元組)

};

har* device = "//Device//NPF_{06864041-9387-44DC-AF44-37779B0F2E9E}";

pcap_t* adhandle = NULL;

char errbuf[PCAP_ERRBUF_SIZE] = { 0 };

void main()

{

if((adhandle = pcap_open(device, 0x10000, PCAP_OPENFLAG_PROMISCUOUS, 1000, NULL, errbuf)) == NULL)

{

printf("[pcap_open error] : %s/n", errbuf);

return;

}

char buffer[64] = { 0 };

ether_header* pether_header = (ether_header*)buffer;

ip_header* pip_herder = (ip_header*)(buffer + sizeof(ether_header));

udphdr* pudp_herder = (udphdr*)(buffer + sizeof(ether_header) + sizeof(ip_header));

pether_header->ether_dhost[0] = 1; //0x0 * 16 + 0x0;;

pether_header->ether_dhost[1] = 1; //0x2 * 16 + 0x1;

pether_header->ether_dhost[2] = 1; //0x2 * 16 + 0x7;

pether_header->ether_dhost[3] = 1; //0x2 * 16 + 0x3;

pether_header->ether_dhost[4] = 1; //0x7 * 16 + 0x2;

pether_header->ether_dhost[5] = 1; //0xf * 16 + 0xe;

pether_header->ether_shost[0] = 1; //0x0 * 16 + 0x0;;

pether_header->ether_shost[1] = 1; //0x1 * 16 + 0xF;

pether_header->ether_shost[2] = 1; //0xD * 16 + 0x0;

pether_header->ether_shost[3] = 1; //0x1 * 16 + 0x6;

pether_header->ether_shost[4] = 1; //0x6 * 16 + 0x3;

pether_header->ether_shost[5] = 1; //0x7 * 16 + 0x1;

pether_header->ether_type = htons(ETHERTYPE_IP);

//建構IP資料頭

if((sizeof(ip_header) % 4) != 0)

{

printf("[IP Header error]/n");

return;

}

pip_herder->ihl = sizeof(ip_header) / 4;

pip_herder->version = 4;

pip_herder->tos = 0;

pip_herder->tot_len = htons(sizeof(buffer) - sizeof(ether_header));

pip_herder->id = htons(0x1000);

pip_herder->frag_off = htons(0);

pip_herder->ttl = 0x80;

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首部

//pudp_herder->checkl = in_cksum((u_int16_t*)pudp_herder, 24);

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));

if(pcap_sendpacket(adhandle, (const u_char*)buffer, 64) == -1)

{

printf("[pcap_sendpacket error]/n");

return;

}

}