JS 的一些零零碎碎笔记
在 JavaScript 中,atob() 函数是用于解码 Base64 编码的字符串的函数。与 atob() 配套的函数是 btoa(),它用于将字符串编码为 Base64 格式。
1, atob() 函数 功能:将一个 Base64 编码的字符串解码成普通的字符串。
let decodedString = atob(encodedString);
let base64Str = "SGVsbG8gd29ybGQ="; // "Hello world" 的 Base64 编码
let decodedStr = atob(base64Str);
console.log(decodedStr); // 输出: "Hello world"
2. btoa() 函数 功能:将一个普通的字符串编码为 Base64 格式。
let encodedString = btoa(plainString);
let str = "Hello world";
let base64Str = btoa(str);
console.log(base64Str); // 输出: "SGVsbG8gd29ybGQ="
atob('aHR0cHM6Ly93d3cudG9vbGhlbHBlci5jbi9FbmNvZGVEZWNvZGUvQmFzZTY0')
'https://www.toolhelper.cn/EncodeDecode/Base64'

如果数据太长,不好拷贝, 你使用copy 一下. 就是数据拷贝. copy(b(e.Data)) 使用这个命令,数据就到了剪贴板. 不管文本有多么长, 都可以这样的 copy .
{
"PageTotal": 132,
"PageNo": 1,
"Total": 920,
"PageSize": 7,
"Table": [
{
"ID": "3980",
"TITLE": "存在“串通投标”行为是否一定构成串通投标罪?",
"TYPE": "13",
"TM": "2025-04-22 09:46:35"
},
{
"ID": "3979",
"TITLE": "省发改委宣传推广35个优化营商环境工作典型经验做法",
"TYPE": "13",
"TM": "2025-04-22 09:40:39"
},
{
"ID": "3978",
"TITLE": "关于水利工程建设招标投标领域突出问题的通报",
"TYPE": "13",
"TM": "2025-04-22 08:59:17"
},
{
"ID": "3952",
"TITLE": "逾期递交履约保证金,招标人取消其中标资格反而违法!",
"TYPE": "13",
"TM": "2025-03-26 14:40:15"
},
{
"ID": "3951",
"TITLE": "省纪委监委通报6起重点领域公共资源交易腐败典型案例",
"TYPE": "13",
"TM": "2025-03-26 14:23:31"
},
{
"ID": "3948",
"TITLE": "【通报】未取得施工许可证违法施工!福州一房企被处罚",
"TYPE": "13",
"TM": "2025-03-18 18:53:52"
},
{
"ID": "3947",
"TITLE": "问答 | 公开招标货物项目,供应商提供制造商的业绩,有效吗?",
"TYPE": "13",
"TM": "2025-03-18 18:49:26"
}
]
}
参数的 几个
“type”: “12”,
“pageSize”: 9,
“ts”: int(time.time()*1000),
这些都是配套的, 你能够修改,但是,请求被拒绝, 代表是 本地 js 构建 加密请求参数的一部分. . 所以, type, pageSize, ts , 等, 都是 加密的组成要素.
核心就是,保护变化的数据的修改. —> 目的就是限制你的批量. 获取我的数据!! —- 必须限制!!!
请求无非是放在键值对里面, 所以对特殊的关键字, header中的, sign == signatures. 作为搜索的关键的位置. —> JS 中有 encrypted , 或者 decrypted. 等函数. 直接打断点.
怀疑, portal-sign’: ‘60571faea9adbac216c17c6cfd154c9d’, 这个参数, 没有 100% , 只有大概率. –> 他的下一步, 一定是组装, 键值对, 下一步组装 headers, 下一步发送 Ajax. 请求. 所以全局搜索, “portal-sign” , 然后 ,加入一个断点进行测试. 然后右边有跟着的函数的进行测试.
如果连 键不能搜出来. … 那么一定在 header 里面. 爬虫,就是连蒙带猜.
前面搜索 , encrypt( 这个关键字, 直接搜索出来函数.
header 中的 , 携带的参数, 越是生僻, 那么就会越, 搜到的概率就会越大.
真正起到加密作用的地方.

定位 到我想要的 sign 的位置

每个请求,都要走这里, 所以,多次的 run , 然后每次调用都会停下来, 为了达到我们想要的 值, 所以,我们需要, 做一个条件断点 … .
普通断点
条件断点
日志断点
XHR断点

激活断点和取消断点.

判断是否有这个值. 条件断点, 如果为真,那么继续执行.

条件断点的配置

条件断点的生效

取得想要的值

解读代码逻辑:
function d(t) {
for (var e in t)
"" !== t[e] && void 0 !== t[e] || delete t[e];
}
这段代码的作用是:**
遍历对象 t 中的所有属性。
检查属性值:如果属性值不等于空字符串 ("") 且不等于 undefined,则保留该属性;否则,删除该属性。
代码执行流程
输入对象 t:
let t = {
ts: 1745519867513,
type: "12",
IS_IMPORT: 1,
pageSize: 3
};
初始对象 t 是:
{
ts: 1745519867513,
type: "12",
IS_IMPORT: 1,
pageSize: 3
}
调用函数 d(t):
调用 d(t),执行函数中的遍历和条件检查。
遍历对象属性:
e = "ts":
t["ts"] = 1745519867513
1745519867513 是一个有效值(非空字符串、非 undefined),因此该属性保留。
e = "type":
t["type"] = "12"
"12" 是有效值,保留该属性。
e = "IS_IMPORT":
t["IS_IMPORT"] = 1
1 是有效值,保留该属性。
e = "pageSize":
t["pageSize"] = 3
3 是有效值,保留该属性。
结果:
因为所有属性都有有效值(它们都不等于空字符串 "" 且不等于 undefined),所以没有属性会被删除。对象 t 保持不变:
{
ts: 1745519867513,
type: "12",
IS_IMPORT: 1,
pageSize: 3
}
总结
该函数的作用是遍历传入的对象 t,删除所有值为 空字符串 或 undefined 的属性。在此例中,所有的属性值都是有效的,所以对象 t 在执行完该函数后不会发生变化。

固定的—含义—- 就是每次发送请求是不是变化的, 如果是变化的,找到它生成的位置.
刷新,一下, 看变不变, 不变化的话,直接将他处理成为固定的字符串即可.

刷新后,到了这个位置, 自动停了下来.

经过刷新,两次对比,相同,是固定的.

s的位置, 看来,就是一个 new Md5

为了测试 ,是否是标准的 md5, 看一下, 测试, 123456, 是不是, e10adc3949ba59abbe56e057f20f883e


发送数据的提交格式, 就是使用, json 格式作为参数, post 的请求.
python 通过添加到 sign 后的 值,作为 key, value 作为字典,传递到, header 这个超级大的数组里面.

排序按照 ASCII 码 一位一位进行的排序, 如果是 1, 100, 20, 那么就是 1, 优先, 2组, 其后是 20

关于header 头的修改和拼接
目的, 让你无法直接全局搜索到, 这个 portal-sign , 如果是特殊处理了, portal + s + i + g + n 这样的做法. 需要什么? 未知.

这里的一些值,可以向下传递.
最后一步肯定是有加密值的. 如果连续传递参数,那么, 有的 函数位置没有生效, 通过, 调用栈, 可以切换到,有效的值.
test1() // 1 .里面定义
test2() // 2, 里面执行.
test3() // 3, 刚好切换到时候, header 直接处理了!
test4() // 4, 直接处理了.
掐头去尾, —-> 目的是为了控制变量.—–>
它有回溯,继续往回找, 直到找到它的位置即可….


以上的, 就是 启动器的 处理的位置.
———> 倒数第1步
———> 倒数第2步
———> 倒数第3步
———> 倒数第4步
———> 倒数第5步
———> 倒数第6步
每一个都有自己的请求堆栈.
XHR 断点, 就是 xml + http + request ,的这样的断点.

可以把这个勾取消掉. 就是标准的 XHR 断点.
使用了webpack 举例

这里的问题是怎样的解决?
最后要实现的是,这样的请求, 通过它而且让他存在!
———–> 如果解密是一套逻辑, 没有必要从新写一遍了.
拦截器 VUE, 其他前端框架
回来时候, 有响应的 Ajax 响应的拦截器.
还有发送拦截器. —>解开之后 ,再各自回到 自己的响应中去.
回来这一环 , 解密 , 是需要统一做的.
刚才的 是发送拦截器,
解密————–> 响应拦截器 ——> interceptors.request.use(function1)
发送请求——> 发送拦截器,统一发送需要做的.携带 token , authornizetion. 等等. interceptors.request.use(function2)
19:38 , 语法就是 interceptors.use.
js语法中, shirt , 和 unshift , 剔除 和 加入.
n = n.then(t, shift(), t. shift()); —> 那么这里的 t 就是数组.

r.prototype.request = function(e) { // 定义一个名为 request 的函数,这个函数是 r 对象的原型方法
"string" == typeof e ? (e = arguments[1] || {}, // 如果 e 是字符串(即第一个参数是 URL),则取第二个参数为配置对象,若没有则使用空对象
e.url = arguments[0]) : e = e || {}, // 如果 e 不是字符串,则确保 e 为对象,若 e 为 null 或 undefined 则赋为空对象
e = u(this.defaults, e), // 合并默认配置和传入的配置(u 是一个合并函数)
e.method = e.method ? e.method.toLowerCase() : "get"; // 如果配置中有 method 字段,则转换为小写,否则默认为 "get"
var t = [a, void 0] // 初始化一个数组 t,包含两个元素,a 和 undefined(a 可能是某个函数)
, n = Promise.resolve(e); // 使用 Promise.resolve 包裹配置对象 e,返回一个已解决的 Promise 对象
// 遍历 request 拦截器,插入到 t 数组的前面(拦截器的作用是修改请求)
for (this.interceptors.request.forEach(function(e) {
t.unshift(e.fulfilled, e.rejected) // 将拦截器的 fulfilled 和 rejected 函数添加到 t 数组的前面 --- > 往左边扔下去.
}),
// 遍历 response 拦截器,插入到 t 数组的后面(拦截器的作用是修改响应)
this.interceptors.response.forEach(function(e) {
t.push(e.fulfilled, e.rejected) // 将拦截器的 fulfilled 和 rejected 函数添加到 t 数组的后面
}); t.length; ) // 当 t 数组还有元素时继续执行以下代码
n = n.then(t.shift(), t.shift()); // 依次取出 t 数组的元素,并作为 then() 的参数传递,形成 Promise 链
return n // 返回最终的 Promise 对象
}

这里双击可以跳到 js 函数的定义的位置


直接打印 t 的值, 可以知道 , 包不包含, 变量的这个值.
因为是循环拦截器, 里面包含了不同的 功能的模块的 function, 每个请求的头的目的不同,携带的参数不同, 数量不同, 既然, 头 和 尾巴, 都下了断点, 那么 run 即可嘛, 对不, 中间执行完成, 不管干了什么, 直接看结果不可以吗?
多释放,几次, 总会循环到, 带有 url 中包含了 PageSize 的那个参数的 请求对不.

以上是 71节, 请求拦截器
73节 JS 逆向
这里通过 js 的 NodeJS 调用执行. 一个 md5 的算法和 AES的加密算法, 的具体的流程

常规的第一参数就是使用的是, (第一参数, 加密内容, 第二参数, key) ==> JS 代码中, 看见的默认的就是这样的.

在这里是演示的条件. 所以的 扣代码,时候, 能够验证是否正确. 目的是扣到到位, 而不是全部抠下来.包括我们的webpack 都是辅助我们做这一行的.
可以直接 copy 对象—-> 对象就是 nodejs 解密脚本里面的data 值
输入的值需要等待——> 福建资源wang 需要等待, js 才能加载完成.

扣 函数到 nodejs 执行
function u(t) {
for (var e = Object.keys(t).sort(l), n = "", a = 0; a < e.length; a++)
if (void 0 !== t[e[a]])
if (t[e[a]] && t[e[a]] instanceof Object || t[e[a]] instanceof Array) {
var i = JSON.stringify(t[e[a]]);
n += e[a] + i
} else
n += e[a] + t[e[a]];
return n
}
function l(t, e) {
return t.toString().toUpperCase() > e.toString().toUpperCase() ? 1 : t.toString().toUpperCase() == e.toString().toUpperCase() ? 0 : -1
}
function d(t) {
for (var e in t)
"" !== t[e] && void 0 !== t[e] || delete t[e];
// var n = r["a"] + u(t); // 这里的 webpack 的值是固定的. 所以直接给出使用即可.
var n = "B3978D054A72A7002063637CCDF6B2E5" + u(t);
cryptoJs = require("crypto-js")
// md5摘要
// 原始数据
// const data = '123456';
// 生成MD5摘要
// const md5Digest = cryptoJs.MD5(data).toString();
// console.log("md5:", md5Digest)
// return s(n).toLocaleLowerCase()
return cryptoJs.MD5(n).toString().toLocaleLowerCase() // 这里是 md5 , 通通都是本地 JS 构建的. 分分中,我不需要读取这些逻辑.
}
data = {
"ts": 1745778580743,
"type": "12",
"IS_IMPORT": 1,
"pageSize": 3
}
console.log(d(data))
webpack 算是一个小的中级知识点. 这里会涉及到一个简单的调度器, r 你就拿不到. 那么你就做不出来这个案例. 这个案例, 好在哪里呢? 就是 r 这个取到的是固定的值, 因为是固定值,所以直接, 替换到这里 , 就可以挡住了这个位置. 因为在 webpack我的那个值不会拿,所以我使用了一个死值, 万一别人用的变化的值,那么就没有了招式.
你往这里一倒腾, 它就是一个调度器调度的対象. 只不过这个案例这个巧合,使用了固定值. 学完后, 自己使用webpack 去做一做. u(t) 的这个方法,直接使用, 即可, 所以这里直接报告 u(t)的错误.



现在的这个sign 值的已经构建出来了.
JS 如果接下来, 你需要发送 AJAX,
当前的这个整个逻辑都是 js 完成的. 虽然求道的值,是sign值, 但是 有url , 有数据,,有 headers.
到最后 ,发送请求, 分布式, 还是需要python 进行写, 核心解密还是 js 写的. 基本的参数还是请求, 基本的参数还是要从文件中请过来. 逆向这个加密函数时候,
这里设计, 到 了 ,怎么使用 python 的程序去拿 JS 的代码?
这个库的名字叫做, execjs, 来自 pip install PyExecJS 的这个库
execjs, 需要使用一个i编译器, execjs.compile () 就相当于js的一个驱动.相当于调用nodejs 去执行我们的这个函数. 然后去执行我们的编译对象. 这个就是 js 的那个编译对象, 一旦, js 编译器把对象跑起来了, 那么可以调用任意的一个函数, 在 python中,
调用了一个 d 函数, 又没有传递参数.
我们在python 中是需要动态,来,把数据传递给js文件.
最后, 我根本不需要了解这个js 的一套逻辑. 直接扣取这个拷贝.
这个就是 最后的, 效率比python快多了.
到了目前为止,我们在请求上面使用的是, js 调用, 那么响应的 这个部分, 我使用的是 python 的逆向. 那么响应的 response 这个部分, 可以修改成为, JS 逆向.
解决 python 实现的 AES 解密, 通过 js来实现, 通过找到, decrypt( 这个关键字搜索,找到,但是目前的实际情况是.
首先在js 测试来实现.
, n = h.a.enc.Utf8.parse(r["i"])
, a = h.a.AES.decrypt(t, e, {
h.a. 这些东西, 你是造不出来的,我们就该从变量里面拿出来,
报错这个.
ReferenceError: r is not defined
at b (/home/calleng/PycharmProjects1-py390/Project_6T/JS-reverse-6th/Day22:JS逆向实战案例解析三/step3_搬家_js_本地nodejs执行.js:60:41)
at Object.<anonymous> (/home/calleng/PycharmProjects1-py390/Project_6T/JS-reverse-6th/Day22:JS逆向实战案例解析三/step3_搬家_js_本地nodejs执行.js:72:13)
因为是 固定的, 所以没有必要寻找它的来源.就是key 和iv
r['e']
'EB444973714E4A40876CE66BE45D5930'
r['i']
'B5A8904209931867'
所以在这里可以直接进行替换.
如果是动态生成的那么, 得找到它的生成的函数.
如果是固定的东西, 直接拿走.
我们就是一个原则, 怎么舒服怎么来。
74节, douyou案例解析
3,4,5,6,期数, 案例,都不一样, 每一期 都有 10几个案例, 加起来该有60多个案例了.跟着老师 ,独立走上20个, 一定要在老师讲完的基础上, 自己在完整的复盘一遍, 不断穿插着,独立做一些案例, 就是你弄的再明白, 怎么化为自己用, 你得独立做上20,30个案例, 同学们, 这个过程走通了, 没有走通的..
比的就是谁能够将案例做完. 60个案例 ,做完, 还有不会?
做一个全局搜索. 全局搜索password

加上冒号

不是这里. password 后面应该跟着一个函数.
所以引号 + 正则, 所以只有1个函数.


import time
from hashlib import md5, sha1, sha256, sha512
# (1) 构建摘要对象
# hash_md5 = md5()
hash_md5 = sha1() # 40位
hash_md5 = sha256() # 64位
hash_md5 = sha512() # 128位
# (2) 对内容进行摘要,update的参数字节
# data = "alex is a stupid donkey!!! "
data = "123456"
# data = "yuanlaoshi贼拉帅"
# data = str(time.time())
hash_md5.update(data.encode())
# (3) 查询摘要结果
print(hash_md5.hexdigest(), len(hash_md5.hexdigest()))
print(hash_md5.digest(), len(hash_md5.digest()))123456 就是这样的输出, 记住, 有助于快速判断
7c4a8d09ca3762af61e59520943dc26494f8941b 长度–> 40 位
b’|J\x8d\t\xca7b\xafa\xe5\x95 \x94=\xc2d\x94\xf8\x94\x1b’ 20
75节, 比价格器
常规的值的变化, 有两种, 1, 是 random 的随机值的变化, 另外一个是, timestamp 的时间的随机值…
因此对比变化的是这样的.
通过时间固定器,控制变量, 给他固定下来.
Math.random = function (){
return 0.1
}
Date.prototype.getTime = function (){
return 1719407831657
}
这是再 nodejs 解释器中给了限制住了,两个变量.
那么, 再浏览器中,限制, 1,从新加载? 2, 不重新加载的前提下,直接给 ramdom 赋值固定?

这个案例, 挑战性难度还要大一点的.是, 做一做,我们还要给大家讲一讲, 这个 key1,
window.jsSHA = jsSHA;
$.jCryption.setkey = function(e, n, maxdigits) {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var string_length = 128;
var randomstring = '';
for (var i = 0; i < string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
randomstring += chars.substring(rnum, rnum + 1);
}
$.jCryption.key0 = (new jsSHA(randomstring,"ASCII")).getHash("SHA-512", "HEX");
$.jCryption.key1 = "";
var jCryptionKeyPair = function(encryptionExponent, modulus, maxdigits) {
setMaxDigits(parseInt(maxdigits, 10));
this.e = biFromHex(encryptionExponent);
this.m = biFromHex(modulus);
this.chunkSize = 2 * biHighIndex(this.m);
this.radix = 16;
this.barrett = new BarrettMu(this.m);
};
$.jCryption.encryptKey($.jCryption.key0, new jCryptionKeyPair(e,n,maxdigits), function(key) { // 它的这里面使用了一个回调的值,使用了一个值, 是key0, key0 产生了.key1,
$.jCryption.key1 = key;
});
}key.length = 513
key “5fd37f4d03fbb2447e3f03b62eaf836f5140d9454b4a4e6d6ce5afbaed44221aa27ddd5fa8acedd843ed42ca57ad25ab35f15ca1a9896278a8a3666ed7d7bcd92bb0b2a12e0a1081ef463d97817e482b0e9192d2ab9d4255141ec304b932365d47d6e46d0794f501c90335c8aa53831ae619fc259f7b8fbdf2178e921001115d 519bdd848f73cb1542db708dd3465d4780e5aee371a24545229cc992891a62a762fd0ab9bc29e64a16d0ca4f3063fc6f946f067dac65770c82838ef640ce63803e08558988147ec61c7ecce0918836cf0e8f9c9f63ca095a8219a8e6d918a6e14238121d704a99f0617575206d023ee01e721ca8ac1b5018d96870e7af3acc11”
上面就是key值.
想要知道这个key , 通过堆栈就知道,它是怎么过来的. 来自实参的key ,你得找到它调用的位置.




var crypt = keyPair.barrett.powMod(block, keyPair.e);
var text = keyPair.radix == 16 ? biToHex(crypt) : biToString(crypt, keyPair.radix); // text来自拼接 ---> 这里做了 三目运算,生成的.
encrypted += text + " "; // encrypted来自拼接的
charCounter += keyPair.chunkSize;
if (charCounter < encryptObject.length) {
encryptChar();
} else {
var encryptedString = encrypted.substring(0, encrypted.length - 1);
if ($.isFunction(callback)) {
callback(encryptedString); // encryptedString 来自于被 encrypted.substring 截断的.
} else {
return encryptedString;
}找到了关键的运算的位置,我们找到调用的 回的key .我们再它的上面就可以 找到它的来源了.
76节 baiyun Airport 案例

使用正则表达, \b 代表是边界感, 不会存在, Signature前面的单词, 比如 countSignature 这样的单词就不会匹配了.
在这个地方,添加一个断点. —> 条件断点.
e.url.indexOf(“list”) !==-1

只要这个 url 里面 包含list , 那么, 就代表找到了.

这里点击,加载更多, 它会直接触发这个list

先把e的值留在这里

e的 值会生成, {signature: r, timestamp: o, nonce: a} 这几个值. 总之, 就是这个pe(e); 函数生成的.

PE 这是一个箭头函数

折叠后, 右边往左边复制,整体,
然后,形成一个参数
const pe = e => {
let n = ""
, t = e.url;
if (e.params) {
let t = {}
, r = JSON.stringify(e.params)
, o = JSON.parse(r)
, a = Object.keys(o).sort();
for (let e = 0; e < a.length; e++) {
let n = o[a[e]];
null != n && (t[a[e]] = String(o[a[e]]))
}
n = JSON.stringify(t)
} else if ("post" === e.method || "put" === e.method)
n = JSON.stringify(e.data);
else {
let {params: e, reqUrl: r} = be(t);
"" != e && (n = e),
"" != r && (t = r)
}
let r = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (e => {
const n = 16 * Math.random() | 0;
return ("x" == e ? n : 3 & n | 8).toString(16)
}
))
, o = (new Date).getTime()
, a = de(n);
return {
signature: fe("-----BEGIN PRIVATE KEY-----\n MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCdgJNfUFPDNJsL\n HObB1JMu7E1+nuwkFHmXnBU2AOM2dweE+tpmViZo90w+YQIuIS8MoVz60AGHbLE8\n BYcdxQEKmPsqq0Lq/1ltIdp1YcO9W60qSxwpZS+7o73ljRrrtOXcE1UUpH5l07Fh\n ziCIRDI/4ODCA8AJ1kV6IyfPNM2Fes3BEqhMOgw4Z5i4pZHnb4Nm+4kEXmyM+UgQ\n cShcXZA/dx5MXKA2Bbb0I0G6HS3D4nMhnm6IgYWEyT8ngenMOyy+ysBuHWt2j9Cp\n AGLWRyqHigFcKTlP5BSIkU+8sqssab1jvDg2F8MXWuupwF43OVARgHofiwQBAHPo\n PfTfPlMvAgMBAAECggEBAJKQpZNasrfCak0LFgllgZl2uB6OUPy6OPRGgM6CQO3c\n EhlDPp1gqdmf10ltCJRYuOmt91JG4kVddgh+tF+VhgSQm5n3SQxZlqQhjqMQ2Q+L\n Ejd7Mberu6GHHB1TE6wn6IbFTrUo5Z5oQnbbVBa6L3CWGVEyIDCHPpwLvu3pGx+L\n 083dNQUiF8WcSGybl1h4ZapAGdndPYJReKYccNBYu5IzTEjtG3VpMHl56hD8fPV8\n SStYv4sEffyCbze5/KvG3WlG+8n1WzBRMAN1U8Qk3JlMM/g5Y2tL1elI2pQRmjH8\n EVxNUzB9Ob/qk2N6pF4KwhDWjILkHdoXilHMgP5x0gECgYEAy9O9ShtRNwXdFzHe\n v+buyjvWWvwTVRUBehe8BWO1QaZ4c/INw1Ks4pgoKvXyU1DRx5OloIx6BWDbs00O\n 1W1cDue2I/Ymvx5Q/XJmZK4eR2U3a2dmKLKVhCXhJ3y02R/OZ2xQHV3NZXqz88kf\n rEmEKYTW9q2gVsZa82XpQhKnBYECgYEAxdFJ55AkU7VfzpV1x68NUetomB3OWxyq\n Cugn3STLNx7Jw6FaK3dwRz0eKIbwCRxtlluZmxWX0jWSvj3cyLRBIKTD8atUfJW2\n +ESKZb/i961HhhQjXqNfGQpmMdEazNqv0sDzQ5jHHIjc63oty/FjckcC+AaDGZIJ\n VGCet5J5kK8CgYBm2R/Bfgk792R5KLvaHz/MoebmoB1tKB1HqyQ/n/E9AC/1aWUS\n cuwzpk1WaCXvbm98Af9oBJopjpctYSuj+/ugtcDNYo5oj3aUfJ44HTfAFM2jD1iY\n HoydUrPKxf1HNepje17tgoB6vTCCSbEGsU3T2WjSrgei4ZHREVJi+aB3gQKBgEy8\n rm2sxdrPHjZWVlU6+/DOYEm6LkW77d7DRkuMLWTZha1lF0SLVbvc4qkYB1+RbpWI\n PSMjEj0SWTWBa/dTrXwLTpOeQez+avcOJ53m/RXVW0yQ3VOmDor5NMGYe0wCfXhF\n L1kGmB7inMigIcnefxRipa0vYYX217WqsYdGw++zAoGBALKswyV5j1GjVjN+fS1t\n N9R0x+S7cKBqW6Bwj6aAdo4+spmRn9WK4h9Zk2k7BMUiqJKTce6RdW0Ep+aTErRs\n LL0sBHArhQdaQvq0yS57BJUZm3ASrOpp3wkQdDejS3YEKiIQSG2kNFRanh8RbtbA\n ac7pfLikyQm795/qF0H9YHgF\n -----END PRIVATE KEY-----", "Timestamp&" + o + "&" + t + "&" + r + "&" + a),
timestamp: o,
nonce: r
}
}加上一个 测试代码.
使用 pe(e) 传参 e. 复制 object e.
let e = {
"transitional": {
"silentJSONParsing": true,
"forcedJSONParsing": true,
"clarifyTimeoutError": false
},
"adapter": [
"xhr",
"http"
],
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 30000,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1,
"env": {},
"headers": {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json;charset=utf-8"
},
"baseURL": "/",
"url": "/byairport-flight/flight/list",
"method": "post",
"data": {
"type": "1",
"terminal": "",
"day": 0,
"depOrArr": "1",
"pageNum": 1,
"pageSize": 15
}
}
console.log(pe(e))
通过找到 x
/home/calleng/PycharmProjects1-py390/Project_6T/JS-reverse-6th/Day23:JS逆向实战案例解析四/baiyun_airport_step2.js:47
const t = x.getKey(e)
^
ReferenceError: x is not defined
at fe (/home/calleng/PycharmProjects1-py390/Project_6T/JS-reverse-6th/Day23:JS逆向实战案例解析四/baiyun_airport_step2.js:47:15)
at pe (/home/calleng/PycharmProjects1-py390/Project_6T/JS-reverse-6th/Day23:JS逆向实战案例解析四/baiyun_airport_step2.js:29:20)
at Object.<anonymous> (/home/calleng/PycharmProjects1-py390/Project_6T/JS-reverse-6th/Day23:JS逆向实战案例解析四/baiyun_airport_step2.js:96:13)
at Module._compile (node:internal/modules/cjs/loader:1730:14)
at Object..js (node:internal/modules/cjs/loader:1895:10)
at Module.load (node:internal/modules/cjs/loader:1465:32)
at Function._load (node:internal/modules/cjs/loader:1282:12)
at TracingChannel.traceSync (node:diagnostics_channel:322:14)
at wrapModuleLoad (node:internal/modules/cjs/loader:235:24)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:170:5)

上面的小的区域是不用看的.
最快的方式 是 正则, \bx\b 把正则的开关打开

这个函数不用看, 是一个自执行的匿名函数

所以这个 x 是一个全局的.
里面没有, 那么你只能够, 找到导入的别名的变量.

复习一下箭头函数,–> 76 section–> 20:58
77节 diandian案例
只要在开发者模式下, 直接选择第三个选项
右键点击刷新按钮(地址栏左边的 ⟳ 图标)
选择第三选项: 清除缓存并硬刷新

