AST原理(反混淆)

一、AST原理

在这里插入图片描述

jscode = 'var a = "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054";'

在上述代码中,a 是一个变量,它被赋值为一个由 Unicode 转义序列组成的字符串。Unicode 转义序列在 JavaScript 中以 \u 开头,后跟四个十六进制数字,表示一个 Unicode 字符。

这里的字符串 "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054" 实际上是使用 Unicode 转义序列表示的 "hello,AST"。每个 \uXXXX 表示一个字符:

  • \u0068 -> “h”
  • \u0065 -> “e”
  • \u006c -> “l”
  • \u006c -> “l”
  • \u006f -> “o”
  • \u002c -> “,”
  • \u0041 -> “A”
  • \u0053 -> “S”
  • \u0054 -> “T”

所以,变量 a 的值是 "hello,AST"

AST(Abstract Syntax Tree,抽象语法树)是源代码的抽象语法结构的树状表示形式。在 JavaScript 中,AST 会将代码分解成树中的节点,每个节点代表代码中的一个构造(如变量声明、字面量、表达式等)。

如果我们将上述代码转换为 AST,它将包含以下主要节点:

  1. VariableDeclaration:表示变量声明的节点。
    在这里插入图片描述

    里面是函数体的内容
  2. VariableDeclarator:表示声明中的变量和赋值的节点。包含起始位置和结束位置

  3. Identifier:表示变量名 a 的节点。
    在这里插入图片描述

  4. Literal:表示字符串字面量的节点,其值为 "hello,AST"
    在这里插入图片描述

使用 AST 技术,我们可以分析、遍历、修改或解释代码的结构。例如,代码编辑器、编译器和代码转换工具(如 Babel)会使用 AST 来理解和操作源代码。

准备需要替换的JS代码
// 解析js代码  会把js源码转换成ast语法树,返回的结果是json的结构的数据
const parse = require('@babel/parser')
// 在lxml相当于是xpath
// 编写节点和进行转换
const traverse = require('@babel/traverse').default// 准备需要转换的js代码jscode = 'var a = "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054";'var ast = parse.parse(jscode);// 传递两个参数(ast语法数据, 访问器对象)
//遍历、修改 AST 语法树的各个节点
traverse(ast, {
//     根据类别定位标签  , path 是定位之后的地址VariableDeclarator(path){console.log(path.parentPath)}
})
Babel库应用场景

Babel 库的应用场景主要涉及以下几个方面:

  1. 跨浏览器兼容性

    • 现代 JavaScript(如 ES6/ES2015 及更高版本)引入了许多新特性,如箭头函数、类、模块、模板字符串、默认参数等。
    • 不是所有浏览器都支持这些新特性,尤其是旧版本的浏览器(如 Internet Explorer)。
    • Babel 可以将这些现代 JavaScript 代码转换成旧版本的浏览器也能理解和执行的 ES5 代码。
  2. 开发新特性

    • 开发者可以使用最新的 JavaScript 特性来编写代码,提高开发效率和代码质量。
    • Babel 会处理新语法和提案中的特性,让开发者不必等到所有用户的浏览器都支持这些新特性。
  3. 工具链集成

    • Babel 可以集成到现代前端工具链中,如 Webpack、Rollup、Gulp 等。
    • 它可以作为构建过程中的一步,自动编译所有 JavaScript 文件。
  4. 插件和预设

    • Babel 提供了插件系统,允许开发者自定义转换规则。
    • 预设(presets)是一组插件的集合,可以轻松地为特定目的配置 Babel。
  5. 代码优化

    • Babel 插件可以用于代码优化,如移除开发中的代码、简化代码结构等。
  6. 支持 TypeScript、Flow

    • Babel 可以转换 TypeScript 或 Flow 注解的代码,使其成为普通的 JavaScript 代码。

举例说明:

假设您正在使用箭头函数(一种 ES6 特性)编写代码:

const add = (a, b) => a + b;

在旧版本的浏览器中,箭头函数可能不被支持。Babel 可以将上述代码转换为:

var add = function(a, b) {return a + b;
};

这样,即使是不支持 ES6 的旧浏览器也可以正确执行这段代码。

当我们说 Babel 允许代码“运行在当前和旧版本的浏览器或其他环境中”时,意思是 Babel 生成的代码不仅可以在支持最新 JavaScript 特性的最新浏览器中运行,也可以在那些只支持旧 JavaScript 版本的旧浏览器中运行。此外,其他环境可能包括 Node.js、Electron 或任何 JavaScript 引擎。这使得开发者可以编写最新和最优雅的代码,同时确保它在尽可能多的环境中都能工作。

Babel库学习

​ 根据官网介绍,它是一个JavaScript 编译器,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容 的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

  1. @babel/core :Babel 编译器本身,提供了 babel 的编译 API;
  2. @babel/parser :将 JavaScript 代码解析成 AST 语法树;
  3. @babel/traverse :遍历、修改 AST 语法树的各个节点;
  4. @babel/generator :将 AST 还原成 JavaScript 代码;
  5. @babel/types :判断、验证节点的类型、构建新 AST 节点等。
// 安装命令
npm install @babel/parser --save-dev

二、AST语法学习

  • 参考地址:https://www.babeljs.cn/docs/
  • 在线解析:https://astexplorer.net
//练习语法
var a = "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054"
1. AST输出树结构
  1. type: 表示当前节点的类型,我们常用的类型判断方法,就是判断当前的节点是否为某个类型。
  2. start: 表示当前节点的起始位。
  3. end: 表示当前节点的末尾。
  4. loc : 表示当前节点所在的行列位置,里面也有start与end节点,这里的start与上面的start是不同 的,这里的start是表示节点所在起始的行列位置,而end表示的是节点所在末尾的行列位置。
  5. errors:是File节点所特有的属性,可以不用理会。
  6. program:包含整个源代码,不包含注释节点。
    1. sourceType: 通常用于标识源代码的类型,以告诉解析器或编译器它正在处理的代码是模块代码还是脚本代码(Script, Module)
    2. body:包含了程序的主体代码,即程序的主要逻辑。
      1. 语句块:“body” 可能表示一组语句,通常是一个代码块,这些语句按顺序执行。
      2. 函数体:对于函数或方法定义,“body” 包含了函数的主体代码,即函数内部的语句和逻辑。
      3. 类定义:对于类定义,“body” 可能包含类的成员,如属性和方法。
      4. 模块内容:对于模块或文件,“body” 可能包含文件中的顶级语句和声明。
      5. declarations:通常用于表示变量、常量、函数、类等的声明
      6. id:是函数,变量,类的名称
      7. init: 通常代表声明的初始化值
  7. comments:源代码中所有的注释会在这里显示。
2.常见节点类型

在这里插入图片描述

3.babel库学习

​ 根据官网介绍,它是一个JavaScript 编译器,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容 的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

  1. @babel/core :Babel 编译器本身,提供了 babel 的编译 API;
  2. @babel/parser :将 JavaScript 代码解析成 AST 语法树;
  3. @babel/traverse :遍历、修改 AST 语法树的各个节点;
  4. @babel/generator :将 AST 还原成 JavaScript 代码;
  5. @babel/types :判断、验证节点的类型、构建新 AST 节点等。
// 安装命令
npm install @babel/parser --save-dev
1. parser库使用
  • 将JavaScript源代码 转换成一棵 AST 树、返回结果(在这里赋值给 ast )是一个 JSON 结构的数据
const parse = require('@babel/parser')
// JS 转 ast语法树
jscode = `var a = "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054";`
let ast = parse.parse(jscode);
console.log(JSON.stringify(ast,null,'\t'))
2. traverse 库学习
  • 节点插件编写与节点转化
  • 你可以使用 traverse 函数来遍历AST。通常,你需要提供两个参数:AST 和访问器对象。
const parse = require('@babel/parser')
const traverse = require('@babel/traverse').default
// JS 转 ast语法树
jscode = `var a = "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054";`
// 转换js代码为ast树结构
let ast = parse.parse(jscode);// 用查找定位节点(ast结构树, 访问器对象)
traverse(ast, {// 定位VariableDeclarator类别,path是定位之后的地址VariableDeclarator(path){console.log('Found identifier:', path.node.init.value);}
})
1. path属性语法学习
var a = "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054";

path.node :表示当前path下的node节点

path.toString() :当前路径所对应的源代码

path.parentPath :用于获取当前path下的父path,多用于判断节点类型

  • 解析:VariableDeclarator 是一个访问者方法(visitor method),它会在遍历到每个 VariableDeclarator 类型的 AST 节点时被调用。

  • 作用:在反混淆的过程中,需要根据父节点的类型决定如何处理当前节点。例如,如果一个混淆的字符串字面量Literal是函数调用的一部分,那么可能需要以不同的方式处理它。

    • 举例说明:假设我们有以下 ES6 代码片段,我们想要转换箭头函数为普通函数表达式,但只针对在对象字面量中作为属性值的箭头函数:

      const obj = {greet: () => console.log("Hello, world!"),
      };const standaloneGreet = () => console.log("Standalone Hello, world!");
      

      在这个例子中,我们只想转换 obj.greet 中的箭头函数,而不转换 standaloneGreet

      const parser = require('@babel/parser');
      const traverse = require('@babel/traverse').default;
      const t = require('@babel/types');
      const generator = require('@babel/generator').default;
      // 假设的 JavaScript 代码
      const code = `
      const obj = {greet: () => console.log("Hello, world!"),
      };const standaloneGreet = () => console.log("Standalone Hello, world!");
      `;// 解析 JavaScript 代码生成 AST
      const ast = parser.parse(code, {sourceType: 'module', // 依据代码类型选择 "script" 或 "module"
      });traverse(ast, {ArrowFunctionExpression(path) {if (path.parentPath.isObjectProperty()) {// 创建一个 BlockStatement,将原来的表达式包装在 ExpressionStatement 中const blockStatement = t.blockStatement([t.expressionStatement(path.node.body)]);// 创建函数表达式,使用上面创建的 blockStatement 作为 bodyconst functionExpression = t.functionExpression(null, // idpath.node.params, // paramsblockStatement, // bodyfalse, // generatorfalse // async);path.replaceWith(functionExpression);}}
      });// 生成转换后的代码
      const output = generator(ast, { /* options */ }, code);
      // 输出转换后的代码
      console.log(output.code);
      

在这里插入图片描述

  • 在某些情况下,混淆可能会改变代码的结构。通过检查父节点,开发者可以决定是否需要重建代码的某些部分以恢复其原始意图。

    • 分析父节点:通过分析父节点,可以获取更多关于当前节点的信息,比如它是一个独立的变量声明还是一个变量声明列表的一部分。
    • 执行进一步操作:有时候需要基于父节点的信息来决定如何操作当前节点,例如你可能只想修改某个特定函数中的变量声明。
      在这里插入图片描述
  • path.container :用于获取当前path下的所有兄弟节点(包括自身)

  • path.type :获取当前节点类型

  • path.get('') :获取path的子路径,取值的方式有点像Xpath

const parse = require('@babel/parser')
const traverse = require('@babel/traverse').default
// JS 转 ast语法树
jscode = `var a = "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054";
var a = "1111";`
// 转换js代码为ast树结构
let ast = parse.parse(jscode);// 用查找定位节点(ast结构树, 访问器对象)
traverse(ast, {VariableDeclarator(path){// console.log(path.node); // 表示当前path下的node节点// console.log(path.type) // 获取当前节点类型// console.log(path.toString()); // 用来获取当前遍历path的js源代码// console.log(path.parentPath.node); //用于获取当前path下的父path,多用于判断节点类型// console.log(path.get('init').toString()); // 获取下面的节点// console.log(path.container); // 用于获取当前path下的所有兄弟节点(包括自身)// 只获取一个数据console.log(path.node.init.value);// 找到第一个后,可以停止遍历// path.stop();}
})
2. 替换原有节点
  • path.replaceWith :(单)节点替换函数
    • 还原数字相加: var b = 1 + 2
    • 还原字符串拼接: var c = “coo” + “kie”
    • 还原在一行的: var a = 1+1,b = 2+2;var c = 3;
    • 还原在一行的: var d = “1” + 1;
    • 还原在一行的: var e = 1 + ‘2’;
const parse = require('@babel/parser')
const traverse = require('@babel/traverse').default
const types = require('@babel/types')
const generator = require("@babel/generator").default;// JS 转 ast语法树
jscode = `var b = 1 + 2;
var c = "coo" + "kie";
var a = 1+1,b = 2+2;
var c = 3;
var d = "1" + 1;
var e = 1 + '2';
`
// 转换js代码为ast树结构
let ast = parse.parse(jscode);// 用查找定位节点(ast结构树, 访问器对象)
traverse(ast, {BinaryExpression(path) {// 取出数组数据的单独对象var {left, operator, right} = path.node// 数字相加处理if (types.isNumericLiteral(left) && types.isNumericLiteral(right) && operator == "+" || types.isStringLiteral(left) && types.isStringLiteral(right)) {value = left.value + right.value// console.log(value);// 会把原来的节点当中的原来的值进行替换path.replaceWith(types.valueToNode(value))// console.log(path.parentPath.node)}if (types.isStringLiteral(left) && types.isStringLiteral(right) && operator == "+") {value = left.value + right.value// console.log(value);// 会把原来的节点当中的原来的值进行替换path.replaceWith(types.valueToNode(value))}if (types.isStringLiteral(left) && types.isNumericLiteral(right) && operator == "+" || types.isNumericLiteral(left) && types.isStringLiteral(right)) {value = left.value + right.value// console.log(value);// 会把原来的节点当中的原来的值进行替换path.replaceWith(types.valueToNode(value))}}
})
// 将ast还原成JavaScript代码
let {code} = generator(ast);
console.log(code)
  • replaceWithMultiple 多节点替换函数,调用方式

path.replaceWithMultiple(ArrayNode);

  • 实参一般是 Array 类型,它只能用于 Array 的替换。
  • 即所有需要替换的节点在一个Array里面 举例:对如下变量进行处理
替换前:var arr = '3,4,0,5,1,2'['split'](',')
替换后:var arr = ["3", "4", "0", "5", "1", "2"]
const generator = require("@babel/generator").default;
const parse = require('@babel/parser')
const traverse = require('@babel/traverse').default
const types = require('@babel/types')
// JS 转 ast语法树
jscode = `
var arr = '3,4,0,5,1,2'['split'](',')
`
// 转换js代码为ast树结构
let ast = parse.parse(jscode);traverse(ast, {CallExpression(path) {let {callee, arguments} = path.nodelet data = callee.object.valuelet func = callee.property.valuelet arg = arguments[0].valuevar res = data[func](arg)path.replaceWithMultiple(types.valueToNode(res))}
})// 将ast还原成JavaScript代码
let {code} = generator(ast);
console.log(code)
3. 自执行方法还原
!(function () {console.log('123')
})
JS反混淆工具分享
https://tool.yuanrenxue.cn/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/831901.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习笔记 - Python数据清理通用流程简述

一、简述 Python中数据清理是重要的环节,并通过解决缺失值、离群值、重复和不一致等常见问题以进行下一步的数据分析。 通常我们开始一个新的数据项目时,通因为各种各样的问题,通常获得的数据很少能直接上到模型里面去跑,所以要进行清理,清理数据是一个消除错误、异常值和…

centos 杀死一个进程又启动了

在CentOS中,如果一个进程被杀死后又自动启动,可能是由于系统服务管理器(如systemd或init)配置了该进程的重启。以下是检查和处理这种情况的方法: 查找启动脚本: 使用systemctl查找服务文件:syst…

flink mysql数据表同步API CDC

概述: CDC简介 Change Data Capture API CDC同步数据代码 package com.yclxiao.flinkcdcdemo.api;import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.ververica.cdc.connectors.mysql.source.MySqlSource; import com.verv…

Python学习笔记------json

json简介 JSON是一种轻量级的数据交互格式。可以按照JSON指定的格式去组织和封装数据 JSON本质上是一个带有特定格式的字符串 主要功能:json就是一种在各个编程语言中流通的数据格式,负责不同编程语言中的数据传递和交互 为了让不同的语言能够相互通…

【12575嵌入式系统】嵌入式系统考前复习题终结版期末复习自考复习

一、 填空题 1. 嵌入式系统是以应用为中心,以计算机技术为基础,软件硬件可裁剪,适应应用系 统 对 功 能 、 可 靠 性 、 成 本 、 体 积 、 功 耗 严 格 要 求 的 专 用 计 算 机 系统。 2. 请 列 举 2 个 嵌 入 式 实 时 操 作 系 统 &…

Python实战开发及案例分析(2)——单目标优化

在Python中,进行单目标优化主要涉及定义一个优化问题,包括一个目标函数和可能的约束条件,然后选择合适的算法来求解。Python提供了多种库,如SciPy、Pyomo、GEKKO等,用于处理各种优化问题。 案例分析:使用 …

《LTC与铁三角∶从线索到回款-人民邮电》关于铁三角不错的论述

《LTC与铁三角∶从线索到回款-人民邮电》一书中,关于铁三角不错的论述,收藏之:客户责任人的角色定义及核心价值 AR 的核心价值定位主要体现在三个方面:客户关系、 客户满意度、竞争对手 “ 压制 ” 。 维护客户关系&#x…

zabbix之system.run

Zabbix的system.run是一个主动模式的监控项,它允许用户在Zabbix服务器上执行自定义的命令或脚本,并获取执行结果作为监控数据。 system.run监控项的配置包括以下几个关键参数: 命令:要执行的命令或脚本。可以是任何可执行的命令,包括系统命令、脚本文件等。 参数:命令的…

百川2模型解读

简介 Baichuan 2是多语言大模型,目前开源了70亿和130亿参数规模的模型。在公开基准如MMLU、CMMLU、GSM8K和HumanEval上的评测,Baichuan 2达到或超过了其他同类开源模型,并在医学和法律等垂直领域表现优异。此外,官方还发布所有预…

[数据结构]————排序总结——插入排序(直接排序和希尔排序)—选择排序(选择排序和堆排序)-交换排序(冒泡排序和快速排序)—归并排序(归并排序)

文章涉及具体代码gitee: 登录 - Gitee.com 目录 1.插入排序 1.直接插入排序 总结 2.希尔排序 总结 2.选择排序 1.选择排序 ​编辑 总结 2.堆排序 总结 3.交换排序 1.冒泡排序 总结 2.快速排序 总结 4.归并排序 总结 5.总的分析总结 1.插入排…

Visual Studio 2022 工具 选项 没有网络设置问题解决

Visual Studio 2022 工具 选项 没有网络选项了,找了一大圈也没找到。 最后发现Visual Studio 2022的直接使用系统的代理设置了,在浏览器的代理中设置即可。 要使用扩展管理器安装插件,还不能设置pac !!! 顺便记录个pac地址: 1…

Unity---版本控制软件

13.3 版本控制——Git-1_哔哩哔哩_bilibili Git用的比较多 Git 常用Linux命令 pwd:显示当前所在路径 ls:显示当前路径下的所有文件 tab键自动补全 cd:切换路径 mkdir:在当前路径下创建一个文件夹 clear:清屏 vim…

QT, 加载qss文件应用样式

qt 应用中,加载qss文件主要是为了集中管理样式,使用QApplication::setStyleSheet(qssStr) 即可将样式应用到程序中,qss文件中,既可以定义全局样式,也可以针对某些类,甚至某个对象设置样式。关键代码如下&am…

考研就业选择规划【攻略】

考研就业选择规划【攻略】 前言版权推荐考研就业选择首先多问考研就业优势对比我的选择补充 最后 前言 2024-5-5 10:00:02 对于考研就业选择,我的一些看法。 这只是我现在的看法,我不认为现在的我能够看得多远,所以可能局限于现在。 一个…

Linux的socket详解

一、本机直接的进程通信方式 管道(Pipes): 匿名管道(Anonymous pipes):通常用于父子进程间的通信,它是单向的。命名管道(Named pipes,也称FIFO):允…

微星主板安装双系统不能进入Ubuntu的解决办法

在微星主板的台式机上面依次安装了Windows11和Ubuntu22.04。在Ubuntu安装完成后重启,没有出现系统选择界面,直接进入了Windows11。怎么解决?方法如下: (1)正常安装Windows11 (2)安…

《自动机理论、语言和计算导论》阅读笔记:p352-P401

《自动机理论、语言和计算导论》学习第 12 天,p352-P401总结,总计 50 页。 一、技术总结 1.Turing Machine ™ 2.undecidability ​ a.Ld(the diagonalization language) 3.reduction p392, In general, if we have an algorithm to convert insta…

GpuMall的GPU算力资源池化技术有何优势?

GpuMall的GPU算力资源池化技术具有显著的优势,这些优势使得其在智算云领域脱颖而出,为用户提供了高效、灵活且可靠的GPU算力服务。以下是GpuMall GPU算力资源池化技术的主要优势: GpuMall智算云 | 省钱、好用、弹性。租GPU就上GpuMall,面向A…

面向未来:等保测评与持续改进

随着信息技术的不断进步和网络环境的日益复杂,网络安全正面临着前所未有的挑战。等保测评作为提升网络安全管理水平的重要手段,不仅需要应对当前的安全威胁,更应着眼于未来的安全需求。本文将探讨等保测评在持续改进中的角色和实施策略。 ##…

Git系列:config 配置

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…