您的位置:首页 > 编程语言 > C语言/C++

NetAnalyzer笔记 之 三. 用C++做一个抓包程序

2015-08-29 21:04 369 查看
[创建时间:2015-08-27 22:15:17]

NetAnalyzer下载地址

经过前两篇的瞎扯,你是不是已经厌倦了呢,那么这篇让我们来点有意思的吧,什么,用C#。不,这篇我们先来C++的

Winpcap开发环境配置

完成了对Winpcap的介绍,什么,你没看到Winpcap的介绍,左转,百度(其实,真的是不想复制)。我们就需要做一点有用的事情,比如写一个简单的数据采集工具。当然在此之前,我们需要配置Winpcap的开发环境。

(1) 运行环境设置

Win32 平台下Winpcap应用程序需要以下四个动态链接库才能正常运行:wpcap.dll Packet.dll WanPacket.dll pthreadVC.dll,这四个动态链接库在WinPcap驱动程序里。如果没有这个驱动程序,需要到WinPcap官方网站上下载,下载地址为:www.WinPcap.org。下载完后点击安装即可完成Winpcap核心驱动的配置,即计算机有了最初的运行基础。

(2) 开发环境的配置

本次开发使用的组件是由Winpcap官方提供的SDK,从http://www.WinPcap.org 下载Winpcap 的SDK ——WpdPack,

也可以直接在这里下载:http://pan.baidu.com/s/1hqKtsOk

当然在此之前你的爱机一定要装Winpcap

WinPcap SDk里面包含库文件,头文件,文档文件和一些例子。解压到一个指定的目录。



图1 WpdPack

其中的docs为E文,如果E实在不行 点这里http://pan.baidu.com/s/1hqHB6Pu

示例我已经在Visual Studio 2010 下成功过通过

接下来我们看看如何配置

解压缩后把Include目录与Lib目录添加到开发环境中。VC6.0环境如图2所示,

VC6.0: 菜单栏Tools->Option->Directory,在下拉列表Show directories for:中选择Include files对话框中配置Include目录,我将该文件放在D:\WPDPACK\INCLUDE文件夹下。同样改变Show directories for:选中Library files配置Lib目录。



图2 VC6.0环境下Include目录与Lib目录配置

VS2010环境如图3所示,

Visual Studio 2010: 菜单栏 工具->选项->项目和解决方案/项目->VC++目录。有时候可能出现“目录编辑功能被否决”的情况,可以通过 菜单栏 项目->XXX属性->配置属性->VC++目录 中配置,本次使用的就是这种方式。



图3 Visual Studio 2010环境下Include目录与Lib目录配置

通过上面的方式完成了文件引入部分的配置,接下来是导入库文件的方法,在VC6.0中也叫做链接库的配置。

VC6.0:菜单栏Project->Settings->Link->Object/library modules,在其中添加wpcap.lib,Packet.lib,点击OK即可。如图4



图4 VC6.0环境下链接库文件配置

Visual Studio 2010:右击 解决方案资源管理器->添加->现有项…导入Packet.lib和wpacp.lib库文件即可。如图5



图5 .Net中导入的库文件

完成了库文件的配置,现在开始本次配置的最后一步:

新的版本里WinPcap支持远程数据包获取,所以还应当添加一个头文件remote-ext.h ,即#include "remote-ext.h"(记住这条语句要放在#include”pcap.h”之后,否则会出错!)

否则会发生下面的错误:

error C2065: “PCAP_SRC_IF_STRING”: 未声明的标识符

error C3861: “pcap_findalldevs_ex”: 找不到标识符

error C2065: “PCAP_OPENFLAG_PROMISCUOUS”: 未声明的标识符

error C3861: “pcap_open”: 找不到标识符

虽然上面提供了一些解决方法,但似乎没什么作用,于是需要在开发环境中进行配置,因为VC6.0在Windows7下兼容问题,尝试多次未成功运行,所以此处只给出Visual Studio 2010中的配置结果

或者不用添加#include "remote-ext.h".在VC.NET提供的IDE环境中,可以通过执行“项目”菜单中的的“属性”进入该项目的属性配置页,通过选择“配置属性”树中的“C/C++预处理库”选项就增加”WPCAP”和”HAVE_REMOTE”两个标号,。如图6所示



图6 Visual Studio 2010环境下WPCAP与HAVE_REMOTE标号的配置

Winpcap编程实现

通过上面的配置完成了对Winpcap开发环境搭建,接下来我们就可以做一个小程序了。在做抓包程序之前,我们首先要确定从何处采集数据,大家首先想到的就是网卡,所以,第一步就是要获取网卡:

步骤:

(a) 打开Visual Studio 2010 新建项目,语言选择C++,类型选择Win32控制台应用程序,点击确定按钮。



图7 新建项目

(b) 进入想到页面,直接点击下一步,进入第二个页面,因为我们要使用C语言来制作该程序,但是在Visual Studio 2010并没用提供专用的C开发环境,所以上一步中我们选择的是C++模板,因此在接下来的页面,我们要在附加选项中选择“空项目”,这样开发环境就不会导入一些关于C++的东西了,点击完成。



图8 选择空项目

(c) 于是开发环境为我们配置了一个空项目,在解决方案资源管理器中选择源文件,右击在菜单中添加选择新建项,此时打开新建项对话框,找到C++文件,输入文件名称,注意,同时键入扩展名“.c,这样就可以建立一个C源文件,



图9 配置方案结果

(d) 然后按照上面的环境配置方案,对项目中Include目录lib目录进行配置;导入库文件;配置标识号;最后写入如下代码:

#include "pcap.h"
main()
{
pcap_if_t *alldevs;
pcap_if_t *d;
int i=0;
char errbuf[PCAP_ERRBUF_SIZE];

/* 获取本地机器设备列表 */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* auth is not needed */, &alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs_ex: %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");
}

if (i == 0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return;
}
/* 不再需要设备列表了,释放它 */
pcap_freealldevs(alldevs);
}


该段代码来自Winpcap开发文档中的第一节,主要的任务是获取主机网卡设备。

(e) 最后按Ctrl+F5启动,开始编译执行该段。

结果:如图10

这里的编程实现仅仅是为了演示,Winpcap的开发配置过程,更多更为详尽的实现过程请参阅Winpcap开发文档,从Winpcap官网即可获得,所以之后的一些实现原理并不是本书的重点,故不再赘述。



图10 获取网卡执行结果

如果只是,获取到网卡,那也没什么好讲的了,下面是一段示例中的代码,在这段代码中我们不但可以取到网卡,还可以进行数据分析

#ifdef _MSC_VER
/*
* we do not want the warnings about the old deprecated and unsecure CRT functions
* since these examples can be compiled under *nix as well
*/
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "pcap.h"

/* 4 bytes IP address */
typedef struct ip_address
{
u_char byte1;
u_char byte2;
u_char byte3;
u_char byte4;
}ip_address;

/* IPv4 header */
typedef struct ip_header
{
u_char    ver_ihl;        // Version (4 bits) + Internet header length (4 bits)
u_char    tos;            // Type of service
u_short tlen;            // Total length
u_short identification; // Identification
u_short flags_fo;        // Flags (3 bits) + Fragment offset (13 bits)
u_char    ttl;            // Time to live
u_char    proto;            // Protocol
u_short crc;            // Header checksum
ip_address    saddr;        // Source address
ip_address    daddr;        // Destination address
u_int    op_pad;            // Option + Padding
}ip_header;

/* UDP header*/
typedef struct udp_header
{
u_short sport;            // Source port
u_short dport;            // Destination port
u_short len;            // Datagram length
u_short crc;            // Checksum
}udp_header;

/* prototype of the packet handler */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);

int main()
{
pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i=0;
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
u_int netmask;
char packet_filter[] = "ip and udp";
struct bpf_program fcode;

/* Retrieve the device list */
if(pcap_findalldevs(&alldevs, errbuf) == -1)
{
fprintf(stderr,"Error in pcap_findalldevs: %s\n", errbuf);
exit(1);
}

/* Print the list */
for(d=alldevs; d; d=d->next)
{
printf("%d. %s", ++i, d->name);
if (d->description)
printf(" (%s)\n", d->description);
else
printf(" (No description available)\n");
}

if(i==0)
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return -1;
}

printf("Enter the interface number (1-%d):",i);
scanf("%d", &inum);

/* Check if the user specified a valid adapter */
if(inum < 1 || inum > i)
{
printf("\nAdapter number out of range.\n");

/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}

/* Jump to the selected adapter */
for(d=alldevs, i=0; i< inum-1 ;d=d->next, i++);

/* Open the adapter */
if ((adhandle= pcap_open_live(d->name,    // name of the device
65536,            // portion of the packet to capture.
// 65536 grants that the whole packet will be captured on all the MACs.
1,                // promiscuous mode (nonzero means promiscuous)
1000,            // read timeout
errbuf            // error buffer
)) == NULL)
{
fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}

/* Check the link layer. We support only Ethernet for simplicity. */
if(pcap_datalink(adhandle) != DLT_EN10MB)
{
fprintf(stderr,"\nThis program works only on Ethernet networks.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}

if(d->addresses != NULL)
/* Retrieve the mask of the first address of the interface */
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;

//compile the filter
if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
{
fprintf(stderr,"\nUnable to compile the packet filter. Check the syntax.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}

//set the filter
if (pcap_setfilter(adhandle, &fcode)<0)
{
fprintf(stderr,"\nError setting the filter.\n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}

printf("\nlistening on %s...\n", d->description);

/* At this point, we don't need any more the device list. Free it */
pcap_freealldevs(alldevs);

/* start the capture */
pcap_loop(adhandle, 0, packet_handler, NULL);

return 0;
}

/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
struct tm *ltime;
char timestr[16];
ip_header *ih;
udp_header *uh;
u_int ip_len;
u_short sport,dport;
time_t local_tv_sec;

/*
* unused parameter
*/
(VOID)(param);

/* convert the timestamp to readable format */
local_tv_sec = header->ts.tv_sec;
ltime=localtime(&local_tv_sec);
strftime( timestr, sizeof timestr, "%H:%M:%S", ltime);

/* print timestamp and length of the packet */
printf("%s.%.6d len:%d ", timestr, header->ts.tv_usec, header->len);

/* retireve the position of the ip header */
ih = (ip_header *) (pkt_data +
14); //length of ethernet header

/* retireve the position of the udp header */
ip_len = (ih->ver_ihl & 0xf) * 4;
uh = (udp_header *) ((u_char*)ih + ip_len);

/* convert from network byte order to host byte order */
sport = ntohs( uh->sport );
dport = ntohs( uh->dport );

/* print ip addresses and udp ports */
printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
ih->saddr.byte1,
ih->saddr.byte2,
ih->saddr.byte3,
ih->saddr.byte4,
sport,
ih->daddr.byte1,
ih->daddr.byte2,
ih->daddr.byte3,
ih->daddr.byte4,
dport);
}


在代码中只获取了UDP数据包,具体代码如下

char packet_filter[] = "ip and udp";

执行结果:



图11 UDP数据包分析结果
为了操作方便我把其他网卡禁用了

在Winpcap 的开发包中还有其他示例额,自己慢慢去看吧

本篇有卖弄嫌疑,但是也是让大家深入理解Winpcap的具体开发方法,在下一篇我们就一起使用winpcap抓包吧 ^_^

NetAnalyzer下载地址

NetAnalzyer交流群:39753670 (PS 只提供交流平台,群主基本不说话^_^)

[转载请保留作者信息 作者:冯天文 网址:/article/5261201.html]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: