李恒道 发表于 2023-12-8 23:42:14

抖音JSVMP算法逆向(一)

# FBI WARNING
本文基于渔歌的教程以及K哥爬虫的抖音分析得来
https://cloud.tencent.com/developer/article/2208864
# 正文
抓包地址设为我最喜欢的主播 云雀Qqq
https://www.douyin.com/user/MS4wLjABAAAAwHlnPXaL8Is7itEu8SHMocTc3V1zdU4X2sjZVKeNHro?vid=7308030273759776015
抓包发现视频加载地址为
```
https://www.douyin.com/aweme/v1/web/aweme/post/?device_platform=webapp&aid=6382&channel=channel_pc_web&sec_user_id=MS4wLjABAAAAwHlnPXaL8Is7itEu8SHMocTc3V1zdU4X2sjZVKeNHro&max_cursor=0&locate_item_id=25424245245245524&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=1704010&version_name=17.4.0&cookie_enabled=true&screen_width=1436&screen_height=864&browser_language=zh-CN&browser_platform=Win32&browser_name=Chrome&browser_version=119.0.0.0&browser_online=true&engine_name=Blink&engine_version=119.0.0.0&os_name=Windows&os_version=10&cpu_core_num=16&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=50&webid=73026748115794832422&msToken=6VCiWBzn1jh29Fcw2HpZokry78bf6ACWAFHGKLCetv9xwuuHBC0GLAqc5kWD9Pd49qBhe3glQHLT4VCo2zVHo27-Ughk4DeRjFv-bP4Su2-_Ed1BLWPs=&X-Bogus=DFSzswVOKRxANtOstzKMGe9WX7JJ
```

sec_user_id 博主id
msToken和X-Bogus是我们的这次主要目标
分别为6VCiWBzn1jh29Fcw2HpZokr3635f69JMcYRKLCetv9xw363635BC0GLAqc5kWD9Pd49qBhe3glQHLT4VCo2zVHo27-Ughk4DeRjFv-bP4Su2-_Ed1BLWPs=
以及DFSzswVOKRxANtOsFAAFAMme9WX7JJ
根据测试msToken为本地存储中
![图片.png](data/attachment/forum/202312/08/232048yb1qwhjhhrijhno7.png)
那我们的主要目标就是X-Bogus
打下xhr断点
![图片.png](data/attachment/forum/202312/08/232315f6s64xnwb67rwrza.png)
断下后堆栈回溯找到了
![图片.png](data/attachment/forum/202312/08/232300m9tkou8des28c9tj.png)
这种基本就是jsvmp加密了
jsvmp是将原有的代码进行转义成字节码的形式
将大量函数聚集在一个函数中,打乱原有的代码形式,变为字节码控制
同时将函数的变量声明转为堆栈式,将返回内容,函数的调用等都由转义后的代码进行管理
相比ob易解混淆来说更难以进行分析
我们在这里采用插装法
首先覆盖文件
将原有的代码复制,使用v_jstools将变量进行压缩
![图片.png](data/attachment/forum/202312/08/232456fwiwsksnlt0il4wl.png)
覆盖文件后再次触发Xhr断点进行观察
这里O是一个全局变量的管理对象
通过S控制变量的弹出和塞入
P控制执行哪部分的代码
![图片.png](data/attachment/forum/202312/08/232600ijas7sg21in8jzk7.png)
一直往上走可以找到
可以看到for指令
其中P通过j运算得出,而j通过在a数组中A下标得到
最后算出应该执行哪部分代码
![图片.png](data/attachment/forum/202312/08/232722oee0wd3o48v90dt9.png)
根据K哥的教程可以知道应该在全局的头部进行插桩来获取信息
![图片.png](data/attachment/forum/202312/08/232926ym5mv5v65mechpy5.png)
由于这里有两个大的for循环,所以我们最好每个都插一下
K哥的代码是`"位置 1", "索引I", I, "索引A", A, "值S: ", JSON.stringify(S, function(key, value) {if (value == window) {return undefined} return value})`
但是我实际测试发现触发XHR或者循环JSON会出现问题
所以这里我用了一个try...catch捕获以及xhr过滤
如果发现xhr对象就无视,如果发现存在循环就调用一个缓存来处理
```js
          try {
            console.log(
            "位置 1",
            "索引j",
            j,
            "索引A",
            A,
            "值O: ",
            JSON.stringify(O, function (key, value) {
                if (value == window) {
                  return undefined;
                }
                if (value?.send !== undefined) {
                  return "xhr";
                }
                return value;
            })
            );
          } catch (error) {
            if (
            error.message.indexOf("Converting circular structure to JSON") !==
            -1
            ) {
            //循环模式
            try {
                let cache = [];
                console.log(
                  "位置 1",
                  "索引j",
                  j,
                  "索引A",
                  A,
                  "值O: ",
                  JSON.stringify(O, function (key, value) {
                  if (typeof value === "object" && value !== null) {
                      if (cache.indexOf(value) !== -1) {
                        return;
                      }
                      cache.push(value);
                  }
                  if (value == window) {
                      return undefined;
                  }
                  if (value?.send !== undefined) {
                      return "xhr";
                  }
                  return value;
                  })
                );
                cache = null; // reset the cache
            } catch (error) {
                debugger;
            }
            } else {
            debugger;
            }
          }
```
由于覆盖文件了,我就直接在里面写了
同时加了一个window.log更方便什么时候捕捉数据
![图片.png](data/attachment/forum/202312/08/233108guo34d9kjg39h7f9.png)
一共两个点都插入抓了大概8w条数据
搜索xhr断下得到的X-Bogus找到了数据,最后一次是生成9
![图片.png](data/attachment/forum/202312/08/233236ekg8dhxgen3xzeed.png)
9的生成在9的上一个null和9之间
位置是`位置 2 索引j 16 索引A 716 值O:`
同时为了更好的限制数据可以下条件断点`j===16&A===716&O===21`
这部分是K哥爬虫的经验,是多次抓数据进行比对,最后找到的O最后一位具有一定的特征点
下断停到了
![图片.png](data/attachment/forum/202312/08/233553ubaweakek5qb4k56.png)
其中P是charat,m是字符表,w是数组
说明是取字符表中特定位置的数据
![图片.png](data/attachment/forum/202312/08/234116unpai2kwitk33psp.png)
我们原数据中是的W刚好对应了(不同批次抓的数据,有少许出入,只要逻辑贯通就可以)
![图片.png](data/attachment/forum/202312/08/234134an689aadt69t64zq.png)
那问题变成了两个
1.这个字符串是怎么生成的?
2.这个数字是怎么得来的?

我们过两天继续研究...



页: [1]
查看完整版本: 抖音JSVMP算法逆向(一)