
메모리 덤프를 에뮬레이션하기 위한 사용하기 쉬운 라이브러리입니다. 악성코드 분석(설정 추출, 언패킹) 및 일반적인 동적 분석(샌드박싱)에 유용합니다.
# dumpulator
**참고: 이는 작업 진행 중인 프로토타입이므로 그렇게 취급해 주시기 바랍니다. 풀 리퀘스트를 환영합니다! [좋은 첫 번째 이슈](https://github.com/mrexodia/dumpulator/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)로 시작할 수 있습니다.**
미니덤프 파일에서 코드를 에뮬레이션하기 위한 사용하기 쉬운 라이브러리입니다. dumpulator를 사용하는 게시물/동영상 링크는 다음과 같습니다:
- [OALabs](https://oalabs.openanalysis.net)와의 소개 동영상: [Dumpulator - Using Binary Emulation To Automate Reverse Engineering](https://youtu.be/4Pfu98Xx9Yo)
- [Emulating malware with Dumpulator](https://rioasmara.com/2022/07/23/emulating-malware-with-dumpulator/)
- [Emotet x64 Stack Strings Config Emulation | OALabs Research](https://research.openanalysis.net/emotet/emulation/config/dumpulator/malware/2022/05/19/emotet_x64_emulation.html)
- [Native function and Assembly Code Invocation](https://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/)
- [Guloader string decryption (VEH)](https://research.openanalysis.net/guloader/emulation/dumpulator/veh/exceptions/2023/01/15/dumpulator-veh.html)
- [Rhadamanthys | OALabs Research](https://research.openanalysis.net/rhadamanthys/config/ida/shifted%20pointers/peb/_list_entry/_ldr_data_table_entry/2023/01/19/rhadamanthys.html)
- [\[사례 연구\] Dumpulator를 사용하여 문자열 복호화](https://kienmanowar.wordpress.com/2023/05/22/case-study-decrypt-strings-using-dumpulator/)
<sub>여기에 자신의 글을 추가하려면 풀 리퀘스트를 보내 주세요!</sub>
## 예제
### 함수 호출
아래 예제는 `StringEncryptionFun_x64.dmp`를 열고(복사본 [여기](https://github.com/mrexodia/dumpulator/releases/download/v0.0.1/StringEncryptionFun_x64.dmp)에서 다운로드), 메모리를 할당한 후 `0x140001000`에 있는 복호화 함수를 호출하여 `0x140017000`에 있는 문자열을 복호화합니다:
```python
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`의 컴파일된 바이너리는 [여기](https://github.com/mrexodia/dumpulator/releases/download/v0.0.1/StringEncryptionFun.7z)에서 받을 수 있습니다.
### 실행 추적
```python
from dumpulator import Dumpulator
dp = Dumpulator("StringEncryptionFun_x64.dmp", trace=True)
dp.start(dp.regs.rip)
```
그러면 `StringEncryptionFun_x64.dmp.trace` 파일이 생성되며, 실행된 명령어 목록과 모듈 전환 등에 대한 유용한 표시가 포함됩니다. 추적은 에뮬레이션 속도를 _상당히_ 저하시키며, 주로 디버깅을 위한 것임에 유의하세요.
### utf-16 문자열 읽기
```python
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]
```
프롤로그만 실행하고 몇 가지 레지스터를 설정하고 싶다면:
```python
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` 데코레이터를 사용하여 시스템 콜을 (재)구현할 수 있습니다:
```python
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](https://github.com/mrexodia/dumpulator/blob/main/src/dumpulator/ntsyscalls.py)에서 찾을 수 있습니다. API 사용 방법에 대한 많은 예제도 있습니다.
기존 시스템 콜 구현을 후킹하려면 다음과 같이 할 수 있습니다:
```python
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`부터 자신만의 구조체를 쉽게 선언할 수 있습니다:
```python
from dumpulator.native import *
class PROCESS_BASIC_INFORMATION(Struct):
ExitStatus: ULONG
PebBaseAddress: PVOID
AffinityMask: KAFFINITY
BasePriority: KPRIORITY
UniqueProcessId: ULONG_PTR
InheritedFromUniqueProcessId: ULONG_PTR
```
이러한 구조체를 인스턴스화하려면 `Dumpulator` 인스턴스를 사용해야 합니다:
```python
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](https://github.com/x64dbg/x64dbg) 플러그인인 [MiniDumpPlugin](https://github.com/mrexodia/MiniDumpPlugin/releases)을 사용할 수 있습니다.~~ [minidump](https://help.x64dbg.com/en/latest/commands/memory-operations/minidump.html) 명령어는 2022-10-10부터 x64dbg에 통합되었습니다. 덤프를 만들려면 실행을 일시 중지하고 `MiniDump my.dmp` 명령어를 실행하세요.
## 설치
[PyPI](https://pypi.org/project/dumpulator)에서 (최신 [릴리즈](https://github.com/mrexodia/dumpulator/releases)):
```
python -m pip install dumpulator
```
소스에서 설치하려면:
```
python setup.py install
```
개발 환경을 위해 설치하려면:
```
python setup.py develop
```
## 관련 작업
- [Dumpulator-IDA](https://github.com/michaeljgoodman/Dumpulator-IDA): 이 프로젝트는 IDA 내에서 dumpulator 에뮬레이션을 시작하고, 컨텍스트 메뉴를 통해 IDA 뷰에서 주소를 전달하는 작은 POC 플러그인입니다.
- [wtf](https://github.com/0vercl0k/wtf): 분산형, 코드 커버리지 기반, 사용자 정의 가능, 크로스 플랫폼 스냅샷 기반 퍼저로, Microsoft Windows에서 실행되는 사용자 및/또는 커널 모드 대상을 공격하도록 설계되었습니다.
- [speakeasy](https://github.com/mandiant/speakeasy): unicorn 기반의 Windows 샌드박스.
- [qiling](https://github.com/qilingframework/qiling): unicorn 기반의 바이너리 에뮬레이션 프레임워크.
- [Simpleator](https://github.com/ionescu007/Simpleator): Hyper-V 플랫폼 API 기반의 사용자 모드 애플리케이션 에뮬레이터.
dumpulator를 speakeasy나 qiling과 같은 샌드박스와 차별화하는 점은 전체 프로세스 메모리를 사용할 수 있다는 것입니다. 이를 통해 unicorn을 벗어나지 않고도 맬웨어의 큰 부분을 에뮬레이션할 수 있어 성능이 향상됩니다. 또한 실제 Windows 환경을 제공하기 위해 시스템 콜만 에뮬레이션하면 됩니다(실제로 모든 것이 합법적인 프로세스 환경이기 때문입니다).
## 크레딧
- [herrcore](https://twitter.com/herrcore): 이 도구를 만들도록 영감을 주셨습니다.
- [secret club](https://secret.club)
- [JetBrains](https://www.jetbrains.com/opensource/): 무료 PyCharm 라이선스를 제공해 주셨습니다!
- [GraphiqaStock의 이미지](https://www.freepik.com/free-vector/virus-internet_1040653.htm) on Freepik