所谓动态加载技术,就是脱离导入表,编写代码来获取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