Деобфускатор ConfuserEx2 с поддержкой анти-тампера, компрессора, констант, потока управления и восстановления ресурсов.
Улучшенный форк UnconfuserEx с расширенной поддержкой современных вариантов ConfuserEx2, восстановлением анти-взлома, удалением компрессора, восстановлением потока управления и реконструкцией ресурсов.
https://github.com/user-attachments/assets/de2c7fd9-6736-4f39-83c0-3c25aa9c1f24
Если вы когда-нибудь работали с образцами вредоносного ПО, некоторые из них обфусцированы с помощью ConfuserEx. Я пробовал некоторые публичные деобфускаторы против последних версий, но они просто не работали, поэтому я решил форкнуть один публичный, который работал с последней версией, и просто модифицировать его под свои нужды.
Этот репозиторий — форк MadMin3r/UnconfuserEx. Заслуги принадлежат ему. Оригинальный проект сделал сложную работу по созданию целенаправленного деобфускатора для ConfuserEx2, который действительно мог удалять настоящие защиты.
Этот проект делает именно это. Он запускает список удалителей в фиксированном порядке, перезаписывает тела методов/ресурсы/метаданные там, где может, и записывает новую сборку обратно. Важен порядок. Компрессор и анти-взлом должны выполняться рано, потому что остальная часть модуля может даже не быть настоящим IL.
Версия из апстрима уже была полезной, но несколько случаев постоянно всплывали.
Один из них — путь LZMA. Некоторые образцы передают байты, которые выглядят как полезная нагрузка констант/ресурсов, но свойства LZMA — бессмыслица. Если передать их напрямую декодеру, вы получите нелепые размеры словаря и в итоге исключения вроде выхода размеров массива за допустимый диапазон. Поэтому эта версия проверяет свойства, ограничивает размер словаря, ограничивает размер несжатых данных и выходит до того, как декодер выделит что-то нелепое.
LZMA properties => CE FD 62 5F 9F
Invalid LZMA properties byte 0xCE or unreasonable dictionary size
У констант была ещё одна тупая, но реальная проблема. Большая часть кода разрешения хочет, чтобы ID, расположенный перед вызовом геттера, был ldc.i4. Иногда это уже не одна инструкция. Это крошечное арифметическое выражение.
ldc.i4 0x1234
ldc.i4 0x55
xor
call string <const getter>(int32)
Оригинальный форк видит xor, вызывает GetLdcI4Value() и умирает, потому что xor явно не загрузка целого числа. Эта версия проходит обратно по небольшой арифметической последовательности, эмулирует стек, сворачивает её обратно в один ldc.i4, а затем позволяет нормальному решателю продолжать.
Таким образом, вместо того чтобы рассматривать это как совершенно другую защиту констант, она превращается в это:
ldc.i4 0x1261
call string <const getter>(int32)
Затем существующий путь обычного/x86 решателя констант может выполнять свою работу.
Поток управления — вот где было неважно
Удалитель switch может обрабатывать обычную форму switch-диспетчера ConfuserEx. Он обходит блоки, восстанавливает следующий целевой блок, удаляет мёртвые блоки и выдает нормальное тело метода. Но есть образцы, где понимается только часть метода. Если вы мутируете половину метода, а затем обнаруживаете, что он всё ещё обфусцирован, результат хуже бесполезного, потому что теперь у вас сломанный IL и нет чистого способа понять, что произошло.
Поэтому эта версия делает снимок тела метода перед его изменением:
инструкции
обработчики исключений
Если деобфускация выбрасывает исключение или если метод после этого всё ещё выглядит обфусцированным, исходное тело восстанавливается. Журнал всё ещё может сказать «этот метод не решён», но сборка не будет молча повреждена только из-за того, что один метод имел странный диспетчер.
Управление потоком с прыжками/трамплинами также получило свой собственный проход. Некоторые методы — это не просто switch-диспетчеры. Это небольшие цепочки трамплинов, соединённых до тех пор, пока не будет достигнут реальный блок. Теперь они обнаруживаются и сворачиваются, вместо того чтобы игнорироваться на пути только switch.
Удалитель компрессора — это часть, которая должна запускаться раньше всех.
Заглушки компрессора ConfuserEx обычно хранят настоящую сборку сжатой, загружают крошечный загрузчик, распаковывают полезную нагрузку и загружают её во время выполнения.
Удалитель находит форму загрузчика, извлекает встроенную полезную нагрузку, распаковывает её и заменяет модуль на настоящую сборку. Обработаны как нормальные, так и компактные макеты компрессора.
[+] Обнаружен компрессор
[+] Извлечена полезная нагрузка сжатого модуля
[+] Распакован реальный модуль
[+] Продолжение конвейера на распакованной сборке
Анти-взлом теперь имеет два пути.
Обычный/динамический анти-взлом дешифрует тела методов из защищенных секций и записывает восстановленные тела обратно в модуль. JIT анти-взлом более раздражает, потому что тела должны материализоваться, когда время выполнения запрашивает их.
Примерная форма:
найти init
извлечь ключи
найти зашифрованную секцию JIT-тел
вывести ключ для каждого метода
прочитать тело
записать CilBody обратно
Это всё ещё основано на шаблонах. Если заглушка изменилась достаточно сильно, это, очевидно, не сработает.
Ресурсы обрабатываются аналогично константам: найти зашифрованный блок ресурсов, восстановить ключ/форму дешифратора, расшифровать, при необходимости распаковать, затем вернуть ресурсы туда, где их ожидают обычные инструменты .NET.
Также есть опциональный путь перестроения встроенного PE. Некоторые защищенные образцы содержат управляемый PE внутри ресурса. С включенной перестройкой удалитель пытается разобрать и перезаписать эту полезную нагрузку, вместо того чтобы оставлять деобфусцированную внешнюю сборку с нетронутой внутренней.
UnConfuserEx.exe sample.exe sample.clean.exe --rebuild-embedded-pe
Используйте это, если знаете, что образец прячет другую управляемую сборку внутри ресурсов. Если полезная нагрузка не является управляемым PE, путь перестроения должен оставить её в покое.
Сборка:
dotnet build .\UnConfuserEx.sln -c Release
Запуск:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe
Или укажите явный путь вывода:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe
Если вы не укажете путь вывода, он создаст файл рядом с входным с добавлением -deobfuscated к имени.
Для встроенных управляемых полезных нагрузок:
.\UnConfuserEx\bin\Release\net9.0\UnConfuserEx.exe .\protected.exe .\protected.clean.exe --rebuild-embedded-pe
Это текущий список поддерживаемых защит. Это не означает, что каждый возможный форк ConfuserEx работает. Это означает, что конвейер знает, как искать эти формы.