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
UnhookMe — Resolvedor e removedor de hooks dinâmico da API do Windows que detecta e restaura funções com hook (IAT, EAT, patches inline) para invocar chamadas de sistema não monitoradas a partir de malware de Red Team. | Kitploit
Ferramentas/GitHubGitHub/mgeeky/unhookme
Ferramentas DefensivasRed TeamingDesenvolvimento de Payloads
GitHubmgeeky/unhookme

UnhookMe

Resolvedor e removedor de hooks dinâmico da API do Windows que detecta e restaura funções com hook (IAT, EAT, patches inline) para invocar chamadas de sistema não monitoradas a partir de malware de Red Team.

Ver Repositório
34848há 4 anosRevisado 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

UnhookMe - Resolvedor de imports com desenganche dinâmico

Na era de AVs e EDRs intrusivos que introduzem hot-patches nos processos em execução para seus requisitos de óptica aprimorada, os adversários modernos precisam de uma ferramenta robusta para passar por esses guardiões. A implementação proposta de um resolvedor de imports dinâmicos capaz de desenganchar funções usadas em tempo real é mais um passo para fortalecer os esforços de resiliência dos adversários.

A solução que proponho aqui é mudar de usar imports WinAPI resolvidos pelo vinculador, permanecendo visíveis nos cabeçalhos PE do executável compilado (especificamente na Tabela de Endereços de Importação) para favorecer uma abordagem totalmente dinâmica, insistindo em resolver imports apenas de forma dinâmica. Tal resolvedor dinâmico pode ser equipado com lógica de desenganche ocorrendo em segundo plano, sem qualquer tipo de orientação do operador.


Exemplo de uso mais simples

É assim que você pode garantir chamar MessageBoxW desenganchado, desmonitorado:

root@kitploit:~
    RESOLVE(user32, MessageBoxW);
    _MessageBoxW(0, L"Look Ma! I'm unhooked!", L"Third - Unhooked", 0);

Toda a mágica acontece dentro da macro RESOLVE, que constrói um objeto ImportResolver<T> chamado _MessageBoxW.


Demonstração

Animação de demonstração do UnhookMe

Veja como o exemplo do UnhookMe funciona:

  1. Ele nos apresenta a primeira MessageBoxW que não está sujeita a hooking
  2. Em seguida, nós mesmos fazemos hook no prólogo da MessageBoxW para fazê-la sempre retornar 0 sem exibir sua mensagem
  3. Finalmente, resolvemos MessageBoxW dinamicamente usando o resolvedor UnhookingImportResolver, que detectará os patches aplicados no prólogo e restaurará os bytes originais, efetivamente desenganchando a funcionalidade da MessageBoxW.

Enquanto isso, ao exibir as caixas de mensagem, estas são as linhas de log impressas no stdout do console:

root@kitploit:~
[~] Resolved symbol kernel32.dll!CreateFileA
[~] Resolved symbol kernel32.dll!ReadProcessMemory
[~] Resolved symbol kernel32.dll!MapViewOfFile
[~] Resolved symbol kernel32.dll!VirtualProtectEx
[#] Found trampoline hook in symbol: MessageBoxW . Restored original bytes from file.
[~] Resolved symbol user32.dll!MessageBoxW

Como usar?

Há um total de 5 arquivos de código-fonte/cabeçalho C++ que sua solução precisa incluir. No entanto, seu arquivo de programa principal precisa incluir apenas dois cabeçalhos necessários, conforme detalhado abaixo.

  • resolver.h - cabeçalho contendo a maior parte da implementação do UnhookingImportResolver e úteis macrodefinições
  • resolver.cpp - código-fonte com opções globais definidas
  • usings.h - um cabeçalho grande e complicado contendo dezenas de definições de tipos using para WinAPIs comumente usadas
  • PE.cpp - arquivo de código-fonte do analisador PE personalizado
  • PE.h - arquivo de cabeçalho do analisador PE personalizado

Cabeçalhos necessários

Seu programa exigirá apenas dois cabeçalhos a serem incluídos:

root@kitploit:~
#include "usings.h"
#include "resolver.h"

Opções globais

Há algumas opções globais que podem ser alteradas, afetando a forma como o Resolvedor funciona ou relata sua atividade. Elas estão definidas no início do arquivo resolver.cpp:

Opções globais do Resolvedor:

  • globalQuietOption - defina como true se não quiser nenhum tipo de saída
  • globalVerboseOption - defina como true se quiser uma saída detalhada e verbosa
  • globalAntiSplicingOption - desengancha funções resolvidas se elas estiverem com hook.
  • globalLogFilePath - para onde redirecionar as linhas de log de saída. Se vazio, usa stdout.
root@kitploit:~
bool globalQuietOption = false;
bool globalVerboseOption = true;
bool globalAntiSplicingOption = true;

wchar_t globalLogFilePath[MAX_PATH] = L"";

Especificação de tipo de API personalizada

Para usar o Resolvedor, um tipo de ponteiro de função deve ser declarado primeiro com uma instrução using de forma estrita:

root@kitploit:~
    using fn_FunctionName = ReturnType WINAPI (
        ParamType1 paramName1,
        ...,
        ParamTypeN paramNameN,
    );

Este repositório vem com o arquivo de cabeçalho usings.h contendo tipos using predefinidos para dezenas de APIs populares do Windows.

O FunctionName corresponderá à WinAPI que queremos que o ImportResolver resolva, e esse ponteiro de função deve ser marcado como tendo convenção de chamada WINAPI (__stdcall no x86 e __fastcall no x64). O ReturnType deve preceder o modificador de tipo WINAPI.

Resolução e uso da função

Tendo o tipo de ponteiro de função definido como especificado acima, poderemos usá-lo da seguinte maneira:

root@kitploit:~
    RESOLVE(libraryName, FunctionName);
    ReturnType output = _FunctionName(param1, ..., paramN);

A macro RESOLVE cuida de instanciar o objeto modelado ImportResolver e ajustar o nome da biblioteca especificada.

O Resolvedor introduz várias outras macrodefinições oferecendo invocação de construtor fácil de usar em várias circunstâncias:

root@kitploit:~
#define RESOLVE(mod, func)                    RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, ::globalAntiSplicingOption)
#define RESOLVE_NO_UNHOOK(mod, func)          RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, false)

#define RESOLVE_VERBOSE_UNHOOK(mod, func)     RESOLVE_PARAMETERIZED(mod, func, true, true)
#define RESOLVE_VERBOSE_NOUNHOOK(mod, func)   RESOLVE_PARAMETERIZED(mod, func, true, false)
#define RESOLVE_NOVERBOSE_UNHOOK(mod, func)   RESOLVE_PARAMETERIZED(mod, func, false, true)
#define RESOLVE_NOVERBOSE_NOUNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, false, false)

Construtor do Resolvedor:

root@kitploit:~
    template<typename Ret, typename ...Args>
    ImportResolver<Ret WINAPI(Args...)>(
            std::string dllName,
            std::string funcName,
            bool _verbose = false,
            bool _unhook = false,
            bool *_wasItHooked = nullptr
        )

Como funciona?

O resolvedor subjacente utiliza um analisador personalizado de cabeçalhos PE, que processa cada módulo DLL referenciado para mapear suas exportações e verificar a integridade dos cabeçalhos PE do módulo, bem como a integridade dos bytes de stub da função referenciada.

A ideia é a seguinte:

  1. Primeiramente, emitimos LoadLibrary para carregar a biblioteca referenciada pelo usuário (aquela especificada como primeiro parâmetro para a macro RESOLVE) se ela não puder ser alcançada através de GetModuleHandle.
  2. Em seguida, processamos os cabeçalhos PE da biblioteca carregada/referenciada, mapeamos suas exportações, recuperamos o array de endereços de exportação e também calculamos esses endereços nós mesmos para verificação cruzada.
  3. Se o endereço de uma rotina definida na Tabela de Endereços de Exportação da DLL não corresponder ao que esperaríamos, a exportação é considerada com hook na EAT. O mesmo vale se a entrada da nossa Tabela de Endereços de Importação (IAT) do executável para essa função foi alterada e não aponta mais para o local correto na seção de código da DLL - então a função é considerada com hook na IAT.
  4. Supondo que nenhum hook foi encontrado até agora, buscamos os primeiros N bytes do prólogo da função e os comparamos com o que está no arquivo DLL armazenado em disco. Se houver discrepância entre os bytes obtidos da memória e do arquivo - consideramos que a função foi corrigida inline (hot-patched).
  5. Se a função foi considerada com hook - retornamos o endereço original da exportação (aquele que calculamos nós mesmos) e/ou desenganchamos a entrada. Se houver bytes de patch no local, vamos restaurá-los.
  6. Finalmente, para otimizar o impacto no desempenho do resolvedor - armazenamos em cache todas as bases de imagem dos módulos carregados e endereços de funções resolvidas e os retornamos de um cache (sendo std::map) durante acessos subsequentes.

Entre os problemas que esse resolvedor com desenganche dinâmico enfrentou estão as questões de percorrer APIs encaminhadas (uma DLL pode conter um thunk de exportação dizendo que esta função não está implementada neste módulo, mas está em outro) - embora esta implementação tenha suporte para isso, às vezes ela quebra sua lógica de travessia.


☕ Mostre Apoio ☕

Este e outros projetos são resultado de noites sem dormir e muito trabalho duro. Se você gosta do que faço e aprecia que sempre retribuo à comunidade, Considere me comprar um café (ou melhor, uma cerveja) apenas para agradecer! 💪


Autor

root@kitploit:~
   Mariusz Banach / mgeeky, 21
   <mb [at] binary-offensive.com>
   (https://github.com/mgeeky)
Baixar ferramenta