
Plugin de BianryNinja para identificar vulnerabilidades en binarios descompilados con escaneos programáticos y soporte de LLM.
Investigación de vulnerabilidades asistida por LLM para Binary Ninja.
VulnFanatic-NG añade un panel lateral que escanea el binario actual y pregunta a un LLM — un modelo alojado localmente compatible con OpenAI por defecto, o Anthropic Claude, Google Gemini, o Azure OpenAI (ver Backends de LLM) — para juzgar si el código sospechoso es realmente vulnerable. Funciona principalmente a partir de la salida del descompilador (HLIL) de Binary Ninja, recurriendo al ensamblador cuando es necesario, e informa solo problemas confirmados con referencias en las que se puede hacer clic hacia el código.
Un análisis se ejecuta en hasta tres fases (La Fase 3 es opcional y solo en línea):
Encuentra sitios de llamada de funciones peligrosas definidas en
rules/phase1_rules.json — strcpy,
memcpy, sprintf/cadenas de formato, system, alloca, scanf, APIs de
comando/ejecución, RNG débil, la familia free/delete (uso después de
liberación / doble liberación), lecturas de entrada no confiable en buffers
fijos (recv/read/fread/ReadFile), inyección SQL
(sqlite3_exec/mysql_query/PQexec), verificación de certificado TLS
deshabilitada (SSL_CTX_set_verify/curl), SSRF, y gestión de privilegios
inadecuada (setuid/setresgid), la familia memset/bzero, y
comparaciones con una longitud controlada por el atacante (memcmp/strncmp →
bypass de autenticación), en C/C++, Win32, y (con el mejor esfuerzo) Rust FFI.
Cobertura incluye variantes fortificadas _chk (FORTIFY) y con verificación de
límites _s (Anexo-K). Las funciones de salida con formato acotado (snprintf y
variantes) tienen su propia regla segura por defecto, por lo que un argumento de
tamaño correcto no se reporta como desbordamiento. Los sitios de llamada se
encuentran de tres maneras: llamadas directas a los símbolos nombrados; llamadas
enrutadas a través de stubs de reenvío / PLT (se recuperan los llamadores
reales, de modo que no se pierde una importación alcanzada solo a través de un
stub); y — a menos que vulnfanatic.scanIndirectCalls esté desactivado —
llamadas indirectas despachadas a través de un puntero a función o vtable que
Binary Ninja resolvió a una función peligrosa. Para cada sitio de llamada,
construye un contexto interprocedimental centrado en el descompilador, limitado
a un presupuesto de tokens (por defecto 100k):
__*_chk y con verificación
de límites *_s toman argumentos iniciales adicionales, desplazando la posición
del formato/tamaño/destino,s->buf se resuelva al tamaño real del array del campo
en lugar del tamaño del puntero de s; las definiciones de estructuras en la
sección de tipos también llevan tamaños en bytes por campo,0x40 o acotada a [0, 0xff]), que el modelo usa
como verdad fundamental al comparar un tamaño con la capacidad de un buffer en
lugar de adivinar,vulnfanatic.includeStackLayout),if/bucle/ que protegen la
llamada),Ese contexto, junto con un prompt específico de la regla, se envía al modelo, que devuelve un veredicto estructurado. Los no-problemas se descartan. Los prompts están ajustados para un modelo de código local potente (por ejemplo, Qwen2.5-Coder) y le indican que analice todo el flujo y emita solo JSON.
Se le indica al modelo que favorezca el recuerdo — reporte problemas plausibles y relevantes para la seguridad y exprese incertidumbre a través de una Confianza en lugar de descartar cualquier cosa que no pueda probar completamente. Muestra su trabajo en un borrador que cita los fragmentos de código literales en los que se basó (la fuente de entrada, cada protección, el tamaño/longitud, el tipo relevante, y el sumidero), que se almacena en el hallazgo para que puedas auditar el razonamiento.
Cada hallazgo lleva una Confianza (alta/media/baja): alta = toda la cadena se
muestra en el contexto; media = probable, con uno o dos enlaces inferidos; baja = una
pista que merece revisión manual. Esta es la métrica principal (la estimación de
severidad del modelo es un campo secundario). Establece vulnfanatic.minConfidence
para descartar cualquier cosa por debajo de un umbral.
Por defecto VulnFanatic-NG favorece el recuerdo (detectar problemas reales). Si obtienes demasiados falsos positivos, ajústalo con cualquiera de:
vulnfanatic.validationPass (por defecto desactivado) — ejecuta una
segunda pasada del LLM que verifica cada problema marcado contra el mismo contexto
(verificando los fragmentos del borrador y re-rastreando el flujo) y puede corregir
el veredicto o la confianza. Duplica las llamadas al LLM para los candidatos
marcados.
vulnfanatic.validatorModel (más validatorProvider /
validatorBaseUrl / validatorApiKey) para ejecutar la segunda pasada en un
modelo diferente. Una segunda opinión es mucho más útil de un modelo
independiente — comparte menos puntos ciegos y es mucho menos probable que
refrende automáticamente el primer veredicto (los modelos tienden a preferir sus
propias respuestas). Un buen patrón es una cascada: un modelo rápido como
analista (recuerdo amplio) y tu modelo más fuerte como validador, que solo se
ejecuta sobre los candidatos marcados. Deja el modelo validador en blanco para
validar con el modelo analista. El validador debe ser al menos tan capaz como el
analista — uno más débil añade principalmente falsos rechazos. Todo excepto
proveedor/URL base/clave/modelo se hereda de la configuración de conexión del
analista; una clave de validador en blanco reutiliza la clave del analista; y si
el endpoint del validador es inalcanzable, se conserva el primer veredicto (el
hallazgo nunca se pierde por una caída del validador).vulnfanatic.minConfidence (por defecto low) — ajústalo a /
para reportar solo hallazgos más sólidos.Velocidad. La mayor parte de la latencia por llamada es el razonamiento escrito,
por lo que vulnfanatic.verdictReasoning controla cuánto escribe el modelo:
concise (por defecto) — una breve justificación de 1 a 3 frases, sin código
literal. Mucho más rápido que full con poca pérdida de precisión; también puedes
reducir vulnfanatic.maxResponseTokens.full — el borrador detallado con fragmentos citados (más auditable, más
lento).none — solo veredicto. Lo más rápido; combínalo con un backend con capacidad
de razonamiento (vulnfanatic.reasoningEffort) para que el pensamiento interno
del modelo haga el trabajo. En un modelo local simple, none pierde precisión
(sin cadena de pensamiento en absoluto).Funciones de precisión complementarias que siempre están activas (informan al modelo sin suprimir hallazgos):
_s (Anexo K) y _chk (FORTIFY) y las API con longitud acotada como
seguras a menos que el propio argumento de tamaño sea incorrecto.El botón Análisis sin conexión ejecuta la Fase 1 sin modelo — heurísticas
puramente programáticas declaradas en el bloque offline de cada regla en
phase1_rules.json. Marca los sitios de llamada peligrosos y elimina los
obviamente seguros, asignando una Confianza heurística:
memcpy/memmove con una longitud constante,
un strcpy desde una cadena constante, un printf con un formato constante, un
system con un comando constante, etc. — llamadas cuyo argumento rector es una
constante en tiempo de compilación y por lo tanto no puede ser controlado por el
atacante. "Constante" incluye valores que el análisis de conjuntos de valores de
Binary Ninja fijó a un número fijo aguas arriba, no solo argumentos literales.strlen/tamaño, if (len < …)) en algún
lugar del flujo — incluyendo en las funciones llamadas a lo largo de la ruta —
por lo que puede que ya esté manejado. (Una rama que simplemente menciona la
variable sin compararla ya no cuenta, eliminando una fuente de degradaciones
espurias.)Las heurísticas usan un pequeño vocabulario declarativo en las reglas
(constant_safe_args, eliminate_if_all_args_constant, format_arg_lookup,
length_guard_vars, base_confidence, skip) evaluadas por predicados de Python
— sin código incrustado para exec. La mayoría de las reglas tienen una definición
sin conexión (desbordamiento, cadena de formato, ejecución de comandos, scanf,
manejo de rutas, RNG débil, análisis numérico débil, cambios de privilegios, tamaño
de asignación, …). Solo las dos categorías que realmente necesitan análisis semántico
se saltan sin conexión y se dejan para el LLM: la familia free/delete (uso
después de liberación / doble liberación, que necesita seguimiento de la vida útil
del puntero) y la verificación TLS (el error es un valor constante específico
como SSL_VERIFY_NONE). El resumen sin conexión informa cuántos sitios fueron
marcados / eliminados / saltados (necesitan el LLM) / fallidos, para que los
recuentos sumen. Esto es un triaje rápido; para un juicio real — y para las
categorías saltadas — ejecuta el análisis completo con LLM.
Los hallazgos sin conexión aún construyen el mismo contexto interprocedimental
completo que un análisis en línea enviaría (solo para los sitios marcados) y lo
almacenan, de modo que una vez que los triajes, se pueden exportar como datos de
ajuste fino al igual que los hallazgos en línea. Desactívalo con
vulnfanatic.offlineBuildContext si deseas la máxima velocidad sin conexión.
Solo se ejecuta cuando el binario parece tener símbolos reales / nombres de
variables. Localiza funciones sensibles a la seguridad definidas en
rules/phase2_rules.json —
autenticación, criptografía (incl. algoritmos débiles), verificación de
firmas/certificados, manejo de sesiones/tokens, control de acceso, manejo de
secretos/claves, validación de entrada, comparación de secretos no constante en
tiempo, y deserialización insegura — emparejadas por nombre de función y cadenas
referenciadas, luego auditadas por el modelo.
Una auditoría de endurecimiento de firmware contra ataques de inyección de
fallos (glitching de voltaje/reloj/EM) y de canal lateral (tiempo/potencia),
basada en guías de mitigación de ataques hardware. A diferencia de las Fases 1–2
(que encuentran errores), la Fase 3 informa un control de endurecimiento
faltante o violado en una función crítica para la seguridad — por ejemplo:
ramas de fallo por defecto, decisiones de seguridad doblemente verificadas,
validación de contador después de bucles, constantes de estado con alta distancia
de Hamming (frente a 0/1 simple), comparación de secretos de longitud completa en
tiempo constante, acceso/borrado de secretos con desplazamiento aleatorio,
cifrar-luego-verificar (anti-DFA), contadores de integridad de flujo de control,
evitar criptografía en espacio de usuario, y no manejar material de clave sin
procesar directamente
(rules/phase3_rules.json).
Debido a que las optimizaciones del compilador pueden eliminar protecciones a nivel de fuente, estos controles se verifican mejor en el binario compilado — exactamente lo que esto comprueba. La Fase 3 es solo con LLM (en línea), protegida por símbolos, y desactivada por defecto; actívala por análisis con la casilla Fase 3 en la pestaña de Nuevo Análisis (nunca se ejecuta en modo sin conexión).
Los hallazgos se muestran en una tabla (estado, confianza, fase, CWE, función, dirección, título) con un panel de detalles que muestra la explicación, el borrador del análisis y las notas de validación. Haz doble clic en una fila para navegar la vista del binario hasta el código.
Cada hallazgo comienza Sin triangular. Haz clic derecho en una fila para establecer su estado — Marcar como Problema Real, Marcar como Falso Positivo, o Marcar como Sin triangular. Cada cambio de estado muestra un cuadro de texto "Proporciona razón:" (la razón se almacena con el hallazgo). La tabla hace evidente el estado: los Problemas Reales son verde/negrita y se ordenan al principio, los Falsos Positivos son gris/tachados y se ordenan al final, los Sin triangular se sitúan en medio con su color de confianza. Una línea de resumen muestra los recuentos.
Cada pestaña de resultados tiene un botón Exportar triados (ajuste fino)… que
exporta solo los hallazgos triados (Problema Real + Falso Positivo) como JSONL
en formato de chat de OpenAI para ajuste fino: cada ejemplo empareja el prompt
original de sistema+usuario con el veredicto corregido por humano como destino
del asistente (un Falso Positivo enseña is_vulnerable=false con tu razón; un
Problema Real refuerza is_vulnerable=true), para que puedas mejorar
iterativamente la precisión del modelo en tus binarios.
El contexto por hallazgo mostrado en el panel de detalles (y usado para reconstruir
los prompts de ajuste fino) se mantiene, por defecto, completo — controlado por
vulnfanatic.storedContextChars (0 = ilimitado; establece un límite positivo,
p. ej. 4000, para limitar el crecimiento del BNDB a costa de la fidelidad del
contexto).
El panel tiene pestañas. La primera pestaña es siempre Nuevo Análisis, donde estableces:
<marca de tiempo> <modo>, p. ej.
2026-06-15 14:03:50 sin conexión),luego presiona Iniciar Análisis o Análisis sin Conexión. Cada ejecución abre su propia pestaña de resultados y los hallazgos se transmiten en vivo. Todos los análisis se almacenan en el BNDB, por lo que puedes, p. ej., conservar un análisis sin conexión y luego agregar un análisis en línea, o comparar ejecuciones con diferentes conjuntos de reglas, lado a lado — reaparecen como pestañas cuando vuelves a abrir la base de datos. Cerrar una pestaña elimina permanentemente ese análisis del BNDB — para evitar accidentes, muestra una confirmación que requiere marcar "Confirmo que perderé los resultados de para siempre" antes de que se active el botón Eliminar resultados para siempre. Exportar análisis actual… escribe la pestaña seleccionada a Markdown/JSON.
Cada binario abierto tiene su propio estado de panel independiente — sus propias pestañas de análisis y análisis en ejecución. Iniciar un análisis en un binario y cambiar a otro muestra los resultados del segundo binario (y te permite analizarlo por separado); el análisis del primer binario sigue ejecutándose en segundo plano y está intacto cuando vuelves.
La carpeta del paquete de este plugin se llama vulnfanatic_ng (un identificador
válido de Python — Binary Ninja importa el nombre de la carpeta del plugin como un
módulo, por lo que un nombre con guión como VulnFanatic-NG no se cargaría).
(Opcional) Instala el conteo preciso de tokens en el Python de Binary Ninja: ``` pip install tiktoken
Cree un enlace simbólico o copie la carpeta vulnfanatic_ng en el directorio de plugins de usuario de Binary Ninja:
~/Library/Application Support/Binary Ninja/plugins/~/.binaryninja/plugins/%APPDATA%\Binary Ninja\plugins\Por ejemplo, en macOS: ``` ln -s "$(pwd)/vulnfanatic_ng" "$HOME/Library/Application Support/Binary Ninja/plugins/vulnfanatic_ng"
Reinicie Binary Ninja (o ejecute Reload Plugins). Aparece un icono VF en la barra lateral derecha.
Abra Ajustes (el engranaje / Edit ▸ Preferences ▸ Settings) y busque
vulnfanatic. Configure como mínimo:
vulnfanatic.apiProvider selecciona cómo se forman y autentican las solicitudes. El
contrato de veredicto (y todas las indicaciones de reglas) son idénticos entre proveedores.
AWS Bedrock se puede usar a través del proveedor
openaimediante su endpoint compatible con OpenAI, por lo que no necesita un backend dedicado.
Otros ajustes útiles: vulnfanatic.maxContextTokens (predeterminado 100000),
vulnfanatic.maxResponseTokens, vulnfanatic.temperature,
vulnfanatic.reasoningEffort (off/low/medium/high; predeterminado high — pide
al modelo que piense antes de responder donde sea compatible, mapeado por proveedor:
openai/azure reasoning_effort, anthropic pensamiento adaptativo + output_config.effort,
google thinkingConfig dinámico; se elimina automáticamente y se reintenta si un modelo lo rechaza),
vulnfanatic.requestTimeoutSec,
vulnfanatic.callPathMaxDepth / ,
(incluye los cuerpos descompilados de las funciones a lo largo
de la ruta de llamada; predeterminado activado) / (límite, predeterminado 12),
(incluye también otras funciones llamadas a lo largo de la
ruta, que pueden contener las comprobaciones de límites/validación; predeterminado activado) /
(límite, predeterminado 12),
(incluye definiciones de struct/union/enum; predeterminado activado) /
(límite, predeterminado 24),
(rastrea los argumentos de llamada hacia atrás a través de sus productores
/consumidores e incluye esos cuerpos; predeterminado activado) /
(límite, predeterminado 8),
(incluye el diseño de variables de pila de la función llamante
cuando tiene un búfer de tamaño fijo; predeterminado activado),
(también coincide con llamadas peligrosas despachadas a través de un puntero
a función/vtabla resuelto; predeterminado activado — desactive para un escaneo más rápido en binarios muy grandes),
(ejecuta la segunda pasada de doble comprobación; predeterminado desactivado) /
/ /
/ (ejecuta la pasada de validación en un
modelo independiente y separado — en blanco = mismo modelo que el analista) /
(//; descarta hallazgos por debajo de esto; predeterminado
), (informa los sitios que el modelo no pudo puntuar como
pistas "Unscored" con confianza en lugar de descartarlos; predeterminado activado),
(omite sitios de llamada de desbordamiento con todos los argumentos constantes;
predeterminado desactivado), (//; cuánto
razonamiento escribe el modelo por veredicto — la principal palanca de velocidad; predeterminado ),
/
/ (habilita cada fase; La fase 3 es
solo en línea y generalmente se activa por escaneo mediante la casilla Nueva exploración en lugar de aquí),
/ ,
(codificación tiktoken para estimaciones de tokens; retrocede a
una heurística de caracteres si tiktoken no está instalado),
(construye contexto completo para hallazgos fuera de línea para que puedan
exportarse para ajuste fino; predeterminado activado),
(traza detallada del pipeline en la consola; predeterminado desactivado) /
(redacta todos los detalles que identifican el binario para que el registro pueda
compartirse — consulte más abajo),
, (verifica certificados HTTPS;
predeterminado activado) / (paquete CA para HTTPS — consulte
Solución de problemas si encuentra ), y
/ /
(apunte estos a sus propios archivos de reglas para personalizar
detecciones y prompts).
Nota de seguridad: la clave API se almacena en los ajustes de Binary Ninja en texto plano. Prefiera la anulación mediante variable de entorno para claves sensibles.
Establezca vulnfanatic.apiBaseUrl al valor literal TEST para ejecutar sin ningún LLM:
/tmp/vulnfanatic_ng/<binary>-<timestamp>/.Úselo para inspeccionar y validar exactamente lo que VulnFanatic-NG enviaría al modelo, e iterar sobre los prompts/contexto de las reglas sin gastar tiempo de modelo.
Active vulnfanatic.debugLogging para imprimir una traza detallada, paso a paso, del
pipeline de escaneo (tanto en línea como fuera de línea) en el registro/consola de Binary Ninja: cada sitio
de llamada, cada decisión de omisión/eliminación, construcción de contexto (solo tamaño), cada solicitud LLM
(proveedor/modelo/endpoint, reintentos, fallbacks), cada veredicto y cada hallazgo
reportado. Las claves API nunca se registran.
Mientras el registro de depuración está activado, un escaneo en línea mantiene cada candidato en la tabla de resultados en lugar de descartar los que no se convierten en problemas confirmados, cada uno etiquetado con un estado solo de depuración (atenuado, ordenado al final):
Por lo tanto, un escaneo de depuración muestra una fila por candidato en el total /N, y el resumen informa
problemas frente a recuentos de rechazados/omitidos/error por separado. Puede hacer clic derecho en cualquiera de estas
filas para reclasificarlo como Problema real o Falso positivo (lo que lo hace elegible para
exportación de ajuste fino). (Los escaneos fuera de línea no se ven afectados — nunca llaman al LLM.)
Independientemente del modo de depuración, cuando un modelo devuelve una respuesta no analizable — un token
errático como <unused…> de Gemma, prosa en lugar de JSON, o un mensaje vacío (solo un
role, sin content) — el cliente realiza un reintento correctivo, pidiendo de nuevo JSON
solo con el formato de salida estructurada desactivado; si eso tiene éxito, mantiene el formato desactivado
para el resto del escaneo. El cliente también lee el canal de razonamiento
(reasoning_content / reasoning) cuando content está vacío, por lo que los modelos de razonamiento que colocan
su respuesta allí siguen funcionando.
El caso de mensaje vacío es común con modelos de razonamiento como GPT-OSS / o1 servidos
a través de una API compatible con OpenAI (ej. mlx-community/gpt-oss-20b): con
response_format=json_object establecido, el canal de respuesta "final" de armonía a menudo se suprime
y el servidor devuelve {"role": "assistant"} sin contenido. Estos modelos también pueden quemar
todo su presupuesto de salida en el canal de razonamiento y ser truncados a mitad de pensamiento,
devolviendo prosa sin ningún JSON. El reintentado automático recupera los casos relacionados con el formato; si
persiste, desactive vulnfanatic.sendJsonResponseFormat, reduzca
vulnfanatic.reasoningEffort (así que menos presupuesto va al pensamiento), y/o aumente
vulnfanatic.maxResponseTokens. Una respuesta persistente de <unused…>/basura en su lugar generalmente
significa que el prompt supera la ventana de contexto del modelo (establezca vulnfanatic.modelContextWindow
y/o aumente la longitud de contexto del servidor), o que el modelo es una mala opción para salida JSON
estricta (un modelo de código como Qwen2.5-Coder se comporta mucho mejor que Gemma aquí).
Retroceso conservador de recuerdo. Cuando un candidato aún no puede puntuarse después del reintento,
vulnfanatic.flagUnparseableResponses (predeterminado activado) lo reporta de todos modos como un
hallazgo "Unscored" con confianza UNKNOWN — un valor distinto de low (el
modelo nunca produjo un veredicto, por lo que no es un juicio de baja confianza) que se ordena al
final — manteniendo la salida parcial del modelo como explicación, para que no pierda el
sitio, simplemente lo revisa manualmente. Desactívelo para descartar dichos sitios (entonces
aparecen solo como errores de análisis, o filas ERROR de depuración).
También active vulnfanatic.debugAnonymous para hacer que el registro sea seguro de compartir:
redacta todo lo que pueda identificar el archivo analizado — los nombres de símbolos/variables y
direcciones se convierten en hashes salados por ejecución (aún consistentes dentro de una ejecución para que el flujo sea
seguible), el nombre del archivo se oculta, el texto del hallazgo se reemplaza con <redacted>, el
host del endpoint LLM se hashea, y el código descompilado / prompts / contexto se registran solo como
tamaños (nunca el contenido). Así puede enviar un registro de depuración para informar un problema sin
revelar nada sobre su binario.
Los hallazgos — incluido su estado de falso positivo — se almacenan en la base de datos de Binary Ninja.
Se escriben en el .bndb cuando guarda la base de datos (y
se vacían inmediatamente si ya existe un .bndb), por lo que sobreviven al reabrir.
El escaneo analiza cada sitio de llamada coincidente (sin límite), lo que es apropiado para modelos locales. Para un endpoint alojado/de pago, tenga en cuenta el volumen en binarios grandes.
Ambos archivos de reglas comparten un envoltorio con un system_prompt compartido y
output_schema, más una lista de rules. Copie un archivo empaquetado, edite las
funciones/palabras clave/prompts, y apunte vulnfanatic.rulesPhase1Path /
vulnfanatic.rulesPhase2Path a su copia. Las reglas de la Fase 1 coinciden por functions (exacto)
y name_regex; las reglas de la Fase 2 coinciden por name_keywords, name_regex, y
string_keywords. El prompt de cada regla puede usar el marcador de posición {function}.
Las exportaciones triadas están diseñadas para alimentarse directamente de vuelta al modelo. Después de
triar hallazgos en varios binarios y hacer clic en Exportar triados
(ajuste fino)… en cada uno (recolectando los archivos .jsonl en una carpeta),
scripts/finetune_mlx.py ejecuta un ajuste fino LoRA de MLX
sobre ellos.```bash
pip install mlx-lm # Apple Silicon / macOS
python scripts/finetune_mlx.py ./exports
--model mlx-community/Qwen2.5-Coder-7B-Instruct-4bit
--adapter-path ./vf-adapters --iters 800
python scripts/finetune_mlx.py ./exports --model
--fuse --fused-path ./vf-qwen-coder-vuln
El script toma la **carpeta de datos de entrenamiento** como su argumento posicional y el
**`--model`** base (ruta local o ID de repositorio MLX/HF); otros parámetros son opcionales:
`--adapter-path`, `--valid-split` (0.1), `--iters`, `--batch-size` (ajustado automáticamente para
ajustarse a una división pequeña), `--num-layers`, `--learning-rate`, `--max-seq-length`
(`0` = **autoajuste** al ejemplo más largo, limitado a 16384; establezca un valor positivo para
forzarlo), `--fine-tune-type` (`lora`/`dora`/`full`), `--seed`, `--fuse`/`--fused-path`,
y `--dry-run` (preparar datos + imprimir el comando sin entrenar). Todo después de un
literal `--` se reenvía textualmente a `mlx_lm lora`. Fusiona recursivamente cada
`*.jsonl` en la carpeta, valida y **desduplica** los ejemplos de chat, crea la
división `train.jsonl`/`valid.jsonl` que MLX espera, luego lanza `python -m mlx_lm lora`
(y `mlx_lm fuse` con `--fuse`).
Sirva el resultado con un servidor compatible con OpenAI (`mlx_lm.server --model <ruta>`)
y apunte `vulnfanatic.apiBaseUrl` de vuelta a él para escanear con su modelo ajustado.
> Los contextos de VulnFanatic-NG son grandes, por lo que, de forma predeterminada, el script **autoajusta**
> `--max-seq-length` a su ejemplo más largo (redondeado hacia arriba, limitado a **16384 tokens**).
> Las secuencias largas dominan la memoria de entrenamiento, por lo que un modelo grande cerca de este límite puede
> dejar sin memoria a una Mac más pequeña. Si sus ejemplos exceden el límite, se truncan — pase un
> `--max-seq-length` más alto (más memoria) o reduzca `vulnfanatic.storedContextChars`
> antes de exportar. Si el entrenamiento es interrumpido por una señal (ej. `exit -10` / SIGBUS), eso es
> un fallo por falta de memoria: reduzca `--max-seq-length`, agregue `-- --grad-checkpoint`, o use un
> modelo más pequeño.
---
## Desarrollo y pruebas
El plugin tiene cero dependencias de terceros requeridas. Los módulos puros
(`rules`, `tokens`, `llm`, `findings`, `settings`, `prototypes`) están cubiertos por una
suite de pruebas fuera de línea que no necesita ni Binary Ninja ni una red. La suite `tests/`
reside en el repositorio fuente del proyecto (no se envía dentro del
plugin publicado); ejecútela desde allí. Desde el directorio del paquete aún puede
verificar la sintaxis de cada módulo:```
python3 -m py_compile *.py ui/*.py
python3 -m unittest discover -s tests # from the source repository
Los módulos que interactúan con Binary Ninja (context_builder, phase1, phase2) se importan
limpiamente sin Binary Ninja (su acceso a la API está protegido) pero requieren un Binary Ninja
en ejecución para ejercitarse.
strcpy de argv[1] en un
búfer fijo en la pila y llame a system() con la entrada). Compile con símbolos
para ejercitar también la Fase 2.vulnfanatic.apiBaseUrl,
vulnfanatic.apiKey y vulnfanatic.model.SSL: CERTIFICATE_VERIFY_FAILED ... unable to get local issuer certificate —
el certificado del endpoint HTTPS es correcto, pero el Python incluido con Binary Ninja no tiene
un paquete de CA para verificarlo (común en macOS y en Pythons integrados; verá
esto con endpoints alojados como AWS Bedrock, Anthropic, Google, Azure). Solución con una de
las siguientes, en orden de preferencia:
pip install certifi.
VulnFanatic-NG lo detecta automáticamente.vulnfanatic.caBundlePath en un archivo de paquete (o
directorio) — por ejemplo, la ruta impresa por python3 -m certifi, o /etc/ssl/cert.pem.vulnfanatic.tlsVerify (solo para un endpoint de confianza/interno
o un servidor local autofirmado — esto deshabilita la verificación del certificado).HTTP 400 ... tokenizer.chat_template is not set — el modelo que está sirviendo
no tiene una plantilla de chat, por lo que el endpoint /chat/completions no puede formatear los
mensajes. VulnFanatic-NG vuelve automáticamente al endpoint /completions para el
resto del escaneo cuando ve este error, por lo que el escaneo continúa. Para evitar la
primera solicitud fallida por completo, establezca vulnfanatic.apiMode en completions. Alternativamente,
arréglelo del lado del servidor sirviendo un modelo que incluya una plantilla de chat, o pase una a
su servidor — por ejemplo, para vLLM: --chat-template <template.jinja> (o use una
variante de modelo -Instruct/-Chat). La plantilla de chat dedicada generalmente da
mejores resultados que el prompt plano de completions.
No JSON object found ... response looks truncated — la respuesta del modelo se cortó
antes de que el JSON terminara. Dos causas:
vulnfanatic.maxResponseTokens.maxResponseTokens. Los servidores locales a menudo tienen una ventana pequeña (ollama
predeterminado num_ctx=2048). Solución: establezca vulnfanatic.modelContextWindow al valor de la
ventana de su servidor (por ejemplo, ollama num_ctx, llama.cpp -c, vLLM --max-model-len) —
VulnFanatic-NG entonces limita automáticamente el contexto que envía para que el prompt + la respuesta quepan.
También mantenga vulnfanatic.maxResponseTokens razonable (≈8192, no 65535) y/o aumente la
ventana del servidor. Las ventanas pequeñas (≤8k) no pueden contener el contexto interprocedimental completo;
use un modelo/servidor configurado para 32k+.IncompleteRead / Could not complete request ... after N attempt(s) — el
servidor aceptó la solicitud pero cerró la conexión antes de enviar la respuesta
completa. Esto casi siempre significa que el servidor del modelo se cayó o se estancó durante la generación:
falta de memoria (contexto grande + salida larga), un tiempo de espera interno/del trabajador, o un proxy
reiniciando la conexión. VulnFanatic-NG reintenta una vez automáticamente y luego omite ese
sitio. Revise los registros del servidor del modelo para la causa real; reducir
vulnfanatic.maxContextTokens y/o vulnfanatic.maxResponseTokens, o darle al servidor más
memoria / una ventana de contexto más grande, generalmente lo resuelve.
vulnfanatic.phase2ForceEnable para también auditar coincidencias basadas en nombres, o
vulnfanatic.phase2RequireSymbols=off. La compuerta de símbolos es una heurística.response_format=json_object; el cliente
lo tolera y aún así extrae JSON. Desactive vulnfanatic.sendJsonResponseFormat
si su servidor rechaza el parámetro por completo.Apache-2.0 (© Martin Petran) — consulte plugin.json.
switchMAIN→ABCD→strcpy, también las funciones que MAIN y ABCD llaman
en otros lugares), ya que pueden contener las comprobaciones de límites/validación
que protegen el valor peligroso (vulnfanatic.includeCallPathSiblings, completado
mientras el presupuesto lo permita), yrecv/read/getenv
llamadas en la misma función).mediumhighvulnfanatic.skipConstantArgCalls (por defecto desactivado) — salta sitios
de llamada de clase de desbordamiento cuyos argumentos son todos constantes en
tiempo de compilación.| Setting | Significado |
|---|
vulnfanatic.apiProvider | Qué backend LLM llamar: openai (predeterminado), anthropic, google, o azure. Consulte Backends LLM más abajo. Todos los proveedores se alcanzan mediante la biblioteca estándar de Python — no necesita pip install. |
vulnfanatic.apiBaseUrl | Base del endpoint para el proveedor seleccionado (consulte la tabla más abajo). Predeterminado http://localhost:8080/v1. Establézcalo al literal TEST para habilitar el modo de prueba (véase más abajo). |
vulnfanatic.apiKey | Clave API / token de portador. Puede estar en blanco para servidores locales. Anulado por las variables de entorno VULNFANATIC_API_KEY o OPENAI_API_KEY. |
vulnfanatic.model | Requerido (excepto en modo de prueba). El identificador del modelo (para azure, el nombre de despliegue). |
vulnfanatic.apiMode | Solo openai: chat (predeterminado, /chat/completions) vs completions (prompt único aplanado — para modelos base/instruct servidos sin plantilla de chat). |
vulnfanatic.azureApiVersion | Solo azure: el parámetro de consulta api-version (predeterminado 2024-10-21). |
| Proveedor | apiBaseUrl | Autenticación | Notas |
|---|
openai | su servidor, ej. http://localhost:8080/v1 | Authorization: Bearer | Chat/Completions compatible con OpenAI: llama.cpp / ollama / vLLM local, OpenAI, y el endpoint compatible con OpenAI de AWS Bedrock. |
anthropic | en blanco → https://api.anthropic.com | x-api-key + anthropic-version | Messages API de Claude (POST <base>/v1/messages). No se envía temperature (los modelos Claude actuales lo rechazan). |
google | en blanco → https://generativelanguage.googleapis.com | Clave API en la URL | generateContent de Gemini (<base>/v1beta/models/<model>:generateContent). |
azure | https://<resource>.openai.azure.com | Cabecera api-key | Azure OpenAI; establezca model en el nombre de despliegue y azureApiVersion en su versión de API. |
vulnfanatic.callPathMaxPathsvulnfanatic.callPathIncludeBodiesvulnfanatic.callPathMaxBodiesvulnfanatic.includeCallPathSiblingsvulnfanatic.callPathSiblingMaxBodiesvulnfanatic.includeDataTypesvulnfanatic.maxTypeDefsvulnfanatic.includeVariableDataflowvulnfanatic.dataflowMaxFunctionsvulnfanatic.includeStackLayoutvulnfanatic.scanIndirectCallsvulnfanatic.validationPassvulnfanatic.validatorModelvulnfanatic.validatorProvidervulnfanatic.validatorBaseUrlvulnfanatic.validatorApiKeyvulnfanatic.minConfidencelowmediumhighlowvulnfanatic.flagUnparseableResponsesUNKNOWNvulnfanatic.skipConstantArgCallsvulnfanatic.verdictReasoningconcisefullnoneconcisevulnfanatic.runPhase1vulnfanatic.runPhase2vulnfanatic.runPhase3vulnfanatic.phase2RequireSymbolsvulnfanatic.phase2ForceEnablevulnfanatic.tokenizerEncodingvulnfanatic.offlineBuildContextvulnfanatic.debugLoggingvulnfanatic.debugAnonymousvulnfanatic.sendJsonResponseFormatvulnfanatic.tlsVerifyvulnfanatic.caBundlePathCERTIFICATE_VERIFY_FAILEDvulnfanatic.rulesPhase1Pathvulnfanatic.rulesPhase2Pathvulnfanatic.rulesPhase3Path