
메모리 덤프를 에뮬레이션하기 위한 사용하기 쉬운 라이브러리입니다. 악성코드 분석(설정 추출, 언패킹) 및 일반적인 동적 분석(샌드박싱)에 유용합니다.
참고: 이는 작업 진행 중인 프로토타입이므로 그렇게 취급해 주시기 바랍니다. 풀 리퀘스트를 환영합니다! 좋은 첫 번째 이슈로 시작할 수 있습니다.
미니덤프 파일에서 코드를 에뮬레이션하기 위한 사용하기 쉬운 라이브러리입니다. 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
# 프롤로그 다음 명령어에서 멈추고 싶음
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
두 번째 인자로 포인터 값을 전달하면 메모리에서 구조체를 읽습니다. myptr: P[MY_STRUCT]로 포인터를 선언하고 myptr[0]으로 역참조할 수 있습니다.
간단한 x64dbg 플러그인인 MiniDumpPlugin을 사용할 수 있습니다. 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 환경을 제공하기 위해 시스템 콜만 에뮬레이션하면 됩니다(실제로 모든 것이 합법적인 프로세스 환경이기 때문입니다).