Boss直聘数据获取zp_stoken参数JSRPC教学
Boss直聘数据获取zp_stoken参数RPC教学
RPC过某直聘zp_stoken
Python案例教学--爬虫之Boss直聘招聘数据采集(zp_stoken参数逆向分析JSRPC教学)
纯算 稳定但是难度过大
扣代码 补环境 可以调用但是不稳定
这两个方法都可以解决,但是今天要讲的是 秒解法,RPC 有谁还不知道rpc怎么用的吗,如果有需要可以咨询,如果人多就单独出一期教程。
流程:
将 new ABC().z 方法 赋值给window全局
- 将 new ABC().z 方法 赋值给window全局

- 打开rpc软件
- 在浏览器上开接口
- python远程调用
秒了~
具体大部人都清楚
分析必要参数
抓包目标网址
用 无痕 或者 清除所有cookies后,刷新页面(记得打开Preserve log)
使用 search 定位 到数据的请求包
根据网站headers和cookies逐一排查
完整的请求:
headers 包含 accept,accept-language,cache-control,pragma,priority,referer…user-agent,…
coookise 包含 __g,wd_guid,historyState,Hm_lvt_194df3105ad7148dcf2b98a91b5e727a,…, _ _ zp_stoken _ _等等参数。
headers参数排除
一般观察 除了 UA 以外还有没有其他的有规律的字符串(有规律包括,16或者32为字符串(md5加密),AES加密的基本格式等,做多了就知道了)
显然没有,将headers 除 UA外所有的参数注释,依然会成功,如图:
说明headers没有加密参数。
cookies参数排除
cookies就比较重要了,建议一个一个值去排除,一个一个注释,然后运行,如果不出来值,就说明这个值可能存在问题(因为可能刚好测试到这个值,其他的cookies失效了,所以需要多刷新几次页面,复制curl,进行参数对比),或者用fidder的请求,去实现也行。
一个一个参数测试的步骤省略,最终确定,如果有**zp_stoken**这个值,就能够请求成功,如果没有,就请求失败。
这一步就已经确认了 加密参数 为__zp_stoken__。
参数定位(hook) 既然是cookies里面的,直接hook。(如果有不明白hook原理的,可以质询,如果问的人多,就专门写一期hook的讲解) hook代码:
let cookieCache = "";
Object.defineProperty(document, "cookie", {
set: function (val) {
console.log("Hook set cookie => ", val);
if (val.indexOf("__zp_stoken__") !== -1) {
debugger;
}
cookieCache = val;
return val;
},
get: function () {
return cookieCache;
}
});
注意:先清除所有cookies再刷新页面,防止干扰 还有一件事,因为访问页面后,他会有两次页面刷新才会出现,每次刷新,都会更新所有函数,所以会将hook也更新掉,需要断点到第二次页面之后哦~
根据堆栈,一步一步找生成的位置。 很明显,如图所示
code = new ABC().z(seed, parseInt(ts) + (480 + new Date().getTimezoneOffset()) * 60 * 1000)
后面的encodeURIComponent(t)就是我们想要的加密内
- 只要拿到 new ABC().z 这个方法 和 seed 和ts 就能出结果了。
但是这边还有两个参数是我们不知道的一个是t,一个是n,对应我们的参数就是seed和ts。他们是参数传递进来的,所以我们还要往上跟找到他们是啥。最终在上一个方法中找到了他们其实是从Cookie中分别取了__zp_sseed__和__zp_sts__这两个Cookie值,这也是为啥我们的参数要命名seed和ts的原因。
坑1
这边有个坑,多次刷新会发现后端返回的js文件不同,这导致我们每次下的断点,之后刷新并不能正确的停在我们想要的地方,所以我们需要固定相关的js,所有的静态资源都是从首个静态文件调用的,所以需要固定第一个放回的静态页面,固定后他里面调用的js就是固定的了。这样就能保证服务器每次返回的都是我们调试的js了。注:固定文件使用的是浏览器的overwrite功能。
知道了seed和ts是从Cookie中取的,但是我们要知道他不会一直就在Cookie里,肯定也是在哪边设置的,最后在反复的查找中发现。
当我们使用该接口请求成功后,响应头中会对其值进行设置,当我们请求失败他会直接在响应体中返回值。
刷新界面后继续往上跟,可以发现t是这边的r变量传过去的,继续在这边找r变量是怎么来的。
往上翻找到了r的生成位置,这边就是我们的关键代码,如果是要扣算法的话害得继续跟z的逻辑,但是因为我们使用rpc的方式,只需要找到他的生成位置即可。我们固定这条js,在其下方插桩注入我们的代码,将这个生成r的方法注入到一个全局的方法里面去,当我们刷新页面,代码运行到这边时就会将这个逻辑执行,我们就可以在任意的地方调用这个代码生成加密内容了。
// 传入需要的参数,返回加密内容
window.boss = function name(seed, ts) {
return (new e).z(seed, parseInt(ts) + 60 * (480 + (new Date).getTimezoneOffset()) * 1e3)
}
0x04RPC调用
调用测试

上面我们成功找到了加密字段zp_stoken,并插桩让我们可以在任意位置调用其生成逻辑,这边我们测试一下调用。在浏览器控制台调用我们注入的方法并传入对应的参数,发现成功调用生成逻辑,并返回加密内容。
Sekiro RPC框架
这边的sekiro可不是打铁的只狼,是一个提供远程调用的rpc框架。只需要启动他的服务端,然后在需要远程调用的位置使用提供的API注册服务,我们就可以在任意位置调用该服务。
我们使用油猴注入代码到网页中,将注册服务的代码注入到当前的网页。这边有几个重要的点,一个是group,一个是传入的方法名,待会我们远程调用这个服务的时候会用到,可以看到在下面的逻辑中调用了我们之前插桩的函数,调用那个函数生成加密内容进行返回,即可通过远程调用的方式拿到加密的内容。
function SekiroClient(e){
//... 省略内容,这边有一长串的代码,在sekiro文档中有
var client = new SekiroClient("ws://127.0.0.1:5612/business/register?group=boss&clientId=" + Math.random());
client.registerAction("getZpStoken", function (request, resolve, reject) {
try{
var seed = request['seed'];
var ts = request['ts'];
console.log("rpc调用,seed:"+seed+",ts:"+ts);
var result = encodeURIComponent(window.boss(seed, ts));
resolve(result);
}catch(e){
reject("error ->> "+e)
}
});
上面的代码向sekiro服务器中注册了一个方法名为 getZpStoken的服务,这时我们只需要向sekiro服务器发送请求调用该服务即可。参数中的group和action要与上面注册的时候一致。再传入需要的参数即可完成远程调用拿到浏览器生成的加密内容。
params = {
'group': 'boss',
'action': "getZpStoken",
'seed': seed,
'ts': ts
}
rsp = session.get('http://127.0.0.1:5612/business/invoke', params=params)
data = rsp.json()
4调用逻辑分析 在调用接口请求数据时,我们首先要拿到seed和ts的值,但是因为这两项是在请求之后才会变更,我们可以先生成一个正确的token值进行调用,调用成功后会在响应头返回变更后的值,再拿到变更的值调用远程服务去获取加密的token值进行调用,反复循环,即可爬取页面上的所有内容。
jxhczhl/JsRpc: 远程调用(rpc)浏览器方法,免去抠代码补环境
