
Una libreria facile da usare per emulare dump di memoria. Utile per l'analisi malware (estrazione di configurazioni, unpacking) e l'analisi dinamica in generale (sandboxing).
Nota: Questo è un prototipo in fase di sviluppo, trattatelo come tale. Le pull request sono benvenute! Potete iniziare con buone prime issue
Una libreria facile da usare per emulare codice in file minidump. Ecco alcuni link a post/video che utilizzano dumpulator:
Sentitevi liberi di inviare una pull request per aggiungere il vostro articolo qui!
L'esempio seguente apre StringEncryptionFun_x64.dmp (scaricarne una copia qui), alloca della memoria e chiama la funzione di decifratura all'indirizzo 0x140001000 per decifrare la stringa all'indirizzo 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}'")
Il file StringEncryptionFun_x64.dmp viene raccolto al punto di ingresso dell'esempio tests/StringEncryptionFun. Potete ottenere i binari compilati per StringEncryptionFun qui
from dumpulator import Dumpulator
dp = Dumpulator("StringEncryptionFun_x64.dmp", trace=True)
dp.start(dp.regs.rip)
Questo creerà StringEncryptionFun_x64.dmp.trace con un elenco di istruzioni eseguite e alcune indicazioni utili quando si cambia modulo, ecc. Nota che il tracciamento rallenta significativamente l'emulazione ed è principalmente pensato per il debugging.
from dumpulator import Dumpulator
dp = Dumpulator("my.dmp")
buf = dp.call(0x140001000)
dp.read_str(buf, encoding='utf-16')
Supponiamo di avere la seguente funzione:
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]
Vogliamo eseguire solo il prologo e impostare alcuni registri:
from dumpulator import Dumpulator
prolog_start = 0x00007FFFC81C06C0
# vogliamo fermarci all'istruzione dopo il prologo
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)}")
Il flag quiet sopprime i log relativi ai DLL caricati e alle regioni di memoria impostate (utile per script in cui si vuole ridurre la quantità di log).
Potete (re)implementare le syscall usando il decoratore @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
Tutti i prototipi delle funzioni syscall si trovano in ntsyscalls.py. Ci sono anche molti esempi su come utilizzare l'API.
Per agganciare una syscall già implementata, si può fare quanto segue:
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
)
Dalla v0.2.0 è disponibile il supporto per dichiarare facilmente le proprie strutture:
from dumpulator.native import *
class PROCESS_BASIC_INFORMATION(Struct):
ExitStatus: ULONG
PebBaseAddress: PVOID
AffinityMask: KAFFINITY
BasePriority: KPRIORITY
UniqueProcessId: ULONG_PTR
InheritedFromUniqueProcessId: ULONG_PTR
Per istanziare queste strutture è necessario utilizzare un'istanza di 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
Se si passa un puntatore come secondo argomento, la struttura verrà letta dalla memoria. Si possono dichiarare puntatori con mio_ptr: P[MY_STRUCT] e dereferenziarli con mio_ptr[0].
È disponibile un semplice plugin per x64dbg chiamato MiniDumpPlugin Il comando minidump è stato integrato in x64dbg dal 2022-10-10. Per creare un dump, mettere in pausa l'esecuzione ed eseguire il comando MiniDump my.dmp.
python -m pip install dumpulator
Per installare dal sorgente:
python setup.py install
Per installare in ambiente di sviluppo:
python setup.py develop
Ciò che distingue dumpulator da sandbox come speakeasy e qiling è che la memoria completa del processo è disponibile. Ciò migliora le prestazioni perché è possibile emulare grandi parti di malware senza mai uscire da unicorn. Inoltre, solo le syscall devono essere emulate per fornire un ambiente Windows realistico (poiché tutto è effettivamente un ambiente di processo legittimo).