Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

FeedContattoPrivacy© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
fnprint — Abbina le funzioni nei binari in base a ciò che fanno, non a come appaiono i loro byte. fingerprinting comportamentale delle funzioni tramite microesecuzione. | Kitploit
Strumenti/GitHubGitHub/1rhino2/fnprint
Analisi delle VulnerabilitàAnalisi Dinamica del Codice (DAST)Reverse EngineeringAnalisi MalwareAnalisi di BinariAnalisi del Firmware
GitHub1rhino2/fnprint

fnprint

Abbina le funzioni nei binari in base a ciò che fanno, non a come appaiono i loro byte. fingerprinting comportamentale delle funzioni tramite microesecuzione.

Vedi Repository
5546912 giorni faRevisionato da Kitploit
Sito web

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

fnprint

fnprint abbina le funzioni nei binari in base a ciò che fanno, non a come appaiono i loro byte o i loro grafi di flusso di controllo. esegue ogni funzione in un piccolo emulatore con input inventati, registra gli effetti collaterali che produce e calcola un hash di quel comportamento in un'impronta. due funzioni che si comportano allo stesso modo ottengono impronte simili, anche se sono state compilate da un compilatore diverso, a un livello di ottimizzazione diverso o per una CPU diversa.

il motivo di farlo in questo modo: le firme sui byte (FLIRT, FunctionID) si rompono nel momento in cui il codice viene ricompilato, e i matcher CFG (BinDiff, Diaphora) vacillano tra -O0 e -O3 e si arrendono tra architetture diverse. il comportamento sopravvive a tutto questo molto meglio. nessun dato di addestramento, nessun modello, un singolo binario statico.

tre cose che fa e che gli altri non fanno:

  • è sicuro puntarlo contro malware. l'emulatore gira in un worker con privilegi separati sotto una jail seccomp che vieta del tutto exec, socket, aperture di file e memoria eseguibile (unicorn è compilato come interprete no-JIT, quindi non ne ha mai bisogno). un binario malevolo che buca l'emulatore ottiene un processo che può solo calcolare e nient'altro. vedi SECURITY.md. BinDiff, Diaphora, BSim, FLIRT e i matcher ML analizzano tutti byte ostili in-process.
  • abbina tra architetture diverse. indicizza la build x86-64 di cui hai i simboli, dai un nome al firmware aarch64 strippato. il modello degli effetti non vede mai l'ISA. zlib x86-64 -O0 vs aarch64 -O0: 97,7% delle funzioni nominate correttamente.
  • triage ti dà un verdetto, non un diff. due corpora (noto-vulnerabile, patchato), un margine e una coda di revisione delle funzioni che pendono verso il vulnerabile. questo è il workflow n-day come un solo comando.

ELF x86-64 e aarch64. vedi limiti prima di fidarti.

mostrami

fnprint che nomina funzioni in un binario strippato e compilato diversamente

puntalo contro un binario strippato e un corpus di cose di cui hai già i nomi:

$ strip --strip-all mystery.so
$ nm mystery.so
nm: mystery.so: no symbols

$ fnprint index libz.so -o corpus.db          # a build you have symbols for
$ fnprint query mystery.so --corpus corpus.db --threshold 0.6
named 27 function(s):
  addr        sim    graph  name
  0x000022f9  100.0%     0%  adler32_z  (libz.so)
  0x00002a8d  100.0%     0%  compress2  (libz.so)
  0x00004500  100.0%     0%  deflateStateCheck  (libz.so)
  0x0000ad5d  100.0%     0%  inflateBackEnd  (libz.so)
  0x0000b69c  100.0%     0%  inflateStateCheck  (libz.so)
  0x00012f8c  100.0%     0%  _tr_tally  (libz.so)
  ...

quella esecuzione è una build -O0 completamente strippata nominata da un corpus -O2. livello di ottimizzazione diverso, zero simboli rimasti, 27 nomi tornano indietro e tutti e 27 sono corretti. nomina ciò di cui è sicuro e tace sul resto. (la gif sopra è dalla 0.5, che ne trovava 9; la 0.6 recupera anche le funzioni statiche che un .so strippato nasconde dietro le sue export.)

la stessa cosa tra CPU diverse. corpus dalla build x86-64, il target è la build aarch64 dello stesso sorgente, simboli spariti:

$ fnprint index libz_x86_64.so -o corpus.db
$ fnprint query libz_arm64_stripped.so --corpus corpus.db
named 42 function(s):
  addr        sim    graph  name
  0x00001af4  100.0%     0%  adler32_z  (libz_x86_64.so)
  0x0000263c  100.0%     0%  compress2  (libz_x86_64.so)
  0x00002958  100.0%   100%  x2nmodp  (libz_x86_64.so)
  0x00002a98  100.0%     0%  crc32_z  (libz_x86_64.so)
  0x000034c4  100.0%   100%  crc32_combine64  (libz_x86_64.so)
  ...

42 nominate, 42 corrette, da un corpus costruito per una CPU diversa.

l'altra cosa che fa è confrontare due build e dirti quali funzioni hanno cambiato comportamento, cosa comoda quando un fornitore rilascia un nuovo firmware e vuoi sapere cosa si è effettivamente mosso. funziona con o senza nomi dei simboli: con essi allinea per nome, senza allinea le due build su comportamento più grafo delle chiamate, quindi due immagini firmware completamente strippate si confrontano comunque:

$ fnprint match old.so new.so
aligned 38 function pairs by behavior + call graph (no symbol names needed)
  unchanged:  31
  changed:    7
  low-signal: 57 (too small to judge)

changed behavior (lowest similarity first):
   30.5%  compress_block
   43.0%  deflate_rle -> deflate_huff
   68.8%  deflate_slow

per il vero lavoro n-day c'è triage. costruisci un corpus dalla versione nota-vulnerabile di una funzione e uno dalla versione patchata, poi classifica una build sconosciuta rispetto a entrambi. una funzione vicina al lato vulnerabile e chiaramente separata dal lato patchato è ciò che vuoi mettere davanti a un umano, non solo un singolo punteggio di corrispondenza da interpretare:

$ fnprint index vuln.so    -o vuln.db
$ fnprint index patched.so -o patched.db
$ fnprint triage mystery.so --vuln vuln.db --patched patched.db
43 functions triaged: 1 look vulnerable, 0 patched, 42 inconclusive

review queue (vuln-leaning, strongest first):
   addr         vuln%  patched%  margin  matches
   0x000022f9  100.0     27.3   +72.7  adler32_z vs crc32_z

le 42 funzioni identiche in entrambe le versioni tornano inconcludenti di proposito, non possono essere attribuite a nessuno dei due lati e non dovrebbero essere segnalate. --margin e --min-sim controllano quanto i due lati devono separarsi prima che si pronunci.

come funziona

per ogni funzione:

  • mappa il binario e salta alla funzione con spazzatura nei registri degli argomenti.
  • qualsiasi lettura da memoria che non abbiamo predisposto restituisce un valore deterministico e la pagina viene mappata al volo. i puntatori selvaggi non fanno mai crashare l'esecuzione, e lo stesso input dà sempre la stessa traccia. questo è il trucco della microesecuzione di Godefroid.
  • le chiamate ad altre funzioni vengono stub-ate (registrate, poi saltate) così non ci tuffiamo mai in libc e l'esecuzione resta su questa funzione.
  • registriamo un flusso di effetti neutro rispetto all'architettura: quali buffer di argomenti e campi di struct legge e scrive, quali classi di valore scrive (una copia di un input, una piccola costante, un puntatore), le chiamate che fa, i rami che prende, cosa restituisce. gli indirizzi assoluti vengono scartati, si conservano solo offset e forme.
  • quel flusso viene trasformato in shingle e in una firma minhash. la similarità è la frazione di slot minhash corrispondenti, che stima quanto si sovrappone il comportamento di due funzioni. un indice a bande LSH evita che le query confrontino tutto con tutto.
  • le chiamate vengono nominate solo dalla tabella degli import (stub plt, slot got), perché è la parte che sopravvive a strip. le chiamate interne sono anonime nella stampa e diventano invece archi del grafo delle chiamate, da una scansione statica del corpo.
  • nominare e confrontare è un allineamento globale 1:1, non una ricerca del miglior riscontro per funzione: una funzione del corpus può essere rivendicata una sola volta, prima la coppia migliore, poi l'assegnazione viene ripunteggiata due volte con un termine del grafo delle chiamate (quanti dei chiamanti e dei chiamati accoppiati di una coppia sono accoppiati tra loro). è questo che distingue due gemelli comportamentali, ed è questo che impedisce a 27 funzioni -O2 che inlinano tutte lo stesso controllo di stato di rivendicarlo tutte.
Scarica lo strumento