AST反混淆实战:提升JavaScript代码的可读性与调试便利性

博客标题:AST反混淆:提升JavaScript代码的可读性与调试便利性

引言

JavaScript代码混淆是一种常见的保护源码的方法,但这也给代码的维护和调试带来了不小的挑战。抽象语法树(AST)提供了一种结构化的方式来分析和转换代码。本文将通过一个具体的案例,展示如何使用AST进行代码反混淆的具体流程。

AST基础知识

AST是源代码的树状结构表示,每个节点代表了代码中的一个构成元素,如表达式、语句等。使用AST,我们可以方便地对代码进行遍历和变换。

准备工作

首先,我们需要安装@babel/parser@babel/traverse@babel/generator@babel/types这几个npm包,它们将帮助我们解析代码、遍历AST、生成新的代码以及操作AST节点。

npm install @babel/parser @babel/traverse @babel/generator @babel/types

AST反混淆的目标

AST反混淆的目的是为了使代码更加易于阅读和调试。具体目标包括:

  • 代码可读性:将混淆后的代码转换为清晰、易于理解的形式。
  • 调试便利性:简化调试过程,使开发者能够快速定位问题js。

AST反混淆的代码分析步骤

1 判断混淆的类型,找到混淆的位置
是字符串编码的混淆、赋值操作混淆还是表达式混淆等
任何技术都是有限的,没有统一的解混淆的办法,只有具体问题具体分析。查看js代码的语法树,分析混淆的类型就和写Xpath解析找父节点还是兄弟节点一样。

2 明确解混淆后的js代码

AST解析网址
https://astexplorer.net/

AST反混淆的代码实现步骤

1. 读取JS文件并转化为AST

首先,我们需要读取混淆后的JavaScript文件,并使用@babel/parser将其转换为AST语法树。这一步是整个反混淆过程的基础。

let jsCode = fs.readFileSync('path/to/obfuscated.js', 'utf-8');
let ast = parser.parse(jsCode);

2. 遍历AST并进行增删改查

接下来,我们使用@babel/traverse遍历AST语法树。在遍历过程中,我们可以对节点进行增加、删除、修改等操作,以实现代码的反混淆,这里每种混淆都不一样,具体问题具体分析,每个人思路也不完全一样。

  • 增加:为代码添加注释或辅助信息,提高可读性。
  • 删除:移除无用的代码或混淆的部分。
  • 修改:将混淆的变量名、属性名等替换为更易读的形式。
  • 查询:分析代码结构,为后续的修改提供依据。

3. 将AST重新转化为JS代码

最后,我们使用@babel/generator将修改后的AST语法树重新转换为JavaScript代码。这样,我们就得到了可读性更强、更方便调试的代码。

const fs = require('fs');
let result = generator(ast).code;
fs.writeFileSync('path/to/demangled.js', result, 'utf-8');

案例1 表达式混淆

我们有一段混淆后的JavaScript代码,其目的是将几个字符串拼接赋值给变量f,然后通过f访问全局对象G的一个属性。我们的任务是使用AST来反混淆这段代码,使其更加易于阅读和调试。

具体解混淆流程

步骤1:读取并解析代码为AST

首先,我们读取混淆后的JavaScript代码,并使用@babel/parser将其转换为AST语法树。

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;
const types = require('@babel/types');
const fs = require('fs');let jsCode = `f = "cdc_adoQpoasnfa76pfcZLmcf";
f += "cd";
f += "qwe";
f += "qwe";
f += "qad";
f += "\u0065\u006e\u0075\u006d\u0065\u0072\u0061\u0062\u006c\u0065";
C = G[f += "_Array"];`; // 混淆后的JavaScript代码
let ast = parser.parse(jsCode);

步骤2:定义变换逻辑

接着,我们定义变换逻辑,使用@babel/traverse遍历AST。我们将关注两个节点类型:MemberExpressionAssignmentExpression

  • MemberExpression:处理成员表达式,例如G[f]
  • AssignmentExpression:处理赋值表达式,例如f += "cd"

步骤3:处理MemberExpression(成员函数对象)

我们遍历AST,寻找MemberExpression节点。如果属性名是_Array,我们将这个属性名追加到前一个表达式的值上,并移除当前的MemberExpression

traverse(ast, {'MemberExpression'(path){let {object, property} = path.node;let {left, operator, right} = property;let prevExp = path.parentPath.parentPath.getPrevSibling().node;prevExp.expression.right.value += right.value;path.node.property = left;}
})

步骤4:处理AssignmentExpression(赋值操作对象)

然后,我们继续遍历AST,寻找AssignmentExpression节点。如果右侧是字符串字面量,并且操作符是+=,我们将这个字符串追加到前一个赋值表达式的值上,并移除当前的赋值表达式。

traverse(ast, {'AssignmentExpression'(path){let {left, operator, right} = path.node;if (!types.isStringLiteral(right) || operator != '+='){return}let value = right.value;let prevExp = path.parentPath.getPrevSibling().node;prevExp.expression.right.value += right.value;path.remove()}
})//移除当前的赋值表达式。

在这里插入图片描述

步骤5:生成新的代码

最后,我们使用@babel/generator将修改后的AST语法树重新转换为JavaScript代码。

let result = generator(ast).code;
console.log(result);

在这里插入图片描述


示例二:属性名解码

代码分析

这段代码中的对象属性名被编码,我们的目标是将其解码为可读的形式。

// ast反混淆目的为了代码可读性更强,更方便调试。
//
// 1. 读取js文件,使用parser.parse转化为ast语法树
//
// 2. 遍历ast,增删改查ast语法树 path.node
//
// 3. 重新把ast转化为js代码。
const types = require('@babel/types');
const parser = require('@babel/parser');
const generator = require('@babel/generator').default;
const traverse = require('@babel/traverse').default;let jsCode = `
a = {"\u0065\u006e\u0075\u006d\u0065\u0072\u0061\u0062\u006c\u0065": !0,"\u0067\u0065\u0064": n
}
`;let ast = parser.parse(jsCode);// 遍历AST,解码属性名
traverse(ast, {'StringLiteral': function(path) {let decoded = decodeURIComponent(path.node.value);path.node.value = decoded;}
});// 打印反混淆后的代码
console.log(generator(ast).code);

为什么是StringLiteral,因为这里混淆的类型type是Literal
在这里插入图片描述

知识点总结

在JavaScript的抽象语法树(AST)中,不同的节点类型代表了代码中的各种语法结构。

  1. MemberExpression

    • 表示对对象成员的访问,包括属性和方法。例如,在表达式 obj.propobj.method() 中,obj.propobj.method() 都是 MemberExpression 节点。
  2. AssignmentExpression

    • 表示赋值操作。这是基本的赋值(如 x = y),复合赋值(如 x += y),或扩展赋值(如 x ??= y)的语法结构。在 AssignmentExpression 节点中,赋值运算符连接了左侧的变量(或解构模式)和右侧的值或表达式。
  3. StringLiteral

    • 表示字符串字面量。这是用双引号 " 或单引号 ' 包围的文本,例如 "hello"'world'。在AST中,StringLiteral 节点包含了字符串的值。

除了这些,还有一些常用的AST节点类型:

  • Identifier:表示一个变量名或标识符,如 xy
  • BinaryExpression:表示二元表达式,它包含两个操作数和一个运算符,如 x + yx > y
  • CallExpression:表示函数或方法的调用,如 func()obj.method()
  • FunctionDeclaration / FunctionExpression:两者都表示函数,区别在于 FunctionDeclaration 是通过 function 关键字声明的,而 FunctionExpression 是通过赋值给变量的函数表达式。
  • ReturnStatement:表示 return 语句,用于从函数中返回一个值。
  • IfStatement:表示 if 条件语句。
  • ForStatement / WhileStatement:表示循环结构,如 forwhile 循环。

理解这些AST节点类型对于进行代码分析、转换或生成等操作非常重要,特别是在使用编译器工具如Babel时。

结语

通过上述步骤,我们成功地将一段混淆的JavaScript代码反混淆,使其更加易于阅读和调试。掌握这项技能对于前端和逆向开发者来说非常有价值。AST为我们提供了一种强大的方式来分析和转换代码,这不仅有助于提高代码质量,还能在开发过程中节省大量的时间和精力。掌握AST的使用,对于前端开发者来说是一项宝贵的技能。

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

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

相关文章

平安好车主:“保”你车平安,“养”出好生活~

“小朋友 你是否有很多问号,为什么......”从出生到长大,不论我们身居何处,年岁几何,妈妈似乎总有嘱咐不完的话。小时候,总不能理解妈妈的话,只想摆脱唠叨,期盼快快长大。 如今,我们羽翼渐丰,已能驾驭人生,肩负起家庭的重任,但妈妈的话却依然从未落下。不过,此刻的我们,不仅能…

Gitea 仓库事件触发Jenkins远程构建

文章目录 引言I Gitea 仓库事件触发Jenkins远程构建1.1 Jenkins配置1.2 Gitea 配置引言 应用场景:项目部署 I Gitea 仓库事件触发Jenkins远程构建 Gitea支持用于仓库事件的Webhooks 1.1 Jenkins配置 高版本Jenkins需要关闭跨域限制和开启匿名用户访问 在Jenkins启动前加入…

STM32入门开发操作记录(一)——新建工程

目录 一、课程准备1. 课程资料2. 配件清单3. 根目录 二、环境搭建三、新建工程1. 载入器件支持包2. 添加模块3. ST配置4. 外观设置5. 主函数文件 一、课程准备 1. 课程资料 本记录操作流程参考自b站视频BV1th411z7snSTM32入门教程-2023版 细致讲解 中文字幕,课程资…

柯桥韩语培训韩语学习力职场口语韩语中的职场黑话你知道几个?

生活中比较常用的,与职场生活有关的新造词有상사병, 직장살이, 무두절(無頭節)等。一起来看下他们的意思吧... 상사병 상사병是指因为上司多变不定的指示而火大的意思。 직장살이 직장살이用来比喻职场生活也需要看上司的脸色,就像在婆家看婆婆脸色一样…

gorm只查询某一些字段字段的方法Select, 和只查询某一字段方法 Pluck

gorm中默认是查询所有字段的, 如果我们只需要获取某些字段的值,可以通过使用 Select方法来指定要查询的字段来实现, 也可以通过定义一个需要字段的结构体来实现; 而如果我们只需要查询某一个字段的值就可以使用 Pluck方法来获取(这…

【刷题汇总 -- 删除公共字符、两个链表的第一个公共结点、mari和shiny】

C日常刷题积累 今日刷题汇总 - day0121、删除公共字符1.1、题目1.2、思路1.3、程序实现 -- 蛮力法1.4、程序实现 -- 哈希 2、两个链表的第一个公共结点2.1、题目2.2、思路2.3、程序实现 -- 对齐比对法2.4、程序实现 -- 公共端点路程法 3、mari和shiny3.1、题目3.2、思路3.3、程…

[python]基于yolov10+gradio目标检测演示系统设计

【设计介绍】 YOLOv10结合Gradio实现目标检测系统设计是一个结合了最新目标检测技术和快速部署框架的项目。下面将详细介绍这一系统的设计和实现过程。 一、YOLOv10介绍 YOLOv10是YOLO(You Only Look Once)系列的最新版本,由清华大学的研究…

vienna整流器的矢量分析

Vienna整流器使用六个二极管和六个IGBT(或MOSFET)组成,提供三个电平:正极电平(P)、中性点电平(O)和负极电平(N)。通过对功率管的控制,Vienna整流器…

Telegram Bot、小程序开发(一)基础入门

文章目录 一、Telegram Bot是什么?二、Telegram Bot应用场景三、机器人是如何工作的?架构getUpdates 和 webhookswebhooks要求自签名证书 四、如何创建和使用Telegram Bot?整体步骤和流程Bot 的申请过程将机器人添加到 Telegram 群组 一、Tel…

昇思25天打卡营第25天|基于MoblieNetv2的垃圾分类

一、简介: 本次实验主要介绍垃圾分类代码开发的方法。通过读取本地图像数据作为输入,对图像中的垃圾物体进行检测,并且将检测结果图片保存到文件中。 实验目的: 了解熟悉垃圾分类应用代码的编写(Python语言&#x…

卷积神经网络——LeNet——FashionMNIST

目录 一、文件结构二、model.py三、model_train.py四、model_test.py 一、文件结构 二、model.py import torch from torch import nn from torchsummary import summaryclass LeNet(nn.Module):def __init__(self):super(LeNet,self).__init__()self.c1 nn.Conv2d(in_channe…

Autosar Dcm配置-0x28服务ComControl-基于ETAS软件

文章目录 前言DcmDcmDsdDcmDspBswMBswMModeRequestPortBswMModeConditionBswMLogicalExpressionBswMActionBswMActionListBswMRule总结前言 0x28服务主要用来控制非诊断报文的通讯,一般在刷写预编程过程中,用来禁止APP的通信报文,可以减少总线负载率,提高刷写成功率。本文…

[C++] STL :stackqueue详解 及 模拟实现

标题:[C] STL :stack&&queue详解 水墨不写bug 目录 (一)stack简介 (二)queue简介 (三)容器适配器 (四)stack和queue的模拟实现 /*** …

数据结构(初阶1.复杂度)

文章目录 一、复杂度概念 二、时间复杂度 2.1 大O的渐进表示法 2.2 时间复杂度计算示例 2.2.1. // 计算Func2的时间复杂度? 2.2.2.// 计算Func3的时间复杂度? 2.2.3.// 计算Func4的时间复杂度? 2.2.4.// 计算strchr的时间复杂度? …

构造者模式的实现

引言——构造复杂对象的艺术 软件工程中,构造复杂对象的艺术被巧妙地封装在构造者模式(Builder Pattern)中。这种设计模式不仅提供了一种清晰且灵活的方式来构建复杂对象,还使得代码更具可读性和可维护性。构造者模式的核心思想是…

Unity最新第三方开源插件《Stateful Component》管理中大型项目MonoBehaviour各种序列化字段 ,的高级解决方案

上文提到了UIState, ObjectRefactor等,还提到了远古的NGUI, KBEngine-UI等 这个算是比较新的解决方法吧,但是抽象出来,问题还是这些个问题 所以你就说做游戏是不是先要解决这些问题? 而不是高大上的UiImage,DoozyUI等 Mono管理引用基本用法 ① 添加Stateful Component …

安全测试理论

安全测试理论 什么是安全测试? 安全测试:发现系统安全隐患的过程安全测试与传统测试区别 传统测试:发现bug为目的 安全测试:发现系统安全隐患什么是渗透测试 渗透测试:已成功入侵系统为目标的的攻击过程渗透测试与安全…

“好物”推荐+Xshell连接实例+使用Conda创建独立的Python环境

目录 主题:好易智算平台推荐RTX 4090DGPU实例租用演示安装配置torch1.9.1cuda11.1.1环境引言:算力的新时代平台介绍:技术与信任的结晶使用案例:实际使用展示创建实例开始使用连接实例(下文演示使用Xshell连接&#xff…

昇思25天学习打卡营第二十天|基于MobileNetv2的垃圾分类

打卡营第二十天,今天学习的内容是MobileNet垃圾分类,记录一下学习内容: 学习内容 本文档主要介绍垃圾分类代码开发的方法。通过读取本地图像数据作为输入,对图像中的垃圾物体进行检测,并且将检测结果图片保存到文件中…

【ARM】CCI集成指导整理

目录 1.CCI集成流程 2.CCI功能集成指导 2.1CCI结构框图解释 Request concentrator Transaction tracker Read-data Network Write-data Network B-response Network 2.2 接口注意项 记录一下CCI500的ACE slave interface不支持的功能: 对于ACE-Lite slav…