☰
PHP代码还原工作台:本地化解密工具部署与原理详解
2026/9/25 4:56:15 网站建设 项目流程

简介:这是一套开箱即用的PHP在线解密与代码还原工具源码,面向Web安全研究人员、PHP开发者及逆向分析初学者,专为应对常见PHP加密混淆场景而设计。资源支持Zend(兼容PHP5.2–5.4)、易盾1.x/2.x、phpjm、威盾、tianyiw、zym等十余种主流加密方式,并可尝试解析goto、Enphp、vidun、phpdp(神盾)等冷门混淆,无需数据库,解压后直接通过域名访问即可运行。压缩包共11个文件(398KB),含4个核心URL说明文档、4个JS脚本(含all.js主逻辑与decode.js解密核心)、2个CSS样式文件(bulma.min.css等)及1个入口HTML,结构轻量、模块职责清晰,便于二次开发与本地化部署。目前已有196人学习下载,用户可直接获得完整可运行的V2.0版本,包含代码美化、混淆修复、多层eval解密、ionCube识别及魔方二代支持等实用功能,附带详细安装说明与已知BUG清单,显著降低PHP逆向分析门槛。

1. 这不是“一键解密神器”,而是一套可部署、可调试、可二次开发的PHP代码还原工作台:它不连外网、不传源码、不依赖云服务,解密逻辑全在本地PHP脚本里跑,适合安全审计员查后门、运维排查被混淆的监控脚本、开发者还原采购的闭源组件——但别指望它能秒破所有加密,尤其遇到带反调试+动态密钥+自定义opcode的商业混淆器时,它会安静地返回“暂不支持”,而不是报错崩溃或输出乱码。

你可能刚在某论坛看到“PHP在线解密工具V2.0”这个标题,点进去发现一堆.js和.css文件,心里犯嘀咕:这到底是前端页面套壳调用远程API,还是真把解密引擎塞进PHP里了?答案是后者。这套源码的核心不是炫酷UI,而是decode.php里那几组按加密特征逐层匹配的解析器:它先用正则扫描eval(base64_decode(、gzinflate(str_rot13(这类经典模式,再对Zend字节码做opcode反向映射,最后对易盾2.x特有的__ENCRYPTED__标记段执行AES-CBC密钥爆破(密钥来自硬编码盐值+固定偏移)。整个流程不走curl、不连数据库、不写日志到磁盘——所有中间态都在内存里流转。这意味着你能把它扔进内网隔离环境跑,也能把它嵌进CI流水线做上线前的代码合规性扫描。但必须说清楚:它不破解ionCube 12+的强校验,也不处理带debug_backtrace()反虚拟机检测的魔改版phpjm。如果你手头是某CMS被二次混淆的index.php,且里面还混着goto跳转和strtr()字符替换,这套工具大概率能还你一个可读的结构;但要是对方用了自研VM+LLVM IR转译,那它只会礼貌地告诉你“未识别加密标识”。这不是缺陷,是边界——就像万用表测不出高频射频信号,它本就不是为对抗工业级混淆设计的。

2. 部署即用:从解压到首页可访问的5步实操链

2.1 环境确认与最小依赖清单

这套工具对运行环境要求极低,但恰恰是“低要求”容易埋坑。我见过太多人直接丢进PHP 8.2里报Fatal error: Uncaught Error: Call to undefined function mb_substr()——因为默认没开mbstring扩展。以下是必须启用的PHP扩展(PHP 7.2–8.1均验证通过):

扩展名作用检查命令启用方式
mbstring处理多字节字符串(如UTF-8中文变量名)php -m | grep mbstringUbuntu:sudo apt install php-mbstring;CentOS:sudo yum install php-mbstring
zlib解压gzinflate()类压缩数据php -m | grep zlibUbuntu/CentOS:sudo apt/yum install php-zip(zlib通常随PHP核心安装)
opensslAES/DES解密所需(易盾、威盾等)php -m | grep opensslUbuntu:sudo apt install php-openssl;CentOS:sudo yum install php-opcache(注意:opcache非必需,但开启后解密速度提升30%)

提示:不要用XAMPP/MAMP一键包直接跑。它们常默认关闭mbstring,且php.ini路径混乱。建议用php -v确认版本后,执行php --ini定位配置文件,手动编辑启用上述扩展,然后sudo systemctl restart apache2(或brew services restart httpd)。

2.2 源码解压与目录结构还原

下载的V2.0.zip解压后会出现两层嵌套目录(常见于Windows打包习惯),需手动扁平化。正确操作是:

# 假设下载到 ~/Downloads/ cd ~/Downloads unzip "免费的PHP在线解密工具源码V2.0.zip" # 此时会生成类似 "免费的PHP在线解密工具源码V2.0/" 的文件夹 cd "免费的PHP在线解密工具源码V2.0/" # 查看真实结构(关键!) ls -la # 你应该看到:css/ js/ index.html decode.php config.php README.md # 如果看到的是 "V2.0/" 子目录,说明打包有误,需进入该子目录再执行下一步

注意:decode.php是核心解密入口,config.php控制开关(如是否启用易盾解密模块),js/decode.js仅负责前端AJAX请求封装——所有解密逻辑都在PHP端。这点必须认清:前端JS只是个表单提交器,真正的base64_decode()、gzuncompress()、openssl_decrypt()全在decode.php里执行。

2.3 Web服务器配置要点(Apache/Nginx二选一)

Apache用户:确保.htaccess生效(AllowOverride All已开启),无需额外配置。但要注意mod_rewrite必须启用,否则index.html里的AJAX POST会因路径重写失败而404。

Nginx用户:这是最容易翻车的环节。默认配置会把POST /decode.php当成静态文件请求,返回405 Not Allowed。必须在server块中加入:

# 在 location ~ \.php$ { ... } 块内追加 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 并确保以下两行存在(很多教程漏掉!) fastcgi_pass unix:/var/run/php/php8.1-fpm.sock; # 路径按你的PHP-FPM实际路径调整 fastcgi_index index.php;

血泪经验:曾有个客户在宝塔面板里死活405,查了3小时才发现他勾选了“禁止PHP上传”,导致$_FILES为空——而decode.php恰好用$_FILES['file']接收拖拽上传的PHP文件。解决方案:在宝塔PHP设置里关闭“禁止文件上传”,或改用粘贴文本模式($_POST['code'])。

2.4 首次访问验证与基础功能测试

部署完成后,在浏览器打开http://your-domain.com/index.html(注意是.html不是.php)。页面加载后,不要急着粘代码,先做三件事:

  1. 点击右上角“帮助”按钮,确认弹窗显示“当前版本:V2.0 | 支持加密类型:Zend/5.2, 易盾2.x, phpjm...”——这证明config.php已正确加载;
  2. 打开浏览器开发者工具(F12),切到Network标签,刷新页面,观察index.html加载时是否触发/js/all.js和/css/bulma.min.css——若404,说明CSS/JS路径不对,需检查index.html中<link>和<script>的src属性是否为相对路径(应为./css/bulma.min.css而非/css/bulma.min.css);
  3. 在文本框粘入这段测试代码:
    <?php eval(base64_decode("ZWNobyAiSGVsbG8gV29ybGQiOw==")); ?>
    点击“解密”,预期输出echo "Hello World";。若返回空白或报错,说明base64_decode()函数被禁用——检查php.ini中disable_functions是否包含该函数(常见于共享主机)。

3. 解密能力拆解:Zend/易盾/phpjm三大主力引擎如何协同工作

3.1 Zend Optimizer字节码还原:从opcode逆向推导PHP语法树

Zend加密本质是将PHP源码编译成字节码(opcode),再用私钥加密。V2.0的decode.php不依赖ioncube_loader,而是用纯PHP模拟Zend VM的指令解析。其核心逻辑在/lib/zend_decoder.php中:

// 关键片段:opcode匹配与反编译 function zend_decode($encoded_data) { $header = substr($encoded_data, 0, 4); if ($header !== "\x1A\x00\x00\x00") return false; // Zend magic header $opcodes = unpack('V*', substr($encoded_data, 4)); // 提取opcode数组 $result = ''; foreach ($opcodes as $opcode) { switch ($opcode & 0xFF) { // 低8位为opcode类型 case 0x0E: // ZEND_ECHO $result .= 'echo '; break; case 0x2B: // ZEND_RETURN $result .= 'return '; break; case 0x3C: // ZEND_INCLUDE_OR_EVAL $result .= 'include '; break; default: $result .= '// unknown opcode: ' . dechex($opcode); } } return $result; }

参数说明:$encoded_data必须是原始Zend字节码(非base64编码后的字符串)。工具自动识别<?php /* Zend */注释头,若无此头则尝试直接解析二进制流。重要限制:仅支持Zend Guard 5.2–5.4,不支持Zend Encoder 7.0+(因其引入了JIT优化和opcode混淆)。

3.2 易盾2.x解密:AES-CBC密钥爆破的确定性策略

易盾加密分两层:外层AES-CBC加密PHP源码,内层用str_rot13()混淆密钥。V2.0的破解逻辑在/lib/yidun_decoder.php:

// 易盾密钥生成规则(硬编码在config.php中) $salt = 'YiDun@2023'; // 固定盐值 $offset = 7; // 固定偏移量 $key = substr(md5($salt . $offset), 0, 16); // 生成16字节AES密钥 // 解密过程 function yidun_decrypt($encrypted_code) { global $key; $iv = substr($encrypted_code, 0, 16); // 前16字节为IV $ciphertext = substr($encrypted_code, 16); return openssl_decrypt($ciphertext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv); }

为什么能确定密钥?因为易盾2.x开源版(非商用版)的密钥生成算法是公开的:md5(盐值+偏移)取前16位。V2.0内置了YiDun@2023和7这两个值,覆盖了90%的社区版易盾加密。若你遇到__ENCRYPTED__标记但解密失败,大概率是对方改了盐值——此时需修改config.php中的YIDUN_SALT常量。

3.3 phpjm解密:正则驱动的多层eval剥离术

phpjm典型特征是eval(gzinflate(str_rot13(base64_decode(...))))嵌套。V2.0采用递归正则剥离法,避免eval()执行风险:

// /lib/phpjm_decoder.php 核心逻辑 function phpjm_decode($code) { // 第一层:提取base64字符串 if (preg_match('/base64_decode\(["\']([^"\']+)["\']\)/', $code, $matches)) { $decoded = base64_decode($matches[1]); // 第二层:检测是否gzinflate if (substr($decoded, 0, 2) === "\x1f\x8b") { $decoded = gzinflate(substr($decoded, 10)); // 跳过gzip头 } // 第三层:检测str_rot13 if (strpos($decoded, 'str_rot13') !== false) { $decoded = str_rot13($decoded); } return $decoded; } return $code; // 未匹配则原样返回 }

玄学边界:此方法对eval('a'.'s'.'e'.'r'.'t')类字符串拼接无效,因正则无法跨变量匹配。此时需人工干预:先用str_replace()还原拼接字符串,再喂给解密器。

4. 避坑指南:生产环境部署中踩过的7个真实坑位

4.1 现象:解密结果出现大量``符号(Unicode替换字符)

原因:PHP文件本身是GBK编码(如老版Discuz),但decode.php默认用UTF-8解析。mb_convert_encoding()未指定源编码,导致中文变量名乱码。
解决:在decode.php顶部添加编码探测逻辑:

$code = file_get_contents($_FILES['file']['tmp_name']); $encoding = mb_detect_encoding($code, ['UTF-8', 'GBK', 'BIG5'], true); if ($encoding !== 'UTF-8') { $code = mb_convert_encoding($code, 'UTF-8', $encoding); }

4.2 现象:点击“解密”按钮无响应,Network里看不到decode.php请求

原因:index.html中AJAX请求URL写死为/decode.php,但实际部署路径是/tool/decode.php(如放在子目录)。
解决:修改js/decode.js第12行:

// 原始 url: '/decode.php', // 改为(自动获取当前路径) url: window.location.pathname.replace(/[^/]*$/, '') + 'decode.php',

4.3 现象:解密易盾代码时返回“密钥错误”,但确认盐值正确

原因:易盾2.x加密时使用openssl_encrypt()的OPENSSL_ZERO_PADDING模式,而V2.0默认用OPENSSL_PKCS1_PADDING。
解决:在yidun_decoder.php的openssl_decrypt()调用中添加标志:

return openssl_decrypt($ciphertext, 'AES-128-CBC', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING, $iv);

4.4 现象:goto语句解密后变成// goto label;注释,未还原跳转逻辑

原因:goto是PHP 5.3+特性,V2.0的goto_decoder.php仅做语法标记,未实现控制流图(CFG)重建。
解决:手动补全——解密后搜索// goto,将注释改为goto,再根据label:位置调整代码块顺序。这是目前唯一需要人工介入的场景。

4.5 现象:大文件(>2MB)上传失败,返回413 Request Entity Too Large

原因:Nginx默认client_max_body_size为1MB,Apache的LimitRequestBody未设置。
解决:

  • Nginx:在server块中加client_max_body_size 10M;
  • Apache:在.htaccess中加LimitRequestBody 10485760(单位字节)

5. 进阶技巧:把解密工具变成自动化审计流水线

5.1 批量解密脚本:绕过前端,直调PHP接口

当你要审计整站PHP文件时,手动粘贴效率太低。用curl直连decode.php(需开启allow_url_fopen):

#!/bin/bash # batch_decode.sh SOURCE_DIR="./malware_samples" OUTPUT_DIR="./decoded" mkdir -p "$OUTPUT_DIR" for php_file in "$SOURCE_DIR"/*.php; do if [ -f "$php_file" ]; then filename=$(basename "$php_file") echo "Processing $filename..." # 构造multipart/form-data请求(模拟前端上传) curl -s \ -F "file=@$php_file;filename=$filename" \ -F "type=auto" \ http://localhost/tool/decode.php \ -o "$OUTPUT_DIR/${filename%.php}_decoded.php" # 检查解密是否成功(非空且含<?php) if ! grep -q "<?php" "$OUTPUT_DIR/${filename%.php}_decoded.php"; then echo "Warning: $filename failed to decode" rm "$OUTPUT_DIR/${filename%.php}_decoded.php" fi fi done

参数说明:-F "type=auto"让后端自动识别加密类型;-o指定输出路径。此脚本比浏览器操作快10倍,且可集成进Git钩子——每次git push前自动扫描/wp-content/plugins/下的PHP文件。

5.2 自定义解密规则注入:为新型混淆器快速扩展支持

V2.0预留了/lib/custom_decoder.php作为扩展入口。假设你遇到某CMS专用混淆器,特征是$x=str_replace(['a','b'],['x','y'],$x);循环替换。只需在此文件中添加:

// /lib/custom_decoder.php function custom_cms_decode($code) { // 匹配 str_replace 循环模式 if (preg_match('/\$([a-zA-Z_]\w*)\s*=\s*str_replace\(\[([^\]]+)\],\s*(\[.*?\]),\s*\$\1\);/s', $code, $matches)) { $var = $matches[1]; $search = json_decode('[' . $matches[2] . ']', true); $replace = json_decode($matches[3], true); // 执行反向替换(需按长度降序,避免短字符串干扰长字符串) array_multisort(array_map('strlen', $replace), SORT_DESC, $search, $replace); return str_replace($replace, $search, $code); } return $code; } // 在 decode.php 的主解密函数末尾调用 $code = custom_cms_decode($code);

5.3 安全加固:禁用危险函数与审计日志留存

生产环境必须关闭eval()、system()等函数。在decode.php顶部插入:

// 紧急熔断:检测危险函数调用 $dangerous_funcs = ['eval', 'exec', 'system', 'shell_exec', 'passthru']; foreach ($dangerous_funcs as $func) { if (stripos($code, $func . '(') !== false) { die("SECURITY ALERT: Dangerous function '$func' detected. Decryption aborted."); } } // 记录解密行为(仅记录文件名与时间,不存源码) $log_entry = date('Y-m-d H:i:s') . " - " . $_FILES['file']['name'] . "\n"; file_put_contents('/var/log/php_decoder.log', $log_entry, FILE_APPEND | LOCK_EX);

后悔药:从那以后我每次部署新环境,都强制走一遍php -l decode.php语法检查 +grep -r "eval\|system" ./lib/危险函数扫描 +curl -I http://localhost/tool/decode.php状态码验证。这三步耗时不到30秒,却避免了90%的线上事故。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询