
Плагин Binary Ninja, который двунаправленно синхронизирует анализ с репозиторием Ghidra Server через подпроцесс Java-моста.
Плагин для Binary Ninja, который подключается к репозиторию Ghidra Server и импортирует его результаты анализа — символы, имена функций и комментарии — напрямую в открытое представление бинарного файла Binary Ninja.
У Ghidra и Binary Ninja есть свои сильные стороны. Этот плагин позволяет использовать обе программы для одного и того же бинарного файла без ручного копирования имён или комментариев между ними. Подключитесь к запущенному Ghidra Server, просмотрите его репозитории и дважды щёлкните любой файл проекта, чтобы перенести его анализ в текущее открытое представление BN.
Синхронизация работает в обоих направлениях.
| BN ← Ghidra (импорт) | BN → Ghidra (checkin) | |
|---|---|---|
| Символы (метки, имена функций) | ✓ | ✓ |
| Комментарии (EOL/PRE/POST/PLATE/REP) | ✓ | ✓ |
| Сигнатуры функций (тип возврата, соглашение о вызовах) | ✓ | ✓ |
| Параметры функций (переименование, смена типа, добавление) | ✓ | ✓ |
| Типы данных (struct/union/enum/typedef + указатель/массив) | ✓ | ✓ |
| Приравнивания (имена констант + ссылки) | ✓ | ✓ |
| Закладки | ✓ | ✓ |
| Типизированные элементы данных | ✓ | ✓ |
| Флаги функций (thunk, no-return, inline) | ✓ как теги BN | — |
| Локальные переменные (с учётом хранилища) | ✓ | частично — сопоставление регистрового хранилища не реализовано |
Binary Ninja (C++ plugin)
│ TCP / newline-delimited JSON
▼
ghidra-bridge-*.jar (Java, runs as a subprocess)
│ Java RMI / SSL
▼
Ghidra Server (ghidraSvr, running on the network)
Плагин при загрузке запускает подпроцесс Java (далее «мост»). Мост поддерживает RMI-соединение с Ghidra Server и общается с плагином по простому JSON-протоколу через локальный TCP-сокет. Это позволяет держать весь Java/RMI-код вне процесса C++ и даёт JVM запускаться в фоне, пока BN завершает загрузку.
JVM моста также инициализирует фреймворк Application из Ghidra при запуске, чтобы путь записи мог использовать высокоуровневые API программной модели Ghidra (ProgramDB, DataTypeManager, SymbolTable, FunctionManager), а не низкоуровневые записи db.Table.putRecord() — см. Путь записи при checkin ниже.
| Путь | Язык | Роль |
|---|---|---|
plugin/ | C++ / Qt6 | Плагин боковой панели Binary Ninja |
bridge/ | Java 17 | RMI-клиент Ghidra + JSON-сервер моста |
Плагин (C++):
plugin.cpp — регистрирует настройки и виджет боковой панели; сразу запускает JVM моста при загрузкеGhidraConnection.cpp — синглтон; управляет жизненным циклом моста и всеми операциями на базе RMIBridgeProcess.cpp — запускает JAR моста как подпроцесс с каналами stdout/stderr; читает строку рукопожатия READY port=NBridgeClient.cpp — TCP-клиент; отправляет JSON-запросы, принимает ответы, диспетчеризует асинхронные событияSyncEngine.cpp — применяет GhidraDbExport к BinaryView (символы, комментарии, флаги)ui/ProjectPanel.cpp — виджет боковой панели: дерево репозиториев, диалог подключения, журнал активностиui/ConnectDialog.cpp — диалог host/port/user/passwordМост (Java):
BridgeMain.java — разбор аргументов; инициализирует UniversalIdGenerator и фреймворк Application из Ghidra; запускает TCP-сервер; печатает READY port=N в stdoutBridgeServer.java — принимает одно TCP-соединение клиента и передаёт его в BridgeConnectionBridgeConnection.java — диспетчер JSON-запросов; сериализует ответы API Ghidra в JSON; обрабатывает opCheckin (создаёт новую версию программы на сервере)GhidraSession.java — аутентифицированная RMI-сессия; оборачивает RemoteRepositoryServerHandleEventStreamer.java — фоновый поток на каждый открытый репозиторий; отправляет RepositoryChangeEvent в плагин как асинхронные JSON-событияDatabaseExporter.java — путь чтения: извлекает таблицы символов/комментариев/флагов функций/типов данных/приравниваний/закладок из ManagedBufferFileHandle (буфер удалённой БД Ghidra) через прямой доступ к db.jarProgramApplier.java — путь записи: открывает файл буфера как настоящий ProgramDB и применяет все изменения со стороны BN через высокоуровневые API Ghidra (см. Путь записи при checkin ниже)DatabaseImporter.java — устаревшие низкоуровневые помощники записи, оставленные только как тестовый шов; производственный apply(...) делегирует в ProgramApplieropCheckin открывает управляемый буферный файл программы в режиме записи, конструирует поверх него ProgramDB и применяет изменения со стороны BN через API программной модели Ghidra. Низкоуровневые записи db.Table.putRecord() избегаются — они были источником всех багов повреждения при checkin, с которыми мы когда-либо сталкивались:
| Запись не того уровня | Режим отказа |
|---|---|
setIntValue(col, longTypeId) в таблице Function Data | IntField.setLongValue молча усекает через l2i → StackPurge повреждается при каждом обновлении сигнатуры |
setByteValue(col, isUnion) в V5V6 Composite Data Types | столбец является BooleanField в Ghidra 12.x → IllegalFieldAccessException ("Illegal field access") |
setIntValue(col, 0) в столбце V2 Typedef Flags | столбец является ShortField → тот же сбой, другая схема |
| Запись заголовка composite без строк настроек компонентов | CompositeEditorModel.cloneAllComponentSettings выбрасывает ArrayIndexOutOfBoundsException при открытии struct в Ghidra |
Запись символа PARAMETER с SYM_ADDR_COL = RAM-адрес | Address is not a VariableAddress выбрасывается FunctionDB.loadSymbolBasedVariables при любом доступе к функции |
Передача null DBChangeSet в DBHandle.save() | сервер записывает файл change-data размером 0 байт → следующий checkout падает с EOFException в ProgramContentHandler.loadProgramChangeSet |
У ProgramApplier этих ловушек нет, потому что он маршрутизирует через DataTypeManager.addDataType, SymbolTable.createLabel, Listing.setComment, Function.setReturnType и т. д. — API, которые автоматически поддерживают инварианты взаимосвязанных таблиц Ghidra. Он также запускает проход cleanupBadVariableSymbols в начале каждого checkin, чтобы вычистить повреждения, оставленные в базе старыми версиями моста.
server-package/CleanupBadVariableSymbols.java — это автономный GhidraScript, выполняющий ту же очистку через analyzeHeadless — полезно, когда файл слишком повреждён, чтобы открыться в GUI Ghidra.
./test.sh # macOS / Linux: tiers 0-3 (C++ unit + BN-headless + Java)
test.bat # Windows equivalent
test.bat --parity # cross-DB parity tier only (C++ BN tests + gradlew parityTest)
test.bat --e2e # live Ghidra-server E2E (starts a local ghidraSvr)
Набор организован в пять уровней. Уровни 2–4 существуют, чтобы доказать одно свойство: одни и те же совместимые данные оказываются сохранёнными как в .bndb, так и в базе программы Ghidra (матрица совместимости в начале этого README).
| Уровень | Что | Где | Условие |
|---|---|---|---|
| 0 | Чистые модульные тесты | plugin/test/*.cpp (binja-ghidra-tests), мост *Test.java | всегда |
| 1 | Round-trip БД Ghidra | мост *RoundTripTest.java (ProgramApplier против настоящего ProgramDB) | требует ghidra.home / GHIDRA_HOME |
| 2 | Round-trip BN BinaryView/.bndb | plugin/test/bn/ (binja-ghidra-bn-tests; headless binaryninjacore) | чисто пропускается без headless-совместимой лицензии BN (учитывается env BN_LICENSE) |
| 3 | Кросс-БД паритет | CanonicalParityTest (C++ и Java) против общих эталонов в testdata/parity/fixtures/ | вместе с уровнями 1+2 |
| 4 | E2E с живым сервером | мост LiveServerE2ETest — поднимает настоящий ghidraSvr во временном каталоге, наполняет через analyzeHeadless, прогоняет checkout → export → checkin → re-export по RMI | test.bat --e2e (устанавливает GHIDRA_E2E=1) |