在對網路資料包發送解析進行編程的時候,人們普遍使用的是Win socket通訊端技術,但該技術僅限於網路層及其以上的傳輸層,對於下層應用的不多,為了瞭解資料包發送的真正機制,需要使用winpcap來進行資料包發送。下面針對兩種協議使用winpcap進行資料包的發送。
winpcap:http://www.winpcap.org/archive/
使用winpcap發送TCP協議資料包
首先本機上應該安裝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;
- 定義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;
- 定義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發送資料包的過程和原理,相信針對其他協議也是大同小異。