
Простая в использовании библиотека для эмуляции дампов памяти. Полезна для анализа вредоносного ПО (извлечение конфигураций, распаковка) и динамического анализа в целом (песочница).
Примечание: Это прототип в разработке, пожалуйста, относитесь к нему соответственно. Pull request'ы приветствуются! Вы можете начать с хороших первых задач
Простая в использовании библиотека для эмуляции кода в minidump-файлах. Вот несколько ссылок на публикации/видео, использующие dumpulator:
Не стесняйтесь отправить pull request, чтобы добавить сюда свою статью!
В приведённом ниже примере открывается StringEncryptionFun_x64.dmp (скачать копию здесь), выделяется память и вызывается функция дешифрования по адресу 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. Скомпилированные бинарники 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.
Чтобы перехватить существующую реализацию системного вызова, можно сделать следующее:
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: