某红书请求头x-s加密逆向分析

声明
本文内容仅供学习交流使用,不得用于商业或非法用途。抓包内容、敏感网址、数据接口已脱敏处理,严禁转载、修改或二次传播。若有侵权,请联系作者删除。

逆向目标

- 目标:X-S加密
- 网址:aHR0cHM6Ly93d3cueGlhb2hvbmdzaHUuY29tL2V4cGxvcmU=

请求分析

切换频道后,定位到目标数据的接口请求,发现请求头中存在多个疑似加密参数,包括:X-Mns、X-S、X-S-Common,X-T为时间戳,经过测试,大部分接口仅对 X-S 进行校验,因此本次重点分析 X-S 的生成逻辑。
在这里插入图片描述

X-S逆向

全局搜索关键字 x-s 后,发现在多个 JS 文件中出现。可以在这些文件中逐一打上断点进行调试。为了节省时间,这里直接给出定位结果:
在这里插入图片描述
进入对应的 JS 源代码页面后,搜索 x-s,并在所有可疑位置设置断点。刷新页面后,代码在下图所示的位置被成功拦截。
在这里插入图片描述
x-s 的值保存在 v 中,而 v = f(p, u) || {}。先来看 p 和 u 分别代表什么,p 是请求的 url 的一部分,u 是请求体。
在这里插入图片描述
一开始看到 f = encrypt_sign,以为就是生成 x-s 的方法,但实际加密后结果太短,明显不对。往下看发现,f 实际由一个三元表达式决定。继续跟进,调用 window._webmsxyw(p, u)后得到的值才是正确的 x-s。
在这里插入图片描述
进入 window._webmsxyw 后可以看到代码已经被混淆,直接分析加密逻辑难度较大,既然该方法可以通过window调用,那我们就把所有的JS代码扣下来,补全所需要的环境就好了。
在这里插入图片描述

补环境

在本地运行扣下来的 JS 代码时,首先遇到 window 未定义的错误。
在这里插入图片描述

1.补Window

window = global;
// 防止网站检测
delete global;
delete Buffer;

接着运行,遇到 createElement 未定义的错误,这是 document 对象中的方法,暂时补空函数。
在这里插入图片描述

2.补Document

document = {
  createElement: function (tagName) {}
}

继续运行时遇到了 TypeError: Cannot read properties of undefined (reading ‘getAttribute’) 的错误,不清楚 getAttribute 具体是哪个对象在调用。此时,可以使用代理(Proxy)来帮助我们分析。
在这里插入图片描述

代理示例,把经常检测的环境都加上

function getEnvs(proxyObjs) {
    for (let i = 0; i < proxyObjs.length; i++) {
        const handler = `{
      get: function(target, property, receiver) {
      if (property !== "Math" && property !== "isNaN" && property !== "encodeURI"){
      if (target[property] && typeof target[property] != "string" && Object.keys(target[property]).length>3){
      }else{
        console.log("方法:", "get  ", "对象:", "${proxyObjs[i]}", "  属性:", property, "  属性类型:", typeof property, ", 属性值:", target[property], ", 属性值类型:", typeof target[property]);}}
        return target[property];
      },
      set: function(target, property, value, receiver) {
        console.log("方法:", "set  ", "对象:", "${proxyObjs[i]}", "  属性:", property, "  属性类型:", typeof property, ", 属性值:", value, ", 属性值类型:", typeof target[property]);
        return Reflect.set(...arguments);
      }
    }`;
        eval(`try {
            ${proxyObjs[i]};
            ${proxyObjs[i]} = new Proxy(${proxyObjs[i]}, ${handler});
        } catch (e) {
            ${proxyObjs[i]} = {};
            ${proxyObjs[i]} = new Proxy(${proxyObjs[i]}, ${handler});
        }`);
    }
}

proxyObjs = ['window', 'document', 'location', 'navigator', 'history', 'screen', 'localStorage']
getEnvs(proxyObjs);

在使用代理调试时,我们发现 document 对象中缺少 documentElement 属性,我们把它补上。
在这里插入图片描述

document = {
  createElement: function (tagName) {  },
  documentElement: {}
}

执行 createElement 时仍然报错,说明直接将 createElement 替换为空函数的做法并不奏效。实际上,createElement 创建标签后,紧接着会执行 getContext 方法。因此,我们需要进一步分析 createElement 创建了什么标签。
在这里插入图片描述
将createElement的内容改成:

createElement: function (tagName) {
    console.log(tagName)
}

查看控制台输出的为canvas。
在这里插入图片描述
补上canvas,同时,在执行 document.createElement 时,应针对不同标签类型进行判断,确保根据创建的标签返回正确的对象。

canvas = {
    getContext: function (tagName) {
    }
}
document = {
    createElement: function (tagName) {
        console.log(tagName);
        if (tagName === 'canvas') {
            return canvas;
        } else if (tagName === 'div'){
            return {};
        }
    },
    documentElement: {}
}

再次运行,发现没有报错了。接下来,测试 window._webmsxyw(p, u)是否能够成功生成 x-s。
在这里插入图片描述
运行结果显示,虽然成功得到了 x-s,但其长度明显不对。这是因为当前环境尚未完全补全,接下来需要对属性值为 undefined 的对象进行补全。
在这里插入图片描述
补全 document.cookie 后,运行结果显示缺失 localStorage。
在这里插入图片描述
3.补LocalStorage

localStorage = {
    getItem: function () {}
}

成功得到 x-s,并且长度明显增加,但不要高兴得太早。在使用这个值进行请求时,仍然无法获取数据。原因是浏览器生成的 x-s 长度为672,而我们生成的只有648,没办法,继续补环境吧。
在这里插入图片描述
4.补Navigator

navigator = {
    appCodeName: "Mozilla",
    appName: "Netscape",
    appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.47",
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.20447',
    platform: "Win32",
    webdriver: false,
    language: 'zh-CN',
}

5.补Location

location = {
    "ancestorOrigins": {},
    "href": "xxx",
    "origin": "xxx",
    "protocol": "https:",
    "host": "xxx",
    "hostname": "xxx",
    "port": "",
    "pathname": "/explore",
    "search": "",
    "hash": ""
}

到目前为止,环境已经补得差不多了,但最终结果仍然是648位。如果继续补充缺失的部分,还是无法得到正确结果。真正的关键在于 getItem 方法。之前只是简单地补了一个空函数,实际上它应该像 createElement 那样进行处理。具体实现方式就不再展示了,相信聪明的你一定知道如何处理,最终结果如下。
在这里插入图片描述

结果展示

首页:
在这里插入图片描述
作者笔记:
在这里插入图片描述
笔记详情:
在这里插入图片描述
评论:
在这里插入图片描述
希望这篇文章对你有所帮助,如果你有更好的思路,欢迎在评论区交流!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值