PULONG pkg_addr)
{
//RtlInitUnicodeString(&routine_name, L"NdisDeregisterProtocol");
//routine_addr = MmGetSystemRoutineAddress(&routine_name);
ULONG dereg_addr; //www.jybase.net
GetNdisDeregisterProtocol(&dereg_addr);
if (!dereg_addr) {
KdPrint(("NdisReg @ DriverEntry : MmGetSystemRoutineAddress
errro!\n"));
}
//得到地址后,直接硬编码得到ndisProtocolList
//以及 ndisProtocolListLock
*list_addr = *(ULONG*)(dereg_addr + 0x26);
*lock_addr = *(ULONG*)(dereg_addr + 0x18);
*pkg_addr = *(ULONG*)(dereg_addr + 0xb);
*pkg_addr -= 0x20;
KdPrint(("the list_addr is %08x, lock_addr is %08x, pkg_addr is %08x\n",
*list_addr,*lock_addr,*pkg_addr));
}
其中 NdisDeregisterProtocol的地址是无法用MmGetSystemRoutine来得到的,笔者这里用
了一种方法:在代码中写上 call NdisDeregisterProtocol 语句,再用汇编中 call 指令会
压入返回地址的特点来得到 call NdisDeregisterProtocol 的地址,进而根据 call 指令的
结构得到 NdisDeregisterProtocol 的地址,这样可以避免在代码中硬编码查找该偏移。病
毒经常使用该方法来得到当前的运行基址。关于 call 指令的结构,请读者查阅相关资料。
结果如图 2、图3、图 4所示:

接下来在 DriverEntry 得到 ndisWorkerQueue 的地址。关于得到 NDIS.SYS 中
DriverEntry 地址的方法,笔者这里用 ZwCreateFile->ObReferenceObjectByHandle 得到
DEVICE_OBJECT 后,得到 DRIVER_OBJECT,最后再根据 DRIVER_OBJECT.DriverInit 就是
DriverEntry的地址了,代码如下:
VOID
GetNdisDriverEntry(PULONG routine_addr)
{
UNICODE_STRING obj_name;
HANDLE file_handle;
PFILE_OBJECT file_obj;
NTSTATUS ntStatus;
PDEVICE_OBJECT target_dev;
PDRIVER_OBJECT target_drv;
IO_STATUS_BLOCK ioStatus;
OBJECT_ATTRIBUTES objattr = {0};
RtlInitUnicodeString( &obj_name, L"\\Device\\Ndis" );
InitializeObjectAttributes(&objattr,
&obj_name,
OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,
NULL,
NULL);
ntStatus = ZwCreateFile(
&file_handle,
SYNCHRONIZE|FILE_ANY_ACCESS,
&objattr,
&ioStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_RANDOM_ACCESS |
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NO_INTERMEDIATE_BUFFERING,
NULL,
0);
if (!NT_SUCCESS(ntStatus)) {
KdPrint(("NdisReg @ GetNdisDriverEntry : ZwCreateFile error!\n"));
ZwClose( file_handle );
}
ntStatus = ObReferenceObjectByHandle(
file_handle,
FILE_READ_DATA,
NULL,
KernelMode,
(PVOID*)&file_obj,
NULL);
if (!NT_SUCCESS(ntStatus)) {
KdPrint(("NdisReg @ GetNdisDriverEntry : ObReferenceObjectByHandle
error!\n"));
ZwClose( file_handle );
ObDereferenceObject( file_obj );
}
target_dev = file_obj->DeviceObject;
target_drv = target_dev->DriverObject;
*routine_addr = target_drv->DriverInit;
g_ndisDriverObject = target_drv;
KdPrint(("NdisReg @ GetNdisDriverEntry : the routine_addr is %08x\n",
*routine_addr));
}
VOID
get_queue_addr(PULONG queue_addr)
{
ULONG routine_addr;
GetNdisDriverEntry(&routine_addr);
*queue_addr = *(ULONG*)(routine_addr + 0x1b4);
KdPrint(("NdisReg @ DriverEntry : get_queue_addr queue_addr is %08x\n",
*queue_addr));
}
最后在 NdisReEnumerateProtocolBindings中,可以得到 ndisCheckProtocolBindings
的地址。 NdisReEnumerateProtocolBindings的取得地址的方法和NdisDeregisterProtocol |