Skip to content
KitploitKITPLOIT
OutilsExploitsBlog
Log in
Soumettre
OutilsExploitsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

FluxContactConfidentialité© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
fnprint — Faire correspondre les fonctions dans les binaires par ce qu'elles font, et non par l'apparence de leurs octets. Empreinte comportementale de fonctions via micro-exécution. | Kitploit
Outils/GitHubGitHub/1rhino2/fnprint
Analyse des VulnérabilitésAnalyse Dynamique de Code (DAST)Rétro-ingénierieAnalyse de MalwareAnalyse de BinairesAnalyse de Micrologiciel
GitHub1rhino2/fnprint

fnprint

Faire correspondre les fonctions dans les binaires par ce qu'elles font, et non par l'apparence de leurs octets. Empreinte comportementale de fonctions via micro-exécution.

Voir le dépôt
55467il y a 11 joursVérifié par Kitploit
Site web

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

fnprint

fnprint fait correspondre les fonctions dans les binaires par ce qu'elles font, et non par l'apparence de leurs octets ou de leurs graphes de flot de contrôle. il exécute chaque fonction dans un minuscule émulateur avec des entrées inventées, enregistre les effets de bord qu'elle produit, et hache ce comportement en une empreinte. deux fonctions qui se comportent de la même manière obtiennent des empreintes similaires, même si elles ont été compilées par un compilateur différent, à un niveau d'optimisation différent, ou pour un CPU différent.

l'intérêt de procéder ainsi : les signatures d'octets (FLIRT, FunctionID) se cassent dès que le code est recompilé, et les matchers de CFG (BinDiff, Diaphora) deviennent instables entre -O0 et -O3 et abandonnent entre architectures. le comportement survit à tout cela bien mieux. pas de données d'entraînement, pas de modèle, un seul binaire statique.

trois choses qu'il fait que les autres ne font pas :

  • il est sûr de le pointer vers un malware. l'émulateur s'exécute dans un worker à privilèges séparés sous une jail seccomp qui interdit purement et simplement exec, les sockets, l'ouverture de fichiers et la mémoire exécutable (unicorn est compilé comme interpréteur sans JIT afin de ne jamais en avoir besoin). un binaire conçu pour s'échapper de l'émulateur obtient un processus qui peut calculer et rien d'autre. voir SECURITY.md. BinDiff, Diaphora, BSim, FLIRT et les matchers ML analysent tous des octets hostiles dans le processus.
  • il fait correspondre entre architectures. indexez la build x86-64 pour laquelle vous avez les symboles, nommez le firmware aarch64 strippé. le modèle d'effets ne voit jamais l'ISA. zlib x86-64 -O0 vs aarch64 -O0 : 97,7 % des fonctions nommées correctement.
  • triage vous donne un verdict, pas un diff. deux corpus (vulnérable connu, patché), une marge, et une file de revue des fonctions qui penchent vers le vulnérable. c'est le workflow n-day en une seule commande.

ELF x86-64 et aarch64. voir limites avant de lui faire confiance.

montrez-moi

fnprint nommant des fonctions dans un binaire strippé et compilé différemment

pointez-le vers un binaire strippé et un corpus de choses que vous avez déjà nommées :

$ 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)
  ...

cette exécution est une build -O0 entièrement strippée nommée à partir d'un corpus -O2. niveau d'optimisation différent, zéro symbole restant, 27 noms reviennent et tous les 27 sont corrects. il nomme ce dont il est confiant et reste silencieux sur le reste. (le gif ci-dessus provient de la 0.5, qui en trouvait 9 ; la 0.6 récupère aussi les fonctions statiques qu'un .so strippé cache derrière ses exports.)

la même chose entre CPU. corpus issu de la build x86-64, la cible est la build aarch64 de la même source, symboles disparus :

$ 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 nommées, 42 correctes, à partir d'un corpus construit pour un CPU différent.

l'autre chose qu'il fait est de differ deux builds et de vous dire quelles fonctions ont changé de comportement, ce qui est pratique quand un fournisseur livre un nouveau firmware et que vous voulez savoir ce qui a réellement bougé. il fonctionne avec ou sans noms de symboles : avec, il aligne par nom, sans, il aligne les deux builds sur le comportement plus le graphe d'appels, donc deux images de firmware entièrement strippées se diffent quand même :

$ 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

pour le vrai travail n-day il y a triage. construisez un corpus à partir de la version vulnérable connue d'une fonction et un à partir de la version patchée, puis classez une build inconnue par rapport aux deux. une fonction proche du côté vulnérable et clairement séparée du côté patché est ce que vous voulez mettre devant un humain, pas juste un score de correspondance unique que vous devez interpréter :

$ 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

les 42 fonctions identiques dans les deux versions reviennent non concluantes à dessein, elles ne peuvent être rattachées à aucun des deux côtés et ne devraient pas être signalées. --margin et --min-sim contrôlent à quel point les deux côtés doivent se séparer avant qu'il ne tranche.

comment ça marche

pour chaque fonction :

Télécharger l’outil