原理是这样的:
每个用户态进程的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();
}