AST原理(反混淆)

news2024/5/18 19:29:26
一、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 作为 body
            const functionExpression = t.functionExpression(
              null, // id
              path.node.params, // params
              blockStatement, // body
              false, // generator
              false // 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.node
        let data = callee.object.value
        let func = callee.property.value
        let arg = arguments[0].value
        var 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.coloradmin.cn/o/1642172.html

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

相关文章

【Linux】 OpenSSH_7.4p1 升级到 OpenSSH_9.6p1(亲测无问题,建议收藏)

👨‍🎓博主简介 🏅CSDN博客专家   🏅云计算领域优质创作者   🏅华为云开发者社区专家博主   🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入&#xff01…

【linuxC语言】vfork、wait与waitpid函数

文章目录 前言一、函数使用1.1 vfork1.2 wait1.3 waitpid 二、示例代码总结 前言 在Linux系统编程中,vfork()、wait() 和 waitpid() 函数是处理进程管理和控制流的重要工具。这些函数允许我们创建新进程、等待子进程结束并获取其退出状态,从而实现进程间…

【CAP探索者指南】掌握分布式世界的三角平衡术,一致性、可用性、分区容错性大揭秘!

关注微信公众号 “程序员小胖” 每日技术干货,第一时间送达! 引言 在现代的微服务架构中,系统被拆分成了许多小型服务,每个服务可能有自己的数据库。这种架构带来了灵活性和可扩展性,但也引入了新的挑战,…

高质量数据至关重要:phi-1.5论文笔记

导语 phi-系列模型是微软研究团队推出的轻量级人工智能模型,旨在实现“小而精”的目标,能够实现在低功耗设备上例如智能手机和平板电脑上部署运行。截止目前,已经发布到了phi-3模型,本系列博客将沿着最初的phi-1到phi-1.5&#x…

深入浅出 BERT

Transformer 用于学习句子中的长距离依赖关系,同时执行序列到序列的建模。 它通过解决可变长度输入、并行化、梯度消失或爆炸、数据规模巨大等问题,比其他模型表现更好。使用的注意力机制是神经架构的一部分,使其能够动态突出显示输入数据的…

功能测试_分类_用例_方法

总结 测试分类 按阶段分类 是否查看源代码分类 是否运行分类 是否自动化 其他分类 软件质量模型 开发模型-瀑布模型 测试过程模型 v w 测试用例八大要素 用例编号 用例标题 …

DHCPv4_CLIENT_ALLOCATING_07: 发送DHCPDECLINE消息并重新启动配置过程

测试目的: 验证当DOIP客户端检测到分配的IP地址已被使用时,能够发送DHCPDECLINE消息给服务器并重新启动配置过程。 描述: 本测试用例旨在模拟DOIP客户端在接收到DHCP服务器分配的IP地址后,通过地址解析协议(ARP&…

华为LTC线索与回款中的线索培育工具:9格构想

在《LTC与铁三角∶从线索到回款-人民邮电.》一书中,说到线索的管理,书中的9格构想不错,收藏之: 九格构想这一工具的使用顺序依次是诊断原因、探究 影响、构想能力。 1. 诊断原因( R1-R2-R3 ) 企业应先用…

Claude聊天机器人推出全新iOS客户端及团队专属计划

Anthropic 正在使其 Claude AI 更易于在移动设备上访问。该公司发布了适用于 iOS 的 Claude 移动应用程序,任何用户都可以免费下载。与聊天机器人的移动网络版本类似,该应用程序跨设备同步用户与 Claude 的对话,允许他们从计算机跳转到应用程序(反之亦然),而不会丢失聊天…

MATLAB数值类型

MATLAB 数值 MATLAB支持各种数字类,包括有符号和无符号整数以及单精度和双精度浮点数。默认情况下,MATLAB将所有数值存储为双精度浮点数。 您可以选择将任何数字或数字数组存储为整数或单精度数字。 所有数值类型都支持基本数组运算和数学运算。 转换…

数据结构篇其四---栈:后进先出的魔法世界

前言 栈的学习难度非常简单,前提是如果你学过顺序表和单链表的话,我直接说我的观点了,栈就是有限制的顺序表和单链表。 栈只允许一端进行插入删除。栈去除了各种情况复杂的插入删除,只允许一端插入删除的特性,这一种数…

Pytorch基础:torch.load_state_dict()方法在加载时不会检查类型

相关阅读 Pytorch基础https://blog.csdn.net/weixin_45791458/category_12457644.html?spm1001.2014.3001.5482 笔者在使用torch.nn.module的load_state_dict中出现了一个问题,一个被注册的张量在加载后居然没有变化,一开始以为是加载出现了问题&#…

ATTCK的优缺点分别是什么

ATT&CK(Adversarial Tactics, Techniques, and Common Knowledge)框架是一个广泛使用的资源,它提供了对网络威胁的深入洞察,特别是关于攻击者可能采取的战术、技术和程序(TTPs)。以下是ATT&CK框架的优缺点: 优点: 全面的威胁情报:ATT&CK框架详细描述了各种…

Linux基础之gcc/g++

目录 一、gcc/g的介绍 二、一个程序的翻译过程 2.1 预处理阶段 2.2 编译阶段 2.3 汇编阶段 2.4 链接阶段 三、动静态库简介 四、动静态库的优缺点 一、gcc/g的介绍 首先,先简单的介绍一下gcc/g。 GCC(GNU Compiler Collection)是一个…

MySql安装到配置-超详细版

哈喽宝子们,好久不见,大一五一有没有出去玩呀~反正我是没有出去,就5月1号那天晚上跟室友去看了个电影,然后这几天基本都在宿舍“卷”,其实也不是啦,就是学习学习,因为一方面,暑期实习…

debian10 (armbian) 配置CUPS 服务

更新apt apt-update安装相关软件 apt-get install ghostscript apt-get install dc apt-get install foomatic-db-engine apt-get install cups3.修改配置文件 nano /etc/cups/cupsd.conf Listen localhost:631改为 Listen 0.0.0.0:631 以下四段配置加入Allow All # Only li…

大模型公开可用的模型检查点或 API

文章目录 公开可用的模型检查点或 APILLaMA 变体系列大语言模型的公共 API 公开可用的模型检查点或 API 众所周知,大模型预训练是一项对计算资源要求极高的任务。因此,经过预训练的公开模型检查点(Model Checkpoint)对于推动大语言…

2024牛客五一集训派对day2 Groundhog Looking Dowdy 个人解题思路

前言: 被实验室教练要求要打的这次五一牛客的训练赛,这些区域赛难度的题对于大一的我来说难度实在是太高了,我和我的队友只写了一些非常简单的签到题,其他题目都没怎么看(我们太弱了),但我可以分…

Spring Cloud学习笔记(Hystrix):execute,queue,observe,toObservable样例和特性

这是本人学习的总结,主要学习资料如下 - 马士兵教育 1、Overview2、execute()2.1、Overview2.2、示例 3、queue()3.1、Overview3.2、示例 4、observe()4.1、Overview4.2、示例 5、toObservable()5.1、observe()和toObservable()的区别 1、Overview 我们知道Hystrix…

一文看懂卷积神经网络CNN(1)—前馈神经网络

目录 参考资料 一、神经网络 1、人脑神经网络 2、人工神经网络 3、神经网络的发展历史 二、前馈神经网络 1、神经元 (1)Sigmoid型函数 ① Logistic函数 ②Tanh函数 ③两个函数形状对比 (2)ReLU函数 ① 带泄露的ReLU函…