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. bzoj 1064 noi2008 假面舞会题解

    莫名其妙的变成了我们的noip互测题... 其实这题思想还是比较简单的,只是分类不好分而已 其实就是一个dfs的事 首先,非常明显,原题目中的所有关系可以抽象成一个图(这是...显而易见的吧...) ...

  2. easyUI拖动课程进课程表

    <!DOCTYPE html><html><head> <meta charset="utf-8"> <title>拖动 ...

  3. DDD领域模型数据访问权限之用户权限(十)

    BAS_PRService岗位和角色服务: public class BAS_PRService { //岗位 private IRepository<BAS_Post> ireposit ...

  4. MVCJSONJQuery分页实现

    思路: 1.用Ado.NET获取数据 2.控制器中创建一个方法参数为搜索条件 3.返回前台一个Json对象,把对象用一个类封装 4.用JQuery接收数据 public ActionResult In ...

  5. mydumper备份原理和使用方法

    mydumper介绍 MySQL自身的mysqldump工具支持单线程工作,依次一个个导出多个表,没有一个并行的机,这就使得它无法迅速的备份数据. mydumper作为一个实用工具,能够良好支持多线程 ...

  6. Codeforces Round #319 (Div. 2) D - Invariance of Tree

    Invariance of Tree 题目大意:给你一个有1-n组成的序列p,让你构造一棵树,如果节点a和b之间有一条边,则p[a]和p[b]之间也有一条边. 思路:没啥思路,看了题解菜爆. 我们可以 ...

  7. Codeforces Round #309 (Div. 2) -D. Kyoya and Permutation

    Kyoya and Permutation 这题想了好久才写出来,没看题解写出来的感觉真的好爽啊!!! 题目大意:题意我看了好久才懂,就是给你一个序列,比如[4, 1, 6, 2, 5, 3],第一个 ...

  8. Docker dockerfile镜像编码

    一. 大多数docker基础镜像使用locale查看编码,发现默认编码都是POSIX,这会导致中文乱码.解决方法如下: 二.首先使用locale -a查看容器所有语言环境 三.dockerfile中加 ...

  9. python 字符串组成MySql 命令时,字符串含有单引号或者双引号导致出错解决办法

    引用自:https://blog.csdn.net/zhaoya_huangqing/article/details/48036839 一.在组成SQL语句并发送命令时完全按照Python中的样式去传 ...

  10. 11.Django|中间件

    中间件 文件夹为middlewareDemo 中间件是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出.因为改变的是全局,所以需要谨 ...