js逆向工具-初学AST解混淆

article/2025/9/19 17:15:39

目录

        • 一、AST简单了解
        • 二、babel环境安装
        • 三、快速入门例子
          • 1、入门例子讲解:修改变量值
          • 2、入门案例代码:修改变量值
        • 四、实际案例1-ob混淆之ast还原
          • 1、数组 + 移位自执行函数 + 解密字符串函数 还原
          • 2、定义的对象Object有规律的key和value 还原
          • 3、while + switch控制流平坦化

一、AST简单了解

  • 以下文章简单了解下,可以通过案例熟悉再回头再来看这些文章
  • babel手册1、babel手册2
  • 13个示例快速入门JS抽象语法树
  • ast的概念和babel这一工具的具体使用
  • 可视化显示AST结构工具
    在这里插入图片描述
  • ast的作用:ast作用主要是将混淆的难读的js还原成可读性高的js,对整个js的逻辑不做任何改变,你可以理解为化繁为简,可见性的瘦身,也许网页上是1w多行的混淆代码,瘦身后只有不到1千行;脱混淆后要注意替换到网页试试看能否能用
  • 处理逻辑:就是一颗树,不停的找分支,确定当前分支的各个属性特性,即定位要改的节点就是找type,然后对该type节点操作即可,你可以简单理解为html里面找到了父标签然后对子标签进行操作,理解为xpath或者css选择器的逻辑也不为过
  • 十分要注意的:每次还原逻辑的时候,都要保证当前代码是可以正常运行的,然后再进行下一步还原逻辑

二、babel环境安装

  • node安装:node环境安装
  • babel相关包安装:在cmd窗口下执行如下命令
    npm install -g @babel/core
    npm install -g @babel/parser
    npm install -g @babel/template
    npm install -g @babel/traverse
    npm install -g @babel/types
    npm install -g @babel/generator
    

三、快速入门例子

1、入门例子讲解:修改变量值
  • 分为三部分:引入原js文件,ast操作节点还原逻辑,还原后的js文件存储
  • 首先打开可视化显示AST结构工具,查看原js文件结构,我们这里只需要关注type节点是什么,以及你想操作的结果在哪个节点下
    在这里插入图片描述
  • 比如我这里想把原js文件var a = 1通过ast操作变成var a = "shirmay",那对应的type节点VariableDeclarator,要修改的是该节点的init键
  • 对应代码脚本,其中t.stringLiteral(“shirmay”)即要赋的值
    var traverses_1 = {VariableDeclarator(path) {var cur_node = path.node;// console.log(cur_node.id.name);// console.log(cur_node.init.value);cur_node.init = t.stringLiteral("shirmay");  // 修改变量值}
    }
    
  • 最终完整的ast操作js修改变量值的脚本,完整代码在下面案例1,各个代码注释已经在图片中标注,后续我们只需要修改traverses还原逻辑即可,其它基本就是个固定框架
    在这里插入图片描述
2、入门案例代码:修改变量值
  • 把原js文件var a = 1通过ast操作变成var a = "shirmay"
  • 原js文件:demo_1.js
    var a = 1;
    
  • ast还原文件:ast_demo_1.js
    // 将JS源码转换成语法树
    var parser = require("@babel/parser");
    // 为parser提供模板引擎
    var template = require("@babel/template").default;
    // 遍历AST树节点
    var traverse = require("@babel/traverse").default;
    // 操作节点,比如判断节点类型,生成新的节点等
    var t = require("@babel/types");
    // 将语法树转换为源代码
    var generator = require("@babel/generator").default;// 1、读取原始文件
    var fs = require("fs");
    var jscode = fs.readFileSync("demo_1.js", {encoding: "utf-8"}); 
    console.log("原js文件内容:", jscode);
    // 2、对原始文件进行操作,使用traverse遍历语法树,因为变量定义为VariableDeclarator类型,所以我们只对type为VariableDeclarator的节点进行处理
    var ast = parser.parse(jscode); 
    var traverses_1 = {VariableDeclarator(path) {var cur_node = path.node;// console.log(cur_node.id.name);// console.log(cur_node.init.value);cur_node.init = t.stringLiteral("shirmay");  // 修改变量值}
    }
    traverse(ast, traverses_1) 
    // 3、生成还原后的js文件
    var ast_js_code = generator(ast);
    console.log("ast操作后的js文件内容:", ast_js_code.code)
    

四、实际案例1-ob混淆之ast还原

  • ob混淆分为以下几个步骤:
    • ①【数组 + 移位自执行函数 + 解密字符串函数】还原
    • ②【定义的对象Object,有规律的key和value】还原
    • ③【while + switch 组合控制流平坦化】还原
  • 分为三部分:引入原js文件,ast操作节点还原逻辑,还原后的js文件存储,通用的代码结构如下,我们每次需要该段就是还原逻辑的处理traverses_1、traverses_2、traverses_3等…
    // 通用的引用模块
    var parser = require("@babel/parser");  // 将JS源码转换成语法树AST
    var template = require("@babel/template").default;  // 为parser提供模板引擎
    var traverse = require("@babel/traverse").default;  // 遍历AST树节点操作
    var types = require("@babel/types");  // 操作节点,比如判断节点类型,生成新的节点等
    var generator = require("@babel/generator").default;  // 将语法树AST转换为js代码
    var fsrw = require("fs");
    var jscode = fsrw.readFileSync("ob_1_before.js", {encoding: "utf-8"});  // 读取原始混淆文件
    var ast_parse = parser.parse(jscode);  //将JS源码转换成语法树AST// 定义每个还原逻辑
    var traverses_1 = {........................}
    var traverses_2 = {........................}
    var traverses_3 = {........................}
    // 还原逻辑运行
    traverse(ast_parse, traverses_1) 
    traverse(ast_parse, traverses_2) 
    traverse(ast_parse, traverses_3) // 生成还原后的js文件
    var ast_js_code = generator(ast_parse);
    // console.log("ast操作后的js文件内容:", ast_js_code.code)
    fsrw.writeFileSync('./ob_1_after.js', ast_js_code.code)
    
1、数组 + 移位自执行函数 + 解密字符串函数 还原
  • 其目的是将js文件的如此类的$_0x4f70('\x30\x78\x31\x34', '\x64\x5a\x59\x67') + '\x6f\x64' 还原成"nDAod",其中还涉及了将十六进制字符串还原成正常易读的字符串,最终效果图如下
    在这里插入图片描述

  • 首先打开可视化显示AST结构工具,查看原js文件结构,并确定解密函数字符串为"$_0x4f70",分出前三部分并加载到内存后删除
    在这里插入图片描述

    // 获取解密函数前三部分(大数组+自执行函数+解密字符串函数),并写入内存后删除
    var decryptFunNameStr = "$_0x4f70"
    let descrypt_strfun_js = '';
    for(let i=0;i<=2;i++){ descrypt_strfun_js += generator(ast_parse.program.body[i], {compact:true}).code  // compact:true, 设置js压缩delete ast_parse.program.body[i]
    }
    eval(descrypt_strfun_js);
    
  • 其次观察$_0x4f70(’\x30\x78\x63\x62’, ‘\x69\x59\x23\x63’)树结构,需要解混淆的节点type为CallExpression,所以我们对CallExpression节点逐个判断,满足当前节点的callee.name是解密字符串函数时,对当前节点进行统一解混淆替换
    在这里插入图片描述

    // 调用解密函数的逻辑还原
    var decryptFunNameStr = "$_0x4f70"
    var traverses_1 = {CallExpression(path) {var cur_node = path.node;       if(cur_node.callee.name === decryptFunNameStr && cur_node.arguments.length === 2){path.replaceInline(types.valueToNode(eval(path.toString())));}}
    }
    
  • 最后关于一些十六进制,unicode的字符串还原,借鉴前人经验,直接删除extra这个节点即可
    在这里插入图片描述

    // 处理十六进制等字符串还原
    var traverses_2 = {// A.处理十六进制字符串,针对"\x68\x65\x6c\x6c"  》》 'hell'NumericLiteral(path) {cur_node = path.node;if (cur_node.extra && /^0[obx]/i.test(cur_node.extra.raw)) {cur_node.extra = undefined;}},// B. 处理Unicode字符串,针对"\u0068\u0065\u006c\u006c" 》》'hell'StringLiteral(path) {cur_node = path.node;if (cur_node.extra && /\\[ux]/gi.test(cur_node.extra.raw)) {cur_node.extra = undefined;}},// C. 字符串合并,exit的方式可以往深层判断是否都是满足这个, 针对 "whi" + "le " + "(tr" + "ue)" + " {}" 》》 "while (true) {}"BinaryExpression: {exit: function(path){  cur_node = path.node;if (cur_node.left.type==="StringLiteral"&&cur_node.right.type==="StringLiteral"&&cur_node.operator==='+'){path.replaceWith(types.valueToNode(cur_node.left.value + cur_node.right.value))}}}
    }
    
2、定义的对象Object有规律的key和value 还原
  • 本案例的对象Object是_0x3f5d3a,后又赋值给了对象 _0x1104e6 ,再后面一直调用的也是 _0x1104e6 对象
  • 所以先将_0x3f5d3a临时存储,然后再分析 _0x1104e6 对象引用时如何替换,其中我们发现_0x3f5d3a对象的值就两种类型,要么是function,要么是字符串,所以在后面分析 _0x1104e6 引用时,就只分析这两种情况
    在这里插入图片描述
    在这里插入图片描述
  • 对象Object是_0x3f5d3a临时存储代码
    // 将有规律的对象临时存储,并删除相应的节点
    var temp_obj = {};
    var regularObjNameStr = '_0x3f5d3a'
    var traverses_3 = {AssignmentExpression(path){var cur_node = path.node;       if((cur_node.right.type === 'FunctionExpression' || cur_node.right.type === 'StringLiteral')){if(cur_node.left.object.name === regularObjNameStr){temp_obj[cur_node.left.property.value] = cur_node.right;path.remove();}}},
    }
    
  • _0x1104e6 对象的值是字符串时被引用
    在这里插入图片描述
  • _0x1104e6 对象的值是函数时被引用
    在这里插入图片描述
  • ast代码,注意本案例当中cur_node.callee.object.name === '_0x1104e6'
    // 有规律的对象引用替换
    var refererObjNameStr = '_0x1104e6'
    var traverses_4 = {MemberExpression(path){cur_node = path.node;if(cur_node.object.name === refererObjNameStr && (path.inList || path.parent.type === 'AssignmentExpression' || path.parent.type === 'BinaryExpression'|| path.parent.type === 'MemberExpression')){path.replaceInline(temp_obj[cur_node.property.value])}},CallExpression(path){cur_node = path.nodeif(cur_node.callee.object && cur_node.callee.object.name === refererObjNameStr){const y_node = temp_obj[cur_node.callee.property.value];if (y_node && y_node.body.body[0].argument.type === 'BinaryExpression'){const operator = y_node.body.body[0].argument.operator;path.replaceInline(types.binaryExpression(operator, cur_node.arguments[0], cur_node.arguments[1]))}else if(y_node && y_node.body.body[0].argument.type === 'CallExpression'){const arg = cur_node.arguments.slice(1);path.replaceInline(types.callExpression(cur_node.arguments[0], arg))}}}
    }
    
3、while + switch控制流平坦化
  • 控制流平坦化还原思路参考Nanda的这篇文章

  • 控制流基本语法包括两种:一种是if / else 条件语句判断,另一种是while / switch / case 条件语句判断,本案例是第二种,还原效果如图
    在这里插入图片描述

  • 还原逻辑,可以将控制流数组先取出来》然后遍历控制流数组,依次取出依次取出对应的SwitchCase节点》再将每个SwitchCase节点的consequent代码块添加到临时数组中,如果有continue则删除该代码节点》遍历完后用用临时数组替换掉SwitchStatement节点
    在这里插入图片描述

    // 控制流平坦化while + switch
    var traverses_5 = {WhileStatement(path){cur_node = path.node;if(cur_node.body.body[0].type === "SwitchStatement"){var swithStm = cur_node.body.body[0];     // 找到path节点的前兄弟节点,即 _0x1eb1f3所在的节点,然后获取 _0x1eb1f3数组var arrNodePath = path.getAllPrevSiblings()[1]var arrValue = arrNodePath.node.declarations[0].init.callee.object.value.split('|');// SwitchCase节点遍历并临时存储到一个数组     var caseList = swithStm.cases;     var temp_arr_switch_case = [];arrValue.map(targetIdx => { var targetBody = caseList[targetIdx].consequent;    // 如果最后一个节点是,则删除ContinueStatement块(continue语句)     if (types.isContinueStatement(targetBody[targetBody.length - 1])){targetBody.pop();    }       temp_arr_switch_case = temp_arr_switch_case.concat(targetBody)     });// 多个节点替换一个节点的话使用replaceWithMultiple方法     path.replaceWithMultiple(temp_arr_switch_case);           }},
    }
    

http://chatgpt.dhexx.cn/article/TaVKmBMt.shtml

相关文章

推荐.Net、C# 逆向反编译四大工具利器

转&#xff1a;https://blog.csdn.net/kongwei521/article/details/54927689 在项目开发过程中&#xff0c;估计也有人和我遇到过同样的经历&#xff1a;运行环境出现了重大Bug亟需解决、或者由于电脑挂了、旧代码覆盖新代码&#xff0c;而在这种情况下&#xff0c;我们不能直接…

某科技js逆向

分析 地址->https://qimingp*.cn/fino*a/project/ 捕获ajax请求&#xff0c;发现返回的数据是加密的&#xff0c;如下图&#xff1a; 打开搜索&#xff0c;定位如下&#xff1a; 经过查找&#xff0c;定位到11058行&#xff0c;如下&#xff1a; 分析代码&#xff0c;发现首…

BUUCTF 逆向工程(reverse)之内涵的软件

用IDA32位打开 一看到这个就是知道这里是获取flag的关键&#xff08;因为花括号{}&#xff09;。一开始以为是用了某种加密方式需要转换一下。结果它显示的&#xff1a;{49d3c93df25caad81232130f3d2ebfad}这部分就是flag。 所以这题的flag为&#xff1a;flag{49d3c93df25caa…

010Editor逆向分析

主要内容&#xff1a; 010Editor介绍 16进制编辑器&#xff1a;16进制修改、文本修改、模板解析各种文件格式、对比文件 010暴力破解分析 1、找到注册的窗口 2、测试注册窗口的反应 3、根据反应做出下一步分析 猜测API&#xff0c;API下断点动态调试 敏感字符串&#xff0c;程序…

Web前端——CSS伪类和伪元素

CSS伪类&#xff1a; 1.伪类的概念&#xff1a; 可以理解为描述元素的某种状态&#xff0c;用于当已有元素处于的某个状态时&#xff0c;为其添加对应的样式&#xff0c;这个状态是根据用户行为而动态变化的。 2.伪类的语法&#xff1a; 标签:伪类{设置的样式&#xff0c;伪类…

CSS伪类

CSS中伪选择器有两种分别是伪元素选择器和伪类选择器。为了向后续版本兼容&#xff0c;伪元素选择器常用"::"开头&#xff0c;而伪类选择器用“:”开头。本篇主要讲解以下伪类选择器&#xff1a; :first-child:last-childonly-childonly-of-type:nth-child(n)nth-la…

【JavaScript 逆向】AST 技术反混淆

前言 通过浏览器工具可以清楚的看到网站正在运行的 HTML 和 JavaScript 代码&#xff0c;所以对 JavaScript 代码进行混淆处理是一些网站常用的反爬措施&#xff0c;例如下文介绍到的字符串混淆、控制流平坦化等&#xff0c;这使得 JavaScript 的可读性变得很差&#xff0c;难以…

Css预编译神器

最近&#xff0c;有靓仔吐槽在编译css代码时&#xff0c;每次写选择器都会变成CV大神&#xff0c;虽说有CV加持但是呢依然会觉得很麻烦&#xff0c;毕竟手速不像年轻时候那样为所欲为 在这里呢给推荐大家用一款神级插件&#xff0c;也是小编参与完成的轻量级插件–sass&#x…

CSS 伪类

CSS 伪类 CSS 伪类是添加到选择器的关键字&#xff0c;用于指定所选元素的特殊状态。例如&#xff0c;伪类 :hover 可以用于选择一个按钮&#xff0c;当用户的指针悬停在按钮上时&#xff0c;设置此按钮的样式。 举例说明: button:hover {color: blue; }伪类由冒号&#xff…

逆向分析并修改Hello World程序《逆向工程核心原理》《软件逆向工程原理与实践》

文章目录 OllyDbg窗口及快捷键步骤1&#xff1a;VS生成需逆向的文件步骤2&#xff1a;OllyDbg中打开该程序的exe文件&#xff0c;找到需修改的位置步骤3&#xff1a;修改修改1&#xff1a;修改指令修改2&#xff1a;修改字符串修改3&#xff1a;输出任意英文 软件逆向工程原理与…

js逆向案例-css字体反爬

目录 一、反爬点二、反爬分析1、js逆向解密响应参数2、css字体伪元素分析一、反爬点 案例网站响应参数js加密, css字体伪元素隐藏,以及style取值等逻辑判断 二、反爬分析 1、js逆

SQL 结构化查询语言

导读 MySql是我们常用的数据库,javaEE常用几款(Oracle,PostgreSQL,DB2或IBM),SQLite是用于嵌入式设备里的小型数据库,例如Android或IOS,而掌握SQL语句,就相当于掌握了所有的常见关系化数据库,需要同学们重点掌握以及经常复习 MySQL数据库服务器、数据库和表的关系 一般一个项…

《数据库系统》(三) 结构化查询语言

hello大家好,今天我们来学习结构化查询语言。教妹学数据库,没见过这么酷炫的标题吧?“语不惊人死不休”,没错,标题就是这么酷炫。 我的妹妹小埋18岁,校园中女神一般的存在,成绩优异体育万能,个性温柔正直善良。然而,只有我知道,众人眼中光芒万丈的小埋,在过去是一个…

mysql 结构化数据库_【MySQL】——MySQL数据库和SQL结构化查询语言概述

【MySQL】——MySQL数据库和SQL结构化查询语言概述 【MySQL】——MySQL数据库和SQL结构化查询语言概述 文章目录数据库和SQL语言【1】数据库概述 【2】SQL语言 【3】MySQL数据库 【4】启动/停止MySQL服务 【1】数据库概述 数据的传输&#xff1a;数据库 —> Web服务器 —>…

MySQL结构化查询语言

结构化查询语言sql包含以下四部分&#xff1a; 1.DDL //数据定义语言&#xff0c;create,drop,alter 2.DML //数据操作语言&#xff0c;insert,update,delete 3.DQL //数据查询语言&#xff0c;select 4.DCL //数据控制语言&#xff0c;grant,commit,rollback 以下就增删查…

结构化查询语言SQL基本功能及其概念

SQL语法 可以把SQL分为两部分数据操作语言DML和数据定义语言DDL。 SQL&#xff08;结构化查询语言&#xff09;有用于执行查询、更新、删除、插入记录的语法。 SQL的DML部分&#xff1a; select-从数据库表中获取数据。insert into-向数据库表中插入数据update-更新数据库表中…

Rasa中文聊天机器人开发指南(3):Core篇

文章目录 1. 对话管理1.1 多轮对话1.2 对话管理 2. Rasa Core2.1 Stories2.2 Domain2.3 Responses2.4 Actions2.5 Policies2.6 Slots2.6.1 Slots Type2.6.2 Slots Set2.6.3 Slots Get 2.7 Form2.8 Interactive Learning 3. 改进ChitChatAssistant项目3.1 config.yml3.2 weather…

Rasa开发使用 Rasa_NLU及Rasa_Core模型训练与测试

文章目录 Rasa术语 Rasa_NLU1. Pipeline2. 准备工作&#xff1a;训练MITIE模型文件3. rasa_nlu 语料4. 训练模型5. 测试验证 Rasa Core1. Stories可视化stories 2. Domain3. 训练对话模型测试对话模型 测试聊天机器人 Rasa Rasa是一个开源机器学习框架&#xff0c;用于构建上下…

浅读Rasa3.2.5源码(rasa train、rasa shell)

目录 浅读Rasa3.2.5源码&#xff08;rasa train、rasa shell&#xff09;一、 分析 __main__.py&#xff08;1&#xff09;. 解析main.py的部分代码&#xff08;2&#xff09;. rasa常用命令 二、 训练阶段&#xff08;1&#xff09;. 准备训练数据&#xff08;2&#xff09;. …

2.rasa架构

rasa架构 消息处理 此图显示了使用Rasa构建的助手如何响应消息的基本步骤&#xff1a; 这些步骤分别是&#xff1a; 1. 收到消息并将其传递给解释器(Interpreter)&#xff0c;解释器将其转换为包含原始文本&#xff0c;意图和找到的任何实体的字典。这部分由NLU处理。 2. 跟踪…