极验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…

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…

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

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

语音识别--降噪

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

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;它常用于创建和编辑网页所需的图像、…

手动配置dns后网速变慢

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

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;用户可进入家具网站可查看…

探索大模型能力--prompt工程

1 prompt工程是什么 1.1 什么是Prompt&#xff1f; LLM大语言模型终究也只是一个工具&#xff0c;我们不可能每个人都去训一个大模型&#xff0c;但是我们可以思考如何利用好大模型&#xff0c;让他提升我们的工作效率。就像计算器工具一样&#xff0c;要你算10的10倍&#x…

Java IO类之FilterOutputStream的研究与应用

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

RCE学习(一)

一.知识点 RCE&#xff1a;远程命令/代码执行漏洞&#xff0c;简称为RCE漏洞&#xff0c;可以直接向服务器后台远程注入操作系统的命令或者代码&#xff0c;从而拿到服务器后台的权限。RCE分为远程执行命令&#xff08;执行ping命令&#xff09;和远程代码执行eval 简单来说就…

VMware导入ova/ovf虚拟机文件

1.文件-打开-ova文件 2.为新虚拟机起名称 3.等待导入 4.导入完成&#xff0c;可以开始使用 参考链接&#xff1a;VMware导入ova/ovf虚拟机文件

浅谈Session和Cookie

各位大佬光临寒舍&#xff0c;希望各位能赏脸给个三连&#xff0c;谢谢各位大佬了&#xff01;&#xff01;&#xff01; 目录 1.Cookie 2.Sesssion&#xff08;会话&#xff09; 3.Session和Cookie的联系 4.总结 1.Cookie Cookie是客户端存储数据的机制&#xff0c;一般是…

英语学习笔记5——Nice to meet you.

Nice to meet you. 很高兴见到你。 词汇 Vocabulary Mr. 先生 用法&#xff1a;自己全名 / 姓 例如&#xff1a;Mr. Zhang Mingdong 或 Mr. Zhang&#xff0c;绝对不能是 Mr. Mingdong&#xff01; Miss 女士&#xff0c;小姐 未婚 用法&#xff1a;自己全名 / 姓 例如&#…

【ROMA核心特性数据、服务、消息、设备集成及统一运维】

1、数据集成 FDI旨在解决多种数据源的快速灵活集成能力&#xff0c;您可以在任意时间、任意地点、任意系统之间实现实时数据订阅和定时增量数据迁移。 &#xff08;1&#xff09;数据集成任务的生命周期管理 &#xff08;2&#xff09;FDI支持修改数据集成任务的信息、查看数…

初识Node.js-认识node(安装Node.js环境配置)

目录 一、node介绍 1.概念和特点 2.核心功能 3.应用场景 二、Node初使用 1.安装node配置 windows上安Node.js 1.windows安装包&#xff08;.msi&#xff09; 2、Windows 二进制文件 (.exe)安装 Linux 上安装 Node.js 直接使用已编译好的包 Ubuntu 源码安装 Node.js …

【JVM】垃圾回收机制(Garbage Collection)

目录 一、什么是垃圾回收&#xff1f; 二、为什么要有垃圾回收机制&#xff08;GC&#xff09;&#xff1f; 三、垃圾回收主要回收的内存区域 四、死亡对象的判断算法 a&#xff09;引用计数算法 b&#xff09;可达性分析算法 五、垃圾回收算法 a&#xff09;标记-清除…