
Позволяет эмулировать нативную библиотеку Android, а также экспериментальную эмуляцию iOS.
Позволяет эмулировать нативную библиотеку Android, а также экспериментальную эмуляцию iOS.
Это образовательный проект, чтобы узнать больше о форматах файлов ELF/MachO и ассемблере ARM.
Используйте его на свой страх и риск!
unidbg поддерживает Model Context Protocol (MCP) для отладки с помощью ИИ. Когда отладчик активен, введите mcp в консоли, чтобы запустить MCP-сервер, к которому могут подключаться ИИ-инструменты (например, Cursor).
unidbg MCP имеет два режима работы:
Режим 1: Отладка по точкам останова — Подключите отладчик и запустите свой код. При срабатывании точки останова Breaker.debug() приостанавливает эмулятор — введите mcp в консоли, чтобы запустить MCP-сервер и позволить ИИ помочь с анализом. Доступны все инструменты отладки (регистры, память, дизассемблирование, выполнение по шагам, трассировка и т.д.). После возобновления выполнения при следующей точке останова отладчик снова остановится. Когда выполнение завершится без срабатывания точек останова, процесс завершится и MCP остановится.
Debugger debugger = emulator.attach();
debugger.addBreakPoint(address);
// run your emulation logic — debugger pauses when breakpoint is hit
Режим 2: Пользовательские инструменты (повторяемые) — Используйте McpToolkit для регистрации пользовательских инструментов и позвольте ИИ повторно запускать целевые функции с разными параметрами. Нативная библиотека загружается один раз; после каждого выполнения процесс остаётся активным, и 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";
// call encryption with input
}
});
toolkit.run(emulator.attach());
Когда отладчик останавливается, введите mcp (или mcp 9239, чтобы указать порт) в консоли. Затем добавьте в настройки MCP в Cursor:
{
"mcpServers": {
"unidbg-mcp-server": {
"url": "http://localhost:9239/sse"
}
}
}
Статус и информация
Регистры и дизассемблирование
Память
Точки останова и выполнение
Трассировка
| Инструмент | Описание |
|---|---|
trace_code | Трассировать инструкции со значениями чтения/записи регистров (regs_read, prev_write) |
trace_read / trace_write | Трассировать чтение/запись памяти в диапазоне адресов |
Вызовы функций
| Инструмент | Описание |
|---|---|
call_function | Вызвать нативную функцию по адресу с типизированными аргументами (hex, string, bytes, null). Возвращает значение с разрешением символов и предпросмотром памяти |
call_symbol | Вызвать экспортируемую функцию по модулю + имени символа, например libc.so + malloc |
Только iOS (доступно, когда Family=iOS)
Используйте McpToolkit для регистрации пользовательских инструментов, каждый из которых реализует интерфейс McpTool. Это заменяет ручную диспетчеризацию if-else чистыми самодостаточными классами инструментов. К этому моменту нативная библиотека полностью загружена (JNI_OnLoad / точка входа уже выполнены), поэтому код внутри execute() каждого инструмента — это логика целевой функции для анализа. ИИ может устанавливать точки останова и трассировки перед запуском пользовательского инструмента, а затем проверять результаты выполнения с разными входными данными без перезапуска процесса.
Пример для Android — См. Utilities64.java для примера Android JNI с пользовательскими MCP-инструментами:
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 — См. IpaLoaderTest.java для примера загрузки iOS IPA с пользовательскими MCP-инструментами:
IpaLoader ipaLoader = new IpaLoader64(ipa, new File("target/rootfs/ipa"));
LoadedIpa loader = ipaLoader.load(this);
emulator = loader.getEmulator();
loader.callEntry();
module = loader.getExecutable();
McpToolkit toolkit = new McpToolkit();
toolkit.addTool(new McpTool() {
@Override public String name() { return "dumpClass"; }
@Override public String description() { return "Dump an ObjC class definition by name"; }
@Override public String[] paramNames() { return new String[]{"className"}; }
@Override public void execute(String[] params) {
String className = params.length > 0 ? params[0] : "AppDelegate";
IClassDumper classDumper = ClassDumper.getInstance(emulator);
System.out.println("dumpClass(" + className + "):\n" + classDumper.dumpClass(className));
}
});
toolkit.addTool(new McpTool() {
@Override public String name() { return "readVersion"; }
@Override public String description() { return "Read the TelegramCoreVersionString from the executable"; }
@Override public void execute(String[] params) {
Symbol sym = module.findSymbolByName("_TelegramCoreVersionString");
if (sym != null) {
Pointer pointer = UnidbgPointer.pointer(emulator, sym.getAddress());
if (pointer != null) {
System.out.println("_TelegramCoreVersionString=" + pointer.getString(0));
}
}
}
});
toolkit.run(emulator.attach());
После запуска MCP-сервера ИИ может вызывать эти инструменты через MCP для запуска эмуляций с пользовательскими параметрами, установки точек останова, трассировки выполнения и проверки результатов — всё это без перезапуска процесса.
Низкоуровневый API: Вы также можете использовать
Debugger.addMcpTool()+Debugger.run(DebugRunnable)напрямую для полного контроля.McpToolkit— это высокоуровневая обёртка, устраняющая диспетчеризацию if-else.
Отслеживайте выделения памяти на стороне гостя (mmap/munmap/brk), чтобы обнаруживать утечки в эмулируемом нативном коде. Используйте try-with-resources — отслеживание начинается при создании, а отчёт об утечках автоматически выводится при закрытии.
try (MemoryTracker tracker = emulator.traceMemoryLeaks()) {
module.callFunction(emulator, "targetFunction", arg1, arg2);
}
Каждый блок с утечкой включает гостевую ARM-трассировку стека (модуль+смещение+символ) и трассировку стека Java на хосте. Пример вывода:
=== Memory Leak Report ===
Tracking duration: 42ms
Total allocations: 5
Total deallocations: 3
Leaked blocks: 2
Total leaked size: 32768 bytes (32.0 KB)
--- Leak #1 ---
Address: 0x40001000, Size: 16384 (16.0 KB), Perms: rw-
Guest Backtrace:
#0 0x40123456 libexample.so+0x3456 (malloc+0x12)
#1 0x40124000 libexample.so+0x4000 (doSomething+0x48)
Host Stack Trace:
com.github.unidbg.linux.AndroidElfLoader.mmap2(AndroidElfLoader.java:785)
...
Вы также можете получить отчёт программно до закрытия:
try (MemoryTracker tracker = emulator.traceMemoryLeaks()) {
module.callFunction(emulator, "targetFunction", arg1, arg2);
List<AllocationRecord> leaks = tracker.getLeaks();
assert leaks.isEmpty() : "Memory leak detected!";
}
Потокобезопасный пул объектов для повторного использования экземпляров эмулятора в нескольких потоках, позволяющий избежать накладных расходов на повторную инициализацию.
public class MyWorker implements Worker {
private final AndroidEmulator emulator;
public MyWorker() {
emulator = AndroidEmulatorBuilder.for64Bit().build();
// load .so, call JNI_OnLoad, etc.
}
@Override
public void destroy() {
emulator.close();
}
public byte[] doWork(byte[] input) {
// call native methods and return the result
}
}
// Create a worker pool (max = CPU cores, lazy-initialized)
WorkerPool pool = WorkerPoolFactory.create(MyWorker::new);
// Or specify max workers explicitly
// WorkerPool pool = WorkerPoolFactory.create(MyWorker::new, 4);
// Optional: customize idle timeout (default 10 minutes, minimum 1 minute)
pool.setIdleTimeout(30); // idle workers destroyed after 30 minutes
// Optional: customize minimum kept-alive workers (default 1, minimum 1)
pool.setMinIdle(2); // always keep at least 2 workers alive
// Optional: pre-create workers eagerly (default 0, fully lazy)
pool.setInitialSize(4); // eagerly create 4 workers on startup
// Concurrent invocation from multiple threads
ExecutorService executor = Executors.newFixedThreadPool(100);
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
try (WorkerLoan<MyWorker> loan = pool.borrow(1, TimeUnit.MINUTES)) {
if (loan != null) {
byte[] result = loan.get().doWork(input);
}
} // worker is automatically returned to the pool
});
}
executor.shutdown();
executor.awaitTermination(10, TimeUnit.MINUTES);
pool.close(); // destroy all workers and release resources
Полный пример см. в TTEncryptWorker.java.
Простые тесты в каталоге src/test:

Другие тесты:
| Инструмент | Описание |
|---|
check_connection | Статус эмулятора: семейство, архитектура, возможности бэкенда, isRunning, загруженные модули |
list_modules / get_module_info | Перечислить загруженные модули, получить подробную информацию, включая количество экспортируемых символов и зависимости |
list_exports | Перечислить экспортируемые/динамические символы модуля с опциональным фильтром и деманглингом C++ |
find_symbol | Найти символ по имени или ближайший символ по адресу |
get_threads | Перечислить все потоки/задачи в эмуляторе |
| Инструмент | Описание |
|---|
get_registers / get_register / set_register | Чтение/запись регистров ЦП |
disassemble | Дизассемблировать инструкции по адресу (адреса переходов автоматически аннотируются именами символов) |
assemble | Ассемблировать текст инструкции в машинный код |
get_callstack | Получить текущий стек вызовов (backtrace) |
| Инструмент | Описание |
|---|
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 | Возобновить выполнение. Используйте poll_events для ожидания breakpoint_hit или execution_completed |
step_over / step_into / step_out | Шаг с обходом, шаг с заходом (на N инструкций), шаг из функции |
next_block | Остановиться на следующем базовом блоке (только Unicorn) |
step_until_mnemonic | Остановиться на следующей инструкции, соответствующей мнемонике, например bl, ret (только Unicorn) |
poll_events | Опрашивать события breakpoint_hit, execution_completed, trace |
| Инструмент | Описание |
|---|
inspect_objc_msg | Проверить вызов objc_msgSend: показать имя класса получателя и селектор, например -[NSString length] |
get_objc_class_name | Получить имя класса ObjC объекта по заданному адресу (только разбор памяти, без изменения состояния) |
dump_objc_class | Дамп определения класса ObjC (свойства, методы, протоколы, ivars) |
dump_gpb_protobuf | Дамп схемы сообщения GPB protobuf в формате .proto (только 64-бит) |