Lingue: English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Français | Deutsch | Español | Italiano | Русский | العربية
NeverD può caricare un file Python come plugin di prima classe. I plugin Python condividono con quelli nativi gli stessi metadati, ciclo di vita, ordinamento, regole sui nomi duplicati, flusso di eventi e ABI C della sessione. Il pacchetto supportato per lo sviluppo è neverd-plugin; non importare direttamente il bridge privato _neverd_plugin.
NEVERD_ENABLE_PYTHON_PLUGINS è ON per impostazione predefinita. Una build abilitata richiede un interprete CPython 3.10 o successivo e la relativa libreria di sviluppo per l’embedding, rilevabili da CMake:
cmake -S . -B build -G Ninja \
-DNEVERD_ENABLE_PYTHON_PLUGINS=ON \
-DPython3_EXECUTABLE="$(python3 -c 'import sys; print(sys.executable)')"
cmake --build buildImpostare -DNEVERD_ENABLE_PYTHON_PLUGINS=OFF per ottenere una libneverd esclusivamente nativa, senza dipendenza di link da CPython. Una build con Python copia il pacchetto corrispondente e gli esempi in build/bin/sdk/python/; la directory è installabile direttamente anche con python3 -m pip install build/bin/sdk/python.
Ogni modulo dichiara esattamente una classe decorata:
from neverd_plugin import Event, Plugin, PluginType, Session
@Plugin(
name="Analysis Report",
version="1.0.0",
author="Your team",
description="Reports basic information about the loaded binary",
type=PluginType.PROCESSOR,
)
class AnalysisReport:
def on_init(self, session: Session) -> int | None:
print(session.architecture)
return None
def on_run(self, session: Session, arg: int) -> int | None:
print(session.file_path, session.function_count)
return 0
def on_event(self, event: Event) -> int | None:
print(event.type.name)
return None
def on_term(self) -> None:
passTutti gli hook sono facoltativi. None indica il successo; un risultato intero deve rientrare in un int C. Le versioni dei metadati usano SemVer rigoroso. I nomi devono essere stringhe UTF-8 non vuote e qualsiasi metadato con un NUL incorporato viene rifiutato.
Gli esempi nel repository sono minimal.py, analysis_report.py e semantic_optimizer.py, che mostra le API di ottimizzazione vincolate alla prova.
L’API C può caricare in modo deterministico uno specifico file .py oppure esaminare una directory:
if (!neverd_plugins_load_file(session, "plugins/report.py")) {
const char *message = neverd_last_error(session);
/* log message */
neverd_free_string(message);
}
neverd_plugins_init(session);
int result = neverd_plugins_run(session, "Analysis Report", 0);
neverd_plugins_term(session);neverd_plugins_list_json identifica ogni voce con "kind":"python" o "kind":"native". La ricerca nelle directory è ordinata per percorso canonico e accetta librerie native e file Python nella stessa directory. Percorsi canonici duplicati e nomi di plugin duplicati sono errori.
Session riconvalida le capacità dell’host prima di ogni chiamata C. La sua interfaccia tipizzata include metadati di file, architettura e formato, bitness e conteggi delle tabelle, viste delle funzioni, caricamento e analisi, lettura di byte, disassemblaggio, decompilazione e query comuni. Per le operazioni avanzate, session.raw espone ogni dichiarazione in neverd_plugin.abi:
count = session.raw.session_call("neverd_plugins_count")
version = session.raw.owned_string("neverd_version")
object_bytes = session.raw.session_borrowed_bytes("neverd_roundtrip_obj")Per le funzioni LowIR native, session.symbolic_explore restituisce risultati di percorso tipizzati, tracce dei blocchi di base, utilizzo delle risorse e predicati di percorso facoltativi:
result = session.symbolic_explore(
0x401000,
max_paths=64,
max_steps=1 << 16,
max_block_visits=3,
include_expressions=True,
)
if not result.exact:
print(result.unmodelled_ops)
for path in result.paths:
print(path.outcome, path.blocks, path.predicate)complete è false quando un limite di percorsi, passi, visite del ciclo o rami non risolti interrompe l’esplorazione. exact richiede inoltre che nessuna operazione sia stata sostituita in modo conservativo da uno stato sconosciuto; le operazioni LowIR non supportate, le chiamate senza riepilogo e le scritture tramite indirizzi non risolti vengono conteggiate in unmodelled_ops. Le sessioni EVM e SBF non espongono l’esplorazione LowIR nativa.
session.lowir_concolic segue un percorso LowIR nativo da intervalli di byte espliciti del registro di ingresso e restituisce solo i candidati del solver che una nuova esecuzione verifica nella stessa occorrenza della decisione di controllo:
from neverd_plugin import ConcolicRegisterSeed
report = session.lowir_concolic(
0x401000,
[ConcolicRegisterSeed(offset=56, bytes=4, value=0)],
)
for flip in report.flips:
if flip.candidate_id is not None:
print(report.candidates[flip.candidate_id].seed)L’offset del registro è un offset in byte nel file di registri NeverD, non un puntatore nativo o un numero di registro. Il report è sempre non esaustivo; UNSAT, i limiti del solver e i rifiuti di proiezione o replay restano risultati tipizzati, non eccezioni.
session.audit() e session.hunt() restituiscono report JSON analizzati (lo stesso schema del CLI). Richiedono una sessione nativa sollevata:
audit = session.audit()
hunt = session.hunt(max_paths=64, max_steps=1 << 16)
print(audit.get("ok"), hunt.get("findings"))Le sessioni EVM e SBF rifiutano queste chiamate.
Le sei varianti di evento immutabili sono BINARY_LOADED, BINARY_CLOSING, FUNCTION_SELECTED, ADDRESS_CHANGED, ANALYSIS_DONE e PATCH_APPLIED. Le stringhe del payload vengono copiate durante il callback; i campi non pertinenti alla variante sono None.
Non conservare mai una Session per usarla dopo la terminazione. La capsule nativa viene invalidata prima dell’inizio di on_term e prima che la sessione nativa possa essere liberata. Una chiamata successiva fallisce con RuntimeError invece di dereferenziare memoria obsoleta.
session.sanitize() esegue la transazione sperimentale tutto-o-rifiuta binary-sanitizer-v1 e restituisce un SanitizeResult immutabile solo dopo aver validato un receipt autenticato, completo e coerente. Gli host non Darwin rifiutano prima del lifting, della generazione delle guardie, della creazione del candidato o della modifica del namespace. PUBLISH_INDETERMINATE e PUBLISHED_INCOMPLETE sono errori: la destinazione può esistere e va ispezionata prima dell’uso o di un nuovo tentativo.
Un receipt Darwin completo autentica soltanto l’oggetto directory di destinazione mantenuto durante la transazione. Poiché può essere rinominato dopo l’apertura, il receipt non garantisce che il pathname originale continui a indicarlo durante o dopo il ritorno e non è un binding durevole e verificabile in modo indipendente. Chi riapre successivamente il percorso deve conservare un anchor esterno e riautenticare l’oggetto. Python non offre oggi replay nativo dell’intero processo; NativeProcessReplayAdapter è soltanto un confine C++ fail-closed di disponibilità/factory Phase 0, nel quale tutti gli host riportano tutte le capacità false e nessuna tabella di operazioni.
synthesize_expression è separata da simplify_expression, mantenuta per la
compatibilità ABI e limitata alle MBA. Una riscrittura viene confermata solo se
il solver restituisce ProofStatus.EQUIVALENT. Controesempi, prove incomplete e
budget di ricerca esauriti conservano l’espressione originale e riportano
separatamente esito e lavoro di ricerca e prova.
ProofStatus.INVALID identifica una richiesta di prova malformata e resta
distinto da ProofStatus.UNKNOWN, dovuto al budget; entrambi rifiutano la
riscrittura in modo fail-closed.
optimize_llvm_ir combina il punto fisso semantico di NeverD e la pipeline LLVM
standard selezionata su una copia transazionale, restituendo solo il modulo
verificato e confermato:
from neverd_plugin import (
LLVMOptimizationLevel,
OptimizationMode,
ProofStatus,
optimize_llvm_ir,
synthesize_expression,
)
rewrite = synthesize_expression(
"(x >> 4) + ((x >> 2) >> 2)", exhaustive=True
)
if rewrite.changed:
assert rewrite.proof_status is ProofStatus.EQUIVALENT
module = optimize_llvm_ir(
llvm_ir,
mode=OptimizationMode.DEEP,
llvm_level=LLVMOptimizationLevel.O2,
enable_synthesis=True,
exhaustive=True,
)
print(module.output_ir, module.semantic_rewrites, module.proof_queries)I client di produzione possono limitare separatamente lavoro e arità MBA,
ricerca di sintesi e lavoro SAT, e convergenza LLVM. Per
simplify_expression, exhaustive=True seleziona la politica MBA senza limiti
di arità e lavoro e rimuove i limiti di annidamento e larghezza del parser
nativo. Per synthesize_expression, rimuove i limiti del parser, del lavoro di
ricerca e di SAT mantenendo la grammatica specificata dal chiamante; per
optimize_llvm_ir, rimuove i limiti di convergenza, ricerca e SAT. Python non
aggiunge ulteriori limiti alle espressioni; restano validi i limiti di sicurezza
della memoria e di rappresentazione IR. Gli ingressi C sono
neverd_simplify_expr, neverd_synthesize_expr e neverd_optimize_llvm_ir,
con funzioni di rilascio tipizzate e adattatori JSON versionati.
Le eccezioni Python non attraversano mai C++ durante l’unwinding. NeverD acquisisce il traceback completo e formattato e lo espone tramite neverd_last_error. Ogni percorso canonico di plugin viene caricato con un nome di modulo univoco; alla terminazione il modulo viene rimosso e un caricamento successivo ottiene un nuovo stato di modulo e classe. CPython viene inizializzato una sola volta, il GIL di bootstrap viene rilasciato e i callback acquisiscono il GIL su qualsiasi thread host. NeverD non finalizza mai un interprete che potrebbe condividere con un altro componente.
I plugin eseguono Python arbitrario nel processo NeverD e possono chiamare l’intera API C. Caricare solo file attendibili. Questo è un confine di estensione, non una sandbox.
Per il supporto dell’editor e del type checker, installare il pacchetto Python puro oppure inserire l’albero dei sorgenti in PYTHONPATH:
python3 -m pip install -e pluginsdk/python
PYTHONPATH=pluginsdk/python python3 -m unittest discover \
-s pluginsdk/python/tests -v
python3 -m mypy --config-file pluginsdk/python/pyproject.toml \
pluginsdk/python/neverd_plugin
PYTHONPATH=pluginsdk/python python3 scripts/check_python_plugin_sdk.pyL’audit richiede la corrispondenza esatta fra ogni dichiarazione C esportata e la relativa firma ctypes e regola di proprietà. Controlla inoltre i valori del linguaggio di output, le versioni CMake e del pacchetto, i feature flag della CI, le versioni fissate delle Action, il flusso degli artefatti e la policy OIDC di PyPI. I test dell’adattatore nativo sono NeverDPluginRuntimeTests; quelli di Python integrato sono NeverDPythonRuntimeTests e NeverDPythonPluginTests.
Il workflow Python Plugin SDK crea un wheel e una distribuzione sorgente, installa entrambi in ambienti puliti e carica gli artefatti verificati. La pubblicazione avviene solo per una GitHub Release pubblicata, tramite l’environment pypi protetto da approvazione e Trusted Publishing; non viene usato alcun token PyPI a lunga durata.