/*
程序功能:
1、構造arp包,並發送。程序參數順序:源IP、目的IP、mac地址、flag
2、獲取網絡中的ARP數據包,解析數據包的內容。程序參數:日志文件名
winpacp中文技術文檔(基本是英文的):http://www.ferrisxu.com/WinPcap/html/index.html
*/
一、構造arp包
在構造之前先了解一下arp包的結構,先從網上找了張圖

從圖中可以看出以太網首部占14字節,以太網ARP字段占28字節。其中op字段為操作類型,1表示ARP請求、2表示ARP應答
再介紹幾個要用到的pcap函數
函數功能:列出當前所有可用的網絡設備(網卡),將設備信息存入pcap_if_t結構列表中
參數:1、alldevsp 指向pcap_if_t結構列表的指針的地址(注意這裡是pcap_if_t指針的地址,而不是pcap_if_t結構的地址)
有些地方這裡可能會寫pcap_if結構,其實pcap_if和pcap_if_t就是同一個東西,我們來看看在pcap.h中是怎麼定義的

pcap_if結構體成員:
Struct pcap_if {
struct pcap_if *next; //指向下一個鏈表成員
char *name; //網卡名稱
chat *description; //網卡描述信息
struct pcap_addr address;
u_int flags; //接口標志
}
2、errbuf 錯誤緩沖區,要求長度至少為PCAP_ERRBUF_SIZE 字節,那麼PCAP_ERRBUF_SIZE是多大呢
這在pcap.h中宏定義的,如下圖

這個錯誤緩沖區用來做什麼呢?在函數錯誤返回時(返回值為-1)會向錯誤緩沖中填充錯誤信息,錯誤信息為可打印ASCII碼
函數正確時返回0
2、pcap_t * pcap_open_live ( char * device, int snaplen, int promisc,int to_ms, char * errbuf )
函數功能:在網絡中打開一個活動的捕獲<這是winpcap技術文檔給出的說明,也就是指定從一個網絡設備捕獲數據包,我是這麼理解的>
函數的返回值為一個結構體指針pcap_t即為struct pcap。pcap_t結構體有點長就不做說明了,裡面就是捕獲句柄的一些信息
參數: <文檔是英文的不知道有沒有翻譯對>
device 設備名
snaplen 單包最大捕捉字節數(若數據包大於snaplen,只有前面snaplen字節大小的數據被捕獲)
promisc 混雜模式(即使該參數是false,也可能由其他原因導致網絡接口為混雜模式)
to_ms 指定毫秒級讀超時(當一個數據包被發現時,並不一定立即返回數據包,它會等待一段時間,允許一個操作從系統內核讀取多個數據 包。不是所有的平台都支持讀超時,在不支持的平台上讀超時會被忽略。)<在支持讀超時的平台上若讀超時為0,將導致永不超時>
errbuf 用於返回錯誤或警告信息
3、void pcap_close ( pcap_t *p )
關閉pcap_open_live()獲取的包捕獲句柄,釋放相關資源
源碼:
1 /*
2 構造並發送ARP包
3 2015年6月24日15:44:21
4 blog:http://www.cnblogs.com/wd1001/
5 */
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <pcap.h>
9
10 #pragma comment(lib, "wpcap.lib")
11 #pragma comment(lib, "wsock32.lib")
12 #pragma comment(lib, "ws2_32.lib")
13
14 main(int argc, char **argv)
15 {
16 u_char packet[100];
17 pcap_if_t *alldevs;
18 pcap_if_t *d;
19 int inum;
20 int i=0,j,k,temp[3];
21 pcap_t * adhandle;
22 char errbuf[PCAP_ERRBUF_SIZE];
23 /* 獲取設備列表 */
24
25 if (argc != 5)//argc==5,及程序後面有四個參數
26 {
27 printf("usage: %s inerface", argv[0]);
28 return -1;
29 }
30
31
32 if (pcap_findalldevs(&alldevs, errbuf) == -1)
33 {
34 fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
35 exit(1);
36 }
37 /* 數據列表 */
38 for(d=alldevs; d; d=d->next)
39 {
40 printf("%d. %s", ++i, d->name);
41 if (d->description)
42 printf(" (%s)\n", d->description);
43 else
44 printf(" (No description available)\n");
45 }
46 if(i==0)
47 {
48 printf("\n找不到網卡! 檢查是否安裝WinPcap.\n");
49 return -1;
50 }
51 printf("Enter the interface number (1-%d):",i);
52 scanf("%d", &inum);
53 if(inum < 1 || inum > i)
54 {
55 printf("\nInterface number out of range.\n");
56 /* 釋放設備列表 */
57 pcap_freealldevs(alldevs);
58 return -1;
59 }
60 /* 轉到選擇的設備 */
61 for(d=alldevs, i=0; i< inum-1;d=d->next, i++);
62 /* 打開設備 */
63 if ( (adhandle= pcap_open_live(d->name, //設備名
64 65536, // 最大捕捉字節數
65 1, // 混雜模式
66 1000, // 讀入超時
67 errbuf // 錯誤緩沖
68 ) ) == NULL)
69 {
70 /*打開失敗*/
71 fprintf(stderr,"\n打開失敗. %s 不被winpcap支持\n",d->name);
72 /* 釋放列表 */
73 pcap_freealldevs(alldevs);
74 return -1;
75 }
76 /* 釋放設備列表 */
77 pcap_freealldevs(alldevs);
78
79 /* 填充數據段 */
80
81 //flag為1表示ARP請求
82 if('1'==argv[4][0])
83 {
84 //源MAC地址
85 k=0;
86 for(i=0;i<18;i=i+3)
87 {
88 temp[0]=(int)argv[3][i];
89 temp[1]=(int)argv[3][i+1];
90 if(temp[0]>96) //當輸入mac為小寫字母時字符轉換為16進制
91 temp[0]=temp[0]-87;
92 else if(temp[0]>64)
93 temp[0]=temp[0]-55;//當輸入mac為大寫字母時字符轉換為16進制
94 else
95 temp[0]=temp[0]-48;//當輸入mac為數字時字符轉換為16進制
96 if(temp[1]>96)
97 temp[1]=temp[1]-87;
98 else if(temp[1]>64)
99 temp[1]=temp[1]-55;
100 else
101 temp[1]=temp[1]-48;
102 packet[22+k]=packet[6+k]=temp[0]*16+temp[1];
103 k++;
104 }
105
106 //發送ARP請求時目的MAC全置為ff
107 for(i=0;i<6;i++)
108 {
109 packet[i]=packet[32+i]=0xff;
110 }
111 }
112
113 //flag=2:ARP應答
114 else
115 {
116 //目的MAC地址
117 k=0;
118 for(i=0;i<18;i=i+3)
119 {
120 temp[0]=(int)argv[3][i];
121 temp[1]=(int)argv[3][i+1];
122 if(temp[0]>96)
123 temp[0]=temp[0]-87;
124 else if(temp[0]>64)
125 temp[0]=temp[0]-55;
126 else
127 temp[0]=temp[0]-48;
128 if(temp[1]>96)
129 temp[1]=temp[1]-87;
130 else if(temp[1]>64)
131 temp[1]=temp[1]-55;
132 else
133 temp[1]=temp[1]-48;
134 packet[k]=packet[32+k]=temp[0]*16+temp[1];
135 k++;
136 }
137 //應答ARP請求時把源MAC置為0
138 for(i=0;i<6;i++)
139 {
140 packet[6+i]=packet[22+i]=0x00;
141 }
142 }
143
144 //源IP地址
145 k=0;
146 temp[2]=0; //指向每個字節初始位置
147 for(i=0;i<4;i++)
148 {
149 temp[0]=0;
150 temp[1]=0;
151 for(j=0;j<4;j++)
152 {
153 if(argv[1][j+temp[2]]>='0'&&argv[1][j+temp[2]]<='9')
154 {
155 temp[0]=(int)argv[1][j+temp[2]]-48;
156 temp[1]=temp[1]*10+temp[0];
157 //printf("%d %d\n",temp[0],temp[1]);
158 }
159 else
160 {
161 //當遇到點時j自加1目的是讓temp[2]+j指向下一字節的第一位
162 j++;
163 break;
164 }
165 }
166 packet[28+k]=temp[1];
167 k++;
168 temp[2]+=j;
169 }
170 //目標IP地址
171 k=0;
172 temp[2]=0;
173 for(i=0;i<4;i++)
174 {
175 temp[0]=0;
176 temp[1]=0;
177 for(j=0;j<4;j++)
178 {
179 if(argv[2][j+temp[2]]>='0'&&argv[2][j+temp[2]]<='9')
180 {
181 temp[0]=(int)argv[2][j+temp[2]]-48;
182 temp[1]=temp[1]*10+temp[0];
183 //printf("%d %d\n",temp[0],temp[1]);
184 }
185 else
186 {
187 j++;
188 break;
189 }
190 }
191 packet[38+k]=temp[1];
192 k++;
193 temp[2]+=j;
194 }
195 //ARP首部
196 packet[12]=0x08;//12、13位為幀類型
197 packet[13]=0x06;
198 packet[14]=0x00;//14、15位為硬件類型
199 packet[15]=0x01;
200 packet[16]=0x08;//16、17位為協議類型
201 packet[17]=0x00;
202 packet[18]=0x06;//硬件地址長度
203 packet[19]=0x04;//協議地址長度
204 packet[20]=0x00;//op
205 packet[21]=(int)argv[4][0]-48;//op(1為請求2為應答)
206
207
208 /* 填充發送包的剩余部分 */
209 for(i=0;i<18;i++)
210 {
211 packet[42+i]=0;
212 }
213 //這裡後四個字節本應該是校驗位,這裡就不算了,寫個日期紀念一下
214 packet[60]=0x20;
215 packet[61]=0x15;
216 packet[62]=0x6;
217 packet[63]=0x24;
218 /* 發送包 */
219 pcap_sendpacket(adhandle, packet, 64);
220 printf("Success!\n");
221
222 return 0;
223 }
運行結果:
輸入參數:隨意設置源IP,目的IP和mac地址

選擇網卡並發送,抓包結果如下

二、獲取網絡中的ARP數據包,解析數據包的內容
在上面的基礎上再解析ARP數據包就簡單了
同樣在解析前先介紹幾個要用到的函數
1、int pcap_compile ( pcap_t * p, struct bpf_program * fp, char * str,int optimize , bpf_u_int32 netmask )
函數功能:將字符串str編譯進一個過濾程序,將程序中高級的過濾表達式,轉換成能被內核級的過濾引擎所處理的東西
參數:1、p為pcap_open_live返回的一個捕獲句柄
2、fp為一個指向bpf_program結構的指針,由pcap_compile()函數填寫
bpf_program結構為:
struct bpf_program {
u_int bf_len;
struct bpf_insn *bf_insns;
};
bpf_insn結構為:
struct bpf_insn {
u_short code;
u_char jt;
u_char jf;
bpf_int32 k;
};
3、str 過濾串表達式
4、optimize 優化控制,是否執行結果代碼優化(optimize controls whether optimization on the resulting code is performed)
5、netmask 子網掩碼
2、int pcap_setfilter (pcap_t *p, struct bpf_program *fp)
函數功能:在捕獲過程中綁定一個過濾器
源碼:
1 /*
2 獲取網絡中的ARP數據包,解析數據包的內容
3 2015年6月24日19:36:36
4 blog:http://www.cnblogs.com/wd1001/
5 */
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <pcap.h>
9 #pragma comment(lib, "wpcap.lib")
10 #pragma comment(lib, "wsock32.lib")
11 #pragma comment(lib, "ws2_32.lib")
12 //定義ARP包數據
13 typedef struct arppkt
14 {
15 unsigned short hdtyp;//硬件類型
16 unsigned short protyp;//協議類型
17 unsigned char hdsize;//硬件地址長度
18 unsigned char prosize;//協議地址長度
19 unsigned short op;//(操作類型)操作值:ARP/RARP
20 u_char smac[6];//源MAC地址
21 u_char sip[4];//源IP地址
22 u_char dmac[6];//目的MAC地址
23 u_char dip[4];//目的IP地址
24 }arpp;
25 int main(int argc,char * argv[] )
26 {
27 struct tm * timeinfo;
28 struct tm *ltime;
29 time_t rawtime;
30 FILE * fp=NULL;
31 int result;
32 int i=0,inum;
33 pcap_if_t * alldevs;//指向pcap_if_t結構列表指針
34 pcap_if_t * d;
35 pcap_t * adhandle;//定義包捕捉句柄
36 char errbuf[PCAP_ERRBUF_SIZE];//錯誤緩沖最小為256
37 u_int netmask; //定義子網掩碼
38 char packet_filter[]="ether proto \\arp";
39 struct bpf_program fcode;
40 struct pcap_pkthdr * header;
41 const u_char * pkt_data;
42 //打開日志文件
43 if((fp=fopen(argv[1],"a"))==NULL)
44 {
45 printf("打開文件失敗!\n");
46 exit(0);
47 }
48 if (argc != 2)//argc==2,及程序後面有1個參數
49 {
50 printf("程序%s需要一個日志文件名參數!\n", argv[0]);
51 return -1;
52 }
53 //當前所有可用的網絡設備
54 if (pcap_findalldevs(&alldevs, errbuf) == -1)
55 {
56 fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
57 exit(1);
58 }
59 //列出網絡設備
60 for(d=alldevs; d; d=d->next)
61 {
62 printf("%d. %s", ++i, d->name);
63 if (d->description)
64 printf(" (%s)\n", d->description);
65 else
66 printf(" (沒有描述可用)\n");
67 }
68 if(i==0)
69 {
70 printf("\n找不到網卡! 檢查是否安裝WinPcap.\n");
71 return -1;
72 }
73 printf("選擇對應網卡編號 (1-%d):",i);
74 scanf("%d", &inum);
75 if(inum < 1 || inum > i)
76 {
77 printf("\n輸入的編號超出范圍!\n");
78 /* 釋放設備列表 */
79 pcap_freealldevs(alldevs);
80 return -1;
81 }
82 /* 轉到選擇的設備 */
83 i=0;
84 d=alldevs;
85 while(i<inum-1)
86 {
87 d=d->next;
88 i++;
89 }
90 if ( (adhandle= pcap_open_live(d->name,65536, 1,1000,errbuf) ) == NULL)
91 {
92 /*打開失敗*/
93 fprintf(stderr,"\n打開失敗. %s 不被winpcap支持\n",d->name);
94 /* 釋放設備列表 */
95 pcap_freealldevs(alldevs);
96 return -1;
97 }
98 /* 釋放設備列表 */
99 pcap_freealldevs(alldevs);
100
101 //獲得子網掩碼
102 netmask=((sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
103 //編譯過濾器,只捕獲ARP包
104 if(pcap_compile(adhandle,&fcode,packet_filter,1,netmask)<0)
105 {
106 printf("\nUnable to compile the packet filter.Check the syntax.\n");
107 pcap_freealldevs(alldevs);
108 return -1;
109 }
110 //設置過濾器
111 if(pcap_setfilter(adhandle,&fcode)<0)
112 {
113 printf("\nError setting the filter.\n");
114 pcap_freealldevs(alldevs);
115 return -1;
116 }
117 //輸出每次修改文件時間
118 time ( &rawtime );
119 timeinfo = localtime ( &rawtime );
120 printf("--------------修改時間:%s",asctime (timeinfo));
121 fprintf(fp,"-----------修改時間:%s",asctime (timeinfo));
122 fflush(fp);//刷新緩沖流
123 while((result=pcap_next_ex(adhandle,&header,&pkt_data))>=0)
124 {
125 //循環解析ARP數據包
126 if(result==0)
127 continue;
128 //解析ARP包,結果輸出到屏幕與文件
129 arppkt* arph = (arppkt *)(pkt_data +14);
130 //輸出操作時間
131 ltime=localtime(&header->ts.tv_sec);
132 printf("時間:%s",asctime (ltime));
133 fprintf(fp,"時間:%s",asctime (ltime));
134 //輸出源IP
135 printf("源IP:");
136 fprintf(fp,"源IP:");
137 for(i=0;i<3;i++)
138 {
139 printf("%d.",arph->sip[i]);
140 fprintf(fp,"%d.",arph->sip[i]);
141 }
142 printf("%d\t",arph->sip[3]);
143 fprintf(fp,"%d.\t",arph->sip[3]);
144 //輸出目的IP
145 printf("目的IP:");
146 fprintf(fp,"目的IP:");
147 for(i=0;i<3;i++)
148 {
149 printf("%d.",arph->dip[i]);
150 fprintf(fp,"%d.",arph->dip[i]);
151 }
152 printf("%d\t",arph->dip[3]);
153 fprintf(fp,"%d\t",arph->dip[3]);
154 //輸出源mac
155 printf("源mac:");
156 fprintf(fp,"源mac:");
157 for(i=0;i<5;i++)
158 {
159 printf("%x-",arph->smac[i]);
160 fprintf(fp,"%x-",arph->smac[i]);
161 }
162 printf("%x\t",arph->smac[5]);
163 fprintf(fp,"%x\t",arph->smac[5]);
164 //輸出目的mac
165 printf("目的mac:");
166 fprintf(fp,"目的mac:");
167 for(i=0;i<5;i++)
168 {
169 printf("%x-",*(pkt_data+i));
170 fprintf(fp,"%x-",*(pkt_data+i));
171 }
172 printf("%x\t",*(pkt_data+5));
173 fprintf(fp,"%x\t",*(pkt_data+5));
174 //輸出操作類型
175 printf("操作類型(ARP/RARP):");
176 fprintf(fp,"操作類型(ARP/RARP):");
177 if(arph->op==256)
178 {
179 printf("ARP\t");
180 fprintf(fp,"ARP\t");
181 }
182 else
183 {
184 printf("RARP\t");
185 fprintf(fp,"RARP\t");
186 }
187 printf("\n");
188 fprintf(fp,"\n");
189 printf("--------------------------------------\n");
190 fprintf(fp,"--------------------------------------\n");
191 fflush(fp);
192 }
193 fclose(fp);
194 return 0;
195 }