Skip to main content

Shellcode 编写

测试方法

使用 Keystone 引擎,可以让 Shellcode 的编写更加流畅。Keystone 是一个汇编框架,可以与多种语言绑定,包括 Python。这样的话,我们可以在 Python 脚本中写入汇编代码,然后让 Keystone 框架完成剩下的任务。

我们首先需要通过 pip 安装 keystone 引擎:

pip3 install keystone-engine

然后使用如下的脚本模板,我们需要做的是在 CODE 变量中写入汇编代码。之后,汇编代码会被转换为 Shellcode 并被 CType 库所调用的 API 执行。

import ctypes, struct
from keystone import *

CODE = (
    " start:                             "  #
    "   int3                            ;"  #   Breakpoint for Windbg. REMOVE ME WHEN NOT DEBUGGING!!!!
    "   mov   ebp, esp                  ;"  #
    "   add   esp, 0xfffff9f0           ;"  #   Avoid NULL bytes
............
)

# Initialize engine in X64-64bit mode
ks = Ks(KS_ARCH_X64, KS_MODE_64)
encoding, count = ks.asm(CODE)
print("Encoded %d instructions..." % count)

sh = b""
for e in encoding:
    sh += struct.pack("B", e)
shellcode = bytearray(sh)

ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
                                          ctypes.c_int(len(shellcode)),
                                          ctypes.c_int(0x3000),
                                          ctypes.c_int(0x40))

buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)

ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
                                     buf,
                                     ctypes.c_int(len(shellcode)))

print("Shellcode located at address %s" % hex(ptr))
input("...ENTER TO EXECUTE SHELLCODE...")

ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.c_int(ptr),
                                         ctypes.c_int(0),
                                         ctypes.c_int(0),
                                         ctypes.pointer(ctypes.c_int(0)))

ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht), ctypes.c_int(-1))


定位所需 DLL

TEB 0x60 处,访问到 PEB 的指针:

mov rax, gs:[0x60]

image.png

在 PEB 的 0x18 处,访问到结构体 _PEB_LDR_DATA 的指针:

mov rsi,[rax+0x18]

image.png

访问该 _PEB_LDR_DATA 结构体,里面有多个成员。下半部分的输出给出了更详细的结构体成员信息,其中重要的是 3 个 双向链表,分别是 InLoadOrderModuleListInMemoryOrderModuleList,和 InInitializationOrderModuleList

image.png

InLoadOrderModuleList 按加载顺序显示上一个和下一个模块,InMemoryOrderModuleList 按内存放置顺序显示,InInitializationOrderModuleList 按初始化顺序显示。因此,即便上半部分的输出只告诉我们了 InMemoryOrderModuleList 成员,也是足够了。

InMemoryOrderModuleList 是一个 _LIST_ENTRY 类型的结构体,有着 2 个成员 FlinkBlink,在双向链表中分别用于访问下一个上一个条目:

image.png

 

PEB
|
|---> _PEB_LDR_DATA
      |
      |---> InLoadOrderModuleList (_LIST_ENTRY)
      |     |
      |     |---> _LDR_DATA_TABLE_ENTRY (module 1)
      |     |---> _LDR_DATA_TABLE_ENTRY (module 2)
      |     |---> ...
      |
      |---> InMemoryOrderModuleList (_LIST_ENTRY)
      |     |
      |     |---> _LDR_DATA_TABLE_ENTRY (module 1)
      |     |---> _LDR_DATA_TABLE_ENTRY (module 2)
      |     |---> ...
      |
      |---> InInitializationOrderModuleList (_LIST_ENTRY)
            |
            |---> _LDR_DATA_TABLE_ENTRY (module 1)
            |---> _LDR_DATA_TABLE_ENTRY (module 2)
            |---> ...



定位所需 API



调用 API





参考:

https://www.exploit-db.com/exploits/50291 

https://www.exploit-db.com/exploits/40890