@wulu007/stealth-hook 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 wulu007
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+
2
+ # StealthHook
3
+
4
+ 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
+
6
+ [![npm version](https://img.shields.io/npm/v/stealth-hook)](https://www.npmjs.com/package/stealth-hook)
7
+ [![MIT license](https://img.shields.io/npm/l/stealth-hook)](https://github.com/wulu007/StealthHook/blob/main/LICENSE)
8
+
9
+ ## Features
10
+
11
+ - 🎯 **Stealth Hooking** - Modify functions while passing `toString()` checks
12
+ - 🔄 **Auto iframe Detection** - Automatically detect and hook functions in iframes
13
+ - 🛡️ **Type Safe** - Full TypeScript support
14
+
15
+ ## Installation
16
+
17
+ ### npm
18
+
19
+ ```bash
20
+ npm install stealth-hook
21
+ ```
22
+
23
+ ### pnpm
24
+
25
+ ```bash
26
+ pnpm add stealth-hook
27
+ ```
28
+
29
+ ### In Userscripts
30
+
31
+ ```javascript
32
+ // @require https://unpkg.com/stealth-hook@latest/dist/index.js
33
+ // @grant unsafeWindow
34
+ // @run-at document-start
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Basic Example
40
+
41
+ ```javascript
42
+ import { hookScope } from 'stealth-hook'
43
+
44
+ hookScope(({ hookMethod }, win) => {
45
+ // Note: use the win parameter
46
+ hookMethod(win.console, 'log', (target, args, thisArg) => {
47
+ console.log('[Hooked]', ...args)
48
+ return target(...args)
49
+ })
50
+ }, rootWindow)
51
+ ```
52
+
53
+ ### Userscript Example
54
+
55
+ ```javascript
56
+ // ==UserScript==
57
+ // @name StealthHook Example
58
+ // @namespace https://example.com/
59
+ // @version 0.1.0
60
+ // @description Hook Example
61
+ // @author You
62
+ // @match *://*/*
63
+ // @require https://unpkg.com/stealth-hook@latest/dist/index.js
64
+ // @grant unsafeWindow
65
+ // @run-at document-start
66
+ // ==/UserScript==
67
+
68
+ (function () {
69
+ StealthHook.hookScope(({ hookMethod }, win) => {
70
+ // Note: use the win parameter
71
+ hookMethod(win.console, 'log', (target, args, thisArg) => {
72
+ console.log('[Custom Log]', ...args)
73
+ return target(...args)
74
+ })
75
+
76
+ hookMethod(win.console, 'table', (target, args, thisArg) => {
77
+ console.log('[Custom Table]', args)
78
+ return target(...args)
79
+ })
80
+ }, unsafeWindow)
81
+ })()
82
+ ```
83
+
84
+ #### Parameters
85
+
86
+ | Parameter | Type | Description |
87
+ | --------- | ------------ | ---------------------------------------------- |
88
+ | `target` | Function | Original function with `this` auto-bound |
89
+ | `args` | Array | Arguments array of the original function |
90
+ | `thisArg` | this type | Current `this` context (caller) |
91
+
92
+ ## License
93
+
94
+ MIT © [wulu007](https://github.com/wulu007)
package/dist/index.cjs ADDED
@@ -0,0 +1,211 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ hookScope: () => hookScope
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/descriptor.ts
28
+ var getDescriptor = /* @__PURE__ */ (() => {
29
+ const $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
30
+ const $getPrototypeOf = Object.getPrototypeOf;
31
+ return function(obj, prop) {
32
+ let curr = obj;
33
+ while (curr) {
34
+ const desc = $getOwnPropertyDescriptor(curr, prop);
35
+ if (desc)
36
+ return desc;
37
+ curr = $getPrototypeOf(curr);
38
+ }
39
+ return void 0;
40
+ };
41
+ })();
42
+
43
+ // src/fake.ts
44
+ var createWrapper = /* @__PURE__ */ (() => {
45
+ const $ownKeys = Reflect.ownKeys;
46
+ const $defineProperty = Object.defineProperty;
47
+ return function(handler, fn) {
48
+ const wrapper = {
49
+ [fn.name](...args) {
50
+ return handler(fn, args, this);
51
+ }
52
+ }[fn.name];
53
+ const keys = $ownKeys(fn);
54
+ for (const key of keys) {
55
+ if (key === "arguments" || key === "caller")
56
+ continue;
57
+ const desc = getDescriptor(fn, key);
58
+ if (desc) {
59
+ $defineProperty(wrapper, key, desc);
60
+ }
61
+ }
62
+ return wrapper;
63
+ };
64
+ })();
65
+
66
+ // src/hook.ts
67
+ var hook = (() => {
68
+ const nativeStrMap = /* @__PURE__ */ new WeakMap();
69
+ const nativeFnMap = /* @__PURE__ */ new WeakMap();
70
+ const $toString = Function.prototype.toString;
71
+ const $defineProperty = Object.defineProperty;
72
+ const $reflectApply = Reflect.apply;
73
+ return {
74
+ method(obj, fnName, handler) {
75
+ const original = obj[fnName];
76
+ const desc = getDescriptor(obj, fnName);
77
+ if (!desc || !original || typeof original !== "function")
78
+ return;
79
+ const wrapper = createWrapper(handler, original);
80
+ nativeStrMap.set(wrapper, $toString.call(original));
81
+ nativeFnMap.set(wrapper, original);
82
+ $defineProperty(obj, fnName, { ...desc, value: wrapper });
83
+ },
84
+ bindThisMethod(obj, fnName, handler) {
85
+ this.method(obj, fnName, (target, args, thisArg) => {
86
+ handler(() => $reflectApply(target, thisArg, args), args, thisArg);
87
+ });
88
+ },
89
+ constructor(obj, fnName, handler) {
90
+ const original = obj[fnName];
91
+ const desc = getDescriptor(obj, fnName);
92
+ if (!desc || !original || typeof original !== "function")
93
+ return;
94
+ const wrapper = createWrapper(handler, original);
95
+ if (wrapper.prototype && "constructor" in wrapper.prototype) {
96
+ wrapper.prototype.constructor = wrapper;
97
+ }
98
+ nativeStrMap.set(wrapper, $toString.call(original));
99
+ nativeFnMap.set(wrapper, original);
100
+ $defineProperty(obj, fnName, { ...desc, value: wrapper });
101
+ },
102
+ getNativeString(fn) {
103
+ if (nativeStrMap.has(fn))
104
+ return nativeStrMap.get(fn);
105
+ return $reflectApply($toString, fn, []);
106
+ },
107
+ getNativeFunction(hookedFn) {
108
+ return nativeFnMap.get(hookedFn);
109
+ }
110
+ };
111
+ })();
112
+
113
+ // src/logger.ts
114
+ var createLogger = /* @__PURE__ */ (() => {
115
+ const $log = console.log;
116
+ const $warn = console.warn;
117
+ const $error = console.error;
118
+ return (prefix) => ({
119
+ log: (...args) => $log(`[${prefix}]`, ...args),
120
+ warn: (...args) => $warn(`[${prefix}]`, ...args),
121
+ error: (...args) => $error(`[${prefix}]`, ...args)
122
+ });
123
+ })();
124
+
125
+ // src/index.ts
126
+ function hookScope(callback, rootWindow) {
127
+ const interceptorSetupMap = /* @__PURE__ */ new WeakSet();
128
+ const hookedWindows = /* @__PURE__ */ new WeakMap();
129
+ let windowCounter = 0;
130
+ const $reflectApply = Reflect.apply;
131
+ const getWindowId = (win) => {
132
+ if (!hookedWindows.has(win)) {
133
+ const id = win === rootWindow ? "main" : `iframe-${++windowCounter}`;
134
+ hookedWindows.set(win, id);
135
+ }
136
+ return hookedWindows.get(win);
137
+ };
138
+ const setupToStringHook = (win) => {
139
+ hook.method(win.Function.prototype, "toString", (target, args, thisArg) => {
140
+ const fakeString = hook.getNativeString(thisArg);
141
+ if (fakeString)
142
+ return fakeString;
143
+ return $reflectApply(target, thisArg, args);
144
+ });
145
+ };
146
+ function setupIframeInterceptor(win, applyCallback) {
147
+ if (interceptorSetupMap.has(win))
148
+ return;
149
+ interceptorSetupMap.add(win);
150
+ const logger = createLogger(getWindowId(win));
151
+ try {
152
+ const NodeProto = win.Node.prototype;
153
+ const hookInsertMethod = (methodName) => {
154
+ hook.method(NodeProto, methodName, (target, args, thisArg) => {
155
+ const result = $reflectApply(target, thisArg, args);
156
+ try {
157
+ const node = args[0];
158
+ if (!node || node.tagName !== "IFRAME")
159
+ return result;
160
+ const iframe = node;
161
+ const handleIframe = () => {
162
+ const childWin = iframe.contentWindow;
163
+ if (childWin) {
164
+ applyCallback(childWin);
165
+ setupIframeInterceptor(childWin, applyCallback);
166
+ }
167
+ };
168
+ handleIframe();
169
+ } catch (e) {
170
+ logger.warn(`Error in ${String(methodName)} hook:`, e);
171
+ }
172
+ return result;
173
+ });
174
+ };
175
+ hookInsertMethod("appendChild");
176
+ hookInsertMethod("insertBefore");
177
+ hookInsertMethod("replaceChild");
178
+ } catch (e) {
179
+ logger.warn("\u274C Failed to setup iframe interceptor", e);
180
+ }
181
+ }
182
+ const applyToWindow = (win) => {
183
+ if (hookedWindows.has(win))
184
+ return;
185
+ const logger = createLogger(getWindowId(win));
186
+ setupToStringHook(win);
187
+ const utils = {
188
+ hookMethod: (obj, fnName, handler) => {
189
+ hook.method(obj, fnName, handler);
190
+ },
191
+ hookBindThisMethod(obj, fnName, handler) {
192
+ hook.bindThisMethod(obj, fnName, handler);
193
+ },
194
+ getNativeFunction: hook.getNativeFunction,
195
+ getNativeString: hook.getNativeString,
196
+ getCurrentWindowId: () => getWindowId(win)
197
+ };
198
+ try {
199
+ callback(utils, win);
200
+ logger.log("\u2705 StealthHook applied");
201
+ } catch (e) {
202
+ logger.warn("User callback failed:", e);
203
+ }
204
+ };
205
+ applyToWindow(rootWindow);
206
+ setupIframeInterceptor(rootWindow, applyToWindow);
207
+ }
208
+ // Annotate the CommonJS export names for ESM import in node:
209
+ 0 && (module.exports = {
210
+ hookScope
211
+ });
@@ -0,0 +1,39 @@
1
+ type AnyFunction = (...args: any[]) => any;
2
+ interface HookHandler<T extends AnyFunction = AnyFunction> {
3
+ /**
4
+ * @param target 原函数
5
+ * @param args 原函数的参数数组
6
+ * @param thisArg 当前的 this 上下文 (调用者)
7
+ */
8
+ (target: T, args: Parameters<T>, thisArg: ThisType<T>): ReturnType<T>;
9
+ }
10
+
11
+ declare const hook: {
12
+ method<T extends object>(obj: T, fnName: keyof T, handler: HookHandler): void;
13
+ bindThisMethod<T extends object>(obj: T, fnName: keyof T, handler: HookHandler): void;
14
+ constructor<T extends AnyFunction>(obj: T, fnName: keyof T, handler: HookHandler): void;
15
+ getNativeString(fn: AnyFunction): string | undefined;
16
+ getNativeFunction<T extends AnyFunction>(hookedFn: T): T | undefined;
17
+ };
18
+
19
+ declare global {
20
+ interface Window {
21
+ Node: typeof Node;
22
+ Function: typeof Function;
23
+ }
24
+ }
25
+ interface HookUtils {
26
+ 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
+ getCurrentWindowId: () => string | number;
29
+ getNativeFunction: typeof hook.getNativeFunction;
30
+ getNativeString: typeof hook.getNativeString;
31
+ }
32
+ /**
33
+ * 开启 Hook 作用域
34
+ * @param callback 在此处定义你的 Hook 逻辑
35
+ * @param rootWindow 初始目标窗口
36
+ */
37
+ declare function hookScope(callback: (utils: HookUtils, win: Window) => void, rootWindow: Window): void;
38
+
39
+ export { type HookUtils, hookScope };
@@ -0,0 +1,39 @@
1
+ type AnyFunction = (...args: any[]) => any;
2
+ interface HookHandler<T extends AnyFunction = AnyFunction> {
3
+ /**
4
+ * @param target 原函数
5
+ * @param args 原函数的参数数组
6
+ * @param thisArg 当前的 this 上下文 (调用者)
7
+ */
8
+ (target: T, args: Parameters<T>, thisArg: ThisType<T>): ReturnType<T>;
9
+ }
10
+
11
+ declare const hook: {
12
+ method<T extends object>(obj: T, fnName: keyof T, handler: HookHandler): void;
13
+ bindThisMethod<T extends object>(obj: T, fnName: keyof T, handler: HookHandler): void;
14
+ constructor<T extends AnyFunction>(obj: T, fnName: keyof T, handler: HookHandler): void;
15
+ getNativeString(fn: AnyFunction): string | undefined;
16
+ getNativeFunction<T extends AnyFunction>(hookedFn: T): T | undefined;
17
+ };
18
+
19
+ declare global {
20
+ interface Window {
21
+ Node: typeof Node;
22
+ Function: typeof Function;
23
+ }
24
+ }
25
+ interface HookUtils {
26
+ 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
+ getCurrentWindowId: () => string | number;
29
+ getNativeFunction: typeof hook.getNativeFunction;
30
+ getNativeString: typeof hook.getNativeString;
31
+ }
32
+ /**
33
+ * 开启 Hook 作用域
34
+ * @param callback 在此处定义你的 Hook 逻辑
35
+ * @param rootWindow 初始目标窗口
36
+ */
37
+ declare function hookScope(callback: (utils: HookUtils, win: Window) => void, rootWindow: Window): void;
38
+
39
+ export { type HookUtils, hookScope };
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var StealthHook = (() => {
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ hookScope: () => hookScope
25
+ });
26
+
27
+ // src/descriptor.ts
28
+ var getDescriptor = /* @__PURE__ */ (() => {
29
+ const $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
30
+ const $getPrototypeOf = Object.getPrototypeOf;
31
+ return function(obj, prop) {
32
+ let curr = obj;
33
+ while (curr) {
34
+ const desc = $getOwnPropertyDescriptor(curr, prop);
35
+ if (desc)
36
+ return desc;
37
+ curr = $getPrototypeOf(curr);
38
+ }
39
+ return void 0;
40
+ };
41
+ })();
42
+
43
+ // src/fake.ts
44
+ var createWrapper = /* @__PURE__ */ (() => {
45
+ const $ownKeys = Reflect.ownKeys;
46
+ const $defineProperty = Object.defineProperty;
47
+ return function(handler, fn) {
48
+ const wrapper = {
49
+ [fn.name](...args) {
50
+ return handler(fn, args, this);
51
+ }
52
+ }[fn.name];
53
+ const keys = $ownKeys(fn);
54
+ for (const key of keys) {
55
+ if (key === "arguments" || key === "caller")
56
+ continue;
57
+ const desc = getDescriptor(fn, key);
58
+ if (desc) {
59
+ $defineProperty(wrapper, key, desc);
60
+ }
61
+ }
62
+ return wrapper;
63
+ };
64
+ })();
65
+
66
+ // src/hook.ts
67
+ var hook = (() => {
68
+ const nativeStrMap = /* @__PURE__ */ new WeakMap();
69
+ const nativeFnMap = /* @__PURE__ */ new WeakMap();
70
+ const $toString = Function.prototype.toString;
71
+ const $defineProperty = Object.defineProperty;
72
+ const $reflectApply = Reflect.apply;
73
+ return {
74
+ method(obj, fnName, handler) {
75
+ const original = obj[fnName];
76
+ const desc = getDescriptor(obj, fnName);
77
+ if (!desc || !original || typeof original !== "function")
78
+ return;
79
+ const wrapper = createWrapper(handler, original);
80
+ nativeStrMap.set(wrapper, $toString.call(original));
81
+ nativeFnMap.set(wrapper, original);
82
+ $defineProperty(obj, fnName, { ...desc, value: wrapper });
83
+ },
84
+ bindThisMethod(obj, fnName, handler) {
85
+ this.method(obj, fnName, (target, args, thisArg) => {
86
+ handler(() => $reflectApply(target, thisArg, args), args, thisArg);
87
+ });
88
+ },
89
+ constructor(obj, fnName, handler) {
90
+ const original = obj[fnName];
91
+ const desc = getDescriptor(obj, fnName);
92
+ if (!desc || !original || typeof original !== "function")
93
+ return;
94
+ const wrapper = createWrapper(handler, original);
95
+ if (wrapper.prototype && "constructor" in wrapper.prototype) {
96
+ wrapper.prototype.constructor = wrapper;
97
+ }
98
+ nativeStrMap.set(wrapper, $toString.call(original));
99
+ nativeFnMap.set(wrapper, original);
100
+ $defineProperty(obj, fnName, { ...desc, value: wrapper });
101
+ },
102
+ getNativeString(fn) {
103
+ if (nativeStrMap.has(fn))
104
+ return nativeStrMap.get(fn);
105
+ return $reflectApply($toString, fn, []);
106
+ },
107
+ getNativeFunction(hookedFn) {
108
+ return nativeFnMap.get(hookedFn);
109
+ }
110
+ };
111
+ })();
112
+
113
+ // src/logger.ts
114
+ var createLogger = /* @__PURE__ */ (() => {
115
+ const $log = console.log;
116
+ const $warn = console.warn;
117
+ const $error = console.error;
118
+ return (prefix) => ({
119
+ log: (...args) => $log(`[${prefix}]`, ...args),
120
+ warn: (...args) => $warn(`[${prefix}]`, ...args),
121
+ error: (...args) => $error(`[${prefix}]`, ...args)
122
+ });
123
+ })();
124
+
125
+ // src/index.ts
126
+ function hookScope(callback, rootWindow) {
127
+ const interceptorSetupMap = /* @__PURE__ */ new WeakSet();
128
+ const hookedWindows = /* @__PURE__ */ new WeakMap();
129
+ let windowCounter = 0;
130
+ const $reflectApply = Reflect.apply;
131
+ const getWindowId = (win) => {
132
+ if (!hookedWindows.has(win)) {
133
+ const id = win === rootWindow ? "main" : `iframe-${++windowCounter}`;
134
+ hookedWindows.set(win, id);
135
+ }
136
+ return hookedWindows.get(win);
137
+ };
138
+ const setupToStringHook = (win) => {
139
+ hook.method(win.Function.prototype, "toString", (target, args, thisArg) => {
140
+ const fakeString = hook.getNativeString(thisArg);
141
+ if (fakeString)
142
+ return fakeString;
143
+ return $reflectApply(target, thisArg, args);
144
+ });
145
+ };
146
+ function setupIframeInterceptor(win, applyCallback) {
147
+ if (interceptorSetupMap.has(win))
148
+ return;
149
+ interceptorSetupMap.add(win);
150
+ const logger = createLogger(getWindowId(win));
151
+ try {
152
+ const NodeProto = win.Node.prototype;
153
+ const hookInsertMethod = (methodName) => {
154
+ hook.method(NodeProto, methodName, (target, args, thisArg) => {
155
+ const result = $reflectApply(target, thisArg, args);
156
+ try {
157
+ const node = args[0];
158
+ if (!node || node.tagName !== "IFRAME")
159
+ return result;
160
+ const iframe = node;
161
+ const handleIframe = () => {
162
+ const childWin = iframe.contentWindow;
163
+ if (childWin) {
164
+ applyCallback(childWin);
165
+ setupIframeInterceptor(childWin, applyCallback);
166
+ }
167
+ };
168
+ handleIframe();
169
+ } catch (e) {
170
+ logger.warn(`Error in ${String(methodName)} hook:`, e);
171
+ }
172
+ return result;
173
+ });
174
+ };
175
+ hookInsertMethod("appendChild");
176
+ hookInsertMethod("insertBefore");
177
+ hookInsertMethod("replaceChild");
178
+ } catch (e) {
179
+ logger.warn("\u274C Failed to setup iframe interceptor", e);
180
+ }
181
+ }
182
+ const applyToWindow = (win) => {
183
+ if (hookedWindows.has(win))
184
+ return;
185
+ const logger = createLogger(getWindowId(win));
186
+ setupToStringHook(win);
187
+ const utils = {
188
+ hookMethod: (obj, fnName, handler) => {
189
+ hook.method(obj, fnName, handler);
190
+ },
191
+ hookBindThisMethod(obj, fnName, handler) {
192
+ hook.bindThisMethod(obj, fnName, handler);
193
+ },
194
+ getNativeFunction: hook.getNativeFunction,
195
+ getNativeString: hook.getNativeString,
196
+ getCurrentWindowId: () => getWindowId(win)
197
+ };
198
+ try {
199
+ callback(utils, win);
200
+ logger.log("\u2705 StealthHook applied");
201
+ } catch (e) {
202
+ logger.warn("User callback failed:", e);
203
+ }
204
+ };
205
+ applyToWindow(rootWindow);
206
+ setupIframeInterceptor(rootWindow, applyToWindow);
207
+ }
208
+ return __toCommonJS(index_exports);
209
+ })();
package/dist/index.js ADDED
@@ -0,0 +1,184 @@
1
+ // src/descriptor.ts
2
+ var getDescriptor = /* @__PURE__ */ (() => {
3
+ const $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
4
+ const $getPrototypeOf = Object.getPrototypeOf;
5
+ return function(obj, prop) {
6
+ let curr = obj;
7
+ while (curr) {
8
+ const desc = $getOwnPropertyDescriptor(curr, prop);
9
+ if (desc)
10
+ return desc;
11
+ curr = $getPrototypeOf(curr);
12
+ }
13
+ return void 0;
14
+ };
15
+ })();
16
+
17
+ // src/fake.ts
18
+ var createWrapper = /* @__PURE__ */ (() => {
19
+ const $ownKeys = Reflect.ownKeys;
20
+ const $defineProperty = Object.defineProperty;
21
+ return function(handler, fn) {
22
+ const wrapper = {
23
+ [fn.name](...args) {
24
+ return handler(fn, args, this);
25
+ }
26
+ }[fn.name];
27
+ const keys = $ownKeys(fn);
28
+ for (const key of keys) {
29
+ if (key === "arguments" || key === "caller")
30
+ continue;
31
+ const desc = getDescriptor(fn, key);
32
+ if (desc) {
33
+ $defineProperty(wrapper, key, desc);
34
+ }
35
+ }
36
+ return wrapper;
37
+ };
38
+ })();
39
+
40
+ // src/hook.ts
41
+ var hook = (() => {
42
+ const nativeStrMap = /* @__PURE__ */ new WeakMap();
43
+ const nativeFnMap = /* @__PURE__ */ new WeakMap();
44
+ const $toString = Function.prototype.toString;
45
+ const $defineProperty = Object.defineProperty;
46
+ const $reflectApply = Reflect.apply;
47
+ return {
48
+ method(obj, fnName, handler) {
49
+ const original = obj[fnName];
50
+ const desc = getDescriptor(obj, fnName);
51
+ if (!desc || !original || typeof original !== "function")
52
+ return;
53
+ const wrapper = createWrapper(handler, original);
54
+ nativeStrMap.set(wrapper, $toString.call(original));
55
+ nativeFnMap.set(wrapper, original);
56
+ $defineProperty(obj, fnName, { ...desc, value: wrapper });
57
+ },
58
+ bindThisMethod(obj, fnName, handler) {
59
+ this.method(obj, fnName, (target, args, thisArg) => {
60
+ handler(() => $reflectApply(target, thisArg, args), args, thisArg);
61
+ });
62
+ },
63
+ constructor(obj, fnName, handler) {
64
+ const original = obj[fnName];
65
+ const desc = getDescriptor(obj, fnName);
66
+ if (!desc || !original || typeof original !== "function")
67
+ return;
68
+ const wrapper = createWrapper(handler, original);
69
+ if (wrapper.prototype && "constructor" in wrapper.prototype) {
70
+ wrapper.prototype.constructor = wrapper;
71
+ }
72
+ nativeStrMap.set(wrapper, $toString.call(original));
73
+ nativeFnMap.set(wrapper, original);
74
+ $defineProperty(obj, fnName, { ...desc, value: wrapper });
75
+ },
76
+ getNativeString(fn) {
77
+ if (nativeStrMap.has(fn))
78
+ return nativeStrMap.get(fn);
79
+ return $reflectApply($toString, fn, []);
80
+ },
81
+ getNativeFunction(hookedFn) {
82
+ return nativeFnMap.get(hookedFn);
83
+ }
84
+ };
85
+ })();
86
+
87
+ // src/logger.ts
88
+ var createLogger = /* @__PURE__ */ (() => {
89
+ const $log = console.log;
90
+ const $warn = console.warn;
91
+ const $error = console.error;
92
+ return (prefix) => ({
93
+ log: (...args) => $log(`[${prefix}]`, ...args),
94
+ warn: (...args) => $warn(`[${prefix}]`, ...args),
95
+ error: (...args) => $error(`[${prefix}]`, ...args)
96
+ });
97
+ })();
98
+
99
+ // src/index.ts
100
+ function hookScope(callback, rootWindow) {
101
+ const interceptorSetupMap = /* @__PURE__ */ new WeakSet();
102
+ const hookedWindows = /* @__PURE__ */ new WeakMap();
103
+ let windowCounter = 0;
104
+ const $reflectApply = Reflect.apply;
105
+ const getWindowId = (win) => {
106
+ if (!hookedWindows.has(win)) {
107
+ const id = win === rootWindow ? "main" : `iframe-${++windowCounter}`;
108
+ hookedWindows.set(win, id);
109
+ }
110
+ return hookedWindows.get(win);
111
+ };
112
+ const setupToStringHook = (win) => {
113
+ hook.method(win.Function.prototype, "toString", (target, args, thisArg) => {
114
+ const fakeString = hook.getNativeString(thisArg);
115
+ if (fakeString)
116
+ return fakeString;
117
+ return $reflectApply(target, thisArg, args);
118
+ });
119
+ };
120
+ function setupIframeInterceptor(win, applyCallback) {
121
+ if (interceptorSetupMap.has(win))
122
+ return;
123
+ interceptorSetupMap.add(win);
124
+ const logger = createLogger(getWindowId(win));
125
+ try {
126
+ const NodeProto = win.Node.prototype;
127
+ const hookInsertMethod = (methodName) => {
128
+ hook.method(NodeProto, methodName, (target, args, thisArg) => {
129
+ const result = $reflectApply(target, thisArg, args);
130
+ try {
131
+ const node = args[0];
132
+ if (!node || node.tagName !== "IFRAME")
133
+ return result;
134
+ const iframe = node;
135
+ const handleIframe = () => {
136
+ const childWin = iframe.contentWindow;
137
+ if (childWin) {
138
+ applyCallback(childWin);
139
+ setupIframeInterceptor(childWin, applyCallback);
140
+ }
141
+ };
142
+ handleIframe();
143
+ } catch (e) {
144
+ logger.warn(`Error in ${String(methodName)} hook:`, e);
145
+ }
146
+ return result;
147
+ });
148
+ };
149
+ hookInsertMethod("appendChild");
150
+ hookInsertMethod("insertBefore");
151
+ hookInsertMethod("replaceChild");
152
+ } catch (e) {
153
+ logger.warn("\u274C Failed to setup iframe interceptor", e);
154
+ }
155
+ }
156
+ const applyToWindow = (win) => {
157
+ if (hookedWindows.has(win))
158
+ return;
159
+ const logger = createLogger(getWindowId(win));
160
+ setupToStringHook(win);
161
+ const utils = {
162
+ hookMethod: (obj, fnName, handler) => {
163
+ hook.method(obj, fnName, handler);
164
+ },
165
+ hookBindThisMethod(obj, fnName, handler) {
166
+ hook.bindThisMethod(obj, fnName, handler);
167
+ },
168
+ getNativeFunction: hook.getNativeFunction,
169
+ getNativeString: hook.getNativeString,
170
+ getCurrentWindowId: () => getWindowId(win)
171
+ };
172
+ try {
173
+ callback(utils, win);
174
+ logger.log("\u2705 StealthHook applied");
175
+ } catch (e) {
176
+ logger.warn("User callback failed:", e);
177
+ }
178
+ };
179
+ applyToWindow(rootWindow);
180
+ setupIframeInterceptor(rootWindow, applyToWindow);
181
+ }
182
+ export {
183
+ hookScope
184
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@wulu007/stealth-hook",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "description": "A stealth JavaScript hook library with automatic iframe detection and function toString spoofing.",
6
+ "author": "wulu007",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/wulu007/stealth-hook",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/wulu007/stealth-hook.git"
12
+ },
13
+ "keywords": [
14
+ "hook",
15
+ "scriptcat",
16
+ "Tampermonkey",
17
+ "monkey patch",
18
+ "stealth"
19
+ ],
20
+ "exports": {
21
+ "import": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js"
24
+ },
25
+ "default": {
26
+ "types": "./dist/index.d.ts",
27
+ "require": "./dist/index.cjs"
28
+ }
29
+ },
30
+ "types": "./dist/index.d.ts",
31
+ "files": [
32
+ "LICENSE",
33
+ "README.md",
34
+ "dist"
35
+ ],
36
+ "devDependencies": {
37
+ "@antfu/eslint-config": "^7.1.0",
38
+ "@types/node": "^25.0.9",
39
+ "eslint": "^9.39.2",
40
+ "tsup": "^8.5.1",
41
+ "tsx": "^4.21.0",
42
+ "typescript": "^5.9.3"
43
+ },
44
+ "publishConfig": {
45
+ "access": "public",
46
+ "registry": "https://registry.npmjs.org/"
47
+ },
48
+ "scripts": {
49
+ "build": "tsup",
50
+ "dev": "tsup --watch",
51
+ "typecheck": "tsc --noEmit"
52
+ }
53
+ }