补环境框架的搭建

  1. js逆向补环境原理及基础知识 - 爬虫之家
  2. js反爬检测原理及方法 - 爬虫之家
  3. 补浏览器环境框架之调试环境搭建 - 爬虫之家
  4. 浏览器环境基本框架设计及补window环境 - 爬虫之家
  5. js逆向今日头条“补浏览器环境” - 爬虫之家

node、v8和chrome的关系

image.png

经常检测的六大属性,浏览器对象查询:https://developer.mozilla.org/zh-CN/docs/Web

  • window
  • screen
  • history
  • navigator
  • document
  • location
  • global (node对象)

优质范文

vjstools自动补环境

参考

插件地址https://github.com/cilame/v_jstools

安装步骤:直接下载 .zip文件然后解压,在chrome的扩展程序管理界面加载文件夹,就能安装插件了

使用:image.png

image.png

然后生成临时环境

常规补环境

有两种设置代理对象的方式

第一种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function getEnvs(proxyObjs) {
for (let i = 0; i < proxyObjs.length; i++) {
const handler = `{
get: function(target, property, receiver) {
console.log("方法:", "get ", "对象:", "${proxyObjs[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", target[property], ", 属性值类型:", typeof target[property]);
return target[property];
},
set: function(target, property, value, receiver) {
console.log("方法:", "set ", "对象:", "${proxyObjs[i]}", " 属性:", property, " 属性类型:", typeof property, ", 属性值:", value, ", 属性值类型:", typeof target[property]);
return Reflect.set(...arguments);
}
}`;
eval(`try {
${proxyObjs[i]};
${proxyObjs[i]} = new Proxy(${proxyObjs[i]}, ${handler});
} catch (e) {
${proxyObjs[i]} = {};
${proxyObjs[i]} = new Proxy(${proxyObjs[i]}, ${handler});
}`);
}
}
proxyObjs = ['window', 'document', 'location', 'navigator', 'history', 'screen']
getEnvs(proxyObjs);

第二种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
window = new Proxy(window, {
set(target, property, value, receiver) {
console.log("设置属性set window", property, typeof value);
return Reflect.set(...arguments);
},
get(target, property, receiver) {
console.log("获取属性get window", property, typeof target[property]);
return target[property]
}
});

document = new Proxy(document, {
set(target, property, value, receiver) {
console.log("设置属性set document", property, typeof value);
return Reflect.set(...arguments);
},
get(target, property, receiver) {
console.log("获取属性get document", property, typeof target[property]);
return target[property]
}
});

navigator = new Proxy(navigator, {
set(target, property, value, receiver) {
console.log("设置属性set navigator", property, typeof value);
return Reflect.set(...arguments);
},
get(target, property, receiver) {
console.log("获取属性get navigator", property, typeof target[property]);
return target[property]
}
});

location = new Proxy(location, {
set(target, property, value, receiver) {
console.log("设置属性set location", property, typeof value);
return Reflect.set(...arguments);
},
get(target, property, receiver) {
console.log("获取属性get location", property, typeof target[property]);
return target[property]
}
});

history = new Proxy(history, {
set(target, property, value, receiver) {
console.log("设置属性set history", property, typeof value);
return Reflect.set(...arguments);
},
get(target, property, receiver) {
console.log("获取属性get history", property, typeof target[property]);
return target[property]
}
});

screen = new Proxy(screen, {
set(target, property, value, receiver) {
console.log("设置属性set screen", property, typeof value);
return Reflect.set(...arguments);
},
get(target, property, receiver) {
console.log("获取属性get screen", property, typeof target[property]);
return target[property]
}
});

对象的 toString 方法检测

1
2
3
4
5
6
7
var Object_toString = Object.prototype.toString;
Object.prototype.toString = function () {
let _temp = Object_toString.call(this, arguments);
console.log("调用对象:", this);
console.log("对象的toString函数值为:" + _temp);
return _temp;
};

补环境步骤

  1. 观察最后获取了哪一个属性值

    image.png

  2. 比对浏览器node环境 属性值的差异

    image.png

    image.png

  3. 替换掉属性值,保持和浏览器一致

1
2
3
document.toString = function toString(){
return '[object HTMLDocument]'
};
  1. 直到两边计算结果一致

    image.png

一些笔记

  • location

image.png

image.png

node环境为 undefined, 补充:

1
new JSDOM("<!DOCTYPE html><p>Hello world</p>", {"url": "https://www.zhihu.com/search"});
  • CanvasRenderingContext2D

CanvasRenderingContext2D:cancas的2d绘图对象,可以使用canvas dom对象的 getContext() 方法并把”2d”作为参数,从而获取对象

1
2
3
4
// 浏览器 console
mycanvas = document.createElement("canvas")
mycanvas.getContext("2d").toString()
>>> '[object CanvasRenderingContext2D]'

image.png

hook 代码:

1
2
3
4
5
6
7
8
9
10
11
var Object_toString = Object.prototype.toString;
Object.prototype.toString = function () {
let _temp = Object_toString.call(this, arguments);
console.log("调用对象:", this);
console.log("对象的 toString 函数值为:" + _temp);

if (this.constructor.name === 'CanvasRenderingContext2D'){
return '[object CanvasRenderingContext2D]'
}
return _temp;
};
  • 常规属性

image.png

补充:

1
2
window._resourceLoader = undefined;
window._sessionHistory = undefined;
  • 原型链

image.png

image.png

hook 代码:

1
2
3
4
5
6
7
8
9
10
11
var Function_toString = Function.prototype.toString;
Function.prototype.toString = function () {
let _temp = Function_toString.call(this, arguments);
console.log(this);
console.log("Function.prototype.toString: " + _temp);
// 保持和浏览器一致
if(this.name === 'Window'){
return 'function Window() { [native code] }'
}
return _temp;
};

工具

  • 志远js补环境框架

  • bestV8,用来运行js的V8环境

    1
    2
    说明:https://mp.weixin.qq.com/s/8z_Y421AiRDxGtG762ze6g
    python版本:https://github.com/wangluozhe/pybestV8

实战:网洛者第十一题(难度:困难)

http://spider.wangluozhe.com/challenge/11

image.png

fiddler 抓包 _signature

image.png

定位到 get_signature 函数

image.png

进入到函数,其实就是 11.js文件, 发现内部是一个自执行函数 + jsvmp

image.png

调用方式则是通过 window.get_signature(),在浏览器试运行一下,可以正常运行

需要注意,只有在 spider.wangluozhe.com 网站能运行

image.png

接下来只需确保在node能正常运行就可以啦(作者也明说了,这题考查的是 history 补环境

首先使用jsdom模拟window环境,执行安装命令: cnpm install jsdom@20.0.0

1
2
3
const { JSDOM} = require("jsdom");
const dom = new JSDOM("<!DOCTYPE html><p>Hello world</p>");
window = dom.window;

报错1:

image.png

显示 get_signature 方法没有赋值到 window对象里去,但是浏览器运行就没有这个问题

搜了一下源码,定位到这里

1
i = typeof global == nn[Vn] ? window : global

所以在文件头添加 global = window;

报错2:

image.png

还是缺少依赖,cnpm install crypto-js,安装后补充文件头

1
const CryptoJS = require('crypto-js');

还是报一样的错误,回去看网页源码,注意到这里:

image.png

image.png

好家伙,原来是赋值在 window里面

直接把整个js文件拿下来,补到源码里面,终于运行成功 ✨✨✨

image.png