PHP 代码混淆处理思路

2020-08-26 15:18:29 浏览数 (1)

昨天在一个 PHP 的群里看到一个图片,图片如下:

看到这个图片,我觉得这应该是某个收费项目的源码,收费的项目为什么还要提供源码,这就是 PHP 的问题之一吧。

很多人也许想要修改这样的源码,但是无奈源码又是这样的让人看不懂。拿到这样的源码,我估计很多想要修改源码的一部分人就被卡住了。在这种情况下,我想说的是,作者既然这么做了,就是不希望被别人修改。如果真的觉得项目好的话,其实可以去付费的,毕竟软件是每个软件工程师的汗水。

虽然话是这么说,但是如果只是单纯的想要学习,也不产生什么利益的话,遇到这样的问题有什么办法呢?虽然我对 PHP 不怎么懂,但是我知道对于 PHP 这种源代码层面的处理想要还原问题不大(我自己的臆想,毕竟各种的处理方法可能很多,只是我不知罢了),关键在于还原一下值不值。当然了,PHP 的加密还存在引擎级别的,我觉得引擎级别还是有难度的。但是如果只是源码级别的处理,我觉得发挥想象其实每个人都是可以去尝试还原的,因为还原的可能性还是很大的。

这类代码我没怎么见过,针对上面那个图片,我没有拿到源文件,只有这个图片。针对这个图片,我给出一个处理的思路,和大家进行交流。

说说我的思路

说说如果是我处理的话,我处理的思路吧。

  • 首先将代码格式化,用很多工具都可以进行格式化,比如 PHPStorm;
  • 这样的代码格式化后显然是没有太大的用处的,格式化的目的在于要把整个源码规范一下,然后尝试把整个代码中的 goto 语句去掉;因为代码的执行是顺序的,也就是从文件的开头到结尾这么进行执行,如果能把 goto 去掉的话,你就得到了一份真正的执行顺序的代码,其实 goto 就是无条件的跳转,我们将离散的用 goto 连接的代码,变成线性的就可以了;
  • 除了满屏的 goto 语句以外,剩下的就是一些看不懂的字符了,可能有人会说是加密了之类的,其实是看的不够仔细。仔细观察的话,其实只是被编码了。我们可以从代码中有明显特征的位置开始还原,什么是明显特征?看下图。

图中是一个 date() 函数,这个函数我们每个人都会用,它的参数是字符串。PHP 中用来限定字符串的符号分两种,分别是 单引号 和 双引号,在平时为了代码的运行速度,我们写代码通常会使用单引号,而字符串当中有转义字符时,我们就要去使用双引号。而这里 date() 函数中的字符串其实就是转义字符。这些看起来被加密的东西其实就是一些 ASCII 码,说白了就是考验大家的基础。“”开头的是 8 进制,“x”开头的是 16 进制,"131" 是大写字母“Y”,“55”是字符“-”,剩下的还要我说吗?还不会?找一份 ASCII 码表对着看看不行么?

有了第三步的基础,还原剩下的部分难吗?

尝试

我去网上找了类似的一个文件,然后自己尝试用代码去还原它的结构,也就是我上面思路的第二步。毕竟文件有点大,还是写代码还原靠谱。代码写了不到 200 行,还原差不多 20 多行的代码。可以说是有进展的,为什么没有全部还原呢?其实是有原因的,因为在格式化以后,我用代码进行处理的时候,没有逐个的去处理各种可能(因为这部分花时间比较多),我只是处理了部分的情况。有些格式化后的代码,和我想要的预期也不太相同,比如多行连续标签,标签后面接 goto 之类的情况,我没有去一一处理,因为我为的不是还原源码,而是验证我的思路。给出关键代码的结构,完整的源码就不提供了(具体的处理我删掉了),我自己都没有写完,而且也不算复杂。

代码语言:javascript复制
<?php

class decode
{
    private $f;
    private $arr = [];
    private $lineNo = 0;

    public function openfile()
{
        $this->f = fopen('./test.php', 'r');
    }

    public function closefile()
{
        fclose($this->f);
    }

    public function de()
{
        $this->openfile();
        while (!feof($this->f)) {
            $line = fgets($this->f);
            $this->lineNo   ;
            $this->parse($line);
        }

        $this->closefile();
        $this->output();
        $this->outputcode();
    }

    public function delrn($str)
{
    }

    public function parse($line)
{
        // 处理 goto 的
        if (strpos($line, 'goto') !== false) {

        }

        // 处理标号后的代码
        if (strpos($line, ':') !== false) {
            

            // 标号后面是单行代码
            if (strpos($line, ';') !== false) {

            }

            if (strpos($line, ':') !== false) {

            }

            if (strpos($line, 'goto') !== false) {
            
            }

            // 标号后面是多行代码
            if (strpos($line, '{') !== false) {
                $code = $this->delrn($line);
                $i = 1; // 记录左括号{的数量
                while ($line = fgets($this->f)) {
                    $this->lineNo   ;
                    if (strpos($line, '{') !== false) {
                        
                    }

                    if (strpos($line, '}') !== false) {

                    }

                    $code = $code . ' ' . $this->delrn($line);
                }
            }

            // 标号后面是个function
            if (strpos($line, 'function') !== false) {
                $code = $line;
                $i = 0; // 记录左括号{的数量
                while ($line = fgets($this->f)) {
                    $this->lineNo   ;
                    if (strpos($line, '{') !== false) {

                    }

                    if (strpos($line, '}') !== false) {

                    }

                    $code = $code . ' ' . $line;
                }
            }

            return ;
        }
    }

    // 中间文件
    public function output()
{
        $f = fopen('./output.php', 'w');

        foreach ($this->arr as $k => $v) {
      
        }

        fclose($f);
    }

    // 最终文件
    public function outputcode()
{
        $f = fopen('./code.php', 'w');

        while (true) {
            
        }

        fclose($f);
    }

    public function next($key, $arr)
{
        $bret = false;

        foreach($arr as $k => $v) {
            if ($bret == true) {
                return $k;
            }
            if ($k === $key) {
                $bret = true;
            }
        }

        return false;
    }
}

$decode = new decode();
$decode->de();

总结

这种对代码的处理一般应该被称为“代码混淆”,而这种代码混淆的方式算是简单的。这种工具其实可以自己实现一个,按行读取每一行的 PHP 代码,然后给每行代码随机生成一个行号,然后用 goto 连接,最后进行乱序。然后可以把“字符串”处理成“转义字符”。当然了,其实还有很多可以处理的方法,只要把能想到的处理方法定义成规则,你的代码混淆工具处理后的 PHP 代码会比这个要复杂。

知道了混淆的思路,那么反混淆的话,其实也是这种思路,可以人肉进行处理,如果量大就不合适人肉了。量大就需要写工具去自动化完成了。

最后,我想再次和大家说,我们都是做软件开发的,请珍惜每一位软件工程师的汗水。盗取别人的成果,其实是在破坏这个行业,也是在违法。我们面对各种问题时,还是抱着学习和提高自身能力出发。

0 人点赞