吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2418|回复: 9
收起左侧

[Web逆向] 手把手带你AST还原某验三代(一)

[复制链接]
mysticz 发表于 2025-9-5 14:37
一、前言
前几天逛论坛看到一篇AST的还原某验三代。感觉写的不甚详细。作为小白的我看的云里雾里。因此也一篇文章,记录还原的过程。让小白也可以手把手还原混淆。

二、混淆文件解析。
三代有三个混淆js文件,分别是slide.7.9.3.js,fullpage.9.2.0-guwyxh.js,gct.js。发包的w值在slide.7.9.3.js中,三个文件大差不差,因此我们只看slide.7.9.3.js。

1.png
代码结构
可以看到,代码由四个函数和一个自执行大数组组成。

通过观察如下代码,可以看到其中lACSb是还原字符的代码。自执行函数就是业务逻辑代码。

[JavaScript] 纯文本查看 复制代码
var $_DAGJB = lACSb.$_Ce
                  , $_DAGIo = ['$_DAHCK'].concat($_DAGJB)
                  , $_DAHAM = $_DAGIo[1];
                $_DAGIo.shift();
                var $_DAHBL = $_DAGIo[0];
                (0,
                this[$_DAHAM(490)])($_DAGJB(824))[$_DAGJB(198)]();
            }
        }),
        $_BAV(ie[$_CJFi(232)], ue[$_CJEB(232)]),


其中$_DAGJB = lACSb.$_Ce类似的代码$_DAGJB(824)是一种字符赋值。

[JavaScript] 纯文本查看 复制代码
var $_DAGJB = lACSb.$_Ce
, $_DAGIo = ['$_DAHCK'].concat($_DAGJB)
, $_DAHAM = $_DAGIo[1];
$_DAGIo.shift();
var $_DAHBL = $_DAGIo[0];

如上是第二种赋值。先赋值$_DAGJB,在通过concat合成一个数组,在取$_DAGIo[1]下标为2的数组,其实就是取lACSb.$_Ce。然后shift移除第一个数组。而var $_DAHBL = $_DAGIo[0];是个假植,就是没有用到的东西。

因此字符还原,我们需要处理两种情况。

接下来是控制流,代码如下

[JavaScript] 纯文本查看 复制代码
function o() {
          var $_DBGEn = lACSb.$_DN()[6][16];
          for (; $_DBGEn !== lACSb.$_DN()[9][14];) {
            switch ($_DBGEn) {
              case lACSb.$_DN()[9][16]:
                var t = n(".wrap")["$_BFHo"]();
                $_DBGEn = lACSb.$_DN()[12][15];
                break;
              case lACSb.$_DN()[9][15]:
                r === t && 0 !== r || 5 < i ? e["$_CHCz"]() : (i += 1, r = t, v(o, 100));
                $_DBGEn = lACSb.$_DN()[9][14];
                break;
            }
          }
        }


我们可以看到var $_DBGEn = lACSb.$_DN()[6][16];其中lACSb.$_DN取了某些值,赋值给了$_DBGEn,然后for循环判断了取得另一些值,在通过switch中case的值进行判断。因此可以看出来他的控制流不是类似其他ob的那种数组一样。其实就是在循环中按顺序执行,在结果相等的情况下跳出。只需要我们把所有的值写道map中,然后循环判断,写入写出即可。

接下来我们开始AST还原

三、还原模板。
每次还原前我们直接在模板里面写,代码如下

[JavaScript] 纯文本查看 复制代码
const fs = require("fs");
const parser = require('@babel/parser')
const traverse = require('@babel/traverse').default;
const types = require('@babel/types');
const generator = require("@babel/generator").default;
let jscode = fs.readFileSync('slide.7.9.3.js', 'utf8');
let ast = parser.parse(jscode);
/////////
还原代码

////////
let {code} = generator(ast);  //{}解包的意思   等价于let code=generator(ast).code
fs.writeFile('还原.js', code, (err) => {
});

然后先写入两个必备的通用还原代码,

[JavaScript] 纯文本查看 复制代码
traverse(ast, {
    NumericLiteral({node}) {
        if (node.extra && /^0[obx]/i.test(node.extra.raw)) {
            node.extra = undefined;
        }
    },
    StringLiteral({node}) {
        if (node.extra && /\\[ux]/gi.test(node.extra.raw)) {
            node.extra = undefined;
        }
    },
});
traverse(ast, {
    NumericLiteral: function (path) {
        if (path.node.extra && path.node.value) { 
            const numstr = path.node.extra;
            const numstring = path.node.value;
            path.replaceWith(types.numericLiteral(numstring)); 
        } 
    },

}, opts = {});



这两个通用的可以还原如下编码,使代码清晰

2.png
编码
接下来还原字符混淆

四、混淆字符还原。
我们把代码复制到ast的解析站https://astexplorer.net/

3.png
ast解析
可以看到var $_DAGJB = lACSb.$_Ce的type是VariableDeclaration,因此,我们需要先过滤VariableDeclaration

[JavaScript] 纯文本查看 复制代码
traverse(ast,{
  VariableDeclarator(path) {
  })

我们需要先找到lACSb.$_Ce代码。可以看到是在init里面,类型是MemberExpression

4.png
lACSb.$_Ce
所以我们先判断type类型是MemberExpression的在放行

if(types.isMemberExpression(path.node.init)) {
}
然后我们在看一下init的代码,用generator.code

[JavaScript] 纯文本查看 复制代码
let namecode=generator(path.node.init).code;
console.log(namecode);

5.png
namecode
可以看到筛选出来了一堆,我们直接namecode==="lACSb.$_Ce"判断过滤

接下来我们需要找到赋值的地方,可以看到赋值时id的name参数

6.png
astname
我们直接取path.node.init.name,可以看到结果是正确的

7.png
name参数
接下来我们找这个参数的绑定,取他的path作用域

我们定义两个值,并循环取到的path,在取path的parent,顺便打印一下parentPath的值

[JavaScript] 纯文本查看 复制代码
let bd=path.scope.getBinding(path.node.id.name);
let repath=bd &&bd.referencePaths
for (let i = 0; i < repath.length; i++) {
    let rep=repath[i].parentPath
    console.log(rep.toString())
}

日志如下

[JavaScript] 纯文本查看 复制代码
$_DAGEI(75)
$_DAGEI(75)
$_DAGEI(986)
$_DAGEI(986)
$_DAGEI(1080)
['$_DAHCK'].concat($_DAGJB)
$_DAGJB(824)

可以看到取到了两种类型的值,这两种就是我们上面说到的,两种情况,我们分开处理。因此需要判断下,分成两种情况处理。

[JavaScript] 纯文本查看 复制代码
if(rep.toString().includes("concat")){}
else{}

我们先处理else的情况,当else的时候,那么就是$_DAGEI(1080),我们此时就只需要提取里面的参数传入lACSb.$_Ce然后替换即可。

[JavaScript] 纯文本查看 复制代码
argsm=lACSb.$_Ce.call(null,rep.node.arguments[0].value)
rep.replaceWith(types.valueToNode(argsm));

此时我们第一种就还原完成

8.png
第一种还原完成
接下来我们看第二种

我们先前判断了if(rep.toString().includes("concat")){},接下来我们就在这里面继续

先看结构,我们现在已经获取了['$_DAHCK'].concat($_DAGJB),那么接下来就是获取$_DAGIo的值

[JavaScript] 纯文本查看 复制代码
var $_DAGJB = lACSb.$_Ce
, $_DAGIo = ['$_DAHCK'].concat($_DAGJB)
, $_DAHAM = $_DAGIo[1];

直接跟上面一样,获取name,然后获取binding和父路径

[JavaScript] 纯文本查看 复制代码
let cat=rep.parent.id.name
let catbind=path.scope.getBinding(cat)
let catpath=catbind &&catbind.referencePaths
for (let i = 0; i < catpath.length; i++) {
let catrep= catpath[i].parentPath
}

这样就获取到了$_DAGIo[1],然后我们继续获取name,相同的操作

[JavaScript] 纯文本查看 复制代码
let bdcat=path.scope.getBinding(catid);
let pathcat=bdcat &&bdcat.referencePaths
for (let i = 0; i <pathcat.length; i++) {
    let repcat= pathcat[i].parentPath
}

到这一步就已经获取到了所有的参数,然后我们直接取值,替换即可

[JavaScript] 纯文本查看 复制代码
argsmcat=lACSb.$_Ce.call(null,repcat.node.arguments[0].value)
repcat.replaceWith(types.valueToNode(argsmcat));

9.png
替换结果
我们到还原代码看一下

10.png
还原完成
可以看到此时已经把字符还原的差不多了。

免费评分

参与人数 8吾爱币 +7 热心值 +7 收起 理由
John_Mac + 1 + 1 热心回复!
唐小样儿 + 1 + 1 我很赞同!
whereismy + 1 谢谢@Thanks!
aigc + 1 我很赞同!
Victor365 + 1 + 1 热心回复!
zhangxg + 1 谢谢@Thanks!
helian147 + 1 + 1 热心回复!
无名 + 2 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

zhangxg 发表于 2025-9-5 22:03
不明觉厉,让人想跟着大佬脚本来一遍
dapc 发表于 2025-9-7 01:18
究竟AST是什么??
aspartate aminotransferase?

点评

AST:Abstract Syntax Tree - 抽象语法树  详情 回复 发表于 2025-9-7 12:05
涛之雨 发表于 2025-9-7 12:05
dapc 发表于 2025-9-7 01:18
究竟AST是什么??
aspartate aminotransferase?

AST:Abstract Syntax Tree - 抽象语法树
llgxdnn 发表于 2025-9-7 22:34
需要一定技术功底啊,小白表示看不懂。
ljl9090 发表于 2025-9-8 10:10
这里   let bdcat=path.scope.getBinding(catid);  catid 是什么呢  ,前面部分看着还行,后面 “concat“部分 有一些不完整。
nftion 发表于 2025-9-8 11:34
感谢分享
dapc 发表于 2025-9-14 02:02
涛之雨 发表于 2025-9-7 12:05
AST:Abstract Syntax Tree - 抽象语法树

抽象语法树, 谢谢解释。
azjj120 发表于 2025-9-27 12:27
厉害,虽然看不懂。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-1-31 13:45

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表