Как расшифровать php код
Расшифровка и деобфускация PHP-скриптов
Тема декодирования зашифрованных PHP-скриптов уже однажды мной затрагивалась в посте PHP и зашифрованный код. В нем я описал способ получения значений всех инициализированных переменных и списка объявленных функций в скрипте, зашифрованным протектором ionCube. Тогда, в 2009 году, расшифровать скрипт под ionCube было проблематично – на тот момент существовали лишь платные сервисы. Однако с ростом популярности ionCube стало ясно, что рано или поздно его постигнет участь Zend Encoder (сейчас Zend Guard), павшего в 2008 году перед ставшими повсеместно доступными “дезендерами”. Действительно, за последнее время появилось множество декодеров ionCube, можно даже найти бесплатные онлайн-сервисы. Один из них позволяет расшифровать практически любые скрипты. О нем, а также о других способах расшифровки расскажу в данной статье.
Прежде всего необходимо иметь представление о том, какие существуют виды защиты PHP-скриптов. Всего их можно выделить три:
Итак, имея скрипт, зашифрованный неизвестным для вас алгоритмом, прежде всего необходимо определить с помощью чего он зашифрован. В этом может помочь отличный инструмент PCL’s PHPiD. Распознает множество протекторов, например тот же ionCube:
Определив протектор, можно поискать существующие решения для расшифровки того или иного энкодера. Но в большинстве случаев поможет сервис dezend.me.
Он открылся совсем недавно, но уже сейчас имеет большой функционал: расшифровка серверных средств защиты ionCube 6.5, Zend, Nu-Coder, а также декодирование и деобфускация любых скриптов, зашифрованных без использования дополнительных расширений PHP. К сожалению, результаты расшифровки ionCube оставляют желать лучшего, в некоторых участках кода тестового скрипта пришлось исправлять вручную. А вот универсальный декодер справляется заметно лучше, правда не хватает нормального форматирования кода. В целом, сервис оставил положительные впечатления, надеюсь, не закроется 🙂
Деобфускация PHP кода
Так уж случилось, что на днях мне попался один нужный PHP скрипт, но обфусцированный.
По какой-то причине он никак не работал. Я пишу на PHP достаточно давно, и мне очень нравится отлавливать нестандартные ситуации в скриптах, особенно когда при выполнении в логах нет ошибок, а скрипт просто не выполняет своих предназначенных обязанностей, руки так и чесались расшифровать. Последней каплей стало то, что автора этого скрипта не было в сети, чтобы помочь решить мою проблему. Скрипт кстати куплен моим товарищем, собственно он и попросил помочь.
Цель данной статьи, показать принцип дешифровки, зашифрованных скриптов, чтобы например расшифровать залитый злоумышленником вирус на PHP.
Анализирование кода
Вот один из этих скриптов, открывая такой скрипт в блокноте, мы увидим:
Разобьем для начала его на строки:
и сохраним в файл encoded_script.php
Видно что тут код разбит на 4е части. В каждой выполняется eval.
Для удобства дешифровки eval воспользуемся расширением для PHP — Evalhook. Его написал Stefan Esser за что ему большущее спасибо. С помощью него можно расшифровывать в несколько проходов например монстров закодированных через www.php-crypt.com функциями eval, gzuncompress, base64_decode и тд.
Установка evalhook
Скачиваем архив с исходниками
Распаковываем и собираем расширение для PHP
расширение готово, теперь его можно динамически подгружать в консоль и смотреть зашифрованный скрипт.
где encoded_script.php — закодированный файл.
Для получения исходного кода просто жмем «Y» несколько раз в процессе деобфускации. Каждый новый Y будет расшифровывать следующий eval. Ну что ж вооружимся консолью и в бой!
Деобфускация и декодирование
1-й eval расшифровали, нужный нам код находится между символами”—-“. Впоследствии после всех шагов нужно будет его скопировать в файл. Нажимаем Y и продолжаем расшифровывать.
2-й проход:
Ну вот и всё, дальше продолжать нет смысла, потому что внутри нашего шифрованного файла, как видно по коду, includ’ится еще один зашифрованный файл, но мы его потом отдельно расшифруем. Главное правило сколько у нас eval, столько раз жмём Y, иначе наш скрипт выполнится вплоть до исполнения кода целиком, нам это не нужно.
Собираем полученное в один файл
Чтобы не было всё в каше без пробелов пропустим это добро через php beautifier, дабы получить нормальные отступы.
Наш код уже становится похож на человечный
сохраним полученный код в тот же файл и выполним его просто через консоль (можно и в браузере):
получаем заветные названия функций:
Окончательная замена функций:
в итоге в файле unencoded_script_full.php будет полностью искомый скрипт с нормальными функциями, полученный код смотреть здесь.
Кстати деобфускация помогла мне решить проблему, из-за чего не работал скрипт, но это уже другая история…
P.S. Естественно скрипты могут быть зашифрованы различными и более ухищренными способами и мой способ не панацея, я лишь показал логику дешифровки таких скриптов. Например некоторые обфускаторы могут всем переменным присвоить другие имена, и понять логику таких скриптов уже будет сложнее.
UPD от автора скрипта:
zuziken (20:30:15 2/02/2012)
*THUMBS UP*
zuziken (21:10:42 2/02/2012)
Спс за обзор, но код пишет программист, а не я:-)
Кодирование и декодирование PHP кода
Я занимаюсь восстановлением исходников PHP из закодированного вида.
В этой статье я расскажу о том, как обстоят дела с кодированием и декодированием PHP в настоящее время.
Очень краткий ликбез по внутреннему устройству интерпретатора PHP
При выполнении PHP-скрипта, он парсится и компилируется в опкоды внутренней виртуальной машины PHP.
Из каждого файла PHP получаются:
— массив классов: в каждом классе — информация о классе, свойства класса и массив методов класса
— массив функций
— «тело скрипта» — код вне классов и функций
Для краткости, всю внутреннюю структуру скомпилированного файла, готового к выполнению, в этой статье я называю “опкодами“.
Сами опкоды (операции внутренней виртуальной машины PHP) внутри какой-нибудь функции выглядят так:
Важный момент: файлы в скомпилированном виде достаточно сильно отличаются даже между подверсиями интерпретатора PHP. Оно и понятно: сам для себя скомпилировал — сам и выполнил.
Как работают энкодеры
Существуют два принципиально разных типа энкодеров.
Первые — работают исключительно средствами самого языка. Они делают код нечитаемым с помощью base64-кодирования, zip-ования, разных манипуляций со строками, и все в конце концов используют функцию eval(). Все это очень похоже на обфускаторы в Javascript. Выглядит это как-то так:
Снимается такая защита очень просто, в самых сложных случаях — за несколько часов. Еще один крупный минус — серьезно страдает производительность. Поэтому для серьезного применения такую защиту использовать не рекомендуется.
Второй тип энкодеров использует свои подключаемые модули для интерпретатора PHP, которые называются загрузчиками (loader-ы). В этом случае, как правило, кодируется не сам исходный код, а результаты его компиляции, т.е. внутренние структуры и опкоды. Это уже гораздо более серьезная защита — даже если раскодировать сами опкоды, по ним еще надо восстановить исходный PHP-код. К тому же, с точки зрения производительности, дополнительные затраты на раскодирование часто компенсируются экономией на компилировании кода, т.е. скорость выполнения закодированных скриптов зачастую даже выше, чем у исходного кода.
Во время загрузки интерпретатора PHP, loader-ы энкодеров вешают свои обработчики на функции загрузки PHP-файлов, компиляции и выполнения, для того, чтобы работа с закодированными файлами была бы прозрачной для самого интерпретатора.
Основная сложность для энкодеров — это сделать так, чтобы опкоды, скомпилированные под одной версией PHP во время кодирования, работали бы под другой версией PHP при декодировании. Практически все loader-ы у всех энкодеров после декодирования делают необходимые правки, чтобы обеспечить такую совместимость. Главный игрок на этом рынке — IonCube — в свое время приложил огромные усилия для решения этой задачи, и его loader-ы могут на лету корректно выполнить опкоды от PHP 4.x на PHP 5.x, а по возможности — даже наоборот!
Обфускация
Также, для дополнительной защиты, большинство энкодеров дает возможность обфусцировать идентификаторы: имена переменных, названия функций, классов. Этот процесс, как правило, односторонний — наподобие хэширования, и к тому же в результате часто получаются имена с непечатными символами, которые отлично работают, но которые нельзя использовать напрямую в декомпилированных текстах. Например, как записать функцию с именем… *диктую по байтам* 0x0D, 0x07, 0x03, 0x0B, 0x02, 0x04, 0x06?
Отдельно внимание уделяется тому, чтобы обфусцированные имена работали бы корректно. Например, в коде вызывается функция checkLicense — loader обфусцирует название на лету, получает < 0x0D, 0x07, 0x03, 0x0B, 0x02, 0x04, 0x06 >и ищет уже этот ключ в хэш-таблице с названиями функций.
Zend Guard даже предоставляет run-time функции zend_obfuscate_function_name и zend_obfuscate_class_name, которые позволяют вычислить обфусцированные имена для функций и классов, чтобы облегчить связывание закодированных файлов с незакодированными.
Декодеры наносят ответный удар
Для создания декодера нужны две вещи: получать расшифрованные опкоды и уметь декомпилировать их в исходный код PHP.
Для получения опкодов кому-то пришла в голову светлая идея — сделать свою сборку интерпретатора PHP, которая бы вместо выполнения раскодированного скрипта — отправляла бы его на декомпилирование. Не нужно возиться с чтением формата энкодера и его защитами — loader энкодера сам делает всю нужную работу!
Какое-то время это работало неплохо, потом авторы некоторых энкодеров додумались заменять раскодированные функции заглушками, а реальный код прятать и доставать каждую вызываемую функцию только в момент ее непосредственного выполнения.
В ответ, авторы декодеров стали модифицировать loader-ы от энкодеров, чтобы те не применяли такие заглушки.
Довольно большой минус оказался в том, что для каждой версии PHP у каждого энкодера были свои loader-ы, которые к тому же частенько обновлялись. Приходилось много и часто патчить, хотя и несложно — просто отключить вызов функции-другой.
Это надолго затормозило тех, кто «патчил loader-ы». Во-первых, пришлось долго разбираться, почему вроде бы правильно вытащенные опкоды декомпилируются с ошибками. Во-вторых, тут уже не получалось просто поменять пару байт в loader-е.
На сцену вышли те немногие, кто прикладывал больше усилий — реверсил и разбирался в формате закодированных файлов.
Вторая часть в работе декодера — декомпилирование. Это — сложная, но интересная, чисто алгоритмическая задача.
Когда-то светлые головы написали пару неплохих алгоритмов декомпилирования для PHP. Большинство тех, кто занимается декодированием PHP сейчас, написать свой декомпилятор не могут, поэтому используют те, что есть, с минимальными правками.
Все декомпиляторы в открытом доступе правильно восстанавливают только 90-95%% кода. Остальное приходится исправлять вручную, и тут очень большую роль играют опыт программирования на PHP и опыт декомпилирования, т.к. ошибки возникают обычно типовые.
Подводя итог: никакого полностью автоматического декодирования для основных коммерческих энкодеров пока нет.
Как защититься от декодирования
Ясно, что рано или поздно любой закодированный код будет вскрыт, если это будет нужно. Но зная, как устроена работа декодеров, можно серьезно осложнить этот процесс:
Юридические аспекты
Вообще говоря, декодирование PHP-файлов после коммерческих энкодеров — нелегально. Технически это связано с тем, что для полноценного декодирования нужно декомпилировать и анализировать сами энкодеры, а закон и пользовательские соглашения это прямо запрещают.
На территории Евросоюза есть такая лазейка: разрешается «обеспечивать совместимость экземпляров ПО, которыми вы владеете, и для этого, при необходимости, обходить встроенные системы защиты». При этом прямой запрет на reverse engineering у каждого энкодера все-таки имеет приоритет.
Получается, что «я скачал программу из Интернета, которая достала мне незашифрованные опкоды» или «я использовал специальную сборку интерпретатора PHP, которая сохраняет расшифрованные опкоды» — это условно-легальные способы декодирования. «Условно» — потому что если дело все-таки дойдет до суда, еще непонятно, кто окажется прав.
Понятное дело, что создатели энкодеров предпочли бы, чтобы закодированные файлы никто и никогда не мог бы раскодировать. Но у тех, кто остался с закодированные кодом после недобросовестных фрилансеров, или после исчезновения компании-разработчика (что бывает очень часто), мнение про декодирование — диаметрально противоположное.
Интересные факты и байки
Большая часть энкодеров последние пару лет всего лишь чуть-чуть меняет формат файлов «под капотом», и выпускается под видом новой версии.
При обфускации коротких имен достаточно часто возникают коллизии. Видимо, в таких случаях, тех.поддержка энкодеров просто советует не пользоваться обфускацией.
Фрилансеры настолько часто используют куски кода из документации PHP и со StackOverflow, что словарь, составленный из идентификаторов, взятых оттуда из примеров, позволяет обычно деобфусцировать под 90% всех имен в среднем проекте.
За все время работы я встретил всего лишь пять различных декомпиляторов PHP. Три из них были написаны русскоязычными программистами, еще один — китайцем и еще один — божились, что французом. Мелочь, а приятно — горд за «наших» 🙂
При этом большинство русскоязычных клиентов просит по-свойски сделать работу бесплатно 🙂
Несколько раз получалось так, что декодировать определенный формат файлов мог только я. И одни и те же файлы приходили на декодирование через нескольких разных посредников одновременно.
Особенно меня повеселила такая история: негр с африканским именем и со швейцарским гражданством, разругался с фрилансером-программистом из Австралии, не заплатил ему за работу и остался с парочкой закодированных недоделанных файлов на своем сайте. Долго искал на фрилансерских биржах того, кто их раскодирует, пока наконец один индус не впарил ему свои услуги.
Три недели этот индус кормил заказчика завтраками, а сам усиленно искал реального исполнителя. Параллельно заказчик (жук еще тот) под другим именем и сам продолжал искать других декодировщиков на все тех же фрилансерских биржах. Нашел меня, отдал мне проект… и тут же, буквально через полчаса, ко мне постучался индус и с чувством явного облегчения стал уговоривать сделать и его проект тоже. Я сравнил файлы, и…
Конечно, стоило бы в воспитательных целях взять у обоих 100% предоплаты… но я просто заставил их пообщаться и разобраться между собой.
По итогам, индус до сих пор не забывает поздравить меня с днем рождения.
Заказчик даже дал мне бонус, а сейчас переехал в Эстонию (!) потому что там дешевле жить, и периодически уговаривает меня поучаствовать в каких-то его сомнительных прожектах.
UPD. Пришлось вырезать часть примера с eval-закодированым кодом, потому что Kaspersky выдавал на него предупреждение. Спасибо nokimaro!
Методика деобфускации PHP-скриптов для чайников. Полезные советы.
Как я уже говорил выше, задача деобфускации PHP-cкриптов очень актуальна. Достаточно взглянуть на один популярный хак-форум:
Что ж, давайте по шагам разберем, как в большинстве случаев деобфусцируются скрипты на PHP. Возьмем пример прямо из темы на вышеупомянутом форуме с просьбами людей, не желающих разобраться самостоятельно либо думающих, что это невероятно сложно.
Для примера берем совершенно абстрактный, накрытый неизвестным мне, но в последнее время чрезвычайно популярным обфускатором, скрипт: original.php.
Сохраняем его в директорию нашего Web-сервера под именем original.php, а затем дублируем его же под другим именем, например, script.php. Позже я поясню, для чего это надо. Открываем скрипт и начинаем его изучение.
Обратите внимание, что редактирую я скрипт script.php, а оригинал original.php оставил без изменений. Обращаемся в браузере к скрипту script.php и видим вывод: base64_decode. Понятно, значит, перед вызовом eval() ничего плохого не происходит, и можно смело менять его на print():
Снова запускаем script.php через браузер и видим следующий вывод:
Уже по этому неразборчивому коду можно догадаться, что он открывает какой-то файл на выполнение и что-то с ним делает (скорее всего, читает). Кроме того, тут появился очередной вызов eval(). Выведя значение переменной $OOO000O00 видим, что это не что иное, как имя функции fopen(). Какой же файл открывает данный код? Воспользуемся подсветкой Notepad++:
Видим, что это имя текущего файла, то есть script.php. Мы уже серьезно изменили этот скрипт, но можно же заставить код считывать оригинал, который мы заботливо припасли!
Далее, выводя содержимое переменной $O0O00OO00, убеждаемся, что это имя функции fread и файл действительно читается. Сначала считывается 0х49f байтов, которые никуда не сохраняются, а затем еще 0х17c, и вот с ними скрипт уже что-то делает. Чтобы определить, что именно он с ними творит, выводим как обычно содержимое переменных $OOO0000O0 (это base64_decode) и $OOO00000O (это strtr). Таким образом, считанное содержимое сначала прогоняется через strtr(), а затем через base64_decode(). Проще говоря, производится раскодирование. А раскодированное содержимое затем пропихивается в eval(). Теперь мы его снова можем безболезненно заменить на print():
Снова исполняем через браузер script.php и видим очередную порцию кода:
Добавляем этот код в файл script.php и начинаем его разбирать:
С помощью подсветки Notepad++ (выделяя по очереди переменные с исковерканными именами) узнаем уже знакомую конструкцию base64_decode(strtr(fread())), только вот в fread теперь количество байтов для считывания указано неявно, но и это не проблема, ведь чуть выше оно пишется в переменную, которая и передается в fread:
Здесь же явно вызывается str_replace, меняющая в считанной и декодированной строке все вхождения подстроки “__FILE__” на имя нашего файла. Значит, меняем замену на “original.php”, как мы делали выше:
И снова запускаем скрипт из браузера. Ура! Наконец-то мы получили исходный код скрипта! В нем немного исковерканы некоторые имена переменных, но это уже не проблема, так как мы видим исходный код!
Вот и все, скрипт расшифрован без проблем и сложностей! Таким методом, пошагово анализируя кусочки кода, которые нам встречаются, можно снять любую обфускацию, потратив на это не более получаса! Проведя такую работу несколько раз с разными типами обфускаторов и разными скриптами, вы научитесь распознавать их сразу по внешнему виду и расшифровывать буквально за 5 минут!
Стоит заметить, что в накрытых рассмотренным обфускатором скриптах не стоит ничего менять (в том числе и комментарии), потому что скрипт считывает сам себя, а смещения, используемые при считывании, жестко забиты в файл. Таким образом, если поправить начальный комментарий, например, укоротив его всего на 1 символ, мы превращаем скрипт в неработоспособный текстовик.
Также отмечу, что при деобфускации данного скрипта я производил множество лишних действий (для того, чтобы как можно более подробно показать пошаговый процесс снятия обфускатора), но все это можно проделать гораздо быстрее и оптимальнее, если немного знать, как устроен тот или иной обфускатор.
Для конкретных обфускаторов можно написать автоматический скрипт-деобфускатор, чтобы свести работу к минимуму.
В свое время я уже писал скрипт, позволяющий снимать конкретно такую обфускацию, и с радостью делюсь им с вами. Этот скрипт также умеет и снимать привязку (так как такой обфускатор, который я рассмотрел в статье, умеет делать привязку скрипта к конкретному домену). Скачать деобфускатор!
Методика деобфускации PHP-скриптов для чайников. Полезные советы.: 44 комментария
Спасибо за полезную и интересную статью.
Пускай Каими расскажет, как он дотнет обфусцировал во втором квесте 🙂
DX, привет.
Можешь посоветовать наиболее мощный обфускатор кода, на твой взгляд.
Твой обфускатор снимается так же не сложно, как и описанный в статье.
Спасибо!
Привет, не могу, потому что все обфускаторы примитивны, да и нельзя сделать обфускатор для PHP настолько сложным, чтобы его нельзя было снять в течение пары часов. Можно, конечно, использовать Zend Optimizer или ioncube, но и для их снятия есть готовые пути.
По деобфускации ioncube не подскажешь? Не нашел решений.
После снятия куба функции остаются зашифрованными.
Это другой уровень, на уровне самого интерпретатора PHP и Zend, а не просто скрипт. Не в курсе, как такое снимают.
Извиняюсь, конечно, но статья бесолезная. Смысл показывать и так очевидные действия?
Кому-то бесполезная, а кому-то и очень полезная. Я, например, не знал всего этого (и в свое время не прошел квест от Kaimi дальше этапа с обфускацией пхп).
А мне было интересно почитать.
Автору респект, Статья правда поучительная, вот только вопрос, возможно ли, что после деобфускации код будет не совсем в рабочем состоянии. Или это у меня ручки кривые))?
Если всё сделано правильно, то код будет полностью рабочий)
Благодарю за статью, очень помогло. Даже не думал, что так просто.
Подскажите как теперь код привести к чистому
вот что получилось
а хотелось бы что бы это выглядело без этих 00000 и не понятных крякозяб
Брать и руками приводить.
если знаешь как расскажи пожалуйста и желательно по подробней я впервый раз этим занимаюсь а очень надо
В твоем случае “расскажи как” эквивалентно “сделай за меня”. Если было бы желание, прочитал бы статейку и сам допер, ничего сложного нет, просто однообразная работа. Если желания нет, то проще заказать у каких-нибудь фрилансеров.
И снова запускаем скрипт из браузера. Ура! Наконец-то мы получили исходный код скрипта! В нем немного исковерканы некоторые имена переменных, но это уже не проблема, так как мы видим исходный код!
а да сверху я выложил именно исходный код а не вывод браузера
здраствуйте, часть кода расшифровал как описано здесь (http://habrahabr.ru/blogs/php/137459/).
Никак не могу понять что делать здесь
$GLOBALS[‘_414705932_’][51](round(0) + 2.5 + 2.5) => “”,
$GLOBALS[‘_414705932_’][52](round(0) + 5.3333333333333 + 5.3333333333333 + 5.3333333333333) => “”,
$GLOBALS[‘_414705932_’][53](round(0) + round(0 + 1.6 + 1.6 + 1.6 + 1.6 + 1.6)) => “”,
$GLOBALS[‘_414705932_’][54](round(0) + round(0 + 0.75 + 0.75 + 0.75 + 0.75)) => “”,
$GLOBALS[‘_414705932_’][55](round(0) + 10.333333333333 + 10.333333333333 + 10.333333333333) => “”,
$GLOBALS[‘_414705932_’][56](round(0) + 4.3333333333333 + 4.3333333333333 + 4.3333333333333) => “”,
Огромное человеческое спасибо
для чайника поясни на 1-м примере пожалуйста
Вместо этих lllllllll когда-то были осмысленные имена переменных, но обфускатор их необратимо похерил. Тут уже только ручками остаётся догадываться по смыслу, что было, и переназывать эти переменные самому.
Kaimi или dx, подскажите каким обфускатором был обработан пример используемый в статье?












