
Complemento de Binary Ninja que sincroniza el análisis de forma bidireccional con un repositorio de Ghidra Server, mediante un subproceso de puente Java.
Un plugin de Binary Ninja que se conecta a un repositorio de Ghidra Server e importa su análisis —símbolos, nombres de funciones y comentarios— directamente en una vista de binario abierta de Binary Ninja.
Ghidra y Binary Ninja tienen cada uno sus fortalezas. Este plugin te permite usar ambos sobre el mismo binario sin copiar manualmente nombres o comentarios entre ellos. Conéctate a un Ghidra Server en ejecución, explora sus repositorios y haz doble clic en cualquier archivo de proyecto para traer su análisis a la vista de BN abierta actualmente.
La sincronización va en ambas direcciones.
| BN ← Ghidra (importación) | BN → Ghidra (checkin) | |
|---|---|---|
| Símbolos (etiquetas, nombres de funciones) | ✓ | ✓ |
| Comentarios (EOL/PRE/POST/PLATE/REP) | ✓ | ✓ |
| Firmas de funciones (tipo de retorno, convención de llamada) | ✓ | ✓ |
| Parámetros de funciones (renombrar, retipar, añadir) | ✓ | ✓ |
| Tipos de datos (struct/union/enum/typedef + puntero/array) | ✓ | ✓ |
| Ecuaciones (nombres de constantes + referencias) | ✓ | ✓ |
| Marcadores | ✓ | ✓ |
| Elementos de datos tipados | ✓ | ✓ |
| Flags de funciones (thunk, no-return, inline) | ✓ como etiquetas de BN | — |
| Variables locales (con reconocimiento de almacenamiento) | ✓ | parcial — el mapeo de almacenamiento en registros no está implementado |
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)
El plugin lanza un subproceso Java (el "bridge") al cargarse. El bridge mantiene la conexión RMI con el Ghidra Server y habla un protocolo JSON simple de vuelta al plugin a través de un socket TCP local. Esto mantiene todo el código Java/RMI fuera del proceso C++ y permite que la JVM arranque en segundo plano mientras BN termina de cargarse.
La JVM del bridge también inicializa el framework Application de Ghidra al arrancar, para que la ruta de escritura pueda usar las APIs de alto nivel del modelo de programa de Ghidra (ProgramDB, DataTypeManager, SymbolTable, FunctionManager) en lugar de escrituras directas con db.Table.putRecord() — consulta Checkin write path más abajo.
| Ruta | Lenguaje | Rol |
|---|---|---|
plugin/ | C++ / Qt6 | Plugin de barra lateral de Binary Ninja |
bridge/ | Java 17 | Cliente RMI de Ghidra + servidor puente JSON |
Plugin (C++):
plugin.cpp — registra los ajustes y el widget de barra lateral; inicia de forma anticipada la JVM del bridge al cargarGhidraConnection.cpp — singleton; gestiona el ciclo de vida del bridge y todas las operaciones respaldadas por RMIBridgeProcess.cpp — lanza el JAR del bridge como subproceso con tuberías stdout/stderr; lee la línea de handshake READY port=NBridgeClient.cpp — cliente TCP; envía peticiones JSON, recibe respuestas, despacha eventos asíncronosSyncEngine.cpp — aplica un GhidraDbExport a un BinaryView (símbolos, comentarios, flags)ui/ProjectPanel.cpp — widget de barra lateral: árbol de repositorios, diálogo de conexión, registro de actividadui/ConnectDialog.cpp — diálogo de host/puerto/usuario/contraseñaBridge (Java):
BridgeMain.java — análisis de argumentos; inicializa UniversalIdGenerator y el framework Application de Ghidra; inicia el servidor TCP; imprime READY port=N en stdoutBridgeServer.java — acepta una conexión de cliente TCP y le entrega una BridgeConnectionBridgeConnection.java — despachador de peticiones JSON; serializa las respuestas de la API de Ghidra a JSON; gestiona opCheckin (crea una nueva versión del programa en el servidor)GhidraSession.java — sesión RMI autenticada; envuelve RemoteRepositoryServerHandleEventStreamer.java — hilo en segundo plano por cada repositorio abierto; envía RepositoryChangeEvents al plugin como eventos JSON asíncronosDatabaseExporter.java — ruta de lectura: extrae las tablas de símbolos/comentarios/flags de funciones/tipos de datos/ecuaciones/marcadores desde un ManagedBufferFileHandle (el búfer de BD remoto de Ghidra) mediante acceso directo a db.jarProgramApplier.java — ruta de escritura: abre el archivo de búfer como un ProgramDB real y aplica todos los cambios del lado de BN a través de las APIs de alto nivel de Ghidra (consulta Checkin write path más abajo)DatabaseImporter.java — helpers de escritura directa heredados, conservados solo como punto de prueba; el apply(...) de producción delega en ProgramApplieropCheckin abre el archivo de búfer gestionado del programa en modo escritura, construye un ProgramDB sobre él y aplica los cambios del lado de BN a través de las APIs del modelo de programa de Ghidra. Se evitan las escrituras directas con db.Table.putRecord() — fueron la fuente de todos los bugs de corrupción en el checkin que hemos encontrado:
| Escritura en el nivel incorrecto | Modo de fallo |
|---|---|
setIntValue(col, longTypeId) en la tabla Function Data | IntField.setLongValue trunca silenciosamente con l2i → StackPurge se corrompe en cada actualización de firma |
setByteValue(col, isUnion) en V5V6 Composite Data Types | la columna es BooleanField en Ghidra 12.x → IllegalFieldAccessException ("Illegal field access") |
setIntValue(col, 0) en la columna V2 Typedef Flags | la columna es ShortField → el mismo fallo, esquema distinto |
| Escribir la cabecera de un compuesto sin las filas de configuración de componentes | CompositeEditorModel.cloneAllComponentSettings lanza ArrayIndexOutOfBoundsException cuando el struct se abre en Ghidra |
Escribir un símbolo PARAMETER con SYM_ADDR_COL = dirección RAM | Address is not a VariableAddress lanzado por FunctionDB.loadSymbolBasedVariables en cualquier acceso a la función |
Pasar null como DBChangeSet a DBHandle.save() | el servidor escribe un archivo de datos de cambios de 0 bytes → el siguiente checkout falla con EOFException en ProgramContentHandler.loadProgramChangeSet |
ProgramApplier no tiene estas trampas porque enruta a través de DataTypeManager.addDataType, SymbolTable.createLabel, Listing.setComment, Function.setReturnType, etc. — APIs que mantienen automáticamente las invariantes de las tablas entrelazadas de Ghidra. También ejecuta una pasada cleanupBadVariableSymbols al inicio de cada checkin para purgar la corrupción dejada en la base de datos por versiones anteriores del bridge.
server-package/CleanupBadVariableSymbols.java es un GhidraScript independiente que ejecuta la misma limpieza mediante analyzeHeadless — útil cuando un archivo está demasiado corrupto para abrirlo en la GUI de 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)
La suite está organizada en cinco niveles. Los niveles 2–4 existen para demostrar una propiedad: los mismos datos compatibles terminan almacenados tanto en el .bndb como en la base de datos del programa de Ghidra (la matriz de compatibilidad al inicio de este README).