动态加载技术的几种方法

所谓动态加载技术,就是脱离导入表,编写代码来获取API调用
主要实现目标就是动态获取指定DLL的基地址,然后通过查找其导出表以获取API地址
其中获得指定DLL基地址方法有很多,下面列出几种
硬编码方式就不说了,这种办法虽然代码量少但太糟糕,经常出现各种问题
在说明具体方法之前先把4G地址空间的分布说明下
用户态低2GB空间分配
0x00000000~0x0000FFFF     空指针区,用于帮助程序员避免引用错误的指针,访问此处导致访问越权
0x00010000~0x7FFEFFFF     专用进程地址空间
0x7EFDE000~0x7EFDEFFF    用于第一个线程的线程环境块(TEB)调试时可以看到fs寄存器为0x7EFDE000。系统会在这一页前面创建附加TEB(从地址0x7FFDD000开始向上)       
0x7FFDF000~0x7FFDFFFF    进程环境块(有时fs寄存器又会等于7FFDF000,不知道为什么)
0x7FFE0000~0x7FFEFFFF    共享的用户数据页
0x7FFE1000~0x7FFEFFFF    拒绝访问区域(共享用户数据页面以后剩余的64KB)
0x7FFF0000~0x7FFFFFFF    拒绝访问区域,用于防止线程跨越用户/系统空间边界传送缓存区。在变量MmUserProbeAddress中包含此页的起始地址
核心态高2GB空间分配  //取自WINDOWS PE权威指南 ,下面几个地址分布有些少了位数,我是凭着感觉加上去的,不一定正确
0x80000000~0xc0000000    内核执行体,HAL和硬件驱动程序
0xc0000000~0xc0800000    进程页表和超空间
0xc0800000~0xFFFBE000    系统高速缓存,分页缓冲池,非分页缓冲池
0xFFFBE000~0xFFFC0000    崩溃转储驱动程序区域
0xFFFC0000~0xFFFFFFFF    保留给HAL使用
   
一:
从进城地址空间开始搜索,下面贴上WIN32汇编的代码实现
_Handler proc _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatchertext
pushad
mov esi,_lpExceptionRecord
assume esi:ptr EXCEPTIONRECORD
mov edi,_lpContext
assume edi:ptr CONTEXT
mov eax,_lpSEH
push [eax+0ch]
pop [edi].regEbp
push [eax+08]
pop [edi].regEip
push eax
pop [edi].regEsp
assume edi:nothing,esi:nothing
popad
mov eax,ExceptionContinueExecution
ret
_Handler endp

_GetKernel32Base proc uses edi esi ebx dwEsp
call @F
@@:
pop ebx
sub ebx,offset @B

;安装SEH
assume fs:nothing
push ebp
lea eax, [ebx+offset _safeplace]
push eax
lea eax,[ebx + offset _Handler]
push eax
push fs:[0]
mov fs:[0],esp

mov eax,dwEsp
and eax,0ffff0000h      ;预订地址空间区域的分配粒度(64KB),至于4KB了,那是页面大小分配粒度
.while eax>=70000000h
.if word ptr [eax] == IMAGE_DOS_SIGNATURE
mov edi,eax
add edi,[eax+03ch]
.if word ptr [edi] == IMAGE_NT_SIGNATURE
jmp find
.endif
.endif
_safeplace:
sub eax,10000h      ;模块载入基地址分配粒度(64KB)
.endw
mov eax,0
find:
pop fs:[0]
add esp,0ch
ret
_GetKernel32Base endp

start:
mov eax,[esp]                                    ;[esp]即为用户代码的返回地址,这个地址处在CreateProcess函数中,而这个函数在kernel32.dll中,所以可以按照这个地址向下搜索找到kernel32基地址
invoke _GetKernel32Base,eax

也可以直接从用户态最高地址向下搜索,直到找到DLL特征如kernel32.dll的某个函数
;数据段
    .data

szText  db 'kernel32.dll的基地址为%08x',0
szOut   db '%08x',0dh,0ah,0
szBuffer db 256 dup(0)

;代码段
    .code

start:

   call loc0
   db 'GetProcAddress',0  ;特征函数名

loc0:
   pop edx            ;edx中存放了特征函数名所在地址
   push edx
   mov ebx,7ffe0000h  ;从高地址开始

loc1:
   cmp dword ptr [ebx],905A4Dh
   JE loc2   ;判断是否为MS DOS头标志

loc5:
   sub ebx,00010000h

   pushad         ;保护寄存器1
   invoke IsBadReadPtr,ebx,2
   .if eax
     popad        ;恢复寄存器1
     jmp loc5
   .endif
   popad          ;恢复寄存器1

   jmp loc1



loc2:   ;遍历导出表
   mov esi,dword ptr [ebx+3ch] 
   add esi,ebx ;ESI指向PE头
   mov esi,dword ptr [esi+78h]
   nop
 
   .if esi==0
     jmp loc5
   .endif
   add esi,ebx ;ESI指向数据目录中的导出表
   mov edi,dword ptr [esi+20h] ;指向导出表的AddressOfNames
   add edi,ebx ;EDI为AddressOfNames数组起始位置
   mov ecx,dword ptr [esi+18h] ;指向导出表的NumberOfNames
   push esi


   xor eax,eax
loc3:
   push edi
   push ecx
   mov edi,dword ptr [edi]
   add edi,ebx  ;edi指向了第一个函数的字符串名起始
   mov esi,edx  ;esi指向了特征函数名起始
   xor ecx,ecx
   mov cl,0eh  ;特征函数名的长度
   repe cmpsb
   pop ecx
   pop edi
   je loc4    ;找到特征函数,转移
   add edi,4  ;edi移动到下一个函数名所在地址
   inc eax    ;eax为计数
   loop loc3

   jmp loc5
loc4:
   ;特征函数匹配成功,输出模块基地址
    
    invoke wsprintf,addr szBuffer,addr szText,ebx
    invoke MessageBox,NULL,addr szBuffer,NULL,MB_OK
    ret
    end start

二:
从SEH框架开始查找,根据WINDOWS PE权威指南上说的,操作系统结构化异常处理默认分配的的异常处理程序指向kernel32._except_handler3函数,不过经实验在WIN7中这个函数实在ntdll中,所以可能只适用与XP前系统
代码的话可以
assume fs:nothing
mov        eax,fs:[0]
mov        eax,dword ptr [eax+4h]
invoke     _GetKernel32Base,eax        ;这就是上面那个基址搜索函数

三:
从PEB查找
assume fs:nothing
mov eax,fs:[30h]
mov eax,[eax+0ch]
mov esi,[eax+1ch]
lodsd
mov eax,[eax+8]
只需要上面几行代码就可以获得kernel32基地址,具体涉及到TEB,PEB,PEB_LDR_DATA结构,但是用这种方法在WIN7下获得的是kernelbase.dll的基址而不是kernel32,不过这个DLL中也有部分kernel32的函数,虽然没有LoadLibrary函数,但是有LoadLibraryEx函数,所以如果用这种方法的话就应该查找LoadLibraryEx函数地址
不过貌似在哪看到过这种方法不一定有用,因为kernel加载地址可能随机的,不过WIN7,XP都有用。。。。
总之_GetKernel32Base函数搜索基址这种方法最稳定了


下面直接给出动态API调用的hellow world程序吧!
include		windows.inc

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 数据段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		.const

szCaption	db	'恭喜',0
szText		db	'非导入表调用成功!',0
szLoadLibrary	db	'LoadLibraryA',0
szGetProcAddress db	'GetProcAddress',0
szUser32	db	'user32',0
szMessageBox	db	'MessageBoxA',0

		.data?
ALoadLibrary	dd	?
AGetProcAddress dd      ?
AMessageBox	dd	?
dwKernel32Base	dd	?

;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		.code
_Handler proc _lpExceptionRecord,_lpSEH,_lpContext,_lpDispatchertext
	pushad
	mov	esi,_lpExceptionRecord
	assume	esi:ptr EXCEPTIONRECORD
	mov	edi,_lpContext
	assume	edi:ptr CONTEXT
	mov	eax,_lpSEH
	push	[eax+0ch]
	pop	[edi].regEbp
	push	[eax+08]
	pop	[edi].regEip
	push	eax
	pop	[edi].regEsp
	assume	edi:nothing,esi:nothing
	popad
	mov	eax,ExceptionContinueExecution
	ret
_Handler endp

_GetKernel32Base proc uses edi esi ebx dwEsp
	call	@F
	@@:
	pop	ebx
	sub	ebx,offset @B

	;安装SEH
	assume	fs:nothing
	push	ebp
	lea	eax, [ebx+offset _safeplace]
	push	eax
	lea	eax,[ebx + offset _Handler]
	push	eax
	push	fs:[0]
	mov	fs:[0],esp

	mov	eax,dwEsp
	and	eax,0ffff0000h

	.while	eax>=70000000h
		.if word ptr [eax] == IMAGE_DOS_SIGNATURE
			mov	edi,eax
			add	edi,[eax+03ch]
			.if word ptr [edi] == IMAGE_NT_SIGNATURE
				jmp	find
			.endif
		.endif
		_safeplace:
		sub	eax,10000h
	.endw
	mov	eax,0
	find:
	pop	fs:[0]
	add	esp,0ch
	ret
_GetKernel32Base endp

_GetApi	proc	_hModule,_lpszApi
	local	@dwReturn,@dwSize
	pushad
	
	call	@F
	@@:
	pop	ebx
	sub	ebx,@B
	
	assume	fs:nothing
	push	ebp
	push	[ebx+offset error]
	push	[ebx+offset _Handler]
	push	fs:[0]
	mov	fs:[0],esp
	
	mov	edi,_lpszApi
	mov	ecx,-1
	xor	eax,eax
	cld
	repnz	scasb
	sub	edi,_lpszApi
	mov	@dwSize,edi

	mov	esi,_hModule
	add	esi,[esi+3ch]
	assume	esi:ptr IMAGE_NT_HEADERS
	mov	esi,[esi].OptionalHeader.DataDirectory.VirtualAddress
	add	esi,_hModule
	assume	esi:ptr IMAGE_EXPORT_DIRECTORY

	mov	ebx,[esi].AddressOfNames
	add	ebx,_hModule
	xor	edx,edx
	.while  edx <	[esi].NumberOfNames
		push	esi
		mov	edi,[ebx]
		add	edi,_hModule
		mov	esi,_lpszApi
		mov	ecx,@dwSize
		cld
		repz	cmpsb
		.if	!ecx
			pop	esi
			jmp	@F
		.endif
		next:
		pop	esi
		inc	edx
		add	ebx,4
	.endw
	jmp	error
	@@:
	sub	ebx,[esi].AddressOfNames
	sub	ebx,_hModule
	shr	ebx,1
	add	ebx,[esi].AddressOfNameOrdinals
	add	ebx,_hModule
	movzx	eax,word ptr [ebx]
	shl	eax,2
	add	eax,[esi].AddressOfFunctions
	add	eax,_hModule

	mov	eax,[eax]
	add	eax,_hModule
	mov	@dwReturn,eax
	error:
	pop	fs:[0]
	add	esp,0ch
	assume	esi:nothing
	popad
	mov	eax,@dwReturn
	ret
_GetApi endp

start:
		mov	eax,[esp]
		invoke	_GetKernel32Base,eax
		.if	eax
			mov	dwKernel32Base,eax
			invoke	_GetApi,eax, offset szGetProcAddress
			mov	AGetProcAddress,eax
		.endif
		.if	AGetProcAddress
			push	offset szLoadLibrary
			push	dwKernel32Base
			call	AGetProcAddress
			.if	eax
				mov	ALoadLibrary,eax
				push	offset szUser32
				call	eax
				.if	eax
					push	offset szMessageBox
					push	eax
					call	AGetProcAddress
					.if	eax
						mov	AMessageBox,eax
					.endif
				.endif
			.endif
		.endif
		.if	AMessageBox
			push	MB_YESNO
			push	offset szCaption
			push	offset szText
			push	NULL
			call	AMessageBox
		.endif
		ret;invoke	ExitProcess,NULL
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		end	start

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值