JS逆向实战系列二|某头条msToken参数生成逻辑与扣取指南

JS逆向破解

引言

兄弟们,上篇文章《JS逆向实战系列一 |某头条a_bogus参数逆向分析与算法还原》详细讲解了a_bogus的整个”JS逆向实战”过程。但事情还没完!/api/pc/list/feed接口除了a_bogus参数,还包含一个msToken参数。本章就带着大家详细解密msToken参数的过程。

本文目录

  1. 数据抓包实战
  2. 参数搜索大法
  3. 接口分析与思考
  4. XHR断点定位
  5. 扣取加密代码
  6. 补全运行环境
  7. 运行结果验证
  8. 常见问题与解决方案(FAQ)
  9. 获取完整课件

1. 数据抓包实战

老规矩,先打开F12 Network面板,刷新页面,找到那个熟悉的/api/pc/list/feed请求。

仔细查看Query String Parameters(查询字符串参数),除了上回解决的a_bogus,这次重点关注另一个参数——msToken。它通常是一长串看似无规律的字符,同样每次请求都会变化。

msToken参数抓包

在请求URL中,可以清晰看到我们需要msToken参数(图中红色标注部分)。这就是我们的目标。

2. 参数搜索大法

首先尝试在源代码中搜索msToken参数。与上篇文章不同,这次搜索出现了大量结果!

大部分结果都指向/web/common接口,其Response Headers中的set-cookie指令包含msToken=xxxxx。这表明msToken参数是通过请求/web/common接口获取的。这就是第一步:找到参数来源。

抓包搜索参数

3. 接口分析与思考

既然msToken来自/web/common接口,我们需要将注意力转向这个接口。通过数据抓包分析,发现:

  • 接口使用POST请求方式
  • 请求链接需要传递msToken参数
  • 请求主体包含加密的staData参数
  • 响应主体不包含msToken参数

有趣的是:/web/common接口本身也需要msToken参数,但尚未获取到msToken如何传递?

数据抓包

通过火狐浏览器的”编辑并重发”功能测试发现,不传递msToken参数也可行,但必须传递主体的strData参数。于是重点转向解决strData加密参数。

数据抓包

【补充内容】 
请看上图,在 /web/common 接口的响应头(Response Headers)中,红色框选部分下面 有一个名为 x-ms-token 的字段。这个字段的值,正是我们整个逆向流程的终极目标!我们后续在Python代码中,就是要提取这个响应头字段的值,来作为主接口所需的 msToken 参数。请务必理解这一点,这是连接“加密参数生成”和“最终结果获取”的桥梁。

4. XHR断点定位

直接搜索strData无果,说明参数没有在源代码中明文赋值。于是使用更高级的XHR断点功能,直接拦截包含/web/common的请求。

XHR断点

刷新页面后,代码执行在请求发出前成功暂停。通过Call Stack堆栈分析和单步调试(F10/F11),最终将生成位置锁定在代码d = s.apply(b, u)。此时变量d的值即为生成的strData。

数据抓包堆栈
数据抓包加密函数

步入函数深入分析,发现关键变量s指向了一个复杂的匿名函数u,这就是我们要找的加密函数本体!这就是成功关键。

JS逆向解密代码分析

关键代码结构如下:

javascript

(u = function e() {
    // ... 复杂的加密逻辑 ...
})._v = [s, o, v],
u._u = e,
p[++l] = u

5. 扣取加密代码

bdms.js文件拷贝到本地,找到s指向的u函数。将u函数赋值给window.msToken,这样在外部就可以通过window.msToken调用这个加密函数了。

抓包扣代码

6. 补全运行环境

创建env.js环境补全文件:

javascript

window = global
delete global
delete Buffer
XMLHttpRequest =  function(){}
window.requestAnimationFrame = function(){}
document ={}

在断点处查看参数u的值,复制参数内容用于后续调用。这是环境补全的重要步骤。

抓包调试

创建main.js主入口文件:

javascript

require('./env');
require('./bdms');

const args = [{"battery":..., ...}]; // 具体参数省略
const result = window.msToken.apply(null, args);
console.log(result);

7. 运行结果验证

在终端中运行node main.js命令。如果一切顺利,将成功生成strData参数的值!这就是最终成果。

JS逆向破解

msToken生成逻辑的特别补充

首先,非常感谢这位111兄弟的尖锐评论,您说得非常对!确实,文章在从strData跳转到最终msToken的衔接上交代不清,让初学者困惑,在此致歉。现将核心逻辑梳理如下:

完整逻辑链:
我们的目标不是strData本身,而是用它作为“钥匙”去请求/web/common接口,该接口会在响应头中返回真正的msToken

  1. 逆向目标: 生成合法的strData(POST请求体)。
  2. 关键请求: 用strData去请求/web/common接口。
  3. 最终获取: 从接口的响应头(Headers) 中提取x-ms-token的值。

核心Python代码示意(获取msToken的关键步骤):

import execjs
from requests import request


def LoadJs(path='mian.js'):
    # 加载脚本
    with open(path, "r", encoding='utf-8') as file:
        js_code = file.read()
    return execjs.compile(js_code)


def get_msToken(url: str, js=None, cookie=None):
    url = "https://mssdk.bytedance.com/web/common"
    params = {
        "ms_appid": 24,
        "msToken": '0mHpz_Jq0qsGpC7bm7xC7G6Ve-ktB3cz587rUx3Q2L23tSM4LC-brY4EXzHjBuogzMrplTKdhDW1X0T-d0MBa9Cbu9OMg5tRRR0JD61rOT5IUGnDHoULUxru7rlrRCXY'
    }

    # 修正:使用 call 方法调用 JavaScript 函数
    str_data = js.call('get_msToken', url)

    data = {
        "magic": 538969122,
        "version": 1,
        "dataType": 8,
        "strData": str_data,
        "tspFromClient": 1718114084818,
        "ulr": 0
    }

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:142.0) Gecko/20100101 Firefox/142.0",
        "Accept": "*/*",
        "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Cookie": cookie or "",
        "Content-Type": "text/plain;charset=UTF-8",
        "Referer": url or "",
        "Origin": url or "",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "cross-site",
        "Connection": "keep-alive",
        "Priority": "u=4",
        "Pragma": "no-cache",
        "Cache-Control": "no-cache"
    }

    # 发送POST请求
    try:
        response = request(method="POST",
                           url=url,
                           json=data,
                           params=params,
                           headers=headers)
        if response.status_code == 200 and 'resultCode' not in response.text:
            ms_token = response.headers.get('x-ms-token')

            return ms_token
        else:
            return None
    except Exception as e:
        print(f"请求异常: {e}")
        return None


if __name__ == '__main__':
    Obj = LoadJs()
    msToken = get_msToken('填写视频入口地址', Obj)
    print(f"msToken: {msToken}")

(建议在此处插入图片:展示Python代码成功运行后打印出的msToken值)

简单总结:扣代码 → 生成strData → 请求接口 → 提取响应头x-ms-token → 得到msToken。希望这个说明能解开大家的疑惑。

简单总结:扣JS代码 → 生成strData → 请求/web/common接口 → 从响应头提取x-ms-token → 得到最终的msToken。希望这个解释能让大家豁然开朗。



8. 常见问题与解决方案(FAQ)

Q1: 为什么搜索msToken能找到结果,而strData找不到?
A: msToken在set-cookie指令中明文出现,而strData是加密参数,在代码中经过混淆处理。这是中常见的混淆技术。

Q2: 补环境后运行还是报错怎么办?
A: 根据报错信息继续补全缺失的环境变量,这是一个迭代过程。需要耐心和细心。

Q3: 生成的strData参数无效如何解决?
A: 检查参数是否与浏览器中完全一致,特别是时间戳、随机数等动态参数。这是常见问题。

Q4: 过段时间方法失效了怎么办?
A: 这是正常现象,需要重新分析新的加密逻辑,但本文提供的方法论是通用的。

9. 获取完整课件

关注公众号:孤狼网络科技,回复:msToken,即可获取本文涉及的完整代码课件(包含详细调试截图、补全后的环境文件、可运行代码示例)。如果你想深入学习JS逆向,这些资料将非常有帮助。

总结
msToken的逆向过程与a_bogus类似,但需要多一层接口分析。掌握从抓包到补环境的完整方法论,就能应对各种JS逆向挑战。如果在实践过程中遇到问题,

下期预览

以上就是本次破解 “msToken” 实战的全部内容。希望本篇JS逆向教程对您有所帮助。林石工作室下期将为您带来《JS逆向实战系列三 | 某头条_signature签名破解与完整实现》的解析,感谢关注。

5 Comments

  1. 111

    你这写的有屌用啊,最后获取到的是strData也不是msToken值啊,看了好几遍也没看懂你的思路。

  2. Fate

    是抖音更新了吗,现在照您的方法找不到了

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注