Ghidra para Análise de Firmware: Um Guia Prático para Recuperar a Lógica de Binários
Este guia prático explora o uso do Ghidra para engenharia reversa de firmwares, focando em técnicas para recuperar a lógica de binários. Aprenda a definir endereços de carregamento, mapear memória, identificar funções por strings e muito mais.
MundiX News·27 de junho de 2026·10 min de leitura·👁 1 views
A engenharia reversa de firmwares é uma tarefa complexa que exige ferramentas poderosas e um entendimento profundo de como o software interage com o hardware. O Ghidra, uma ferramenta gratuita e extensível da NSA, tornou-se um recurso indispensável para pesquisadores de segurança e engenheiros reversos. Este artigo detalha um guia prático para utilizar o Ghidra na análise de firmwares, com o objetivo de recuperar a lógica de binários, abordando desde a configuração inicial até técnicas avançadas de análise.
O processo de engenharia reversa raramente é linear e pode exigir semanas para uma compreensão completa de um sistema. O Ghidra, apesar de seu alto limiar de entrada, oferece funcionalidades robustas para desvendar a complexidade de firmwares. Um dos primeiros passos cruciais é determinar o endereço de carregamento correto do firmware. Frequentemente, firmwares não são carregados no endereço zero da memória; em vez disso, os endereços iniciais são reservados para o ponteiro de pilha inicial e o vetor de reset. O CPU inicia a execução a partir do vetor de reset, permitindo a carga de diferentes firmwares, como o código de aplicação principal ou um bootloader de recuperação. Sem a configuração adequada do endereço de carregamento, o desassemblador pode gerar referências incorretas para funções, strings e outros objetos, levando a uma análise equivocada. Esse endereço pode ser determinado consultando o datasheet do chip, analisando a Tabela de Vetores de Interrupção (IVT), buscando referências a strings ou identificando outras estruturas de firmware conhecidas. A IVT, geralmente localizada no deslocamento zero, é uma tabela de endereços de manipuladores de interrupção. Configurar cada entrada como um ponteiro e desassemblar o manipulador de reset antes da Análise Automática pode otimizar a análise, garantindo que ela siga a ordem de execução correta.
Após definir o endereço de carregamento, é essencial configurar o mapa de memória no Ghidra. A memória flash, já presente com o endereço base definido, deve ser marcada como somente leitura, pois firmwares geralmente não escrevem na imagem carregada. Isso garante que strings e outras estruturas de dados somente leitura sejam exibidas corretamente. Além disso, é importante definir uma área de SRAM como leitura/escrita, utilizando os endereços do manual de referência do microcontrolador. Isso permite a modificação de tipos de variáveis e a inicialização de dados. Para microcontroladores ARM, o carregamento de arquivos SVD (System View Description) através de um script como o SVD-Loader é fundamental para mapear a localização dos registradores de periféricos, que interagem com sub-rotinas. Esses arquivos descrevem as localizações dos registradores e os carregam no Ghidra com tipos de dados bem definidos. Para arquiteturas como M68000, a localização dos periféricos é configurada dinamicamente através de registradores como VBR, RAMBAR e MBAR, que precisam ser identificados na sequência de inicialização do firmware.
A anotação extensiva é uma prática recomendada no Ghidra. Mesmo que uma função ou endereço de dados pareça insignificante, atribuir uma etiqueta (atalho 'l') ou um comentário (atalho ';') pode ser crucial posteriormente. Renomear variáveis e corrigir seus tipos de dados (Ctrl+l) melhora significativamente a legibilidade da descompilação, especialmente ao retornar a um projeto após um período. A busca por funções através de strings é um método direto, pois strings em firmwares muitas vezes servem como a única forma de documentação deixada pelo fornecedor. Logs de depuração, verificações de assert e mensagens de exceção podem fornecer protótipos de funções completos, permitindo renomear funções e definir parâmetros corretos. Tabelas de endereços podem conter cabeçalhos ou nomes de funções próximos aos ponteiros, que podem ser encontrados manualmente ou através de referências cruzadas. É importante não negligenciar strings que podem ser construídas na pilha, em vez de armazenadas em seções de dados estáticas. Isso pode ocorrer por otimização, ofuscação ou decisões de projeto relacionadas à alocação de memória e tempo de vida de objetos. O Ghidra pode não exibir essas atribuições de string de forma intuitiva, mas a anotação com tipos de dados corretos pode ajudar na visualização. A caça por valores mágicos, como cookies DHCP ou EtherType ARP, também pode ser uma forma eficaz de identificar funções. O uso de sistemas operacionais de tempo real (RTOS) adiciona outra camada de complexidade, com recursos globais compartilhados e alocação de memória mais dinâmica. Entender como as tarefas são iniciadas, identificando os ponteiros de função passados para as funções de criação de tarefas, é vital. O Ghidra BSim, um plugin de correspondência difusa de código, é excelente para encontrar funções de biblioteca. Ao compilar bibliotecas conhecidas (como RTOS ou stacks TCP/IP) com informações de depuração e carregá-las no Ghidra, é possível criar uma base de assinaturas de funções. O BSim pode então comparar as funções do firmware com essa base, identificando correspondências mesmo com versões diferentes de bibliotecas ou compiladores. A importação de arquivos de cabeçalho de bibliotecas conhecidas pode preencher o Gerenciador de Tipos de Dados com typedefs, estruturas e protótipos de funções, auxiliando na anotação da descompilação. Embora o parser de código-fonte do Ghidra tenha suas limitações, ele pode ser útil. As 'Bookmarks' no Ghidra, geradas durante a autoanálise ou criadas manualmente, podem revelar tabelas de endereços, funções não definidas e falhas de desassemblagem que exigem correção manual. Ao mergulhar no código assembly, especialmente quando a descompilação não é ideal (indicada por variáveis como 'in_', 'in_stack_', 'unaff_', 'extraout_'), é crucial entender as convenções de chamada da arquitetura, como os parâmetros são passados (pilha ou registradores) e a ordem de leitura. Funções variádicas ou com convenções de chamada específicas, como 'thiscall', podem precisar ser definidas manualmente na janela 'Edit Function Signature'. Finalmente, scripts e plugins, como o SVD-Loader, VxWorks Symbol Table Finder, Leaf Blower, FindCrypt e Stack String Explorer, podem automatizar tarefas e anotar funcionalidades de interesse. Modelos de Linguagem Grandes (LLMs) também se mostraram úteis para reconhecer rapidamente algoritmos comuns em código descompilado e assembly, embora a verificação dos resultados seja essencial. Ferramentas como GhidrAssist e GhidraMCP integram LLMs para análise autônoma e anotação de símbolos, mas a segurança de dados deve ser considerada ao usar LLMs com informações comercialmente sensíveis.
🛡️⚡
Pare de pesquisar. Comece a hackear.
O MundiX é seu copiloto de pentest com IA: comandos exatos, análise de outputs e próximo passo na kill chain — em segundos.
Sem cartão para começar · Planos a partir de R$49/mês
A engenharia reversa de firmwares é uma tarefa complexa que exige ferramentas poderosas e um entendimento profundo de como o software interage com o hardware. O Ghidra, uma ferramenta gratuita e extensível da NSA, tornou-se um recurso indispensável para pesquisadores de segurança e engenheiros reversos. Este artigo detalha um guia prático para utilizar o Ghidra na análise de firmwares, com o objetivo de recuperar a lógica de binários, abordando desde a configuração inicial até técnicas avançadas de análise.
O processo de engenharia reversa raramente é linear e pode exigir semanas para uma compreensão completa de um sistema. O Ghidra, apesar de seu alto limiar de entrada, oferece funcionalidades robustas para desvendar a complexidade de firmwares. Um dos primeiros passos cruciais é determinar o endereço de carregamento correto do firmware. Frequentemente, firmwares não são carregados no endereço zero da memória; em vez disso, os endereços iniciais são reservados para o ponteiro de pilha inicial e o vetor de reset. O CPU inicia a execução a partir do vetor de reset, permitindo a carga de diferentes firmwares, como o código de aplicação principal ou um bootloader de recuperação. Sem a configuração adequada do endereço de carregamento, o desassemblador pode gerar referências incorretas para funções, strings e outros objetos, levando a uma análise equivocada. Esse endereço pode ser determinado consultando o datasheet do chip, analisando a Tabela de Vetores de Interrupção (IVT), buscando referências a strings ou identificando outras estruturas de firmware conhecidas. A IVT, geralmente localizada no deslocamento zero, é uma tabela de endereços de manipuladores de interrupção. Configurar cada entrada como um ponteiro e desassemblar o manipulador de reset antes da Análise Automática pode otimizar a análise, garantindo que ela siga a ordem de execução correta.
Após definir o endereço de carregamento, é essencial configurar o mapa de memória no Ghidra. A memória flash, já presente com o endereço base definido, deve ser marcada como somente leitura, pois firmwares geralmente não escrevem na imagem carregada. Isso garante que strings e outras estruturas de dados somente leitura sejam exibidas corretamente. Além disso, é importante definir uma área de SRAM como leitura/escrita, utilizando os endereços do manual de referência do microcontrolador. Isso permite a modificação de tipos de variáveis e a inicialização de dados. Para microcontroladores ARM, o carregamento de arquivos SVD (System View Description) através de um script como o SVD-Loader é fundamental para mapear a localização dos registradores de periféricos, que interagem com sub-rotinas. Esses arquivos descrevem as localizações dos registradores e os carregam no Ghidra com tipos de dados bem definidos. Para arquiteturas como M68000, a localização dos periféricos é configurada dinamicamente através de registradores como VBR, RAMBAR e MBAR, que precisam ser identificados na sequência de inicialização do firmware.
A anotação extensiva é uma prática recomendada no Ghidra. Mesmo que uma função ou endereço de dados pareça insignificante, atribuir uma etiqueta (atalho 'l') ou um comentário (atalho ';') pode ser crucial posteriormente. Renomear variáveis e corrigir seus tipos de dados (Ctrl+l) melhora significativamente a legibilidade da descompilação, especialmente ao retornar a um projeto após um período. A busca por funções através de strings é um método direto, pois strings em firmwares muitas vezes servem como a única forma de documentação deixada pelo fornecedor. Logs de depuração, verificações de assert e mensagens de exceção podem fornecer protótipos de funções completos, permitindo renomear funções e definir parâmetros corretos. Tabelas de endereços podem conter cabeçalhos ou nomes de funções próximos aos ponteiros, que podem ser encontrados manualmente ou através de referências cruzadas. É importante não negligenciar strings que podem ser construídas na pilha, em vez de armazenadas em seções de dados estáticas. Isso pode ocorrer por otimização, ofuscação ou decisões de projeto relacionadas à alocação de memória e tempo de vida de objetos. O Ghidra pode não exibir essas atribuições de string de forma intuitiva, mas a anotação com tipos de dados corretos pode ajudar na visualização. A caça por valores mágicos, como cookies DHCP ou EtherType ARP, também pode ser uma forma eficaz de identificar funções. O uso de sistemas operacionais de tempo real (RTOS) adiciona outra camada de complexidade, com recursos globais compartilhados e alocação de memória mais dinâmica. Entender como as tarefas são iniciadas, identificando os ponteiros de função passados para as funções de criação de tarefas, é vital. O Ghidra BSim, um plugin de correspondência difusa de código, é excelente para encontrar funções de biblioteca. Ao compilar bibliotecas conhecidas (como RTOS ou stacks TCP/IP) com informações de depuração e carregá-las no Ghidra, é possível criar uma base de assinaturas de funções. O BSim pode então comparar as funções do firmware com essa base, identificando correspondências mesmo com versões diferentes de bibliotecas ou compiladores. A importação de arquivos de cabeçalho de bibliotecas conhecidas pode preencher o Gerenciador de Tipos de Dados com typedefs, estruturas e protótipos de funções, auxiliando na anotação da descompilação. Embora o parser de código-fonte do Ghidra tenha suas limitações, ele pode ser útil. As 'Bookmarks' no Ghidra, geradas durante a autoanálise ou criadas manualmente, podem revelar tabelas de endereços, funções não definidas e falhas de desassemblagem que exigem correção manual. Ao mergulhar no código assembly, especialmente quando a descompilação não é ideal (indicada por variáveis como 'in_', 'in_stack_', 'unaff_', 'extraout_'), é crucial entender as convenções de chamada da arquitetura, como os parâmetros são passados (pilha ou registradores) e a ordem de leitura. Funções variádicas ou com convenções de chamada específicas, como 'thiscall', podem precisar ser definidas manualmente na janela 'Edit Function Signature'. Finalmente, scripts e plugins, como o SVD-Loader, VxWorks Symbol Table Finder, Leaf Blower, FindCrypt e Stack String Explorer, podem automatizar tarefas e anotar funcionalidades de interesse. Modelos de Linguagem Grandes (LLMs) também se mostraram úteis para reconhecer rapidamente algoritmos comuns em código descompilado e assembly, embora a verificação dos resultados seja essencial. Ferramentas como GhidrAssist e GhidraMCP integram LLMs para análise autônoma e anotação de símbolos, mas a segurança de dados deve ser considerada ao usar LLMs com informações comercialmente sensíveis.
📤 Compartilhar & Baixar
🧰 Ferramentas recomendadas
Divulgação: alguns links são patrocinados. Podemos receber comissão se você comprar — sem custo extra para você. Só indicamos o que faz sentido para a comunidade.