正文
author: WeaponX
0x01 背景
团队大佬在做PHP代码审计的时候发现PHP代码是被混淆过的。虽然可以通过自己手动解密可以还原原先的PHP代码,但是混淆过程比较复杂且自己写脚本还原非常麻烦。所以,我这边通过PHP底层的操作对混淆后的PHP代码进行还原。
0x02 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
参数的值
<?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扩展的原理就是用我们的函数hook
zend_compile_string
函数,将函数的参数打印出来后再交还给
zend_compile_string
函数执行即可。
./ext/ext_skel --extname=decrypt_code
首先,我们写一个自己的hook函数。此函数的功能就是判断
eval
函数的参数是否为字符串,如果不是,则按原路径执行;如果是,则将参数打印出来后按照原路径执行。