JS逆向XB参数(一)

一.写在前面

        前面我们已经了解了一些JS逆向的知识,现在我们就来将其付诸于实践。以XX为例,批量得到某铁道(游戏)的视频。其实我们在之前已经尝试过使用selenium来获取X音上的yuan神(游戏)的视频,并且成功了,但是,这样的弊端很大,因为速度非常慢,而且各种验证码包括滑块点选之类的甩你脸上,所以效果不是很好,而这次我们尝试JS逆向它的有加密的参数,这样就能够非常快的爬取全部视频。下面的一切内容仅供学习交流

二.分析网络请求

        我们来到XXXX用户主页,打开浏览器开发者工具。

4169298a0b5a4d78a62c4eb8043e51d1.png

刷新一下页面,我们就能够看到很多网络请求。首先我们应该明确一点,现在的很多网页都不是一下子把所有的内容给你加载出来的,而是随着用户操作才发送网络请求(Ajax请求或者其他一些请求),然后再渲染给你看到,而这些请求往往可以在浏览器开发工具的Fetch/XHR面板下看到,我们切换到这个面板下,查看这些请求,就能够看到一个非常可疑的请求:

ebfa9efba44648a09caf5da537a89102.png

点开预览我们就能够看到很多似乎是视频描述的文本,然后继续往里面寻找,打开一个aweme_id,就能够发现里面有一些URL,点开,我们就能够看到,非常幸运,就是视频的URL。

dadd465df9944434a6e5e05c2bc8b13d.png

我们回到标头,查看该URL的特点,然后在筛选器里面筛选这种类型的网络请求。

c3b6690a46e7419ea65f00802b6b208a.png

每个请求里面都包含了大概十多个视频的信息,随着我们鼠标往下滑,我们就能看到更多这样的请求被拦截。所以我们有理由这样设想:假如我们能够构造这些请求,不就能够获取全部视频的地址了么?进而就可以全部爬取了。

三.加密参数分析

        于是我们点开一个请求,看看它都携带了哪些查询字符串。我们重点关注的是那些变化的参数,不变的参数对我们没有太大意义。经过我们的观察,看起来像是加密的参数的只有这两个:msToken和一个XB参数dfd88ac8abfc47639b2aa716305bf63d.png

这个msToken参数经过我们的一番寻找,原来他就是cookie里面的一个值,70fe2e2b6a84488cb13b77ab2f8cd686.png

所以这个参数我们就可以暂且不管了,重点要看看这个XB参数,我们没有找到它是哪里冒出来的,所以我们有理由相信,它应该是在JS代码运行过程中被加密了。回到这个请求的标头,我们复制一小段URL的内容,到源代码上打上一个XHR断点。

0765b7dbbf054b11a828b5d7414e9cfc.png

然后刷新页面,我们就能够看到代码被卡住了,说明有这样的URL被发送。

62e164bf2fbb4c988d64f1f3bb07b0ab.png

我们将鼠标放到这个this上,或者是在控制台页面上面打印。f6a0522afa7742abad2231a84ac055b6.png

我们发现里面好像确实是包含了一个URL,而且带有XB参数(太长了,截屏没有截全)。所以我们猜想XB参数在之前就被加密装好了。所以我们把目光集中到旁边的调用堆栈,往上跟栈。来到了一个被混淆的函数。367ba5be7a014fb9ba272d92df74ee5d.png

这里的代码全部是看不懂的十六进制,但是我们不用管,既然跟栈到了这里来,说明确实是有经过这一步的,所以我们在这里打上一个断点,并把之前那个XHR断点取消掉,再次刷新页面。页面再次进入调试状态。

8aff04e2cd81467f97ec3b6259be53e8.png

我们观察这行代码,它似乎是把什么值赋给了左边那一坨,所以我们不妨在控制台打印一下右边那个函数到底返回了什么,以及它的参数。fac82124b3644b5493f98c05515777d6.png

我们看到这个函数返回了一个很像URL的东西,函数的两个参数一个是空,一个有点东西。我们仔细观察这个URL,发现它和我们之前那个URL不太一样,这个是/aweme/v1/web/app/installed,而我们之前那个URL应该是/aweme/v1/web/aweme/post/的。到这里,我们心里面不禁起了疑问:明明我们跟栈到了上一步,为什么这个玩意儿的好像跟我们预想的不太一样?于是,我们心里有了一个想法,难道这个函数(右边那一坨)被执行了很多次,而且每次执行的结果不一定相同?所以我们尝试在这里打上一个日志断点,再次刷新页面。8d68f8bd3a9d42babffba4d6cd2eba75.png

来到控制台,我们发现,果然这个函数被执行了很多次,而且每次的结果都不太一样。2f75d5a333a0497592ca60ec355df6a0.png

我们怀着侥幸的心理,在筛选器里面搜索看看有没有加密参数XB的信息,因为在这一步,XB参数很可能也已经被加密完成了的。

cde1d1908280402981317071258a52bc.png

我们发现居然还真有,而且有的就是单独一个XB参数。这样我们就大概理清了:原来每次这个方法都会被执行很多次,每次的结果都不一样,而且有时候它的返回值就是一个XB加密参数。

        那么XB参数究竟是在哪里被加密的呢?既然日志断点能够打印出XB值,那么我们完全有能力让其在是XB值的时候断住,然后再往上跟栈,找到加密的地方。没错,我们试着打上一个条件断点,而XB值的一个很大的特点就是长度是28,所以我们就以这个为条件

8027036a56804767abe98e05e219e146.png

再次刷新页面,进入到了调试模式,说明我们捕获到了XB值,我们再控制台上打印,发现确实是XB值。6ef6902eaf2c4c479de00b48250f36da.png

这下就非常好了。我们把鼠标移动到这个函数上面,看看它到底是在哪里定义的:90b1a407716b45f39782b23682de0f16.png

于是我们就来到了它定义的地方。54f80fd9821d4506850eda623d04b873.png

那么它会是加密XB参数的地方么?我们不妨打上断点,再次刷新试试,在控制台上打印出这个函数的返回值。

400aa8e2971d4291a34df7047e05986b.png

非常完美,我们终于找到了XB加密的地方,就是这个函数。而且我们也能够知道它的两个参数是什么东西了。

四.模拟执行JS代码

        既然找到了JS代码里面加密XB的地方,那么我们该怎么将其为我们所用呢?难道要去读懂里面的逻辑么?肯定不是,我们只要用它就行了。首先我们肯定要一个能够运行JS代码的地方,这里我就选择了VSCODE,并安装了node.js环境。最开始我尝试只把这个加密的函数抠出来,然后尝试运行,发现缺了很多东西,而且补了很久加了很多东西都还是报错缺东西。所以后来我干脆直接把这一整个JS代码复制下来,单独保存为一个JS文件,然后只补了几个东西,就没有报错了。

window=global
document={}
document.addEventListener=function(){}
// 创建一个新的 Navigator 对象
const customNavigator = {
    userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0'
};

// 将新的 Navigator 对象赋值给 window.navigator
if (typeof window !== 'undefined') {
    Object.defineProperty(window, 'navigator', {
        value: customNavigator,
        configurable: true,
        enumerable: true,
        writable: false  // 这里设为 false,以防止后续再次尝试修改
    });
} else {
    console.error("Window object is not defined. This code may not work in non-browser environments.");
}

上面的代码是写在整个JS文件的最上头的,补的是window和UA还有document,因为我不懂JS代码,所以有些是问的GPT,然后又到处百度改的(能用就行)。

6b9f6a268b094d61bd094bac71811faa.png这个圈起来的代码意思是把这个加密XB的关键函数赋给window,这样我们就能在外面调用了。

let key1='device_platform=webapp&aid=6383&channel=channel_pc_web&sec_user_id=MS4wLjABAAAAsWXxS-KIWEuFJf2oy9E5RwDFzqIElxMsYccLGHCTh-J7z4WhTCehpsQwkv0n_8pl&max_cursor=1707386400000&locate_query=false&show_live_replay_strategy=1&need_time_list=0&time_list_query=0&whale_cut_token=&cut_version=1&count=18&publish_video_strategy_type=2&pc_client_type=1&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Edge&browser_version=121.0.0.0&browser_online=true&engine_name=Blink&engine_version=121.0.0.0&os_name=Windows&os_version=10&cpu_core_num=8&device_memory=8&platform=PC&downlink=6.6&effective_type=4g&round_trip_time=150&webid=7214703537787274789&msToken=TH_OPzj7n4OKMM8fqvvJRTCyfjziCOGnsex9yTR0HbFsqUeG6RPrRFM-V6r5dFVzSbqQP88GjZHNgAFDbGqqcru8rxOc07-1R9uvhsz9kYJuSrnaMEnazZg='
let key2=null

console.log(window.encodeXB(key1,key2))

我们传入两个参数,试着调用一下,打印结果。

446f063eb8f043e0a90c38409a2adfd1.png

JS代码成功打印出了加密后的XB值

五.建立服务,与python代码联动

        现在又出现了一个新的问题,虽然我们能成功计算出XB值,但是我们要写爬虫啊,JS代码我可不会,怎么办呢?其实有这样两个解决办法,第一种就是利用python的一个执行JS代码的库,这样我们就可以利用python来调用这些JS代码,第二种方法就是建立一个简单的JS服务端,就像你发送requests请求到别人的服务器,然后返回响应一样,这里我们使用第二种办法。用JS代码建立一个服务端,接受一个post请求,返回我们计算的值。

const express = require('express');

const app = express();
const port = 3000; // 选择一个你喜欢的端口

// 使用body-parser中间件来解析POST请求的请求体
app.use(express.json());

// 处理POST请求
app.post('/', (req, res) => {
  // 从请求体中获取数据
  const requestData = req.body;
  // 检查 requestData 是否为 JSON 数据
  if (typeof requestData === 'object' && requestData !== null) {
    // 获取 requestData 的所有键
    const keys = Object.keys(requestData);

    // 检查是否有至少一个键
    if (keys.length > 0) {
      // 获取第一个键
      const firstKey = keys[0];

      // 获取第一个键对应的值
      const firstValue = requestData[firstKey];

      //console.log(`First Key: ${firstKey}, First Value: ${firstValue}`);

      // 在这里可以进行任何你想要的处理
      const responseData = window.encodeXB(firstValue,null);
  
      res.send(responseData);
    } else {
      console.error('Empty JSON data in the request body');
    }
  } else {
    console.error('Invalid JSON data in the request body');
  }
  
});

// 启动服务器
app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});

这个服务端代码是和我们加密XB参数的代码放在一起的,我们运行服务端代码,它就会监听一个端口,这样当我们携带一个post的http请求去访问的时候,服务端就会解析我们的请求体的数据,然后调用加密XB参数的方法,并返回给我们值。我们先在VSCODE里面运行服务端的代码,可以看到成功运行了。

375bd638dab44b3a8631be11a6f625fb.png

然后呢,我们就可以在pycharm里面编写我们的爬虫代码了。

import requests
header = {...}
def getXB(key):
    """
    这个函数用来发送一个http请求向服务端获取对应的XB值,注意需要先启动VSCODE里面的服务端
    :param key: 加密用的字符串,需要除了XB值的前面的查询字符串
    :return: 返回XB字符串
    """
    data={
        'key':key
    }
    response=requests.post(url="http://localhost:3000",json=data)
    return response.text

def getData(data):
    """
    这个函数传入一段字符串,然后构造url请求,并返回内容。注意传入的字符串是从?后面开始到XB参数之前,XB之前那个&不要加
    :param data:
    :return:
    """
    baseurl="https://www.douyin.com/aweme/v1/web/aweme/post/?"
    #data="device_platform=webapp&aid=6383&channel=channel_pc_web&sec_user_id=MS4wLjABAAAAsWXxS-KIWEuFJf2oy9E5RwDFzqIElxMsYccLGHCTh-J7z4WhTCehpsQwkv0n_8pl&max_cursor=0&locate_query=false&show_live_replay_strategy=1&need_time_list=1&time_list_query=0&whale_cut_token=&cut_version=1&count=18&publish_video_strategy_type=2&pc_client_type=1&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Edge&browser_version=121.0.0.0&browser_online=true&engine_name=Blink&engine_version=121.0.0.0&os_name=Windows&os_version=10&cpu_core_num=8&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=150&webid=7214703537787274789&msToken=VFu2Zu4uX3Ej6Gzcs4moEbx_B-vRZudzOgZXbQOce7zmotHxJhDw8VWwRhlccIeoSoZ9zJ__kEIbsnAmJx4fkY246GZS8e07ls1L6dem9oOTBMrq_qakzsgB0BikFAYI"
    XB=getXB(data)
    url=baseurl+data+'&X-Bogus='+XB
    #print(url)
    response=requests.get(url=url,headers=header)
    return response.text

if __name__=="__main__":
    data="device_platform=webapp&aid=6383&channel=channel_pc_web&sec_user_id=MS4wLjABAAAAsWXxS-KIWEuFJf2oy9E5RwDFzqIElxMsYccLGHCTh-J7z4WhTCehpsQwkv0n_8pl&max_cursor=1707537600000&locate_query=false&show_live_replay_strategy=1&need_time_list=0&time_list_query=0&whale_cut_token=&cut_version=1&count=18&publish_video_strategy_type=2&pc_client_type=1&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1536&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Edge&browser_version=121.0.0.0&browser_online=true&engine_name=Blink&engine_version=121.0.0.0&os_name=Windows&os_version=10&cpu_core_num=8&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=200&webid=7214703537787274789&msToken=mrmatvU-fZ10rREVP9M106uwYtZoFeuUhylNVqN7gzGGRUwoXECAh_ip05nF4gKJLaeluP9HfEJzRCrUu0AJVhSpXf13kQ-TPoqsWpjp7LLLP5JmQH8UG1-GO8qA019B"
    print(getData(data))

然后我们尝试运行一下:8aab57adb50e4250b141dd705800edea.png

可以看到,pycharm与VSCODE成功联系了,而且返回了预期的数据。至于后面你是要用多线程去爬取所有视频还是异步爬虫,那都不是很重要了。

 

 

 

 

 

 

 

 

 

 

 

  • 18
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值