-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathhook.js
425 lines (369 loc) · 15.6 KB
/
hook.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
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
(() => {
if (!window) {
return;
}
if (window.astHookDone) {
return;
}
window.astHookDone = true;
/**
* 暴露给外面的接口,方法前缀起到命名空间的作用
*
* @param name 对象的属性名或者变量的名称
* @param value 对象的属性值或者变量的值
* @param type 声明是什么类型的,对象属性值还是变量赋值,以后或者还会有其它的
* @returns {string}
*/
astHook = window._hook = window.hook = window.astHook = function (name, value, type) {
let newValue = null;
try {
newValue = _hook(name, value, type);
} catch (e) {
console.error(e);
}
return newValue || value;
}
astHook.hookCallback = [];
function _hook(name, value, type) {
let newValue = null;
for (let callback of astHook.hookCallback) {
try {
hijackValue = callback(name, value, type);
if (hijackValue){
newValue = hijackValue;
}
} catch (e) {
console.error(e);
}
}
return newValue;
}
(() => {
const initDbMessage = "AST HOOK: 如果本窗口内有多个线程,每个线程栈的数据不会共享,初始化线程栈数据库: \n "
+ window.location.href;
console.log(initDbMessage);
// 用于存储Hook到的所有字符串类型的变量
const stringsDB = window.astHook.stringsDB = window.astHook.stringsDB || {
varValueDb: [],
codeLocationExecuteTimesCount: []
};
const { varValueDb, codeLocationExecuteTimesCount } = stringsDB;
// 从一个比较大的数开始计数,以方便在展示的时候与执行次数做区分,差值过大就不易混淆
let execOrderCounter = 100000;
function stringPutToDB(name, value, type) {
if (!value) {
return;
}
// TODO 更多类型搞进来
let valueString = "";
let valueTypeof = typeof value;
if (valueTypeof === "string") {
valueString = value;
} else if (valueTypeof === "number") {
valueString = value;
}
if (!valueString) {
return;
}
// 获取代码位置
const codeLocation = getCodeLocation();
varValueDb.push({
name,
// TODO Buffer类结构直接运算Hook不到的问题仍然没有解决...
// 默认情况下把所有变量都toString保存到字符串池子中
// 有一些参数就是放在Buffer或者什么地方以字节形式存储,当使用到的时候直接与字符串相加toString,
// 这种情况如果只监控变量赋值就监控不到了,这是不想添加更多监控点的情况下的折中方案...
// 所以干脆在它还是个buffer的时候就转为字符串
value: valueString,
type,
execOrder: execOrderCounter++,
codeLocation
});
// 这个地方被执行的次数统计
if (codeLocation in codeLocationExecuteTimesCount) {
codeLocationExecuteTimesCount[codeLocation]++;
} else {
codeLocationExecuteTimesCount[codeLocation] = 1;
}
}
window.getCodeLocation = getCodeLocation = function () {
const callstack = new Error().stack.split("\n");
while (callstack.length > 0 && callstack[0].indexOf("astHook") === -1) {
callstack.shift();
}
if (callstack.length < 2) {
return null;
}
callstack.shift();
return callstack.shift();
}
// 添加Hook回调
window.astHook.hookCallback.push(stringPutToDB);
})();
(() => {
// 检索字符串数据库
const astHook = window.astHook;
const stringsDB = astHook.stringsDB;
window.hijackMap = hijackMap = {};
function hijackValue(name, value, type) {
if (window.hijackMap && Object.keys(window.hijackMap).length !== 0) {
const codeLocation = getCodeLocation();
// 如果命中,则强制进行锁定赋值
let uuid = `${codeLocation}${name}${type}`;
let newValue = hijackMap[uuid];
if (newValue) {
return newValue;
}
}
}
window.hijack = function (execOrder, newValue) {
let varValueDb = astHook.stringsDB.varValueDb[execOrder - 100000];
if (!varValueDb) {
console.warn(`查找失败!数据库中找不到调用堆栈序号${execOrder}`);
return;
}
let { codeLocation, name, type } = varValueDb
let uuid = `${codeLocation}${name}${type}`;
hijackMap[uuid] = newValue;
}
window.cancelHijack = function (execOrder){
if (window.hijackMap && Object.keys(window.hijackMap).length !== 0){
delete window.hijackMap[execOrder];
}
}
window.clearHijack = function(){
for (let key in window.hijackMap){
delete window.hijackMap[key];
}
window.hijackMap = {};
}
// 添加Hook回调
window.astHook.hookCallback.push(hijackValue);
})();
(() => {
// 检索字符串数据库
const astHook = window.astHook;
const stringsDB = astHook.stringsDB;
// 发送消息时的域名,用于识别内部消息
const messageDomain = "astHook";
const messageTypeSearch = "search";
// 防止消息重复处理
const alreadyProcessMessageIdSet = new Set();
window.addEventListener("message", event => {
const eventData = event.data;
if (!eventData || eventData.domain !== messageDomain) {
return;
}
// 如果已经处理过的话,则不再处理
const messageId = eventData.messageId;
if (alreadyProcessMessageIdSet.has(messageId)) {
return;
}
if (eventData.type === messageTypeSearch) {
const pattern = eventData.pattern;
const isEquals = eventData.isEquals;
const fieldName = eventData.fieldName;
const isNeedExpansion = eventData.isNeedExpansion;
_search(fieldName, pattern, isEquals, isNeedExpansion);
alreadyProcessMessageIdSet.add(messageId);
_searchParentAndChildren(messageId, fieldName, pattern, isEquals, isNeedExpansion);
}
});
window.search = window.searchByValue = astHook.search = astHook.searchByValue = function (pattern, isEquals = true, isNeedExpansion = true) {
const fieldName = "value";
// 先搜索当前页面
_search(fieldName, pattern, isEquals, isNeedExpansion);
const messageId = new Date().getTime();
alreadyProcessMessageIdSet.add(messageId);
// 然后递归搜索父页面和子页面
_searchParentAndChildren(messageId, fieldName, pattern, isEquals, isNeedExpansion);
}
window.view = function (order) {
showResultByExecOrder(order);
}
window.searchByName = astHook.searchByName = function (pattern, isEquals = false, isNeedExpansion = false) {
const fieldName = "name";
// 先搜索当前页面
_search(fieldName, pattern, isEquals, isNeedExpansion);
const messageId = new Date().getTime();
alreadyProcessMessageIdSet.add(messageId);
// 然后递归搜索父页面和子页面
_searchParentAndChildren(messageId, fieldName, pattern, isEquals, isNeedExpansion);
}
function _searchParentAndChildren(messageId, fieldName, pattern, isEquals, isNeedExpansion) {
const searchMessage = {
"domain": messageDomain,
"type": messageTypeSearch,
"fieldName": fieldName,
"messageId": messageId,
pattern,
isEquals,
isNeedExpansion
}
// 子页面
const iframeArray = document.getElementsByTagName("iframe");
if (iframeArray.length) {
for (let iframe of iframeArray) {
iframe.contentWindow.postMessage(searchMessage, "*");
}
}
// 父页面
if (window.parent) {
window.parent.postMessage(searchMessage, "*");
}
}
function _search(filedName, pattern, isEquals, isNeedExpansion) {
const result = [];
const expansionValues = isNeedExpansion ? expansionS(pattern) : [pattern];
for (let s of stringsDB.varValueDb) {
let isMatch = false;
if (typeof pattern === "string") {
if (isEquals) {
for (let newPattern of expansionValues) {
isMatch = isMatch || (newPattern === s[filedName]);
}
} else {
for (let newPattern of expansionValues) {
isMatch = isMatch || (s[filedName] && s[filedName].indexOf(newPattern) !== -1);
}
}
} else if (pattern instanceof RegExp) {
isMatch = pattern.test(s[filedName]);
} else if (typeof pattern === "number") {
isMatch = pattern === s.value;
}
if (!isMatch) {
continue;
}
const codeInfo = parseCodeLocation(s.codeLocation)
result.push({
name: s.name,
value: abbreviationPattern(pattern, s[filedName]),
type: s.type,
execOrder: s.execOrder,
codeName: codeInfo.codeName,
codeAddress: codeInfo.codeAddress,
execTimes: stringsDB.codeLocationExecuteTimesCount[s.codeLocation]
});
}
showResult(result);
}
// 对搜索值进行一个扩大,以便能够搜索到更多结果
function expansionS(s) {
const result = [];
// 原字符串是要放进去的
result.push(s);
if (typeof s !== "string") {
return result;
}
// url编码后
try {
const t = encodeURIComponent(s);
if (result.indexOf(t) === -1) {
result.push(t);
}
} catch (e) {
}
// url解码后
try {
const t = decodeURIComponent(s);
if (result.indexOf(t) === -1) {
result.push(t);
}
} catch (e) {
}
try {
const t = s.replace(/ /g, "+");
if (result.indexOf(t) === -1) {
result.push(t);
}
} catch (e) {
}
return result;
}
let tempResultList = [];
function showResult(result) {
tempResultList.length = 0;
tempResultList = result;
let message = "\n在线程栈: \n" + window.location.href + "\n";
if (!result.length) {
message += "中没有搜索到结果。\n\n";
console.log(message);
console.log("\n\n\n");
return;
}
message += `中搜到${result.length}条结果: \n\n`;
console.log(message);
result.push({
"name":"","value":"","type":"","execTimes":"","execOrder":"","codeName":"","codeAddress":""
})
console.table(result,["name","value","type","execTimes","execOrder","codeName","codeAddress"]);
}
function showResultByExecOrder(execOrder) {
if (tempResultList.length == 0) {
console.warn("查询失败!请先确定已经检索到堆栈信息!");
} else {
let idx = -1;
tempResultList.forEach((res, index) => {
if (res.execOrder == execOrder) {
idx = index;
}
})
if (idx > -1) {
console.group(`堆栈序号:${execOrder}`);
let result = tempResultList[idx];
for (let key in result) {
switch (key) {
case "name":
console.log(`变量名:${result[key]}`);
break;
case "value":
console.log(`变量值:${result[key]}`);
break;
case "type":
console.log(`变量类型:${result[key]}`);
break;
case "execTimes":
console.log(`执行次数:${result[key]}`);
break;
case "execOrder":
console.log(`执行顺序:${result[key]}`);
break;
case "codeName":
console.log(`所在函数:${result[key]}`);
break;
case "codeAddress":
console.log(`代码位置:${result[key]}`);
break;
}
}
console.groupEnd();
} else {
console.warn("堆栈信息中查询不到你要查询的结果!请先确定查询序号是否有误!");
}
}
}
function abbreviationPattern(pattern, value) {
if (typeof pattern !== "string" || pattern.length < 40) {
return value;
}
const newPattern = pattern.slice(0, 15) + "......" + pattern.slice(pattern.length - 15, pattern.length);
return value.replace(pattern, newPattern);
}
window.parseCodeLocation = parseCodeLocation = function (codeLocation) {
const codeInfo = {};
let matcher = codeLocation.match(/\((.+?)\)/);
if (matcher != null && matcher.length > 1) {
codeInfo.codeAddress = matcher[1];
} else {
codeInfo.codeAddress = codeLocation;
}
matcher = codeLocation.match(/at (.+?)\(/);
if (matcher != null && matcher.length > 1) {
codeInfo.codeName = matcher[1]
}
return codeInfo;
}
})();
// todo eval
})();