转载请标明是引用于 http://blog.csdn.net/chenyujing1234
欢迎大家拍砖!
弄了半天终于编译通过了,看来驱动的开发确实没有应用来得简单啊。
由于DDK提供的ndis.h有4个,什么时候要调用哪个我确实不清楚:
(1)我先用#include <d:\winddk\7600.16385.1\inc\ddk\ndis.h>
结果报错了:
1>1>errors in directory e:\g2\fft\usbdri~1\ndismo~1\ndismo~4\driver3 1>1>e:\g2\fft\usbdriver\ndismonitor\ndismonitor_v1-00_kernel_vs\driver3\ndishook.c(380) : error C2037: left of 'ProtocolNextOpen' specifies undefined struct/union '_NDIS_OPEN_BLOCK' 1>1>e:\g2\fft\usbdriver\ndismonitor\ndismonitor_v1-00_kernel_vs\driver3\ndishook.c(393) : error C2037: left of 'AdapterHandle' specifies undefined struct/union '_NDIS_OPEN_BLOCK' 1>1>e:\g2\fft\usbdriver\ndismonitor\ndismonitor_v1-00_kernel_vs\driver3\ndishook.c(734) : error C2037: left of 'ReceiveHandler' specifies undefined struct/union '_NDIS_OPEN_BLOCK'
定位代码是在:
NDIS_OPEN_BLOCK* GetNextOpen( IN NDIS_OPEN_BLOCK* pnobOpenBlock ) { // Return the information required. return pnobOpenBlock->ProtocolNextOpen; }
我想是因为找不到结构体NDIS_OPEN_BLOCK的定义的原因。关于此结构体请参考文章<<NDIS的NDIS_PROTOCOL_BLOCK和NDIS_OPEN_BLOCK的介绍>>。
于是在DDK包中执行ProtocolNextOpen这个变量,总共出现在三个地方:
怪不得会编译出错,原来在
d:\winddk\7600.16385.1\inc\ddk\ndis.h中根据就没有此成员变量。
看一看以上三个含有ProtocolNextOpen的地方的定义吧:
(1、1)D:\WINDDK\3790.1830\inc\ddk\w2k\ndis.h
(1、2) D:\WINDDK\3790.1830\inc\ddk\wnet\ndis.h
(1、3)D:\WINDDK\3790.1830\inc\ddk\wxp\ndis.h
综上看来我们有两种可选:
D:\WINDDK\3790.1830\inc\ddk\w2k\ndis.h
D:\WINDDK\3790.1830\inc\ddk\wxp\ndis.h
(2)试看看D:\WINDDK\3790.1830\inc\ddk\wxp\ndis.h吧
报以下错误:
1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(7156) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition 1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(10163) : error C2061: syntax error : identifier 'W_SEND_HANDLER' 1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(10164) : error C2061: syntax error : identifier 'WTransferDataHandler' 1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(10164) : error C2059: syntax error : ';' 1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(10169) : error C2061: syntax error : identifier 'WSendPacketsHandler' 1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(10169) : error C2059: syntax error : ';' 1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(10171) : error C2061: syntax error : identifier 'CancelSendPacketsHandler' 1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(10171) : error C2059: syntax error : ';' 1>1>d:\winddk\3790.1830\inc\ddk\wxp\ndis.h(10182) : error C2061: syntax error : identifier 'QC'
这么多错误,我想放弃引方案。
(3)试看看D:\WINDDK\3790.1830\inc\ddk\w2k\ndis.h
只有两个报错:
1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(5567) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(5567) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(5567) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(5567) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(5567) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(5567) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(5567) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(5567) : error C2011: '_MAP_REGISTER_ENTRY' : 'struct' type redefinition
(3、1)既然是重定义,那么我把它的定义屏蔽掉吧:
(3、2)再试看看,这个错误消除了,现在还剩下一个错:
1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(9297) : error C1083: Cannot open include file: 'afilter.h': No such file or directory 1>Compiling - ndishook.c 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(9297) : error C1083: Cannot open include file: 'afilter.h': No such file or directory 1>Compiling - vpcknt.c 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(9297) : error C1083: Cannot open include file: 'afilter.h': No such file or directory 1>Compiling - ndishook.c 1>1>d:\winddk\3790.1830\inc\ddk\w2k\ndis.h(9297) : error C1083: Cannot open include file: 'afilter.h': No such file or directory
这个好解决,给它指定绝对路径嘛:
改进:(经网友xkjcf要求,把绝对路径去掉)
(3、2、1)在sources文件中添加INCLUDES路径;
路径总线:
A、我测试发现“工具->选项->项目解决方案->C++目录->包含里设置的INCLUDE路径”与sources路径下的INCLUDE路径有优先级,
其中sources中的路径有更高的优先级。
B、在sources文件中若是有多个路径,且都含有同一个.h文件,那么此时包哪一个是没有顺序关系的。
eg:
在此sources下有:
两个路径下都有usbdlib.h 文件,可是我在引用它时:
却引用到了后者的路径,以致报错:
(3、2、2)
再试看看,哈哈^-^ ,另人兴奋的.sys文件产生了。
与我数的Windows驱动的入口开发一样,这里的DriverEntry没有什么特别的地方:
1、1 创建设备和设备SymbolicLink名字。
ntStatus = IoCreateDevice( DriverObject, sizeof( DEVICE_EXTENSION ), & uszDriverString, FILE_DEVICE_UNKNOWN, 0, FALSE, & pDeviceObject );
// Point uszDeviceString at the device name. RtlInitUnicodeString( & uszDeviceString, L"\\DosDevices\\" SYSDRIVER_NAME_WIDE ); // Create symbolic link to the user-visible name. ntStatus = IoCreateSymbolicLink( & uszDeviceString, & uszDriverString );
创建SymbolicLink时名字为:\\DosDevices\\VPCKnt。
这里说明驱动中的SymbolicLink命名的一个特点:DosDevices的符号链接名就是??, 所以"\\DosDevices\\XXXX"其实就是"\\??\\XXXX"
TEXT(\\\\.\VPCKnt),
1、2 分配派遣函数
DriverObject->DriverUnload = VPCKernelNtDriverUnload; DriverObject->MajorFunction[ IRP_MJ_CREATE ] = VPCKernelNtDispatchCreate; DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = VPCKernelNtDispatchClose; DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = VPCKernelNtDispatchIoctl;
以上函数是包含完成NDIS抓包的所有逻辑,我们重点分析。
1、3 PDEVICE_EXTENSION扩展附加及初始化
PDEVICE_EXTENSION为我们息定义的结构体,把它附加到设备象中,这样以后在派遣函数中我们就可以从扩展数据中获得我们自定义的数据:
typedef struct _DEVICE_EXTENSION { // Boolean表明是否用户态的service/application已经创建文件对象 ULONG WasCreated; FAST_MUTEX WasCreatedFastMutex; // InterceptNDIS表明是否我们不得不去中断NDIS活动 ULONG InterceptNDIS; // 关于NDIS Hook的状态信息 PNT_PROTOCOL_LIST NdisProtocolList; PNDISHOOK_HANDLER_STUB NdisStubsList; DWORD NdisStubsNum; // 客户端想要表现的Open Adapter的列表 OALIST_ITEM* OaList; DWORD OaListItems; NDIS_SPIN_LOCK OaListSpinLock; // 等待被接收的包列表 NEXT_PACKET* PacketsBuff; DWORD PacketsBuffMaxItems; DWORD PacketsBuffStart /* first allocated; -1 => empty */, PacketsBuffEnd /* first free */; DWORD PacketsLost; PKEVENT PacketsReadyEvent; NDIS_SPIN_LOCK PacketsBuffSpinLock; // Loopback trick stuff. DWORD dwLtLoopbackAdapterIpAddress; // tipically: 0xA9FE1981; zero if lt is disabled. DWORD dwLtTrickNatIpAddress; // tipically: 0xA9FE1980; zero if lt is disabled. // Other. NDIS_SPIN_LOCK ReceiveWorkItemSpinLock; DWORD ReceiveWorkItemIdCounter; NDIS_SPIN_LOCK PacketSerialCounterSpinLock; DWORD PacketSerialCounter; } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
extension = pDeviceObject->DeviceExtension;
初始化同步操作互斥体及其他逻辑变量:
// Initialize WasCreated and its mutex. extension->WasCreated = 0; ExInitializeFastMutex( & extension->WasCreatedFastMutex ); // Initialize InterceptNDIS. extension->InterceptNDIS = 0; // 初始化NDIS状态变量 extension->NdisProtocolList = NULL; extension->NdisStubsList = NULL; extension->NdisStubsNum = 0; // 初始化OaList事件 extension->OaList = NULL; extension->OaListItems = 0; NdisAllocateSpinLock( & extension->OaListSpinLock ); // 初始化包Buffer extension->PacketsBuff = NULL; extension->PacketsBuffMaxItems = 0; extension->PacketsBuffStart = -1; extension->PacketsBuffEnd = 0; extension->PacketsLost = 0; extension->PacketsReadyEvent = NULL; NdisAllocateSpinLock( & extension->PacketsBuffSpinLock ); // 初始化其他res.... NdisAllocateSpinLock( & extension->ReceiveWorkItemSpinLock ); extension->ReceiveWorkItemIdCounter = 0; NdisAllocateSpinLock( & extension->PacketSerialCounterSpinLock ); extension->PacketSerialCounter = 0; // Initialize the lt stuff. extension->dwLtLoopbackAdapterIpAddress = 0; extension->dwLtTrickNatIpAddress = 0;
此函数相对简单,只是根据DeviceExtension中自定义的数据来做“仅允许一个文件对象的创建”的处理而已。
标识客户端关闭了后去做清理工作:
(1)擦除已经分配的打开的adapters;
ReleaseOaList ();
(2)停止中断;
extension->InterceptNDIS = 0;
(3)Unhook NDIS Handler functions;
Unhook NDIS其实就是重新装载原始的句柄指针,在Unhook NDIS 的前后都调用KeDelayExecutionThread让CPU停下来半秒。
关于KeDelayExecutionThread请参考我的文章: <<DDK下的Sleep函数KeDelayExecutionThread>>
KeDelayExecutionThread( UserMode, FALSE, & liWaitElapse ); // Unhook NDIS Handler functions. if ( extension->NdisProtocolList ) UnhookInstalledProtocols( (PNT_PROTOCOL) ( (BYTE*) extension->NdisProtocolList + sizeof( NT_PROTOCOL_LIST ) ), extension->NdisProtocolList->dwProtocolsNum, (PNT_OPEN_ADAPTER) ( (BYTE*) extension->NdisProtocolList + sizeof( NT_PROTOCOL_LIST ) + extension->NdisProtocolList->dwProtocolsNum * sizeof( NT_PROTOCOL ) ), extension->NdisProtocolList->dwOpenAdaptersNum ); KeDelayExecutionThread( UserMode, FALSE, & liWaitElapse );
以上UnhookIntalledProtocols函数的参数大家看得有点晕了,其实这是作者对extension->NdisProtocoList的一个数据结构定义罢了:
typedef struct _NT_PROTOCOL_LIST { // ...Info... DWORD dwProtocolsNum; DWORD dwOpenAdaptersNum; // ...Data... // // x List of NT_PROTOCOL(s) follows. // x List of NT_OPEN_ADAPTER(s) follows. // } NT_PROTOCOL_LIST, *PNT_PROTOCOL_LIST;
它的最前面是存着协议和Adapter数据的个数信息,后面分别是存着协议数组和Adapter数据。
NTSTATUS UnhookInstalledProtocols( PNT_PROTOCOL pntpProtocols, DWORD dwProtocolsSize, PNT_OPEN_ADAPTER pntoaAdapters, DWORD dwAdaptersSize ) { DWORD i; PNT_OPEN_ADAPTER pntoaThisAdapter; // 重载原始的句柄指针. for ( i=0; i<dwAdaptersSize; i++ ) { pntoaThisAdapter = & pntoaAdapters[ i ]; // Restore the Original Pointers. if ( pntoaThisAdapter->Original_SendHandler ) RestoreHandlerPointerSecure( & pntoaThisAdapter->pobBlockPtr->SendHandler, pntoaThisAdapter->Original_SendHandler, pntoaThisAdapter->Stub_SendHandler ); if ( pntoaThisAdapter->Original_ReceiveHandler ) RestoreHandlerPointerSecure( & pntoaThisAdapter->pobBlockPtr->ReceiveHandler, pntoaThisAdapter->Original_ReceiveHandler, pntoaThisAdapter->Stub_ReceiveHandler ); if ( pntoaThisAdapter->Original_PostNt31ReceiveHandler ) RestoreHandlerPointerSecure( & pntoaThisAdapter->pobBlockPtr->PostNt31ReceiveHandler, pntoaThisAdapter->Original_PostNt31ReceiveHandler, pntoaThisAdapter->Stub_PostNt31ReceiveHandler ); if ( pntoaThisAdapter->Original_SendPacketsHandler ) RestoreHandlerPointerSecure( & pntoaThisAdapter->pobBlockPtr->SendPacketsHandler, pntoaThisAdapter->Original_SendPacketsHandler, pntoaThisAdapter->Stub_SendPacketsHandler ); if ( pntoaThisAdapter->Original_TransferDataHandler ) RestoreHandlerPointerSecure( & pntoaThisAdapter->pobBlockPtr->TransferDataHandler, pntoaThisAdapter->Original_TransferDataHandler, pntoaThisAdapter->Stub_TransferDataHandler ); if ( pntoaThisAdapter->Original_ReceivePacketHandler ) RestoreHandlerPointerSecure( & pntoaThisAdapter->pobBlockPtr->ReceivePacketHandler, pntoaThisAdapter->Original_ReceivePacketHandler, pntoaThisAdapter->Stub_ReceivePacketHandler ); if ( pntoaThisAdapter->Original_TDCompleteHandler && pntoaThisAdapter->pmbMiniportPtr ) RestoreHandlerPointerSecure( & pntoaThisAdapter->pmbMiniportPtr->TDCompleteHandler, pntoaThisAdapter->Original_TDCompleteHandler, pntoaThisAdapter->Stub_TDCompleteHandler ); } // Return to the caller. return STATUS_SUCCESS; }
(4)释放内存;
(4、1)释放协议数组中每个成员中的pbWorkItemHeader
static VOID ReleaseReceiveWorkItems () { PDEVICE_EXTENSION extension = g_pDeviceObject->DeviceExtension; int i, c; NT_PROTOCOL* pBase = NULL; NT_PROTOCOL* pThis = NULL; if ( extension->NdisProtocolList == NULL || extension->NdisProtocolList->dwProtocolsNum == 0 ) { return; } pBase = (NT_PROTOCOL*) ( (BYTE*) extension->NdisProtocolList + sizeof( NT_PROTOCOL_LIST ) ); NdisAcquireSpinLock( & extension->ReceiveWorkItemSpinLock ); c = extension->NdisProtocolList->dwProtocolsNum; for ( i=0; i<c; i ++ ) { pThis = & pBase[ i ]; if ( pThis->pbWorkItemHeader ) { // free... ExFreePool( pThis->pbWorkItemHeader ); pThis->pbWorkItemHeader = NULL; pThis->uiWorkItemHeaderSize = 0; } } NdisReleaseSpinLock( & extension->ReceiveWorkItemSpinLock ); }
(4、2)释放包Buffer
static VOID ReleasePackets ()
此函数主要主是一个switch ..... case 语句,case了应用程序对函数的的四种IOCTROL码。
(5、1)IOCTL_VPCKNT_GET_VERSION
因为Irp->AssociatedIrp.SystemBuffer指向的Buffer就是应用层得到的Out Buffer,所以把版本号写到这块内存就对了。
(5、2)IOCTL_VPCKNT_INITIALIZE_HOOK
(5、2、1)分配包Buffer
分配的包的最大个数,由上层决定,默认是32。
extension->PacketsBuff = (NEXT_PACKET*) ExAllocatePool( NonPagedPool, ihiHookInput.dwPacketsBuffMaxItems * sizeof( NEXT_PACKET ) ); if ( extension->PacketsBuff ) { extension->PacketsBuffMaxItems = ihiHookInput.dwPacketsBuffMaxItems; memset( extension->PacketsBuff, 0, ihiHookInput.dwPacketsBuffMaxItems * sizeof( NEXT_PACKET ) ); }
(5、2、2)获得包准备好事件
调用ObReferenceObjectByHandle,由应用层的hNotificationEvent得到对应的PKEVENT。(原理参考:<<Window XP驱动开发(二十二) 驱动程序的同步处理>>)
if ( ihiHookInput.hNotificationEvent ) { NTSTATUS ntEvRes = ObReferenceObjectByHandle( ihiHookInput.hNotificationEvent, 0, (POBJECT_TYPE) NULL, UserMode, (PVOID) & extension->PacketsReadyEvent, (POBJECT_HANDLE_INFORMATION) NULL); if ( ntEvRes != STATUS_SUCCESS ) extension->PacketsReadyEvent = NULL; }
(5、2、3)注册协议并安装协议
关于这部分的内容特别重要,我们将在“6、核心源码”中分析。
__try { nsRegRes = RegisterFakeProtocol( & hFakeProtocolHandle, SYSDRIVER_NAME_ANSI ); if ( nsRegRes == STATUS_SUCCESS ) { nsHookRes = HookInstalledProtocols( & pplProtocolList, & pnhhsStubsList, & dwStubsNum, hFakeProtocolHandle ); } } __except ( EXCEPTION_EXECUTE_HANDLER ) { }
安装后得到Ndis Hook的结果,即下图的数据结构:
(5、2、4)把Ndis Hook的结果返回到应用程序的Out Buffer中;
pbOutputBufferPayload = (BYTE*) Irp->AssociatedIrp.SystemBuffer + sizeof( ihoHookOutput ); // Inform about the Ndis Hook results. if ( nsHookRes == STATUS_SUCCESS ) // NDIS Hook Ok. { // Info. ihoHookOutput.bNdisHookSucceeded = 1; ihoHookOutput.dwProtocolListBufferSize = sizeof( NT_PROTOCOL_LIST ) + pplProtocolList->dwProtocolsNum * sizeof( NT_PROTOCOL ) + pplProtocolList->dwOpenAdaptersNum * sizeof( NT_OPEN_ADAPTER ); // Data. memcpy( pbOutputBufferPayload, pplProtocolList, ihoHookOutput.dwProtocolListBufferSize ); pbOutputBufferPayload += ihoHookOutput.dwProtocolListBufferSize; } else // NDIS Hook Failed. { // Info. ihoHookOutput.bNdisHookSucceeded = 0; ihoHookOutput.dwProtocolListBufferSize = 0; } // Copy the Header structure. * (INITIALIZE_HOOK_OUTPUT*) Irp->AssociatedIrp.SystemBuffer = ihoHookOutput;
(5、3)IOCTL_VPCKNT_SUBMIT_OALIST
(5、4)IOCTL_VPCKNT_GET_NEXT_PACKET
(6、1) 注册协议
注册的PROTOCOL_CHARACTERISTICS为NDIS 4.0版本的。
NDIS40_PROTOCOL_CHARACTERISTICS ndis40pcFakeProtCharacts;
在初始化ndis40pcFakeProtCharacts结构体时给它预留了15个自定义的函数,根据实现需要可完成这15个函数的定义。
ndis40pcFakeProtCharacts.OpenAdapterCompleteHandler = & FakeProtocol_OpenAdapterComplete; ndis40pcFakeProtCharacts.CloseAdapterCompleteHandler = & FakeProtocol_CloseAdapterComplete; ndis40pcFakeProtCharacts.SendCompleteHandler = & FakeProtocol_SendComplete; ndis40pcFakeProtCharacts.TransferDataCompleteHandler = & FakeProtocol_TransferDataComplete; ndis40pcFakeProtCharacts.ResetCompleteHandler = & FakeProtocol_ResetComplete; ndis40pcFakeProtCharacts.RequestCompleteHandler = & FakeProtocol_RequestComplete; ndis40pcFakeProtCharacts.ReceiveHandler = & FakeProtocol_Receive; ndis40pcFakeProtCharacts.ReceiveCompleteHandler = & FakeProtocol_ReceiveComplete; ndis40pcFakeProtCharacts.StatusHandler = & FakeProtocol_Status; ndis40pcFakeProtCharacts.StatusCompleteHandler = & FakeProtocol_StatusComplete; NdisInitializeString( & ndis40pcFakeProtCharacts.Name, pszProtocolName ); ndis40pcFakeProtCharacts.ReceivePacketHandler = & FakeProtocol_ReceivePacket; ndis40pcFakeProtCharacts.BindAdapterHandler = & FakeProtocol_BindAdapter; ndis40pcFakeProtCharacts.UnbindAdapterHandler = & FakeProtocol_UnbindAdapter; ndis40pcFakeProtCharacts.PnPEventHandler = & FakeProtocol_PnpEvent; ndis40pcFakeProtCharacts.UnloadHandler = & FakeProtocol_UnloadProtocol;
然后调用NdisRegisterProtocol注册。
(6、2)安装协议
(6、2、1)分配要求的内存
包括PNT_PROTOCOL(1M,作为协议数组)、PNT_OPEN_ADAPTER(1M,作为协议Adpater 的数组)、PNDISHOOK_HANDLER_STUB(128K,用于Hook句柄的存根)
(6、2、2)循环访问列表协议,获得已安装的列表协议句柄;
循环获得协议的方法是调用GetNextProtocol函数,此函数根据NDIS50_PROTOCOL_BLOCK结构体中的NextProtocol 来访问下一个协议,能这样做的原因是因为协议之间是一条单链表。
这里把NDIS_HANDLE 强转为NDIS50_PROTOCOL_BLOCK类型,再次说明了两者其实是同一结构的。
NDIS_HANDLE GetNextProtocol( IN NDIS_HANDLE hProtocolHandle ) { DWORD dwNdisVersion; NDIS50_PROTOCOL_BLOCK* pn50pbProtBlockPtr; // Return the Next Protocol in the Linked List. dwNdisVersion = GetNdisVersion (); if ( dwNdisVersion == 0xFFFFFFFF ) return NULL; switch( dwNdisVersion ) { case 0x00050000: case 0x00050001: // Windows 2000 / Windows XP NDIS Version. pn50pbProtBlockPtr = (NDIS50_PROTOCOL_BLOCK*) hProtocolHandle; return (NDIS_HANDLE) pn50pbProtBlockPtr->NextProtocol; default: // Unrecognized NDIS Version. Exit. return NULL; } }
(6、2、3)根据列表协议句柄,获得对应的Characteristics数据结构。
NDIS50_PROTOCOL_CHARACTERISTICS* GetProtocolCharacteristics( IN NDIS_HANDLE hProtocolHandle ) { DWORD dwNdisVersion; NDIS50_PROTOCOL_BLOCK* pn50pbProtBlockPtr; // Return the Protocol Characteristics information for this protocol. dwNdisVersion = GetNdisVersion (); if ( dwNdisVersion == 0xFFFFFFFF ) return NULL; switch( dwNdisVersion ) { case 0x00050000: case 0x00050001: // Windows 2000 / Windows XP NDIS Version. pn50pbProtBlockPtr = (NDIS50_PROTOCOL_BLOCK*) hProtocolHandle; return & pn50pbProtBlockPtr->ProtocolCharacteristics; default: // Unrecognized NDIS Version. Exit. return NULL; } }
从以上两步的结果中提取信息进行编号保存在协议数组中。
(6、2、4)从协议句柄中获得协议中打开的Adapter。
从结果中提取信息进行编号保存在协议Adapter数组中。
综合(6、2、3)(6、2、4)我们得到了协议、Characteristics及协议Adapter的关系图。
(6、2、5)Hook处理各种Handle
(6、2、5、1)Hook 发送句柄
首先判断原有的SendHandler是否可以Hook:
其实原理很简单,因为Hook的句柄我们会存在一个区域内,通过判断是否在这个区域内能判断是不是Hook过。
static BOOLEAN CanHook( IN PVOID fnptr ) { if ( fnptr == NULL || g_pbCanHookRefMemStart == NULL || g_pbCanHookRefMemEnd == NULL ) // # pointer problem... return FALSE; else if ( ((BYTE*)fnptr) >= g_pbCanHookRefMemStart && ((BYTE*)fnptr) <= g_pbCanHookRefMemEnd ) // # already hooked... return FALSE; else // # ok, can hook... return TRUE; }
然后填充存根代码:
bPushImm32Opcode、dwOperationID、bJmpRel32Opcode分别填充固定的码值,这没什么可讲;
但dwJmpDisplacement值的得到有点费解(若有知道的网友可否告诉我一下)
static void FillStubCode( PNDISHOOK_HANDLER_STUB pnhhsThisStub, DWORD dwThisAdapterOrd, DWORD dwAddInfo ) { // Fill the Stub structure. pnhhsThisStub->bPushImm32Opcode = 0x68; pnhhsThisStub->dwOperationID = ( dwThisAdapterOrd << 0x10 ) | dwAddInfo; pnhhsThisStub->bJmpRel32Opcode = 0xE9; pnhhsThisStub->dwJmpDisplacement = (DWORD) & HandlerGeneralDispatcher - ( (DWORD) pnhhsThisStub + FIELD_OFFSET( NDISHOOK_HANDLER_STUB, bJmpRel32Opcode ) ) - 5; }
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!(重点中的重点,改日讲解)
static PVOID g_vpvHandlersVector[] = { & New_SendHandler, // SENDHANDLER_FNID & New_ReceiveHandler, // RECEIVEHANDLER_FNID & New_PostNt31ReceiveHandler, // POSTNT31RECEIVEHANDLER_FNID & New_SendPacketsHandler, // SENDPACKETSHANDLER_FNID & New_TransferDataHandler, // TRANSFERDATAHANDLER_FNID & New_ReceivePacketHandler, // RECEIVEPACKETHANDLER_FNID & New_TDCompleteHandler // TDCOMPLETEHANDLER_FNID };
static void __declspec(naked) HandlerGeneralDispatcher ( void ) { __asm { // ### Load in EAX and ECX the Code found in the Stack. ### mov eax, dword ptr[ esp ] // eax = Code: 0xYYYYZZZZ. (y = Adapter Ordinal, z = Handler Ordinal.) mov ecx, eax // ecx = Code: 0xYYYYZZZZ. shr eax, 0x10 // eax = 0x0000YYYY and ecx, 0xFFFF // ecx = 0x0000ZZZZ mov dword ptr[ esp ], eax // Updates Code in Stack: 0x0000YYYY // ### Call the appropriate Handler based on the Handler Ordinal. ### lea eax, [ offset g_vpvHandlersVector + ecx * 4 ]; call dword ptr[ eax ] // ### Pop in ECX the "HookPrivateStorage" parameter. ### pop ecx // ### Check whether we have to call the Original Handler or we have to return control to the Caller. ### cmp ecx, 0x1000 ja callOriginalHandler // ### Return the Control to the Caller. ### pop edx add esp, ecx jmp edx callOriginalHandler: // ### Call the Original Handler. ### jmp ecx } }
最后将记录原有的发送句柄后(目的是在UnhookInstalledProtocols时能重载原始句柄指指),将自己的发送Handle附加到原有的发送句柄上。
pntoaThisAdapter->Original_SendHandler = pobAdapter->SendHandler; pntoaThisAdapter->Stub_SendHandler = pnhhsThisStub; pobAdapter->SendHandler = (SEND_HANDLER) pnhhsThisStub;
这样pobAdapter->SendHandler 就会调用我们自定义的New_SendHandler函数了。关于它的处理我们在“7、 自定义处理函数讲解”中进行讲解。
(6、2、5、2)Hook 接收句柄
与Hook 发送句柄类似。
(6、2、5、3)Hook postnt31接收句柄
与Hook 发送句柄类似。
(6、2、5、4)Hook 发送包句柄
与Hook 发送句柄类似。
(6、2、5、5)Hook 传送数据句柄
与Hook 发送句柄类似。
(6、2、5、6)Hook 接收包句柄
与Hook 发送句柄类似。
(6、2、5、6)Hook 传送数据完成句柄
与Hook 发送句柄类似。
(7、1)New_SendHandler
以入参HookPrivateStorage来标识哪一协议,哪一Adapter;
如果判断如果需要中断NDIS请求,那么就调用Intercept_SendHandler将此数据包进行截获;
截获完成后,根据返回结果判断是否要将原始的处句柄返回回去(默认是需要将原始句柄返回),这样我们截获了包后此包能再次发送出去。
NDIS_STATUS __cdecl New_SendHandler( IN OUT DWORD HookPrivateStorage, IN OUT DWORD CallingFnRetAddress, IN OUT NDIS_HANDLE MacBindingHandle, IN OUT PNDIS_PACKET Packet ) { NDIS_STATUS nsRetStatus = NDIS_STATUS_SUCCESS; SEND_HANDLER pfnOriginal = NULL; DWORD OpenAdapterId = HookPrivateStorage; PDEVICE_EXTENSION pdeExtension = g_pDeviceObject->DeviceExtension; BOOLEAN bExecuteOriginalHandler = TRUE; // Get a pointer to the Original Handler. pfnOriginal = g_pntoaAdapters[ OpenAdapterId ].Original_SendHandler; // Check if we have to intercept the NDIS Request. if ( pdeExtension->InterceptNDIS ) bExecuteOriginalHandler = Intercept_SendHandler( & g_pntpProtocols[ g_pntoaAdapters[ OpenAdapterId ].dwProtocolOrd ], & g_pntoaAdapters[ OpenAdapterId ], & nsRetStatus, & MacBindingHandle, & Packet ); // Make call the Original Handler and return. if ( bExecuteOriginalHandler ) HookPrivateStorage = (DWORD) pfnOriginal; // Original Handler. else HookPrivateStorage = 2 * 0x4; // Passed Parameters Size. return nsRetStatus; }
(7、1、1)在Intercept_SendHandler里先判断此Adapter是不是要被处理:
// process. if ( HaveToProcess( pnoaAdapter ) ) {
判断原理是判断它是不是在OaList列表中。
(7、1、2)如果判断此Adapter是需要处理的那么就调用NdisPacket2MemoryRegion为此包分配分内存。
首先调用系统API NdisQueryPacket查询此PNDIS_PACKET包的PNDIS_BUFFER和总共的内存大小。
NdisQueryPacket( Packet, NULL, NULL, & pnbCurrent, & uiTotalSize );
然后根据PNDIS_PACKET包的内存大小及我们要添加的头的大小算出自定义包所需要的内存大小:
ulMemSize = ulHeaderBufferSize + uiTotalSize; if ( ulMemSize == 0 ) return NULL; pbMem = (BYTE*) ExAllocatePool( NonPagedPool, ulMemSize ); if ( pbMem == NULL ) return NULL;
最后把输入的包的BUFFER复制到我们自定义的内存中。
while( pnbCurrent ) { NdisQueryBuffer( pnbCurrent, & pvPtr, & uiPtrDim ); if ( pvPtr == NULL || uiCount + uiPtrDim > uiTotalSize ) { ExFreePool( pbMem ); return NULL; } else { memcpy( pbMem + uiCount, pvPtr, uiPtrDim ); uiCount += uiPtrDim; } NdisGetNextBuffer( pnbCurrent, & pnbCurrent ); }
(7、1、3)把从(7、1、2)中得到的自定义数据内存保存到extern的包队列中,并通过事件告知应用程序可以取包了。
VOID QueuePacket( IN PNT_OPEN_ADAPTER pnoaAdapter, IN BYTE* pbData, IN DWORD dwDataSize, BYTE bDirection, DWORD dwSerial ) { PDEVICE_EXTENSION extension = g_pDeviceObject->DeviceExtension; // add. NdisAcquireSpinLock( & extension->PacketsBuffSpinLock ); if ( extension->PacketsBuff && extension->PacketsBuffMaxItems ) { NEXT_PACKET* pThis = & extension->PacketsBuff[ extension->PacketsBuffEnd ]; // // manage the circular buffer. // // compare end and start. if ( extension->PacketsBuffEnd == extension->PacketsBuffStart ) { // ### free ### if ( pThis->pbData ) ExFreePool( pThis->pbData ); // ### inc counter ### extension->PacketsLost ++; // ### inc start pos ### extension->PacketsBuffStart ++; if ( extension->PacketsBuffStart == extension->PacketsBuffMaxItems ) extension->PacketsBuffStart = 0; } // inc end pos. extension->PacketsBuffEnd ++; if ( extension->PacketsBuffEnd == extension->PacketsBuffMaxItems ) extension->PacketsBuffEnd = 0; if ( extension->PacketsBuffStart == -1 ) extension->PacketsBuffStart = 0; // // fill this one. // pThis->dwOpenAdapterOrdinal = pnoaAdapter->dwOrdinal; pThis->dwPacketSerial = dwSerial; pThis->pbData = pbData; pThis->dwDataLength = dwDataSize; pThis->bDirection = bDirection; // // set the user event. // if ( extension->PacketsReadyEvent ) KeSetEvent( extension->PacketsReadyEvent, 0, FALSE ); } NdisReleaseSpinLock( & extension->PacketsBuffSpinLock ); // return. return; }
(7、1、4)如果是ARP或IP协议那么发送包回到协议
VOID SendPacketHook( IN PNT_PROTOCOL pnpProtocol, IN PNT_OPEN_ADAPTER pnoaAdapter, IN BYTE* pbData, IN DWORD dwDataSize )