《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

浙公网安备 33010602011771号