PHP-Parser运用之goto解密经历PHP-Parser运用之goto解密经历

作者 : 郭然 本文共8269个字,预计阅读时间需要21分钟 发布时间: 2020-04-11 共753人阅读

一、初识PHP-Parser

19年底跟着一个旧同事搞一个商城类小程序的项目,开发过程中遇到了一些加密的PHP文件,询问后才知道这是微擎的2C加密,里面很多goto的跳转。每次同事都是发一个微信好友解密(说是以前加的专门做解密的)。百度了一下发现也很多网站在做在线解密的。这种都是按照文件大小来收费(kb),一个文件几块钱,但对于一个项目的文件费用也不少。同事说PHP-Parser 解析器可以做到解密。不管怎么加密都要能被解析器解析,都会转换为AST-抽象语法树。春节遇到疫情不能出去就闲着折腾下。

  • PHP Parser 是由 nikic 开发的一款 php 抽象语法树(AST)解析工具,github下载链接php-parse 地址https://github.com/nikic/PHP-Parser
    下载好后新建文件试运行官方例子,成功后能获得官方例子一样的抽象树。

    PHP-Parser运用之goto解密经历PHP-Parser运用之goto解密经历一、初识PHP-Parser二、探索解密方法二、浅谈AST语法三、总结
    image.png
  • 主要方法:
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);(创建解析器)
$ast = $parser->parse($code);(传入代码并返回抽象语法树)
$prettyPrinter->prettyPrintFile($ast);(把抽象语法树转换为php代码)

二、探索解密方法

1.百度找到一篇怎样手动解密微擎框架的goto语句?的百度经验。文中很清楚的说明了解密原理。加密文件都是大量的label和goto语句,label里面就是代码内容,只是label顺序是打乱的。需要通过goto语句跳转串联起来。最后把label都去掉就是源代码了。
原理简单,对于代码少的是可行的,但对于代码量几千行的文件这手动的工作量是够呛的。
2.理解了上面原理后,想着能不能用php写个方法去解决?然后尝试用PHP Parser传入加密的代码获取AST抽象语法树,获取到如下图的AST。对比php加密文件发现$ast数组中的每个元素对应内容中的每一行。Label与Goto_中间就是我们要的代码。按照goto_的跳转把顺序排出来,然后把Laber和Goto_删掉就是源代码了。排序后的数组通过prettyPrintFile()方法转为php代码。

PHP-Parser运用之goto解密经历PHP-Parser运用之goto解密经历一、初识PHP-Parser二、探索解密方法二、浅谈AST语法三、总结
AST抽象树.png

3.特别要说明一下的是color{red}{if、for、foreach、switch、class里面代码块是在stmts中也需要独立排序}
另外php的goto语句是有限制的:只能在同一个文件和作用域中跳转,无法跳出一个函数或者类方法,也无法跳入另一个函数,更无法跳入任何循环或者switiche结构中。
写了个递归传入第一个语句Label把代码按goto顺序串联起来。试了两个加密文件成功的代码解密出来了,有点点小激动,刚试第三个文件就翻车了,报内存溢出而且解密时间很久。然后把php配置中php.ini(memory_limit )改到1024m了。再试了其他,发现文件稍微大点的(40kb)文件就会超时或者内存溢出。

4.各个节点输出内存定位到调用prettyPrintFile()的地方,一开始以为是自己组装ast数组的算法有问题,对比解密出来的文件逻辑上没发现有什么,就真以为内存不够用问题,然后就在错误的路上越走越远,猜想是不是ast数组太大一次转化不了,然后就搞成了一句一句代码去调用方法转换php代码再输入到文件中。最后成功的输出解密后的代码,但解密后的文件大小既然有10M。难怪会内存溢出,再想想不科学啊。谁会写代码一个文件写这么多。所以问题应该还是出在组装的ast数组。

5.再仔细的看了小文件解密出来的代码发现很多重复的代码,主要在发生在if语句里面的跟下面的内容。如下图

image_cf.png

图中switch里面的内容是一样的。但是对比加密文件中的逻辑又是对的,但怎么看代码都是写法有问题。然后就百度想找点相关资料,然后看到一位大佬(破解微擎2C(goto混淆)解密之旅)说goto混淆加密在if代码块有做混淆,需要反向解密。

二、浅谈AST语法

1.需要做反向解密就需要知道ast中if元素中的语法内容,然后去修改条件condition。这就必须要去弄懂ast的语法,之前基本没接触过,关于写ast的资料也很少,应该是属于比较底层的技术。看了很多也还一知半解。有兴趣的可以去了解下AST(抽象语法树)超详细,有助于写出好的框架,或者处理代码等。
2.查看ast知道if类中cond字段主要保存的是条件,依然不是很懂if里面的语法,就想到把相关加密内容抽出来,然后修改条件对比ast抽象树的内容。
if ($request > 1) {}

大于1的条件.png

if ($request ) {}

有值不为空的条件.png

if (!$request ) {}

取非的条件.png

 

针对纯if块,去修改cond达到条件反向,然后把if中里面的goto和外面的goto调换。如下的B1K3t和o3vsQ调换。

if (!$condition) {
    goto B1K3t;
}
goto o3vsQ;

测试的加密代码

goto PQNsu;
d3ERK:
$do = $_GPC["144157"];
goto EPIqU;
o3vsQ:
$xtitlea = urldecode($_GPC["x78x74x69x74154x65x61"]);
goto tD6yl;
EoytB: global $_GPC, $_W;
goto ogF_j;
vBbWf:
$op = strlen($_GPC["x6f160"]) > 1 ? $_GPC["157x70"] : "154x69163164";
goto Md80W;
EPIqU:
if (!(strlen($_GPC["x78164x69x74x6c145141"]) > 0)) {
    goto B1K3t;
}
goto o3vsQ;
y3LjA:
switch ($op) {
    case "163150141162x65":
        goto URziN;
        ka34Z:
        goto GM3zF;
        goto dJE3d;
        Suxnm:
        goto t5lwU;
        goto vJNaD;
        tL1wK:
        if (!empty($_GPC["156x65167"]) && $_GPC["156x65167"] == 1) {
            goto qWt4U;
        }
        goto VpT2e;
        VpT2e:
        include $this->template("157154x64x2f122145x63x6f162144x2f" . $op);
        goto Suxnm;
        vJNaD: qWt4U:
        goto A2IVc;
        URziN:
        $times = array("x73164141x72x74" => date("13155x6d55x64") . "40x3060x3a60x3072x30x30", "145x6e144" => date("x59x2dx6d55x64") . "40x3263x3a65x39x3a65x39");
        goto tL1wK;
        A2IVc:
        include $this->template("155171155x61156x6114714557" . strtolower($_GPC["144x6f"]) . "57" . $op);
        goto OUmi_;
        OUmi_: t5lwU:
        goto ka34Z;
        dJE3d:
    case "x67145164x73145x61x63150x6a163x6fx6e":
        goto MEVAi;
        H7vbb:
        $params = array();
        goto VPN1q;
        GW6qL: Nq9hx:
        goto BI3pO;
        ZpEwm:
        $params["x3ax73x74141x72x74x5fx74x69155x65"] = strtotime($times["x73164x61x72x74"]);
        goto IJ5Yo;
        jjraf: KizGW:
        goto IDenM;
        VFMDk:
        $where = "x20127110x4512210540165x6e151141x63x69x6475x3ax75156x69x61143x69x6440";
        goto H7vbb;
        Ic4HW:
        $params["x3a157160145x6e151144"] = "x25" . $_GPC["157160x65156x69x64"] . "45";
        goto uCX6b;
        j0Xsg:
        $listmodel = pdo_fetchall($sql, $params);
        goto k23WD;
        KIS67:
        $where .= "40101116x4440165x6ex69x78x5f164x69x6dx65x73164141155x7050143x72x65141x74145x74x6915514551x3ex3d72163x74141x72x7413716415115514540x41116104x20165156x69x78137x74151155x65x73164x61x6d16050x63162x65141164x65164x69x6dx65x29x3c75x3ax65156x64137164x6915514540";
        goto ZpEwm;
        zE3CJ:
        $sql = "x53x45x4cx45103x54x20x43117x5511612450x2a51x20x46122117115x20x2040" . $fulltable . $where;
        goto F3hpp;
        JoJ5V:
        $fulltable = tablename($tablename);
        goto zE3CJ;
        VOG6F:
        $times = $_GPC["x74151155145x73"];
        goto KIS67;
        W0s2c:
        $where .= "x20x41116104x20157160x65x6ex69144x20x4cx49113105x2072x6f160145x6ex69x6440";
        goto Ic4HW;
        MDXuE:
        $sql = "123105x4c105103x54x20x2a40106122117x4d40x20{$fulltable}40x2040{$where}40x4fx52x44x45x5240x42x59x20" . $ararysort["157x72144145162"] . "40x4cx49115x49x54x20" . $ararysort["157146x66x73145x74"] . "x2c" . $ararysort["x6c151155151x74"];
        goto j0Xsg;
        MEVAi:
        $ararysort = ararysorts();
        goto VFMDk;
        VPN1q:
        if (empty($_GPC["x6f160x65x6ex69144"])) {
            goto Cu8TN;
        }
        goto W0s2c;
        HZsFd:
        $jsondate["162157x77163"] = array();
        goto ZmKAL;
        IJ5Yo:
        $params["x3a145156x64137x74x69155x65"] = strtotime($times["145x6e144"]);
        goto GW6qL;
        ZmKAL:
        xc_ajax($jsondate);
        goto jjraf;
        xZbm2: Mi4bU:
        goto HZsFd;
        leRgM:
        $jsondate = array();
        goto z12xr;
        pg3e7:
        goto KizGW;
        goto xZbm2;
        ehhW4:
        if (empty($jsondate["164157164x61x6c"])) {
            goto Mi4bU;
        }
        goto MDXuE;
        BI3pO:
        $params["165x6ex69141143151144"] = $_W["165x6e151141143151x64"];
        goto JoJ5V;
        O4Ft1:
        if (empty($_GPC["164151155x65163"])) {
            goto Nq9hx;
        }
        goto VOG6F;
        IDenM:
        goto GM3zF;
        goto O_71I;
        F3hpp:
        $total = pdo_fetchcolumn($sql, $params);
        goto leRgM;
        z12xr:
        $jsondate["x74157164141154"] = pdo_fetchcolumn($sql, $params);
        goto ehhW4;
        lQ1Aw:
        xc_ajax($jsondate);
        goto pg3e7;
        k23WD:
        $jsondate["x72x6fx77x73"] = $listmodel;
        goto lQ1Aw;
        uCX6b: Cu8TN:
        goto O4Ft1;
        O_71I:
    case "x73x74141164x75163137x63150x61156147145":
        goto WyrOc;
        WyrOc:
        $request = pdo_update($tablename, array("x73x74141164x75163" => $_GPC["163x74141x74x75163"], "141x70x70x6cx79164151x6d145" => date("x595515555x6440110x3ax69x3ax73")), array("x75156151x61x63x69x64" => $_W["165156x69141143x69144"], "151x64" => $_GPC["x69x64"]));
        goto B6H7M;
        tt_Il:
        goto POSQh;
        goto fpjiG;
        JTPLL:
        xc_message(-1, null);
        goto tt_Il;
        qm8pg:
        goto GM3zF;
        goto mH417;
        zAERF:
        xc_message(1, null);
        goto WQO1O;
        fpjiG: FutW7:
        goto zAERF;
        B6H7M:
        if ($request) {
            goto FutW7;
        }
        goto JTPLL;
        WQO1O: POSQh:
        goto qm8pg;
        mH417:
}
goto ih3iG;
PQNsu:
defined("111116137111x41") or exit("x41x63143x65163x73x20104145156x69x65x64");
goto EoytB;
cw07p: B1K3t:
goto y3LjA;
ogF_j:
$uniacid = $_W["x75156x69x61x63x69x64"];
goto vBbWf;
tD6yl:
$xtitleb = urldecode($_GPC["170x74151x74154145142"]);
goto cw07p;
ih3iG: L8RpU:
goto Oyht_;
Md80W:
$tablename = "x78143x5f142x65x61165x74171x5f141160160x6c171";
goto d3ERK;
Oyht_: GM3zF:

解密后的代码

defined("IN_IA") or exit("Access Denied");
global $_GPC, $_W;
$uniacid = $_W["uniacid"];
$op = strlen($_GPC["op"]) > 1 ? $_GPC["op"] : "list";
$tablename = "xc_beauty_apply";
$do = $_GPC["do"];
if (strlen($_GPC["xtitlea"]) > 0) {
    $xtitlea = urldecode($_GPC["xtitlea"]);
    $xtitleb = urldecode($_GPC["xtitleb"]);
}
switch ($op) {
    case "share":
        $times = array("start" => date("Y-m-d") . " 00:00:00", "end" => date("Y-m-d") . " 23:59:59");
        if (!empty($_GPC["new"]) && $_GPC["new"] == 1) {
            include $this->template("mymanage/" . strtolower($_GPC["do"]) . "/" . $op);
        }
        include $this->template("old/Record/" . $op);
        break;
    case "getseachjson":
        $ararysort = ararysorts();
        $where = " WHERE uniacid=:uniacid ";
        $params = array();
        if (!empty($_GPC["openid"])) {
            $where .= " AND openid LIKE :openid ";
            $params[":openid"] = "%" . $_GPC["openid"] . "%";
        }
        if (empty($_GPC["times"])) {
            $params["uniacid"] = $_W["uniacid"];
            $fulltable = tablename($tablename);
            $sql = "SELECT COUNT(*) FROM   " . $fulltable . $where;
            $total = pdo_fetchcolumn($sql, $params);
            $jsondate = array();
            $jsondate["total"] = pdo_fetchcolumn($sql, $params);
            if (empty($jsondate["total"])) {
                $jsondate["rows"] = array();
                xc_ajax($jsondate);
            }
            $sql = "SELECT * FROM  {$fulltable}   {$where} ORDER BY " . $ararysort["order"] . " LIMIT " . $ararysort["offset"] . "," . $ararysort["limit"];
            $listmodel = pdo_fetchall($sql, $params);
            $jsondate["rows"] = $listmodel;
            xc_ajax($jsondate);
        }
        $times = $_GPC["times"];
        $where .= " AND unix_timestamp(createtime)>=:start_time AND unix_timestamp(createtime)<=:end_time ";
        $params[":start_time"] = strtotime($times["start"]);
        $params[":end_time"] = strtotime($times["end"]);
        $params["uniacid"] = $_W["uniacid"];
        $fulltable = tablename($tablename);
        $sql = "SELECT COUNT(*) FROM   " . $fulltable . $where;
        $total = pdo_fetchcolumn($sql, $params);
        $jsondate = array();
        $jsondate["total"] = pdo_fetchcolumn($sql, $params);
        if (empty($jsondate["total"])) {
            $jsondate["rows"] = array();
            xc_ajax($jsondate);
        }
        $sql = "SELECT * FROM  {$fulltable}   {$where} ORDER BY " . $ararysort["order"] . " LIMIT " . $ararysort["offset"] . "," . $ararysort["limit"];
        $listmodel = pdo_fetchall($sql, $params);
        $jsondate["rows"] = $listmodel;
        xc_ajax($jsondate);
        break;
    case "status_change":
        $request = pdo_update($tablename, array("status" => $_GPC["status"], "applytime" => date("Y-m-d H:i:s")), array("uniacid" => $_W["uniacid"], "id" => $_GPC["id"]));
        if ($request) {
            xc_message(1, null);
        }
        xc_message(-1, null);
        break;
}

三、总结

至此算法已经完美的代码解密出来。
如果需要完整工程项目可以去这里下载(需付费):
https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111debQpZDda&ft=t&id=613418130993
安装好PHP7.0或者以上版本就可以跑了。

赞赏

微信赞赏支付宝赞赏

VIP部落提供编程技术、教育培训、优惠购物以及各类软件和网站源码、模板等资源下载。
VIP部落 » PHP-Parser运用之goto解密经历PHP-Parser运用之goto解密经历

常见问题FAQ

提供最优质的资源集合

立即查看 了解详情