我们已经准备好了,你呢?

2024我们与您携手共赢,为您的企业形象保驾护航!

原帖地址:

出于对破解文件作者的尊重,文末附件中数据部分已经删除,只保留算法部分,需要使用完整文件请到原帖下载。

实验样本

据说“加密效果是业内最高的”?

随意上传一个php文件,然后下载加密后的文件,也就是我们要解密的文件。

简要分析

我们先来看一下加密文件。

可以看出这是一个正常的PHP文件,但是所有的变量名都是乱码,得益于PHP引擎支持任意字符集的变量名,加密文件变量名的字节数全部在ASCII范围之外,均为0x80以上的字符。

我们可以看到中间有一个PHP代码段结束标记,前面有一个$xxx;,用来结束脚本运行。这意味着结束标记后面的数据不会被正常输出。它后面的数据很有可能是源文件的加密数据,前面的PHP代码只是用来解密的。

调试前的准备

这里使用的IDE是(一开始我用的是这个,后来发现这个更好)。

首先,安装 PHP Debug 插件。

php注释语句_php注释一段代码_php注释符号

然后,按照说明安装插件。

注意:运行未知的PHP代码还是很危险的,最好在虚拟机上运行,​​在真机上确保你的PHP Debug插件可以正常设置断点。断开网络,最好同时打开任务管理器,一旦出现未知现象(比如CPU占用或者磁盘占用),或者没有设置调试断点,或者出现一些问题,立即结束PHP进程。

开始调试代码格式

这段代码太乱了,需要格式化一下。

一开始我用的是自带的代码格式化,格式化之后数据就变了,而且对未知字符集的支持还比较差。

然后我就想分析一下PHP文件的AST(抽象语法树),看看能不能把变量名改成可显示的字符。但是后来想想不行,因为这种代码肯定有eval,改了变量名之后,eval字符串里的变量名就对应不上了。

我找到了这个工具:

首先是 nikic/php-。

然后将下面的代码保存成文件(如.php),读取下载的1.php,将格式化后的代码写入2.php中。

use PhpParser\Error;use PhpParser\ParserFactory;use PhpParser\PrettyPrinter;require 'vendor/autoload.php';$code = file_get_contents('1.php');$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);
file_put_contents('2.php', $prettyCode);

然后,执行php.php。

使用此方法格式化的PHP文件内容没有损坏,因此我们可以继续分析它。

如果这不起作用,您只能使用十六进制编辑器查找 ; 和 } 并手动替换它们,并添加 \r\n。

调试

我们得先把前两行注释掉,不然一旦出现错误就莫名其妙了。

error_reporting(0);
ini_set("display_errors", 0);

保存。然后就完了,代码又乱了。

我们需要一个支持不可显示字符的编辑器,或者......将显示编码更改为非多字节字符集,如(ISO 8859-1)

现在,让我们开始调试。

在第一行设置断点。执行php2.php运行程序。然后一步一步调试,执行过程中注意观察变量的值,分析函数的执行流程。

利用调试功能,我们可以方便的查看变量的具体内容。

php注释符号_php注释一段代码_php注释语句

踏入这一行,好像有些不对劲。

php注释一段代码_php注释语句_php注释符号

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

我们是用命令行来运行的,所以执行完这句话之后,程序肯定会结束。

让我们让它完成,注释掉这一行,在它下面设置一个断点,然后重新运行该程序。

下面一行是读取当前文件,这句话没有错。

$f = file_get_contents(constant('rnfzwpch'));

接下来就到了验证操作环境的时候了。

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

将其注释掉,保存并重新运行。

当然你也可以使用调试控制台执行$[''] = '127.0.0.1';等指令来通过验证。

php注释一段代码_php注释符号_php注释语句

看下面的代码,我想到了exe的反调试,不得不佩服想到这个办法的人,防止断点调试,如果断点调试的话,这里会超过100毫秒。

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

我们可以在这条语句后面直接设置断点,让它们连续执行,这样就不会超过100毫秒了。当然,直接注释掉是最残暴的办法。

我们需要通过“单步进入”来研究下面的eval,不过结果对我们影响不大,当然将其注释掉也是可以的。

php注释符号_php注释语句_php注释一段代码

接下来是验证数据完整性。

!strpos(decode_func(substr($f, -45, -1)), md5(substr($f, 0, -46))) ? $undefined1() : $undefined2;

这里的$和$都没有定义,如果验证失败,会调用$,程序Error退出,如果验证成功,虽然$变量不存在,但是就一个,问题不大。

它是文件中的最后一个函数,负责字符串解码。

这个验证方式是解密文件尾部然后和前面文件体的md5对比,这个执行肯定会失败。

退出程序,注释掉,然后再次运行。

$decrypted = str_rot13(@gzuncompress(decode_func(substr($f, -2358, -46))));

我们找到了这次解码的关键句子,可以看到解密的代码已经出来了。

php注释一段代码_php注释符号_php注释语句

代码完成后,终于到了执行脚本的时候了。

$f_varname = '_f_';$decrypted = check_and_decrypt(${$f_varname});
set_include_path(dirname(${$f_varname}));$base64_encoded_decrypted = base64_encode($decrypted);$eval_string = 'eval(base64_decode($base64_encoded_decrypted));';$result = eval($eval_string);
set_include_path(dirname(${$f_varname}));return $result;

费尽周折,还是那个eval语句。

如何输出内容呢?在$后面加一行就可以了。

结果

通用解密程序

我们可以继续分析他的解密算法

算法是固定的,但是它内置了一个密钥,我们只需要通过字符串函数提取密钥就可以了。

最终的解码过程如下。

function decrypt($data, $key){
    $data_1 = '';
    for ($i = 0; $i < strlen($data); $i++) {
        $ch = ord($data[$i]);
        if ($ch < 245) {
            if ($ch > 136) {
                $data_1 .= chr($ch / 2);
            } else {
                $data_1 .= $data[$i];
            }
        }
    }
    $data_1 = base64_decode($data_1);
    $key = md5($key);
    $j = $ctrmax = 32;
    $data_2 = '';
    for ($i = 0; $i < strlen($data_1); $i++) {
        if ($j <= 0) {
            $j = $ctrmax;
        }
        $j--;
        $data_2 .=  $data_1[$i] ^ $key[$j];
    }
    return $data_2;
}function find_data($code){
    $code_end = strrpos($code, '?>');
    if (!$code_end) {
        return "";
    }
    $data_start = $code_end + 2;
    $data = substr($code, $data_start, -46);
    return $data;
}function find_key($code){
    // $v1 = $v2('bWQ1');    // $key1 = $v1('??????');    $pos1 = strpos($code, "('" . preg_quote(base64_encode('md5')) . "');");
    $pos2 = strrpos(substr($code, 0, $pos1), '$');
    $pos3 = strrpos(substr($code, 0, $pos2), '$');
    $var_name = substr($code, $pos3, $pos2 - $pos3 - 1);
    $pos4 = strpos($code, $var_name, $pos1);
    $pos5 = strpos($code, "('", $pos4);
    $pos6 = strpos($code, "')", $pos4);
    $key = substr($code, $pos5 + 2, $pos6 - $pos5 - 2);
    return $key;
}$input_file = $argv[1];$output_file = $argv[1] . '.decrypted.php';$code = file_get_contents($input_file);$data = find_data($code);if (!$code) {
    echo '未找到加密数据', PHP_EOL;
    exit;
}$key = find_key($code);if (!$key) {
    echo '未找到秘钥', PHP_EOL;
    exit;
}$decrypted = str_rot13(gzuncompress(decrypt($data, $key)));
file_put_contents($output_file, $decrypted);echo '解密后文件已写入到 ', $output_file, PHP_EOL;

本程序可以免费解密本网站上的所有加密代码。

用法:php .php 1.php

摘要 附录 代码欣赏

// 先把这两行去掉,防止出现什么问题,我们还什么都不知道。// error_reporting(0);// ini_set("display_errors", 0);if (!defined('msvigqgq')) {
    define('msvigqgq', __FILE__);
    if (function_exists('func2') == false) {
        // 第一个函数返回 'base64_decode' ,这个函数不依赖其他任何函数,单纯地返回一个字符串 'base64_decode'。        function func1()        {
            $v1 = '6f6e66723634';
            $v2 = 'pa';
            $v3 = '7374725f';
            $v4 = 'H' . '*';
            $v2 .= 'ck'; // $v2 = 'pack';            $v1 .= '5f717270627172'; // $v1 = '6f6e667236345f717270627172';            $v3 .= '726f743133'; // $v3 = '7374725f726f743133';            // $v5 = $v2($v4, $v3);            $v5 = pack('H*', '7374725f726f743133');
            // $v5 = 'str_rot13';            // $v6 = $v5($v2($v4, $v1));            $v6 = str_rot13(pack('H*', '6f6e667236345f717270627172'));
            // $v6 = 'base64_decode';            return $v6;
        }
        // 第二个函数接受两个参数,要注意第一个参数还是一个引用参数。        function func2(&$arg1, $arg2)        {
            // 第一句是令一堆变量等于 func4            // $v1 - $v5 都使用 func4 解码一个字符串,结果如下            $v1 = 'str_rot13';
            $v2 = 'strrev';
            $v3 = 'gzuncompress';
            $v4 = 'stripslashes';
            $v5 = 'explode';
            // $v6 = $v1($v2($v3($v4(func4('??????')))));            // $v6 = str_rot13(strrev(gzuncompress(stripslashes(func4('??????')))));            $v6 = ',chr,addslashes,rand,gzuncompress,assert_options,assert,file_get_contents,substr,unpack,constant,strpos,create_function,str_rot13,md5,set_include_path,dirname,preg_replace,base64_encode,base64_decode,';
            // $v7 = $v5($v6);            // $v7 = explode($v6);            $v7 = array(
                0 => "",
                1 => "chr",
                2 => "addslashes",
                3 => "rand",
                4 => "gzuncompress",
                5 => "assert_options",
                6 => "assert",
                7 => "file_get_contents",
                8 => "substr",
                9 => "unpack",
                10 => "constant",
                11 => "strpos",
                12 => "create_function",
                13 => "str_rot13",
                14 => "md5",
                15 => "set_include_path",
                16 => "dirname",
                17 => "preg_replace",
                18 => "base64_encode",
                19 => "base64_decode",
                20 => "",
            );
            $arg1 = $v7[$arg2];
            // 看到这里知道了,这个函数就是用来需要用的提取函数名的        }
        // 第三个函数被主程序调用了        // 不过分析之后发现这个 $arg1 参数并没有用到        // 这个函数的前半部分是防止调试        // 后半部分是提取后面加密的代码        function func3($arg1)        {
            global $_v1, // $_v1 = 'file_get_contents';            $_v3, // $_v3 = 'substr';            $_v4, // $_v4 = 'assert';            $_v5, // $_v5 = 'assert_options';            $_v6, // $_v6 = 'unpack';            $_v7, // $_v7 = 'constant';            $_v8, // $_v8 = 'preg_replace';            $_v9, // $_v9 = 'base64_encode';            $_v10, // $_v10 = 'gzuncompress';            $_v11, // $_v11 = 'create_function';            $_v12, // $_v12 = 'strpos';            $_v13, // $_v13 = 'addslashes';            $_v14, // $_v14 = 'str_rot13';            $_v15, // $_v15 = 'md5';            $_v16, // $_v16 = 'set_include_path';            $_v17; // $_v17 = 'dirname';            // 这里有一堆变量等于 func4,然后用他们解码得到 $v1 - $v5            $v1 = 'php_sapi_name';
            $v2 = 'die';
            $v3 = 'cli';
            $v4 = 'microtime';
            $v5 = '1000';
            // $v1() == $v3 ? $v2() : '';            // 这句话在调试的时候需要注释掉            php_sapi_name() == 'cli' ? die() : '';
            // file_get_contents(constant(func4('??????')));            $v7 = file_get_contents(__FILE__);
            // $v8 = $v4(true) * $v5;            $v8 = microtime(true) * 1000;
            eval("");
            // if ($v4(true) * $v5 - $v8 > 100) {            // 这里是防止下断点调试的,下断点调试,这里就超过 100 毫秒了,直接注释掉            if (microtime(true) * 1000 - $v8 > 100) {
                // $v2();                die();
            }
            // eval(func4('??????'));            eval('if(strpos(__FILE__, msvigqgq) !== 0){$exitfunc();}');
            // $_v12(func4($_v3($v7, func4('??????'), func4('??????'))), $_v15($_v3($v7, func4('??????'), func4('??????')))) ? $v9() : $v10;            // 这里的 $v9 和 $v10 都没有定义,如果验证失败,就会调用 $v9 会直接出错退出程序            // 而如果验证成功 $v10 变量不存在则没问题            // 验证方法就是把文件尾部分解密和前面的文件主体部分的md5对比,直接注释掉            !strpos(func4(substr($v7, -45, -1)), md5(substr($v7, 0, -46))) ? $v9() : $v10;
            // 这两个数值是通过 func4 解码得到的            $v11 = '-2586';
            $v12 = '-46';
            // $v12 = $_v14(@$_v10(func4(substr($v7, $v11, $v12))));            $v12 = str_rot13(@gzuncompress(func4(substr($v7, $v11, $v12))));
            return $v12;
        }
        // 第四个函数有点复杂,这是一个解码函数,用的是异或算法解密,所有调用 func4 的位置都没有提供 $arg2        function func4($arg1, $arg2 = '')        {
            $v1 = 'base64_decode';
            // $v2 - $v4 通过 base64_decode 解码得到            $v2 = 'ord';
            $v3 = 'strlen';
            $v4 = 'chr';
            // $arg2 = !$arg2 ? $v2('?') : $arg2;            // $arg2 = !$arg2 ? 136 : $arg2;            $arg2 = 136;
            // 这里 $v5 不存在,所以 $v6 = null;            $v6 = $v5;
            // for (; $v6 < $v3($arg1); $v6++) {            for (; $v6 < strlen($arg1); $v6++) {
                // $v7 .= $v2($arg1[$v6]) < $v2('?') ? $v2($arg1[$v6]) > $arg2 && $v2($arg1[$v6]) < 245 ? $v4($v2($arg1[$v6]) / 2) : $arg1[$v6] : '';                $v7 .= ord($arg1[$v6]) < 245 ? ord($arg1[$v6]) > $arg2 && ord($arg1[$v6]) < 245 ? chr(ord($arg1[$v6]) / 2) : $arg1[$v6] : '';
            }
            // $v8 = $v1($v7);            $v8 = base64_decode($v7);
            $v9 = 'md5'; // $v9 通过 base64_decode 解码得到            $v6 = $v5;
            // $arg2 = $v9('8_Q.L2');            // $arg2 = md5('8_Q.L2');            $arg2 = 'fac02565267d815643cecee75a16c7bd';
            // $v10 = $ctrmax = $v3($arg2);            // $v10 = $ctrmax = strlen($arg2);            $v10 = $ctrmax = 32;
            // for (; $v6 < $v3($v8); $v6++) {            for (; $v6 < strlen($v8); $v6++) {
                $v10 = $v10 ? $v10 : $ctrmax;
                $v10--;
                $v11 .= $v8[$v6] ^ $arg2[$v10];
            }
            return $v11;
        }
    }
}global $_v1, // $_v1 = 'file_get_contents';$_v2, // $_v2 = 'chr';$_v3, // $_v3 = 'substr';$_v4, // $_v4 = 'assert';$_v5, // $_v5 = 'assert_options';$_v6, // $_v6 = 'unpack';$_v7, // $_v7 = 'constant';$_v8, // $_v8 = 'preg_replace';$_v9, // $_v9 = 'base64_encode';$_v10, // $_v10 = 'gzuncompress';$_v11, // $_v11 = 'create_function';$_v12, // $_v12 = 'strpos';$_v13, // $_v13 = 'addslashes';$_v14, // $_v14 = 'str_rot13';$_v15, // $_v15 = 'md5';$_v16, // $_v16 = 'set_include_path';$_v17; // $_v17 = 'dirname';// 然后一堆变量等于 func2if (!$_v1) {
    // 使用 func2 用传递引用变量的方法赋值,简化之后如下    $_v1 = 'file_get_contents';
    $_v3 = 'substr';
    $_v6 = 'unpack';
    $_v10 = 'gzuncompress';
    $_v11 = 'create_function';
    $_v12 = 'strpos';
    $_v13 = 'addslashes';
    $_v14 = 'str_rot13';
    $_v15 = 'md5';
    $_v16 = 'set_include_path';
    $_v17 = 'dirname';
    $_v8 = 'preg_replace';
    $_v9 = 'base64_encode';
    $_v7 = 'constant';
    $_v5 = 'assert_options';
    $_v4 = 'assert';
    $_v2 = 'chr';
    $v1 = 'rand';
}// 一堆变量等于 func4,然后用 func4 解码$v2 = '_f_';$v3 = func3(${$v2});// $_v16($_v17(${$v2}));set_include_path(dirname(${$v2}));// $v4 = $_v9($v3);$v4 = base64_encode($v3);// $v5 = func4('??????');// 解密之后的原文不是 $v4,这里是翻译之后的$v5 = 'eval(base64_decode($v4));';// $v5 = $_v8(func4('??????'), $v5, func4('??????'));// mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )// PCRE 修饰符 e (PREG_REPLACE_EVAL)// Warning: This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0.// If this deprecated modifier is set, preg_replace() does normal substitution of backreferences in the replacement string, evaluates it as PHP code, and uses the result for replacing the search string. Single quotes, double quotes, backslashes (\) and NULL chars will be escaped by backslashes in substituted backreferences.// 换句话说 preg_replace 如果带 e 的话,第一步,正常地进行正则表达式替换(反向引用也会被正常替换,就是完全正常的正则替换),第二步,把结果 eval 作为最终结果// 简而言之 $v5 = eval($v5);$v5 = preg_replace('/0dcaf9/e', $v5, '0dcaf9');// 把上述几步统一一下 $v5 = eval(func3($_f_));// $_v16($_v17(${$v2}));set_include_path(dirname(${$v2}));// 把解码之后的文件运行结果返回return $v5;

附件下载请参阅原论坛帖子。

--官方论坛

--推荐给朋友

二维码
扫一扫在手机端查看

本文链接:https://by928.com/6662.html     转载请注明出处和本文链接!请遵守 《网站协议》
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。

项目经理在线

我们已经准备好了,你呢?

2020我们与您携手共赢,为您的企业形象保驾护航!

在线客服
联系方式

热线电话

13761152229

上班时间

周一到周五

公司电话

二维码
微信
线