一个简单的php加密的理解

前言

原帖子 www.52pojie.cn/thread-1991...

一段简单的 php 代码加密,大佬使用了一段 python 代码给解密出来了,但是我没太理解整个逻辑

于是在本地跑了一遍,尝试理解整个解密流程,这里记录下整个学习过程

可读化显示

加密代码使用了非可读字符的变量名,需要重命名处理为可读的

使用 php-parser 将代码的变量进行重命名及格式化显示,安装模块 composer require nikic/php-parser

遍历 AST 树 enterNode 的时候进行变量重命名,变量命名格式为 var? 数字递增

php 复制代码
<?php
require 'vendor/autoload.php';

use PhpParser\Error;
use PhpParser\Node;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;

$code = file_get_contents('db.php');

$parserFactory = new ParserFactory();
$traverser = new NodeTraverser();
$prettyPrinter = new PrettyPrinter\Standard();

$varMap = [];
$counter = 0;

$parser = $parserFactory->createForHostVersion();

$traverser->addVisitor(new class($varMap, $counter) extends NodeVisitorAbstract {
    private $varMap;
    private $counter;

    public function __construct(&$varMap, &$counter) {
        $this->varMap = &$varMap;
        $this->counter = &$counter;
    }

    public function enterNode(Node $node) {
        if ($node instanceof Node\Expr\Variable && is_string($node->name)) {
            $name = $node->name;
            if (!isset($this->varMap[$name])) {
                $this->varMap[$name] = 'var' . $this->counter++;
            }
            $node->name = $this->varMap[$name];
        }
    }
});

try {
    $stmts = $parser->parse($code);
    $stmts = $traverser->traverse($stmts);
    $prettyCode = $prettyPrinter->prettyPrintFile($stmts);
    file_put_contents('db-rename.php', $prettyCode);
} catch (Error $e) {
    echo "Parse error: ", $e->getMessage();
}

原始的代码显示为如下,使用上述代码经过重命名处理以及格式化处理后显示如下

可以很清晰度看到,前面都是变量的定义,并且是使用了 $var0 的不同索引组成的变量名

本地调试

通过调试发现, $var10 是将代码文件的内容重新读了一遍

并取了最后两行赋值给 <math xmlns="http://www.w3.org/1998/Math/MathML"> v a r 11 和 var11 和 </math>var11和var12,原始的文件只有两行代码,格式化后行数变了,和原来逻辑对不上

<math xmlns="http://www.w3.org/1998/Math/MathML"> v a r 13 截取了代码的一部分,并进行了 m d 5 计算,得到 m d 5 值保存为 var13 截取了代码的一部分,并进行了 md5 计算,得到 md5 值保存为 </math>var13截取了代码的一部分,并进行了md5计算,得到md5值保存为var14

但是从这里看 md5 值并没有直接使用对比,而是直接替换为空了,也可能在嵌套的 eval 中使用了?

这个 eval 中的代码就是 @eval(base64_decode(str_replace($md5, '', strtr('......', '...', '...')))

由于 eval 里面还有嵌套的 eval,而且 eavl 内部的一些变量信息经处理后已经丢失了(被重命名为 $var? 了)

这里就没继续分析了,按原帖的思路将 eval 结果直接另存为文件,然后再拼接起来

原代码最后部分应该是密钥的处理部分,大佬的实现更为巧妙,直接就绕过了,这里附上原帖的 python 处理代码

python 复制代码
import os

with open("db.php", "rb") as f:
    db = f.read()

assert db.count(b"@eval(") == 1
assert db.count(b");unset(") == 1
s = db.index(b"@eval(") + 0
e = db.index(b");unset(") + 2

db_eval2fwrite_1 = db.replace(b"@eval(", b'fwrite(fopen("db_1.fwrite","wb"),')
with open("db_eval2fwrite_1.php", "wb") as f:
    f.write(db_eval2fwrite_1)

os.system("php db_eval2fwrite_1.php")

with open("db_1.fwrite", "rb") as f:
    db_1 = f.read()

assert db_1.count(b" eval(") == 1

db_1 = b'fwrite(fopen("db_2.fwrite","wb"),' + db_1[db_1.index(b" eval(") + 6 :]

db_eval2fwrite_2 = db[:s] + db_1 + db[e:]
with open("db_eval2fwrite_2.php", "wb") as f:
    f.write(db_eval2fwrite_2)
os.system("php db_eval2fwrite_2.php")

with open("db_2.fwrite", "rb") as f:
    db_2 = f.read()

with open("result.php", "wb") as f:
    f.write(b"<?php\n" + db_2)
相关推荐
AI 智能服务8 小时前
第6课__本地工具调用(文件操作)
服务器·人工智能·windows·php
松涛和鸣11 小时前
49、智能电源箱项目技术栈解析
服务器·c语言·开发语言·http·html·php
晚枫歌F12 小时前
io_uring的介绍和实现
开发语言·php
Tisfy15 小时前
网站访问耗时优化 - 从数十秒到几百毫秒的“零成本”优化过程
服务器·开发语言·性能优化·php·网站·建站
xifangge202515 小时前
PHP 错误日志在哪里看?Apache / Nginx / PHP-FPM 一次讲清
nginx·php·apache
ShoreKiten18 小时前
ctfshow-web257【保姆级wp】
php·web
chilavert31820 小时前
技术演进中的开发沉思-303计算机原理:加密基础
开发语言·算法·php·计算机原理
AC赳赳老秦1 天前
DeepSeek 私有化部署避坑指南:敏感数据本地化处理与合规性检测详解
大数据·开发语言·数据库·人工智能·自动化·php·deepseek
桃花键神2 天前
Undetectable接入亮数据代理IP深度测评:高效、稳定、适配性极强的海外多账号运营利器
网络协议·tcp/ip·php
catchadmin2 天前
PHP 8.5 #[\NoDiscard] 揪出“忽略返回值“的 Bug
php