《封号码罗》python爬虫之AST在js逆向中switch-case反控制流平坦化2(二十三)

《封号码罗》python爬虫之AST在js逆向中switch-case反控制流平坦化(二十二) js逆向-switch-case反控制流平坦化 阅读详情

ast处理之前的js源码:链接:https://pan.baidu.com/s/1Duf7G8i2W3GQ1vt46NnxWg?pwd=x9rd
提取码:x9rd
文章是学习明妃ast的笔记,加了学习时的一些注释,对照学习看不懂的地方进行了debugger方式查看运行结果,内容不多,慢慢敲一遍。
平时主要是自学,不系统,getbinding和scope这些玩意儿拿捏不住,不过,两层for循环我还是能看得懂滴!

const parser = require("@babel/parser");
const generator = require("@babel/generator").default;
const traverse = require("@babel/traverse").default;
const t = require("@babel/types");
const fs = require("fs");

const js_code = fs.readFileSync(
    "./boos_01_源码.js", {encoding: "utf-8"}
);

const ast = parser.parse(js_code, {sourceType: "module"});

function character_decoding_array(path) {
    let node = path.node;
    if (!t.isIdentifier(node.object.object)) {
        return
    }
    if (t.isStringLiteral(node.property)) {
        // node.property.extra.raw = node.property.extra.rawValue
        delete node.property.extra.raw
    }
    if (t.isStringLiteral(node.object.property)) {
        // node.object.property.extra.raw = node.object.property.extra.rawValue
        delete node.object.property.extra.raw
    }
}

function character_decoding_string(path) {
    let node = path.node;
    if (!t.isIdentifier(node.object)) {
        return
    }
    if (t.isStringLiteral(node.property)) {
        // node.property.extra.raw = node.property.extra.rawValue
        delete node.property.extra.raw
    }
}

function case_handle_one(path) {
    // 控制流平坦化  主要针对在case中通过+=运算符修改流程的控制流
    path.traverse({
        // 只处理分发器时单个声明的
        VariableDeclaration(p) {
            // 第一步:通过特征定位分发器 var J3I = 11;
            let declarations = p.node.declarations;
            if (!(declarations.length === 1 && t.isNumericLiteral(declarations[0].init))) {
                return
            }
            // // key: 有兄弟节点时,为当前节点,在容器中的索引,没有时,是 当前节点属性名
            let whilePath = p.getSibling(p.key + 1);
            if (!t.isWhileStatement(whilePath)) {
                // 声明语句后面不是while节点就退出
                return;
            }
            // 第二步:获取控制器的名称和值
            let shufferVar = {name: declarations[0].id.name, value: declarations[0].init.value}
            console.log("分发器初始值:", shufferVar);
            // 第三步:映射case,为了方便获取case代码块
            let _case = {};
            whilePath.node.body.body[0].cases.map((v) => {
                // v就是一个一个的 SwitchCase 节点,v.consequent时case里的代码块 数组结构
                let switchCase = v.consequent;
                // v.test.value 这个获取的是case后面的数值  case 1:  里面的1
                _case[v.test.value] = switchCase
            });
            // console.log(_case);
            // 第四步:构造分支流程  构造出前后分支的代码
            let shufferFlow = [], // 分支流程数组
                flag = false;   // 没有 break 标识
            whilePath.node.body.body[0].cases.map((v) => {
                // 获取最后一个代码块  大多数是 break ,不是 break 的话,该 cases 执行完就 跳到下一个 case
                let n = v.consequent.slice(-1)[0];
                // console.log(n);
                if (t.isBreakStatement(n)) {
                    let caseNum = v.test.value;
                    console.log("遍历分发器的值:", caseNum);
                    let nextNode = v.consequent.slice(-2)[0];
                    if (nextNode.expression.left.name === shufferVar.name) {
                        // 变量名 与 分发器起始值一样 更新
                        let nextNum = caseNum;   // 当前流程  eval计算下依次流程
                        eval(`nextNum
                        ${nextNode.expression.operator}
                        ${nextNode.expression.right.value}`);
                        flag && (nextNum -= 1) && (flag = false);
                        shufferFlow.push([caseNum, nextNum])
                    }
                } else {
                    if (!t.isReturnStatement(n)) {
                        // 不是 return 执行完直接到 下一个 cases,但 下一个 case 需要单独处理
                        let caseNum = v.test.value;
                        let nextNode = caseNum + 1;
                        flag = true;
                        shufferFlow.push([caseNum, nextNode])
                    }
                }
            });
            console.log("前后分支关系:", JSON.stringify(shufferFlow), shufferFlow.length)
            // 通过 节点起始  流程还原
            let shufferFlow_ = [];
            shufferFlow.map((v1, i1) => {
                if (shufferVar.value === v1[0]) {
                    shufferFlow_.push(v1);  // 起始值 和 下一个值
                    for (let i = 0; i < shufferFlow.length; i++) {
                        for (let j = 0; j < shufferFlow.length; j++) {
                            if (shufferFlow_[i][1] === shufferFlow[j][0]) {
                                shufferFlow_.push(shufferFlow[j]);
                                break
                            }
                        }
                    }
                }
            });
            console.log("正常分支流程:", JSON.stringify(shufferFlow_));
            // 构造分发器
            let shufferArr = [];
            shufferFlow_.reduce((k1, k2) => {
                k1 && shufferArr.push(...k1);
                k2[1] > 0 && shufferArr.push(k2[1])
            });
            console.log("分发器数组:", JSON.stringify(shufferArr), shufferArr.length)
            // 获取完整 分发器后,只需要把 每个 case 里面的代码,按照分发器 提取,添加到 父节点的 body 里面就好,
            // 这个过程使用判断 删除一点代码就好了
            p.remove();  // 删除分发器初始值
            whilePath.remove();  // 删除 while 节点
            let parentPath = whilePath.parent;  // while循环的父节点
            shufferArr.map((v) => {
                if (t.isBreakStatement(_case[v].slice(-1)[0])) {
                    _case[v].pop(); // 删除 break 节点
                    _case[v].pop() // 删除 更新流程 节点
                }
                parentPath.body.push(..._case[v])  //// 添加到 父path 节点
            });
            console.log("新生父节点 body 数量:", parentPath.body.length);
        }
    })
}

function case_handle_two(path) {
    // 控制流平坦化  主要针对i++类型的流程
    path.traverse({
        VariableDeclaration(p) {
            // 第一步:通过特征 定位分发器位置 var Hr2 = 0
            //             , sAW = [6, 5, 19, 37, 38, 35, 20, 30, 17, 27, 12, 42, 15];
            // 在搞这个之前,要先知道Array.prototype 属性表示 Array 构造函数的原型,并允许您向所有Array对象添加新的属性和方法
            // 字符解码后Array["prototype"]["p"] = Array["prototype"]["push"]; 所有array对象都有一个p属性,p有是继承原先的push属性
            // var Rfa = ZhV.p;  ==> var Rfa = ZhV.push
            // Rfa.apply(ZhV, l5s)  表示在原有分发器的基础上push了一组array类型的分发器,明白了这些就可以继续往下看了
            let declarations = p.node.declarations;
            if (declarations.length != 2) {  // 判断长度
                return
            }
            if (!t.isNumericLiteral(declarations[0].init, {value: 0})) {
                // 条件是初始值为0的赋值是否是数值类型的判断
                return;
            }
            if (!t.isArrayExpression(declarations[1].init)) {
                // 第二个是数组类型的判断
                return;
            }
            // 获取while 循环 p的下一个兄弟节点
            let whilePath = p.getSibling(p.key + 1);
            if (!t.isWhileStatement(whilePath)) {
                return;  //
            }
            // 第二步:获取分发器初始数组
            // 先通过 定位 while 节点,获取前兄弟节点,判断是否是 初始分发器数组
            let shufferVAr = declarations[1].id.name; // 分发器名称
            let shufferArr = {value: declarations[1].init.elements};
            console.log("初始 分发器:", {name: shufferVAr, lenght: shufferArr.value.length});
            // 第三步:映射 case 代码块,并 记录 更新分发器变量名
            // 映射为了 方面获取 case 代码块,映射过程,可以通过 apply 的特征,记录其他分发器数组的变量名
            // return;
            let _case = {};
            let shufferVArName = {arrName: {}};
            whilePath.node.body.body[0].cases.map((v) => {  // v 是每一个 case
                let switchCase = v.consequent;  // case里面的代码块, 数组结构
                if (t.isExpressionStatement(switchCase.slice(-2)[0])) {
                    let n = switchCase.slice(-2)[0];  // h = M4l;

                    if (n.expression.callee // Rfa.apply(ZhV, j10);     //  push 新的分发器
                        && t.isMemberExpression(n.expression.callee)   // CBf()  过滤直接调用函数的形式
                        && n.expression.callee.property.name === "apply"
                        && n.expression.arguments[0].name === shufferVAr
                    ) {
                        // // 记录 另一部分 分发器变量名
                        shufferVArName.arrName[n.expression.arguments[1].name] = true;
                        switchCase.splice(-2, 1)  // 删除 apply 语句行
                    }
                }
                // _case[6] = 对应的语句块
                _case[v.test.value] = switchCase
            });
            // 第四步:组件完整分发器
            // 每次取从 tmpShufferArr 删除头部值并获得,来进行过一遍流程分支,
            // 过的过程中动态添加 tmpShufferArr 值,来获得完整的分发器
            let tmpShufferArr = shufferArr.value.concat();  // 深复制
            shufferArr = [];  // 分发器置空
            let a = 0;
            while (tmpShufferArr.length !== 0) { // 这样写 每次更新临时分发器时,能增加循环
                // shift() 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值
                let index = tmpShufferArr.shift().value; // 每次从 临时分发器 头部获取 流程值

                let consequent = _case[index];  // 从_case中获取对应的语句块
                shufferArr.push(index);   // 从 临时分发器 获得的流程值 才是真实的

                let n = consequent.slice(-1)[0];  // 获取最后一个代码块
                // console.log(a++);
                if (!t.isBreakStatement(n) && !t.isReturnStatement(n)) {
                    // 为了处理:结尾 不是 break 和 return 的 case,下一次直接进入下一个 case 代码块
                    // unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。
                    tmpShufferArr.unshift({value: index + 1})  // 更新临时分发器
                }
                // var l5s = 获取倒数第二个语句块,判断是否是分发器
                let b = consequent.slice(-2)[0];
                if (t.isVariableDeclaration(b) && b.declarations[0]
                    && shufferVArName.arrName[b.declarations[0].id.name]) {
                    // 更新临时分发器
                    tmpShufferArr.push(...b.declarations[0].init.elements);
                    _case[index].splice(-2, 1)
                }
                if (t.isVariableDeclaration(b) && b.declarations[0]
                    && b.declarations[0].init && b.declarations[0].init.object
                    && b.declarations[0].init.object.name === shufferVAr  // 形式 var Rfa = ZhV.p;
                    && b.declarations[0].init.property.name === "p") {
                    _case[index].splice(-2, 1)  // 删除 push 声明
                }
            }
            console.log("完整分发器:", JSON.stringify(shufferArr))
            // 第五步:还原 遍历分发器,获取case代码块,添加到父节点的body里,并删除break
            p.remove(); // 删除分发器
            whilePath.remove();  // 删除 while 节点
            let parentPath = whilePath.parent;
            shufferArr.map((v) => {
                if (t.isBreakStatement(_case[v].slice(-1)[0])) {
                    _case[v].pop() // 删除分发器
                }
                // _case[v] 是一个数组 ...解构之后一次性push到body
                parentPath.body.push(..._case[v])
            })

        }
    })
}

let visitor = {
    MemberExpression: {
        enter: [character_decoding_array, character_decoding_string]
    },
    "FunctionDeclaration|FunctionExpression": {
        enter: [
            case_handle_one,
            case_handle_two
        ]
    }
};
// 平坦流处理

// charCodeAt() 返回在指定的位置的字符的 Unicode 编码
// charAt() 	返回在指定位置的字符
traverse(ast, visitor);

let {code} = generator(ast, opts = {jsescOption: {"minimal": true}});
// 文件保存
fs.writeFile('./boos_01_new.js', code, (err) => {
});
JavaScript 逆向工程:利用 AST 实现控制流平坦化 在JavaScript逆向工程中,代码混淆是一个常见的问题,它使得直接阅读和理解代码变得困难。控制流平坦化(Control Flow Flattening)是解决这一问题的关键技术之一。本文将深入浅出地探讨如何利用抽象语法树(AST)来识别和平坦化混淆的控制流。 阅读详情

相关推荐

一款JavaScript 混淆(Obfuscator)工具(Tool)的研究(四)花指令和控制流平坦化

源代码: // Paste your JavaScript code here function hi() { console.log("Hello World!"); var d = new Date(); var time = d.getHours(); if (time<10) { document.write("<b>早上好</b>")...

lacoucou的专栏 1944

利用AST对抗js混淆(三) 控制流平坦化(Control Flow Flattening)的处理

控制流平坦化 本文主要分享了两个控制流平坦化的例子。

lacoucou的专栏 6061

js逆向之常见的压缩和混淆

js逆向之常见的压缩和混淆

Deng872347348的博客 1341

for循环

上代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head><body>

weixin_50266677的博客 1391

JS逆向进阶:控制流平坦化与VM保护的破解思路

如果说控制流平坦化是把代码打乱,那 VM 保护就是把代码彻底“加密”。它不依赖原生 JS 的语法结构,而是自定义一套指令集和解释器,把原始逻辑编译成字节码,运行时由解释器逐条取指执行。你在源码里看不到任何原始逻辑,只能看到一大段看不懂的解释器和一串数字数组。这是目前强度最高的 JS 保护手段,也是逆向进阶的标志性门槛。JS 逆向的进阶过程,本质上是从“改代码”到“理解执行体系”的升级。控制流平坦化玩的是控制流重构的把戏,VM 保护玩的是指令集重定义的套路,但只要摸透了它们的运行原理,就没有拆不开的黑盒。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 964

【硬件工程师职业细分】揭秘硬件基带工程师

你知道硬件工程师的细分领域硬件基带工程师吗? 它们和常见的硬件工程师又有哪些不同? 福利待遇怎么样?有哪些职业要求? 快来涨上一波知识吧~

woshizzai的博客 1万+

js常见入门加密技术之控制流平坦化

通过上述处理,代码中的控制流语句被拆分成了多个小的语句块,并使用了一些无限循环和continue语句来增加代码复杂性。这种改变使得代码的执行流程变得更加复杂和难以理解,增加了代码的混淆程度,从而提高了程序的安全性。具体而言,它会将这些语句拆分成多个小的、相互嵌套的语句,使得代码中的控制流路径更加复杂、难以理解。以上是一个简单的例子,实际使用控制流平坦化技术时,可能会使用更加复杂的算法和变换方式来混淆代码。值得注意的是,控制流平坦化并不会改变程序的行为,它只是改变了程序的结构和执行流程。

mxd01848的博客 978

livebos报表开发

文章目录1.参数的设定2.展示的内容3.综合展示的效果4.调用过程5.可以在数据库里面查看6.注意事项7.过程代码 1.参数的设定 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flb5C49A-1619516341977)(/uploads/xinxibuneibuzhishiku/images/m_bc71bb4608053778dbf29e06cd102ae1_r.png)] 2.展示的内容 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-

阿浪的专栏 709

有关js逆向的保护机制

因此,在设计和开发应用程序时,应该综合考虑多种安全措施,并不断关注安全领域的最新动态,及时更新和改进保护策略。1. 对关键代码进行加密:可以使用一些加密算法对重要的 JavaScript 代码进行加密,在运行时再进行解密。2. 动态加载关键代码:将关键代码分割并在运行时动态加载,可以增加攻击者分析代码的难度。同时,它们也提供了一些插件和配置选项,可以进一步增强代码的安全性,如代码分割、资源指纹等。3. 控制流平坦化:通过改变代码的控制流结构,使代码的执行流程变得复杂和难以追踪。四、使用浏览器安全机制。

m0_57836225的博客 793

用于多种无人机协作的群体智能算法:综合综述

随着无人机技术的快速高水平发展,将多架无人机作为自主控制的编队应用于复杂情况是一个新兴领域的代表之一。在实现自主控制的挑战中,碰撞避免、任务分配、路径规划和编队重构是关键功能。本文对各种经典的无人机群智能算法进行了系统的总结和分析。

erjia_的博客 1万+

【TI毫米波雷达】IWR6843AOPEVM-G与DCA1000EVM实战:从硬件连接到数据流解析

本文详细解析了TI毫米波雷达开发套件IWR6843AOPEVM-G与DCA1000EVM的硬件连接、模式配置及数据流处理全流程。针对官方手册中的常见配置错误,提供了避坑指南和实测验证的正确设置方法,包括开关配置、网络参数等关键步骤。通过mmWave Studio软件配置雷达参数,并深入解析数据采集与处理流程,帮助开发者高效实现毫米波雷达应用开发。

weixin_30653023的博客 368

控制流平坦化(js逆向知识)

平坦流就是把原来的js代码逻辑放到一个又一个case中,使用switch包括,然后再每一个case中处理逻辑,还可以在case中跳到下一个case语句中,从而实现代码的混淆。这里的u.sent是什么呢?这里的u.sent是上一个执行逻辑,也就是说这里的。

2302_80729149的博客 1200

一文搞懂雷达脉冲压缩和匹配滤波器

为了解决传统单频脉冲雷达面临的作用距离和空间分辨力之间的矛盾,脉冲压缩理论被提出。在接收端设计一个和发射信号能够“共轭匹配”的网络来实现脉冲压缩。接收到的回波信号通过匹配滤波器时,滤波器的增益在信号越强的频率点越大;在信号越弱的频率点,增益越小,通过这样的方式使信号在时域更加集中。脉冲压缩技术大致可分为线性调频(LFM)和相位编码。其中LFM信号易于通过硬件或各种技术产生,但是进行匹配滤波后主旁瓣比高达13.2dB,在检测多个目标时,强目标的回波旁瓣可能会把弱小信号覆盖,导致弱小信号的检测受到影响。

FPGA入门到跑路 9933

JS加密入门之控制流平坦化2

JS混淆加密是一种将JavaScript代码进行混淆和加密的技术,旨在使代码更难以阅读和理解,从而增加破解难度。控制流平坦化是其中一种常用的混淆技术,它可以使代码的控制流程变得非常复杂,增加代码的可读性和可维护性的难度。以上代码可以加密和解密一个字符串,但是它非常容易被破解,因为加密和解密的过程都是固定的,没有任何随机性和变化。如果您对文章内容有不同看法,或者疑问,欢迎到评论区留言,或者私信我都可以。如遇自己源码加密后没备份,可以找我们解决解出恢复源码,任何加密都可以。

mxd01848的博客 409

【STK】手把手教你利用STK进行导弹和反导仿真01 - STK/MMT模块安装部署

【STK】手把手教你利用STK进行导弹和反导仿真01 - STK/MMT模块安装部署MMT模块与STK的版本是一一对应的,比如我现在手上的版本是MMT9的,那么我使用的STK的版本也必须是9版本的,如果你现在正在使用的是更高版本的STK,比如说10、11.2、11.6、12.2,那么该怎么办呢?这个经本人尝试,目前是可以多个STK版本共存的,我们可以根据自己的需要安装多个版本的STK下面开始讲解具体的安装步骤,

weixin_44184852的博客 2917

基于STM32+华为云设计的智能鱼缸

本项目基于STM32单片机设计一款基于物联网的智能鱼缸系统,并通过应用侧接口开发上位机APP实现远程对鱼缸参数的检测和控制。该系统包括水温检测、水质检测、自动或手动换水、氛围灯灯光变换和自动或手动喂食等功能,可以帮助用户更好地管理鱼缸,提高鱼儿的生存率,同时增强用户对家庭装饰品的掌控感和科技感。

1万+

使用AST还原JavaScript控制流平坦化

使用AST还原JavaScript控制流平坦化,通过解析代码生成AST,识别主循环和状态变量,重建控制流并生成还原后的代码,提高逆向分析效率

a2645099022的博客 1369

OV5645摄像头原理图设计,已量产

在嵌入式视觉项目里,摄像头小板真正决定量产稳定性的,往往不是把传感器接出来这件事本身,而是电源域有没有拆清、参考时钟够不够干净、主板调试时有没有留出可落地的接口。ATK-MC5645 这张原理图的价值,就在于它没有把 OV5645 裸接到一排连接器上,而是把 AFVDD_2V8、DOVDD_1V8、DVDD_1V5、24MHz 时钟和 MIPI/控制信号都收敛在一块独立模组上。

不脱发的程序猿 328

switch case 异步机制的简单实现

而非阻塞状态机里的实现也是查询时间,但要根据其返回值来判断是否到没到时间,到了就手动加一步,下次轮询该任务就可以执行下一步了,没到就退出,就可以轮询下一个任务了。异步是在线程之内的,没有异步机制的话一个线程里就不能同时跑多个任务,多个任务只能挨个进行,就像执行普通函数那样。异步机制本质上就是非阻塞状态机(switch case),单片机常用,这是一个系统化的工程,需要让其所有功能都是非阻塞的,也就是让所有的阻塞的功能都要通过不断查询来获得结果,然后再将这些需要查询的功能每个都单独放一个 case 里。

qq_41544116的博客 644

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

AST处理switch语句

其中 discriminant是swicth判断值,cases是一个数组,里边有每个case是中代码块。其中consequent是一个数组,里边是要执行的语句。test是case后边的具体值。一个标准的switch语句js模板如下。

之度的博客 548

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

某酷ckey签名生成算法系列--(三)ast代码控制流平坦化

某酷ckey签名生成算法系列--(三)ast代码控制流平坦化 观察三个switch的值分别是Ci、mi和Ai。而这三个值又因为li的确定而确定的。也就是说已知li的值,就可以分别计算出Ci、mi和Ai,就可以确定执行的是哪一条语句。那么反过来,如果已知Ci、mi和Ai三个值,也可以反向计算出li的值了。 那么按照这个思路。就可以把多重的case的值计算出其对应最上一层的值,变成最简单的switch结构,形如下面 ******省略代码****** for (var li = 16996; void 0 !=

zjq592767809的博客 1854

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

AST解web控制流平坦化

此代码可以解决大部分 while if else 控制流平坦化

weixin_50108437的博客 687

Python操作AST解JS混淆

通过生成语法树(AST),可快速修改代码中的一些混淆处理,从而简化代码,便于后续分析。

李玺 5762

Stable Diffusion网络结构模块解析之switch

​ 在深度学习模型中,注意力机制是一种让模型能够“集中精力”在某一部分数据上的方法。就像你在做数学题时,可能会更关注那些复杂的计算步骤一样,模型也需要在处理数据时关注最重要的部分。

介绍AI领域相关知识 1145

AST反混淆实战|某里滑块三重switch压缩思路分享

本文思路源自 渔滒 的某里AST反混淆代码,本人仅在此做个归纳总结,并感谢渔滒的思路。因法律风险,不提供源码,只有思路,所以请不要问我要源码。最新版的某里已经到了140前缀(几个月前)...

Python3 爬虫实战 1:应对特殊字体,爬取猫眼电影实时排行榜 2758

ast反混淆进阶--反控制流平坦化

实现目的:标识符简化 处理前: var _0xb28de8 = "3|1|2".spilt("|"), _0x8de8b2 = 0; while(true) { switch(_0xb28de8[_0x8de8b2++]) { case '1': var a = 1; continue; case '2': var b = 3; continue;

jia666666的博客 2783
上一篇: 关于某日头条_signature参数逆向生成纯补环境
下一篇: 关于js逆向某日头条参数_signature生成中jsvmp的补环境
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值