网上说的比较常见的4种方法:
1、通过DriverEntry传入的DriverObject参数的DriverSection成员指向LDR_DATA_TABLE_ENTRY结构,通过遍历这张表得到ntoskrnl的基址和大小
2、ZwQuerySystemInformation大法
3、搜索内存
4、利用KPCR结构
存在的问题:
1、第1种方法和第4种方法得到的结果比ZwQuerySystemInformation少一个
2、第1种方法如果输出BaseDllName是ntoskrnl.exe,如果输出FullDllName则是:\WINDOWS\system32\ntkrnlpa.exe,地址都是:804d8000,不明白为何
环境:虚拟机VMWare:WIN XP SP3 + WDK ---- WINXP Check方式编译
#include <ntddk.h> //---------------------------------// //下面的结构包含了一些重要信息。如:PsLoadedModuleList ,它是Windows加载的所有内核模块构成的链表的表头。 //PsLoadedModuleList就是如下这个结构体中InLoadOrderLinks。即为LDR_DATA_TABLE_ENTRY结构的第一项。 #pragma pack(push)//结构定义 #pragma pack(1) typedef struct _LDR_DATA_TABLE_ENTRY { LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; union { ULONG TimeDateStamp; PVOID LoadedImports; }; PVOID EntryPointActivationContext; PVOID PatchInformation; } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; #pragma pack(pop) //---------------------------------------------------------------------------------------------------//函数声明 NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath); NTSTATUS DriverUnload(); //Method3用到,指定当前线程运行在那个处理器 NTKERNELAPI VOID KeSetSystemAffinityThread (KAFFINITY Affinity); NTKERNELAPI VOID KeRevertToUserAffinityThread ( VOID ); NTKERNELAPI NTSTATUS ZwQuerySystemInformation( IN ULONG SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, IN PULONG ReturnLength OPTIONAL ); #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(PAGE, DriverUnload) //---------------------------------------------------------------------------------------------------//变量、常量、结构定义 UNICODE_STRING BaseName; #define SystemModuleInformation 11 //Method2要用到11功能号 typedef struct _SYSTEM_MODULE_INFORMATION_ENTRY { ULONG Unknow1; ULONG Unknow2; #ifdef _WIN64 ULONG Unknow3; ULONG Unknow4: #endif PVOID Base; ULONG Size; ULONG Flags; USHORT Index; USHORT NameLength; USHORT LoadCount; USHORT ModuleNameOffset; char ImageName[256]; }SYSTEM_MODULE_INFORMATION_ENTRY, *PSYSTEM_MODULE_INFORMATION_ENTRY; typedef struct _SYSTEM_MODULE_INFORMATION { ULONG Count;//内核中以加载的模块的个数 SYSTEM_MODULE_INFORMATION_ENTRY Module[1]; }SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION; //---------------------------------------------------------------------------------------------------// /* 用到了DriverObject域的InLoadOrderLinks链表 注意: 下面的代码会用到一个宏: --------------------------------------------------------------------------------------------------------------------- CONTAINING_RECORD 这样的一个宏,它的定义如下: #define CONTAINING_RECORD(address, type, field) ((type *)( (PCHAR)(address) - (ULONG_PTR)(&((type*)0)->field))) 根据网上资料:就是address -(field在type中的偏移) ---------------------------------------------------------------------------------------------------------------------- */ VOID Method1(IN PDRIVER_OBJECT DriverObject)//遍历链表 { ULONG Base=0;//模块基地址 LDR_DATA_TABLE_ENTRY* SectionBase=NULL; LIST_ENTRY* Entry=NULL; LIST_ENTRY InLoadOrderLinks; ULONG num=0; Entry=((LIST_ENTRY*)DriverObject->DriverSection)->Flink; do{ SectionBase=CONTAINING_RECORD(Entry,LDR_DATA_TABLE_ENTRY,InLoadOrderLinks);//得到这个Entry所属的Section的地址,此方法经过验证可行 if (SectionBase->EntryPoint && SectionBase->BaseDllName.Buffer && SectionBase->FullDllName.Buffer && SectionBase->LoadCount ) { DbgPrint("方法一遍历模块名称:%wZ,地址:%x\n",&(SectionBase->FullDllName),SectionBase->DllBase); //DbgPrint("方法一遍历模块名称:%wZ,地址:%8X\n",&(SectionBase->BaseDllName),SectionBase->DllBase); num++; /*if(!RtlCompareUnicodeString(&(SectionBase->BaseDllName),&BaseName,FALSE)) { DbgPrint("方法一模块名称:%wZ,地址:%x\n",&(SectionBase->BaseDllName),SectionBase->DllBase); }*/ } Entry=Entry->Flink; }while(Entry!=((LIST_ENTRY*)DriverObject->DriverSection)->Flink);//直到遍历回来 DbgPrint("方法一得到模块总数:%d\n",num); } void Method2()//ZwQuerySystemInformation大法 { PVOID pBuffer=0;//缓冲区 NTSTATUS Result;//查询结果 ULONG NeedSize; PSYSTEM_MODULE_INFORMATION pSystemModuleInformation;//将结果强制转换为该类型 ULONG BufferSize = 0x5000;//初始分配内存大小,没有采用查询再分配的循环方法 ULONG ModuleCount;//模块总数 ULONG i; do { pBuffer=ExAllocatePool(NonPagedPool,BufferSize); if(pBuffer==NULL) { DbgPrint("分配内存失败!\n"); return FALSE; } Result=ZwQuerySystemInformation(SystemModuleInformation,pBuffer,BufferSize,&NeedSize); if(Result==STATUS_INFO_LENGTH_MISMATCH )//分配不够 { ExFreePool(pBuffer); //大小乘以2,重新分配 BufferSize*=2; } else if(!NT_SUCCESS(Result))//失败,放弃吧 { DbgPrint( "查询失败,错误码:%8X\n", Result ); ExFreePool(pBuffer); return FALSE; } }while( Result == STATUS_INFO_LENGTH_MISMATCH ); pSystemModuleInformation = (PSYSTEM_MODULE_INFORMATION)pBuffer;//类型转换 ModuleCount=pSystemModuleInformation->Count;//模块总数 for(i=0;i<ModuleCount;i++) { DbgPrint( "方法二遍历模块名称:%s,地址:%8X\n", pSystemModuleInformation->Module.ImageName, pSystemModuleInformation->Module.Base ); } DbgPrint("方法二得到模块总数:%d\n",ModuleCount); ExFreePool(pBuffer); return TRUE; } VOID Method3(ULONG Base)//搜索内存,从0x80000000-----0xa0000000 { ; } //内核中FS寄存器指向KPCR结构,每个处理器都有一个,使用第一个处理器即可其中比较重要的是KdVersionBlock这个指标, 它指向一个DBGKD_GET_VERSION64这个结构. //这个结构体里面包含了一些重要信息。如:PsLoadedModuleList ,它是Windows加载的所有内核模块构成的链表的表头 //两个处理器对应的KPCR结构是有区别的, 只有第一个处理器的KPCR域KdVersionBlock才指向DBGKD_GET_VERSION64这个结构. //-------------------------------------仔细观察定义会发现,这个跟使用DriverObject方法达到的链表示一样的! void Method4() { ULONG Addr;//内核地址 LIST_ENTRY* Entry=NULL; LIST_ENTRY InLoadOrderLinks; LDR_DATA_TABLE_ENTRY* SectionBase=NULL;//LdrData->DllBase,LdrData->FullDllNme ULONG num=0; //-----------------------------------------------------------------------------//在莫灰灰基础上修改一小部分 KeSetSystemAffinityThread(1);//使当前线程运行在第一个处理器上 _asm { push eax mov eax,FS:[0x34] ;指向KdVersionBlock的指标 add eax,18h ;得到指向PsLoadedModuleList的地址,即该指针的地址,指针里存有PsLoadedModuleList的地址 mov eax,[eax] ;得到PsLoadedModuleList的地址 mov eax,[eax] ;得到PsLoadedModuleList的内容 //mov eax,[eax+18h] ;取出DllBase, 即ntoskrnl.exe的基地址 mov Addr,eax pop eax } KeRevertToUserAffinityThread();//恢复线程运行的处理器 //----------------------------------------------------------------------// 以下跟方法一重复 Entry=(LIST_ENTRY*)Addr; do { SectionBase=CONTAINING_RECORD(Entry,LDR_DATA_TABLE_ENTRY,InLoadOrderLinks);//得到这个Entry所属的Section的地址,此方法经过验证可行 if (SectionBase->EntryPoint && SectionBase->BaseDllName.Buffer && SectionBase->FullDllName.Buffer && SectionBase->LoadCount ) { DbgPrint("方法四遍历模块名称:%wZ,地址:%8X\n",&(SectionBase->FullDllName),SectionBase->DllBase); num++; } Entry=Entry->Flink; }while(Entry!=(LIST_ENTRY*)Addr);//直到遍历回来 DbgPrint("方法四得到模块总数:%d\n",num); } NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath) { ULONG EntryAddr; _asm { push ecx; lea ecx,[ebp][4];//得到DriverEntry返回地址 mov EntryAddr,ecx; pop ecx; } EntryAddr=*(ULONG*)EntryAddr; DbgPrint("驱动返回地址:%8X\n",EntryAddr); RtlInitUnicodeString(&BaseName,L"ntoskrnl.exe"); DbgPrint("驱动加载成功!\n"); //-------------------------------// Method1(pDriverObject); //-------------------------------// Method2(); //-------------------------------// Method3(); //-------------------------------// Method4(); //-------------------------------// pDriverObject->DriverUnload=DriverUnload; return STATUS_SUCCESS; } NTSTATUS DriverUnload() { DbgPrint("驱动卸除成功\n"); }
0则评论给“[驱动开发]得到内核模块地址的方法”