Что такое язык PHP, зна­ют, навер­ное, все. Не все зна­ют, что на нем мож­но писать прак­тичес­ки пол­ноцен­ные при­ложе­ния, защищен­ные упа­ков­щиком SourceGuardian, деком­пилято­ра для которо­го в сво­бод­ном дос­тупе не сущес­тву­ет. Сегод­ня мы раз­берем прак­тичес­кий спо­соб быс­трой отладки такого бай­тко­да под Windows без необ­ходимос­ти под­робно раз­бирать его струк­туру. Какие‑то спе­циаль­ные инс­тру­мен­ты нам тоже не понадо­бят­ся — толь­ко лов­кость рук и нем­ного магии.

На этот раз в наших руках ока­залась прог­рамма, реали­зован­ная в виде локаль­ного веб‑при­ложе­ния. Выг­лядит это так: в адресной стро­ке бра­узе­ра под 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.exe. Нель­зя прос­то так взять и запус­тить его из отладчи­ка. Более того, к его про­цес­су нель­зя даже при­соеди­нить­ся пря­мым спо­собом — он работа­ет в фоновом режиме и скрыт в спис­ке активных про­цес­сов для при­соеди­нения x64dbg. Но есть малень­кая хит­рость: если зай­ти в парамет­ры x64dbg и в самой даль­ней, обыч­но скры­той вклад­ке «Про­чее» выб­рать режим «Уста­новить x64dbg опе­ратив­ным отладчи­ком (JIT)», то мож­но отла­живать даже скры­тые про­цес­сы. Для это­го в окне дис­петче­ра задач жмем пра­вой кноп­кой мыши на Apache HTTP Server и выбира­ем «Отладка».

Продолжение доступно только участникам

Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте

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

Вариант 2. Открой один материал

Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.


Подписаться
Уведомить о
1 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии