網上說的比較常見的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");
}