Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
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.

FeedsContatoPrivacidade© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
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órioSite
55468há 11 diasRevisado pelo Kitploit

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 corresponde funções em binários pelo que elas fazem, não pela aparência dos 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 transforma esse comportamento em um hash de fingerprint. duas funções que se comportam da mesma forma recebem fingerprints semelhantes, mesmo que tenham sido compiladas por um compilador diferente, em um nível de otimização diferente ou para uma CPU diferente.

o motivo de fazer assim: assinaturas de bytes (FLIRT, FunctionID) quebram no momento em que o código é recompilado, e matchers de CFG (BinDiff, Diaphora) ficam instáveis entre -O0 e -O3 e desistem entre arquiteturas. o comportamento sobrevive a tudo isso muito melhor. sem dados de treinamento, sem modelo, um único binário estático.

três coisas que ele faz que os outros não fazem:

  • é seguro apontá-lo para malware. o emulador roda em um worker com privilégios separados sob uma jaula seccomp que proíbe exec, sockets, abertura de arquivos e memória executável de forma absoluta (o unicorn é compilado como interpretador sem JIT para que nunca precise de uma). um binário malicioso que escape do emulador obtém um processo que só consegue calcular e nada mais. veja SECURITY.md. BinDiff, Diaphora, BSim, FLIRT e os matchers de ML todos analisam bytes hostis no próprio processo.
  • ele faz correspondência entre arquiteturas. indexe o build x86-64 para o qual você tem símbolos, nomeie o firmware aarch64 sem símbolos. o modelo de efeitos nunca vê a ISA. zlib x86-64 -O0 vs aarch64 -O0: 97,7% das funções nomeadas corretamente.
  • triage dá um veredito, não um diff. dois corpora (vulnerável conhecido, corrigido), uma margem e uma fila de revisão das funções que pendem para o lado vulnerável. esse é o fluxo de trabalho de n-day como um único comando.

ELF x86-64 e aarch64. veja limites antes de confiar nele.

mostre-me

fnprint nomeando funções em um binário sem símbolos e compilado de forma diferente

aponte-o para um binário sem símbolos e um corpus de coisas para as quais você já tem nomes:

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

essa execução é um build -O0 totalmente sem símbolos nomeado a partir de um corpus -O2. nível de otimização diferente, zero símbolos restantes, 27 nomes retornam e todos os 27 estão certos. ele nomeia aquilo sobre o que tem confiança e fica quieto sobre o resto. (o gif acima é da versão 0.5, que encontrou 9; a 0.6 também recupera as funções estáticas que um .so sem símbolos esconde atrás das suas exportações.)

a mesma coisa entre CPUs. corpus do build x86-64, o alvo é o build aarch64 do mesmo código-fonte, sem símbolos:

$ 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 nomeadas, 42 certas, a partir de um corpus construído para uma CPU diferente.

a outra coisa que ele faz é comparar dois 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. funciona com ou sem nomes de símbolos: com eles, alinha por nome; sem eles, alinha os dois builds por comportamento mais o grafo de chamadas, então duas imagens de firmware totalmente sem símbolos ainda podem ser comparadas:

$ 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

para trabalho real de n-day existe o triage. construa um corpus a partir da versão conhecida como vulnerável de uma função e outro a partir da versão corrigida, depois classifique um build desconhecido contra ambos. uma função próxima do lado vulnerável e claramente separada do lado corrigido é o que você quer colocar na frente de um humano, não apenas uma única pontuação de correspondência que você precisa interpretar:

$ 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 em ambas as versões retornam como inconclusivas de propósito, elas não podem ser atribuídas a nenhum dos lados e não deveriam ser sinalizadas. --margin e --min-sim controlam o quão forte os dois lados precisam se separar antes de ele se comprometer.

como funciona

para cada função:

  • mapeia o binário e salta para a função com lixo nos registradores de argumentos.
  • 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 stubadas (registradas e depois ignoradas) para que nunca mergulhemos na libc e a execução permaneça sobre esta função.
  • registramos um fluxo de efeitos neutro em relação à arquitetura: quais buffers de argumentos 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, desvios 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 correspondentes, o que estima o quanto o comportamento de duas funções se sobrepõe. um índice de bandas LSH evita que as consultas comparem tudo contra tudo.
  • as chamadas são nomeadas apenas a partir da tabela de importação (stubs plt, slots got), porque essa é a parte que sobrevive ao strip. chamadas internas são anônimas na impressão e se tornam arestas do grafo de chamadas, a partir de uma varredura estática do corpo.
  • nomeação e comparação são um alinhamento global 1:1, não uma busca do melhor acerto por função: uma função do corpus pode ser reivindicada uma vez, melhor par primeiro, depois a atribuição é reavaliada duas vezes com um termo de grafo de chamadas (quantos dos chamadores e chamados emparelhados de um par estão emparelhados entre si). é isso que distingue dois gêmeos comportamentais e o que impede que 27 funções -O2 que todas fazem inline da mesma verificação de estado reivindiquem todas a mesma coisa.
Baixar ferramenta