网页逆向-补某js环境的this缺失

一般拿到一个代码,先跑一遍main.js,建议在toolsFunc里把proxy关掉跑,这样可以生成尽量少的日志,日志里只有关键函数缺失的信息

首先报错Document_head_get函数缺失

1
{tools|dispatch -> 环境缺失:Document_head_get ->错误:HTMLHeadElement is not defined 

document.head会返回当前页面的head标签,head标签实例的__proto__是HTMLHeadElement,需要把这个给脱下来,脱环境脚本运行getEnvCode(HTMLHeadElement)

拿到函数报错,第一步先在浏览器中执行,看到底返回的什么东西,是返回的实例,还是返回的字符串等,不同的返回值有不同的补法

然后在envFuncs.js里实现Document_head_get,生成一个head标签,设置这个标签对象的原型

1
2
3
4
5
sandBox.envFuncs.Document_head_get=function () {
let _head={}
Object.setPrototypeOf(_head,HTMLHeadElement.prototype)
return _head
}

然后补Navigator_javaEnabledNavigator_languages_get两个方法的缺失,这两个方法一个返回布尔值,一个返回字符串,和上面返回head标签实例的不同,不需要额外再补对象环境

1
2
3
4
5
6
sandBox.envFuncs.Navigator_javaEnabled=function () {
return false
}
sandBox.envFuncs.Navigator_languages_get=function () {
return ['zh-CN', 'en', 'en-GB', 'en-US']
}

继续处理报错

1
{Document_getElementsByTagName|未实现Document_getElementsByTagName_base} 

意思是缺失base标签,导致js代码再尝试获取base标签的时候获取不到,所以需要在userVar.js中添加标签,所有新增标签的操作都在userVar.js中实现

1
2
3
!function () {
let _base=document.createElement('base')
}()

此时会报错未实现对应的Document_createElement_base,去envFuncs.js中实现

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
//创建标签
sandBox.envFuncs.Document_createElement=function (tagName,options) {
let tag={};
switch (tagName) {
case "div":
Object.setPrototypeOf(tag,HTMLDivElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
case "meta":
Object.setPrototypeOf(tag,HTMLMetaElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
case "head":
Object.setPrototypeOf(tag,HTMLHeadElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
case "input":
Object.setPrototypeOf(tag,HTMLInputElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
case "a":
Object.setPrototypeOf(tag,HTMLAnchorElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
case "i":
Object.setPrototypeOf(tag,HTMLElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
case "canvas":
Object.setPrototypeOf(tag,HTMLCanvasElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
case "script":
Object.setPrototypeOf(tag,HTMLScriptElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
case "base":
Object.setPrototypeOf(tag,HTMLBaseElement.prototype)
tag=sandBox.proxy(tag,`Document_createElement_${tagName}_${sandBox.getID()}`)
break;
default:
console.log(`未实现Document_createElement_${tagName}`)
break;
}
sandBox.tags.push(tag)
return tag;
}

然后在浏览器执行let b = document.createElement("base");b.__proto__,发现调用base标签的原型是HTMLBaseElement,需要去把HTMLBaseElement的环境再脱下来

继续处理报错,刚才创建了base标签,现在要完善Document_getElementsByTagName方法,来获取base标签

1
{Document_getElementsByTagName|未实现Document_getElementsByTagName_base} 
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
//获取标签
sandBox.envFuncs.Document_getElementsByTagName=function (tagName){
let collections=[]
switch (tagName) {
case "meta":
collections=sandBox.getCollections('[object HTMLMetaElement]')
Object.setPrototypeOf(collections,HTMLCollection.prototype)
collections=sandBox.proxy(collections,`Document_getElementsByTagName_${tagName}`)
break;
case "i":
collections=sandBox.getCollections('[object HTMLElement]')
Object.setPrototypeOf(collections,HTMLElement.prototype)
collections=sandBox.proxy(collections,`Document_getElementsByTagName_${tagName}`)
break;
case "script":
collections=sandBox.getCollections('[object HTMLScriptElement]')
Object.setPrototypeOf(collections,HTMLScriptElement.prototype)
collections=sandBox.proxy(collections,`Document_getElementsByTagName_${tagName}`)
break;
case "base":
collections=sandBox.getCollections('[object HTMLBaseElement]')
Object.setPrototypeOf(collections,HTMLBaseElement.prototype)
collections=sandBox.proxy(collections,`Document_getElementsByTagName_${tagName}`)
break;
default:
console.log(`{Document_getElementsByTagName|未实现Document_getElementsByTagName_${tagName}}`)
break;
}
return collections
}

继续处理报错

1
{HTMLCanvasElement_getContext未实现:webgl2

首先要知道这个getContext是干啥的,在浏览器中创建一个canvas并执行getContext("webgl2")

1
2
3
let canvas = document.createElement("canvas");canvas.getContext("webgl2")
//返回
WebGL2RenderingContext {canvas: canvas, drawingBufferWidth: 300, drawingBufferHeight: 150, drawingBufferColorSpace: 'srgb', unpackColorSpace: 'srgb', …}

然后需要脱WebGL2RenderingContext环境,浏览器运行getEnvCode(WebGL2RenderingContext),把环境丢到envs目录下

然后补HTMLCanvasElement_getContext方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
sandBox.envFuncs.HTMLCanvasElement_getContext=function (name) {
let ctx={}
switch (name){
case '2d':
Object.setPrototypeOf(ctx,CanvasRenderingContext2D.prototype)
ctx=sandBox.proxy(ctx,`HTMLCanvasElement_getContext_${name}`)
break;
case 'webgl' || 'experimental-webgl' :
Object.setPrototypeOf(ctx,WebGLRenderingContext.prototype)
ctx=sandBox.proxy(ctx,`HTMLCanvasElement_getContext_${name}`)
break;
case 'webgl2':
Object.setPrototypeOf(ctx,WebGL2RenderingContext.prototype)
ctx=sandBox.proxy(ctx,`HTMLCanvasElement_getContext_${name}`)
break;
default:
console.log(`{HTMLCanvasElement_getContext | 未实现:${name}}`)
break;
}
sandBox.setProtoAttribute.call(this,'context',ctx)
return ctx
}

然后补异步事件缺失

1
2
3
4
5
{EventTarget_addEventListener | 异步事件:wheel} 
{EventTarget_addEventListener | 异步事件:mousemove}
{EventTarget_addEventListener | 异步事件:touchmove}
{EventTarget_addEventListener | 异步事件:click}
{EventTarget_addEventListener | 异步事件:keydown}

在asyncCode.js中可以实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let MouseEvents=[
{
"clientX": 154,
"clientY": 317,
"type": "mousemove",
"button": 0
},
]
for(let i=0;i<MouseEvents.length;i++){
let mouseEvent=MouseEvents[i]
let type=mouseEvent['type']
let listeners=sandBox.asyncCode.eventListener[type]
for(let j=0;j<listeners.length;j++){
let listen=listeners[j]
listen['func'].call(listen['this'],mouseEvent)
}
}

注意这里的鼠标事件要只给mousemove的,给mousedown这种的话js代码识别不了,因为匹配事件的逻辑就是根据事件type去匹配对应的函数进行调用,如果找不到对应的类型直接报错了

有的异步事件可以完全不用管,不会影响结果生成