@wulu007/stealth-hook 0.0.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -5
- package/dist/index.cjs +78 -37
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.global.js +78 -37
- package/dist/index.js +78 -37
- package/package.json +14 -6
package/README.md
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
2
1
|
# StealthHook
|
|
3
2
|
|
|
4
3
|
A lightweight JavaScript hook library for intercepting and modifying function behavior in the browser. Designed for stealth modifications with automatic iframe detection and recursive hooking.
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
<div align="center">
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@wulu007/stealth-hook)
|
|
8
|
+
[](https://www.npmjs.com/package/@wulu007/stealth-hook)
|
|
9
|
+
[](https://github.com/wulu007/StealthHook/blob/main/LICENSE)
|
|
10
|
+
[](https://bundlephobia.com/package/@wulu007/stealth-hook)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
8
13
|
|
|
9
14
|
## Features
|
|
10
15
|
|
|
@@ -29,7 +34,7 @@ pnpm add stealth-hook
|
|
|
29
34
|
### In Userscripts
|
|
30
35
|
|
|
31
36
|
```javascript
|
|
32
|
-
// @require https://unpkg.com/stealth-hook@latest/dist/index.js
|
|
37
|
+
// @require https://unpkg.com/@wulu007/stealth-hook@latest/dist/index.global.js
|
|
33
38
|
// @grant unsafeWindow
|
|
34
39
|
// @run-at document-start
|
|
35
40
|
```
|
|
@@ -60,7 +65,7 @@ hookScope(({ hookMethod }, win) => {
|
|
|
60
65
|
// @description Hook Example
|
|
61
66
|
// @author You
|
|
62
67
|
// @match *://*/*
|
|
63
|
-
// @require https://unpkg.com/stealth-hook@latest/dist/index.js
|
|
68
|
+
// @require https://unpkg.com/@wulu007/stealth-hook@latest/dist/index.global.js
|
|
64
69
|
// @grant unsafeWindow
|
|
65
70
|
// @run-at document-start
|
|
66
71
|
// ==/UserScript==
|
package/dist/index.cjs
CHANGED
|
@@ -124,16 +124,18 @@ var createLogger = /* @__PURE__ */ (() => {
|
|
|
124
124
|
|
|
125
125
|
// src/index.ts
|
|
126
126
|
function hookScope(callback, rootWindow) {
|
|
127
|
+
const hookedProtoSet = /* @__PURE__ */ new WeakSet();
|
|
127
128
|
const interceptorSetupMap = /* @__PURE__ */ new WeakSet();
|
|
128
|
-
const
|
|
129
|
+
const windowIdMap = /* @__PURE__ */ new WeakMap();
|
|
130
|
+
const iframeListenerMap = /* @__PURE__ */ new WeakMap();
|
|
129
131
|
let windowCounter = 0;
|
|
130
132
|
const $reflectApply = Reflect.apply;
|
|
131
133
|
const getWindowId = (win) => {
|
|
132
|
-
if (!
|
|
134
|
+
if (!windowIdMap.has(win)) {
|
|
133
135
|
const id = win === rootWindow ? "main" : `iframe-${++windowCounter}`;
|
|
134
|
-
|
|
136
|
+
windowIdMap.set(win, id);
|
|
135
137
|
}
|
|
136
|
-
return
|
|
138
|
+
return windowIdMap.get(win);
|
|
137
139
|
};
|
|
138
140
|
const setupToStringHook = (win) => {
|
|
139
141
|
hook.method(win.Function.prototype, "toString", (target, args, thisArg) => {
|
|
@@ -143,54 +145,93 @@ function hookScope(callback, rootWindow) {
|
|
|
143
145
|
return $reflectApply(target, thisArg, args);
|
|
144
146
|
});
|
|
145
147
|
};
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
const handleIframeNode = (node, applyCallback) => {
|
|
149
|
+
const element = node;
|
|
150
|
+
const tryHook = () => {
|
|
151
|
+
try {
|
|
152
|
+
const childWin = element.contentWindow;
|
|
153
|
+
if (childWin) {
|
|
154
|
+
applyCallback(childWin);
|
|
155
|
+
setupObserver(childWin, childWin.document, applyCallback);
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
tryHook();
|
|
161
|
+
const oldListener = iframeListenerMap.get(element);
|
|
162
|
+
if (oldListener)
|
|
163
|
+
element.removeEventListener("load", oldListener);
|
|
164
|
+
element.addEventListener("load", tryHook);
|
|
165
|
+
iframeListenerMap.set(element, tryHook);
|
|
166
|
+
};
|
|
167
|
+
function setupObserver(win, targetNode, applyCallback) {
|
|
168
|
+
if (interceptorSetupMap.has(targetNode))
|
|
148
169
|
return;
|
|
149
|
-
interceptorSetupMap.add(
|
|
170
|
+
interceptorSetupMap.add(targetNode);
|
|
150
171
|
const logger = createLogger(getWindowId(win));
|
|
151
172
|
try {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
applyCallback(childWin);
|
|
165
|
-
setupIframeInterceptor(childWin, applyCallback);
|
|
173
|
+
const observer = new win.MutationObserver((mutations) => {
|
|
174
|
+
for (const mutation of mutations) {
|
|
175
|
+
if (mutation.type === "childList") {
|
|
176
|
+
mutation.addedNodes.forEach((node) => {
|
|
177
|
+
const tagName = node.tagName;
|
|
178
|
+
if (tagName === "IFRAME" || tagName === "FRAME" || tagName === "OBJECT") {
|
|
179
|
+
handleIframeNode(node, applyCallback);
|
|
180
|
+
} else if (node instanceof win.HTMLElement) {
|
|
181
|
+
if (node.childElementCount > 0) {
|
|
182
|
+
const frames = node.querySelectorAll("iframe, frame, object");
|
|
183
|
+
frames.forEach((frame) => handleIframeNode(frame, applyCallback));
|
|
184
|
+
}
|
|
166
185
|
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
186
|
+
});
|
|
187
|
+
} else if (mutation.type === "attributes") {
|
|
188
|
+
if (mutation.target instanceof win.Element) {
|
|
189
|
+
logger.log(`\u{1F504} Detected attribute change on <${mutation.target.tagName}>`, mutation.attributeName);
|
|
190
|
+
handleIframeNode(mutation.target, applyCallback);
|
|
191
|
+
}
|
|
171
192
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
observer.observe(targetNode, {
|
|
196
|
+
childList: true,
|
|
197
|
+
subtree: true,
|
|
198
|
+
attributes: true,
|
|
199
|
+
attributeFilter: ["src", "srcdoc", "data"]
|
|
200
|
+
});
|
|
178
201
|
} catch (e) {
|
|
179
|
-
logger.warn("\u274C Failed to setup
|
|
202
|
+
logger.warn("\u274C Failed to setup MutationObserver", e);
|
|
180
203
|
}
|
|
181
204
|
}
|
|
205
|
+
const setupShadowDomHook = (win, applyCallback) => {
|
|
206
|
+
try {
|
|
207
|
+
if (win.Element.prototype.attachShadow) {
|
|
208
|
+
hook.method(win.Element.prototype, "attachShadow", (original, args, scope) => {
|
|
209
|
+
const shadowRoot = $reflectApply(original, scope, args);
|
|
210
|
+
if (shadowRoot) {
|
|
211
|
+
setupObserver(win, shadowRoot, applyCallback);
|
|
212
|
+
}
|
|
213
|
+
return shadowRoot;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
};
|
|
182
219
|
const applyToWindow = (win) => {
|
|
183
|
-
|
|
220
|
+
try {
|
|
221
|
+
const proto = win.Function.prototype;
|
|
222
|
+
if (hookedProtoSet.has(proto))
|
|
223
|
+
return;
|
|
224
|
+
hookedProtoSet.add(proto);
|
|
225
|
+
} catch {
|
|
184
226
|
return;
|
|
227
|
+
}
|
|
185
228
|
const logger = createLogger(getWindowId(win));
|
|
186
229
|
setupToStringHook(win);
|
|
230
|
+
setupShadowDomHook(win, applyToWindow);
|
|
187
231
|
const utils = {
|
|
188
232
|
hookMethod: (obj, fnName, handler) => {
|
|
189
233
|
hook.method(obj, fnName, handler);
|
|
190
234
|
},
|
|
191
|
-
hookBindThisMethod(obj, fnName, handler) {
|
|
192
|
-
hook.bindThisMethod(obj, fnName, handler);
|
|
193
|
-
},
|
|
194
235
|
getNativeFunction: hook.getNativeFunction,
|
|
195
236
|
getNativeString: hook.getNativeString,
|
|
196
237
|
getCurrentWindowId: () => getWindowId(win)
|
|
@@ -203,7 +244,7 @@ function hookScope(callback, rootWindow) {
|
|
|
203
244
|
}
|
|
204
245
|
};
|
|
205
246
|
applyToWindow(rootWindow);
|
|
206
|
-
|
|
247
|
+
setupObserver(rootWindow, rootWindow.document, applyToWindow);
|
|
207
248
|
}
|
|
208
249
|
// Annotate the CommonJS export names for ESM import in node:
|
|
209
250
|
0 && (module.exports = {
|
package/dist/index.d.cts
CHANGED
|
@@ -20,11 +20,14 @@ declare global {
|
|
|
20
20
|
interface Window {
|
|
21
21
|
Node: typeof Node;
|
|
22
22
|
Function: typeof Function;
|
|
23
|
+
MutationObserver: typeof MutationObserver;
|
|
24
|
+
HTMLIFrameElement: typeof HTMLIFrameElement;
|
|
25
|
+
HTMLElement: typeof HTMLElement;
|
|
26
|
+
Element: typeof Element;
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
29
|
interface HookUtils {
|
|
26
30
|
hookMethod: <T extends object, K extends keyof T>(obj: T, fnName: K, handler: HookHandler<T[K] extends AnyFunction ? T[K] : AnyFunction>) => void;
|
|
27
|
-
hookBindThisMethod: <T extends object, K extends keyof T>(obj: T, fnName: K, handler: HookHandler<T[K] extends AnyFunction ? T[K] : AnyFunction>) => void;
|
|
28
31
|
getCurrentWindowId: () => string | number;
|
|
29
32
|
getNativeFunction: typeof hook.getNativeFunction;
|
|
30
33
|
getNativeString: typeof hook.getNativeString;
|
package/dist/index.d.ts
CHANGED
|
@@ -20,11 +20,14 @@ declare global {
|
|
|
20
20
|
interface Window {
|
|
21
21
|
Node: typeof Node;
|
|
22
22
|
Function: typeof Function;
|
|
23
|
+
MutationObserver: typeof MutationObserver;
|
|
24
|
+
HTMLIFrameElement: typeof HTMLIFrameElement;
|
|
25
|
+
HTMLElement: typeof HTMLElement;
|
|
26
|
+
Element: typeof Element;
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
29
|
interface HookUtils {
|
|
26
30
|
hookMethod: <T extends object, K extends keyof T>(obj: T, fnName: K, handler: HookHandler<T[K] extends AnyFunction ? T[K] : AnyFunction>) => void;
|
|
27
|
-
hookBindThisMethod: <T extends object, K extends keyof T>(obj: T, fnName: K, handler: HookHandler<T[K] extends AnyFunction ? T[K] : AnyFunction>) => void;
|
|
28
31
|
getCurrentWindowId: () => string | number;
|
|
29
32
|
getNativeFunction: typeof hook.getNativeFunction;
|
|
30
33
|
getNativeString: typeof hook.getNativeString;
|
package/dist/index.global.js
CHANGED
|
@@ -124,16 +124,18 @@ var StealthHook = (() => {
|
|
|
124
124
|
|
|
125
125
|
// src/index.ts
|
|
126
126
|
function hookScope(callback, rootWindow) {
|
|
127
|
+
const hookedProtoSet = /* @__PURE__ */ new WeakSet();
|
|
127
128
|
const interceptorSetupMap = /* @__PURE__ */ new WeakSet();
|
|
128
|
-
const
|
|
129
|
+
const windowIdMap = /* @__PURE__ */ new WeakMap();
|
|
130
|
+
const iframeListenerMap = /* @__PURE__ */ new WeakMap();
|
|
129
131
|
let windowCounter = 0;
|
|
130
132
|
const $reflectApply = Reflect.apply;
|
|
131
133
|
const getWindowId = (win) => {
|
|
132
|
-
if (!
|
|
134
|
+
if (!windowIdMap.has(win)) {
|
|
133
135
|
const id = win === rootWindow ? "main" : `iframe-${++windowCounter}`;
|
|
134
|
-
|
|
136
|
+
windowIdMap.set(win, id);
|
|
135
137
|
}
|
|
136
|
-
return
|
|
138
|
+
return windowIdMap.get(win);
|
|
137
139
|
};
|
|
138
140
|
const setupToStringHook = (win) => {
|
|
139
141
|
hook.method(win.Function.prototype, "toString", (target, args, thisArg) => {
|
|
@@ -143,54 +145,93 @@ var StealthHook = (() => {
|
|
|
143
145
|
return $reflectApply(target, thisArg, args);
|
|
144
146
|
});
|
|
145
147
|
};
|
|
146
|
-
|
|
147
|
-
|
|
148
|
+
const handleIframeNode = (node, applyCallback) => {
|
|
149
|
+
const element = node;
|
|
150
|
+
const tryHook = () => {
|
|
151
|
+
try {
|
|
152
|
+
const childWin = element.contentWindow;
|
|
153
|
+
if (childWin) {
|
|
154
|
+
applyCallback(childWin);
|
|
155
|
+
setupObserver(childWin, childWin.document, applyCallback);
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
tryHook();
|
|
161
|
+
const oldListener = iframeListenerMap.get(element);
|
|
162
|
+
if (oldListener)
|
|
163
|
+
element.removeEventListener("load", oldListener);
|
|
164
|
+
element.addEventListener("load", tryHook);
|
|
165
|
+
iframeListenerMap.set(element, tryHook);
|
|
166
|
+
};
|
|
167
|
+
function setupObserver(win, targetNode, applyCallback) {
|
|
168
|
+
if (interceptorSetupMap.has(targetNode))
|
|
148
169
|
return;
|
|
149
|
-
interceptorSetupMap.add(
|
|
170
|
+
interceptorSetupMap.add(targetNode);
|
|
150
171
|
const logger = createLogger(getWindowId(win));
|
|
151
172
|
try {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
applyCallback(childWin);
|
|
165
|
-
setupIframeInterceptor(childWin, applyCallback);
|
|
173
|
+
const observer = new win.MutationObserver((mutations) => {
|
|
174
|
+
for (const mutation of mutations) {
|
|
175
|
+
if (mutation.type === "childList") {
|
|
176
|
+
mutation.addedNodes.forEach((node) => {
|
|
177
|
+
const tagName = node.tagName;
|
|
178
|
+
if (tagName === "IFRAME" || tagName === "FRAME" || tagName === "OBJECT") {
|
|
179
|
+
handleIframeNode(node, applyCallback);
|
|
180
|
+
} else if (node instanceof win.HTMLElement) {
|
|
181
|
+
if (node.childElementCount > 0) {
|
|
182
|
+
const frames = node.querySelectorAll("iframe, frame, object");
|
|
183
|
+
frames.forEach((frame) => handleIframeNode(frame, applyCallback));
|
|
184
|
+
}
|
|
166
185
|
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
186
|
+
});
|
|
187
|
+
} else if (mutation.type === "attributes") {
|
|
188
|
+
if (mutation.target instanceof win.Element) {
|
|
189
|
+
logger.log(`\u{1F504} Detected attribute change on <${mutation.target.tagName}>`, mutation.attributeName);
|
|
190
|
+
handleIframeNode(mutation.target, applyCallback);
|
|
191
|
+
}
|
|
171
192
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
observer.observe(targetNode, {
|
|
196
|
+
childList: true,
|
|
197
|
+
subtree: true,
|
|
198
|
+
attributes: true,
|
|
199
|
+
attributeFilter: ["src", "srcdoc", "data"]
|
|
200
|
+
});
|
|
178
201
|
} catch (e) {
|
|
179
|
-
logger.warn("\u274C Failed to setup
|
|
202
|
+
logger.warn("\u274C Failed to setup MutationObserver", e);
|
|
180
203
|
}
|
|
181
204
|
}
|
|
205
|
+
const setupShadowDomHook = (win, applyCallback) => {
|
|
206
|
+
try {
|
|
207
|
+
if (win.Element.prototype.attachShadow) {
|
|
208
|
+
hook.method(win.Element.prototype, "attachShadow", (original, args, scope) => {
|
|
209
|
+
const shadowRoot = $reflectApply(original, scope, args);
|
|
210
|
+
if (shadowRoot) {
|
|
211
|
+
setupObserver(win, shadowRoot, applyCallback);
|
|
212
|
+
}
|
|
213
|
+
return shadowRoot;
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
};
|
|
182
219
|
const applyToWindow = (win) => {
|
|
183
|
-
|
|
220
|
+
try {
|
|
221
|
+
const proto = win.Function.prototype;
|
|
222
|
+
if (hookedProtoSet.has(proto))
|
|
223
|
+
return;
|
|
224
|
+
hookedProtoSet.add(proto);
|
|
225
|
+
} catch {
|
|
184
226
|
return;
|
|
227
|
+
}
|
|
185
228
|
const logger = createLogger(getWindowId(win));
|
|
186
229
|
setupToStringHook(win);
|
|
230
|
+
setupShadowDomHook(win, applyToWindow);
|
|
187
231
|
const utils = {
|
|
188
232
|
hookMethod: (obj, fnName, handler) => {
|
|
189
233
|
hook.method(obj, fnName, handler);
|
|
190
234
|
},
|
|
191
|
-
hookBindThisMethod(obj, fnName, handler) {
|
|
192
|
-
hook.bindThisMethod(obj, fnName, handler);
|
|
193
|
-
},
|
|
194
235
|
getNativeFunction: hook.getNativeFunction,
|
|
195
236
|
getNativeString: hook.getNativeString,
|
|
196
237
|
getCurrentWindowId: () => getWindowId(win)
|
|
@@ -203,7 +244,7 @@ var StealthHook = (() => {
|
|
|
203
244
|
}
|
|
204
245
|
};
|
|
205
246
|
applyToWindow(rootWindow);
|
|
206
|
-
|
|
247
|
+
setupObserver(rootWindow, rootWindow.document, applyToWindow);
|
|
207
248
|
}
|
|
208
249
|
return __toCommonJS(index_exports);
|
|
209
250
|
})();
|
package/dist/index.js
CHANGED
|
@@ -98,16 +98,18 @@ var createLogger = /* @__PURE__ */ (() => {
|
|
|
98
98
|
|
|
99
99
|
// src/index.ts
|
|
100
100
|
function hookScope(callback, rootWindow) {
|
|
101
|
+
const hookedProtoSet = /* @__PURE__ */ new WeakSet();
|
|
101
102
|
const interceptorSetupMap = /* @__PURE__ */ new WeakSet();
|
|
102
|
-
const
|
|
103
|
+
const windowIdMap = /* @__PURE__ */ new WeakMap();
|
|
104
|
+
const iframeListenerMap = /* @__PURE__ */ new WeakMap();
|
|
103
105
|
let windowCounter = 0;
|
|
104
106
|
const $reflectApply = Reflect.apply;
|
|
105
107
|
const getWindowId = (win) => {
|
|
106
|
-
if (!
|
|
108
|
+
if (!windowIdMap.has(win)) {
|
|
107
109
|
const id = win === rootWindow ? "main" : `iframe-${++windowCounter}`;
|
|
108
|
-
|
|
110
|
+
windowIdMap.set(win, id);
|
|
109
111
|
}
|
|
110
|
-
return
|
|
112
|
+
return windowIdMap.get(win);
|
|
111
113
|
};
|
|
112
114
|
const setupToStringHook = (win) => {
|
|
113
115
|
hook.method(win.Function.prototype, "toString", (target, args, thisArg) => {
|
|
@@ -117,54 +119,93 @@ function hookScope(callback, rootWindow) {
|
|
|
117
119
|
return $reflectApply(target, thisArg, args);
|
|
118
120
|
});
|
|
119
121
|
};
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
const handleIframeNode = (node, applyCallback) => {
|
|
123
|
+
const element = node;
|
|
124
|
+
const tryHook = () => {
|
|
125
|
+
try {
|
|
126
|
+
const childWin = element.contentWindow;
|
|
127
|
+
if (childWin) {
|
|
128
|
+
applyCallback(childWin);
|
|
129
|
+
setupObserver(childWin, childWin.document, applyCallback);
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
tryHook();
|
|
135
|
+
const oldListener = iframeListenerMap.get(element);
|
|
136
|
+
if (oldListener)
|
|
137
|
+
element.removeEventListener("load", oldListener);
|
|
138
|
+
element.addEventListener("load", tryHook);
|
|
139
|
+
iframeListenerMap.set(element, tryHook);
|
|
140
|
+
};
|
|
141
|
+
function setupObserver(win, targetNode, applyCallback) {
|
|
142
|
+
if (interceptorSetupMap.has(targetNode))
|
|
122
143
|
return;
|
|
123
|
-
interceptorSetupMap.add(
|
|
144
|
+
interceptorSetupMap.add(targetNode);
|
|
124
145
|
const logger = createLogger(getWindowId(win));
|
|
125
146
|
try {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
applyCallback(childWin);
|
|
139
|
-
setupIframeInterceptor(childWin, applyCallback);
|
|
147
|
+
const observer = new win.MutationObserver((mutations) => {
|
|
148
|
+
for (const mutation of mutations) {
|
|
149
|
+
if (mutation.type === "childList") {
|
|
150
|
+
mutation.addedNodes.forEach((node) => {
|
|
151
|
+
const tagName = node.tagName;
|
|
152
|
+
if (tagName === "IFRAME" || tagName === "FRAME" || tagName === "OBJECT") {
|
|
153
|
+
handleIframeNode(node, applyCallback);
|
|
154
|
+
} else if (node instanceof win.HTMLElement) {
|
|
155
|
+
if (node.childElementCount > 0) {
|
|
156
|
+
const frames = node.querySelectorAll("iframe, frame, object");
|
|
157
|
+
frames.forEach((frame) => handleIframeNode(frame, applyCallback));
|
|
158
|
+
}
|
|
140
159
|
}
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
160
|
+
});
|
|
161
|
+
} else if (mutation.type === "attributes") {
|
|
162
|
+
if (mutation.target instanceof win.Element) {
|
|
163
|
+
logger.log(`\u{1F504} Detected attribute change on <${mutation.target.tagName}>`, mutation.attributeName);
|
|
164
|
+
handleIframeNode(mutation.target, applyCallback);
|
|
165
|
+
}
|
|
145
166
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
observer.observe(targetNode, {
|
|
170
|
+
childList: true,
|
|
171
|
+
subtree: true,
|
|
172
|
+
attributes: true,
|
|
173
|
+
attributeFilter: ["src", "srcdoc", "data"]
|
|
174
|
+
});
|
|
152
175
|
} catch (e) {
|
|
153
|
-
logger.warn("\u274C Failed to setup
|
|
176
|
+
logger.warn("\u274C Failed to setup MutationObserver", e);
|
|
154
177
|
}
|
|
155
178
|
}
|
|
179
|
+
const setupShadowDomHook = (win, applyCallback) => {
|
|
180
|
+
try {
|
|
181
|
+
if (win.Element.prototype.attachShadow) {
|
|
182
|
+
hook.method(win.Element.prototype, "attachShadow", (original, args, scope) => {
|
|
183
|
+
const shadowRoot = $reflectApply(original, scope, args);
|
|
184
|
+
if (shadowRoot) {
|
|
185
|
+
setupObserver(win, shadowRoot, applyCallback);
|
|
186
|
+
}
|
|
187
|
+
return shadowRoot;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
};
|
|
156
193
|
const applyToWindow = (win) => {
|
|
157
|
-
|
|
194
|
+
try {
|
|
195
|
+
const proto = win.Function.prototype;
|
|
196
|
+
if (hookedProtoSet.has(proto))
|
|
197
|
+
return;
|
|
198
|
+
hookedProtoSet.add(proto);
|
|
199
|
+
} catch {
|
|
158
200
|
return;
|
|
201
|
+
}
|
|
159
202
|
const logger = createLogger(getWindowId(win));
|
|
160
203
|
setupToStringHook(win);
|
|
204
|
+
setupShadowDomHook(win, applyToWindow);
|
|
161
205
|
const utils = {
|
|
162
206
|
hookMethod: (obj, fnName, handler) => {
|
|
163
207
|
hook.method(obj, fnName, handler);
|
|
164
208
|
},
|
|
165
|
-
hookBindThisMethod(obj, fnName, handler) {
|
|
166
|
-
hook.bindThisMethod(obj, fnName, handler);
|
|
167
|
-
},
|
|
168
209
|
getNativeFunction: hook.getNativeFunction,
|
|
169
210
|
getNativeString: hook.getNativeString,
|
|
170
211
|
getCurrentWindowId: () => getWindowId(win)
|
|
@@ -177,7 +218,7 @@ function hookScope(callback, rootWindow) {
|
|
|
177
218
|
}
|
|
178
219
|
};
|
|
179
220
|
applyToWindow(rootWindow);
|
|
180
|
-
|
|
221
|
+
setupObserver(rootWindow, rootWindow.document, applyToWindow);
|
|
181
222
|
}
|
|
182
223
|
export {
|
|
183
224
|
hookScope
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wulu007/stealth-hook",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.2",
|
|
5
|
+
"packageManager": "pnpm@10.26.1",
|
|
5
6
|
"description": "A stealth JavaScript hook library with automatic iframe detection and function toString spoofing.",
|
|
6
7
|
"author": "wulu007",
|
|
7
8
|
"license": "MIT",
|
|
@@ -33,10 +34,19 @@
|
|
|
33
34
|
"README.md",
|
|
34
35
|
"dist"
|
|
35
36
|
],
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup",
|
|
39
|
+
"dev": "tsup --watch",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"lint": "eslint .",
|
|
42
|
+
"lint:fix": "eslint . --fix",
|
|
43
|
+
"prepare": "husky install"
|
|
44
|
+
},
|
|
36
45
|
"devDependencies": {
|
|
37
46
|
"@antfu/eslint-config": "^7.1.0",
|
|
38
47
|
"@types/node": "^25.0.9",
|
|
39
48
|
"eslint": "^9.39.2",
|
|
49
|
+
"husky": "^9.1.7",
|
|
40
50
|
"tsup": "^8.5.1",
|
|
41
51
|
"tsx": "^4.21.0",
|
|
42
52
|
"typescript": "^5.9.3"
|
|
@@ -45,9 +55,7 @@
|
|
|
45
55
|
"access": "public",
|
|
46
56
|
"registry": "https://registry.npmjs.org/"
|
|
47
57
|
},
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"dev": "tsup --watch",
|
|
51
|
-
"typecheck": "tsc --noEmit"
|
|
58
|
+
"lint-staged": {
|
|
59
|
+
"*.{js,ts}": "eslint --fix"
|
|
52
60
|
}
|
|
53
|
-
}
|
|
61
|
+
}
|