前言
最近收到粉丝的私信,其在逆向某个站点时遇到了些问题,在查阅资料未果后,来询问K哥,K哥一向会尽力满足粉丝的需求。网上大多数分析该站点的教程已经不再适用,本文K哥将提供 3 种解决方案
,对于 webpack 不太熟练的小伙伴来说,这是一个很好的练手案例:
逆向目标
- 目标:某点数据,排行榜
- 地址:aHR0cHM6Ly9hcHAuZGlhbmRpYW4uY29tL3JhbmsvaW9zLw==
逆向过程
抓包分析
打开开发者人员工具,随便打开一个区域排行榜,在 Network 中即会抓包到相应的排行榜数据接口,即
/pc/app/v1/rank
,响应内容如下:
请求参数如下,其中主要参数为 K
参数,需要通过算法生成,其他参数 time,country_id 等,是时间戳以及一些固定的 id 值,k 值每次都会发生变化,需要进行分析研究:
本文将会用多种方法实现数据的采集,适合不同的技术群体。
协议采集
逆向分析
该接口是通过 XHR 进行请求的,我们直接下一个 XHR 断点 api.diandian.com/pc/app/v1/rank
,刷新排行榜,成功断了下来:
并没有发现任何 k 值,所以我们找网络拦截器,通过堆栈找到 m.request 的地方,在此处下一个断点,断下来以后,查看 m 变量,里面存储了很多回调方法:
通过查看 m 方法,我们在 onRequest
方法上下一个断点,同时在方法末尾也下断点,同时将上图的断点进行方向,再次查看排行榜,发现在 onRequest 上成功断了下来。同时我们发现,在调用 t 函数之前并没有 k 参数的生成:
我们继续执行,在方法结束末尾断点断了下来,发现右侧已经有 k 参数的生成。所以,由此判断,k 参数是通过 t 函数生成的:
我们进入 t 函数,在函数的开头和结尾分别下一个断点,发现通过 M 函数以后,生成了 k 值:
继续跟进 M 函数,我们查看 k 值是如何生成的:
通过分析得到 k 值的生成逻辑为:
var r = h()(t.params, !1), o = Object(y.a)(r, path, {s: n.s,k: n.k,l: n.l,d: n.d,sort: n.sort,num: n.num
}, "get");
t.params.k = o
所以他是通过 y.a 传入 r、path 以及大数组生成的,我们可以通过将 y.a 函数扣出来实现 k 参数的生成。
第一种思路我们可以看看 y 函数是如何被定义,可以看到它是 webpack 打包,调用 2294 模块来实现的:
第二种思路,我们进入 y.a 函数,进行算法的还原,其生成逻辑如下:
发现是通过 Object(l.b) 函数加密成字节集,然后通过 t.from 方法编码成 base64 进行展示,所以我们只需将这两部分进行算法还原即可复现 k 参数的生成,当然本文将会用不同的方法进行分析。
手动 webpack + 补环境
在 y=n(2294) 下断点,刷新排行榜,在该行成功断下来:
进入分发器 n 中,将 runtime.js 全部拿下,放到我们本地:
拿到本地以后,将分发器导出,window.kk=r
。
控制台通过 n.m[模块名] 将所需的模块进行查找:
拿到对应模块后,将 js 里的模块复制放到我们刚刚扣的分发器中:
通过模块,调用加密函数,查看报错信息:
a = window.kk(2294)
r = {"start_time": 1717776000,"end_time": 1718345618
}
n = {"proxy": "/app","target": "","sort": "dd","num": 10,"s": "d044bec62c1c9f9eee1ebd567e501719","k": "93086c0e7c41cf46","l": "091043cf5d1393af","d": 0
}
path = "/v2/user/monitor/msg"
o = Object(a.a)(r, path, {s: n.s,k: n.k,l: n.l,d: n.d,sort: n.sort,num: n.num
}, "get");
console.log(o)
这种错误就是提示缺少对应模块,我们只需根据调用堆栈向上查看,补上缺失的模块即可:
然后在控制台用 n.m[模块名] 进行模块查找,然后重复上述操作将找到的模块放入分发器即可,它这个站模块分布在几百个 js 中,也算是一种代码混淆了:
最后板凳坐穿,全部模块找完大概 6w 多行代码吧,结果如下:
自动扣 webpack 模块
网上自动扣 webpack 的方法很多,但是对于几百个 js 文件的模块来说,可能就不太适用,上部分手动通过 n.m[模块名] 进行模块查找的方法是最通用的,但是对于多个 js 文件模块就略显繁琐。所以我们可以通过重写分发器的方法,将加载的模块自动保存然后导出。
首先方法同上,先找到分发器的位置,在 r.e 及它之前下个断点:
刷新页面,发现在 r.e 的地方成功断住,我们将以下 js 代码在控制台进行注入:
window.code = '';
r = function (e) {if (r[e])return r[e].exports;var d = r[e] = {i: e,l: !1,exports: {}};console.log(e)window.code += e + ':' + o[e] + ',\r\n'return o[e].call(d.exports, d, d.exports, r),d.l = !0,d.exports
}
然后回车刷新浏览器,进行一遍查看排行榜的操作,发现控制台就会自动打印加载的模块:
控制台输入 copy(window.code)
将模块导出,然后同上述方法一样放到分发器中即可,再挂上代理,将常规的 document、navigator 补一下即可调用。最后结果如下:
算法还原
分析完 webpack 与补环境以后,我们最后来讲讲如何用算法生成。上文提到,我们进入 y.a 函数后,发现他主要是通过 Object(l.b) 和 t.from 这两个函数生成的,进入 Object(l.b) 发现是一个 AES 方法:
其中又发现了这个 t.from 方法,这个方法其实就是一个 utf8 编码,复现如下:
t=[]
t.from=function (hexString, encoding) {if (encoding !== "utf8") {throw new Error("Unsupported encoding");}// 将每个字符转换为对应的 UTF-8 编码的数值let byteArray = new Uint8Array(hexString.split('').map(char => char.charCodeAt(0)));return byteArray;
}
剩余的加密方法,我们引库复现即可:
const crypto = require('crypto');
var c = crypto.createDecipheriv("aes-128-cbc", n, o);
return d += c.update(e, "hex", "utf8"),d += c.final("utf8")
同时将 c 与 _ 这俩个函数也补一下(补函数的话,遵循和原函数一致即可,如果读不懂原函数,可能就会卡在某一部分):
function c(a) {return function(t) {return t;};
}
var n = c()(t); function _(n) {return typeof n === 'object' && n !== null;
}
最后全部函数实现完毕以后,结果如下:
只需 70 行即可完成 k 参数的生成,至此全部流程分析完毕。
八爪鱼采集
对于新手来说,0 代码实现数据采集是不二选择,同时他还可以设置代理 IP 进行免封操作。使用教程也非常简单,进入官网选择对应的系统版本进行下载安装:
安装完成以后打开软件,首先看到的是他拥有一个模板采集,里面内置了很多已经配置好的采集任务,点击即可一键应用:
我们点击“模板”或者“更多”,搜索点点数据,发现搜索未果:
那我们只能手动去新建一个任务,选择左侧新建,然后输入你要采集的网址:
进去以后,等待网页加载完毕,然后选择自动识别,它会根据页面的布局自动生成几套采集模板:
同时你也点击切换识别结果来自由切换识别模板:
很多时候识别的结果不尽如意,你可以选择删除某些多余的字段,同时也可以点击页面元素进行文本提取或者鼠标点击等操作:
在所有需要采集的东西都配置完毕以后,我们可以点击设置,进行代理的设置:
这里代理选择,我们采用快代理的私密代理或者独享代理进行配置即可:
所有任务都准备完成以后,即可保存进行任务的采集:
关于八爪鱼进阶的玩法还有自动打码与点击翻页等,特殊场景需要进行更多的实际应用。