一个简朴的php加密的理解

打印 上一主题 下一主题

主题 1781|帖子 1781|积分 5343

媒介

原帖子 https://www.52pojie.cn/thread-1991616-1-1.html
一段简朴的 php 代码加密,大佬使用了一段 python 代码给解密出来了,但是我没太理解整个逻辑
于是在本地跑了一遍,尝试理解整个解密流程,这里记录下整个学习过程
可读化显示

加密代码使用了非可读字符的变量名,必要重命名处理为可读的
使用 php-parser 将代码的变量进行重命名及格式化显示,安装模块 composer require nikic/php-parser
遍历 AST 树 enterNode 的时间进行变量重命名,变量命名格式为 var? 数字递增
  1. <?php
  2. require 'vendor/autoload.php';
  3. use PhpParser\Error;
  4. use PhpParser\Node;
  5. use PhpParser\NodeTraverser;
  6. use PhpParser\NodeVisitorAbstract;
  7. use PhpParser\ParserFactory;
  8. use PhpParser\PrettyPrinter;
  9. $code = file_get_contents('db.php');
  10. $parserFactory = new ParserFactory();
  11. $traverser = new NodeTraverser();
  12. $prettyPrinter = new PrettyPrinter\Standard();
  13. $varMap = [];
  14. $counter = 0;
  15. $parser = $parserFactory->createForHostVersion();
  16. $traverser->addVisitor(new class($varMap, $counter) extends NodeVisitorAbstract {
  17.     private $varMap;
  18.     private $counter;
  19.     public function __construct(&$varMap, &$counter) {
  20.         $this->varMap = &$varMap;
  21.         $this->counter = &$counter;
  22.     }
  23.     public function enterNode(Node $node) {
  24.         if ($node instanceof Node\Expr\Variable && is_string($node->name)) {
  25.             $name = $node->name;
  26.             if (!isset($this->varMap[$name])) {
  27.                 $this->varMap[$name] = 'var' . $this->counter++;
  28.             }
  29.             $node->name = $this->varMap[$name];
  30.         }
  31.     }
  32. });
  33. try {
  34.     $stmts = $parser->parse($code);
  35.     $stmts = $traverser->traverse($stmts);
  36.     $prettyCode = $prettyPrinter->prettyPrintFile($stmts);
  37.     file_put_contents('db-rename.php', $prettyCode);
  38. } catch (Error $e) {
  39.     echo "Parse error: ", $e->getMessage();
  40. }
复制代码
原始的代码显示为如下,使用上述代码经过重命名处理以及格式化处理后显示如下
可以很清晰度看到,前面都是变量的定义,并且是使用了 $var0 的差别索引组成的变量名


本地调试

通过调试发现, $var10 是将代码文件的内容重新读了一遍
并取了末了两行赋值给 $var11 和 $var12,原始的文件只有两行代码,格式化后行数变了,和原来逻辑对不上
$var13 截取了代码的一部分,并进行了 md5 盘算,得到 md5 值生存为 $var14

但是从这里看 md5 值并没有直接使用对比,而是直接更换为空了,也大概在嵌套的 eval 中使用了?
这个 eval 中的代码就是 @eval(base64_decode(str_replace($md5, '', strtr('......', '...', '...')))

由于 eval 内里还有嵌套的 eval,而且 eavl 内部的一些变量信息经处理后已经丢失了(被重命名为 $var? 了)
这里就没继续分析了,按原帖的思路将 eval 效果直接另存为文件,然后再拼接起来
原代码末了部分应该是密钥的处理部分,大佬的实现更为巧妙,直接就绕过了,这里附上原帖的 python 处理代码
  1. import os
  2. with open("db.php", "rb") as f:
  3.     db = f.read()
  4. assert db.count(b"@eval(") == 1
  5. assert db.count(b");unset(") == 1
  6. s = db.index(b"@eval(") + 0
  7. e = db.index(b");unset(") + 2
  8. db_eval2fwrite_1 = db.replace(b"@eval(", b'fwrite(fopen("db_1.fwrite","wb"),')
  9. with open("db_eval2fwrite_1.php", "wb") as f:
  10.     f.write(db_eval2fwrite_1)
  11. os.system("php db_eval2fwrite_1.php")
  12. with open("db_1.fwrite", "rb") as f:
  13.     db_1 = f.read()
  14. assert db_1.count(b" eval(") == 1
  15. db_1 = b'fwrite(fopen("db_2.fwrite","wb"),' + db_1[db_1.index(b" eval(") + 6 :]
  16. db_eval2fwrite_2 = db[:s] + db_1 + db[e:]
  17. with open("db_eval2fwrite_2.php", "wb") as f:
  18.     f.write(db_eval2fwrite_2)
  19. os.system("php db_eval2fwrite_2.php")
  20. with open("db_2.fwrite", "rb") as f:
  21.     db_2 = f.read()
  22. with open("result.php", "wb") as f:
  23.     f.write(b"<?php\n" + db_2)
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

盛世宏图

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表