На этот раз в наших руках оказалась программа, реализованная в виде локального веб‑приложения. Выглядит это так: в адресной строке браузера под Windows следует набрать localhost
, после чего прямо там же отобразится интерфейс программы. Все это чудо реализовано при помощи локального сервера Apache, являющегося частью программы. Разумеется, приложение имеет ряд лицензионных ограничений — триал, не работающие до момента покупки лицензии функции и прочее. Все эти явления мы постараемся побороть.
warning
Вся информация предоставлена исключительно в ознакомительных и обучающих целях. Ни автор, ни редакция не несут ответственности за любой возможный вред, причиненный материалами данной статьи. Нарушение лицензии при использовании ПО может преследоваться по закону.
Обычно подобные ограничения реализуются набором PHP-скриптов, которые представляют собой обычные текстовые файлы. Но не в нашем случае. Все скрипты, из которых состоит наше приложение, представляют собой мешанину букв, цифр и спецсимволов, однако при ближайшем рассмотрении мы видим, что у них имеется одинаковый заголовок вида:
<?php if(!function_exists('sg_load')){$__v=phpversion();$__x=explode('.',$__v);$__v2=-$__x[0].'.'.(int)$__x[1];$__u=strtolower(substr(php_uname(),0,3));$__ts=(@const-ant('PHP_ZTS') || @constant('ZEND_THREAD_SAFE')?'ts':'');$__f=$__f0='ixed.'.$__-v2.$__ts.'.'.$__u;$__ff=$__ff0='ixed.'.$__v2.'.'.(int)$__x[2].$__ts.'.'.$__u;$_-_ed=@ini_get('extension_dir');$__e=$__e0=@realpath($__ed);$__dl=function_exists-('dl') && function_exists('file_exists') && @ini_get('enable_dl') && !@ini_get(-'safe_mode');if($__dl && $__e && version_compare($__v,'5.2.5','<') && function_-exists('getcwd') && function_exists('dirname')){$__d=$__d0=getcwd();if(@$__d[1]-==':') {$__d=str_replace('\','/',substr($__d,2));$__e=str_replace('\','/',sub-str($__e,2));}$__e.=($__h=str_repeat('/..',substr_count($__e,'/')));$__f='/ixed-/'.$__f0;$__ff='/ixed/'.$__ff0;while(!file_exists($__e.$__d.$__ff) && !file_exi-sts($__e.$__d.$__f) && strlen($__d)>1){$__d=dirname($__d);}if(file_exists($__e.-$__d.$__ff)) dl($__h.$__d.$__ff); else if(file_exists($__e.$__d.$__f)) dl($__h.-$__d.$__f);}if(!function_exists('sg_load') && $__dl && $__e0){if(file_exists($_-_e0.'/'.$__ff0)) dl($__ff0); else if(file_exists($__e0.'/'.$__f0)) dl($__f0);}i-f(!function_exists('sg_load')){$__ixedurl='http://www.sourceguardian.com/loader-s/download.php?php_v='.urlencode($__v).'&php_ts='.($__ts?'1':'0').'&php_is='.@c-onstant('PHP_INT_SIZE').'&os_s='.urlencode(php_uname('s')).'&os_r='.urlencode(p-hp_uname('r')).'&os_m='.urlencode(php_uname('m'));$__sapi=php_sapi_name();if(!$-__e0) $__e0=$__ed;if(function_exists('php_ini_loaded_file')) $__ini=php_ini_loa-ded_file(); else $__ini='php.ini';if((substr($__sapi,0,3)=='cgi')||($__sapi=='c-li')||($__sapi=='embed')){$__msg="\nPHP script '".__FILE__."' is protected by SourceGuardian and requires a SourceGuardian loader '".$__f0."' to be installed.
Мешанина цифр и букв — строковый аргумент функции sg_load
. Налицо явная обфускация, причем для ее идентификации даже не надо применять чудесную утилиту Phpid от Manhunter: в коде явно указано имя протектора — SourceGuardian. Как ни странно, но в сети есть примерное описание формата кодирования этого обфускатора. Аргумент sg_load
представляет собой строку, начинающуюся с 16 шестнадцатеричных символов, за которыми следует набор двоичных данных в кодировке Base64.
sg_load('12345678CHECKSUM/BASE64/BASE64/BASE64/BASE64=');
Сначала идет контрольная сумма, вычисляющаяся по символам, начиная с открывающего тега PHP и вплоть до восьмого символа аргумента sg_load
. Затем эта контрольная сумма сравнивается со следующими восемью символами аргумента, чтобы проверить целостность резервного кода. Алгоритм контрольной суммы представляет собой 32-битную версию контрольной суммы BSD. Двоичные данные в кодировке Base64 декодируются и раскрывают двоичный формат с четырьмя типами данных.
Тип char
используется для представления отдельных символов, небольших чисел и логических значений. Целые числа хранятся в 32-битном формате little endian (тип int
), а строки могут либо оканчиваться нулем (тип zstr
), либо иметь длину с префиксом (тип lstr
). Вначале анализируется первый заголовок, который содержит номер версии и настройки защиты на случай, если файл привязан к определенному IP-адресу или имени хоста. Первый байт блока данных в первом заголовке определяет назначение следующих байтов, пока не будет найден байт 0xFF
. Например, значение 0x2
указывает, что выполнение ограничено именем хоста, а значение 0x4
указывает, что далее следует длина второго заголовка.
Как только смещение зашифрованного второго заголовка вычисляется из первого заголовка, он дешифруется с использованием блочного шифра Blowfish в режиме CBC. Для его расшифровки могут использоваться как встроенные ключи, так и внешние, например из лицензионных файлов или получаемые по сети. Расшифровка кода без них невозможна.
Каждый успешно расшифрованный блок содержит три целых числа и фактические данные. Первое целое число — это контрольная сумма, вычисленная по простым данным. Второе целое число содержит длину незашифрованных данных, а третье целое число — размер данных после распаковки. Контрольные суммы вычисляются с использованием ранее упомянутой 32-битной контрольной суммы BSD. Если первое целое число совпадает с вычисленной контрольной суммой, расшифровка прошла успешно.
После расшифровки данные еще и распаковываются при помощи алгоритма Лемпеля — Зива. SourceGuardian использует реализацию lzo1x
и lzss
для файлов, закодированных с помощью более старой версии SourceGuardian. C использованием этой техники сжимаются и шифруются второй заголовок и блоки данных PHP. Подобно первому заголовку, синтаксический анализатор перебирает данные и извлекает значения второго заголовка. Он содержит информацию об ограничениях среды, среди которых присутствует имя владельца лицензии, номер лицензии, дата создания файла и срок действия файла.
За вторым заголовком следуют данные PHP. SourceGuardian может хранить несколько его версий для совместимости с разными версиями PHP. Для каждой версии используется один блок данных. Блоки состоят из двух целых чисел, указывающих совместимую версию PHP и размер зашифрованных данных, а также фактических данных PHP. Если найден совместимый блок данных для текущей версии PHP, он расшифровывается. В общем, вся эта информация, конечно, очень полезна для создания собственного распаковщика, но повергает в уныние объемом работы, который предстоит проделать для его реализации. Наша же цель, как обычно, — решить задачу максимально простым способом при помощи подручных средств.
Чтобы добиться желаемого, можно сдампить уже расшифрованный и распакованный шитый код непосредственно из виртуальной машины PHP. Такие методы широко распространены и описаны в интернете. Советуют как минимум три варианта:
Opcache, since PHP 7.1
php -d opcache.opt_debug_level=0x10000 test.php
phpdbg, since PHP 5.6
phpdbg -p* test.php
vld, third-party extension
php -d vld.active=1 test.php
Результатом данного действия станет дамп скомпилированной программы во внутренних инструкциях шитого кода, по которому можно восстановить логику. Однако мы не станем использовать этот способ. Во‑первых, для него нам понадобится Linux либо придется повозиться с компиляцией утилит под Windows. Во‑вторых, даже получив листинг, мы не сможем проанализировать логику работы скрипта непосредственно во время выполнения, тем более внутри работающей программы. И самый главный аргумент против — создатели SourceGuardian вовсе не ламеры: они подстраховались от подобной возможности, о чем нам снова поведал интернет. Умные люди, конечно, нашли способ борьбы с этим явлением, но он довольно‑таки трудозатратен.
Давай для начала попробуем просто загрузить PHP-программу в привычный для нас отладчик x64dbg, как бы странно это ни звучало. Ты спросишь, каким образом при помощи Windows-дебаггера можно отлаживать код PHP? Да запросто. Для понимания этого попробуем разобраться, что же такое PHP.
PHP — это платформенно‑независимый язык сценариев, который анализируется и выполняется интерпретатором. Интерпретатор PHP написан на Си и может быть скомпилирован под любую платформу. В отличие от таких языков, как Cи, код PHP не компилируется в исполняемый файл. Вместо этого во время выполнения код РНР‑приложения компилируется в байт‑код движком, который называется Zend Engine. После компиляции его инструкции (коды операций) выполняются собственной виртуальной машиной Zend Engine. У нее есть виртуальный процессор и собственный набор инструкций. Эти инструкции являются более высокоуровневыми, чем обычный машинный код, и не выполняются непосредственно центральным процессором. Вместо этого виртуальная машина предоставляет обработчик для каждой инструкции, который анализирует команду виртуальной машины и запускает собственный код ЦП.
Как видишь, интерпретатор инструкций можно загрузить в отладчик и трассировать шитый код с его помощью. В нашем случае он встроен в Apache HTTP Server, находящийся в модуле httpd.
. Нельзя просто так взять и запустить его из отладчика. Более того, к его процессу нельзя даже присоединиться прямым способом — он работает в фоновом режиме и скрыт в списке активных процессов для присоединения x64dbg. Но есть маленькая хитрость: если зайти в параметры x64dbg и в самой дальней, обычно скрытой вкладке «Прочее» выбрать режим «Установить x64dbg оперативным отладчиком (JIT)», то можно отлаживать даже скрытые процессы. Для этого в окне диспетчера задач жмем правой кнопкой мыши на Apache HTTP Server и выбираем «Отладка».
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»