原型链操作:

判断对象的类型:Object.prototype.toString.call()

hook函数,防检测:

1
2
3
Function.prototype.toString = function () {
return `function ${this.name}() { [native code] }`;
}

函数native化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
!function (){
const $toString = Function.prototype.toString;
const symbol = Symbol(); // 独一无二的属性
const myToString = function (){
return typeof this === 'function' && this[symbol] || $toString.call(this);
}
function set_native(func, key, value){
Object.defineProperty(func, key, {
enumerable: false,
configurable: true,
writable: true,
value: value
});
}
delete Function.prototype.toString;
set_native(Function.prototype, "toString", myToString);
set_native(Function.prototype.toString, symbol, "function toString() { [native code] }");
globalThis.setNative = function (func, funcname) {
set_native(func, symbol, `function ${funcname || func.name || ''}() { [native code] }`);
}
}();

实现效果:

1
2
3
4
5
6
7
add = function xxx(a, b){
return a + b;
}
setNative(add, "add");
console.log(add.toString());

// function add() { [native code] }

函数重命名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
reNameFunc = function reNameFunc(func, name){
Object.defineProperty(func, "name", {
configurable:true,
enumerable:false,
writable:false,
value:name
});
}


add = function xxx(){
return a+ b;
}

reNameFunc(add, "get add");
console.log(add.name);

call、apply、arguments、bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// call方法第一个参数是this指针,即调用者,第二个参数开始,就是原函数的实际参数
// apply 第一个参数也是this指针,调用者,第二个参数是一个数组,把时间参数进行打包的一个数组内

function info(height, weight){
console.log(`${this.name}:${this.age}`);
console.log(`${height}:${weight}`);
}
let user = {
"name": "小王",
"age":12
}
info.call(user, 156, 42);
info.apply(user, [156, 42]);
info.bind(user)(1,2,3)

// arguments : 类似一个数组的对象: //函数参数个数不确定时,使用apply 和arguments配合使用。
function test(){
let user = {
"name": "小王",
"age":12
}
info.apply(user, arguments);
}
test(160, 50)

hook 函数

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
ld = {}; // 全局对象

// 函数native化
!function (){
const $toString = Function.prototype.toString;
const symbol = Symbol(); // 无一无二的属性
const myToString = function (){
return typeof this === 'function' && this[symbol] || $toString.call(this);
}
function set_native(func, key, value){
Object.defineProperty(func, key, {
enumerable: false,
configurable: true,
writable: true,
value: value
});
}
delete Function.prototype.toString;
set_native(Function.prototype, "toString", myToString);
set_native(Function.prototype.toString, symbol, "function toString() { [native code] }");
ld.setNative = function (func, funcname) {
set_native(func, symbol, `function ${funcname || func.name || ''}() { [native code] }`);
}
}();

// 函数重命名
ld.reNameFunc = function reNameFunc(func, name){
Object.defineProperty(func, "name", {
configurable:true,
enumerable:false,
writable:false,
value:name
});
}

// hook 插件
hook = function (func, funcInfo, isDebug, onEnter, onLeave, isExec){
// func : 原函数,需要hook的函数
// funcInfo: 是一个对象,objName,funcName属性
// isDebug: 布尔类型, 是否进行调试,关键点定位,回溯调用栈
// onEnter:函数, 原函数执行前执行的函数,改原函数入参,或者输出入参
// onLeave: 函数,原函数执行完之后执行的函数,改原函数的返回值,或者输出原函数的返回值
// isExec : 布尔, 是否执行原函数,比如无限debuuger函数
if(typeof func !== 'function'){
return func;
}
if(funcInfo === undefined){
funcInfo = {};
funcInfo.objName = "globalThis";
funcInfo.funcName = func.name || '';
}
if(isDebug === undefined){
isDebug = false;
}
if(!onEnter){
onEnter = function (obj){
console.log(`{hook|${funcInfo.objName}[${funcInfo.funcName}]正在调用,参数是${JSON.stringify(obj.args)}}`);
}
}
if(!onLeave){
onLeave = function (obj){
console.log(`{hook|${funcInfo.objName}[${funcInfo.funcName}]正在调用,返回值是[${obj.result}}]`);
}
}
if(isExec === undefined){
isExec = true;
}
// 替换的函数
hookFunc = function (){
if(isDebug){
debugger;
}
let obj = {};
obj.args = [];
for (let i=0;i<arguments.length;i++){
obj.args[i] = arguments[i];
}
// 原函数执行前
onEnter.call(this, obj); // onEnter(obj);
// 原函数正在执行
let result;
if(isExec){
result = func.apply(this, obj.args);
}
obj.result = result;
// 原函数执行后
onLeave.call(this, obj); // onLeave(obj);
// 返回结果
return obj.result;
}
// hook 后的函数进行native
ld.setNative(hookFunc, funcInfo.funcName);
ld.reNameFunc(hookFunc, funcInfo.funcName);
return hookFunc;
}

function add(a,b){
// 输出信息
console.log("正在执行原函数add方法");
return a+b;
}

let funcInfo = {
"objName":"Obj",
"funcName": "add"
}
onEnter = function (obj) {
console.log("正在执行原函数调用前的操作", obj.args);
obj.args[0] = 10;
}
onLeave = function (obj) {
console.log("正在执行原函数调用后的操作", obj.result);
obj.result = 16;
}

add = hook(add, funcInfo, true, onEnter, onLeave, true);
// hook 特征隐藏
console.log(add.toString());
console.log(Function.prototype.toString.call(add));
console.log(add.name);

代理和反射

1
2
3
4
5
6
7
8
9
10
11
user = new Proxy(user, {
get:function (target,prop,receiver){// 三个参数
// target : 原始对象, user
// prop:属性
// receiver: 代理后的对象
console.log(`正在获取${prop.toString()}`);
let result = Reflect.get(target,prop,receiver);// 反射:执行原始操作
console.log(`返回值:${result}`);
return result;
}
});

代理器完整版

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// 9.Proxy其他拦截方法
// deleteProperty: 拦截对对象属性的 delete 操作
// has: 针对 in 操作符的代理方法。
// ownKeys: 拦截自身属性遍历。
// getPrototypeOf: 当读取代理对象的原型时,该方法就会被调用。
// setPrototypeOf: 当设置对象原型时,该方法就会被调用。
// isExtensible: 用于拦截对对象的 Object.isExtensible()
// preventExtensions: 对Object.preventExtensions()的拦截

// 链接: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy

ld = {};
ld.config = {};
ld.config.proxy = true; // 代理开关

ld.getType = function getType(obj){
return Object.prototype.toString.call(obj);
}

ld.proxy = function proxy(obj, objName){
// obj: 原始对象
// objName: 原始对象的名字
if(!ld.config.proxy){
return obj;
}
let handler = {
get:function (target,prop,receiver){
let result;
try {//防止报错
result = Reflect.get(target,prop,receiver);
let type = ld.getType(result);
if(result instanceof Object){
console.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`);
// 递归代理
result = ld.proxy(result, `${objName}.${prop.toString()}`);
}else if(typeof result === "symbol"){
console.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],ret:[${result.toString()}]}`);
}else{
console.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],ret:[${result}]}`);
}

}catch (e) {
console.log(`{get|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`);
}
return result;
},
set:function (target,prop,value,receiver){
let result;
try{
result = Reflect.set(target,prop,value,receiver);
let type = ld.getType(value);
if(value instanceof Object){
console.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`);
}else if(typeof value === "symbol"){
console.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],value:[${value.toString()}]}`);
}else{
console.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],value:[${value}]}`);
}
}catch (e){
console.log(`{set|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`);
}
return result;
},
// get的另一种方式,通过描述符对象
getOwnPropertyDescriptor:function (target, prop){
let result;
try{
result = Reflect.getOwnPropertyDescriptor(target, prop);
let type = ld.getType(result);
console.log(`{getOwnPropertyDescriptor|obj:[${objName}] -> prop:[${prop.toString()}],type:[${type}]}`);
// if(typeof result !== "undefined"){
// result = ld.proxy(result, `${objName}.${prop.toString()}.PropertyDescriptor`);
// }
}catch (e){
console.log(`{getOwnPropertyDescriptor|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`);
}
return result;
},
// set 的另一种方式,属性定义
defineProperty: function (target, prop, descriptor){
let result;
try{
result = Reflect.defineProperty(target, prop, descriptor);
console.log(`{defineProperty|obj:[${objName}] -> prop:[${prop.toString()}]}`);
}catch (e) {
console.log(`{defineProperty|obj:[${objName}] -> prop:[${prop.toString()}],error:[${e.message}]}`);
}
return result;
},
// hook 函数调用
apply:function (target, thisArg, argumentsList){
// target: 函数对象
// thisArg: 调用函数的this指针
// argumentsList: 数组, 函数的入参组成的一个列表
let result;
try{
result = Reflect.apply(target, thisArg, argumentsList);
let type = ld.getType(result);
if(result instanceof Object){
console.log(`{apply|function:[${objName}], type:[${type}]}`);
}else if(typeof result === "symbol"){
console.log(`{apply|function:[${objName}], result:[${result.toString()}]}`);
}else{
console.log(`{apply|function:[${objName}], result:[${result}]}`);
}
}catch (e) {
console.log(`{apply|function:[${objName}],error:[${e.message}]}`);
}
return result;
},
// hook 构造器,也即是 hook new guan'ji
construct:function (target, argArray, newTarget) {
// target: 函数对象
// argArray: 参数列表
// newTarget:代理对象
let result;
try{
result = Reflect.construct(target, argArray, newTarget);
let type = ld.getType(result);
console.log(`{construct|function:[${objName}], type:[${type}]}`);
}catch (e) {
console.log(`{construct|function:[${objName}],error:[${e.message}]}`);
}
return result;

},
deleteProperty:function (target, propKey){
let result = Reflect.deleteProperty(target, propKey);
console.log(`{deleteProperty|obj:[${objName}] -> prop:[${propKey.toString()}], result:[${result}]}`);
return result;
},
has:function (target, propKey){ // in 操作符
let result = Reflect.has(target, propKey);
console.log(`{has|obj:[${objName}] -> prop:[${propKey.toString()}], result:[${result}]}`);
return result;
},
ownKeys: function (target){
let result = Reflect.ownKeys(target);
console.log(`{ownKeys|obj:[${objName}]}`);
return result
},
getPrototypeOf:function(target){
let result = Reflect.getPrototypeOf(target);
console.log(`{getPrototypeOf|obj:[${objName}]}`);
return result;
},
setPrototypeOf:function(target, proto){
let result = Reflect.setPrototypeOf(target, proto);
console.log(`{setPrototypeOf|obj:[${objName}]}`);
return result;
},
preventExtensions:function(target){
let result = Reflect.preventExtensions(target, proto);
console.log(`{preventExtensions|obj:[${objName}]}`);
return result;
},
isExtensible:function(target){
let result = Reflect.isExtensible(target, proto);
console.log(`{isExtensible|obj:[${objName}]}`);
return result;
}
};
return new Proxy(obj, handler);
}

使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
user = {
"name" : "小明"
}
Object.defineProperty(user, "age",{
configurable: false,
enumerable:true,
value:10
});

user = ld.proxy(user, "user");
delete user.age;// 删除不成功
console.log(user.age);
// delete user.name;// 删除成功
console.log(user.name);
// 判断属性是否存在
console.log("age" in user);
console.log("name" in user);
// 获取对象所有自身可枚举属性组成的一个数组
console.log(Object.keys(user));
// 获取原型对象
console.log(user.__proto__);
// 设置原型对象
let testObj = {};
user.__proto__ = testObj;