Um deobfuscator ConfuserEx2 com suporte para anti tamper, compressor, constants, control flow e resource recovery.
Fork aprimorado do UnconfuserEx com suporte melhorado para variantes modernas do ConfuserEx2, recuperação anti-tamper, remoção de compressor, restauração de fluxo de controle e reconstrução de recursos.
https://github.com/user-attachments/assets/de2c7fd9-6736-4f39-83c0-3c25aa9c1f24
Se você já mexeu com amostras de malware, algumas delas são ofuscadas com ConfuserEx, e eu tentei alguns deobfuscadores públicos contra a versão mais recente, mas simplesmente não funcionavam, então decidi bifurcar um público que funcionava contra essa versão mais recente e apenas modificá-lo de acordo com minhas necessidades.
Este repositório é um fork de MadMin3r/UnconfuserEx. O crédito também vai para ele. O projeto original fez a parte difícil de criar um deobfuscador focado no ConfuserEx2 que realmente conseguia remover proteções reais.
É isso que este projeto faz. Executa uma lista de removedores em uma ordem fixa, reescreve corpos de métodos/recursos/metadados onde pode e escreve um novo assembly de volta. A parte importante é a ordem. O compressor e o anti-tamper precisam acontecer cedo, porque o resto do módulo pode nem ser IL real ainda.
A versão upstream já era útil, mas alguns casos continuavam aparecendo.
Um deles era o caminho LZMA. Algumas amostras fornecem bytes que parecem a carga útil de constantes/recursos, mas as propriedades LZMA são sem sentido. Se você passar isso diretamente para o decodificador, obtém tamanhos de dicionário estúpidos e, eventualmente, exceções como dimensões de array excedendo o intervalo suportado. Então esta versão verifica as propriedades, limita o tamanho do dicionário, limita o tamanho descompactado e sai antes que o decodificador aloque algo ridículo.
LZMA properties => CE FD 62 5F 9F
Invalid LZMA properties byte 0xCE or unreasonable dictionary size
Constantes tinham outro problema idiota, mas real. Grande parte do código do resolvedor espera que o ID na frente da chamada do getter seja um ldc.i4. Às vezes não é mais uma instrução. É uma pequena expressão aritmética.
ldc.i4 0x1234
ldc.i4 0x55
xor
call string <const getter>(int32)
O fork original vê xor, chama GetLdcI4Value() e morre porque xor obviamente não é uma carga de inteiro. Esta versão retrocede sobre a pequena sequência aritmética, emula a pilha, colapsa de volta em um único ldc.i4 e então deixa o resolvedor normal continuar.
Então, ao invés de tratar isso como uma proteção de constantes totalmente diferente, vira isto:
ldc.i4 0x1261
call string <const getter>(int32)
Então o caminho existente do resolvedor de constantes normal/x86 pode fazer seu trabalho.
O fluxo de controle era onde estava mais ou menos
O removedor de switch consegue lidar com a forma normal do dispatcher switch do ConfuserEx. Ele percorre blocos, recupera o próximo alvo, exclui blocos mortos e emite um corpo de método são. Mas existem amostras onde apenas parte do método é compreendida. Se você mutar metade de um método e depois descobrir que ele ainda está ofuscado, a saída é pior que inútil porque agora você tem IL quebrado e nenhuma maneira limpa de raciocinar sobre o que aconteceu.
Então esta versão tira um snapshot do corpo do método antes de tocá-lo:
instructions
exception handlers
Se a desofuscação lançar uma exceção, ou se o método ainda parecer ofuscado depois, o corpo original é restaurado. O log ainda pode dizer "este método não foi resolvido", mas o assembly não é silenciosamente corrompido só porque um método tinha um dispatcher estranho.
O fluxo de controle de jump/trampolim também ganhou seu próprio passe. Alguns métodos não são apenas dispatchers switch. São pequenos trampolins de branch encadeados até que o bloco real seja alcançado. Esses agora são detectados e dobrados, em vez de serem ignorados pelo caminho apenas de switch.
O removedor de compressor é a parte que precisa ser executada antes de todos os outros.
Stubs de compressor do ConfuserEx geralmente mantêm o assembly real comprimido, inicializam um pequeno loader, descomprimem a carga útil e a carregam em tempo de execução.
O removedor encontra a forma do loader, extrai a carga útil incorporada, descomprime e troca o módulo pelo assembly real. Layouts de compressor normal e compacto são ambos tratados.
[+] Compressor detected
[+] Extracted compressed module payload
[+] Decompressed real module
[+] Continuing pipeline on unpacked assembly
Anti-tamper tem dois caminhos agora.
Anti-tamper normal/dinâmico descriptografa corpos de métodos de seções protegidas e escreve os corpos restaurados de volta no módulo. Anti-tamper JIT é mais irritante porque os corpos devem ser materializados quando o tempo de execução os solicita.
A forma aproximada é:
find init
extract keys
find encrypted JIT body section
derive per method key
read body
write CilBody back
Isso ainda é baseado em padrão. Se o stub mudou o suficiente, vai falhar, obviamente.
Recursos são tratados de forma semelhante às constantes: encontrar o blob de recurso criptografado, recuperar a forma da chave/descriptografador, descriptografar, descomprimir se necessário, então colocar os recursos de volta onde as ferramentas .NET normais esperam.
Há também um caminho opcional de reconstrução de PE incorporado. Algumas amostras protegidas carregam um PE gerenciado dentro de um recurso. Com a reconstrução ativada, o removedor tenta analisar e reescrever essa carga útil também, em vez de deixar um assembly externo desofuscado com um interno intocado.
UnConfuserEx.exe sample.exe sample.clean.exe --rebuild-embedded-pe
Use isso quando você souber que a amostra esconde outro assembly gerenciado dentro de recursos. Se a carga útil não for um PE gerenciado, o caminho de reconstrução deve deixá-la em paz.
Compile:
dotnet build .\UnConfuserEx.sln -c Release
Execute:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe
Ou forneça um caminho de saída explícito:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe
Se você não fornecer um caminho de saída, ele escreve um ao lado da entrada com -deobfuscated adicionado ao nome.
Para cargas úteis gerenciadas incorporadas:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe --rebuild-embedded-pe
Esta é a lista de suporte atual. Não significa que todos os forks possíveis do ConfuserEx funcionem. Significa que essas são as formas que o pipeline sabe procurar.