Методика деобфускации PHP-скриптов для чайников. Полезные советы.
Вторник, 24. Январь 2012
Раздел: PHP, Для новичков, автор: dx
Очень часто непосвященные люди задают вопросы вроде "Как расшифровать обфусцированный PHP-скрипт?", "Хороша ли защита PHP-скрипта обфускатором?" и даже "Помогите деобфусцировать! Вам же не сложно?". Цель этой статьи - показать, что обфускаторы дают в 90% случаев никакущую защиту (которая способна защитить только от людей, увидевших язык программирования первый раз в жизни). Снять ее можно обычно минут за 10-20, получив исходный PHP-скрипт в первозданном виде. Остальные 10% случаев - это немного усложненная защита, которая, впрочем, снимается теми же путями. Если вы желаете самостоятельно научиться снимать обфускацию со скриптов, то эта статья специально для вас!
Как я уже говорил выше, задача деобфускации PHP-cкриптов очень актуальна. Достаточно взглянуть на один популярный хак-форум:

Что ж, давайте по шагам разберем, как в большинстве случаев деобфусцируются скрипты на PHP. Возьмем пример прямо из темы на вышеупомянутом форуме с просьбами людей, не желающих разобраться самостоятельно либо думающих, что это невероятно сложно.
Для деобфускации нам потребуются руки, трезвая голова и Notepad++ (можно и другой редактор, желательно умеющий подсвечивать одинаковые подстроки в документе - это ускорит снятие обфускатора).
Для примера берем совершенно абстрактный, накрытый неизвестным мне, но в последнее время чрезвычайно популярным обфускатором, скрипт: original.php.
Сохраняем его в директорию нашего Web-сервера под именем original.php, а затем дублируем его же под другим именем, например, script.php. Позже я поясню, для чего это надо. Открываем скрипт и начинаем его изучение.

Видим, что сам скрипт по размеру небольшой, зато в конце файла, уже после скрипта, находится некий зашифрованный кусок текста. Видим также вызов инструкции PHP eval(), которая исполняет переданный ей PHP-код. Первым делом меняем eval на print, чтобы просмотреть, что же она выполняет. Дабы убедится, что скрипт не натворит на нашем компьютере ничего плохого, просматриваем то, что идет выше - а это безобидный вызов urldecode(), несколько конкатенаций строк и присвоение значений паре переменных. Кроме того, перед вызовом eval() выполняется функция, имя которой хранится в переменной $OOO0000O0. Проверим, что эта функция не делает чего-либо зловредного, для начала просто выведя ее имя и временно закомментировав строку с eval:

Обратите внимание, что редактирую я скрипт script.php, а оригинал original.php оставил без изменений. Обращаемся в браузере к скрипту script.php и видим вывод: base64_decode. Понятно, значит, перед вызовом eval() ничего плохого не происходит, и можно смело менять его на print():

Снова запускаем script.php через браузер и видим следующий вывод:
$O000O0O00=$OOO000O00($OOO0O0O00,'rb');$O0O00OO00($O000O0O00,0x49f);$OO00O00O0=$OOO0000O0($OOO00000O($O0O00OO00($O000O0O00,0x17c),'EnteryouwkhRHYKNWOUTAaBbCcDdFfGgIiJjLlMmPpQqSsVvXxZz0123456789+/=','ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'));eval($OO00O00O0);
Уже по этому неразборчивому коду можно догадаться, что он открывает какой-то файл на выполнение и что-то с ним делает (скорее всего, читает). Кроме того, тут появился очередной вызов eval(). Выведя значение переменной $OOO000O00 видим, что это не что иное, как имя функции fopen(). Какой же файл открывает данный код? Воспользуемся подсветкой Notepad++:

Видим, что это имя текущего файла, то есть script.php. Мы уже серьезно изменили этот скрипт, но можно же заставить код считывать оригинал, который мы заботливо припасли!

Далее, выводя содержимое переменной $O0O00OO00, убеждаемся, что это имя функции fread и файл действительно читается. Сначала считывается 0х49f байтов, которые никуда не сохраняются, а затем еще 0х17c, и вот с ними скрипт уже что-то делает. Чтобы определить, что именно он с ними творит, выводим как обычно содержимое переменных $OOO0000O0 (это base64_decode) и $OOO00000O (это strtr). Таким образом, считанное содержимое сначала прогоняется через strtr(), а затем через base64_decode(). Проще говоря, производится раскодирование. А раскодированное содержимое затем пропихивается в eval(). Теперь мы его снова можем безболезненно заменить на print():

Снова исполняем через браузер script.php и видим очередную порцию кода:
$OO00O00O0=str_replace('__FILE__',"'".$OOO0O0O00."'",$OOO0000O0($OOO00000O($O0O00OO00($O000O0O00,$OO00O0000),'EnteryouwkhRHYKNWOUTAaBbCcDdFfGgIiJjLlMmPpQqSsVvXxZz0123456789+/=','ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')));fclose($O000O0O00);eval($OO00O00O0);
Добавляем этот код в файл script.php и начинаем его разбирать:

С помощью подсветки Notepad++ (выделяя по очереди переменные с исковерканными именами) узнаем уже знакомую конструкцию base64_decode(strtr(fread())), только вот в fread теперь количество байтов для считывания указано неявно, но и это не проблема, ведь чуть выше оно пишется в переменную, которая и передается в fread:
$OO00O0000=0x2f24;
Здесь же явно вызывается str_replace, меняющая в считанной и декодированной строке все вхождения подстроки "__FILE__" на имя нашего файла. Значит, меняем замену на "original.php", как мы делали выше:

После всего этого мы видим вызов fclose - значит, с файлом делаться больше ничего не будет. Остается последний вызов eval(), который мы в очередной раз меняем на print():

И снова запускаем скрипт из браузера... Ура! Наконец-то мы получили исходный код скрипта! В нем немного исковерканы некоторые имена переменных, но это уже не проблема, так как мы видим исходный код!

Вот и все, скрипт расшифрован без проблем и сложностей! Таким методом, пошагово анализируя кусочки кода, которые нам встречаются, можно снять любую обфускацию, потратив на это не более получаса! Проведя такую работу несколько раз с разными типами обфускаторов и разными скриптами, вы научитесь распознавать их сразу по внешнему виду и расшифровывать буквально за 5 минут!
Стоит заметить, что в накрытых рассмотренным обфускатором скриптах не стоит ничего менять (в том числе и комментарии), потому что скрипт считывает сам себя, а смещения, используемые при считывании, жестко забиты в файл. Таким образом, если поправить начальный комментарий, например, укоротив его всего на 1 символ, мы превращаем скрипт в неработоспособный текстовик.
Также отмечу, что при деобфускации данного скрипта я производил множество лишних действий (для того, чтобы как можно более подробно показать пошаговый процесс снятия обфускатора), но все это можно проделать гораздо быстрее и оптимальнее, если немного знать, как устроен тот или иной обфускатор.
Для конкретных обфускаторов можно написать автоматический скрипт-деобфускатор, чтобы свести работу к минимуму.
В свое время я уже писал скрипт, позволяющий снимать конкретно такую обфускацию, и с радостью делюсь им с вами. Этот скрипт также умеет и снимать привязку (так как такой обфускатор, который я рассмотрел в статье, умеет делать привязку скрипта к конкретному домену). Скачать деобфускатор!

flisk :
Спасибо за полезную и интересную статью.
[Ответить]
cb^3 :
Действительно полезно!)
[Ответить]
3mph :
Пускай Каими расскажет, как он дотнет обфусцировал во втором квесте :)
[Ответить]
true :
DX, привет.
Можешь посоветовать наиболее мощный обфускатор кода, на твой взгляд.
Твой обфускатор снимается так же не сложно, как и описанный в статье.
Спасибо!
[Ответить]
dx:
Январь 27th, 2012 at 22:03
Привет, не могу, потому что все обфускаторы примитивны, да и нельзя сделать обфускатор для PHP настолько сложным, чтобы его нельзя было снять в течение пары часов. Можно, конечно, использовать Zend Optimizer или ioncube, но и для их снятия есть готовые пути.
[Ответить]
komyak:
Январь 30th, 2012 at 23:15
По деобфускации ioncube не подскажешь? Не нашел решений.
После снятия куба функции остаются зашифрованными.
[Ответить]
dx:
Январь 31st, 2012 at 00:37
Это другой уровень, на уровне самого интерпретатора PHP и Zend, а не просто скрипт. Не в курсе, как такое снимают.
SHiNiGaMi :
Извиняюсь, конечно, но статья бесолезная. Смысл показывать и так очевидные действия?
[Ответить]
Kaimi:
Январь 28th, 2012 at 17:08
Зато есть куда посылать просителей отсюда https://forum.antichat.net/thread144505.html
[Ответить]
flisk:
Январь 29th, 2012 at 15:10
Кому-то бесполезная, а кому-то и очень полезная. Я, например, не знал всего этого (и в свое время не прошел квест от Kaimi дальше этапа с обфускацией пхп).
[Ответить]
zloid:
Февраль 13th, 2012 at 19:13
А мне было интересно почитать.
Анон :
http://habrahabr.ru/blogs/php/137459/
[Ответить]
NetSpider :
куча мала:
http://netspider.com.ua/index.php/tag/deobfuscation/
[Ответить]
PHP Generic Eval Unpacker :
[...] предыдущей статье dx рассказывал о ручной методике снятия типовой и [...]