原理是這樣的:
每個使用者態進程的WinMain代碼都是由啟動代碼(CRTStartup)調用的,而啟動代碼是kernel32.dll裡的某個函數調用的,當然也可能沒有WinMain和WinMainCRTStart之類的函數(如Delphi編譯出的代碼),但進程啟動後,kernel32.dll中的一個的未知代碼位置一定儲存在堆棧裡的,所以從頂往底測試堆棧裡的元素,一定可以找到kernel32.dll的基址,寫了一小段代碼,在Win XP SP2,VC++ 6.0 SP5下測試通過,可以找到我機器上的kernel32.dll的基址是0x7C800000.
CODE:
#include <stdio.h>
#include <conio.h>
#include <windows.h>
DWORD GetKernel32(void)
{
DWORD TryAddr=0;
PIMAGE_DOS_HEADER pdos=NULL;
PIMAGE_NT_HEADERS pnt=NULL;
PIMAGE_EXPORT_DIRECTORY pied=NULL;
DWORD *pKernel=NULL;
int i=0;
PDWORD pEsp=(PDWORD)&pEsp;
BOOL bFound=FALSE;
do
{
__try
{
TryAddr=*pEsp;
for(i=0;i<4;i++)
{
TryAddr&=0xffff0000<<(i<<2);
pdos=(PIMAGE_DOS_HEADER)TryAddr;
pnt=(PIMAGE_NT_HEADERS)(TryAddr+pdos->e_lfanew);
if(pdos->e_magic=='ZM'&&pnt->Signature=='EP')
{
pied=(PIMAGE_EXPORT_DIRECTORY)(TryAddr+pnt->OptionalHeader.DataDirectory[0].VirtualAddress);
pKernel=(DWORD *)(TryAddr+pied->Name);
//KERNEL32 or Kernel32
if((*pKernel=='NREK'&&*(pKernel+1)=='23LE')||
(*pKernel=='nreK'&&*(pKernel+1)=='23le'))
{
bFound=TRUE;
break;
};//end if pKernel
}//end if pdos
}//end for i
}
__except(1)
{
NULL;
}
pEsp++;
}while(!bFound);
return TryAddr;
}
int main(void)
{
printf("KERNEL32.DLL at:0x%.8X/n",GetKernel32());
return getchar();
}