某点爬虫
前言:本人对各方向的知识了解浅薄,所以想通过实战来借此学习一些知识,因此在本篇的过程中,本人会较为详细地介绍各个流程与细节点,并在实战过程中进行自学习
实际上,对于小说网站的实际爬虫应用并没有我下面的如此复杂,一些现成的开源爬虫工具已经满足了大部分的需求。但是为了追求技术上的提升,我们还是按照游戏规则来进行
抓包分析
首先我们任意打开一本小说,并对其进行抓包,可以发现关键的信息只有前三条,当打开第三条请求的响应体,可以发现包含文本的节点已经被返回,因此我们只需要对这三次请求进行分析

第一条请求的响应是202,也就是接收到了请求,但是需要进一步处理,这里可以看到加载了一个名为probe.js的文件,该文件是我们后面分析的重点

第二条请求则是向probe.js发送了请求,返回了js代码,很明显有js混淆

第三条请求,可以发现请求头中携带了一个陌生的数据w_tsfp,而返回中则包含我们需要的文本,通过提取就可以完成我们的目标

很明显我们的任务就关注在了probe.js上,目标应该是如何通过分析该文件后,自动生成w_tsfp参数
分析probe.js的主干部分
在这里重点推荐这位大佬的帖子jsvmp编译与反编译详解 (3)——某讯新版vmp反编译 - dream的小站,因为我们后续的核心也是对vmp进行反编译,而我们所获得的probe.js代码与这位大佬演示过程中的代码是一致的,除了符号由于混淆存在不同。大树底下好乘凉
我们将获得的probe.js文件:
- 保存在本地,你可以手动进行复制粘贴,或者直接在浏览器的
Network中找到并保存,或者在抓包软件中进行保存 - 进行格式化,我这里是使用
Babel将其进行了转换 - 按照函数进行折叠,vscode中的折叠函数快捷键为
(Ctrl + K, Ctrl + 0)
最终可以得到如下一系列函数,我们可以看到最后是调用了
1 | |

这里可以将连续调用进行拆解
1 | |
而又因为var __TENCENT_CHAOS_VM = function () ,可以得知__TENCENT_CHAOS_VM的实际函数为最终的return g(g,I)

我们再跟踪进g函数,注意,由于代码混淆,所以可以看到上面也存在g函数,而我此时指的是箭头所指的g函数。部分符号存在是为了混淆,所以请读者包括后续分清我所代指的符号
这里进入到g(g,I)函数中后可以发现,存在两个新的 g(g, I, o, Y, r)函数,分别赋值给了C与a,可以看到此时的 g(g, I, o, Y, r)函数的参数为五个,这与第二段中的参数列表吻合(13447, [], window, ["Promise", void 0, "apply", ...], void 0)
而g(g,I)函数函数的返回值由 return I ? C : a;进行控制,而I为g(g,I)传入的参数,此时传入的为false,也就是返回a所对应的函数,那么C与a函数有什么区别呢

可以通过文本比对工具发现,两者仅有"use strict";的区别,即是否开启js的严格模式

至此,整个对于__TENCENT_CHAOS_VM函数的调用逻辑已经清晰了:
1 | |
分析probe.js的枝叶部分
既然我们已经理顺了probe.js的调用流程,于是我们开始跟着主干的调用流程,分析每个子步骤中的函数与功能,可以看到在__TENCENT_CHAOS_VM中有四个函数与四个变量,我们开始逐一进行分析

g函数
该函数接收三个参数,最终返回一个由A起始,总共有B步,每步步长为g的等差数组。最终生成的数学表达式如下
[ A₀ + g, A₀ + 2g, A₀ + 3g, ..., A₀ + B*g ]
1 | |
I函数
base64编码,但注意此时的B并没有被使用,而后面使用的是外部变量a
1 | |
E函数
ZigZag编码
1 | |
C函数
Varint编码
1 | |
当然,我们此处可以不用对这些编码进行深入分析,合理猜测这部分是将传入的数据进行解码
此处的a对应了上面的自定义编码表,由g函数辅助生成,然后是一些变量的初始化,一直最后虚拟机启动的函数h()

可以看到此时的E作为字节码数字,而F充当PC

因此在前面的解码部分,实际上就是解码得到了我们的初始化输出,于是我们便可以直接使用他原来的代码制作成接口,将得到的数据进行返回即可。接下来我们将重点关注虚拟机部分

分析VMP
首先我们将原本的switch-case逻辑转变为函数调用的逻辑,这里使用@babel/traverse · Babel来设定规则并提取函数
- 函数提取
- 增加翻译
- 将原本字节码翻译成自定义的汇编
- 通过程序运行状态验证汇编是否正确
- 优化汇编 APPCH优化