《Oday安全:软件漏洞分析技术》之API的动态加载

一、引言  

  在编写ShellCode的过程中需要动态加载DLL和API函数,根据ShellCode的运行环境,可以得到API动态加载代码的三点基本要求:1、具备通用性。2、具备可移植性。3、代码尽量小。

  1、通用性。代码能在不同平台间使用。如xp、vista和win7.

  2、可移植性。代码能被加载至任意地址并执行。

  3、代码尽量小。在完成相同功能的前提下,使得生成后的代码尽量小。

二、技术原理

  1、通用性的实现。

      Windows所有的应用程序都会加载ntdll.dll和kernel32.dll这两个核心模块。实现动态加载API的两个核心函数 LoadLibraryA和GetProcAddress就位于kernel32.dll中,要获得这两个函数的前提条件是获取kernel32.dll的地址。

  主要思路是通过访问FS->TEB->PEB->PEB_LDR_DATA->InInitializationOrderModuleList来获取kernel32.dll的地址,由于kernel32.dll的地址总是位于位于InInitializationOrderModuleList结构的第二个链表节点中,因此可以直接获取到kernel32.dll装载的地址。

  然后根据kernel32.dll基址访问PE文件头的导出表结构找到对应的API。

  2、可移植性

  ShellCode可能加载到任意地址,可移植性的关键在于代码中所有数据和代码不能有绝对地址引用,特别是模块名和函数名,它们必须采用特殊的存储方式来达到这一要求。

  调用LoadLibraryA需要传递一个包含被调用模块C风格字符串的指针,考虑ShllCode的约束条件(不能存在绝对地址引用),主要的思路是将单字节的模块名当做4字节的数据来看待。直接把数据压入栈中,由于栈的生长方向是从高地址到地址和访问数据的方向(从低地址到高地址)不同,会使函数的调用失败,因此需要将数据逆向压入栈中。

  eg: "user32.dll"的二进制存储代码为:

  0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00

  数据长度为11,不是4的倍数,对于不足4的部分用0作为填充。填充后的二进制代码为

  0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2E, 0x64, 0x6C, 0x6C, 0x00, 0x00

  逆向后的二进制代码为

  0x00, 0x00, 0x6C, 0x6C, 0x64, 0x2E, 0x32, 0x33, 0x72, 0x65, 0x73, 0x75

  转换为DWORD为

  0x00006C6C, 0x642E3233, 0X72657375

  依次将3个DWORD压入栈中就得到了C风格字符串"user32.dll"

  查找API使用API名的hash值作为索引,通过比较hash值来确认函数是否为需要查找的值。

  hash算法根据API名由数字和字母组成这一特点来设计。ASII编码使用了 8个比特中的低7位,最高位始终为0。所以ASII中的字符有效的位数为7,因此我们选用7作为运算因子。算法的设计思路是使数据中的每一位都参与到运算中,以下给出汇编代码。

hash_loop:
    xor     edx, edx
next_byte: 
    lodsb
    movzx   eax, al
    ror     edx, 7
    add     edx, eax
    test    eax, eax
    jne     next_byte    

  3、代码尽量小

   (1)尽量短的指令:loadsb, loadsw, loadsd, stosb, stosw, stosd等串传送指令替代mov指令。xchg指令交换两个变量,在特殊情况下可以用来替代mov指令。

  (2)对api函数名尽量使用hash值的方式存储,可以节省大量空间。

三、最终代码

  使用MASM32进行开发,需要包含windows.inc

 1 ;**************************************************************************
 2 ; _GetFunctionAddress
 3 ; in    [esp]:function name 32-bit hash
 4 ;       ebx:module handle
 5 ; out   eax:function address
 6 _GetFunctionAddress proc
 7     cld
 8     mov     edi, ebx
 9     add     edi,[edi+3ch]
10     assume  edi:ptr IMAGE_NT_HEADERS
11     mov     edi,[edi].OptionalHeader.DataDirectory.VirtualAddress
12     add     edi, ebx
13     assume  edi:ptr IMAGE_EXPORT_DIRECTORY
14     xor     ecx, ecx
15 next_name:
16     mov     esi, [edi].AddressOfNames
17     add     esi, ebx
18     mov     esi, [esi+ecx*4]
19     add     esi, ebx
20 ;**************************************************************************
21 ; calculate hash code
22 ; in     esi : string
23 ; out    edx : hash code
24 hash_loop:
25     xor     edx, edx
26 @@: lodsb
27     movzx   eax, al
28     ror     edx, 7
29     add     edx, eax
30     test    eax, eax
31     jne     @B
32     cmp     edx, [esp+4]
33     je      find_api_address
34     inc     ecx
35     jmp     next_name
36 find_api_address:    
37     dec     ecx
38     mov     eax, [edi].AddressOfNameOrdinals
39     add     eax, ebx
40     movzx   eax, word ptr [eax+ecx*2]
41     add     ax,  word ptr [edi].nBase
42     mov     esi, [edi].AddressOfFunctions
43     add     esi, ebx
44     mov     eax, [esi+eax*4]
45     add     eax, ebx
46     ret
47 _GetFunctionAddress endp 

 

 

 

posted @ 2013-04-11 01:43  riceHuang  阅读(373)  评论(0)    收藏  举报