今天嘗試用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;
}
}