Тебе, вероятно, уже надоело обсуждать деобфускацию .NET-приложений, поэтому попробуем вернуться к другому распространенному и популярному в определенных кругах продукту — Python. В свое время мы разбирали реверс скомпилированного байт‑кода, обычного, кастомного и даже скомпилированного в натив. Однако, как я уже говорил в предыдущей статье про .NET, тема очень обширная и неисчерпаемая. Конечно, это справедливо и для Python.
В этот раз мы обсудим известный обфускатор PyArmor, до которого у нас до сих пор руки не доходили. Обфускатор этот действительно известный, про него умные люди сочинили множество обзоров и наснимали большое количество туториалов по реверсу. Казалось бы, и делать нам тут особо нечего. Но время идет, защиты совершенствуются, так что попробуем выяснить, как обстоят дела с этим обфускатором на текущий момент.
Ставим задачу по привычной схеме: дано некое графическое приложение, точнее демоверсия приложения, которая отличается от рабочей тем, что на выходе поперек результирующей картинки появляется множество раздражающих надписей «DEMO», от которых неплохо было бы избавиться.

По традиции запрашиваем у Detect It Easy его мнение о природе нашего приложения и получаем ответ:
PE64Operation Linker: Compiler: Language: Tool: Packer: (Overlay: Archive: Data:
На первый взгляд, перед нами классический случай использования пакета PyInstaller, завернутого в исполняемый exe-модуль. Мы такие разбирали в статье «Змеиная анатомия. Вскрываем и потрошим PyInstaller». Если ты забыл или пропустил эту статью, напомню, что для разбора подобных пакетов на составляющие файлы я советовал применить утилиту PyInstaller Extractor или лучше Pydumpck, который вдобавок пытается по мере возможности декомпилировать .pyc-модули в исходный питоновский код.
Pydumpck (выдав попутно множество предупреждений) вполне справляется с поставленной задачей, распаковывает из нашего exe-файла целый каталог с разнообразными библиотеками.

Полученные таким способом исходники имеют две основные проблемы: хоть они и очень похожи на настоящие, но не работают — запущенный главный модуль (на скриншоте выделен) при запуске ругается на отсутствие импортируемой функции:
ImportError: cannot import name '__pyarmor__' from 'pyarmor_runtime_000000' (unknown location)Вдобавок при ближайшем рассмотрении лежащего рядом декомпилированного файла становится видно, что, кроме вызова этой функции, являющейся оберткой зашифрованного кода, в модуле нет вообще ничего:
from pyarmor_runtime_000000 import __pyarmor____pyarmor__(__name__, __file__, b'PY000000\x00\x03\n\x00o\r\r\n...')На этом этапе уже очевидно, что нужно усиленно копать в сети на предмет инструкций по реверсу этого приложения. И их действительно великое множество! Как принято у молодых амбициозных хакеров, в основном это ютубовские видеоролики, раздражающие меня крайним неудобством подачи технической информации. Поэтому выбираем в качестве базового источника статью с «Гитхаба», как самую толковую, хотя и несколько винтажную: четыре года прошло с тех пор, как проект перестал обновляться. Она предлагает аж целых три метода восстановления кода, обфусцированного PyArmor. Два из них предполагают дамп запущенного приложения, третий вроде как позволяет вытащить код без запуска анализируемой программы.
Однако, попробовав применить эти способы на практике, мы обнаруживаем, что не все так шоколадно, как казалось. Проблема возникает сразу же: утилита Process Hacker 2, рекомендуемая автором для инъекции кода в приложение, за прошедшее время превратилась в тыкву под названием System Informer, при этом ее жестоко кастрировали, убрав именно тот инструмент, который нам был нужен, — Inject DLL. Разумеется, на торрентах еще остался исходный вариант этого инструмента, но в итоге и он нам не помогает. То ли формат инъекции PyInjector устарел (ему тоже пять лет), то ли PyArmor таки научился с подобными вещами бороться, но при попытке инъекции программа избавляется от внедренного PyInjector максимально быстро, никакого дампа сделать не удается.
Первые два метода для нас не годятся, не годится и третий. Как выясняется, автор слукавил, сказав, что запускать он ничего не будет: по факту он все‑таки пытается запустить считанный из .pyc-файла маршализированный объект через exec. Естественно, ничего у него не выходит, по причине того, что (как я уже говорил выше) модулю для нормальной загрузки не хватает внешней функции __pyarmor__. Судя по описанию, в ранних версиях она содержалась в модуле _pytransform., который входит в стандартный дистрибутив PyArmor и распаковывается из пакета PyInstaller. Сейчас же она находится в модуле pyarmor_runtime_000000/, который, похоже, тоже сменил свой формат и поэтому работать вместо _pytransform. не хочет.
Не подходят в качестве замены и входящие в актуальный дистрибутив PyInstaller модули pyarmor_runtime. и pytransform3., которые мы тоже пробуем использовать для очистки совести. Просмотр пары роликов укрепляет наши опасения: судя по всему, за прошедшую пятилетку PyArmor если не радикально, то весьма серьезно сменил свой алгоритм, чтобы предлагаемые ранее методы его лечения не просто не работали, но даже не были базой, от которой можно отталкиваться для допиливания деобфускатора.
Поэтому не теряем времени на погружение в чужую логику дампа и деобфускации (у нас, повторяю, цель другая — анализ и патч конкретного приложения) и следуем своим собственным путем, начало которого я указал в статье «Змеиная анатомия. Вскрываем и потрошим PyInstaller». Берем наш любимый отладчик x64dbg, аттачимся им к запущенному процессу и прерываем его (благо программа любезно предоставляет нам модальный MessageBox для удобства этого действия). Проанализируем стек возвратов.

Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
