这一节,我们看一下winpcap的相关知识。
winpcap 是不能嗅探本地回环包的,不像npcap可以直接检索单本地的LoopBack(127.0.0.1)
如果非要的话,也是有两种方法的。
1、把本地包的下一跳直接设置成网关的,本地宝就会通过网卡到网关,这样就能捕捉到了。详细教程可以参考这里。
该方式把所有本地包都从网关过了一下,导致效率低了。
2、导出wpcap.dll的导出函数。重新设计。
1、环境集成
首先在官网下载winpcap安装包(WinPcap - Home),然后下载依赖文件()。
或者直接下载我弄好的咯((1条消息) WinPcap-4-1-3.exe安装包WpdPack-4-1-2.zip开发依赖包资源-CSDN文库)
把这些文件拷贝到你的开发工程里。基本就是库的集成方式。注意在预编译头里添加这几项: _CRT_SECURE_NO_WARNINGS
_WINSOCK_DEPRECATED_NO_WARNINGS
WPCAP
HAVE_REMOTE
2、获取网卡信息
int main()
{
pcap_if_t *alldevs;
pcap_if_t *d;
char errbuf[PCAP_ERRBUF_SIZE+1];
/* Retrieve the device list */
if(pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}
/* Scan the list printing every entry */
for(d=alldevs;d;d=d->next)
{
ifprint(d);
}
/* Free the device list */
pcap_freealldevs(alldevs);
return 1;
}
void ifprint(pcap_if_t *d)
{
pcap_addr_t *a;
char ip6str[128];
/* Name */
printf("%s\n",d->name);
/* Description */
if (d->description)
printf("\tDescription: %s\n",d->description);
/* Loopback Address*/
printf("\tLoopback: %s\n",(d->flags & PCAP_IF_LOOPBACK)?"yes":"no");
/* IP addresses */
for(a=d->addresses;a;a=a->next) {
printf("\tAddress Family: #%d\n",a->addr->sa_family);
switch(a->addr->sa_family)
{
case AF_INET:
printf("\tAddress Family Name: AF_INET\n");
if (a->addr)
printf("\tAddress: %s\n",iptos(((struct sockaddr_in *)a->addr)->sin_addr.s_addr));
if (a->netmask)
printf("\tNetmask: %s\n",iptos(((struct sockaddr_in *)a->netmask)->sin_addr.s_addr));
if (a->broadaddr)
printf("\tBroadcast Address: %s\n",iptos(((struct sockaddr_in *)a->broadaddr)->sin_addr.s_addr));
if (a->dstaddr)
printf("\tDestination Address: %s\n",iptos(((struct sockaddr_in *)a->dstaddr)->sin_addr.s_addr));
break;
case AF_INET6:
printf("\tAddress Family Name: AF_INET6\n");
#ifndef __MINGW32__ /* Cygnus doesn't have IPv6 */
if (a->addr)
printf("\tAddress: %s\n", ip6tos(a->addr, ip6str, sizeof(ip6str)));
#endif
break;
default:
printf("\tAddress Family Name: Unknown\n");
break;
}
}
printf("\n");
}
3、捕捉包
int main()
{
pcap_if_t* alldevs; //适配器列表,它是一个链表的数据结构
pcap_if_t* d; //保存某个适配器
pcap_t* fp;
int res;
struct pcap_pkthdr* header;
const u_char* pkt_data;
time_t local_tv_sec;
struct tm* ltime;
char timestr[16];
int count = 1;
int i = 0, inum;
char errbuf[PCAP_ERRBUF_SIZE];
u_int netmask;
struct bpf_program bpf_filter; //BPF过滤规则
char bpf_filter_string[] = "icmp"; //过滤规则字符串,只分析IPv4的数据包 ip ; port 443;icmp
printf("===============Adapter List===============\n");
//获取本地设备列表
if (pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}
//输出列表
for (d = alldevs; d != NULL; d = d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No description available)\n");
printf("\tLoopback: %s\n", (d->flags & PCAP_IF_LOOPBACK) ? "yes" : "no");
}
if (i == 0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return -1;
}
//获取选择编号
while (1)
{
printf("\nEnter the interface number (1-%d): ", i);
scanf("%d", &inum);
if (inum > 0 && inum <= i)
break;
}
//跳到用户选择的适配器
for (d = alldevs, i = 0; i < inum - 1; ++i, d = d->next);
//打开适配器
if ((fp = pcap_open_live(d->name, 65536, PCAP_OPENFLAG_PROMISCUOUS, 1000, errbuf)) == NULL)
{
fprintf(stderr, "\nError openning adapter: %s\n", errbuf);
pcap_freealldevs(alldevs);
return -1;
}
//检查链路层的类型 只看以太网
if (pcap_datalink(fp) != DLT_EN10MB)
{
fprintf(stderr, "This program only run on Ethernet networks\n");
pcap_close(fp);
pcap_freealldevs(alldevs);
return -1;
}
// 设置过滤规则
if (d->addresses != nullptr) {
netmask = ((struct sockaddr_in*)(d->addresses->netmask))->sin_addr.S_un.S_addr;
}
else {
/* If the interface is without addresses we suppose to be in a C class network */
netmask = 0xffffff;
}
pcap_compile(fp, &bpf_filter, bpf_filter_string, 0, netmask); //编译过滤规则
pcap_setfilter(fp, &bpf_filter);//设置过滤规则
printf("The program is working......\n");
printf("The capture file is saving as 'data.txt'\n");
printf("You can input 'ctrl + C' to stop the program\n");
//if ((file = freopen("data.txt", "w", stdout)) == 0)
// printf("Cannot open the file.\n");
while ((res = pcap_next_ex(fp, &header, &pkt_data)) >= 0)
{
//超时
if (res == 0)
continue;
//将时间戳转化为可识别格式
local_tv_sec = header->ts.tv_sec;
ltime = localtime(&local_tv_sec);
strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);
//输出编号、时间戳和包长度
printf("==============================================================================\n");
printf("No.%d\ttime: %s\tlen: %ld\n", count++, timestr, header->len);
printf("==============================================================================\n");
char temp[LINE_LEN + 1];
//输出包
for (i = 0; i < header->caplen; ++i)
{
printf("%.2x ", pkt_data[i]);
if (isgraph(pkt_data[i]) || pkt_data[i] == ' ')
temp[i % LINE_LEN] = pkt_data[i];
else
temp[i % LINE_LEN] = '.';
if (i % LINE_LEN == 15)
{
temp[16] = '\0';
printf(" ");
printf("%s", temp);
printf("\n");
memset(temp, 0, LINE_LEN);
}
}
printf("\n");
//分析数据包
ethernet_protocol_packet_handle(NULL, header, pkt_data);
}
if (res == -1)
{
printf("Error reading the packets: %s\n", pcap_geterr(fp));
pcap_close(fp);
pcap_freealldevs(alldevs);
fclose(stdin);
if (file)
fclose(file);
return -1;
}
//释放
pcap_close(fp);
pcap_freealldevs(alldevs);
fclose(stdin);
if (file)
fclose(file);
return 0;
}
分析数据包就是解析数据,去掉以太网头(14个字节)和 网络层ip头 以及传输层的tcp或者udp 头,如果是icmp去掉icmp头,即可解析出来应用层数据。