冰凌汇编

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 96|回复: 0
收起左侧

js 通过ast抽象语法树对代码进行分析

[复制链接]
11544wwwq 发表于 2022-3-27 22:57:15
最近做完一个项目,项目是Windows桌面客户游戏产品,基于NW架构开发,核心代码是基于js语言,cocos2d框架开发的h5游戏,分析js加密的过程当中使用到了js AST抽象语法树,对魂效果的js代码进行反混淆,增加代码可读性,更同意解密js相关的业务逻辑。


项目需求不复杂,就是一个打地鼠的游戏,每个房间两个人,游戏开始后,谁比对手多打10个地鼠就算赢。游戏本身很多通过按键xx等jiaob软件录制软件做的脚本在游戏**,客户觉得慢了,想以xieyi或者请求的方式定制软件。


游戏长这样
js 通过ast抽象语法树对代码进行分析 - 11544wwwq_冰凌汇编

开始以为是unity  后来研究软件安装包 其实就是nw,和electron一样,使用的是js前端的技术栈

js 通过ast抽象语法树对代码进行分析 - 11544wwwq_冰凌汇编
确定是js,通过fd抓包找到登录链接,就能看到项目源码,抓包后在Chrome中打开如下

js 通过ast抽象语法树对代码进行分析 - 11544wwwq_冰凌汇编

发现代码中导出都是混淆过的代码,可读性非常差。通过Chrome network对游戏的协yi分析,了解到游戏是通过websocket加密,搜索websocket、onopen、onmessage、onclose、oneroor,等websocket比用的关键词,找到相关代码如下


可以看到这个源码文件导出都是_0x2b3c9d[_0x2568('0x3cb')]这样的代码,看上去像是从一个数组中,取对应索引的值,而且单引号内的0x开头的是16进制的数字,自己用微软的计算器,可以转换成10进制。

把代码拉到第一行,看到第一行的代码,其实就是一个大数组,里面基本都是明文,可以确定,这个代码文件里面的所有_0x2b3c9d[0xxxxx],就是从第一行的这个大树组里面取值。

js 通过ast抽象语法树对代码进行分析 - 11544wwwq_冰凌汇编
这个大树组维护了几百个值,源文件几万行,不可能手工一个个拿计算器换算成10进制的数值后,再去找数组对应的索引值。那这个项目没法做。

这时候就需要使用到js AST抽象语法树相关的概念。

各位下次遇到js源码分析,代码里面有一个这样的值固定的很大的数组,其他代码中导出都是类似于数组[索引],这样的代码结构,就可以使用到抽象语法树的技术对代码进行反混淆。使用起来也相对简单。

给大家提供一个AST抽象语法树,代码结构分析的网站 https://astexplorer.net/,打开后将上面找到的websocket相关代码复制到左边,得到如下界面。



js 通过ast抽象语法树对代码进行分析 - 11544wwwq_冰凌汇编
其实就是根据js语言的编译规则, 将所有的胆码拆分成相应的单元, 对代码结构进行分析。找到对应的代码结构,就可以通过编码的方式,将所有数组【索引】这样结构的代码全部找出来,然后在进行替换。

具体编码方式如下, 需要使用到nodejs的环境,先通过npm安装相应的包,然后把这个大树组复制进去,通过上面AST分析找到需要替换的节点,通过node fs模块,读取源代码,然后再对符合条件的节点进行替换。

js 通过ast抽象语法树对代码进行分析 - 11544wwwq_冰凌汇编

没法截图了 上传附件了, 除了上面包引入,和大数组, 其他关键代码如下

[JavaScript] 纯文本查看 复制代码
// 数组调用切换为字符串
const traverse_arr = {
    Identifier: {
        exit(path) {
            if (t.isIdentifier(path.node, {
                    name: '_0x2568'
                })) {
                // 参数
                let args = path.parentPath.node.arguments
                // 节点替换
                if (args) {
                    args = args[0]
                    const len = arr.length
                    let index = parseInt(args.value, 16)
                    const val = arr[index]
                    path.parentPath.replaceWith(t.expressionStatement(t.stringLiteral(val)))
                }
            }
        }
    }
}

const traverse_member = {
    MemberExpression(path) {
        let property = path.get('property')
        if (property.isStringLiteral()) {
            const val = property.node.value
            path.node.computed = false
            property.replaceWith(t.Identifier(val))
        }
    }
}


const jscode = fs.readFileSync("./demo.js", {
    encoding: "utf-8"
})

const ast = parser.parse(jscode)
traverse(ast, traverse_arr)
traverse(ast, remove_commma)
traverse(ast, remove_var_commma)
// traverse(ast, traverse_member)
const {
    code
} = generator(ast)

fs.writeFile('./decode.js', code, (err) => {});

最后将反混淆之后的代码进行输出。得到基本能够看得懂的明文代码如下



没法上传附件了,随便扣一段反混淆之后的明文代码,结合上面截图中的混淆代码对比看一下

[JavaScript] 纯文本查看 复制代码
var _0x90c116 = _0x43e0bb("./MessageHelper"),
        _0xbab33a = _0x43e0bb("./Logger"),
        _0xe92f43 = _0x43e0bb("../Net/NetMsgHandler"),
        _0x2072e8 = function () {
      function _0x5064cc() {
        this["connected"] = !0x1, this["socket"] = null;
      }

      return _0x5064cc["prototype"]["handleConnect"] = function (_0x342fe6) {
        this["connected"] = _0x342fe6, null != this["onConnect"] && this['onConnect'](_0x342fe6);
      }, _0x5064cc["prototype"]["handleOpen"] = function () {
        this["handleConnect"](!0x0);
      }, _0x5064cc['prototype']['handleMessage'] = function (_0x2bd9a6) {
        if (null != this["onMessage"]) {
          var _0x22c348 = _0x90c116["default"]["decode"](_0x2bd9a6["data"]);

          this["onMessage"](_0x22c348["tag"], _0x22c348["tagGame"], _0x22c348["body"]);
        } else _0xbab33a["logger"]["error"]("handleMessage:: this.onMessage is null! isConnected=%s", _0xe92f43["netMsgHandler"]["isConnected"]());
      }, _0x5064cc["prototype"]["handleError"] = function () {
        this['handleConnect'](!0x1);
      }, _0x5064cc["prototype"]["handleClose"] = function () {
        null != this["onClose"] && this["onClose"](), this["connected"] = !0x1;
      }, _0x5064cc["prototype"]["connect"] = function (_0x18d706, _0x3fd53f) {
        try {
          'WebSocket' in window ? this["socket"] = new WebSocket("ws://" + _0x18d706 + ':' + _0x3fd53f) : "MozWebSocket" in window && (this['socket'] = new MozWebSocket("ws://" + _0x18d706 + ':' + _0x3fd53f)), this['socket']["binaryType"] = "arraybuffer", this["socket"]["session"] = this, this["socket"]["onopen"] = this["handleOpen"]["bind"](this), this['socket']["onmessage"] = this["handleMessage"]['bind'](this), this["socket"]["onclose"] = this["handleClose"]['bind'](this), this["socket"]["onerror"] = this["handleError"]['bind'](this);
        } catch (_0x201db4) {
          return this["handleConnect"](!0x1), void _0xbab33a["logger"]["error"](_0x201db4["message"] + '\x0a' + _0x201db4["stack"]);
        }
      }, _0x5064cc["prototype"]["setCallback"] = function (_0x59d05b, _0x26b217, _0x3d761e) {
        this['onConnect'] = _0x59d05b, this["onMessage"] = _0x26b217, this["onClose"] = _0x3d761e;
      }, _0x5064cc['prototype']['send'] = function (_0x2ef6f4, _0x30ae76, _0xf65e69) {
        return this["socket"]['readyState'] === WebSocket["OPEN"] && (this["socket"]["send"](_0x90c116["default"]["encode"](_0x2ef6f4, _0x30ae76, _0xf65e69)), !0x0);
      }, _0x5064cc["prototype"]["sendGameMsg"] = function (_0x4dbbf3, _0x37be87, _0x2298b8) {
        return this["socket"]["readyState"] === WebSocket['OPEN'] && (this["socket"]["send"](_0x90c116["default"]["encodeGameMsg"](_0x4dbbf3, _0x37be87, _0x2298b8)), !0x0);
      }, _0x5064cc["prototype"]["close"] = function () {
        console["log"]('net.Close------>'), this['socket']['close']();
      }, _0x5064cc["prototype"]["getWSState"] = function () {
        return null == this['socket'] ? WebSocket["CLOSED"] : this["socket"]["readyState"];
      }, _0x5064cc;
    }();


反混淆之后,socket onmessage 等等,将上面大树组里面的值都替换到源代码里面去,就能很方便的对代码进行分析了。


有js,websocket,java,c#,安卓,等技术相关的协yi、逆向项目需求的老板可以找我详聊。


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|站点统计|Archiver|小黑屋|RSS|冰凌汇编 ( 滇ICP备2022002049号 滇公网安备 53032102000029号)|网站地图

GMT+8, 2022-9-25 07:30 , Processed in 0.130336 second(s), 9 queries , Redis On.

冰凌汇编 - 建立于2021年12月20日

Powered by Discuz! © 2001-2022 Comsenz Inc.

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