
メモリダンプをエミュレートするための使いやすいライブラリ。マルウェア解析(設定抽出、アンパッキング)や一般的な動的解析(サンドボックス)に有用です。
注意: これは作業中のプロトタイプです。そのように扱ってください。プルリクエストは歓迎します!good first issues から始めることができます。
ミニダンプファイル内のコードをエミュレートするための使いやすいライブラリです。以下は dumpulator を使用した記事や動画へのリンクです:
記事を追加するには、プルリクエストを送信してください!
以下の例では、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
# 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)}")
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 インスタンスを使用する必要があります:
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
2 番目の引数としてポインタ値を渡すと、構造体がメモリから読み取られます。myptr: P[MY_STRUCT] でポインタを宣言し、myptr[0] で逆参照できます。
MiniDumpPlugin というシンプルな x64dbg プラグインがありました minidump コマンドは2022-10-10以降 x64dbg に統合されています。ダンプを作成するには、実行を一時停止し、MiniDump my.dmp コマンドを実行します。
python -m pip install dumpulator
ソースからインストールする場合:
python setup.py install
開発環境用にインストールする場合:
python setup.py develop
dumpulator が speakeasy や qiling のようなサンドボックスと異なる点は、完全なプロセスメモリが利用可能なことです。これにより、unicorn を離れることなくマルウェアの大部分をエミュレートできるため、パフォーマンスが向上します。さらに、現実的な Windows 環境を提供するためにシステムコールのみをエミュレートする必要があります(実際、すべてが正当なプロセス環境であるため)。