WinPcap是一个开源的网络抓包模块,顾名思义,它只能工作在Windows下,但本文介绍的知识并不局限于任何操作系统和开发语言,因为网络协议本身是没有这些区别的。阅读本指南之前,请先下载WinPcap安装到自己的电脑上,目前WinPcap的最新版本是4.1.3,支持基于NT核心的所有操作系统(从NT4一直到Win8),读者可以从官方网站http://www.winpcap.org/install/default.htm下载。
网络其实是分层设计的,一个应用层的网络程序对外通信的时候,大概的流程如下:应用程序(调用WinSock)<--->SPI层<--->TDI层<--->NDIS层<--->MiniPort层<-->物理网卡,实际上,应用程序可以直接调用上面的某一层,例如:直接调用TDI驱动收发网络包;又例如,一般软件防火墙工作于NDIS层,如果你直接使用MiniPort HOOK进行收发数据,那么防火墙是完全不知情的,也不会有任何提示。应用层抓包的话,方法有很多:例如本机的可以使用API HOOK,截取WinSock的调用;或者直接安装一个SPI的HOOK;又或者直接使用RawSocket接口。WinPcap的驱动实际上位于NDIS层,属于NDIS过滤驱动,也就是说,它只是抓包,但不像软件防火墙的passthru模型还可以拦截。另外,它还可以发送数据包。注意:笔者曾经将它翻译为Delphi,发现在以太网环境下(例如局域网)它是通过自己的驱动发布,而对于拨号上网的PPPOE连接,它调用的是微软的NetMon接口,也就是说,如果你的电脑没有安装NetMon,那么在拨号上网的环境下WinpCap可能是无法预期工作的。
调用WinPcap有两种方式,一种是通过packet.dll调用它的驱动npf.sys,另外一种是通过wpcap.dll再间接调用packet.dll来调用它的驱动npf.sys,可能会有人问,既然可以直接调用packet.dll,为什么还要有调用wpcap.dll这种方式?原因很简单:在Unix下(或者Linux)下有一个抓包接口叫Pcap—这也是WinPcap的名称来由,wpcap.dll对外提供了相同的接口函数,便于程序员在不同的平台下移植。本文介绍的是packet.dll调用方式,对wpcap.dll接口感兴趣的朋友可以参考韩国程序员写的一个控件包Snoop2,我将它的下载放在本文的末尾(说句题外话,WinPcap的安装程序,实际上是释放sys和dll到系统,然后安装sys为服务,具体可以参考这个Snoop2(Jingtao修改版),里面直接集成了sys和dll,所以无需先安装WinPcap了,但是由于当时这个版本时间很早,所以还支持Win98系统,读者可以将里面的sys和dll文件替换成现在的最新版本再编译)。
要调用WinPcap,第一步当然是打开需要操作的网卡,因为有可能一台电脑上有好几张网卡。WinPcap提供了一个函数叫PacketGetAdapterNames,用于获取系统已经安装的所有网卡,函数声明如下:
TPacketGetAdapterNames = function(pStr: PAnsiChar; BufferSize: PULONG): Byte; cdecl;
 其中pStr是一个用于接收网卡名称的缓冲区,BufferSize是该缓冲区的大小,如果函数成功,则返回非0值。需要注意的是,网络的名称有多种形式,一种是设备名称,一种是友好名称,这个函数返回的格式是:设备名称+两个0+网卡名称,在WinPcap里面打开某块网卡,需要提供的是设备名称,packet.dll的源代码里面大概的实现如下:
function PacketGetAdaptersNPF(): Byte;
var
LinkageKey, AdapKey, OneAdapKey: HKEY;
RegKeySize: DWORD;
Status: Longint;
i: Integer;
dim: DWORD;
RegType: DWORD;
TName: array[..] of AnsiChar;
TAName: array[..] of AnsiChar;
AdapName: array[..] of AnsiChar;
TcpBindingsMultiString: PAnsiChar;
FireWireFlag: UINT;
//
// Old registry based WinPcap names
//
// CHAR npfCompleteDriverPrefix[MAX_WINPCAP_KEY_CHARS];
// UINT RegQueryLen; npfCompleteDriverPrefix: array[..MAX_WINPCAP_KEY_CHARS - ] of AnsiChar; // = NPF_DRIVER_COMPLETE_DEVICE_PREFIX;
DeviceGuidName: array[..] of AnsiChar;
label tcpip_linkage;
begin
RegKeySize := ;
FillChar(npfCompleteDriverPrefix, sizeof(npfCompleteDriverPrefix), #);
StrCopy(npfCompleteDriverPrefix, NPF_DRIVER_COMPLETE_DEVICE_PREFIX);
//TRACE_ENTER("PacketGetAdaptersNPF"); //
// Old registry based WinPcap names
//
// Get device prefixes from the registry Status := RegOpenKeyEx(HKEY_LOCAL_MACHINE,
'SYSTEMCurrentControlSetControlClass{4D36E972-E325-11CE-BFC1-08002BE10318}',
,
KEY_READ,
AdapKey); if (Status <> ERROR_SUCCESS) then
begin
//TRACE_PRINT("PacketGetAdaptersNPF: RegOpenKeyEx ( Class\{networkclassguid} ) Failed");
goto tcpip_linkage;
end; i := ; //TRACE_PRINT("PacketGetAdaptersNPF: RegOpenKeyEx ( Class\{networkclassguid} ) was successful");
//TRACE_PRINT("PacketGetAdaptersNPF: Cycling through the adapters in the registry:"); //
// Cycle through the entries inside the {4D36E972-E325-11CE-BFC1-08002BE10318} key
// To get the names of the adapters
//
//while((Result = RegEnumKey(AdapKey, i, AdapName, sizeof(AdapName)/2)) == ERROR_SUCCESS)
while ((RegEnumKey(AdapKey, i, AdapName, sizeof(AdapName) div )) = ERROR_SUCCESS) do
begin
Inc(i);
FireWireFlag := ;
//
// Get the adapter name from the registry key
//
Status := RegOpenKeyEx(AdapKey, AdapName, , KEY_READ, OneAdapKey);
if (Status <> ERROR_SUCCESS) then
begin
//TRACE_PRINT1("%d) RegOpenKey( OneAdapKey ) Failed, skipping the adapter.",i);
continue;
end; //
//
// Check if this is a FireWire adapter, looking for "1394" in its ComponentId string.
// We prevent listing FireWire adapters because winpcap can open them, but their interface
// with the OS is broken and they can cause blue screens.
//
dim := sizeof(TName);
Status := RegQueryValueEx(OneAdapKey,
'ComponentId',
nil,
nil,
PBYTE(@TName[]),
@dim); if (Status = ERROR_SUCCESS) then
begin
if (IsFireWire(TName) <> ) then
begin
FireWireFlag := INFO_FLAG_DONT_EXPORT;
end;
end; Status := RegOpenKeyEx(OneAdapKey, 'Linkage', , KEY_READ, LinkageKey);
if (Status <> ERROR_SUCCESS) then
begin
RegCloseKey(OneAdapKey);
//TRACE_PRINT1("%d) RegOpenKeyEx ( LinkageKey ) Failed, skipping the adapter",i);
continue;
end; dim := sizeof(DeviceGuidName);
Status := RegQueryValueExA(LinkageKey,
'Export',
nil,
nil,
PBYTE(@DeviceGuidName[]),
@dim); if (Status <> ERROR_SUCCESS) then
begin
RegCloseKey(OneAdapKey);
RegCloseKey(LinkageKey);
//TRACE_PRINT1("%d) Name = SKIPPED (error reading the key)", i);
continue;
end; if (strlen(DeviceGuidName) >= strlen('Device')) then
begin
// Put the DeviceNPF_ string at the beginning of the name
StrPCopy(TAName, Format('%s%s', [npfCompleteDriverPrefix,
DeviceGuidName + strlen('Device')]));
end
else
continue; //terminate the string, just in case
TAName[sizeof(TAName) - ] := #;
//TRACE_PRINT2("%d) Successfully retrieved info for adapter %s, trying to add it to the global list...", i, TAName);
// If the adapter is valid, add it to the list. PacketAddAdapterNPF(TAName, FireWireFlag); RegCloseKey(OneAdapKey);
RegCloseKey(LinkageKey); end; // while enum reg keys RegCloseKey(AdapKey); tcpip_linkage:
//
// no adapters were found under {4D36E972-E325-11CE-BFC1-08002BE10318}. This means with great probability
// that we are under Windows NT 4, so we try to look under the tcpip bindings.
// //TRACE_PRINT("Adapters not found under SYSTEM\CurrentControlSet\Control\Class. Using the TCP/IP bindings."); Status := RegOpenKeyEx(HKEY_LOCAL_MACHINE,
'SYSTEMCurrentControlSetServicesTcpipLinkage',
,
KEY_READ,
LinkageKey); if (Status = ERROR_SUCCESS) then
begin
// Retrieve the length of th binde key
// This key contains the name of the devices as devicefoo
//in ASCII, separated by a single '\0'. The list is terminated
//by another '\0'
Status := RegQueryValueExA(LinkageKey,
'bind',
nil,
@RegType,
nil,
@RegKeySize); // Allocate the buffer
TcpBindingsMultiString := GlobalAllocPtr(GMEM_MOVEABLE or GMEM_ZEROINIT, RegKeySize + ); if (TcpBindingsMultiString = nil) then
begin
//TRACE_PRINT("GlobalAlloc failed allocating memory for the registry key, returning.");
//TRACE_EXIT("PacketGetAdaptersNPF");
Result := ;
Exit;
end; // Query the key again to get its content
Status := RegQueryValueExA(LinkageKey,
'bind',
nil,
@RegType,
PBYTE(@TcpBindingsMultiString[]),
@RegKeySize); RegCloseKey(LinkageKey); // Scan the buffer with the device names
i := ;
while True do
begin
if (TcpBindingsMultiString[i] = #) then
break; StrPCopy(TAName, Format('%s%s', [npfCompleteDriverPrefix, TcpBindingsMultiString + i + strlen('Device')]));
//
// TODO GV: this cast to avoid a compilation warning is
// actually stupid. We shouls check not to go over the buffer boundary!
//
Inc(i, strlen(PAnsiChar(TcpBindingsMultiString + i)) + ); // If the adapter is valid, add it to the list.
PacketAddAdapterNPF(TAName, );
end; GlobalFreePtr(TcpBindingsMultiString);
end else
begin end;
Result := ;
end;
    另外,IpHlp函数也提供了获取网卡信息的接口,而且网卡名称跟WinPcap的一样。由于IpHlp可以获取网卡的更多信息,例如:IP地址、物理地址、网关IP等信息,所以我们可以结合它来实现更加友好的选择界面。下面是本讲实现的最终效果图:

WinPcap权威指南(一)的更多相关文章

  1. WinPcap权威指南(三):ARP协议

    ARP协议在局域网内使用的非常广泛,它的数据包类型分为请求包和答复包.Windows系统内部有一个缓冲区,保存了最近的ARP信息,可以在cmd下使用命令arp -a来显示目前的缓存,或者使用命令arp ...

  2. JavaScript权威指南 - 函数

    函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...

  3. JavaScript权威指南 - 对象

    JavaScript对象可以看作是属性的无序集合,每个属性就是一个键值对,可增可删. JavaScript中的所有事物都是对象:字符串.数字.数组.日期,等等. JavaScript对象除了可以保持自 ...

  4. JavaScript权威指南 - 数组

    JavaScript数组是一种特殊类型的对象. JavaScript数组元素可以为任意类型,最大容纳232-1个元素. JavaScript数组是动态的,有新元素添加时,自动更新length属性. J ...

  5. 《Ansible权威指南》笔记(3)——Ad-Hoc命令集,常用模块

    五.Ad-Hoc命令集1.Ad-Hoc命令集通过/usr/bin/ansible命令实现:ansible <host-pattern> [options]    -v,--verbose  ...

  6. 《Ansible权威指南》笔记(1)——安装,ssh密钥登陆,命令

    2016-12-23 读这本<Ansible权威指南>学习ansible,根据本书内容和网上的各种文档,以及经过自己测试,写出以下笔记.另,这本书内容很好,但印刷错误比较多,作者说第二版会 ...

  7. 读《Android编程权威指南》

    因为去年双十二购买了一折的<Android 编程权威指南(第一版)>,在第二版出来后图灵社区给我推送了第二版的优惠码,激动之余就立马下单购买电子书,不得不说Big Nerd Ranch G ...

  8. maven权威指南学习笔记(五)—— POM

    1. 简介 Archetype插件通过 pom.xml 文件创建了一个项目.这就是项目对象模型 (POM),一个项目的声明性描述. 当Maven运行一个目标的时候,每个目标都会访问定 义在项目POM里 ...

  9. maven权威指南学习笔记(一)——简介

    maven是什么?有什么用? Maven是一个项目管理工具,它包含了     一个项目对象模型 (Project Object Model),     一组标准集合,     一个项目生命周期(Pro ...

随机推荐

  1. WAP、触屏版网站及APP的区别

     1.电脑版网站: 电脑版网站是指用户通过台式或者笔记本电脑浏览器打开的网站,也就是我们平时上网所访问的网站.其支持和兼容IE6.IE7.IE8.IE9.IE10.Firefox.Chrome等各种主 ...

  2. vue构建项目全过程

    1.node版本请更新到6.9.X版本以上,不然npm依赖会出问题 2.命令行里运行npm install --global vue-cli 3.npm install --global webpac ...

  3. php实现概率性随机抽奖代码

    1.初始数据: 权重越大,抽取的几率越高 [奖品1, 权重 5], [ 奖品2, 权重6], [ 奖品3, 权重 7], [ 奖品4, 权重2] 2.处理步骤: 1)N = 5 + 6 + 7 + 2 ...

  4. jquery----data使用

    - .data() - .data("key", value) 保存值,value可以是字符串,也可以是数组,也可以是jquery对象- .data("key" ...

  5. 怎样在win7 IIS中部署网站?

    IIS作为微软web服务器的平台,可以轻松的部署网站,让网站轻而易举的搭建成功,那么如何在IIS中部署一个网站呢,下面就跟小编一起学习一下吧. 第一步:发布IIS文件 1:发布你所要在IIS上部署的网 ...

  6. codeforces 750D New Year and Fireworks【DFS】

    题意:烟花绽放时分为n层,每层会前进ti格,当进入下一层是向左右45°分开前进. 问在网格中,有多少网格至少被烟花经过一次? 题解:最多30层,每层最多前进5格,烟花的活动半径最大为150,每一层的方 ...

  7. asp.net core 微信扫码支付(扫码支付,H5支付,公众号支付,app支付)之1

    2018-08-13更新生成二维码的方法 在做微信支付前,首先要了解你需要什么方式的微信支付,目前本人做过的支付包含扫码支付.H5支付.公众号支付.App支付等,本人使用的是asp.net mvc c ...

  8. BZOJ3064 Tyvj 1518 CPU监控 线段树

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3064 题意概括 一个序列,要你支持以下操作: 1. 区间询问最大值 2. 区间询问历史最大值 3. ...

  9. Scrapy爬虫学习笔记 - windows \ linux下搭建开发环境2

    四.虚拟环境的安装和配置 virtualenv可以搭建虚拟且独立的python运行环境, 使得单个项目的运行环境与其它项目独立起来. virtualenv本质上是个python包 虚拟环境可以将开发环 ...

  10. 洛谷 P1474 货币系统 Money Systems(经典)【完全背包】+【恰好装满的最大方案数量】

    题目链接:https://www.luogu.org/problemnew/show/P1474 题目描述 母牛们不但创建了它们自己的政府而且选择了建立了自己的货币系统.由于它们特殊的思考方式,它们对 ...