Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
fnprint — Corresponder funções em binários pelo que elas fazem, não pela aparência de seus bytes. Impressão digital comportamental de funções via microexecução. | Kitploit
Ferramentas/GitHubGitHub/1rhino2/fnprint
Análise de VulnerabilidadesAnálise Dinâmica de Código (DAST)Engenharia ReversaAnálise de MalwareAnálise de BináriosAnálise de Firmware
GitHub1rhino2/fnprint

fnprint

Corresponder funções em binários pelo que elas fazem, não pela aparência de seus bytes. Impressão digital comportamental de funções via microexecução.

Ver Repositório
20215há 18h 29mAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

fnprint

fnprint encontra funções em binários pelo que elas fazem, não pela aparência de seus bytes ou grafos de fluxo de controle. Ele executa cada função em um pequeno emulador com entradas inventadas, registra os efeitos colaterais que ela produz e gera um fingerprint desse comportamento. Duas funções que se comportam da mesma forma obtêm fingerprints semelhantes, mesmo que tenham sido compiladas por um compilador diferente ou em um nível de otimização diferente.

A ideia de fazer dessa forma: assinaturas de bytes (FLIRT, FunctionID) quebram no momento em que o código é recompilado, e comparadores de CFG (BinDiff, Diaphora) ficam instáveis entre -O0 e -O3. O comportamento sobrevive muito melhor a ambos.

Apenas ELF x86-64 por enquanto. Veja limitações antes de confiar nele.

demonstração

Aponte-o para um binário stripped e um corpus de coisas para as quais você já tem nomes:

root@kitploit:~
$ 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
named 9 function(s):
  0x000022f9  100.0%  adler32_z
  0x00002a8d  100.0%  compress2
  0x0000ad5d  100.0%  inflateBackEnd
  0x0000adc4   86.7%  inflate_fast
  0x00002e67   80.5%  crc32_z
  ...

Essa execução é uma build totalmente stripped em -O0, identificada a partir de um corpus -O2. Nível de otimização diferente, zero símbolos restantes, e os nomes voltam corretos. Ele nomeia aquilo de que está confiante e fica em silêncio sobre o resto.

A outra coisa que ele faz é fazer diff de duas builds e dizer quais funções mudaram de comportamento, o que é útil quando um fornecedor lança um novo firmware e você quer saber o que realmente mudou:

root@kitploit:~
$ fnprint match old.so new.so
compared 84 functions present in both
  unchanged:  53
  changed:    1
  low-signal: 31 (too small to judge)

changed behavior (lowest similarity first):
   61.7%  deflate_stored

Para trabalho real de n-day existe o triage. Construa um corpus a partir da versão conhecidamente vulnerável de uma função e outro a partir da versão corrigida; depois classifique uma build desconhecida contra ambos. Uma função próxima do lado vulnerável e claramente separada do lado corrigido é o que você quer colocar diante de um humano, e não apenas uma pontuação única de correspondência que você precise interpretar:

root@kitploit:~
$ 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

As 42 funções idênticas nas duas versões retornam inconclusivas de propósito; elas não podem ser atribuídas a nenhum dos lados e não devem ser sinalizadas. --margin e --min-sim controlam o quão separados os dois lados precisam estar antes que ele se comprometa.

como funciona

Para cada função:

  • mapeia o binário e salta para a função com lixo nos registradores de argumento.
  • qualquer leitura de memória que não configuramos retorna um valor determinístico e a página é mapeada na hora. Ponteiros selvagens nunca derrubam a execução, e a mesma entrada sempre produz o mesmo trace. Esse é o truque de microexecução de Godefroid.
  • chamadas a outras funções são substituídas por stubs (registradas e depois puladas), para nunca mergulharmos na libc e a execução continuar sobre esta função.
  • registramos um fluxo de efeitos neutro em relação à arquitetura: quais buffers de argumento e campos de struct ele lê e escreve, quais classes de valor ele escreve (uma cópia de uma entrada, uma constante pequena, um ponteiro), chamadas que faz, ramos que toma, o que retorna. Endereços absolutos são descartados; apenas offsets e formas são mantidos.
  • esse fluxo é transformado em shingles e uma assinatura minhash. A similaridade é a fração de slots minhash coincidentes, o que estima o quanto o comportamento de duas funções se sobrepõe. Um índice de bandas LSH evita que consultas comparem tudo contra tudo.

Sem dados de treinamento, sem modelo. A mesma ideia aparece na literatura como Blanket Execution (Egele et al., USENIX Security 2014); fnprint é uma abordagem prática e mantida dessa ideia, com uma CLI que você pode realmente usar.

instalação

Requer uma toolchain Rust e as bibliotecas unicorn + capstone.

root@kitploit:~
# debian/ubuntu/kali
sudo apt install libunicorn-dev libcapstone-dev

cargo install --path cli
# or just
cargo build --release   # binary at target/release/fnprint

uso

root@kitploit:~
fnprint index <binary> [-o out.db]      fingerprint every function, optionally to a db
fnprint match <a> <b>                   diff two binaries (or .db files) by behavior
fnprint query <target> --corpus <db>    name unknown functions from a corpus
fnprint triage <t> --vuln <db> --patched <db>   rank a build against vuln vs patched corpora
fnprint eval <a> <b>                     accuracy metrics using symbol names as truth
fnprint dump <binary> <func>            print the recorded effect trace (debugging)

match e query aceitam um ELF ou um .db que você construiu com index, para que você possa gerar a fingerprint de um corpus uma vez e reutilizá-lo.

precisão

A precisão rank-1 é: para uma função na build A, classificar todas as funções na build B por similaridade; o primeiro resultado é o correto? Essa é exatamente a tarefa de nomear binários sem símbolos. Medido no zlib 1.3.1 (84 funções), reproduzível com bench/run.sh:

Tabela completa, além de uma segunda biblioteca (lua), em bench/NUMBERS.md.

eval também reporta recall@3 / recall@5 e uma taxa de abstenção, já que apenas o rank-1 esconde muita coisa. Em gcc O0 -> O2, o primeiro resultado está certo 93% das vezes, mas a função correta está no top 5 em 96,6% das vezes; portanto, um pequeno orçamento de revisão fecha a maior parte da lacuna. Ele também se abstém (recusa uma classificação confiante de "iguais") nos pares sobre os quais não tem certeza, em vez de adivinhar; é por isso que a precisão permanece alta enquanto o recall no mesmo limite é baixo.

A leitura honesta: quando pelo menos um dos lados tem alguma riqueza comportamental (qualquer coisa com -O0/-O1, ou um par entre compiladores diferentes em -O0), ele fica na faixa de 80-98%. Quando ambos os lados são fortemente otimizados, o comportamento que conseguimos observar fica ralo e ele cai para algo próximo de um cara ou coroa. Essa é a fronteira difícil para um comparador de passagem única e sem treinamento, e ele não finge o contrário.

limitações

  • funções minúsculas. Thunks e acessores de uma linha não fazem o suficiente para gerar fingerprint, então ele os omite (esses são os contadores de "low-signal" e "with enough signal").
  • cálculo puro. Duas checksums que ambas leem um buffer e retornam um número parecem iguais, porque do lado de fora quase são.
  • lógica profunda atrás de uma pré-condição real. A microexecução com entrada inventada exercita o comportamento de entrada de uma função. Uma mudança enterrada em um estado que nunca alcançamos com entrada inventada não aparecerá em match. Ele captura mudanças estruturais e de caminho inicial, não qualquer ajuste profundo.
  • otimização pesada em ambos os lados, como mostram os números acima.
  • ofuscação pesada (especialmente baseada em VM) vai destruí-lo.

trabalhos anteriores e onde isso se encaixa

  • FLIRT / FunctionID / Lumina: assinaturas de bytes. Exatas, rápidas, quebram ao recompilar.
  • BinDiff / Diaphora: estrutura de grafo. Boas, mas frágeis entre otimizações e arquiteturas.
  • Ghidra BSim: vetores de características do decompilador. Mais próximo em espírito, praticamente de uma única arquitetura.
  • Asm2Vec / SAFE / jTrans: embeddings aprendidos. Fortes, mas precisam de treinamento e não generalizam para arquiteturas em que ninguém treinou.
  • microexecução (Godefroid, 2014) e Blanket Execution (Egele et al., 2014): as raízes acadêmicas dessa abordagem. Nenhuma ferramenta mantida a lançou.

fnprint é a opção sem treinamento e com foco em comportamento. O modelo de efeitos já é neutro em relação à arquitetura, o que é a base para a correspondência entre CPUs.

roteiro

  • arm64 e mips, para você gerar a fingerprint de uma função em x86 e encontrá-la em um firmware de roteador sem símbolos. O modelo de efeitos já é neutro em relação à arquitetura; isso é principalmente infraestrutura de emulador por arquitetura.
  • cobertura de caminhos mais inteligente. A inversão ingênua de branch está no código (explore_depth, desativada por padrão), mas adiciona ruído específico de build em caminhos impossíveis e prejudica a precisão entre builds nos testes; por isso precisa de um filtro de consistência de caminhos antes de merecer seu lugar.
  • carregadores de PE e Mach-O.
  • plugins para ghidra / ida que chamam a CLI e renomeiam no lugar as funções correspondentes.

licença

MIT. Veja LICENSE.

Baixar ferramenta
parrank-1precisão
gcc O0 -> O197.1%93.8%
gcc O0 -> O293.1%83.3%
gcc O0 -> O391.3%100.0%
gcc/clang O097.7%97.1%
gcc O2 -> O356.5%66.7%
gcc/clang O259.1%50.0%