博客列表 >PHPJiaMi 免扩展加密分析及解密

PHPJiaMi 免扩展加密分析及解密

新月网络的博客
新月网络的博客原创
2017年10月30日 09:33:261533浏览

0x00 前言

前几天去玩了 pwnhub 公开赛的题目,源码下载之后发现是 PHPJiaMi 加密。之前有分析过 phpjm 加密并写出过解密文件,所以研究下这个 PHPJiaMi。
PHP 免扩展加密的主流加密方法采用了 ascii 码 129-255 的乱码来实现变量名、函数名混淆,编辑器打开后就是一堆乱码,造成不可读。

加密流程:源码 -> 加密处理(压缩,替换,BASE64,转义)-> 安全处理(验证文件 MD5 值,限制 IP、限域名、限时间、防破解、防命令行调试)-> 加密程序成品,再简单的说:源码 + 加密外壳 == 加密程序 (该段出处)

0x01 解密准备

这里做演示,我写了 phpinfo() 然后去 http://www.phpjiami.com/ 生成加密文件,打开之后,果然都是一片乱码。
使用代码修复工具 http://zhaoyuanma.com/phpcodefix.html 将 ascii 不可见字符的变量修复成正常的变量名,再 PHP 代码美化,方便下一步分析。

0x02 函数分析

代码内有三个函数,由于每次加密这三个函数的顺序都不一样,这个以传参方式区分这三个函数
fun1 = ($var1, $var2 = '') = 核心函数,将乱码转成正常字符串
fun2 = (&$var1, $var2) = 校验 IP、域名,防止被破解。最后一句是解密整个 php 文件
fun3 = ($var1) = 将需要用到的函数赋值给 N 个全局变量

先从 fun1 开始逆起,在编辑器中双击变量名,该变量高亮之后,可以看到它怎么变化,在哪里被使用。
一句句语句逆下去,发现有语法错误,其实是代码修复后的 bug,用 winhex 打开定位到这句代码,发现是三元运算符

$var2 = !$var2 ? ord('乱码') : $var2;

接下来是一句很奇怪很无用的代码,再下一句是 for 循环,我猜测是给 $i 赋值

for($i=0; $i<strlen($var1); $i++)

下个 for 循环里又是一个三元运算符,手动修复后,基本 fun1 的代码就出来。
接下来就是运行 fun1 函数,但是碰到个坑点,fun1 有很多处用到乱码做运算,而乱码不能直接拷到编辑器中。
用 winhex 将 16 进制的乱码字符复制出来,在运算的时候 pack("H*","乱码") 将它还原回乱码即可。

function fun1($var1, $var2 = '')
{
   $md5 = md5(pack("H*",'FBE3FCFAF9E0'));//乱码随机字符串1
   $var2 = !$var2?ord(pack("H*",'8C')):$var2;//乱码随机字符串2
   
   $str = '';    for($i=0; $i<strlen($var1); $i++)
   {
       $str .= ord($var1{$i}) < ord(pack("H*",'F5')) ? ((ord($var1{$i}) > $var2 && ord($var1{$i}) < ord(pack("H*",'F5'))) ? chr(ord($var1{$i}) / 2) : $var1{$i}) : '';
   }
   $de1 =  base64_decode($str);
   $len = $len2 = strlen($md5);
   $str2 = '';    for($i=0; $i<strlen($de1); $i++)
   {
       $len = $len ? $len : $len2;
       $len--;
       $str2 .= $de1[$i] ^ $md5[$len];
   }    return $str2;
}

接着开始还原 fun2 代码,fun2 中前面都调用了 fun1 解密字符串,解密可得到 fun2 后面需要用到的函数名。有了 fun1,后面解密都非常顺利

function fun2(&$var1, $var2){
   $de = str_rot13(strrev(gzuncompress(stripslashes(fun1(pack('H*','8ECA349A37A639E43946ACE4B242F4EED8E8A0CC4444E639E894C2C69CB0384134EEEEC6CA3233B433ECD89CC6D435A437CE98B0D2A092909E96D030EE8EA2A4D044CADCA2DC32CCE02BAADCEEEA4537D8ACB43298F238E0428EC29646A0CAA2EECA43D2B2C442F4C4358E46429434AE44B0DACAF0AE4190C845EE32AE34C82BC49494E22BCAA49CD6D2E8CA94D2D4A6329C929A3596F2CC36CCD6CCAAF0C646D0A69EC8F02FA2A03938D234C2D2E0DCCCCAC4A8A631AC4238C4A490ECD4CCF0DAF4CAB2'))))));
   $exp = explode(',', $de);
   $var1 = $exp[$var2];
}

$de 解出来是一堆函数名,然后赋值给全局变量,后面解密都需要用到。fun2 解出来是固定的字符串

,chr,addslashes,rand,gzuncompress,assert_options,assert,file_exists,file_get_contents,substr,unpack,constant,strpos,create_function,str_rot13,md5,set_include_path,dirname,preg_replace,base64_encode,base64_decode,

接着暂且先不看 fun3,回到主代码中。

这里创建了一堆全局变量,通过 fun2 赋值,每个变量都代表一个函数名

接着还原 fun3 函数,步骤一样。

function fun3(){
   php_sapi_name() == 'cli' ? die():'';
   $file = file_get_contents(__FILE__);    if(!isset($_SERVER['HTTP_HOST']) && !isset($_SERVER['SERVER_ADDR']) && !isset($_SERVER['REMOTE_ADDR']))
   {        die();
   }
   $time = microtime(true) * 1000;    if(microtime(true) * 1000 - $time > 100)
   {        die();
   }    if(strpos(__FILE__, gtmclaei) !== 0){$exitfunc();}
   !strpos(de1(substr($file, de1('A8414145'), de1('A841AA3D'))), md5(substr($file, de1('AAA23D3D'), de1('A8414190')))) ? unkonw1() : unkonw2();
   $loc1 = fun1('A841AA45ACEE3D3D');
   $loc1 = fun1('A8414190');
   $de = str_rot13(gzuncompress(fun1(substr($file, $loc1, $loc2))));//核心解密
   return $de;
}

后面的代码已经不用再看了,fun2 解出来的就是解密后的原代码。

最后附上解密脚本 http://sec2hack.com/phpjiami.zip


声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议