一. Ast - 反混淆(基础篇)

目录

​编辑

1. 什么是Ast-了解

2. 什么是反混淆-了解

3. 了解Ast结构

4. 思考

5. 前置准备

6. 什么是babel

7. 安装babel

8. ast反混淆代码基本结构

9. babel中的组件

parser与generator 组件

traverse 组件 与visitor

enter与exit

visitor 其他写法

traverse 指定节点向下遍历

types组件

10. path对象

path和node

path常用属性和方法

常用属性:

常用方法:

node节点

scope常用属性和方法

binding常用属性和方法

11. AST 节点类型对照表


AST抽象语法树在线转换工具:AST explorer

  • 这个网站作用:将代码转换成抽象语法树

1. 什么是Ast-了解

  • AST是抽象语法树的缩写。
  • AST是一种用于表示程序代码结构的树状数据结构。
  • 在编译器和解释器中,AST被用于解析和表示源代码的语法结构
  • AST可以看作是源代码的一种抽象表示形式,它去除了源代码中的具体细节,只保留了语法结构和逻辑关系
  • AST中每个节点(Node) 表示源代码的一个语法元素,例如:变量声明,函数定义,循环语句,等,而节点之间的关系表示了语法结构的层次和关联关系
  • 作用:
    • 通过构建AST,编译器解析器可以对源代码进行分析和处理
    • 编译器可以在AST上进行语法检查、类型检查和优化等操作
    • 解析器可以根据AST生成中间代码或目标代码
    • AST还能用于代码重构,代码生成等领域

以下是将正常代码通过在线AST在线网站进行转换为AST语法树

2. 什么是反混淆-了解

什么是混淆?

  • 混淆可以理解为是一种代码加密技术,主要用于隐藏代码的真实功能,以防止代码被逆向工程师分析和修改。通过混淆,让代码变得复杂和难以理解,使得逆向工程师在调试工程中消耗大量的时间或者放弃,从而达到一种保护
  • 混淆总的来说就是一种代码保护方案,将原始代码转换为可读性较差或者没有可读性的代码🤢

什么是反混淆?

  • 反混淆就是将混淆后的代码,还原成具有可读性代码,方便逆向工程师进行调试。

混淆是一种艺术

3. 了解Ast结构

AST在线转换为抽象语法树的网站:AST explorer

  • 这个网站在反混淆中经常用到,因为我们要将被混淆的代码放到上面,然后转换为抽象语法树,然后对抽象语法树的结构进行分析。从而编写还原混淆的代码

在线解析工具使用:

  1. 打开网站后,将parser settings(解析器设置),设置成 @babel-parser,因为我们编写的反混淆代码使用的是 @babel 库,所以选择@babel/parser。在代码中@babel/parser的用处也就是将javascript代码转换为ast语法树结构
  2. 左侧的空白区域我们可以输入一些javascript代码(没混淆的或者混淆后的代码都可以),然后在右侧会展示转换好的ast抽象语法树
  3. 在左侧输入: console.log("0基础菜鸟学习")
  4. 在右侧展示抽象语法树结构的时候,默认的是 树 结构,我们也可以转换为 JSON 结构

节点解释:

  1. type: 表示当前节点的类型
  2. start:表示当前节点的起始位置,图片中的start为0,起始位置是从 c 前面开始起算
  3. end:表示当前节点的末尾
  4. loc:表示当前节点所在的行列位置
    1. loc中的start,表示节点所在起始的行列位置
    2. loc中的end,表示节点所在末尾的行列位置

节点解释:

  • ExpressionStatement表示为当前的代码是一个console.log()
  • ExpressionStatement中会有一个expression的子节点,expression节点的类型是 CallExpression,
  • expression节点中又包含了callee和arguments两个节点,callee可以理解为函数名,而arguments这是它的参数
  • callee节点包含了object,property和computed节点
    • object表示对象,当前对象为 console
    • property表示属性,当前属性为 log
    • computed表示其方式

在后续的ast反混淆编写中,就是将原有的节点进行增删改查。然后得到一个新的代码

4. 思考

场景1:遇到以下情况的时候可以复制粘贴到控制台输出拿到正确值,但是一般不会有这样的场景,因为混淆不可能只混淆一点,而是整个文件代码都混淆,所以想多了

场景2:但是如果遇到这样的场景,如果手动一个一个复制到控制台解开,会很浪费时间,所以学习Ast的用处就是将混淆代码进行批量还原

5. 前置准备

  1. 具备一些javascript基础和nodejs基础
  2. 已安装好nodejs运行环境

6. 什么是babel

  1. 在ast中,babel是一个广泛使用的JavaScript编译器工具。
  2. 它可以将高级的JavaScript代码转换为向后兼容的版本,以便在更旧的浏览器或环境中运行
  3. Babel使用AST(抽象语法树)来分析和修改代码。
  4. 通过在各个阶段插入插件,Babel可以执行各种转换操作,如转换ES6代码为ES5代码、添加Polyfills和补丁、优化代码等。
  5. Babel的主要目标是提供一个灵活且可扩展的工具,以适应不同的项目需求,并帮助开发者更好地利用新的JavaScript语言特性。

7. 安装babel

  • 安装命令:
    • npm i @babel/core --save-dev
    • npm i @babel/types 判断节点类型,构建新的AST节点等
    • npm i @babel/parser 将Javascript代码解析成AST语法树
    • npm i @babel/traverse 遍历,修改AST语法树的各个节点
    • npm i @babel/generator 将AST还原成Javascript代码

8. ast反混淆代码基本结构

以下是使用babel库来反混淆代码的模板

// fs模块 用于操作文件的读写
const fs = require("fs");
// @babel/parser 用于将JavaScript代码转换为ast树
const parser = require("@babel/parser");
// @babel/traverse 用于遍历各个节点的函数
const traverse = require("@babel/traverse").default;
// @babel/types 节点的类型判断及构造等操作
const types = require("@babel/types");
// @babel/generator 将处理完毕的AST转换成JavaScript源代码
const generator = require("@babel/generator").default;


// 混淆的js代码文件
const encode_file = "./encode.js"
// 反混淆的js代码文件
const decode_file = "./decode.js"


// 读取混淆的js文件
let jsCode = fs.readFileSync(encode_file, {encoding: "utf-8"});
// 将javascript代码转换为ast树(json结构)
let ast = parser.parse(jsCode)

// todo 编写ast插件
const visitor = {

}

// 调用插件,处理混淆的代码
traverse(ast,visitor)

// 将处理后的ast转换为js代码(反混淆后的代码)
let {code} = generator(ast);
// 保存代码
fs.writeFile('decode.js', code, (err)=>{});

9. babel中的组件

parser与generator 组件

  • parser和generator这两个组件的作用是相反的。
  • parser用于将js代码转换成ast,generator用于将ast转换成js代码
  • parser将代码转换为ast:
    • let ast = parser.parse(参数一,参数二)
    • 参数一:混淆的js代码
    • 参数二:配置参数
      • sourceType: 默认是script,当解析的js代码中,含有 import,export 等关键字的时候需要指定sourceType为module,不然会报错
  • generator将ast转换为代码:
    • let code = generator(参数一,参数二)
    • 参数一:ast语法树
    • 参数二:配置参数
      • retainLines:表示是否使用与源代码相同的行号,默认为false,也就是输出的是格式化后的代码
      • comments:表示是否保留注释,默认为true
      • compact:表示是否压缩代码,与其相同作用的选项还有minified,concise只不过压缩的程度不一样,minified压缩的最多,concise压缩的最少。
  • parser和generator集合使用的示例代码:
    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;
    
    
    const encode_file = "./encode.js"
    const decode_file = "./decode.js"
    
    
    let jsCode = fs.readFileSync(encode_file, {encoding: "utf-8"});
    // 将js代码转换为ast
    let ast = parser.parse(jsCode,{
        sourceType:"script"
    })
    
    // todo 编写ast插件
    const visitor = {
    
    }
    
    traverse(ast,visitor)
    
    let {code} = generator(ast,{
        retainLines:false,
        comments:true,
        compact:"concise"
    });
    fs.writeFile('decode.js', code, (err)=>{});
    

traverse 组件 与visitor

  • traverse 用于遍历和转换抽象语法树(AST)的工具,转换语法树需要配置visitor使用
  • visitor 是一个对象,里面可以定义一些方法,用来过滤节点
    • visitor 示例代码:
      • 1. 在代码中首先声明了visitor对象,对象的名字可以任意取。
      • 2. 在visitor对象中定义一个名为ExpressionStatement的方法,这个方法的名字是需要遍历的是节点类型。
      • 3. traverse会遍历所有节点,当节点类型为 ExpressionStatement时,调用visitor中对应的方法。
      • 4. 如果想要处理其他节点类型,那么可以继续在visitor中继续定义对应的方法
      • 5. visitor 对象中的方法接收一个参数,traverse在遍历的时候会把当前节点的Path对象传给它,传进去的Path对象不是节点node
      • 6. 最后把visitor作为第二个参数传入traverse中,传给traverse的第一个参数是整个ast。
      • traverse(ast,visitor) 的意思是,从头开始遍历ast中的所有节点,过滤出ExpressionStatement节点,执行相应的方法。在ast中如果有多个ExpressStatement就会输出对应的次数

enter与exit

  • 在遍历节点的过程中,实际上有两次机会来访问节点,enter表示进入节点时,exit表示退出节点时。可以在代码中编写遍历时进入要做的操作和退出时要做的操作。

visitor 其他写法

一个函数同时处理两个节点:

  • 可以把方法名用 | 连接成FunctionExpression|BinaryExperssion形式的字符串,把同一个函数应用到多个节点,也就是说函数中的功能可以同时处理FunctionExpression|BinaryExpression节点

多个函数处理一个节点:

  • 把多个函数应用于同一个节点。把函数赋值给enter或exit,将enter改为接收一个函数数组就行

traverse 指定节点向下遍历

  • traverse 可以指定在任意节点向下遍历。
  • 例如,想要把代码中所有函数的第一个参数改为 x

types组件

  • types组件,主要用于判断节点类型,生成新的节点

判断节点:

  • 方式1:不推荐的判断节点类型写法
  • 方式2:
    • 使用types组件提供的节点判断方式,存在返回true,不存在返回false
    • 语法格式:types.is节点名称

生成新的节点:

  • 生成新的节点,然后使用generator组件转换为代码
  • 也可以按照AST的结构来构造一段json数据。然后生成代码,不过还是推荐使用types组件来生成

10. path对象

path和node

path和node区别在于,path对象中包含node。node只是path对象中的一个属性。

path常用属性和方法

常用属性:

  • path.node 获取当前路径对应的节点
  • path.parent 获取当前路径对应节点的父节点
  • path.parentPath 获取当前路径对应节点的父路径
  • path.scope 表示当前path下的作用域,写插件经常用到
  • path.container 获取当前path下的所有兄弟节点(包括自身)
  • path.type 获取当前path的节点类型
  • path.key 获取当前path的key值,key通常用于path.get函数

常用方法:

  • path.get(key) 获取当前路径下指定属性名(key),对应的子路径。
    • 例如:path.get("body") 获取当前路径下名为 body 的子路径
  • path.getSibling(index)    获取当前路径对应节点的兄弟节点的路径。通过指定索引(index)可以获取相应的兄弟路径。
  • path.getFunctionParent()    获取当前路径对应节点的最近的函数父节点的路径。
  • path.getPrevSibling()    获取当前path的前一个兄弟节点,返回的是path类型。
  • path.getAllPrevSiblings()    获取当前path的所有前兄弟节点,返回的是Array类型,其元素都是path类型。
  • path.getNextSibling()    获取当前path的后一个兄弟节点,返回的是path类型。
  • path.getAllNextSiblings()    获取当前path的所有后兄弟节点,返回的是Array类型,其元素都是path类型。
  • path.evaluate()    用于计算表达式的值,大家可以参考 constantFold 插件的写法。
  • path.findParent()    向上查找满足回调函数特征的path,即判断上级路径是否包含有XXX类型的节点。
  • path.find()    功能与 path.findParent 方法一样,只不过从当前path开始进行遍历。
  • path.getFunctionParent()    获取函数类型父节点,如果不存在,返回 null。
  • path.getStatementParent()    获取Statement类型父节点,这个基本上都会有返回值,如果当前遍历的是 Program 或者 File 节点,则会报错
  • path.getAncestry()    获取所有的祖先节点,没有实参,返回的是一个Array对象。
  • path.isAncestor(maybeDescendant)    判断当前遍历的节点是否为实参的祖先节点.
  • path.isDescendant(maybeAncestor)    判断当前遍历的节点是否为实参的子孙节点.
  • path.traverse(visitor)    遍历当前路径下的所有子节点,并应用指定的 visitor。
  • path.replaceWith(node)    用指定的节点替换当前路径对应的节点。
  • path.remove()    从 AST 中移除当前路径对应的节点。
  • path.insertBefore(nodes)    在当前路径对应节点之前插入一个或多个节点。
  • path.insertAfter(nodes)    在当前路径对应节点之后插入一个或多个节点。
  • path.toString()    用于将 AST 节点转换回对应的源代码字符串。

node节点

  • node节点是path对象中的一个属性
  • node节点中常用的属性:
    • path.node.type 获取当前节点类型
    • path.node.declarations    对于 VariableDeclaration 节点, 获取变量声明列表。
    • path.node.init.value    获取某个节点的值。
    • delete path.node.init;    删除节点,使用系统的 delete 方法。

scope常用属性和方法

  • scope.block    表示当前作用域下的所有node
  • scope.dump()    输出当前每个变量的作用域信息。调用后直接打印,不需要加打印函数
  • scope.crawl()    重构scope,在某种情况下会报错,不过还是建议在每一个插件的最后一行加上。
  • scope.rename(oldName, newName, block)    修改当前作用域下的的指定的变量名,oldname、newname表示替换前后的变量名,为字符串。注意,oldName需要有binding,否则无法重命名。
  • scope.traverse(node, opts, state)    遍历当前作用域下的某些(个)插件。和全局的traverse用法一样。
  • scope.getBinding(name)    获取某个变量的binding,可以理解为其生命周期。包含引用,修改之类的信息

binding常用属性和方法

  • binding.path    用于定位初始拥有binding的path;
  • binding.constant    用于判断当前变量是否被更改,true表示未改变,false表示有更改变量值。
  • binding.referenced    用于判断当前变量是否被引用,true表示代码下面有引用该变量的地方,false表示没有地方引用该变量。注意,引用和改变是分开的。
  • binding.referencePaths    它是一个Array类型,包含所有引用的path,多用于替换。

11. AST 节点类型对照表

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python API和微服务的测试库之httpretty使用详解

概要 在现代软件开发中,API和微服务的测试是确保应用稳定性和功能正确性的关键环节。Python的HTTPretty库提供了一个强大的工具,允许开发者在不实际发起网络请求的情况下模拟HTTP请求和响应。本文将全面介绍HTTPretty的安装、特性、基本与高级功能,并结合实际应用场景,展示…

FebHost:什么是汤加.TO域名?

什么是.TO域名? 汤加域名是指在顶级域.to下注册的域名,.to域名是汤加的国家代码顶级域(ccTLD)。它类似于其他特定国家的域名后缀,如美国的”.us “或英国的”.uk”。 汤加国家介绍 汤加正式名称为汤加王国&#xff…

Java入门基础学习笔记19——关系运算符、逻辑运算符

关系运算符: 判断数据是否满足条件,最终会返回一个判断的结果,这个结果是布尔类型的值:true或false。 注意:在java中判断是否相等一定是“”,不要把“”写成“”,“”是赋值表达式。 package c…

【kubeflow文档】kubeflow介绍与架构

1. kubeflow介绍 Kubeflow项目致力于使机器学习(ML)工作流在Kubernetes上的部署变得简单、可移植和可扩展。目标不是重新创建其他服务,而是提供一种直接的方法,将ML的开源系统部署到不同的基础设施中。无论在哪里运行Kubernetes&a…

2.1 软件工程

第2章 信息技术知识 2.1 软件工程 现状: 开发软件的规模越来越大复杂度越来越高用户需求并不十分明确缺乏软件开发方法和工具方面的有效支持 软件成本日益增长、开发进度难以控制、软件质量无法保证、软件维护困难等问题日益突出。人们开始用工程的方法进行软件…

K8S -----二进制搭建 Kubernetes v1.20

目录 一、准备环境 1.1 修改主机名 1.2 关闭防火墙(三台一起,这里只展示master01) 1.3 在master添加hosts(依旧是三台一起) 1.4 调整内核参数并开启网桥模式 二、部署docker引擎 三、部署 etcd 集群 1.在mast…

水电站机组油压自动化控制系统概述及优势介绍

一、系统背景 我国河流、湖泊分布广泛,落差巨大,蕴藏着丰富的资源优势,我国作为世界第二大能源消耗国,对于电力的需求是巨大的,水力发电具有高效、清洁、能量供给稳定充足的特点,因此,水电工程…

基础ArkTS组件:数据面板组件(图表),日期选择器组件(HarmonyOS学习第三课【3.5】)

tuoz数据面板组件 DataPanel 数据面板组件,用于将多个数据占比情况使用占比图进行展示。 说明 该组件从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 子组件 无 接口 DataPanel(options:{values: numbe…

19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

基础知识要求: Java:方法、while循环、for循环 Python: 方法、while循环、for循环 题目: 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。 示例 1: 输入:head […

深入了解 MyBatis 插件:定制化你的持久层框架

序言 MyBatis 是一个流行的 Java 持久层框架,它提供了简单而强大的数据库访问功能。然而,有时候我们需要在 MyBatis 中添加一些自定义的功能或行为,来满足特定的需求。这时,MyBatis 插件就发挥了重要作用。本文将深入探讨 MyBati…

【C++】深度解析:用 C++ 模拟实现 String 类,探索其底层实现细节

目录 了解string类 string的内存管理 VS下string的结构 ​g下string的结构 string的模拟实现 string的构造函数 浅拷贝 深拷贝 string的遍历 重载 [] 下标访问 迭代器访问 reserve resize 增删查改 push_back() append和 insert和erase find substr swap 流插入…

一个完美的回到顶部按钮

大家好,我是 Just,这里是「设计师工作日常」,今天给大家写了一个丝滑回到顶部的按钮,原生js实现的,兼容性所有主流浏览器,可在vue中使用,适用于网页、h5等。 最新实例通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码js 部分代码完整代…

网络网络层之(5)IPv6协议

网络网络层之(5)IPv6协议 Author: Once Day Date: 2024年5月12日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文档可参考专栏:通信网络技术_Once-Day…

啥都吃的打字练习软件TL(TypeLetters)

今天给大家安利一款啥都吃的打字练习软件——TL(TypeLetters)。 为什么说TL啥都吃呢?很简单,因为不管是科技的、还是时事的、或者潮流的、或者热点的,凡是英文的资料TL都能通通吃下去,所以说TL是来者不拒&…

QT C++ widget layout 嵌套 例子2

在上篇文章中描述了实中套虚(用setLayout),虚中套实(用addWidget)。 本文再加1条,虚中套虚(用addLayout)。 所谓虚中套虚,是layout 套 layout 。 另外用循环代码生成从…

新书速览|MATLAB科技绘图与数据分析

提升你的数据洞察力,用于精确绘图和分析的高级MATLAB技术。 本书内容 《MATLAB科技绘图与数据分析》结合作者多年的数据分析与科研绘图经验,详细讲解MATLAB在科技图表制作与数据分析中的使用方法与技巧。全书分为3部分,共12章,第1…

【鸿蒙+全国产瑞芯微】智慧楼宇解决方案 | 如何实现多场景下智慧化、精细化楼宇管理?

随着数字化、智能化与工作生活的联结日渐紧密,聚焦人性化服务,以数字和科技匹配多重需求,加速商业楼宇智能化转型的脚步,逐步形成智慧楼宇产品矩阵。 方案亮点 01/数字标牌——形象展示 企业文化宣传、公告通知等 播放内容统一远…

blender 制作圆角立方体模型,倒角实现。cocos 使用。导出fbx

图片: 步骤: 1.首先创建一个立方体,这里可以使用默认的立方体。 2.在属性面板选择如“扳手”图标一样的修改器工具。 3.设置数量和段数实现圆角的圆滑效果,没有菱角。 保存导出相关的教程:

object.key()用法

object.key(obj) 一、概念:返回一个由一个给定对象的自身可枚举属性组成的数组。 二、用法: 1、参数为对象:则返回为 对象属性名组成的数组。 let obj {日期:date,姓名:userName,地址:address}console.log(Object.k…

使用Go和JavaScript爬取股吧动态信息的完整指南

引言 在现代金融生态系统中,信息流动的速度和效率对于市场的健康和投资者的成功至关重要。股市信息,特别是来自活跃交流平台如股吧的实时数据,为投资者提供了一个独特的视角,帮助他们洞察市场趋势和投资者情绪。这些信息不仅能够…