现在的位置: 首页 > 综合 > 正文

winpcap一些开发实践

2013年02月09日 ⁄ 综合 ⁄ 共 5694字 ⁄ 字号 评论关闭

这次研究本意是编写一个用于局域网检测IP地址与MAC对应关系的Application,以获知局域网里IP地址的分配情况,在局域网的IP冲突中找到可用的IP地址。

设计与构思

当确定Winpcap库可以满足操纵ARP后就大胆的展开程序了,首先是对界面进行简单的构思,其中必须把界面和功能函数的关系分离好。

程序架构

1 程序基本结构表

模块

说明

UI Class

对话框类class CIPFluxDlg : public CDialog显示信息,接受用户命令

CWinPcap Class

自定义的WinPcap封装类,包括了所有功能函数

WinPcap Library

实现ARP分析功能底层库

主要的调用关系是从上到下,不过WinPcap中使用了回调机制,CWinPcap类的Sniffer函数也使用一个曲折的获得CIPFluxDlg指针的特殊技巧,当然这并非唯一的途径,也许读者会有更好的方法。

界面构思

界面采用对话框结构,包括

本地信息,包括IP地址,MAC编码;

IP地址搜索范围,当然起始IP地址、终止IP地址,都在内部计算过了,而且可以编辑;

搜索按钮;

搜索结果列表,显示搜索到IP-MAC信息。

点击搜索按钮后,后台线程开始搜索并把结果按先后顺序显示在列表中。程序界面如:1 程序界面所示。

1 程序界面

该程序定位为简易的搜索工具,所以界面很简洁,主要的工作还是在于IP搜索功能的实现。

监听模式

一个监听者线程,多个发送者线程。搜索时针对每个IP地址查询创建独立的线程,发完ARP包就完事,其余的工作就让监听者线程完成。通常我们只需要一个监听者线程就可以,这个线程建立后不用退出,直到程序退出。

WinPcap的封装

WinPcap这个库非常专业,有很多网络方面的工具,特别是Sniffer类型工具,如:SnifferProEffectHttpSniffer。它是一个跨平台的库,功能接口比较多,IPFlux用不了那么多,如果直接在对话框类中直接调用的话,会使得代码交错,不便于阅读和修改,所以最好进行封装,这样可以与界面无关,如果以后在别的应用用到的话,将会非常方便。

通过分析,决定划分一些具体的子功能来实现的:

*         1 网卡的枚举,设置和缓冲区分配;

*         2 填充和发送一个ARP包;

*         3 监听线程(Sniffer)

*         4 解析ARP包;

*         5 获得本机IP MAC地址;

*         6 发送线程和接受线程的同步与协调;

这些子功能主要使用WinPcap库函数,笔者封装了一个CWinPcap类。以下分别对子功能的代码作详细说明,其它请参考所附源代码。关于封装类的函数请看2 CWinPcap的主要函数表

2 CWinPcap的主要函数表

函数

描述

int CWinPcap::OpenPcap()

打开Pcap

void CWinPcap::ClosePcap()

关闭Pcap

void CWinPcap::GetData(LPPACKET lp)

分析ARP

void CWinPcap::GetNetInfo(sockaddr_in &sin, CString &strMAC, BYTE mac[])

获取本机信息

int CWinPcap::Sender(ULONG ipCur, ULONG ipMine, BYTE mac[])

发送者函数

int CWinPcap::Sniff()

监听者函数

1 网卡的枚举,设置和缓冲区分配

// success return 0

int CWinPcap::OpenPcap()

{

       WCHAR buf[1024];

       ULONG bufsize;

       int res, i = 0;

       memset ((void*)adapterlist, 0, sizeof(adapterlist));

       res = PacketGetAdapterNames ((char*)buf, &bufsize); // 枚举网卡

       if (res == 0)

       {

              return -1;

       }

       WCHAR *p1, *p2;

       p1 = p2 = buf;

       while ((*p1 != '/0') || (*(p1 - 1) != '/0'))

       {

              if (*p1 == '/0')

              {

                     memcpy (adapterlist[i], p2, 2 * (p1 - p2));

                     p2 = p1 + 1;

                     i++;

              }

              p1++;

       }

       m_iAdapterNum = i;

       m_iSelAdapter = i - 1;

       // 打开最后一个网卡,PC机一般是一个网卡

       m_lpAdapter = PacketOpenAdapter( ((char *)adapterlist + m_iSelAdapter * 1024) );

       if (m_lpAdapter == NULL || (m_lpAdapter->hFile == INVALID_HANDLE_VALUE))

       {

              return -1;

       }

       // 分配sender包空间

       m_lpPacketSender = PacketAllocatePacket();

       if (m_lpPacketSender == NULL)

              return -1;

       return 0;

}

 

2 如何填充和发送一个ARP

2.1 包的分组格式定义

//////////////////////////////////////////////////////////////////////////

// arp.h 定义以太网arp帧结构,封装了winpcap开发包的重要函数

#include <packet32.h>

#include <ntddndis.h>

#include <stdio.h>

#include <conio.h>

#include "afx.h"

#pragma comment(lib,"ws2_32")

#pragma comment(lib,"packet")

#pragma once

#pragma pack(push,1)

// Ether(DLC) header

typedef struct ethdr

{

  unsigned char   eh_dst[6];       // 以太网目的MAC地址

  unsigned char   eh_src[6];       // 以太网源MAC地址

  unsigned short  eh_type;   // 帧类型

}ETHDR,*PETHDR;

// ARP分组格式

typedef struct arphdr

{

  unsigned short  arp_hdr;   // 硬件类型

  unsigned short  arp_pro;   // 协议类型

  unsigned char   arp_hln;  // 硬件地址长度

  unsigned char   arp_pln;  // 协议地址长度

  unsigned short  arp_opt;   // ARP/RARP

  unsigned char   arp_sha[6];     // 发送者地址

  unsigned long   arp_spa;  // 发送者IP地址

  unsigned char   arp_tha[6];     // 接受者地址

  unsigned long   arp_tpa;  // 接受者IP地址

}ARPHDR,*PARPHDR;

#pragma pack(push)

#define ETH_IP                          0x0800

#define ETH_ARP                      0x0806

#define ARP_REQUEST             0x0001

#define ARP_REPLY                  0x0002

#define ARP_HARDWARE         0x0001

#define max_num_adapter            10

class CWinPcap

{

public:

       /* 分析arp包内容*/

       void GetData(LPPACKET lp);

       /* 发送arp请求包*/

       int          Sender(ULONG ipCur, ULONG ipMine, BYTE mac[]);

       /* 监听网卡收到的包*/

       int          Sniff();

       /* 获得网卡信息,IP地址和MAC地址*/

       void GetNetInfo(sockaddr_in &sin, CString &strMAC, BYTE mac[]);

       CWinPcap()

       {

              OpenPcap();

       };

       ~CWinPcap()

       {

              ClosePcap();

       };

       /* 打开网卡并初始化*/

       int          OpenPcap();

       /* 关闭网卡*/

       void ClosePcap();

      

private:

       char        adapterlist[max_num_adapter][1024];  

       /* 网卡*/

       LPADAPTER m_lpAdapter;

       /* 帧缓冲*/

       LPPACKET    m_lpPacketReceiver,

                            m_lpPacketSender;

       int                 m_iAdapterNum;

       int                 m_iSelAdapter;

       npf_if_addr    m_netinfo;

};

2.2 填充和发送一个ARP请求包

/********************************************************************

功能       供发送线程调用,发送一个arp请求包

参数

       ipCur      目标IP地址

       ipMine    本机IP地址

       mac[]      网卡MAC地址                                                                       

********************************************************************/

int CWinPcap::Sender(ULONG ipCur, ULONG ipMine, BYTE mac[])

{

       char              pBufSend[1024];

       ETHDR          eth;

       ARPHDR       arp;

       // Fill the ARP request packets.

       int i;

       for (i = 0; i < 6; i++)

       {

              eth.eh_dst[i] = 0xff;

              arp.arp_tha[i] = 0x00;

       }

       // {填充DLC头和ARP

       eth.eh_type     = htons(ETH_ARP);

       memcpy(eth.eh_src, mac, 6);

      

       arp.arp_hdr     = htons(ARP_HARDWARE);

       arp.arp_pro     = htons(ETH_IP);

       arp.arp_hln     = 6;

       arp.arp_pln     = 4;

       arp.arp_opt     = htons(ARP_REQUEST);

      

       memcpy(arp.arp_sha, mac, 6);

       arp.arp_spa     = htonl(ipMine);

       arp.arp_tpa = htonl(ipCur);

       // }

      

       memset(pBufSend, 0, sizeof(pBufSend));

       memcpy(pBufSend, &eth, sizeof(eth));

       // 装配完整arp

       memcpy(pBufSend + sizeof(eth), &arp, sizeof(arp));

      

       PacketInitPacket(m_lpPacketSender, pBufSend, sizeof(eth) + sizeof(arp));

       if(PacketSendPacket(m_lpAdapter, m_lpPacketSender,TRUE)==FALSE)

       {

              return -1;

       }

       return 0;

}

3 监听线程(Sniffer)

线程函数体实际执行的函数:

int CWinPcap::Sniff()

{

       static CIPFluxDlg *pdlg = (CIPFluxDlg *)AfxGetMainWnd();

  char recvbuf[1024*250];

       DWORD res = 0;

      

       // {初始化网卡,设置为混合模式NDIS_PACKET

【上篇】
【下篇】

抱歉!评论已关闭.