极验4 一键解混淆

提示!本文章仅供学习交流,严禁用于任何商业和非法用途,未经许可禁止转载,禁止任何修改后二次传播,如有侵权,可联系本文作者删除!

AST简介

AST(Abstract Syntax Tree),中文抽象语法树,简称语法树(Syntax Tree),是源代码的抽象语法结构的树状表现形式,树上的每个节点都表示源代码中的一种结构。语法树不是某一种编程语言独有的,JavaScript、Python、Java、Golang 等几乎所有编程语言都有语法树。

在做逆向解混淆中,主要用到了 Babel 的以下几个功能包:

@babel/core:Babel 编译器本身,提供了 babel 的编译 API

@babel/parser:将 JavaScript 代码解析成 AST 语法树

@babel/traverse:遍历、修改 AST 语法树的各个节点

@babel/generator:将 AST 还原成 JavaScript 代码

@babel/types:判断、验证节点的类型、构建新 AST 节点等

常用API

常用节点
在这里插入图片描述
在这里插入图片描述
常见混淆还原

AST 有一个在线解析网站:https://astexplorer.net/ ,常用选择
在这里插入图片描述

极验4 gcaptcha.js 解混淆

字符还原

将混淆代码 和 解混淆后的代码放到ast网站对比结构
在这里插入图片描述
观察下来 将 extra 的row 替换成value 即可,这里直接删除 extra 节点 也可以

替换的对象名称

通过观察 C B I R 对应就是 y S W R Y . _CBIR 对应就是ySWRY. CBIR对应就是ySWRY._Ck 方法,多个地方流程一致, 思路:

1、初始化 方法,自执行

2、找到所有ySWRY.$_Ck 对应的方法名称

3、执行出结果 ,替换节点
在这里插入图片描述

还原 控制流平坦化

解混淆思路

1、 遍历ForStatement 节点找到固定格式代码块

2、 拿到ForStatement 的上一个节点,获取控制流的初始值

3、按照流程计算 switch 的初始值

4、遍历 SwitchCase 节点, 计算case 值, switch值 和case值一致时 向下计算
在这里插入图片描述

删除无关函数

js 头部方法已经不需要,直接节点遍历 找到对应值 remove 掉
在这里插入图片描述
完整代码如下


const parser = require("@babel/parser");
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const generator = require("@babel/generator").default;
const fs = require("fs");// js混淆代码
process.argv.length > 2 ? encode_file = process.argv[2] : encode_file = "./input/jy_0422.js";
process.argv.length > 3 ? decode_file = process.argv[3] : decode_file = "./output/jy_0422_decode.js";// #######################################
// AST解析函数
// #######################################// 简化字符, unicode 字符串 和 表达式 1e3
const simplifyLiteral = {"NumericLiteral|StringLiteral"(path){var node = path.node;if (node.extra === undefined)return;delete node.extra;}
};/*
var t = 5 * Math["random"](2),s = t - 1,n = [];-------------var t = 5 * Math["random"](2);
var s = t - 1;
var n = [];*/
// 简单逗号表达式处理
function deal_VariableDeclarator(path) {if (t.isForStatement(path.parent)) {return;}var node = path.node;let body = [];if (node.declarations && node.declarations.length > 1) {node.declarations.forEach(dec => {body.push(t.variableDeclaration("var", [dec]));});path.replaceInline(body);}
}// 定义一个全局变量,存放待替换变量名
let name_array = [];// 获取待替换的名称
function get_name_array(path) {let {kind, declarations} = path.node;if (kind !== 'var'|| declarations.length !== 3|| declarations[0].init === null|| declarations[0].init.property === undefined)return;if (declarations[0].init.property.name !== funPropertyName[2])return;// 获取待替换节点变量名let name1 = declarations[0].id.name;// 获取待输出变量名let name2 = declarations[2].id.name;// 将变量名存入数组name_array.push(name1, name2);// 删除下一个节点path.getNextSibling().remove();// 删除下一个节点path.getNextSibling().remove();// 删除path节点path.remove()
}// 替换节点
function replace_name_array(path) {let {callee, arguments} = path.nodeif (callee === undefined || callee.name === undefined)return;// 不在name_array中的节点不做替换操作if (name_array.indexOf(callee.name) === -1)return;// 调用ySWRY.$_Ck函数获取结果let value = global_obj_name_fun[funPropertyName[2]](arguments[0].value);// 创建节点并替换结果let string_node = t.stringLiteral(value);path.replaceWith(string_node)
}// 控制流平坦化
function replace_ForStatement(path) {var node = path.node;// 获取上一个节点,也就是VariableDeclarationvar PrevSibling = path.getPrevSibling();// 判断上个节点的各个属性,防止报错if (PrevSibling.type === undefined|| PrevSibling.container === undefined|| PrevSibling.container[0].declarations === undefined|| PrevSibling.container[0].declarations[0].init === null|| PrevSibling.container[0].declarations[0].init.object === undefined|| PrevSibling.container[0].declarations[0].init.object.object === undefined)return;if (PrevSibling.container[0].declarations[0].init.object.object.callee.property.name !== funPropertyName[3])return;// if (PrevSibling.node.declarations[0].init.object.object.callee.property.name !== funPropertyName[3])//     return;// SwitchStatement节点var body = node.body.body;// 判断当前节点的body[0]属性和body[0].discriminant是否存在if (!t.isSwitchStatement(body[0]))return;if (!t.isIdentifier(body[0].discriminant))return;// 获取控制流的初始值var argNode = PrevSibling.container[0].declarations[0].init;var init_arg_f = argNode.object.property.value;var init_arg_s = argNode.property.value;var init_arg = global_obj_name_fun[funPropertyName[3]]()[init_arg_f][init_arg_s];// 提取for节点中的if判断参数的value作为判断参数, 退出循环判断var break_arg_f = node.test.right.object.property.value;var break_arg_s = node.test.right.property.value;var break_arg = global_obj_name_fun[funPropertyName[3]]()[break_arg_f][break_arg_s];// 提取switch下所有的casevar case_list = body[0].cases;var resultBody = [];// 遍历全部的casefor (var i = 0; i < case_list.length; i++) {for (; init_arg != break_arg;) {// 提取并计算case后的条件判断的值var case_arg_f = case_list[i].test.object.property.value;var case_arg_s = case_list[i].test.property.value;var case_init = global_obj_name_fun[funPropertyName[3]]()[case_arg_f][case_arg_s];if (init_arg == case_init) {//当前case下的所有节点var targetBody = case_list[i].consequent;// 删除break节点,和break节点的上一个节点的一些无用代码if (t.isBreakStatement(targetBody[targetBody.length - 1])&& t.isExpressionStatement(targetBody[targetBody.length - 2])&& targetBody[targetBody.length - 2].expression.right.object.object.callee.object.name == global_obj_name) {// 提取break节点的上一个节点AJgjJ.EMf()后面的两个索引值var change_arg_f = targetBody[targetBody.length - 2].expression.right.object.property.value;var change_arg_s = targetBody[targetBody.length - 2].expression.right.property.value;// 修改控制流的初始值init_arg = global_obj_name_fun[funPropertyName[3]]()[change_arg_f][change_arg_s];// global_obj_name.funPropertyName[3][change_arg_f][change_arg_s];targetBody.pop(); // 删除breaktargetBody.pop(); // 删除break节点的上一个节点}//删除breakelse if (t.isBreakStatement(targetBody[targetBody.length - 1])) {targetBody.pop();}resultBody = resultBody.concat(targetBody);break;} else {break;}}}//替换for节点,多个节点替换一个节点用replaceWithMultiplepath.replaceWithMultiple(resultBody);//删除上一个节点PrevSibling.remove();
}// 删除无关函数
function delete_express(path) {let {expression} = path.nodeif (expression === undefined|| expression.left === undefined|| expression.left.property === undefined)return;if (expression.left.property.name === funPropertyName[0]|| expression.left.property.name === funPropertyName[1]|| expression.left.property.name === funPropertyName[2]|| expression.left.property.name === funPropertyName[3]) {path.remove()}
}// 获取全局函数
function getGlobalFunc(){let program_body= ast.program.body;if(program_body && program_body.length === 6){for (var i=0; i<program_body.length-1; i++){// 拿到body 前5个节点自执行globalCode += generator(program_body[i]).code + "\n";if(i<4){// 获取全局 ySWRYglobal_obj_name = program_body[i].expression.left.object.name;// 为了获取对应名称 ySWRY.$_CkfunPropertyName.push(program_body[i].expression.left.property.name);}}return globalCode}
}// 删除无用方法
function delete_func(path) {let {id} = path.node;if(id.name == global_obj_name){path.remove()}
}// #######################################// #######################################
// AST还原流程
// #######################################// 读取需要解码的js文件, 注意文件编码为utf-8格式
let jscode = fs.readFileSync(encode_file, {encoding: "utf-8"});// 将js代码修转成AST语法树
let ast = parser.parse(jscode);console.time("处理完毕,耗时");var global_obj_name = '';   // ySWRY
var globalCode = '';
var funPropertyName = [];// 获取自执行方法
globalCode = getGlobalFunc();
eval(globalCode);
// console.log('funPropertyName--', funPropertyName);  // [ '$_AB', '$_Bx', '$_Ck', '$_DK' ]// 全局方法 ySWRY
global_obj_name_fun = eval("(" + global_obj_name + ")");// 简化字符串
traverse(ast, simplifyLiteral);// AST结构修改逻辑
const visitor = {VariableDeclaration: {enter: [get_name_array]},CallExpression: {enter: [replace_name_array]},ForStatement: {enter: [replace_ForStatement]},ExpressionStatement: {enter: [delete_express]},FunctionDeclaration: {enter: [delete_func]}
};// 遍历语法树节点,调用修改函数
traverse(ast, visitor);resolveSequence_t = {VariableDeclaration: {exit: [deal_VariableDeclarator]},
};// 简单逗号表达式 还原
traverse(ast, resolveSequence_t);console.timeEnd("处理完毕,耗时");// 将ast转成js代码,{jsescOption: {"minimal": true}} unicode -> 中文
let {code} = generator(ast, opts = {jsescOption: {"minimal": true}});
// 将js代码保存到文件
fs.writeFile(decode_file, code, (err) => {
});console.log("end");

最终效果 解混淆部分不需要任何修改,只修改下待还原 的文件名 和 输出文件名, 支持极验4 代任意版本的混淆JS 代码。

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

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

相关文章

Linux专栏03:使用Xshell远程连接云服务器

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ 使用Xshell远程连接云服务器 编号&#xff1a;03 文章目录 使用Xsh…

Redis学习(九)|如何保证MySQL和Redis的数据一致性

文章目录 引言什么是数据一致性为什么会出现不一致的情况如何保证一致性分布式事务异步更新策略双写模式缓存失效策略定时同步策略 结论 引言 数据一致性是任何系统设计中至关重要的一个方面。在涉及到同时使用Redis和MySQL的场景中&#xff0c;保证两者之间的数据一致性尤为重…

Redis 中,集合(Set)

在 Redis 中&#xff0c;集合&#xff08;Set&#xff09;是一种无序的、由字符串组成的数据结构。集合中的每个元素都是唯一的&#xff0c;即集合不允许重复的元素。Redis 集合通过哈希表实现&#xff0c;添加、删除、查找元素的操作都能在平均常数时间内完成。 ### 集合的特…

Jenkins集成Kubernetes 部署springboot项目

文章目录 准备部署的yml文件Harbor私服配置测试使用效果Jenkins远程调用参考文章 准备部署的yml文件 apiVersion: apps/v1 kind: Deployment metadata:namespace: testname: pipelinelabels:app: pipeline spec:replicas: 2selector:matchLabels:app: pipelinetemplate:metada…

【linux系统开发基本命令】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 基本命令dockergitCpython总结 基本命令 sudo 管理员权限&#xff0c;一般用在命令的最前面 例如&#xff1a; sudo rm our.py删除our.py文件 pwd打印当前目录 cd…

软件设计师-应用技术-数据结构及算法题4

考题形式&#xff1a; 第一题&#xff1a;代码填空 4-5空 8-10第二题&#xff1a;时间复杂度 / 代码策略第三题&#xff1a;拓展&#xff0c;跟一组数据&#xff0c;把数据带入代码中&#xff0c;求解 基础知识及技巧&#xff1a; 1. 分治法&#xff1a; 基础知识&#xff1…

STM32F103 ADC驱动程序设计

设计了一种通过软件触发&#xff0c;循环采集的ADC驱动程序 #include "stdbool.h" #include "string.h" #include "stm32f0xx_ll_bus.h" #include "stm32f0xx_ll_gpio.h" #include "stm32f0xx_ll_adc.h" #include "stm…

【Vue3 生命周期与组合式API】

文章目录 一、Vue3 生命周期概述二、Vue3 组合式API 与选项式API 的对比1. 选项式API 生命周期钩子函数常用的生命周期钩子函数包括&#xff1a; 2. 组合式API 的优势组合式API 的主要优势包括&#xff1a;在生命周期管理上的优势 三、Vue3 组合式API 生命周期钩子函数1. setup…

语音识别--降噪

⚠申明&#xff1a; 未经许可&#xff0c;禁止以任何形式转载&#xff0c;若要引用&#xff0c;请标注链接地址。 全文共计3077字&#xff0c;阅读大概需要3分钟 &#x1f308;更多学习内容&#xff0c; 欢迎&#x1f44f;关注&#x1f440;【文末】我的个人微信公众号&#xf…

三剑客之grep

grep初级简单使用&#xff1a;Liunx查找&过滤-CSDN博客 正则表达式-CSDN博客 目录 grep分类 返回值 使用返回值控制流程 grep参数示例 基本参数 grep与正则表达式的搭配使用与示例 看完正则与示例进行无答案式练习 grep 用于在文件或输入流中查找特定模式或文本。…

uniapp的app端推送功能,不使用unipush

1&#xff1a;推送功能使用htmlPlus实现&#xff1a;地址HTML5 API Reference (html5plus.org) 效果图&#xff1a; 代码实现&#xff1a; <template><view class"content"><view class"text-area"><button click"createMsg&q…

什么是DDoS攻击?DDoS攻击的原理是什么?

一、DDoS攻击概念 DDoS攻击又叫“分布式拒绝服务”(Distributed DenialofService)攻击&#xff0c;它是一种通过控制大量计算机、物联网终端或网络僵尸&#xff08;Zombie&#xff09;来向目标网站发送大量请求&#xff0c;从而耗尽其服务器资源&#xff0c;导致正常用户无法访…

设计网页用什么软件

在设计网页时&#xff0c;可以使用多种软件来完成不同的任务。以下是一些常用的网页设计软件&#xff0c;以及它们的特点和用途。 1. Adobe Photoshop&#xff1a; Adobe Photoshop 是一款功能强大的图像编辑软件。在网页设计中&#xff0c;它常用于创建和编辑网页所需的图像、…

详细介绍如何在Windows系统上安装Anaconda

**一、引言** Anaconda是一个开源的Python发行版本&#xff0c;它包含了conda、Python等180多个科学包及其依赖项。Anaconda的优点在于它可以帮助用户解决安装包的依赖问题&#xff0c;方便用户管理多个环境&#xff0c;并且提供了一个完整的科学计算环境。本指南将详细介绍如…

手动配置dns后网速变慢

之前因为自动的dns能上qq但打不开网页&#xff0c;就手动设置了一个&#xff0c;结果近些天时不时出现网页图片加载慢的问题&#xff0c;影响到我看美女图片了&#xff0c;是可忍熟不可忍 测了下网速&#xff0c;很快&#xff0c;下载上传都是三位数的&#xff0c;那显然不是网…

element+ant 组件点

ant中table设置默认排序 a-table中的:column属性,设置对应的列上 item.sortOrder params.sort_type asc ? ascend : descend 判断ant-table中是否有横向坐标轴 const hasHorizontalScrollbar() > {const table document.querySelector(.ant-table-content);//可修改,…

RabbitMQ是如何保证消息可靠性的?——Java全栈知识(16)

RabbitMQ 的消息不可靠也就是 RabbitMQ 消息丢失只会发生在以下几个方面&#xff1a; 生产者发送消息到 MQ 或者 Exchange 过程中丢失。Exchange 中的消息发送到 MQ 中丢失。消息在 MQ 或者 Exchange 中服务器宕机导致消息丢失。消息被消费者消费的过程中丢失。 大致就分为生…

Star-CCM+分配零部件至区域1-将所有零部件分配至区域

前言 Star-CCM中&#xff0c;在划分网格之前需要将零部件分配至区域&#xff0c;然后才可以划分网格。如下图1所示&#xff0c;分配零部件至区域需要选择创建区域的方式、创建边界的方式以及交界面的类型。 图1 将零部件分配至区域 1 创建区域的方式选择 如下图2所示&#x…

基于Spring Boot的家具网站设计与实现

基于Spring Boot的家具网站设计与实现 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea 系统部分展示 系统前台主界面图&#xff0c;用户可进入家具网站可查看…

RFC 791 (2) - Overview

目录 总览 操作 Addressing 与 Fragmentation Addressing Fragmentation 总览 RFC791文档的第二部分就是对IP进行总浏览&#xff1a;可以看到&#xff0c;我们的五层划分是这样的&#xff1a; ------ ----- ----- ----- |Telnet| | FTP | | TFTP| ... | ... | -----…