文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

phpjiami加密原理详解及解密

2023-09-08 20:58

关注

零、引言

最近工作中遇到一些使用phpjiami进行加密的php代码,所以对这个加密进行了详细的分析。
本文包括如下内容:

  1. phpjiami的加密原理
  2. 详细的phpjiami的解密方法
  3. 略带一些Php-parser使用方法

一、管中窥豹-了解phpjiami使用

phpjiami的官方网站为:https://www.phpjiami.com/phpjiami.html
使用phpjiami有几个关键的参数:

  1. 独立加密后,解密的代码会在原本的代码中。如果使用_lib库会生成一个单独的_lib.php,enc.php会通过include(’_lib.php’)进行解密,实际的解密代码和独立加密相同,后面不做单独分析。
  2. 控制参数,免费用户只能锁定ip、文件名和过期时间。
    在这里插入图片描述
    为了测试加密解密的效果创建一个包含类、函数的测试代码
function info($a,$b){    return $a.':'.$b;}class people{    protected $a;    protected $b;    public function __construct($a,$b)    {        $this->a = $a;        $this->b = $b;    }    public function info()    {        return $this->a.':'.$this->b;    }    public static function phpinfo()    {        phpinfo();    }}$name = $_GET['name'];$age = $_GET['age'];echo info($name,$age);$p = new people($name,$age);echo $p->info();people::phpinfo();

加密后代码如下,可以发现如下特点

  1. 函数名、变量名都被替换为了不可见字符,所有代码都缩到了一行,干扰正常分析。
  2. 代码中有3个函数,如果多加密几个文件会发现都是3个函数,因此3个函数就是解密代码运行的关键。
  3. 在代码?>后面还有一坨乱码,猜测保存了原始的加密代码。
    在这里插入图片描述

二、磨刀霍霍-Php-parser美化phpjiami代码

乱码太严重,而且格式不规范,是时候祭出神器PHP-Parser对代码美化一下。(百科:PHP Parser 是由 nikic 开发的一款 php 抽象语法树(AST)解析工具。PHP Parser 同时兼顾接口易用,结构简洁,工具链完善等诸多优点。在工程上,普遍使用 PHP Paser 生成模板代码,或使用其生成的抽象语法树进行静态分析,https://github.com/nikic/PHP-Parser),挖个坑之后有机会再详细研究研究怎么使用,在enphp mzphp2的解密中就需要大量使用。

  1. 安装PHP-Parser
安装PHP-Parser
  1. 使用PhpParser解析加密后的代码获取AST树
$code = file_get_contents('./test_enc.php');$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);try {    $ast = $parser->parse($code);} catch (Error $error) {    echo "Parse error: {$error->getMessage()}\n";    return;}
  1. 使用NodeFinder获取所有的函数,并将乱码函数名替换为func1,func2
$nodeFinder = new NodeFinder;$Funcs = $nodeFinder->findInstanceOf($ast, PhpParser\Node\Stmt\Function_::class);$map = [];$v = 0;foreach ($Funcs as $func) {    $funcname = $func->name->name;    if (!isset($map[$funcname])) {        if (!preg_match('/^[a-z0-9A-Z_]+$/', $funcname)) {            $code = str_replace($funcname, "func" . $v, $code);            $v++;            $map[$funcname] = $v;        }    }}
  1. 使用token_get_all获取php代码中的基本令牌,并将乱码变量名替换为v1,v2
//将乱码变量名,替换变量为$v1,$v2$v = 0;$map = [];$tokens = token_get_all($code);foreach ($tokens as $token) {    if ($token[0] === T_VARIABLE) {        if (!isset($map[$token[1]])) {            if (!preg_match('/^\$[a-zA-Z0-9_]+$/', $token[1])) {                $code = str_replace($token[1], '$v' . $v++, $code);                $map[$token[1]] = $v;            }        }    }}
  1. 美化格式并保存
//美化格式输出$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);try {    $ast = $parser->parse($code);} catch (Error $error) {    echo "Parse error: {$error->getMessage()}\n";    return;}$prettyPrinter = new PrettyPrinter\Standard;$prettyCode = $prettyPrinter->prettyPrintFile($ast);echo $prettyCode;file_put_contents('./test_enc_format.php', $prettyCode);

解密完成后,代码基本可读。
在这里插入图片描述
完整代码如下

use PhpParser\Error;use PhpParser\ParserFactory;use PhpParser\PrettyPrinter;use PhpParser\NodeFinder;require 'vendor/autoload.php';//1. 读取代码并解析$code = file_get_contents('./test_enc.php');$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);try {    $ast = $parser->parse($code);} catch (Error $error) {    echo "Parse error: {$error->getMessage()}\n";    return;}//将乱码函数名,替换函数为func1,fun2$nodeFinder = new NodeFinder;$Funcs = $nodeFinder->findInstanceOf($ast, PhpParser\Node\Stmt\Function_::class);$map = [];$v = 0;foreach ($Funcs as $func) {    $funcname = $func->name->name;    if (!isset($map[$funcname])) {        if (!preg_match('/^[a-z0-9A-Z_]+$/', $funcname)) {            $code = str_replace($funcname, "func" . $v, $code);            $v++;            $map[$funcname] = $v;        }    }}//将乱码变量名,替换变量为$v1,$v2$v = 0;$map = [];$tokens = token_get_all($code);foreach ($tokens as $token) {    if ($token[0] === T_VARIABLE) {        if (!isset($map[$token[1]])) {            if (!preg_match('/^\$[a-zA-Z0-9_]+$/', $token[1])) {                $code = str_replace($token[1], '$v' . $v++, $code);                $map[$token[1]] = $v;            }        }    }}//美化格式输出$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);try {    $ast = $parser->parse($code);} catch (Error $error) {    echo "Parse error: {$error->getMessage()}\n";    return;}$prettyPrinter = new PrettyPrinter\Standard;$prettyCode = $prettyPrinter->prettyPrintFile($ast);echo $prettyCode;file_put_contents('./test_enc_format.php', $prettyCode);

三、一探究竟-phpjiami加密原理

先给出整个加密代码的结构,后面再各个击破

function func0($v0)//反调试,解密代码function func1(&$v1, $v2)//全局函数名恢复function func2($v1, $v2 = '')//输入$v1需要解密的字符串,返回解密后的字符串if(!$v1){n个func1解密函数名并赋值全局变量}$v46=func0(_f_);//读取解密代码set_include_path(dirname('当前文件名'));$v90 = base64_encode($v46);//base64编码代码eval(func2('乱码字符串'));//真正执行的地方,乱码字符串解密后:eval(base64_decode($��ƻ��));//真正执行代码的地方,因为修改了变量名导致最终无法正常执行。set_include_path(dirname('当前文件名'));return null;?>加密压缩的代码+1个随机字符+32字节加密代码的md5值

phpjiami核心就是3个函数,每次加密3个函数的顺序会不一样,可以通过参数进行区分。

func2函数分析(解密函数)

func2函数输入两个参数,解密参数1得到对应的字符串。

  1. base64_decode解密需要用到的函数md5、ord、strlen、chr函数
  2. 计算特定不可见字符串的md5,用于后续异或解密
  3. 小于0xF5设置为 v1[ v1[ v1[i],大于0xF5设置为’’
  4. 异或解密字符串
    在这里插入图片描述
    手动实现类似的代码
function func2($v1, $v2 = ''){    //base64_decode解密使用到的函数,    $md5 = md5(pack("H*", 'EDE5E0E5ECEA'));    $v2 = !$v2 ? 0xf5 : $v2;    $i = 0;    $str = '';    for (; $i < strlen($v1);$i++) {        $str .= ord($v1[$i]) < 0xf5 ? ord($v1[$i]) > $v2 && ord($v1[$i]) < 0xF5 ? chr(ord($v1[$i]) / 2) : $v1[$i] : '';        //v2并未设置,因此小于0xF5设置为$v1[$i],大于0xF5设置为''    }    $str = base64_decode($str);    $i = 0;    $result = '';    $j = $md5_len = strlen($md5);    for(;$i<strlen($str);$i++)//循环和md5值进行异或    {        $j = $j?$j:$md5_len;        $j--;        $result .= $str[$i]^$md5[$j];    }    return $result;}

func1函数分析(全局函数名恢复)

func1通过str_rot13、gzuncompress、stripcslashes、func2对字符串进行解密,并赋值给传入的全局变量。
在这里插入图片描述

手动恢复代码**(ps:因为编码格式不同,直接将加密字符串复制出来会解密失败,这也是为什么在phpstorm里面修改了加密代码保存之后无法正常运行的原因)(pps:可以在010editor里面进行修改,或者phpstorm里面有什么地方进行设置,知道的大佬可以交流一下)**

function func1(&$v1,$v2){    $funcs = str_rot13(gzuncompress(stripcslashes(func2("一串不可见字符"))));    $arrays_func = explode(',',$funcs);    $v1 = $arrays_func[$v2];}

func0函数分析(核心函数)

func0主要用于反调试和最后文件解密。
在这里插入图片描述

反调试1—启动方式反调试:如果是cli启动,则退出程序。

php_sapi_name() == cli ? die():''

反调试2—服务端信息反调试:如果没有设置相关的服务器变量,则退出程序。

if (!isset($_SERVER['HTTP_HOST']) && !isset($_SERVER['SERVER_ADDR']) && !isset($_SERVER['REMOTE_ADDR'])) {    die();}

反调试3—时间反调试:两个语句运行时间超过100毫秒,则退出程序。

$time1 = microtime(true) * 1000;if (microtime(true) * 100 - $time1 > 100) {die();}

反调试4—文件完整性反调试:先读取最后44个字节并调用func2进行解密得到33个字节内容,再读取除了后44个字节的文件内容并计算md5,最后查看md5是非在前面解密内容中。

可以知道加密后文件结构=解密代码+加密压缩后的代码+1字节随机字节+32字节md5(加密代码)

!strpos(func2(substr($file,func2(pack('H*','54ee5947')),func2(pack('H*','\x54\xee\x4d\x3d')))),md5(substr($file,func2(pack('H*','55ce3d3d')),func2('H*','54ee5946'))))?$nothisfunc():$nothisvar;

反反调试最简单的方法就是将所有的反调试注释掉。

最后的代码:计算了需要解密的代码偏移,使用str_rot13、@gzuncompress、func2、substr解密得到最终的代码。
在这里插入图片描述

四、直捣黄龙-完整解密

通过前面的分析可以知道

加密后的文件=解密代码+加密压缩后的代码+1字节随机字节+32字节md5(加密代码)

只需要获取到加密压缩后的代码进行解密就好了(ps:func2中用于计算md5值的不可见字符会变化,需要手动获取)

function func2($v1, $v2 = ''){    //base64_decode解密使用到的函数,    $md5 = md5(pack("H*", 'EDE5E0E5ECEA'));    $v2 = !$v2 ? 0xf5 : $v2;    $i = 0;    $str = '';    for (; $i < strlen($v1);$i++) {        $str .= ord($v1[$i]) < 0xf5 ? ord($v1[$i]) > $v2 && ord($v1[$i]) < 0xF5 ? chr(ord($v1[$i]) / 2) : $v1[$i] : '';        //v2并未设置,因此小于0xF5设置为$v1[$i],大于0xF5设置为''    }    $str = base64_decode($str);    $i = 0;    $result = '';    $j = $md5_len = strlen($md5);    for(;$i<strlen($str);$i++)//循环和md5值进行异或    {        $j = $j?$j:$md5_len;        $j--;        $result .= $str[$i]^$md5[$j];    }    return $result;}$file = file_get_contents('test_enc.php');$enc_code = explode('?>',$file);//try {    $dec_code = str_rot13(@gzuncompress(func2(substr($enc_code[1], 0, -44))));//解密代码    print_r($dec_code);    file_put_contents('test_dec.php',$dec_code);}catch (Error $error){    echo "Parse error: {$error->getMessage()}\n";    return;}

成功解密
在这里插入图片描述

五、总结

phpjiami有点像最早的android加固的感觉。其实获取到的代码还使用了enphp、mzphp2进行了加密,和phpjiami也有点类似,挖个坑后续有空补上解密过程。

参考:

  1. 《PHP解密:zym加密 带乱码调试过程 》https://www.52pojie.cn/thread-693641-1-1.html
  2. 《初探PHP-Parser和PHP代码混淆》https://www.redteaming.top/2020/05/07/%E5%88%9D%E6%8E%A2PHP-Parser%E5%92%8CPHP%E4%BB%A3%E7%A0%81%E6%B7%B7%E6%B7%86/
  3. 《PHPJiaMi 免扩展加密分析及解密》

来源地址:https://blog.csdn.net/abel_big_xu/article/details/127827902

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯