Skip to content

逆向备忘录

更新: 2025/2/24 字数: 0 字 时长: 0 分钟

常用函数

base64编解码

浏览器环境中进行 base64 编解码使用全局的 window 对象自带方法:

  • atob('字符串') 将字符串进行 base64 解码;

  • btoa('字符串') 将字符串进行 base64 编码;

20220212220909

Node 环境中进行 base64 编解码使用特定模块 Buffer 对象方法:

  • Buffer.from('字符串').toString('base64') 将字符串进行 base64 编码;
  • Buffer.from('字符串', 'base64').toString('utf-8') 将字符串进行 base64 解码,在进行 utf-8 编码;
javascript
// Base64 编码
const originalString = 'Hello, World!';
const base64Encoded = Buffer.from(originalString).toString('base64');
console.log('Base64 编码:', base64Encoded);

// Base64 解码
const decodedBuffer = Buffer.from(base64Encoded, 'base64');
const decodedString = decodedBuffer.toString('utf-8');
console.log('Base64 解码:', decodedString);

字节数组转换

CryptoJS 库提供了在 CryptoJS 中的字节数组(WordArray)的转换方法:

  • CryptoJS.enc.Utf8.parse('字符串') 用于将 UTF-8 编码的字符串转换为 CryptoJS 中表示的字节数组(WordArray);
  • CryptoJS.enc.Utf8.parse(字节数组) 用于将 CryptoJS 中的字节数组(WordArray)转换为 UTF-8 编码的字符串;
javascript
// 导入CryptoJS库
const CryptoJS = require('crypto-js');

const Str = 'Hello, World!';
// 将UTF-8编码的字符串转换为CryptoJS中表示的字节数组(WordArray)
const wordArray = CryptoJS.enc.Utf8.parse(Str);
console.log(wordArray);

// 将CryptoJS中的字节数组(WordArray)转换为UTF-8编码的字符串
const utf8String = CryptoJS.enc.Utf8.stringify(wordArray);
console.log(utf8String);

提醒

CryptoJS 库本身不是原生的 JavaScript 标准库的一部分,你需要通过 npm 或其他方式安装它,然后在你的代码中导入它。代码中的 require('crypto-js') 是一种导入 CryptoJS 的方式。

在 JavaScript 中,你可以将 Uint8Array 转换为字符串的方法有多种,以下是常用的两种方法:

方法 1: 使用 TextDecoder

TextDecoder 是一种用于将 Uint8Array 等编码字节数组解码为字符串的 API。它支持多种字符编码。

javascript
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // 示例 Uint8Array
const decoder = new TextDecoder('utf-8');
const string = decoder.decode(uint8Array);

console.log(string); // 输出 "Hello"

方法 2: 使用 String.fromCharCodeapply

String.fromCharCode 方法将 UTF-16 代码单元转换为字符串。你可以通过结合 apply 方法,将 Uint8Array 转换为字符串。

javascript
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // 示例 Uint8Array
const string = String.fromCharCode.apply(null, uint8Array);

console.log(string); // 输出 "Hello"

方法 3: 使用 String.fromCharCodemap

如果你担心数组长度过大导致 apply 方法超出函数参数数量限制,可以使用 map 方法:

javascript
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // 示例 Uint8Array
const string = Array.from(uint8Array).map(byte => String.fromCharCode(byte)).join('');

console.log(string); // 输出 "Hello"

方法 4: 使用 TextDecoderStream

如果你在处理流式数据时,可以使用 TextDecoderStream,它是一个高级用法,可以直接将 Uint8Array 数据流解码为字符串。

javascript
const uint8Array = new Uint8Array([72, 101, 108, 108, 111]); // 示例 Uint8Array
const readableStream = new ReadableStream({
  start(controller) {
    controller.enqueue(uint8Array);
    controller.close();
  }
});

const decoder = new TextDecoderStream();
readableStream.pipeThrough(decoder).getReader().read().then(({ value }) => {
  console.log(value); // 输出 "Hello"
});

选择合适的方法

  • 如果你处理的是 UTF-8 编码的文本,推荐使用 TextDecoder
  • 如果你只需要简单转换,并且数组较短,String.fromCharCode.apply 是快捷的方法。
  • 对于更长的数组或特殊情况,map 方法可能更适合。

要将你提供的 JS 中的 Uint8Array 对象转换为 Python 中的字节数据,可以使用类似的方法。以下是完整的代码和步骤。

JSON 对象

json
{
    "0": 102,
    "1": 178,
    "2": 173,
    "3": 4,
    "4": 67,
    "5": 210,
    "6": 166,
    "7": 172,
    "8": 254,
    "9": 59,
    "10": 142,
    "11": 84,
    "12": 130,
    "13": 132,
    "14": 174,
    "15": 22
}

Python 转换代码

python
# 定义 JSON 对象
json_object = {
    "0": 102,
    "1": 178,
    "2": 173,
    "3": 4,
    "4": 67,
    "5": 210,
    "6": 166,
    "7": 172,
    "8": 254,
    "9": 59,
    "10": 142,
    "11": 84,
    "12": 130,
    "13": 132,
    "14": 174,
    "15": 22
}

# 提取值并转换为 bytes 对象
byte_array = bytes(json_object.values())

print(byte_array)

输出

byte_array 将会包含以下字节数据:

python
b'f\xb2\xad\x04C\xd2\xa6\xac\xfe;\x8eT\x82\x84\xae\x16'

解释

  • json_object.values(): 提取 JSON 对象中的所有值,这些值构成一个序列。

  • bytes(json_object.values()): 将序列转换为 bytes 对象,生成的字节串可以在 Python 中直接使用。

这种方法非常适合将类似的 JSON 对象转换为字节数据,以用于各种需要二进制格式的应用场景。

逆向技巧

关键字搜索

一般来说,网站会加载含有加密算法的 JS 文件来加密数据,因此就会包含一些常见的加密关键词 crypto加密Encrypt加密方法Decrypt解密方法MD5哈希DES加密AES加密Base64编码 等,那么我们就可以通过关键词进行搜索、查询找到加密的位置,从而逆向还原加密过程。

拦截器定位

**拦截器通常是指在网络请求或异步操作中插入自定义逻辑的一种模式。**有的反爬网站在请求的时候,会返回加密的数据,再看 Initiator 启动器,会发现是异步的加载,那么数据解密操作就很有可能是在拦截器里面进行的:

image-20231224135610965

image-20231224140059485

**这里我们就可以全局搜索一下 interceptors 拦截器关键字,选择第一处进入,可以看到 a.interceptors.request.usea.interceptors.response.use 就是请求和响应拦截器,它不会直接拦截异步加载的过程,但可以在发送请求之前或在接收到响应之后执行自定义逻辑。**因为我们是逆向数据解密的过程,因此在 a.interceptors.response.use 处打上断点进行调试:

image-20231224140652391

抠代码

函数拷贝

在抠代码过程中,我们可能会遇到内容特别长的函数,例如下图的 window.md5 函数就特别长,如果用鼠标选中 JS 代码进行复制的话,会选中很长一段,而且在鼠标滑动的途中还可能中断,很不方便。这里我们就可以通过在 console 中输入 window.md5.toString() 将函数变成字符串拷贝出来。

20211015143403

**如果不想复制,还可以在 console 中输入 copy(window.md5.toString()) 将函数字符串拷贝到剪切板。**不过首次使用 copy() 命令可能会给出如下警告:不要将你不理解或没有复习过的代码粘贴到 DevTools 控制台中。这可能允许攻击者窃取您的身份或控制您的计算机。请在下面键入“允许粘贴”以允许粘贴。

image-20231224015139976

补环境

开始补环境前,我们再回顾以下 Node 环境和浏览器环境的差异:

image-20231213000003706

全局对象

**因为在 Node 环境中没有 BOM,所以浏览器环境的全局对象 window 是用不了的,因此这里我们就必须使用 Node 环境的全局对象 global,它类似于浏览器环境中的 window 对象。**在 Node 环境中,可以直接访问 global 对象,例如:

javascript
console.log(global);

**通常情况下,你无需显式使用 global,因为在 Node 环境中,全局变量和函数会自动成为全局对象的属性。**例如:

javascript
// 在 Node.js 中,以下两种方式是等价的
global.myVariable = 42;

// 或者直接
myVariable = 42;

但这里有一个问题就是,从浏览器中抠出来的 JS 代码的凡是使用全局对象的都是 window,没有 global。其实这个问题很简单,只要在 JS 代码最前面加上下面这一句就可以了:

javascript
// global全局对象
window = global;