4. hash扫描获得api函数地址
今天这篇文章也是病毒编写中很重要的一篇文章,“hash扫描获得api函数地址”,通过它我们的病毒就可以在宿主程序中调用相应平台的库函数。呼,说白一点就是实现一个自己的GetProcAddress函数,然后通过上节课的所讲解的思路获得Kernel32基地址,然后来搜索api函数地址。
这篇文章我分为2个栏目:
1. 搜索获得api函数地址的实现。
2. hash算法搜索获得api函数地址的实现。
;;;;写文章比写代码累多了, 希望辛劳的成果能使大家有所收获,也不枉我此套专题。。。。。。。
;-------------------------------------------------
1. 搜索获得api函数地址的实现:
Windows和之前的dos最大的一个特色就是采用了动态链接库, 这样我们省了很多的内存。我们可以想象一下如果我们的程序都采用静态库的话,那么我们的所有程序所占内存是相当庞大的,而
Windows采用了动态链接库(这样保证了物理内存仅有一份此动态链接库的copy),这些动态链接库分别提供给我们用户了各种各样的编程接口来实现相应的功能,当我们用户程序调用它时必须包含相应的
库文件、函数名称,这样我们的PE LOADER在加载时才会将相应的动态链接库加载我们的进程的内存空间(实际上windows是通过分页机制将这些DLL的物理内存地址指向我们程序虚拟空间的页表中,这样
我们共享的是同一物理内存)。
那么我们上面介绍了,windows通过动态链接库提供给我们用户了各种各样的编程接口函数,那么一般的动态链接库的后缀是“.dll”,当然其他的后缀也可以,例如“.exe”, “.sys”,只不
过它们不常用罢了。因为加载器判断的不仅仅是文件后缀名,还有我们的文件结构。
所以一般我们调用某个动态库的函数,我们必须增加这个动态库的导入表结构,这样我们的windows 加载器才会把这个动态库加载到我们的内存空间,并修正导入表结构的导入函数地址,以便
我们的程序能正常的调用函数。那么这个动态链接库是如何输出函数来供我们的用户程序调用呢?它实际上是采用输出表结构来描述本dll需要导出哪些函数来供其他的程序调用,这样其他的用户程序才
能正常的调用此动态链接库的输出函数。
那么关键的点我们已经知道了,那就是动态链接库是采用输出表结构来描述导出函数。那么如果我们调用某动态链接库的输出函数,首先我们肯定是要查找到这些输出函数的地址?在那里查找
?我们肯定是在输出表结构。好明白这点后,我们来看输出表结构。
struct IMAGE_EXPORT_DIRECTORY
Characteristics dd ? ;未使用
TimeDateStamp dd ? ;文件生成时间
MajorVersion dw ? ;主版本号,一般为0
MinorVersion dw ? ;次版本号,一般为0
nName dd ? ;模块的真实名称
nBase dd ? ;基数, 加上序数就是函数地址数组的索引值
NumberOfFunctions dd ? ;AddressOfFunctions阵列的元素个数
NumberOfNames dd ? ;AddressOfNames阵列的元素个数
AddressOfFunctions dd ? ;指向函数地址数组
AddressOfNames dd ? ;函数名字的指针地址
AddressOfNameOrdinals dd ? ;指向输出序列号数组
ends
为了更好的理解输出表结构,我们采用fasm编写一段自己定义输出表结构的dll代码。
format PE GUI 4.0 DLL
include 'win32ax.inc'
entry __DllEntry
.text
;++
;
; BOOL
; DllMain(
; IN HINSTANCE hDllHandle,
; IN DWORD nReason,
; IN LPVOID Reserved
; )
;
; Routine Description:
;
; 测试文件是否是PE文件格式。
;
; Arguments:
;
; (esp) - return address
;
; Data (esp+4) - hDllHandle
; (esp+8) - nReason
; (esp+12)- Reserved
;
; Return Value:
;
; eax = TRUE, initialization succeeds; eax = FALSE, initialization fails。
;
;--
__DllEntry:
xor eax, eax
inc eax
ret 4*3
__MyMessageBox:
xor eax, eax
push eax
@pushsz 'Dll'
@pushsz '一个dll自定导出表结构例子'
push eax
call [MessageBox]
ret
.idata
section '.edata' export data readable
__IMAGE_EXPORT_DIRECTORY:
dd 0, 0, 0, rva szName, 0, 1, 1
dd rva Address_Tab
dd rva FuncName_Tab
dd rva Ordinals_Tab
;dll name
szName db 'Msg.dll', 0
;
Address_Tab:
dd rva __MyMessageBox ;取__MyMessageBox过程 rva地址
FuncName_Tab:
dd rva ($+4) ; ($ + 4) ptr "MyMessageBox"
db 'MyMessageBox', 0
Ordinals_Tab:
dw 0
.fixups
;++
;
; int
; GetApi(
; IN HINSTANCE hModule,
; IN char * lpApiString,
; )
;
; Routine Description:
;
; 获取指定函数的内存地址
;
; Arguments:
;
; (esp) - return address
;
; Data (esp+4) - hDllHandle
; (esp+8) - lpApiString
; Return Value:
;
; eax -> Function Mem Address。
;
;--
GetApi:
pop edx
pop eax ;hModule
pop ecx ;lpApiString
push edx
pushad
mov ebx, eax ;hModule ebx
mov edi, ecx ;lpApiString edi
xor al, al
.Scasb:
scasb
jnz .Scasb
dec edi
sub edi, ecx
xchg edi, ecx ; edi = lpApiString, ecx = ApiLen
mov eax, [ebx+3ch]
mov esi, [ebx+eax+78h] ;Get Export Rva
lea esi, [esi+ebx+IMAGE_EXPORT_DIRECTORY.NumberOfNames]
lodsd
xchg eax, edx ; edx = NumberOfNames
lodsd
push eax ; [esp] = AddressOfFunctions
lodsd
xchg eax, ebp
lodsd
xchg eax, ebp ; ebp = AddressOfNameOrdinals, eax = AddressOfNames
add eax, ebx
mov [esp+4*6], edi ;临时存储
mov [esp+4*5], ecx ;临时存储
.LoopScas:
dec edx
jz .Ret
mov esi, [eax+edx*4]
add esi, ebx
repz cmpsb
jz .GetAddr
mov edi, [esp+4*6]
mov ecx, [esp+4*5]
jmp .LoopScas
.GetAddr:
shl edx, 1
add ebp, edx
movzx eax, word [ebp+ebx]
shl eax, 2
add eax, [esp]
mov eax, [ebx+eax]
add eax, ebx
.Ret:
pop ecx
mov [esp+4*7], eax
popad
ret
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)