
Eine einfach zu verwendende Bibliothek zur Emulation von Speicherabbildern. Nützlich für Malware-Analyse (Konfigurationsextraktion, Entpacken) und dynamische Analyse im Allgemeinen (Sandboxing).
Hinweis: Dies ist ein in Arbeit befindlicher Prototyp, bitte behandeln Sie ihn als solchen. Pull-Requests sind willkommen! Sie können sich mit guten ersten Aufgaben vertraut machen.
Eine einfach zu verwendende Bibliothek zum Emulieren von Code in Minidump-Dateien. Hier sind einige Links zu Beiträgen/Videos, die dumpulator verwenden:
Sie können gerne einen Pull-Request senden, um Ihren Artikel hier hinzuzufügen!
Das folgende Beispiel öffnet StringEncryptionFun_x64.dmp (laden Sie eine Kopie hier herunter), weist etwas Speicher zu und ruft die Entschlüsselungsfunktion bei 0x140001000 auf, um den String bei 0x140017000 zu entschlüsseln:
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}'")
Das StringEncryptionFun_x64.dmp wird am Einstiegspunkt des tests/StringEncryptionFun-Beispiels gesammelt. Sie können die kompilierten Binärdateien für StringEncryptionFun hier herunterladen.
from dumpulator import Dumpulator
dp = Dumpulator("StringEncryptionFun_x64.dmp", trace=True)
dp.start(dp.regs.rip)
Dies erstellt StringEncryptionFun_x64.dmp.trace mit einer Liste ausgeführter Anweisungen und einigen hilfreichen Hinweisen beim Wechseln von Modulen usw. Beachten Sie, dass die Ablaufverfolgung die Emulation erheblich verlangsamt und hauptsächlich für das Debugging gedacht ist.
from dumpulator import Dumpulator
dp = Dumpulator("my.dmp")
buf = dp.call(0x140001000)
dp.read_str(buf, encoding='utf-16')
Angenommen, Sie haben die folgende Funktion:
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]
Sie möchten nur den Prolog ausführen und einige Register einrichten:
from dumpulator import Dumpulator
prolog_start = 0x00007FFFC81C06C0
# we want to stop the instruction after the prolog
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)}")
Das quiet-Flag unterdrückt die Protokolle über geladene DLLs und eingerichtete Speicherbereiche (zur Verwendung in Skripten, in denen Sie Protokollüberflutung reduzieren möchten).
Sie können Syscalls mit dem @syscall-Dekorator (neu) implementieren:
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
Alle Syscall-Funktionsprototypen finden Sie in ntsyscalls.py. Es gibt dort auch viele Beispiele zur Nutzung der API.
Um eine vorhandene Syscall-Implementierung zu hooken, können Sie Folgendes tun:
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
)
Seit v0.2.0 gibt es Unterstützung für das einfache Deklarieren eigener Strukturen:
from dumpulator.native import *
class PROCESS_BASIC_INFORMATION(Struct):
ExitStatus: ULONG
PebBaseAddress: PVOID
AffinityMask: KAFFINITY
BasePriority: KPRIORITY
UniqueProcessId: ULONG_PTR
InheritedFromUniqueProcessId: ULONG_PTR
Um diese Strukturen zu instanziieren, müssen Sie eine Dumpulator-Instanz verwenden:
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
Wenn Sie einen Zeigerwert als zweites Argument übergeben, wird die Struktur aus dem Speicher gelesen. Sie können Zeiger mit myptr: P[MY_STRUCT] deklarieren und sie mit myptr[0] dereferenzieren.
Es gibt ein einfaches x64dbg-Plugin namens MiniDumpPlugin Der Befehl minidump wurde seit 2022-10-10 in x64dbg integriert. Um einen Dump zu erstellen, pausieren Sie die Ausführung und führen Sie den Befehl MiniDump my.dmp aus.
python -m pip install dumpulator
Zur Installation aus dem Quellcode:
python setup.py install
Installation für eine Entwicklungsumgebung:
python setup.py develop
Was dumpulator von Sandboxes wie speakeasy und qiling unterscheidet, ist, dass der gesamte Prozessspeicher verfügbar ist. Dies verbessert die Leistung, da Sie große Teile von Malware emulieren können, ohne Unicorn jemals zu verlassen. Zusätzlich müssen nur Syscalls emuliert werden, um eine realistische Windows-Umgebung bereitzustellen (da alles tatsächlich eine legitime Prozessumgebung ist).