注意:这是一个正在开发中的原型,请视其如此。欢迎提交 Pull Request!您可以从 good first issues 开始入手。
一个易于使用的库,用于模拟 minidump 文件中的代码。以下是一些使用 dumpulator 的帖子/视频链接:
欢迎提交 Pull Request 将您的文章添加到这里!
下面的示例打开 StringEncryptionFun_x64.dmp(在此下载副本 here),分配一些内存,并调用 0x140001000 处的解密函数来解密 0x140017000 处的字符串:
from dumpulator import Dumpulator
dp = Dumpulator("StringEncryptionFun_x64.dmp")
temp_addr = dp.allocate(256)
dp.call(0x140001000, [temp_addr, 0x140017000])
decrypted = dp.read_str(temp_addr)
print(f"decrypted: '{decrypted}'")
StringEncryptionFun_x64.dmp 是在 tests/StringEncryptionFun 示例的入口点收集的。您可以从 here 获取 StringEncryptionFun 的编译二进制文件。
from dumpulator import Dumpulator
dp = Dumpulator("StringEncryptionFun_x64.dmp", trace=True)
dp.start(dp.regs.rip)
这将创建 StringEncryptionFun_x64.dmp.trace,包含执行的指令列表以及在切换模块等时的一些有用指示。请注意,跟踪会显著降低仿真速度,主要用于调试。
from dumpulator import Dumpulator
dp = Dumpulator("my.dmp")
buf = dp.call(0x140001000)
dp.read_str(buf, encoding='utf-16')
假设您有以下函数:
00007FFFC81C06C0 | mov qword ptr [rsp+0x10],rbx ; prolog_start
00007FFFC81C06C5 | mov qword ptr [rsp+0x18],rsi
00007FFFC81C06CA | push rbp
00007FFFC81C06CB | push rdi
00007FFFC81C06CC | push r14
00007FFFC81C06CE | lea rbp,qword ptr [rsp-0x100]
00007FFFC81C06D6 | sub rsp,0x200 ; prolog_end
00007FFFC81C06DD | mov rax,qword ptr [0x7FFFC8272510]
您只想执行序言并设置一些寄存器:
from dumpulator import Dumpulator
prolog_start = 0x00007FFFC81C06C0
# 我们希望在序言之后停止执行
prolog_end = 0x00007FFFC81C06D6 + 7
dp = Dumpulator("my.dmp", quiet=True)
dp.regs.rcx = 0x1337
dp.start(begin=prolog_start, end=prolog_end)
print(f"rsp: {hex(dp.regs.rsp)}")
quiet 标志会抑制有关已加载 DLL 和已设置内存区域的日志(用于脚本中减少日志噪音)。
您可以使用 @syscall 装饰器(重新)实现系统调用:
from dumpulator import *
from dumpulator.native import *
from dumpulator.handles import *
from dumpulator.memory import *
@syscall
def ZwQueryVolumeInformationFile(dp: Dumpulator,
FileHandle: HANDLE,
IoStatusBlock: P[IO_STATUS_BLOCK],
FsInformation: PVOID,
Length: ULONG,
FsInformationClass: FSINFOCLASS
):
return STATUS_NOT_IMPLEMENTED
所有系统调用函数原型可以在 ntsyscalls.py 中找到。那里也有许多关于如何使用 API 的示例。
要 hook 一个现有的系统调用实现,可以执行以下操作:
import dumpulator.ntsyscalls as ntsyscalls
@syscall
def ZwOpenProcess(dp: Dumpulator,
ProcessHandle: Annotated[P[HANDLE], SAL("_Out_")],
DesiredAccess: Annotated[ACCESS_MASK, SAL("_In_")],
ObjectAttributes: Annotated[P[OBJECT_ATTRIBUTES], SAL("_In_")],
ClientId: Annotated[P[CLIENT_ID], SAL("_In_opt_")]
):
process_id = ClientId.read_ptr()
assert process_id == dp.parent_process_id
ProcessHandle.write_ptr(0x1337)
return STATUS_SUCCESS
@syscall
def ZwQueryInformationProcess(dp: Dumpulator,
ProcessHandle: Annotated[HANDLE, SAL("_In_")],
ProcessInformationClass: Annotated[PROCESSINFOCLASS, SAL("_In_")],
ProcessInformation: Annotated[PVOID, SAL("_Out_writes_bytes_(ProcessInformationLength)")],
ProcessInformationLength: Annotated[ULONG, SAL("_In_")],
ReturnLength: Annotated[P[ULONG], SAL("_Out_opt_")]
):
if ProcessInformationClass == PROCESSINFOCLASS.ProcessImageFileNameWin32:
if ProcessHandle == dp.NtCurrentProcess():
main_module = dp.modules[dp.modules.main]
image_path = main_module.path
elif ProcessHandle == 0x1337:
image_path = R"C:\Windows\explorer.exe"
else:
raise NotImplementedError()
buffer = UNICODE_STRING.create_buffer(image_path, ProcessInformation)
assert ProcessInformationLength >= len(buffer)
if ReturnLength.ptr:
dp.write_ulong(ReturnLength.ptr, len(buffer))
ProcessInformation.write(buffer)
return STATUS_SUCCESS
return ntsyscalls.ZwQueryInformationProcess(dp,
ProcessHandle,
ProcessInformationClass,
ProcessInformation,
ProcessInformationLength,
ReturnLength
)
自 v0.2.0 起,支持轻松声明您自己的结构体:
from dumpulator.native import *
class PROCESS_BASIC_INFORMATION(Struct):
ExitStatus: ULONG
PebBaseAddress: PVOID
AffinityMask: KAFFINITY
BasePriority: KPRIORITY
UniqueProcessId: ULONG_PTR
InheritedFromUniqueProcessId: ULONG_PTR
要实例化这些结构体,您必须使用 Dumpulator 实例:
pbi = PROCESS_BASIC_INFORMATION(dp)
assert ProcessInformationLength == Struct.sizeof(pbi)
pbi.ExitStatus = 259 # STILL_ACTIVE
pbi.PebBaseAddress = dp.peb
pbi.AffinityMask = 0xFFFF
pbi.BasePriority = 8
pbi.UniqueProcessId = dp.process_id
pbi.InheritedFromUniqueProcessId = dp.parent_process_id
ProcessInformation.write(bytes(pbi))
if ReturnLength.ptr:
dp.write_ulong(ReturnLength.ptr, Struct.sizeof(pbi))
return STATUS_SUCCESS
如果您将指针值作为第二个参数传递,则结构体将从内存中读取。您可以使用 myptr: P[MY_STRUCT] 声明指针,并使用 myptr[0] 解引用它们。
有一个简单的 x64dbg 插件可用,名为 MiniDumpPlugin 自 2022-10-10 起,minidump 命令已集成到 x64dbg 中。要创建转储,请暂停执行并执行命令 MiniDump my.dmp。
python -m pip install dumpulator
从源代码安装:
python setup.py install
为开发环境安装:
python setup.py develop
dumpulator 与 speakeasy 和 qiling 等沙箱的不同之处在于,完整的进程内存是可用的。这提高了性能,因为您可以在不离开 unicorn 的情况下仿真恶意软件的大部分内容。此外,只需要仿真系统调用即可提供真实的 Windows 环境(因为实际上一切都是合法的进程环境)。