php代码交付混淆,解密混淆的PHP程序
团队⼤佬在做PHP代码审计的时候发现PHP代码是被混淆过的。虽然可以通过⾃⼰⼿动解密可以还原原先的PHP代码,但是混淆过程⽐较复杂且⾃⼰写脚本还原⾮常⿇烦。所以,我这边通过PHP底层的操作对混淆后的PHP代码进⾏还原。
0x02 PHP代码混淆
PHP代码混淆⼀般来说有两种⽅法:
需要PHP扩展
⽆需PHP扩展
本⽂我们主要讲解⽆需PHP扩展的代码混淆的解密。⼤多数的⽆需扩展的php代码混淆原理上都是使⽤eval进⾏代码的执⾏。如果我们能够得到 eval 函数的参数,即可获得解密后的代码。
不过,⼀般来说PHP的混淆都会通过多次 eval 来还原并执⾏php代码,所以我们可以通过hook PHP的eval函数来打印其参数来解密代码。
0x03 hook eval
PHP中的eval函数在Zend⾥需要调⽤ zend_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 参数的值
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程序跑起来
php如何运行代码通过修改 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扩展的原理就是⽤我们的函数hook zend_compile_string 函数,将函数的参数打印出来后再交还给 zend_compile_string 函数执⾏即可。
./ext/ext_skel --extname=decrypt_code
⾸先,我们写⼀个⾃⼰的hook函数。此函数的功能就是判断 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 函数的地址,接着⽤我们的hook函数替换 zend_compile_string 的地址。当 eval 函数调⽤zend_compile_string 时,就调⽤了我们的hook函数。
最后,我们修改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;
}
当这个扩展被卸载的时候将函数的hook解除。
最后,编译我们的扩展
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 ,所以也可以利⽤hook eval 来还原混淆后的加密代码。
0x05 Example
⼀段⽰例代码。
$_uU=chr(99).chr(104).chr(114);$_cC=$_uU(101).$_uU(118).$_uU(97).$_uU(108).$_uU(40).$_uU(36).$_uU(95).$_uU(80).$_u
运⾏⼀下,得到解密后的结果。
./php 3.php
=====================DUMP_CODE========================
function __lambda_func()
=====================DUMP_CODE========================
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论