本代码请仅用于 纯技术研究的 用途,请勿用于商业用途或 非法用途,如果因使用者非法使用造成的法律问题与本作者无关!
仅供学术研究,如有侵权请即时通知,文章会在第一时间内删除!
某直聘在 12 月期间好像,对他的 __zp_stoken__ 生成进行了更新
此次更新后有以下特征
- 动态代码 -- 和以前一样,是动态的代码。以前是每次请求代码都不一样,这次是每天变化代码
- 动态环境检测 -- 加入了动态环境检测,导致若没找到动态点请求成功率会非常低
- 代码混淆 -- 跟进阿里系列,加入了大控制流,使得调试代码成本增大
上面是整体代码和之前的区别。接下来开始分析
此次分析使用补环境的方式来实现模拟生成。并且最后达到了 100% 的成功率。先展示成果。
厚码展示。
开始补环境
请求逻辑这边就不进行分析了。其大致逻辑就是先请求一次对应的链接,在 set-cookie 中拿到 name, ts, seed 这三个参数,然后传入我们的 js 进行加密得到 token 再携带请求一次便可拿到正确数据
我们直接开始代码分析
定位
我们直接使用 hook cookie 的方式定位生成的地方
// ==UserScript==
// @name Hook Cookie
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @include *
// @grant none
// @run-at document-start
// ==/UserScript==
(function () {
'use strict';
var cookie_cache = document.cookie;
Object.defineProperty(document, 'cookie', {
get: function () {
return cookie_cache;
},
set: function (val) {
console.log('Setting cookie', val);
// 填写cookie名
if (val.indexOf('__zp_stoken__') != -1) {
debugger;
}
var cookie = val.split(";")[0];
var ncookie = cookie.split("=");
var flag = false;
var cache = cookie_cache.split("; ");
cache = cache.map(function (a) {
if (a.split("=")[0] === ncookie[0]) {
flag = true;
return cookie;
}
return a;
})
cookie_cache = cache.join("; ");
if (!flag) {
cookie_cache += cookie + "; ";
}
return cookie_cache;
}
});
})();
使用油猴导入上面的代码,在这边清除网站数据刷新,便可直接定位到目的地。
进入会发现,蛙趣,一个大控制流,这样直接调试的话很费时间,所以我们先选择解部分混淆来便于调试。
解混淆结果如下
虽然不是很完美,但是也已经够看了。有需要的可以找我获取,可提供解混淆后的代码。
然后进行替换就可以开开心心的调试了。
补环境
在调试的时候注意这种调用方式,这种一般就是在进行环境检测
之前写的一部分笔记
hl = n["call"](undefined, ql);
zl = hl in La;
gl = o["call"](undefined, wl);
ml = gl in La;
Sl = y["call"](undefined, bl);
有很多类似这样的代码 ["call"](undefined, ....) 这些代码里面最多就是检测一个函数是否存在,进行 typeof 运算,然后返回一个字符串
我们主要是需要这个字符串,那么现在就先不往里面看了,之后再看
而且这段操作是
a = window["a"]
b = typeof a
c = !b
其实即便 b 是 undefined 他也是 false 所以好像是不需要管的
再次分析,又好像是当 M = p["call"](undefined, R); 传入的第二个参数是个奇怪的字符串的时候,便是返回一个字符串,就可以不用往里面看了
z["call"](undefined, m, We);
这种传入的 m 是数组 We 是某个环境值,会将这个环境值添加到 m 数组中
只有一个 undefined 参数的,极有可能就是环境检测的
然后就进行一步一步的调试,发现他所需要的环境便可以达到 100% 成功率了。
部分检测点
光这样感觉没有干货啊,说点他的检测点吧
检测 document.all
这是一个老生常谈的检测了,现在已经有成熟的方案来处理了
我这边使用的是 node 插件来实现的,很简单就能实现他对 typeof document.all 的检测了。
但是,对 document.all 的检测不仅于此
他同时检测了 document.all 和 appendchild 之间的强关系
同时有下面这一段检测,这是检测的最小单元,你们可以先试试
function test() {
var type = typeof document.all
if (type === "undefined") {
var all_len = document.all.length;
var a = document.createElement("a");
document.body.appendChild(a);
var b = document.all[all_len]
if (a === b) {
delete document.all[all_len];
var c = document.all[all_len];
if (a === c) {
document.body.removeChild(a);
var d = document.all[all_len];
if (d === undefined) {
return "成功";
}
}
}
}
return "失败";
}
console.log(test())
返回成功便成功通过检测
检测 top 和 window 之间的关系
玩过老版本的都知道,其实 top 和 window 在改直聘中并不是同一个对象,其原因是其开了一个 f 窗口来加密的这个参数
所以我们对他们分开赋值便可解决
使用 Object.keys 检测了 window 和 document 的元素
这个很好解决 我们直接重写 Object.keys 方法即可。
结语
就分析到这了,再多就不行了
最终你将会得到 100% 成功率的 补环境 代码。加油特种兵。
“加速行业进步,破除行业垄断”
预告下期:akm~