WinPcap编程(二)

0.

这一次具体讲抓包的两种方法。

不过说明之前得知道几点:

  第一,无线网卡的包需要特定网卡驱动才能抓到。

  第二,抓以太网上的包的时候,需要禁用无线网卡。(这一点没搞清楚原因,有了解的希望能给个答案,解个惑。感谢。回学校了问问老师。)

  第三,(建议)清除ARP表,最好自己写个批处理命令。快一点。

1.0 抓包步骤

步骤很简单:先打开适配器列表 --> 选择适配器 --> 通过遍历链表的方式到达你选择的适配器位置 --> 打开设备 --> 开始抓包。

每一个步骤都是一个函数。了解步骤,后面就好办。

首先,了解一个数据类型pcap/pcap_t。它代表一个打开的设备,理解成它就是适配器就行(实际上是这个适配器的描述符)。这个结构体对用户来说是不透明的,它通过wpcap.dll提供的函数,维护了它的内容。

然后,跳转至你的设备在WinPcap编程(一)中存在,就不多说。

之后,打开设备的函数:

pcap_t* pcap_open  ( 

  const char *  source,  

  int  snaplen,  

  int  flags,  

  int  read_timeout,  

  struct pcap_rmtauth *  auth,  

  char *  errbuf   

 ) 

备注:

  第一个参数:source是我们所要打开的设备。当我们获取所有的设备之后,这个source就是d->name。不能为NULL。

  第二个参数:snaplen是我们抓取的数据包的大小,100就是抓取整个数据包的前100B,65536最大,把整个包都包括了。

  第三个参数:flags设置为混杂模式(PCAP_OPENFLAG_PROMISCUOUS ),即不管数据包是否给我,我都去捕获。这样可以捕捉局域网内所有的包。

  第四个参数: read_timeout设置超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取数据操作的时候,不管网络上有没有包,都会在 read_timeout这个时间内响应。

                设置为0意味着没有超时。没有数据包到达,读操作将永远不会返回。

                设置成-1,无论有没有数据包到达,读操作都会立即返回。

  第五个参数:auth远程机登录信息,若本地则为NULL;

  第六个参数:errbuf 出错信息。

1.1抓包的两种方式:

  1.1.1 回调函数的方法进行抓包

  每次抓到包就用回调函数进行处理。处理完接着抓包(如果设置了num的话)。

  抓包函数:

int pcap_loop  ( 

  pcap_t *  p,  

  int  cnt,  

  pcap_handler  callback,  

  u_char *  user   

 ) 

备注0:  

  第一个参数:P即是打开的设备。

  第二个参数:cnt表示捕捉个数。

  第三个参数:回调函数指针。对捕捉的数据包进行操作 。

  第四个参数:用户信息,一般为NULL。

函数返回值:

  返回值为0,捕捉了cnt次,正常返回。

  返回值为-1,发生错误。

  返回值为-2,使用pcap_breakloop()结束循环。

  回调函数抓包还有一种方法, pcap_dispatch() 。

  区别是: pcap_ dispatch() 当超时时间到了(timeout expires)就返回 (尽管不能保证) ,而 pcap_loop() 不会因此而返回,只有当 cnt 数据包被捕获。

      所以,pcap_loop()会在一小段时间内,阻塞网络的利用。pcap_dispatch() 函数一般用于比较复杂的程序中。

  1.1.2 不利用回调函数

  回调函数比较好,但是有时候需要不用回调函数的方法,特别在多线程里面。

  我用C# + WPF写的,用回调函数的话一直出现错误。仔细考虑,应该是托管与非托管的问题。看了SharpPcap的源码,貌似也没有好的解决方案。

  【有好的方案的看见了希望能够提供一下,感谢。】

  

  不利用回调函数抓包的话,方法比较简单,每次只能抓一个包,加个while()循环就OK了。

  

int pcap_next_ex  (
  pcap_t *  p,
  struct pcap_pkthdr **  pkt_header,
  const u_char **  pkt_data
 ) 

备注: 

  第一个参数:打开的设备。

  第二个参数:WinPcap添加的一些信息。(时间戳,捕捉到包的长度,实际包的长度)。

  第三个参数:实际数据包。

函数返回值:

  返回值为1,正常返回;

  返回值为0,超时后返回一个无效的包;

  返回值为-1,发生错误;

  返回值为-2,读到EOF。

源码:

#define WIN32
#include "pcap.h"
#include "winsock.h"
#include "time.h"

/* 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];

    /* 获取本机设备列表 */
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }

    /* 打印列表 */
    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_s("%d", &inum);

    if (inum < 1 || inum > i)
    {
        printf("\nInterface number out of range.\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }

    /* 跳转到选中的适配器 */
    for (d = alldevs, i = 0; i< inum - 1; d = d->next, i++);

    /* 打开设备 */
    if ((adhandle = pcap_open(d->name,          // 设备名
        65536,            // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
        PCAP_OPENFLAG_PROMISCUOUS,    // 混杂模式
        0,             // 读取超时时间
        NULL,             // 远程机器验证
        errbuf            // 错误缓冲池
        )) == NULL)
    {
        fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }

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

    /* 释放设备列表 */
    pcap_freealldevs(alldevs);

    /* 开始捕获 */
    pcap_loop(adhandle, 30, packet_handler, NULL);

    system("pause");
    return 0;
}

/* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
    struct tm ltime = {0};
    char timestr[16];
    time_t local_tv_sec;

    /* 将时间戳转换成可识别的格式 */
    local_tv_sec = header->ts.tv_sec;
    localtime_s(&ltime, &local_tv_sec);
    strftime(timestr, sizeof timestr, "%H:%M:%S", &ltime);

    printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
}

回调函数

#define WIN32
#include "pcap.h"

int main()
{
    pcap_if_t *alldevs;
    pcap_if_t *d;
    int inum;
    int i = 0;
    pcap_t *adhandle;
    int res;
    char errbuf[PCAP_ERRBUF_SIZE];
    struct tm ltime;
    char timestr[16];
    struct pcap_pkthdr *header;
    const u_char *pkt_data;
    time_t local_tv_sec;

    int j = 10;//限制只抓10个包

    /* 获取本机设备列表 */
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
    {
        fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
        exit(1);
    }

    /* 打印列表 */
    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_s("%d", &inum);

    if (inum < 1 || inum > i)
    {
        printf("\nInterface number out of range.\n");
        /* 释放设备列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }

    /* 跳转到已选中的适配器 */
    for (d = alldevs, i = 0; i< inum - 1; d = d->next, i++);

    /* 打开设备 */
    if ((adhandle = pcap_open(d->name,          // 设备名
        65536,            // 要捕捉的数据包的部分
        // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
        PCAP_OPENFLAG_PROMISCUOUS,    // 混杂模式
        1000,             // 读取超时时间
        NULL,             // 远程机器验证
        errbuf            // 错误缓冲池
        )) == NULL)
    {
        fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
        /* 释放设列表 */
        pcap_freealldevs(alldevs);
        return -1;
    }

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

    /* 释放设备列表 */
    pcap_freealldevs(alldevs);

    /* 获取数据包 */
    while ((res = pcap_next_ex(adhandle, &header, &pkt_data)) >= 0&&j>0){

        if (res == 0)
            /* 超时时间到 */
            continue;

        /* 将时间戳转换成可识别的格式 */
        local_tv_sec = header->ts.tv_sec;
        localtime_s(&ltime,&local_tv_sec);
        strftime(timestr, sizeof timestr, "%H:%M:%S", &ltime);
        j--;

        printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
    }

    if (res == -1){
        printf("Error reading the packets: %s\n", pcap_geterr(adhandle));
        return -1;
    }
    system("pause");
    return 0;
}

非回调函数

时间: 2024-10-16 00:34:15

WinPcap编程(二)的相关文章

PL/SQL 编程(二)

1    For循环 语法:begin for i in reverse 1..10 loop insert into users values(i,'奥巴马'): end loop: end; 注意:循环变量 i 是隐含增加的,所以无法看到 2    goto语句 goto 语句用于跳转到特定的位置去执行语句.由于goto语句会减少程序的可读性,所以一般情况下 不建议使用goto语句 3    null语句 null语句不会执行任何操作,但是可以增加程序的可读性 4    创建返回值是一个结果

Win32 Windows编程 二

一.第一个窗口程序 1  入口函数 WinMain 2  窗口处理函数 LRESULT CALLBACK WndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam ) { return DefWindowProc( hWnd, nMsg, wParam, lParam ); } 当窗口处理消息事件时 调用该函数 LRESULT CALLBACK WndProc( HWND hWnd, UINT nMsg, WPARAM wParam,

Linux网络编程(二)

服务套和客户机的信息函数 1.字节转换函数 在网络上面有着许多类型的机器,这些机器在表示数据的字节顺序是不同的, 比如i386芯片是低字节在内存地址的低端,高字节在高端,而alpha芯片却相反. 为了统一起来,在Linux下面,有专门的字节转换函数. unsigned long int htonl(unsigned long int hostlong)     unsigned short int htons(unisgned short int hostshort)     unsigned

js面对对象编程(二):属性和闭包

上篇博客中讲解了一些js对象的基本概念和用法,这篇博客讲解一下js属性方面的:公有属性,私有属性,特权方法. 如果学过java,公有属性,私有属性,特权方法(即可以访问和设置私有属性的方法)一定很熟悉,那么让我们来看看在js里如何实现呢? 1.公有属性 首先看公有的第一层意思是可以被大家所访问的,对外开放的属性,是相对于私有属性而言的: function Person(name,age){ this.name=name; this.age=age; this.getName=function()

winform网络编程(二)

mnesia在频繁操作数据的过程可能会报错:** WARNING ** Mnesia is overloaded: {dump_log, write_threshold},可以看出,mnesia应该是过载了.这个警告在mnesia dump操作会发生这个问题,表类型为disc_only_copies .disc_copies都可能会发生. 如何重现这个问题,例子的场景是多个进程同时在不断地mnesia:dirty_write/2 mnesia过载分析 1.抛出警告是在mnesia 增加dump

Java 图形编程 二:布局管理器之顺序布局

package second; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class Layout { private Frame f; private Button b1,b2,b3,b4,b5,b6,b7,b8,b9,b10;//声明10个button //构造函数,实例化button 1 - 10 Layout(){ b1 = new B

java基础学习总结——GUI编程(二)

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! java基础学习总结——GUI编程(二) 一.事件监听 测试代码一: 1 package cn.javastudy.summary; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 6 public class TestActionEvent { 7 public static void main(String args[]) { 8 Frame f = new Frame("

安卓第八天笔记--网络编程二

安卓第八天笔记--网络编程二 1.网络图片查看器 /** * 网络图片查看器 * 1.获取输入的URL地址,判断是否为空 * 2.建立子线程,获取URl对象new URL(path) * 3.打开连接获取HttpURLConnection conn = (HttpURLConnection) url.openConnection(); * 4.设置连接超时时间conn.setConnectionTimeOut(5000)毫秒 * 5.设置请求方式setRequestMethod * GET或者P

Java 图形编程 二:布局管理器之边界布局

package second; import java.awt.*; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; public class BorderLayoutDemo { private Frame fb; private Button b1,b2,b3,b4,b5;//声明5个button //构造函数,实例化button 1 - 5 BorderLayoutDemo(){ b1 = ne