
Android 네이티브 라이브러리를 에뮬레이션할 수 있으며, 실험적인 iOS 에뮬레이션도 지원합니다.
Android 네이티브 라이브러리 에뮬레이션을 지원하며, 실험적인 iOS 에뮬레이션도 지원합니다.
ELF/MachO 파일 형식과 ARM 어셈블리에 대해 더 배우기 위한 교육용 프로젝트입니다.
사용에 따른 책임은 본인에게 있습니다!
unidbg는 AI 지원 디버깅을 위해 Model Context Protocol (MCP)를 지원합니다. 디버거가 활성화되면 콘솔에 mcp를 입력하여 AI 도구(예: Cursor)가 연결할 수 있는 MCP 서버를 시작합니다.
unidbg MCP에는 두 가지 작동 모드가 있습니다.
모드 1: 중단점 디버그 — 디버거를 연결하고 코드를 실행합니다. 중단점에 도달하면 Breaker.debug()가 에뮬레이터를 일시 중지합니다. 콘솔에 mcp를 입력하여 MCP 서버를 시작하고 AI가 분석을 도와주게 하세요. 모든 디버깅 도구(레지스터, 메모리, 디스어셈블리, 스텝, 트레이스 등)를 사용할 수 있습니다. 재개한 후 다른 중단점에 도달하면 디버거가 다시 일시 중지됩니다. 중단점에 도달하지 않고 실행이 완료되면 프로세스가 종료되고 MCP가 종료됩니다.
Debugger debugger = emulator.attach();
debugger.addBreakPoint(address);
// 에뮬레이션 로직 실행 — 중단점에 도달하면 디버거가 일시 중지됩니다.
모드 2: 사용자 정의 도구 (반복 가능) — McpToolkit을 사용하여 사용자 정의 도구를 등록하고 AI가 다양한 매개변수로 대상 함수를 반복 실행하게 할 수 있습니다. 네이티브 라이브러리는 한 번 로드되며, 각 실행 후 프로세스는 유지되고 MCP는 다음 실행을 위해 활성 상태로 남습니다.
McpToolkit toolkit = new McpToolkit();
toolkit.addTool(new McpTool() {
@Override public String name() { return "encrypt"; }
@Override public String description() { return "Run encryption"; }
@Override public String[] paramNames() { return new String[]{"input"}; }
@Override public void execute(String[] params) {
String input = params.length > 0 ? params[0] : "default";
// input으로 암호화 호출
}
});
toolkit.run(emulator.attach());
디버거가 중단되면 콘솔에 mcp(또는 포트를 지정하려면 mcp 9239)를 입력합니다. 그런 다음 Cursor MCP 설정에 추가합니다:
{
"mcpServers": {
"unidbg-mcp-server": {
"url": "http://localhost:9239/sse"
}
}
}
상태 및 정보
| 도구 | 설명 |
|---|---|
check_connection | 에뮬레이터 상태: Family, 아키텍처, 백엔드 기능, isRunning, 로드된 모듈 |
list_modules / get_module_info | 로드된 모듈 목록, 내보낸 심볼 수 및 종속성을 포함한 세부 정보 |
list_exports | 선택적 필터 및 C++ demangling을 포함한 모듈의 내보낸/동적 심볼 목록 |
find_symbol | 이름으로 심볼 찾기 또는 주소에서 가장 가까운 심볼 찾기 |
get_threads | 에뮬레이터의 모든 스레드/태스크 목록 |
레지스터 및 디스어셈블리
| 도구 | 설명 |
|---|---|
get_registers / get_register / set_register | CPU 레지스터 읽기/쓰기 |
disassemble | 주소의 명령어 디스어셈블 (분기 대상은 심볼 이름으로 자동 주석 처리) |
assemble | 명령어 텍스트를 기계어로 어셈블 |
get_callstack | 현재 호출 스택(백트레이스) 가져오기 |
메모리
| 도구 | 설명 |
|---|---|
read_memory / write_memory | 원시 메모리 바이트 읽기/쓰기 |
read_string / read_std_string | C 문자열 또는 C++ std::string 읽기 (SSO 감지 포함) |
read_pointer | 심볼 해석이 포함된 포인터 체인 읽기 |
read_typed | 형식화된 값으로 메모리 읽기 (int8–int64, float, double, pointer) |
search_memory | 범위/권한 필터로 바이트 패턴 메모리 검색 |
list_memory_map | 권한이 포함된 모든 메모리 매핑 목록 |
allocate_memory / free_memory / list_allocations | 선택적 초기 데이터로 할당(malloc/mmap), 해제 및 메모리 블록 추적 |
patch | 어셈블된 명령어를 메모리에 쓰기 |
중단점 및 실행
| 도구 | 설명 |
|---|---|
add_breakpoint / add_breakpoint_by_symbol / add_breakpoint_by_offset | 주소, 심볼 또는 모듈+오프셋으로 중단점 추가 |
remove_breakpoint / list_breakpoints | 중단점 제거 또는 목록 (디스어셈블리 포함) |
continue_execution | 실행 재개. breakpoint_hit 또는 execution_completed를 기다리려면 poll_events 사용 |
step_over / step_into / step_out | 함수 step over, into (N개 명령어) 또는 out |
next_block | 다음 기본 블록에서 중단 (Unicorn 전용) |
step_until_mnemonic | 니모닉과 일치하는 다음 명령어(예: bl, ret)에서 중단 (Unicorn 전용) |
poll_events | breakpoint_hit, execution_completed, trace 이벤트 폴링 |
트레이스
| 도구 | 설명 |
|---|---|
trace_code | 레지스터 읽기/쓰기 값으로 명령어 트레이스 (regs_read, prev_write) |
trace_read / trace_write | 주소 범위 내 메모리 읽기/쓰기 트레이스 |
함수 호출
| 도구 | 설명 |
|---|---|
call_function | 형식화된 인수(hex, string, bytes, null)로 주소의 네이티브 함수 호출. 심볼 해석 및 메모리 미리보기가 포함된 값 반환 |
call_symbol | 모듈 + 심볼 이름으로 내보낸 함수 호출, 예: libc.so + malloc |
iOS 전용 (Family=iOS일 때 사용 가능)
| 도구 | 설명 |
|---|---|
inspect_objc_msg | objc_msgSend 호출 검사: receiver 클래스 이름과 셀렉터 표시, 예: -[NSString length] |
get_objc_class_name | 주어진 주소의 객체 ObjC 클래스 이름 가져오기 (순수 메모리 파싱, 상태 변경 없음) |
dump_objc_class | ObjC 클래스 정의 덤프 (프로퍼티, 메서드, 프로토콜, ivars) |
dump_gpb_protobuf | GPB protobuf 메시지 스키마를 .proto 형식으로 덤프 (64비트 전용) |
McpToolkit을 사용하여 각각 McpTool 인터페이스를 구현하는 사용자 정의 도구를 등록할 수 있습니다. 이렇게 하면 수동 if-else 분기를 깔끔하고 자족적인 도구 클래스로 대체할 수 있습니다. 이 시점에서 네이티브 라이브러리는 완전히 로드되었으며(JNI_OnLoad / 엔트리 포인트는 이미 실행됨), 각 도구의 execute() 내부 코드는 분석할 대상 함수 로직입니다. AI는 사용자 정의 도구를 트리거하기 전에 중단점과 트레이스를 설정한 다음, 프로세스를 재시작하지 않고 다양한 입력에 대한 실행 결과를 검사할 수 있습니다.
Android 예제 — 사용자 정의 MCP 도구가 포함된 Android JNI 예제는 Utilities64.java를 참조하세요:
DalvikModule dm = vm.loadLibrary(new File("libtmessages.29.so"), true);
dm.callJNI_OnLoad(emulator);
cUtilities = vm.resolveClass("org/telegram/messenger/Utilities");
McpToolkit toolkit = new McpToolkit();
toolkit.addTool(new McpTool() {
@Override public String name() { return "aesCbc"; }
@Override public String description() { return "Run AES-CBC encryption on input data"; }
@Override public String[] paramNames() { return new String[]{"input"}; }
@Override public void execute(String[] params) {
byte[] input = params.length > 0 ? params[0].getBytes() : new byte[16];
aesCbcEncryptionByteArray(input);
}
});
toolkit.addTool(new McpTool() {
@Override public String name() { return "aesCtr"; }
@Override public String description() { return "Run AES-CTR decryption on input data"; }
@Override public String[] paramNames() { return new String[]{"input"}; }
@Override public void execute(String[] params) {
byte[] input = params.length > 0 ? params[0].getBytes() : new byte[16];
aesCtrDecryptionByteArray(input);
}
});
toolkit.addTool(new McpTool() {
@Override public String name() { return "pbkdf2"; }
@Override public String description() { return "Run PBKDF2 key derivation"; }
@Override public String[] paramNames() { return new String[]{"password", "iterations"}; }
@Override public void execute(String[] params) {
String password = params.length > 0 ? params[0] : "123456";
int iterations = params.length > 1 ? Integer.parseInt(params[1]) : 100000;
pbkdf2(password.getBytes(), iterations);
}
});
toolkit.run(emulator.attach());
iOS 예제 — 사용자 정의 MCP 도구가 포함된 iOS IPA 로딩 예제는 IpaLoaderTest.java를 참조하세요:
IpaLoader ipaLoader = new IpaLoader64(ipa, new File("target/rootfs/ipa"));
LoadedIpa loader = ipaLoader.load(this);
emulator = loader.getEmulator();
loader.callEntry();
module = loader.getExecutable();