NDIS网络数据监控程序NDISMonitor(1)-----驱动程序(编译过程与源码讲解)

2023-11-19

转载请标明是引用于 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文件产生了。

 

二、源码分析

 

 1、DriverEntry

与我数的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;

 

2、IRP_MJ_CREATE派遣函数VPCKernelNtDispatchCreate

此函数相对简单,只是根据DeviceExtension中自定义的数据来做“仅允许一个文件对象的创建”的处理而已。

3、 IRP_MJ_CLOSE派遣函数VPCKernelNtDispatchClose

标识客户端关闭了后去做清理工作:

(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 ()


 

5、IRP_MJ_DEVICE_CONTROL派遣函数VPCKernelNtDispatchIoctl

此函数主要主是一个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、核心源码分析

(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、自定义处理函数的讲解

(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 )


 



 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

NDIS网络数据监控程序NDISMonitor(1)-----驱动程序(编译过程与源码讲解) 的相关文章

随机推荐

  • 二叉搜索树(BST的理论剖析+代码实现)

    二叉搜索树 BST树 文章目录 二叉搜索树 BST树 1 二叉搜索树的概念 2 二叉搜索树的结构定义 2 1 二叉搜索树结点模板的定义 2 2 二叉搜索树类模板的定义 3 二叉搜索树的效率 4 二叉搜索树的默认成员函数实现 4 1 BST的
  • MapReduce作业状态一直为ACCEPTED解决过程

    toc 今天在测试Hadoop文件压缩功能时 在之前本地搭建的Hadoop集群上提交了一个MapReduce作业 但是提交后发现一直卡在那不动 18 07 20 17 21 50 WARN util NativeCodeLoader Una
  • Swagget技术·SpringBoot继承Swagger框架详解!

    文章目录 一 Swagger简介 二 SpringBoot集成Swagger 三 配置Swagger 1 Swagger实例Bean是Docket 所以通过配置Docket实例来配置Swaggger 2 可以通过apiInfo 属性配置文档
  • 基于电流采样运放的DCDC电源输出线损补偿电路的详细推导计算

    基于电流采样运放的DCDC电源输出线损补偿电路的详细推导计算 作者 TI 工程师 Kevin Zhang 当DCDC电源输出需要经过一根长线缆才能到达负载时 由于线缆的阻抗产生压降 会导致负载端电压小于实际DCDC输出电压 为保证负载端电压
  • Markdown基本语法

    Markdown基本语法 一 Markdown了解 二 基本语法 1 各级标题的写法 2 段落 3 字体样式 4 分隔线 5 下划线 6 脚注 7 列表 8 代码块 9 引用 10 图片插入 11 链接 12 表格 三 高级技巧 1 HTM
  • Air Video

    http baike baidu com view 8552809 htm 1 百度首页 登录注册 新闻网页贴吧知道MP3图片视频地图百科文库 帮助 首页 自然 文化 地理 历史 生活 社会 艺术 人物 经济 科技 体育 图片 数字博物馆
  • 翠竹林 Opencv+C++之人脸识别

    最近一直在忙课程 老师让我看看他的论文也没放在心上 总算闲下来 看了他在人脸识别方面的相关论文 拿出一篇放在博客上跟大家共同分析下 在看以下内容前 首先要阅读下徐勇老师的这篇论文 A Two Phase Test Sample Sparse
  • Python Timer定时器

    Timer是在threading模块下的 Thread类的派生类 它用于在指定时间后调用一个方法 Timer的构造方法 Timer interval func args kwargs interval 用于设置等待时间 func 要执行的函
  • node-npm 设置淘宝镜像

    1 安装 cnpm 淘宝镜像 npm install g cnpm registry https registry npm taobao org 2 将npm设置为淘宝镜像 npm config set registry https reg
  • swift项目桥接通过cocoapods pod下来的第三方oc文件引入不成功

    最近想试着开始写学着swift项目了 然后就在用cocoapods pod下来的第三方框架的时候出现了问题 先以为是自己创建桥接文件后 配置出错了 但是网上的那些文章都还是能把桥接文件处理好 言归正传 先上图 pod的文件和桥接header
  • 你与小程序开发的距离有多远?

    你与小程序开发的距离有多远 2017年1月9日凌晨 小程序正式发布 对焦10年前iPhone的发布时间 产品之神张小龙显然是想让这一天具有十分重要的历史意义 小程序发布之后 它终于揭开了最终面目 我们不得不承认 这一天 必定是一个新时代的开
  • Java的多态

    一 多态的概念 多态性是指同一操作或方法可以在不同的对象上具有不同的行为 它允许我们通过使用基类或接口类型的引用变量来调用子类或实现类的方法 二 多态的作用 多态性是面向对象编程中的一个重要概念 它可以让我们以一种统一的方式处理不同类型的对
  • Eureka初启动报错:{@org.springframework.beans.factory.annotation.Qualifier(value=httpTraceFilter)}

    学习Eureka时启动报错 之前写服务者消费者接口未报错 到启动Eureka时才发现有误 启动Eureka时报错 信息提示是 Dependency annotations org springframework beans factory
  • 合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间

    以数组 intervals 表示若干个区间的集合 其中单个区间为 intervals i starti endi 请你合并所有重叠的区间 并返回 一个不重叠的区间数组 该数组需恰好覆盖输入中的所有区间 示例 输入 intervals 1 3
  • nginx 反向代理到另一个nginx并在另一个ngixn上面做转发

    目录 一 背景 二 步骤 三 问题总结 一 背景 近期部署项目时遇到一个问题 由阿里云解析到一个公网iP 由该公网ip的nginx进行转发 但转发时 该项目为前后端分离 需要解析到远程服务器的xxx目录 这通过一个nginx转发不好解决 通
  • Linux 开发环境搭建与使用——SlickEdit 的安装与配置

    SlickEdit 的介绍 vim 是公认很好用很完美 但是对新手来说 上手毕竟不是很容易 Windows 下程序员很多都很喜欢Source Insight 这个工具来看代码 各种语法高亮看着很舒服 那在 Linux 平台下 有没有媲美 S
  • ICPC-TOOLs resolver滚榜单工具使用指南

    0 引言 上周突然临危受命 学校ACM社团要举办校内迎新赛 想要使用滚榜功能让揭晓结果变得更加刺激 奈何本人不打ACM 一开始连滚榜都不知道 苦笑 jpg 不过还是跌跌撞撞把功能实现了 介于网络上这方面的教程实在是凤毛麟角 我就把自己的经验
  • vscode远程ssh隧道连接linux服务器并配置conda环境调试python过程【保姆级教程】

    引言 为了彻底摆脱无界面不支持调试的xshell vscode是一款强大的神器 通过与远程服务器建立连接 可以做到远程debug和开发环境的搭建 非常好用 前期准备 vscode一枚 远程服务器用户名 IP 密码 1 本地vscode下载拓
  • python随机森林 交叉验证_随机森林算法详解及Python实现

    一 简介 随机森林是一种比较有名的集成学习方法 属于集成学习算法中弱学习器之间不存在依赖的一部分 其因为这个优点可以并行化运行 因此随机森林在一些大赛中往往是首要选择的模型 随机森立中随机是核心 通过随机的选择样本和选择特征 降低了决策树之
  • NDIS网络数据监控程序NDISMonitor(1)-----驱动程序(编译过程与源码讲解)

    转载请标明是引用于 http blog csdn net chenyujing1234 欢迎大家拍砖 一 编译过程 弄了半天终于编译通过了 看来驱动的开发确实没有应用来得简单啊 由于DDK提供的ndis h有4个 什么时候要调用哪个我确实不