Kernel32加载地址查找的基本方法

搜索内存中的API有许多方法,主要的是要找到KERNEL32.DLL的加载地址,常用的基本方法也就几种。

1、暴力搜索KERNEL32.DLL,同时需要处理内存访问异常

2、通过线程初始化时压入堆栈的ExitThread的地址来得到KERNEL32.DLL的地址。

3、通过SEH异常链表(Windows Xp)

4、通过TEB得到PEB结构的地址,然后获得PEB_LDR_DATA的地址,然后遍历模块链表,得到KERNEL32.DLL的地址。

一、一般DLL加载的地址会在0x70000000到0x80000000之间所以我们可以直接搜索这段区域的内存,但是在这段区域中有些内存区域是访问异常的,所以会用到SEH异常处理的解决方法。(winxp 正常 ,win7 , win8 debug模式下正常,原因 win7下的SEH(结构化异常),解决办法 SEH和SafeSEH

typedef struct _EXCEPTION_INFO
{
	unsigned long Eip;
	unsigned long ExceptionCode;
}EXCEPTIONINFO, *PEXCEPTIONINFO;

typedef struct _EXCEPTION_TASK
{
	unsigned long	prev;//FS:[0]
	unsigned long	handler;
	PEXCEPTIONINFO	pExceptInfo;
}EXCEPTION_TASK, *PEXCEPTION_TASK;

EXCEPTION_DISPOSITION __cdecl ExceptionHandler(
struct _EXCEPTION_RECORD *ExceptionRecord,		// 异常发生时的异常码
	 void * EstablisherFrame,                    // 异常时的ESP值
	 struct _CONTEXT *ContextRecord ,            // 异常发生时的各个寄存器的值
	 void * DispatcherContext)
{
	
	PEXCEPTION_TASK pExceptTask = (PEXCEPTION_TASK)EstablisherFrame;			//堆栈的结构 与 ExceptionInfo结构相对应, 异常时的ESP	
	pExceptTask->pExceptInfo->ExceptionCode = ExceptionRecord->ExceptionCode;	 
	ContextRecord->Esp = (DWORD)EstablisherFrame;								 
	ContextRecord->Eip = pExceptTask->pExceptInfo->Eip;							 
	return ExceptionContinueExecution;											
}

BOOL IsReadable(DWORD dwModuleBase)
{
	EXCEPTIONINFO ExceptionInfo = { 0 };
	ExceptionInfo.ExceptionCode = -1;

	 DWORD	pExceptionEip = (DWORD)&ExceptionInfo.Eip;
	
	/*
	在堆栈中构造EXCEPTION_TASK结构,通过异常传递ESP指针,
	可以访问其他函数的局部变量
	*/
	__asm
	{
		pushad
		mov		eax,	 pExceptionEip
		lea		ebx,	ErrRetAddr
		mov		dword ptr[eax],	  ebx					//给Eip赋出异常时的返回值
		lea		eax,	 ExceptionInfo					
		push	eax										
		push	ExceptionHandler						//异常回调函数
		push	dword ptr fs:[0]						//上一个异常链表头指针
		mov		fs:[0],	 esp						//安装异常处理例程
		//测试基址处的内存是否可读,异常发生处
		mov esi, dwModuleBase
		mov ecx, 10
		rep lodsb		
ErrRetAddr:
		pop		dword ptr fs:[0]						//卸载已经安装的异常处理例程
		pop		eax								//add  esp, 8		平衡堆栈
		pop		eax
		popad
	}

	if (-1 == ExceptionInfo.ExceptionCode)
	{
		return TRUE;//未出现异常
	}
	else
	{
		return FALSE;//出现异常
	}
}

BOOL IsDLL(DWORD dwModuleBase)
{
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwModuleBase;
	PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
	if ((pDosHeader->e_magic == IMAGE_DOS_SIGNATURE) && (pNtHeader->Signature == IMAGE_NT_SIGNATURE))
	{
		if (pNtHeader->FileHeader.Characteristics & 0x2000)//DLL文件特点
		{
			return TRUE;
		}
	}
	return FALSE;
}
//暴力搜索KERNEL32.DLL ---- API
DWORD searchApi()
{
	int i = 0;
	PIMAGE_DOS_HEADER pDosHeader;
	PIMAGE_NT_HEADERS pNtHeader;
	PIMAGE_EXPORT_DIRECTORY pExprotDirectory;
	DWORD dwModuleBase = 0x7FFF0000;

	while (dwModuleBase >= 0x70000000)
	{
		//判断内存是否可以读写 并处理读写产生的异常
		if (!IsReadable(dwModuleBase))
		{
			dwModuleBase -= 0x10000;
			continue;
		}

		//判断是否是dll文件
		if (!IsDLL(dwModuleBase))
		{
			dwModuleBase -= 0x10000;
			continue;
		}
		pDosHeader = (PIMAGE_DOS_HEADER)dwModuleBase;
		pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
		// 3查找Kernel32.DLL地址		
		if ((pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) &&\
			(pNtHeader)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)
		{

			pExprotDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pDosHeader +\
				pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
			char *szDllName = (char*)((DWORD)pDosHeader + pExprotDirectory->Name);
			
			if (0 == _strcmpi(szDllName, "Kernel32.dll"))
			{
				//printf("%s\n", szDllName);
				return dwModuleBase;
			}
		}
		dwModuleBase -= 0x10000;
	}

}


二、线程被初始化时ExitThread被压人堆栈,以便线程通过ret返回时可以执行ExitThread退出线程。而ExitThread是从KERNEL32.DLL中导出的。

dwKernel32Base   dw   0
        mov edi, [esp]		                                        ; edi = 堆栈顶
	and edi, 0ffff0000h					        ; 用 AND 获得初始页
	.while TRUE
		.if word ptr [edi] == IMAGE_DOS_SIGNATURE		; 等于“MZ”吗?
			mov esi, edi								; Yes, next...
			add esi, [esi + IMAGE_DOS_HEADER.e_lfanew]	; 就是 esi + 3ch
			.if word ptr [esi] == IMAGE_NT_SIGNATURE	; 等于“PE”吗?
				mov dwKernel32Base, edi						; Yes, we got it.
				.break
			.endif
		.endif
		;以下等同于sub edi, 010000h,即每次减少64k:
		dec edi
		xor di, di
		.break	.if edi < 070000000h	; 基地址一般不可能小于70000000h
	.endw
 

三、遍历SEH异常链表,获得EXCEPTION_REGISTRATION结构prev为-1的异常处理过程地址,这个异常处理过程地址是位于kernel32.dll,通过它搜索得到kernel32.dll的基地址。我们都知道[fs:0]的ExceptionList 指向EXCEPTION_REGISTRATION结构,所以通过[fs:0]获得EXCEPTION_REGISTRATION结构后,判断prev成员是否是-1,如果是的话则取异常处理过程地址,然后进行搜索。此方法在Windows 7、Windows8  下查找到的是ntdll.dll,而不再是KERNEL32.DLL

struct EXCEPTION_REGISTRATION
      prev          dd         ? 
      handler     dd         ? 
ends

#include "windows.h"
unsigned  int  GetKernelBase()
{
	struct EXCEPTION_REGISTRATION
	{
		unsigned int prev;
		unsigned int handler;
	};
	unsigned int Exception;
	__asm
	{
			push eax
			mov  eax, dword ptr fs:[0]
			mov  Exception, eax
			pop  eax
	}
	EXCEPTION_REGISTRATION *pExceptioRegist = (EXCEPTION_REGISTRATION*)Exception;
	while(-1 != pExceptioRegist->prev)
	{
		pExceptioRegist = (EXCEPTION_REGISTRATION*)pExceptioRegist->prev;
	}
	unsigned handler = pExceptioRegist->handler;
	unsigned kernelBase = handler & 0xFFFF0000;
	PIMAGE_DOS_HEADER pDosHeader = 0;
	PIMAGE_NT_HEADERS pNtHeader = 0;
	while(1)
	{
		pDosHeader = (PIMAGE_DOS_HEADER)kernelBase;
		pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + (unsigned)pDosHeader);
		if (IMAGE_DOS_SIGNATURE == pDosHeader->e_magic && IMAGE_NT_SIGNATURE == pNtHeader->Signature) break;
		kernelBase -= 0x10000;
	}
	return kernelBase;
}


四、通过TEB获得PEB结构的地址,TEB是线程环境块(Thread Environment Block)结构, 我们的fs段选择子所对应的段指向TEB,也就是fs:0(注意这里可不是“[fs:0]”)指向TEB.那么TEB的ProcessEnvironmentBlock结构成员指向我们的PEB进程环境块结构(Process Environment Block),然后通过PEB结构来获得PEB_LDR_DATA。

通过Windbg的dt命令查看TEB的结构

nt!_TEB 
	+0x000 NtTib : _NT_TIB 
	+0x01c EnvironmentPointer : Ptr32 Void 
	+0x020 ClientId : _CLIENT_ID 
	+0x028 ActiveRpcHandle : Ptr32 Void 
	+0x02c ThreadLocalStoragePointer : Ptr32 Void 
	+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
TEB结构的0x30偏移处存储的我们的PEB结构的地址,PEB结构如下:

nt!_PEB 
	+0x000 InheritedAddressSpace : UChar 
	+0x001 ReadImageFileExecOptions : UChar 
	+0x002 BeingDebugged : UChar 
	+0x003 SpareBool : UChar 
	+0x004 Mutant : Ptr32 Void 
	+0x008 ImageBaseAddress : Ptr32 Void 
	+0x00c Ldr : Ptr32 _PEB_LDR_DATA

struct _LIST_ENTRY
prev          dd     ?
handler     dd     ?


用Windbg查看:

nt!_PEB_LDR_DATA
	+0x000 Length : Uint4B 
	+0x004 Initialized : UChar 
	+0x008 SsHandle : Ptr32 Void 
	+0x00c InLoadOrderModuleList : _LIST_ENTRY 
	+0x014 InMemoryOrderModuleList : _LIST_ENTRY 
	+0x01c InInitializationOrderModuleList : _LIST_ENTRY
	+0x024 EntryInProgress : Ptr32 Void

我们看到这个结构中的模块立标有3个_LIST_ENTRY结构,它们分别是

 InLoadOrderModuleList (加载顺序模块列表) 

InMemoryOrderModuleList(内存顺序模块排列) 

InInitializationOrderModuleList(初始化顺序模块列表)。

并且这三个链表的结点是均是指向LDR_MODULE.

typedef struct _LDR_MODULE {
	LIST_ENTRY InLoadOrderModuleList; // +0x00 
	LIST_ENTRY InMemoryOrderModuleList; // +0x08 
	LIST_ENTRY InInitializationOrderModuleList; // +0x10 
	PVOID BaseAddress; // +0x18 
	PVOID EntryPoint; // +0x1c 
	ULONG SizeOfImage; // +0x20 
	UNICODE_STRING FullDllName; // +0x24 
	UNICODE_STRING BaseDllName; // +0x2c
	.....
}LDR_MODULE, *PLDR_MODULE;

我们一般取它的初始化顺序结构(InInitializationOrderModuleList)的Flink成员指向的_LDR_MODULE结构的BaseAddress成员则为我们需要的基地址,Window Xp 及以前的系统第二个为KERNEL32.DLL,从Windows 7开始第三个链表为KERNEL32.DLL。

在Win8下:

unsigned KernelBase;
	__asm
	{
		push eax
		mov eax, fs:[30h] ;Get Peb 
		mov eax, [eax+0ch] ;Get _PEB_LDR_DATA 
		mov eax, [eax+1ch];
		mov eax, [eax] 
		mov eax, [eax] 
		mov eax, [eax+8] ;
		mov KernelBase, eax
			pop eax
	}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值