@hy-sde-org/dsh-browser 0.1.1-rc.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/LICENSE +37 -0
- package/README.md +33 -0
- package/cordis.patch.yml +21 -0
- package/dist/aria-bundle.d.ts +9 -0
- package/dist/aria-bundle.d.ts.map +1 -0
- package/dist/aria-bundle.js +16 -0
- package/dist/aria-bundle.js.map +1 -0
- package/dist/aria.d.ts +45 -0
- package/dist/aria.d.ts.map +1 -0
- package/dist/aria.js +79 -0
- package/dist/aria.js.map +1 -0
- package/dist/assets/background.js.txt +242 -0
- package/dist/assets/manifest.json.txt +14 -0
- package/dist/assets/options.html.txt +53 -0
- package/dist/assets/options.js.txt +23 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/invariant.d.ts +16 -0
- package/dist/invariant.d.ts.map +1 -0
- package/dist/invariant.js +22 -0
- package/dist/invariant.js.map +1 -0
- package/dist/relay/bridge.d.ts +36 -0
- package/dist/relay/bridge.d.ts.map +1 -0
- package/dist/relay/bridge.js +860 -0
- package/dist/relay/bridge.js.map +1 -0
- package/dist/relay/index.d.ts +16 -0
- package/dist/relay/index.d.ts.map +1 -0
- package/dist/relay/index.js +15 -0
- package/dist/relay/index.js.map +1 -0
- package/dist/relay/kind.d.ts +30 -0
- package/dist/relay/kind.d.ts.map +1 -0
- package/dist/relay/kind.js +33 -0
- package/dist/relay/kind.js.map +1 -0
- package/dist/relay/protocol.d.ts +101 -0
- package/dist/relay/protocol.d.ts.map +1 -0
- package/dist/relay/protocol.js +12 -0
- package/dist/relay/protocol.js.map +1 -0
- package/dist/relay/server.d.ts +44 -0
- package/dist/relay/server.d.ts.map +1 -0
- package/dist/relay/server.js +173 -0
- package/dist/relay/server.js.map +1 -0
- package/dist/service.d.ts +157 -0
- package/dist/service.d.ts.map +1 -0
- package/dist/service.js +291 -0
- package/dist/service.js.map +1 -0
- package/dist/stealth-scripts.d.ts +11 -0
- package/dist/stealth-scripts.d.ts.map +1 -0
- package/dist/stealth-scripts.js +2010 -0
- package/dist/stealth-scripts.js.map +1 -0
- package/dist/stealth.d.ts +62 -0
- package/dist/stealth.d.ts.map +1 -0
- package/dist/stealth.js +194 -0
- package/dist/stealth.js.map +1 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/examples/agent-preset/agent.cordis.yml +19 -0
- package/examples/agent-preset/preset.yml +3 -0
- package/package.json +99 -0
|
@@ -0,0 +1,2010 @@
|
|
|
1
|
+
const scripts = [
|
|
2
|
+
{ name: '00_stealth_tampering', source: `// Register native-looking source for common fingerprinted functions without
|
|
3
|
+
// adding own toString properties.
|
|
4
|
+
const patchedFns = [
|
|
5
|
+
[window.alert, "alert"],
|
|
6
|
+
[window.prompt, "prompt"],
|
|
7
|
+
[window.confirm, "confirm"],
|
|
8
|
+
[window.fetch, "fetch"],
|
|
9
|
+
[window.XMLHttpRequest, "XMLHttpRequest"],
|
|
10
|
+
[window.WebSocket, "WebSocket"],
|
|
11
|
+
[window.localStorage?.getItem, "getItem"],
|
|
12
|
+
[window.localStorage?.setItem, "setItem"],
|
|
13
|
+
[window.navigator.geolocation?.getCurrentPosition, "getCurrentPosition"],
|
|
14
|
+
[window.navigator.geolocation?.watchPosition, "watchPosition"],
|
|
15
|
+
[window.CanvasRenderingContext2D?.prototype.getImageData, "getImageData"],
|
|
16
|
+
[window.HTMLCanvasElement?.prototype.toDataURL, "toDataURL"],
|
|
17
|
+
[window.HTMLCanvasElement?.prototype.toBlob, "toBlob"],
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
for (const [fn, name] of patchedFns) {
|
|
21
|
+
patchToString(fn, name);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Patch Object.getOwnPropertyDescriptor to return native-looking accessor
|
|
25
|
+
// source through the shared Function.prototype.toString registry.
|
|
26
|
+
const patchedGetOwnPropertyDescriptor = function getOwnPropertyDescriptor(obj, prop) {
|
|
27
|
+
const descriptor = Reflect_apply(Object_getOwnPropertyDescriptor, this, [obj, prop]);
|
|
28
|
+
if (!descriptor) return descriptor;
|
|
29
|
+
|
|
30
|
+
if (descriptor.get && typeof descriptor.get === "function") {
|
|
31
|
+
patchToString(descriptor.get, "get " + String(prop));
|
|
32
|
+
}
|
|
33
|
+
if (descriptor.set && typeof descriptor.set === "function") {
|
|
34
|
+
patchToString(descriptor.set, "set " + String(prop));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return descriptor;
|
|
38
|
+
};
|
|
39
|
+
patchToString(patchedGetOwnPropertyDescriptor, "getOwnPropertyDescriptor");
|
|
40
|
+
Object_defineProperty(Object, "getOwnPropertyDescriptor", {
|
|
41
|
+
value: patchedGetOwnPropertyDescriptor,
|
|
42
|
+
writable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
enumerable: false,
|
|
45
|
+
});
|
|
46
|
+
` },
|
|
47
|
+
{ name: '01_stealth_activity', source: `{
|
|
48
|
+
const visibilityDescriptors = Object_getOwnPropertyDescriptors({
|
|
49
|
+
get hidden() {
|
|
50
|
+
return false;
|
|
51
|
+
},
|
|
52
|
+
get visibilityState() {
|
|
53
|
+
return "visible";
|
|
54
|
+
},
|
|
55
|
+
get webkitHidden() {
|
|
56
|
+
return false;
|
|
57
|
+
},
|
|
58
|
+
get webkitVisibilityState() {
|
|
59
|
+
return "visible";
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
for (const key of Object_keys(visibilityDescriptors)) {
|
|
63
|
+
const descriptor = visibilityDescriptors[key];
|
|
64
|
+
if (descriptor && typeof descriptor.get === "function") {
|
|
65
|
+
patchToString(descriptor.get, "get " + key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const focusDescriptor = Object_getOwnPropertyDescriptor(
|
|
69
|
+
{
|
|
70
|
+
hasFocus() {
|
|
71
|
+
return document.visibilityState === "visible";
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
"hasFocus",
|
|
75
|
+
);
|
|
76
|
+
if (focusDescriptor && typeof focusDescriptor.value === "function") {
|
|
77
|
+
patchToString(focusDescriptor.value, "hasFocus");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const clearOwnSlot = (name) => {
|
|
81
|
+
const ownDescriptor = Object_getOwnPropertyDescriptor(document, name);
|
|
82
|
+
if (!ownDescriptor) return true;
|
|
83
|
+
if (ownDescriptor.configurable !== true) return false;
|
|
84
|
+
return Reflect_deleteProperty(document, name);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const inheritedSlot = (name, expectedKind) => {
|
|
88
|
+
let proto = Object_getPrototypeOf(document);
|
|
89
|
+
while (proto) {
|
|
90
|
+
const descriptor = Object_getOwnPropertyDescriptor(proto, name);
|
|
91
|
+
if (descriptor && typeof descriptor[expectedKind] === "function") {
|
|
92
|
+
return [proto, descriptor];
|
|
93
|
+
}
|
|
94
|
+
proto = Object_getPrototypeOf(proto);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const defineAccessor = (name) => {
|
|
99
|
+
if (!clearOwnSlot(name)) return;
|
|
100
|
+
const slot = inheritedSlot(name, "get");
|
|
101
|
+
if (!slot || slot[1].configurable !== true) return;
|
|
102
|
+
|
|
103
|
+
Object_defineProperty(slot[0], name, {
|
|
104
|
+
get: visibilityDescriptors[name].get,
|
|
105
|
+
enumerable: slot[1].enumerable,
|
|
106
|
+
configurable: true,
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
defineAccessor("hidden");
|
|
111
|
+
defineAccessor("visibilityState");
|
|
112
|
+
defineAccessor("webkitHidden");
|
|
113
|
+
defineAccessor("webkitVisibilityState");
|
|
114
|
+
|
|
115
|
+
if (clearOwnSlot("hasFocus")) {
|
|
116
|
+
const slot = inheritedSlot("hasFocus", "value");
|
|
117
|
+
if (slot && slot[1].configurable === true) {
|
|
118
|
+
Object_defineProperty(slot[0], "hasFocus", {
|
|
119
|
+
value: focusDescriptor.value,
|
|
120
|
+
writable: slot[1].writable === true,
|
|
121
|
+
enumerable: slot[1].enumerable,
|
|
122
|
+
configurable: true,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
` },
|
|
128
|
+
{ name: '02_stealth_hairline', source: `{
|
|
129
|
+
const htmlElementOffsetHeightDescriptor =
|
|
130
|
+
typeof HTMLElement === "undefined"
|
|
131
|
+
? undefined
|
|
132
|
+
: Object_getOwnPropertyDescriptor(HTMLElement.prototype, "offsetHeight");
|
|
133
|
+
const htmlDivElementOffsetHeightDescriptor =
|
|
134
|
+
typeof HTMLDivElement === "undefined"
|
|
135
|
+
? undefined
|
|
136
|
+
: Object_getOwnPropertyDescriptor(HTMLDivElement.prototype, "offsetHeight");
|
|
137
|
+
const offsetHeightDescriptor =
|
|
138
|
+
htmlDivElementOffsetHeightDescriptor || htmlElementOffsetHeightDescriptor;
|
|
139
|
+
const offsetHeightPrototype = htmlDivElementOffsetHeightDescriptor
|
|
140
|
+
? HTMLDivElement.prototype
|
|
141
|
+
: htmlElementOffsetHeightDescriptor
|
|
142
|
+
? HTMLElement.prototype
|
|
143
|
+
: undefined;
|
|
144
|
+
const elementIdDescriptor =
|
|
145
|
+
typeof Element === "undefined"
|
|
146
|
+
? undefined
|
|
147
|
+
: Object_getOwnPropertyDescriptor(Element.prototype, "id");
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
typeof HTMLDivElement !== "undefined" &&
|
|
151
|
+
offsetHeightPrototype &&
|
|
152
|
+
offsetHeightDescriptor &&
|
|
153
|
+
typeof offsetHeightDescriptor.get === "function" &&
|
|
154
|
+
offsetHeightDescriptor.configurable &&
|
|
155
|
+
elementIdDescriptor &&
|
|
156
|
+
typeof elementIdDescriptor.get === "function"
|
|
157
|
+
) {
|
|
158
|
+
const offsetHeightGetter = new Window_Proxy(offsetHeightDescriptor.get, {
|
|
159
|
+
apply(target, thisArg, args) {
|
|
160
|
+
const height = Reflect_apply(target, thisArg, args);
|
|
161
|
+
|
|
162
|
+
if (
|
|
163
|
+
height === 0 &&
|
|
164
|
+
Object_getPrototypeOf(thisArg) === HTMLDivElement.prototype &&
|
|
165
|
+
Reflect_apply(elementIdDescriptor.get, thisArg, []) === "modernizr"
|
|
166
|
+
) {
|
|
167
|
+
return 1;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return height;
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
patchToString(offsetHeightGetter, "get offsetHeight");
|
|
175
|
+
|
|
176
|
+
Object_defineProperty(
|
|
177
|
+
offsetHeightPrototype,
|
|
178
|
+
"offsetHeight",
|
|
179
|
+
Object_assign({}, offsetHeightDescriptor, {
|
|
180
|
+
get: offsetHeightGetter,
|
|
181
|
+
}),
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
` },
|
|
186
|
+
{ name: '03_stealth_botd', source: `
|
|
187
|
+
// Ensure navigator.webdriver behaves like real Chrome
|
|
188
|
+
if (navigator.webdriver !== false && navigator.webdriver !== undefined) {
|
|
189
|
+
const proto = Object_getPrototypeOf(navigator);
|
|
190
|
+
if (proto && "webdriver" in proto) {
|
|
191
|
+
delete proto.webdriver;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Ensure window.chrome exists and is populated with standard Chrome APIs
|
|
196
|
+
if (!window.chrome) {
|
|
197
|
+
Object_defineProperty(window, "chrome", {
|
|
198
|
+
writable: true,
|
|
199
|
+
enumerable: true,
|
|
200
|
+
configurable: false,
|
|
201
|
+
value: {},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (window.chrome && !("app" in window.chrome)) {
|
|
206
|
+
const STATIC_DATA = {
|
|
207
|
+
isInstalled: false,
|
|
208
|
+
InstallState: {
|
|
209
|
+
DISABLED: "disabled",
|
|
210
|
+
INSTALLED: "installed",
|
|
211
|
+
NOT_INSTALLED: "not_installed",
|
|
212
|
+
},
|
|
213
|
+
RunningState: {
|
|
214
|
+
CANNOT_RUN: "cannot_run",
|
|
215
|
+
READY_TO_RUN: "ready_to_run",
|
|
216
|
+
RUNNING: "running",
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const makeError = (fn) => new TypeError(\`Error in invocation of app.\${fn}()\`);
|
|
221
|
+
|
|
222
|
+
window.chrome.app = {
|
|
223
|
+
...STATIC_DATA,
|
|
224
|
+
get isInstalled() {
|
|
225
|
+
return false;
|
|
226
|
+
},
|
|
227
|
+
getDetails: function getDetails() {
|
|
228
|
+
if (arguments.length) throw makeError("getDetails");
|
|
229
|
+
return null;
|
|
230
|
+
},
|
|
231
|
+
getIsInstalled: function getIsInstalled() {
|
|
232
|
+
if (arguments.length) throw makeError("getIsInstalled");
|
|
233
|
+
return false;
|
|
234
|
+
},
|
|
235
|
+
runningState: function runningState() {
|
|
236
|
+
if (arguments.length) throw makeError("runningState");
|
|
237
|
+
return "cannot_run";
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
patchToString(window.chrome.app.getDetails, "getDetails");
|
|
242
|
+
patchToString(window.chrome.app.getIsInstalled, "getIsInstalled");
|
|
243
|
+
patchToString(window.chrome.app.runningState, "runningState");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (window.chrome && !("csi" in window.chrome) && window.performance?.timing) {
|
|
247
|
+
window.chrome.csi = function csi() {
|
|
248
|
+
const { timing } = window.performance;
|
|
249
|
+
return {
|
|
250
|
+
onloadT: timing.domContentLoadedEventEnd,
|
|
251
|
+
startE: timing.navigationStart,
|
|
252
|
+
pageT: Date.now() - timing.navigationStart,
|
|
253
|
+
tran: 15,
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
patchToString(window.chrome.csi, "csi");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (
|
|
260
|
+
window.chrome &&
|
|
261
|
+
!("loadTimes" in window.chrome) &&
|
|
262
|
+
window.performance?.timing &&
|
|
263
|
+
window.PerformancePaintTiming
|
|
264
|
+
) {
|
|
265
|
+
const { performance } = window;
|
|
266
|
+
const ntEntryFallback = {
|
|
267
|
+
nextHopProtocol: "h2",
|
|
268
|
+
type: "other",
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const protocolInfo = {
|
|
272
|
+
get connectionInfo() {
|
|
273
|
+
const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback;
|
|
274
|
+
return ntEntry.nextHopProtocol;
|
|
275
|
+
},
|
|
276
|
+
get npnNegotiatedProtocol() {
|
|
277
|
+
const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback;
|
|
278
|
+
return ["h2", "hq"].includes(ntEntry.nextHopProtocol) ? ntEntry.nextHopProtocol : "unknown";
|
|
279
|
+
},
|
|
280
|
+
get navigationType() {
|
|
281
|
+
const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback;
|
|
282
|
+
return ntEntry.type;
|
|
283
|
+
},
|
|
284
|
+
get wasAlternateProtocolAvailable() {
|
|
285
|
+
return false;
|
|
286
|
+
},
|
|
287
|
+
get wasFetchedViaSpdy() {
|
|
288
|
+
const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback;
|
|
289
|
+
return ["h2", "hq"].includes(ntEntry.nextHopProtocol);
|
|
290
|
+
},
|
|
291
|
+
get wasNpnNegotiated() {
|
|
292
|
+
const ntEntry = performance.getEntriesByType("navigation")[0] || ntEntryFallback;
|
|
293
|
+
return ["h2", "hq"].includes(ntEntry.nextHopProtocol);
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
const { timing } = window.performance;
|
|
298
|
+
|
|
299
|
+
const toFixed = (num, fixed) => {
|
|
300
|
+
const re = new RegExp("^-?\\\\d+(?:.\\\\d{0," + (fixed || -1) + "})?");
|
|
301
|
+
return num.toString().match(re)[0];
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const timingInfo = {
|
|
305
|
+
get firstPaintAfterLoadTime() {
|
|
306
|
+
return 0;
|
|
307
|
+
},
|
|
308
|
+
get requestTime() {
|
|
309
|
+
return timing.navigationStart / 1000;
|
|
310
|
+
},
|
|
311
|
+
get startLoadTime() {
|
|
312
|
+
return timing.navigationStart / 1000;
|
|
313
|
+
},
|
|
314
|
+
get commitLoadTime() {
|
|
315
|
+
return timing.responseStart / 1000;
|
|
316
|
+
},
|
|
317
|
+
get finishDocumentLoadTime() {
|
|
318
|
+
return timing.domContentLoadedEventEnd / 1000;
|
|
319
|
+
},
|
|
320
|
+
get finishLoadTime() {
|
|
321
|
+
return timing.loadEventEnd / 1000;
|
|
322
|
+
},
|
|
323
|
+
get firstPaintTime() {
|
|
324
|
+
const fpEntry = performance.getEntriesByType("paint")[0] || {
|
|
325
|
+
startTime: timing.loadEventEnd / 1000,
|
|
326
|
+
};
|
|
327
|
+
return toFixed((fpEntry.startTime + performance.timeOrigin) / 1000, 3);
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
window.chrome.loadTimes = function loadTimes() {
|
|
332
|
+
return { ...protocolInfo, ...timingInfo };
|
|
333
|
+
};
|
|
334
|
+
patchToString(window.chrome.loadTimes, "loadTimes");
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const isSecureOrigin = document.location.protocol.startsWith("https");
|
|
338
|
+
if (window.chrome && !("runtime" in window.chrome) && isSecureOrigin) {
|
|
339
|
+
const STATIC_DATA = {
|
|
340
|
+
OnInstalledReason: {
|
|
341
|
+
CHROME_UPDATE: "chrome_update",
|
|
342
|
+
INSTALL: "install",
|
|
343
|
+
SHARED_MODULE_UPDATE: "shared_module_update",
|
|
344
|
+
UPDATE: "update",
|
|
345
|
+
},
|
|
346
|
+
OnRestartRequiredReason: {
|
|
347
|
+
APP_UPDATE: "app_update",
|
|
348
|
+
OS_UPDATE: "os_update",
|
|
349
|
+
PERIODIC: "periodic",
|
|
350
|
+
},
|
|
351
|
+
PlatformArch: {
|
|
352
|
+
ARM: "arm",
|
|
353
|
+
ARM64: "arm64",
|
|
354
|
+
MIPS: "mips",
|
|
355
|
+
MIPS64: "mips64",
|
|
356
|
+
X86_32: "x86-32",
|
|
357
|
+
X86_64: "x86-64",
|
|
358
|
+
},
|
|
359
|
+
PlatformNaclArch: {
|
|
360
|
+
ARM: "arm",
|
|
361
|
+
MIPS: "mips",
|
|
362
|
+
MIPS64: "mips64",
|
|
363
|
+
X86_32: "x86-32",
|
|
364
|
+
X86_64: "x86-64",
|
|
365
|
+
},
|
|
366
|
+
PlatformOs: {
|
|
367
|
+
ANDROID: "android",
|
|
368
|
+
CROS: "cros",
|
|
369
|
+
LINUX: "linux",
|
|
370
|
+
MAC: "mac",
|
|
371
|
+
OPENBSD: "openbsd",
|
|
372
|
+
WIN: "win",
|
|
373
|
+
},
|
|
374
|
+
RequestUpdateCheckStatus: {
|
|
375
|
+
NO_UPDATE: "no_update",
|
|
376
|
+
THROTTLED: "throttled",
|
|
377
|
+
UPDATE_AVAILABLE: "update_available",
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
const makeCustomRuntimeErrors = (preamble, method, extensionId) => ({
|
|
382
|
+
NoMatchingSignature: new TypeError(preamble + "No matching signature."),
|
|
383
|
+
MustSpecifyExtensionID: new TypeError(
|
|
384
|
+
preamble +
|
|
385
|
+
\`\${method} called from a webpage must specify an Extension ID (string) for its first argument.\`,
|
|
386
|
+
),
|
|
387
|
+
InvalidExtensionID: new TypeError(preamble + \`Invalid extension id: '\${extensionId}'\`),
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
const isValidExtensionId = (value) => value.length === 32 && value.toLowerCase().match(/^[a-p]+$/);
|
|
391
|
+
|
|
392
|
+
const sendMessageHandler = {
|
|
393
|
+
apply(target, _ctx, args) {
|
|
394
|
+
const [extensionId, options, responseCallback] = args || [];
|
|
395
|
+
const errorPreamble =
|
|
396
|
+
"Error in invocation of runtime.sendMessage(optional string extensionId, any message, optional object options, optional function responseCallback): ";
|
|
397
|
+
const Errors = makeCustomRuntimeErrors(errorPreamble, "chrome.runtime.sendMessage()", extensionId);
|
|
398
|
+
|
|
399
|
+
const noArguments = args.length === 0;
|
|
400
|
+
const tooManyArguments = args.length > 4;
|
|
401
|
+
const incorrectOptions = options && typeof options !== "object";
|
|
402
|
+
const incorrectResponseCallback = responseCallback && typeof responseCallback !== "function";
|
|
403
|
+
|
|
404
|
+
if (noArguments || tooManyArguments || incorrectOptions || incorrectResponseCallback) {
|
|
405
|
+
throw Errors.NoMatchingSignature;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (args.length < 2) {
|
|
409
|
+
throw Errors.MustSpecifyExtensionID;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (typeof extensionId !== "string") {
|
|
413
|
+
throw Errors.NoMatchingSignature;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (!isValidExtensionId(extensionId)) {
|
|
417
|
+
throw Errors.InvalidExtensionID;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return undefined;
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const connectHandler = {
|
|
425
|
+
apply(target, _ctx, args) {
|
|
426
|
+
const [extensionId, connectInfo] = args || [];
|
|
427
|
+
const errorPreamble =
|
|
428
|
+
"Error in invocation of runtime.connect(optional string extensionId, optional object connectInfo): ";
|
|
429
|
+
const Errors = makeCustomRuntimeErrors(errorPreamble, "chrome.runtime.connect()", extensionId);
|
|
430
|
+
|
|
431
|
+
const noArguments = args.length === 0;
|
|
432
|
+
const emptyStringArgument = args.length === 1 && extensionId === "";
|
|
433
|
+
if (noArguments || emptyStringArgument) {
|
|
434
|
+
throw Errors.MustSpecifyExtensionID;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const tooManyArguments = args.length > 2;
|
|
438
|
+
const incorrectConnectInfoType = connectInfo && typeof connectInfo !== "object";
|
|
439
|
+
if (tooManyArguments || incorrectConnectInfoType) {
|
|
440
|
+
throw Errors.NoMatchingSignature;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const extensionIdIsString = typeof extensionId === "string";
|
|
444
|
+
if (extensionIdIsString && extensionId === "") {
|
|
445
|
+
throw Errors.MustSpecifyExtensionID;
|
|
446
|
+
}
|
|
447
|
+
if (extensionIdIsString && !isValidExtensionId(extensionId)) {
|
|
448
|
+
throw Errors.InvalidExtensionID;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const validateConnectInfo = (info) => {
|
|
452
|
+
if (args.length > 1) {
|
|
453
|
+
throw Errors.NoMatchingSignature;
|
|
454
|
+
}
|
|
455
|
+
if (Object_keys(info).length === 0) {
|
|
456
|
+
throw Errors.MustSpecifyExtensionID;
|
|
457
|
+
}
|
|
458
|
+
Object_entries(info).forEach(([key, value]) => {
|
|
459
|
+
const isExpected = ["name", "includeTlsChannelId"].includes(key);
|
|
460
|
+
if (!isExpected) {
|
|
461
|
+
throw new TypeError(errorPreamble + \`Unexpected property: '\${key}'.\`);
|
|
462
|
+
}
|
|
463
|
+
const mismatch = (propName, expected, found) =>
|
|
464
|
+
TypeError(
|
|
465
|
+
errorPreamble +
|
|
466
|
+
\`Error at property '\${propName}': Invalid type: expected \${expected}, found \${found}.\`,
|
|
467
|
+
);
|
|
468
|
+
if (key === "name" && typeof value !== "string") {
|
|
469
|
+
throw mismatch(key, "string", typeof value);
|
|
470
|
+
}
|
|
471
|
+
if (key === "includeTlsChannelId" && typeof value !== "boolean") {
|
|
472
|
+
throw mismatch(key, "boolean", typeof value);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
if (typeof extensionId === "object") {
|
|
478
|
+
validateConnectInfo(extensionId);
|
|
479
|
+
throw Errors.MustSpecifyExtensionID;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const makeConnectResponse = () => {
|
|
483
|
+
const onSomething = () => ({
|
|
484
|
+
addListener: function addListener() {},
|
|
485
|
+
dispatch: function dispatch() {},
|
|
486
|
+
hasListener: function hasListener() {},
|
|
487
|
+
hasListeners: function hasListeners() {
|
|
488
|
+
return false;
|
|
489
|
+
},
|
|
490
|
+
removeListener: function removeListener() {},
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
name: "",
|
|
495
|
+
sender: undefined,
|
|
496
|
+
disconnect: function disconnect() {},
|
|
497
|
+
onDisconnect: onSomething(),
|
|
498
|
+
onMessage: onSomething(),
|
|
499
|
+
postMessage: function postMessage() {
|
|
500
|
+
if (!arguments.length) {
|
|
501
|
+
throw new TypeError("Insufficient number of arguments.");
|
|
502
|
+
}
|
|
503
|
+
throw new Error("Attempting to use a disconnected port object");
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
return makeConnectResponse();
|
|
509
|
+
},
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
window.chrome.runtime = {
|
|
513
|
+
...STATIC_DATA,
|
|
514
|
+
get id() {
|
|
515
|
+
return undefined;
|
|
516
|
+
},
|
|
517
|
+
connect: null,
|
|
518
|
+
sendMessage: null,
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
const sendMessageProxy = new Window_Proxy(function sendMessage() {}, sendMessageHandler);
|
|
522
|
+
const connectProxy = new Window_Proxy(function connect() {}, connectHandler);
|
|
523
|
+
patchToString(sendMessageProxy, "sendMessage");
|
|
524
|
+
patchToString(connectProxy, "connect");
|
|
525
|
+
window.chrome.runtime.sendMessage = sendMessageProxy;
|
|
526
|
+
window.chrome.runtime.connect = connectProxy;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Suppress Permission.query for automation-controlled
|
|
530
|
+
if (navigator.permissions?.query) {
|
|
531
|
+
if (isSecureOrigin && "Notification" in window) {
|
|
532
|
+
const notificationPermissionGetter = Object_getOwnPropertyDescriptor({
|
|
533
|
+
get permission() {
|
|
534
|
+
return "default";
|
|
535
|
+
},
|
|
536
|
+
}, "permission").get;
|
|
537
|
+
patchToString(notificationPermissionGetter, "get permission");
|
|
538
|
+
Object_defineProperty(Notification, "permission", {
|
|
539
|
+
get: notificationPermissionGetter,
|
|
540
|
+
configurable: true,
|
|
541
|
+
});
|
|
542
|
+
} else if (!isSecureOrigin) {
|
|
543
|
+
const originalQuery = navigator.permissions.query;
|
|
544
|
+
const patchedPermissionsQuery = function query(parameters) {
|
|
545
|
+
if (parameters?.name === "notifications") {
|
|
546
|
+
const status = { state: "denied", onchange: null };
|
|
547
|
+
if (typeof PermissionStatus !== "undefined") {
|
|
548
|
+
Object_setPrototypeOf(status, PermissionStatus.prototype);
|
|
549
|
+
}
|
|
550
|
+
return Promise_resolve(status);
|
|
551
|
+
}
|
|
552
|
+
return originalQuery.call(this, parameters);
|
|
553
|
+
};
|
|
554
|
+
patchToString(patchedPermissionsQuery, "query");
|
|
555
|
+
navigator.permissions.query = patchedPermissionsQuery;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// Remove CDC_ markers from document
|
|
560
|
+
const documentProps = Object_getOwnPropertyNames(document);
|
|
561
|
+
for (const prop of documentProps) {
|
|
562
|
+
if (prop.startsWith("cdc_")) {
|
|
563
|
+
delete document[prop];
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
` },
|
|
567
|
+
{ name: '04_stealth_iframe', source: `const iframeWindowProxies = new WeakMap();
|
|
568
|
+
|
|
569
|
+
const isFrameIndexKey = (key) => {
|
|
570
|
+
if (typeof key !== "string" || key === "") return false;
|
|
571
|
+
const number = +key;
|
|
572
|
+
return number >= 0 && number < 4294967295 && Math_floor(number) === number && \`\${number}\` === key;
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
const getPropertyDescriptor = (object, key) => {
|
|
576
|
+
let current = object;
|
|
577
|
+
while (current) {
|
|
578
|
+
const descriptor = Object_getOwnPropertyDescriptor(current, key);
|
|
579
|
+
if (descriptor) return descriptor;
|
|
580
|
+
current = Object_getPrototypeOf(current);
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
const descriptorWithValue = (target, key, value, writable) => {
|
|
585
|
+
const descriptor = Reflect_getOwnPropertyDescriptor(target, key);
|
|
586
|
+
if (descriptor && descriptor.configurable === false) return descriptor;
|
|
587
|
+
const next = descriptor ? Object_assign({}, descriptor) : { configurable: true, enumerable: true };
|
|
588
|
+
Reflect_deleteProperty(next, "get");
|
|
589
|
+
Reflect_deleteProperty(next, "set");
|
|
590
|
+
next.value = value;
|
|
591
|
+
if (!Reflect_has(next, "writable")) next.writable = writable;
|
|
592
|
+
return next;
|
|
593
|
+
};
|
|
594
|
+
const iframeContentWindowDescriptor =
|
|
595
|
+
typeof HTMLIFrameElement === "undefined"
|
|
596
|
+
? undefined
|
|
597
|
+
: getPropertyDescriptor(HTMLIFrameElement.prototype, "contentWindow");
|
|
598
|
+
const iframeSrcdocDescriptor =
|
|
599
|
+
typeof HTMLIFrameElement === "undefined"
|
|
600
|
+
? undefined
|
|
601
|
+
: getPropertyDescriptor(HTMLIFrameElement.prototype, "srcdoc");
|
|
602
|
+
|
|
603
|
+
const getNativeContentWindow = (iframe) => {
|
|
604
|
+
if (iframeContentWindowDescriptor && iframeContentWindowDescriptor.get) {
|
|
605
|
+
return Reflect_apply(iframeContentWindowDescriptor.get, iframe, []);
|
|
606
|
+
}
|
|
607
|
+
return undefined;
|
|
608
|
+
};
|
|
609
|
+
|
|
610
|
+
const addContentWindowProxy = (iframe) => {
|
|
611
|
+
let state = Reflect_apply(Page_WeakMap_get, iframeWindowProxies, [iframe]);
|
|
612
|
+
if (state) return state.proxy;
|
|
613
|
+
|
|
614
|
+
state = { proxy: undefined, target: undefined, wasConnected: false, setProxyTarget: undefined };
|
|
615
|
+
|
|
616
|
+
const isDiscarded = () => {
|
|
617
|
+
if (iframe.isConnected) state.wasConnected = true;
|
|
618
|
+
return state.wasConnected && !iframe.isConnected;
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
const currentFrameElement = () => {
|
|
622
|
+
if (iframe.isConnected) {
|
|
623
|
+
state.wasConnected = true;
|
|
624
|
+
return iframe;
|
|
625
|
+
}
|
|
626
|
+
return state.wasConnected ? null : iframe;
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
const contentWindowProxy = {
|
|
630
|
+
get(target, key) {
|
|
631
|
+
if (key === "self" || key === "window" || key === "frames" || key === "globalThis") return state.proxy;
|
|
632
|
+
if (key === "frameElement") return currentFrameElement();
|
|
633
|
+
if (key === "closed" && isDiscarded()) return true;
|
|
634
|
+
if (isFrameIndexKey(key)) return undefined;
|
|
635
|
+
if (key === "length") return 0;
|
|
636
|
+
return Reflect_get(target, key);
|
|
637
|
+
},
|
|
638
|
+
getOwnPropertyDescriptor(target, key) {
|
|
639
|
+
if (key === "self" || key === "window" || key === "frames" || key === "globalThis") {
|
|
640
|
+
return descriptorWithValue(target, key, state.proxy, true);
|
|
641
|
+
}
|
|
642
|
+
if (key === "frameElement") {
|
|
643
|
+
return descriptorWithValue(target, key, currentFrameElement(), false);
|
|
644
|
+
}
|
|
645
|
+
if (key === "closed" && isDiscarded()) {
|
|
646
|
+
return descriptorWithValue(target, key, true, false);
|
|
647
|
+
}
|
|
648
|
+
if (isFrameIndexKey(key)) return undefined;
|
|
649
|
+
if (key === "length") return descriptorWithValue(target, key, 0, false);
|
|
650
|
+
return Reflect_getOwnPropertyDescriptor(target, key);
|
|
651
|
+
},
|
|
652
|
+
has(target, key) {
|
|
653
|
+
if (key === "self" || key === "window" || key === "frames" || key === "globalThis" || key === "frameElement") return true;
|
|
654
|
+
if (isFrameIndexKey(key)) return false;
|
|
655
|
+
return Reflect_has(target, key);
|
|
656
|
+
},
|
|
657
|
+
ownKeys(target) {
|
|
658
|
+
const keys = Reflect_ownKeys(target);
|
|
659
|
+
const filtered = [];
|
|
660
|
+
for (let index = 0; index < keys.length; index++) {
|
|
661
|
+
if (!isFrameIndexKey(keys[index])) filtered.push(keys[index]);
|
|
662
|
+
}
|
|
663
|
+
return filtered;
|
|
664
|
+
},
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
const setProxyTarget = (target) => {
|
|
668
|
+
if (state.target === target && state.proxy) return state.proxy;
|
|
669
|
+
state.target = target;
|
|
670
|
+
state.proxy = new Window_Proxy(target, contentWindowProxy);
|
|
671
|
+
return state.proxy;
|
|
672
|
+
};
|
|
673
|
+
state.setProxyTarget = setProxyTarget;
|
|
674
|
+
const initialContentWindow = getNativeContentWindow(iframe);
|
|
675
|
+
if (initialContentWindow) setProxyTarget(initialContentWindow);
|
|
676
|
+
if (iframe.isConnected) state.wasConnected = true;
|
|
677
|
+
Reflect_apply(Page_WeakMap_set, iframeWindowProxies, [iframe, state]);
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
return state.proxy;
|
|
681
|
+
};
|
|
682
|
+
if (
|
|
683
|
+
iframeContentWindowDescriptor &&
|
|
684
|
+
iframeContentWindowDescriptor.get &&
|
|
685
|
+
iframeContentWindowDescriptor.configurable !== false
|
|
686
|
+
) {
|
|
687
|
+
const contentWindowAccessors = {
|
|
688
|
+
get contentWindow() {
|
|
689
|
+
const state = Reflect_apply(Page_WeakMap_get, iframeWindowProxies, [this]);
|
|
690
|
+
if (!state) return getNativeContentWindow(this);
|
|
691
|
+
if (this.isConnected) state.wasConnected = true;
|
|
692
|
+
if (state.wasConnected && !this.isConnected) return null;
|
|
693
|
+
const nativeContentWindow = getNativeContentWindow(this);
|
|
694
|
+
if (!nativeContentWindow) return nativeContentWindow;
|
|
695
|
+
return state.setProxyTarget(nativeContentWindow);
|
|
696
|
+
},
|
|
697
|
+
};
|
|
698
|
+
const contentWindowGetter = Object_getOwnPropertyDescriptor(contentWindowAccessors, "contentWindow").get;
|
|
699
|
+
patchToString(contentWindowGetter, "get contentWindow");
|
|
700
|
+
Object_defineProperty(HTMLIFrameElement.prototype, "contentWindow", {
|
|
701
|
+
get: contentWindowGetter,
|
|
702
|
+
set: iframeContentWindowDescriptor.set,
|
|
703
|
+
enumerable: iframeContentWindowDescriptor.enumerable,
|
|
704
|
+
configurable: iframeContentWindowDescriptor.configurable,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
if (
|
|
710
|
+
iframeSrcdocDescriptor &&
|
|
711
|
+
iframeSrcdocDescriptor.configurable !== false
|
|
712
|
+
) {
|
|
713
|
+
const srcdocAccessors = {
|
|
714
|
+
get srcdoc() {
|
|
715
|
+
if (iframeSrcdocDescriptor.get) {
|
|
716
|
+
return Reflect_apply(iframeSrcdocDescriptor.get, this, []);
|
|
717
|
+
}
|
|
718
|
+
const value = this.getAttribute("srcdoc");
|
|
719
|
+
return value === null ? "" : value;
|
|
720
|
+
},
|
|
721
|
+
set srcdoc(newValue) {
|
|
722
|
+
addContentWindowProxy(this);
|
|
723
|
+
if (iframeSrcdocDescriptor.set) {
|
|
724
|
+
return Reflect_apply(iframeSrcdocDescriptor.set, this, [newValue]);
|
|
725
|
+
}
|
|
726
|
+
return this.setAttribute("srcdoc", newValue);
|
|
727
|
+
},
|
|
728
|
+
};
|
|
729
|
+
const srcdocDescriptor = Object_getOwnPropertyDescriptor(srcdocAccessors, "srcdoc");
|
|
730
|
+
const srcdocGetter = srcdocDescriptor.get;
|
|
731
|
+
const srcdocSetter = srcdocDescriptor.set;
|
|
732
|
+
patchToString(srcdocGetter, "get srcdoc");
|
|
733
|
+
patchToString(srcdocSetter, "set srcdoc");
|
|
734
|
+
Object_defineProperty(HTMLIFrameElement.prototype, "srcdoc", {
|
|
735
|
+
get: srcdocGetter,
|
|
736
|
+
set: srcdocSetter,
|
|
737
|
+
enumerable: iframeSrcdocDescriptor.enumerable,
|
|
738
|
+
configurable: iframeSrcdocDescriptor.configurable,
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
` },
|
|
742
|
+
{ name: '05_stealth_webgl', source: `const webglPlatformSource =
|
|
743
|
+
String(navigator.userAgentData?.platform || navigator.platform || "") +
|
|
744
|
+
" " +
|
|
745
|
+
String(navigator.userAgent || "");
|
|
746
|
+
const webglPlatformText = webglPlatformSource.toLowerCase();
|
|
747
|
+
const webglPlatformKind = webglPlatformText.includes("android")
|
|
748
|
+
? "android"
|
|
749
|
+
: webglPlatformText.includes("iphone") ||
|
|
750
|
+
webglPlatformText.includes("ipad") ||
|
|
751
|
+
webglPlatformText.includes("ipod")
|
|
752
|
+
? "ios"
|
|
753
|
+
: webglPlatformText.includes("mac")
|
|
754
|
+
? "mac"
|
|
755
|
+
: webglPlatformText.includes("win")
|
|
756
|
+
? "windows"
|
|
757
|
+
: webglPlatformText.includes("cros")
|
|
758
|
+
? "cros"
|
|
759
|
+
: "linux";
|
|
760
|
+
|
|
761
|
+
const webglFallbackProfiles = {
|
|
762
|
+
android: {
|
|
763
|
+
vendor: "Qualcomm",
|
|
764
|
+
renderer: "Adreno (TM) 640",
|
|
765
|
+
},
|
|
766
|
+
ios: {
|
|
767
|
+
vendor: "Apple Inc.",
|
|
768
|
+
renderer: "Apple GPU",
|
|
769
|
+
},
|
|
770
|
+
mac: {
|
|
771
|
+
vendor: "Google Inc. (Intel Inc.)",
|
|
772
|
+
renderer: "ANGLE (Intel Inc., Intel(R) Iris(TM) Plus Graphics 640 OpenGL Engine, OpenGL 4.1)",
|
|
773
|
+
},
|
|
774
|
+
windows: {
|
|
775
|
+
vendor: "Google Inc. (Intel)",
|
|
776
|
+
renderer: "ANGLE (Intel, Intel(R) UHD Graphics 620 Direct3D11 vs_5_0 ps_5_0, D3D11)",
|
|
777
|
+
},
|
|
778
|
+
cros: {
|
|
779
|
+
vendor: "Google Inc. (Intel)",
|
|
780
|
+
renderer: "ANGLE (Intel, Mesa Intel(R) UHD Graphics 620 (KBL GT2), OpenGL 4.6)",
|
|
781
|
+
},
|
|
782
|
+
linux: {
|
|
783
|
+
vendor: "Google Inc. (Intel)",
|
|
784
|
+
renderer: "ANGLE (Intel, Mesa Intel(R) UHD Graphics 620 (KBL GT2), OpenGL 4.6)",
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
const webglFallbackProfile = webglFallbackProfiles[webglPlatformKind] || webglFallbackProfiles.linux;
|
|
788
|
+
const webglContextProfiles = new WeakMap();
|
|
789
|
+
const webglIsObjectKey = (value) =>
|
|
790
|
+
(typeof value === "object" && value !== null) || typeof value === "function";
|
|
791
|
+
|
|
792
|
+
const webglLooksSoftware = (value) => {
|
|
793
|
+
const text = String(value || "").toLowerCase();
|
|
794
|
+
return (
|
|
795
|
+
text.includes("swiftshader") ||
|
|
796
|
+
text.includes("llvmpipe") ||
|
|
797
|
+
text.includes("lavapipe") ||
|
|
798
|
+
text.includes("software") ||
|
|
799
|
+
text.includes("mesa offscreen") ||
|
|
800
|
+
text.includes("google inc. (google)")
|
|
801
|
+
);
|
|
802
|
+
};
|
|
803
|
+
|
|
804
|
+
const webglMatchesPlatform = (renderer) => {
|
|
805
|
+
const text = String(renderer || "").toLowerCase();
|
|
806
|
+
if (webglPlatformKind === "windows") {
|
|
807
|
+
return !text.includes("apple") && !text.includes("mesa") && !text.includes("opengl engine");
|
|
808
|
+
}
|
|
809
|
+
if (webglPlatformKind === "mac") {
|
|
810
|
+
return !text.includes("direct3d") && !text.includes("d3d") && !text.includes("mesa");
|
|
811
|
+
}
|
|
812
|
+
if (webglPlatformKind === "android") {
|
|
813
|
+
return (
|
|
814
|
+
text.includes("adreno") ||
|
|
815
|
+
text.includes("mali") ||
|
|
816
|
+
text.includes("powervr") ||
|
|
817
|
+
text.includes("qualcomm")
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
if (webglPlatformKind === "ios") {
|
|
821
|
+
return text.includes("apple");
|
|
822
|
+
}
|
|
823
|
+
return !text.includes("direct3d") && !text.includes("d3d") && !text.includes("apple");
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
const webglGetContextProfile = (target, thisArg) => {
|
|
827
|
+
if (!webglIsObjectKey(thisArg)) return webglFallbackProfile;
|
|
828
|
+
const cached = Reflect_apply(Page_WeakMap_get, webglContextProfiles, [thisArg]);
|
|
829
|
+
if (cached) return cached;
|
|
830
|
+
|
|
831
|
+
let nativeVendor;
|
|
832
|
+
let nativeRenderer;
|
|
833
|
+
try {
|
|
834
|
+
nativeVendor = Reflect_apply(target, thisArg, [0x9245]);
|
|
835
|
+
nativeRenderer = Reflect_apply(target, thisArg, [0x9246]);
|
|
836
|
+
} catch {}
|
|
837
|
+
|
|
838
|
+
const nativeProfile =
|
|
839
|
+
typeof nativeVendor === "string" &&
|
|
840
|
+
typeof nativeRenderer === "string" &&
|
|
841
|
+
nativeVendor &&
|
|
842
|
+
nativeRenderer &&
|
|
843
|
+
!webglLooksSoftware(nativeVendor) &&
|
|
844
|
+
!webglLooksSoftware(nativeRenderer) &&
|
|
845
|
+
webglMatchesPlatform(nativeRenderer)
|
|
846
|
+
? { vendor: nativeVendor, renderer: nativeRenderer }
|
|
847
|
+
: webglFallbackProfile;
|
|
848
|
+
|
|
849
|
+
Reflect_apply(Page_WeakMap_set, webglContextProfiles, [thisArg, nativeProfile]);
|
|
850
|
+
return nativeProfile;
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
const webglGetParameterHandler = {
|
|
854
|
+
apply(target, thisArg, args) {
|
|
855
|
+
const nativeValue = Reflect_apply(target, thisArg, args);
|
|
856
|
+
const param = args[0];
|
|
857
|
+
if (param === 0x1f00 && typeof nativeValue === "string") return "WebKit";
|
|
858
|
+
if (param === 0x1f01 && typeof nativeValue === "string") return "WebKit WebGL";
|
|
859
|
+
if ((param === 0x9245 || param === 0x9246) && typeof nativeValue === "string") {
|
|
860
|
+
const profile = webglGetContextProfile(target, thisArg);
|
|
861
|
+
return param === 0x9245 ? profile.vendor : profile.renderer;
|
|
862
|
+
}
|
|
863
|
+
return nativeValue;
|
|
864
|
+
},
|
|
865
|
+
};
|
|
866
|
+
|
|
867
|
+
const webglFloatPrecisionTypes = {
|
|
868
|
+
0x8df0: true,
|
|
869
|
+
0x8df1: true,
|
|
870
|
+
0x8df2: true,
|
|
871
|
+
};
|
|
872
|
+
|
|
873
|
+
const webglClonePrecisionFormat = (result, values) => {
|
|
874
|
+
const ownKeys = Reflect_ownKeys(result);
|
|
875
|
+
let hasRangeMin = false;
|
|
876
|
+
let hasRangeMax = false;
|
|
877
|
+
let hasPrecision = false;
|
|
878
|
+
for (let index = 0; index < ownKeys.length; index += 1) {
|
|
879
|
+
if (ownKeys[index] === "rangeMin") hasRangeMin = true;
|
|
880
|
+
if (ownKeys[index] === "rangeMax") hasRangeMax = true;
|
|
881
|
+
if (ownKeys[index] === "precision") hasPrecision = true;
|
|
882
|
+
}
|
|
883
|
+
if (!hasRangeMin || !hasRangeMax || !hasPrecision) return result;
|
|
884
|
+
|
|
885
|
+
const clone = Object_create(Object_getPrototypeOf(result));
|
|
886
|
+
for (let index = 0; index < ownKeys.length; index += 1) {
|
|
887
|
+
const key = ownKeys[index];
|
|
888
|
+
const descriptor = Object_getOwnPropertyDescriptor(result, key);
|
|
889
|
+
if (!descriptor) return result;
|
|
890
|
+
if (key === "rangeMin" || key === "rangeMax" || key === "precision") {
|
|
891
|
+
if (!("value" in descriptor)) return result;
|
|
892
|
+
descriptor.value = values[key];
|
|
893
|
+
}
|
|
894
|
+
try {
|
|
895
|
+
Object_defineProperty(clone, key, descriptor);
|
|
896
|
+
} catch {
|
|
897
|
+
return result;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return clone;
|
|
901
|
+
};
|
|
902
|
+
|
|
903
|
+
const webglGetShaderPrecisionFormatHandler = {
|
|
904
|
+
apply(target, thisArg, args) {
|
|
905
|
+
const result = Reflect_apply(target, thisArg, args);
|
|
906
|
+
const precisionType = args[1];
|
|
907
|
+
if (
|
|
908
|
+
!result ||
|
|
909
|
+
webglPlatformKind === "android" ||
|
|
910
|
+
webglPlatformKind === "ios" ||
|
|
911
|
+
!webglFloatPrecisionTypes[precisionType]
|
|
912
|
+
) {
|
|
913
|
+
return result;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const rangeMin = result.rangeMin;
|
|
917
|
+
const rangeMax = result.rangeMax;
|
|
918
|
+
const precision = result.precision;
|
|
919
|
+
if (
|
|
920
|
+
typeof rangeMin !== "number" ||
|
|
921
|
+
typeof rangeMax !== "number" ||
|
|
922
|
+
typeof precision !== "number"
|
|
923
|
+
) {
|
|
924
|
+
return result;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const values = {
|
|
928
|
+
rangeMin: Math_max(rangeMin, 127),
|
|
929
|
+
rangeMax: Math_max(rangeMax, 127),
|
|
930
|
+
precision: Math_max(precision, 23),
|
|
931
|
+
};
|
|
932
|
+
if (
|
|
933
|
+
values.rangeMin === rangeMin &&
|
|
934
|
+
values.rangeMax === rangeMax &&
|
|
935
|
+
values.precision === precision
|
|
936
|
+
) {
|
|
937
|
+
return result;
|
|
938
|
+
}
|
|
939
|
+
return webglClonePrecisionFormat(result, values);
|
|
940
|
+
},
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
const webglInstallMethodProxy = (proto, name, handler) => {
|
|
944
|
+
if (!proto) return;
|
|
945
|
+
const descriptor = Object_getOwnPropertyDescriptor(proto, name);
|
|
946
|
+
if (!descriptor || typeof descriptor.value !== "function") return;
|
|
947
|
+
const proxy = new Window_Proxy(descriptor.value, handler);
|
|
948
|
+
patchToString(proxy, name);
|
|
949
|
+
try {
|
|
950
|
+
Object_defineProperty(proto, name, {
|
|
951
|
+
value: proxy,
|
|
952
|
+
writable: descriptor.writable,
|
|
953
|
+
configurable: descriptor.configurable,
|
|
954
|
+
enumerable: descriptor.enumerable,
|
|
955
|
+
});
|
|
956
|
+
} catch {}
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
if (window.WebGLRenderingContext) {
|
|
960
|
+
webglInstallMethodProxy(WebGLRenderingContext.prototype, "getParameter", webglGetParameterHandler);
|
|
961
|
+
webglInstallMethodProxy(
|
|
962
|
+
WebGLRenderingContext.prototype,
|
|
963
|
+
"getShaderPrecisionFormat",
|
|
964
|
+
webglGetShaderPrecisionFormatHandler,
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
if (window.WebGL2RenderingContext) {
|
|
968
|
+
webglInstallMethodProxy(WebGL2RenderingContext.prototype, "getParameter", webglGetParameterHandler);
|
|
969
|
+
webglInstallMethodProxy(
|
|
970
|
+
WebGL2RenderingContext.prototype,
|
|
971
|
+
"getShaderPrecisionFormat",
|
|
972
|
+
webglGetShaderPrecisionFormatHandler,
|
|
973
|
+
);
|
|
974
|
+
}
|
|
975
|
+
` },
|
|
976
|
+
{ name: '06_stealth_screen', source: `;(() => {
|
|
977
|
+
if (typeof Window_Proxy !== "function" || typeof Reflect_apply !== "function") return;
|
|
978
|
+
|
|
979
|
+
const screenObject = window.screen;
|
|
980
|
+
if (!screenObject) return;
|
|
981
|
+
|
|
982
|
+
const isFiniteNumber = (value) =>
|
|
983
|
+
typeof value === "number" &&
|
|
984
|
+
value === value &&
|
|
985
|
+
value !== Infinity &&
|
|
986
|
+
value !== -Infinity;
|
|
987
|
+
|
|
988
|
+
const readValue = (object, prop, fallback) => {
|
|
989
|
+
try {
|
|
990
|
+
const value = object[prop];
|
|
991
|
+
return value === undefined ? fallback : value;
|
|
992
|
+
} catch {
|
|
993
|
+
return fallback;
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
const finiteNumber = (value, fallback) =>
|
|
998
|
+
isFiniteNumber(value) ? value : fallback;
|
|
999
|
+
|
|
1000
|
+
const positiveNumber = (value, fallback) =>
|
|
1001
|
+
isFiniteNumber(value) && value > 0 ? value : fallback;
|
|
1002
|
+
|
|
1003
|
+
const integerNumber = (value, fallback) =>
|
|
1004
|
+
isFiniteNumber(value) ? Math_floor(value) : fallback;
|
|
1005
|
+
|
|
1006
|
+
const positiveInteger = (value, fallback) =>
|
|
1007
|
+
isFiniteNumber(value) && value > 0 ? Math_floor(value) : fallback;
|
|
1008
|
+
|
|
1009
|
+
const findDescriptorOwner = (object, prop) => {
|
|
1010
|
+
let owner = object;
|
|
1011
|
+
while (owner) {
|
|
1012
|
+
let descriptor;
|
|
1013
|
+
try {
|
|
1014
|
+
descriptor = Object_getOwnPropertyDescriptor(owner, prop);
|
|
1015
|
+
} catch {
|
|
1016
|
+
return undefined;
|
|
1017
|
+
}
|
|
1018
|
+
if (descriptor) return [owner, descriptor];
|
|
1019
|
+
try {
|
|
1020
|
+
owner = Object_getPrototypeOf(owner);
|
|
1021
|
+
} catch {
|
|
1022
|
+
return undefined;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
return undefined;
|
|
1026
|
+
};
|
|
1027
|
+
|
|
1028
|
+
const patchGetter = (object, prop, getValue) => {
|
|
1029
|
+
const found = findDescriptorOwner(object, prop);
|
|
1030
|
+
if (!found) return false;
|
|
1031
|
+
|
|
1032
|
+
const owner = found[0];
|
|
1033
|
+
const descriptor = found[1];
|
|
1034
|
+
if (descriptor.configurable !== true || typeof descriptor.get !== "function") {
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
const originalGet = descriptor.get;
|
|
1039
|
+
const patchedGet = new Window_Proxy(originalGet, {
|
|
1040
|
+
apply(target, thisArg, args) {
|
|
1041
|
+
const nativeValue = Reflect_apply(target, thisArg, args);
|
|
1042
|
+
return getValue(nativeValue, thisArg);
|
|
1043
|
+
},
|
|
1044
|
+
});
|
|
1045
|
+
patchToString(patchedGet, "get " + prop);
|
|
1046
|
+
try {
|
|
1047
|
+
Object_defineProperty(owner, prop, {
|
|
1048
|
+
configurable: descriptor.configurable,
|
|
1049
|
+
enumerable: descriptor.enumerable,
|
|
1050
|
+
get: patchedGet,
|
|
1051
|
+
set: descriptor.set,
|
|
1052
|
+
});
|
|
1053
|
+
return true;
|
|
1054
|
+
} catch {
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
|
|
1059
|
+
const patchStableNumber = (object, prop, value) => {
|
|
1060
|
+
const current = readValue(object, prop, undefined);
|
|
1061
|
+
if (current === value) return;
|
|
1062
|
+
patchGetter(object, prop, () => value);
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
const initialInnerWidth = positiveInteger(readValue(window, "innerWidth", 0), 0);
|
|
1066
|
+
const initialInnerHeight = positiveInteger(readValue(window, "innerHeight", 0), 0);
|
|
1067
|
+
const initialOuterWidth = positiveInteger(
|
|
1068
|
+
readValue(window, "outerWidth", Math_max(initialInnerWidth, 1)),
|
|
1069
|
+
Math_max(initialInnerWidth, 1),
|
|
1070
|
+
);
|
|
1071
|
+
const initialOuterHeight = positiveInteger(
|
|
1072
|
+
readValue(window, "outerHeight", Math_max(initialInnerHeight, 1)),
|
|
1073
|
+
Math_max(initialInnerHeight, 1),
|
|
1074
|
+
);
|
|
1075
|
+
// Real Chrome reserves vertical space for the tab strip + URL bar, so
|
|
1076
|
+
// outerHeight is always taller than innerHeight (~88px on a stock window).
|
|
1077
|
+
// Headless reports outerHeight === innerHeight, which is a well-known tell, so
|
|
1078
|
+
// synthesize a realistic chrome height when the window has no visible chrome.
|
|
1079
|
+
const browserChromeHeight = 88;
|
|
1080
|
+
const targetOuterHeight =
|
|
1081
|
+
initialOuterHeight > initialInnerHeight
|
|
1082
|
+
? initialOuterHeight
|
|
1083
|
+
: initialInnerHeight + browserChromeHeight;
|
|
1084
|
+
const requiredWidth = Math_max(initialInnerWidth, initialOuterWidth, 1);
|
|
1085
|
+
const requiredHeight = Math_max(initialInnerHeight, initialOuterHeight, targetOuterHeight, 1);
|
|
1086
|
+
|
|
1087
|
+
const screenWidth = Math_max(
|
|
1088
|
+
positiveInteger(readValue(screenObject, "width", requiredWidth), requiredWidth),
|
|
1089
|
+
requiredWidth,
|
|
1090
|
+
);
|
|
1091
|
+
const screenHeight = Math_max(
|
|
1092
|
+
positiveInteger(readValue(screenObject, "height", requiredHeight), requiredHeight),
|
|
1093
|
+
requiredHeight,
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
let screenAvailWidth = positiveInteger(
|
|
1097
|
+
readValue(screenObject, "availWidth", screenWidth),
|
|
1098
|
+
screenWidth,
|
|
1099
|
+
);
|
|
1100
|
+
if (screenAvailWidth < requiredWidth || screenAvailWidth > screenWidth) {
|
|
1101
|
+
screenAvailWidth = screenWidth;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
let screenAvailHeight = positiveInteger(
|
|
1105
|
+
readValue(screenObject, "availHeight", screenHeight),
|
|
1106
|
+
screenHeight,
|
|
1107
|
+
);
|
|
1108
|
+
if (screenAvailHeight < requiredHeight || screenAvailHeight > screenHeight) {
|
|
1109
|
+
screenAvailHeight = screenHeight;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
const screenAvailLeft = integerNumber(readValue(screenObject, "availLeft", 0), 0);
|
|
1113
|
+
const screenAvailTop = integerNumber(readValue(screenObject, "availTop", 0), 0);
|
|
1114
|
+
const screenColorDepth = positiveInteger(readValue(screenObject, "colorDepth", 24), 24);
|
|
1115
|
+
const screenPixelDepth = positiveInteger(
|
|
1116
|
+
readValue(screenObject, "pixelDepth", screenColorDepth),
|
|
1117
|
+
screenColorDepth,
|
|
1118
|
+
);
|
|
1119
|
+
|
|
1120
|
+
for (const entry of [
|
|
1121
|
+
["width", screenWidth],
|
|
1122
|
+
["height", screenHeight],
|
|
1123
|
+
["availWidth", screenAvailWidth],
|
|
1124
|
+
["availHeight", screenAvailHeight],
|
|
1125
|
+
["availLeft", screenAvailLeft],
|
|
1126
|
+
["availTop", screenAvailTop],
|
|
1127
|
+
["colorDepth", screenColorDepth],
|
|
1128
|
+
["pixelDepth", screenPixelDepth],
|
|
1129
|
+
]) {
|
|
1130
|
+
patchStableNumber(screenObject, entry[0], entry[1]);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
const outerWidthNeedsPatch =
|
|
1134
|
+
!isFiniteNumber(readValue(window, "outerWidth", undefined)) ||
|
|
1135
|
+
readValue(window, "outerWidth", 0) <= 0 ||
|
|
1136
|
+
Math_floor(readValue(window, "outerWidth", 0)) < initialInnerWidth;
|
|
1137
|
+
if (outerWidthNeedsPatch) {
|
|
1138
|
+
patchGetter(window, "outerWidth", (nativeValue) =>
|
|
1139
|
+
Math_max(
|
|
1140
|
+
positiveInteger(nativeValue, initialOuterWidth),
|
|
1141
|
+
positiveInteger(readValue(window, "innerWidth", requiredWidth), requiredWidth),
|
|
1142
|
+
),
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const outerHeightNeedsPatch =
|
|
1147
|
+
!isFiniteNumber(readValue(window, "outerHeight", undefined)) ||
|
|
1148
|
+
readValue(window, "outerHeight", 0) <= 0 ||
|
|
1149
|
+
Math_floor(readValue(window, "outerHeight", 0)) <= initialInnerHeight;
|
|
1150
|
+
if (outerHeightNeedsPatch) {
|
|
1151
|
+
patchGetter(window, "outerHeight", (nativeValue) =>
|
|
1152
|
+
Math_max(
|
|
1153
|
+
positiveInteger(nativeValue, initialOuterHeight),
|
|
1154
|
+
positiveInteger(readValue(window, "innerHeight", requiredHeight), requiredHeight) +
|
|
1155
|
+
browserChromeHeight,
|
|
1156
|
+
),
|
|
1157
|
+
);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
const initialDevicePixelRatio = positiveNumber(
|
|
1161
|
+
readValue(window, "devicePixelRatio", 1),
|
|
1162
|
+
1,
|
|
1163
|
+
);
|
|
1164
|
+
if (readValue(window, "devicePixelRatio", undefined) !== initialDevicePixelRatio) {
|
|
1165
|
+
patchGetter(window, "devicePixelRatio", () => initialDevicePixelRatio);
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
const visualViewportObject = readValue(window, "visualViewport", null);
|
|
1169
|
+
if (visualViewportObject) {
|
|
1170
|
+
const initialScale = positiveNumber(readValue(visualViewportObject, "scale", 1), 1);
|
|
1171
|
+
if (readValue(visualViewportObject, "scale", undefined) !== initialScale) {
|
|
1172
|
+
patchGetter(visualViewportObject, "scale", (nativeValue) =>
|
|
1173
|
+
positiveNumber(nativeValue, initialScale),
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
const getViewportScale = () =>
|
|
1178
|
+
positiveNumber(readValue(visualViewportObject, "scale", initialScale), initialScale);
|
|
1179
|
+
|
|
1180
|
+
const getViewportWidthFallback = () =>
|
|
1181
|
+
positiveNumber(readValue(window, "innerWidth", requiredWidth), requiredWidth) /
|
|
1182
|
+
getViewportScale();
|
|
1183
|
+
|
|
1184
|
+
const getViewportHeightFallback = () =>
|
|
1185
|
+
positiveNumber(readValue(window, "innerHeight", requiredHeight), requiredHeight) /
|
|
1186
|
+
getViewportScale();
|
|
1187
|
+
|
|
1188
|
+
if (positiveNumber(readValue(visualViewportObject, "width", 0), 0) <= 0) {
|
|
1189
|
+
patchGetter(visualViewportObject, "width", (nativeValue) =>
|
|
1190
|
+
positiveNumber(nativeValue, getViewportWidthFallback()),
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
if (positiveNumber(readValue(visualViewportObject, "height", 0), 0) <= 0) {
|
|
1195
|
+
patchGetter(visualViewportObject, "height", (nativeValue) =>
|
|
1196
|
+
positiveNumber(nativeValue, getViewportHeightFallback()),
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
const getViewportOffsetLeft = () =>
|
|
1201
|
+
finiteNumber(readValue(visualViewportObject, "offsetLeft", 0), 0);
|
|
1202
|
+
const getViewportOffsetTop = () =>
|
|
1203
|
+
finiteNumber(readValue(visualViewportObject, "offsetTop", 0), 0);
|
|
1204
|
+
|
|
1205
|
+
if (!isFiniteNumber(readValue(visualViewportObject, "offsetLeft", undefined))) {
|
|
1206
|
+
patchGetter(visualViewportObject, "offsetLeft", (nativeValue) =>
|
|
1207
|
+
finiteNumber(nativeValue, 0),
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
if (!isFiniteNumber(readValue(visualViewportObject, "offsetTop", undefined))) {
|
|
1212
|
+
patchGetter(visualViewportObject, "offsetTop", (nativeValue) =>
|
|
1213
|
+
finiteNumber(nativeValue, 0),
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
if (!isFiniteNumber(readValue(visualViewportObject, "pageLeft", undefined))) {
|
|
1218
|
+
patchGetter(visualViewportObject, "pageLeft", (nativeValue) =>
|
|
1219
|
+
finiteNumber(
|
|
1220
|
+
nativeValue,
|
|
1221
|
+
finiteNumber(readValue(window, "scrollX", 0), 0) + getViewportOffsetLeft(),
|
|
1222
|
+
),
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
if (!isFiniteNumber(readValue(visualViewportObject, "pageTop", undefined))) {
|
|
1227
|
+
patchGetter(visualViewportObject, "pageTop", (nativeValue) =>
|
|
1228
|
+
finiteNumber(
|
|
1229
|
+
nativeValue,
|
|
1230
|
+
finiteNumber(readValue(window, "scrollY", 0), 0) + getViewportOffsetTop(),
|
|
1231
|
+
),
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
})();
|
|
1236
|
+
` },
|
|
1237
|
+
{ name: '07_stealth_fonts', source: `const commonFonts = [
|
|
1238
|
+
"Arial",
|
|
1239
|
+
"Arial Black",
|
|
1240
|
+
"Arial Narrow",
|
|
1241
|
+
"Arial Rounded MT Bold",
|
|
1242
|
+
"Book Antiqua",
|
|
1243
|
+
"Bookman Old Style",
|
|
1244
|
+
"Calibri",
|
|
1245
|
+
"Cambria",
|
|
1246
|
+
"Cambria Math",
|
|
1247
|
+
"Century",
|
|
1248
|
+
"Century Gothic",
|
|
1249
|
+
"Century Schoolbook",
|
|
1250
|
+
"Comic Sans MS",
|
|
1251
|
+
"Consolas",
|
|
1252
|
+
"Courier",
|
|
1253
|
+
"Courier New",
|
|
1254
|
+
"Garamond",
|
|
1255
|
+
"Geneva",
|
|
1256
|
+
"Georgia",
|
|
1257
|
+
"Gill Sans",
|
|
1258
|
+
"Gill Sans MT",
|
|
1259
|
+
"Helvetica",
|
|
1260
|
+
"Helvetica Neue",
|
|
1261
|
+
"Impact",
|
|
1262
|
+
"Lucida Console",
|
|
1263
|
+
"Lucida Grande",
|
|
1264
|
+
"Lucida Sans Unicode",
|
|
1265
|
+
"MS Gothic",
|
|
1266
|
+
"MS PGothic",
|
|
1267
|
+
"MS Sans Serif",
|
|
1268
|
+
"MS Serif",
|
|
1269
|
+
"Palatino",
|
|
1270
|
+
"Palatino Linotype",
|
|
1271
|
+
"Segoe Print",
|
|
1272
|
+
"Segoe Script",
|
|
1273
|
+
"Segoe UI",
|
|
1274
|
+
"Segoe UI Emoji",
|
|
1275
|
+
"Segoe UI Symbol",
|
|
1276
|
+
"Tahoma",
|
|
1277
|
+
"Times",
|
|
1278
|
+
"Times New Roman",
|
|
1279
|
+
"Trebuchet MS",
|
|
1280
|
+
"Verdana",
|
|
1281
|
+
"Wingdings",
|
|
1282
|
+
"Wingdings 2",
|
|
1283
|
+
"Wingdings 3",
|
|
1284
|
+
"Apple Color Emoji",
|
|
1285
|
+
"Apple SD Gothic Neo",
|
|
1286
|
+
"Hoefler Text",
|
|
1287
|
+
"Menlo",
|
|
1288
|
+
"Monaco",
|
|
1289
|
+
"San Francisco",
|
|
1290
|
+
"SF Pro Display",
|
|
1291
|
+
"SF Pro Text",
|
|
1292
|
+
];
|
|
1293
|
+
|
|
1294
|
+
if ("queryLocalFonts" in window) {
|
|
1295
|
+
const queryLocalFonts = async function queryLocalFonts() {
|
|
1296
|
+
return commonFonts.map((family) => ({
|
|
1297
|
+
family,
|
|
1298
|
+
fullName: family,
|
|
1299
|
+
postscriptName: family.replace(/\\s+/g, ""),
|
|
1300
|
+
style: "Regular",
|
|
1301
|
+
blob: () => Promise_resolve(new Window_Blob([])),
|
|
1302
|
+
}));
|
|
1303
|
+
};
|
|
1304
|
+
patchToString(queryLocalFonts, "queryLocalFonts");
|
|
1305
|
+
Object_defineProperty(window, "queryLocalFonts", {
|
|
1306
|
+
value: queryLocalFonts,
|
|
1307
|
+
writable: true,
|
|
1308
|
+
configurable: true,
|
|
1309
|
+
enumerable: true,
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
const originalGetContext = HTMLCanvasElement.prototype.getContext;
|
|
1314
|
+
const patchedGetContext = function getContext(type, options) {
|
|
1315
|
+
const ctx = originalGetContext.call(this, type, options);
|
|
1316
|
+
if (ctx && type === "2d") {
|
|
1317
|
+
const originalFillText = ctx.fillText;
|
|
1318
|
+
const patchedFillText = function fillText(text, x, y, maxWidth) {
|
|
1319
|
+
const noiseX = (Math_random() - 0.5) * 0.02;
|
|
1320
|
+
const noiseY = (Math_random() - 0.5) * 0.02;
|
|
1321
|
+
return originalFillText.call(
|
|
1322
|
+
this,
|
|
1323
|
+
text,
|
|
1324
|
+
x + noiseX,
|
|
1325
|
+
y + noiseY,
|
|
1326
|
+
maxWidth,
|
|
1327
|
+
);
|
|
1328
|
+
};
|
|
1329
|
+
patchToString(patchedFillText, "fillText");
|
|
1330
|
+
ctx.fillText = patchedFillText;
|
|
1331
|
+
}
|
|
1332
|
+
return ctx;
|
|
1333
|
+
};
|
|
1334
|
+
patchToString(patchedGetContext, "getContext");
|
|
1335
|
+
HTMLCanvasElement.prototype.getContext = patchedGetContext;` },
|
|
1336
|
+
{ name: '08_stealth_audio', source: `// Spoof AudioContext latency values to look like real hardware.
|
|
1337
|
+
const audioLatencyAccessors = {
|
|
1338
|
+
get baseLatency() {
|
|
1339
|
+
return 0.005;
|
|
1340
|
+
},
|
|
1341
|
+
get outputLatency() {
|
|
1342
|
+
return 0.01;
|
|
1343
|
+
},
|
|
1344
|
+
get sampleRate() {
|
|
1345
|
+
return 48000;
|
|
1346
|
+
},
|
|
1347
|
+
};
|
|
1348
|
+
|
|
1349
|
+
const defineAudioGetter = (proto, name) => {
|
|
1350
|
+
const descriptor = Object_getOwnPropertyDescriptor(audioLatencyAccessors, name);
|
|
1351
|
+
if (!descriptor || !descriptor.get) return;
|
|
1352
|
+
patchToString(descriptor.get, "get " + name);
|
|
1353
|
+
Object_defineProperty(proto, name, {
|
|
1354
|
+
get: descriptor.get,
|
|
1355
|
+
configurable: true,
|
|
1356
|
+
enumerable: true,
|
|
1357
|
+
});
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
const spoofLatency = (proto) => {
|
|
1361
|
+
defineAudioGetter(proto, "baseLatency");
|
|
1362
|
+
defineAudioGetter(proto, "outputLatency");
|
|
1363
|
+
};
|
|
1364
|
+
|
|
1365
|
+
if (window.AudioContext) {
|
|
1366
|
+
spoofLatency(AudioContext.prototype);
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
if (window.OfflineAudioContext) {
|
|
1370
|
+
const OriginalOfflineAudioContext = window.OfflineAudioContext;
|
|
1371
|
+
const PatchedOfflineAudioContext = class OfflineAudioContext extends OriginalOfflineAudioContext {
|
|
1372
|
+
constructor(numberOfChannels, length, sampleRate) {
|
|
1373
|
+
super(numberOfChannels, length, sampleRate);
|
|
1374
|
+
|
|
1375
|
+
const originalStartRendering = this.startRendering.bind(this);
|
|
1376
|
+
const patchedStartRendering = async function startRendering() {
|
|
1377
|
+
const buffer = await originalStartRendering();
|
|
1378
|
+
for (let c = 0; c < buffer.numberOfChannels; c++) {
|
|
1379
|
+
const channel = buffer.getChannelData(c);
|
|
1380
|
+
for (let i = 0; i < channel.length; i++) {
|
|
1381
|
+
if (Math_random() < 0.001) {
|
|
1382
|
+
channel[i] += (Math_random() - 0.5) * 1e-8;
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
return buffer;
|
|
1387
|
+
};
|
|
1388
|
+
patchToString(patchedStartRendering, "startRendering");
|
|
1389
|
+
this.startRendering = patchedStartRendering;
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
patchToString(PatchedOfflineAudioContext, "OfflineAudioContext");
|
|
1393
|
+
window.OfflineAudioContext = PatchedOfflineAudioContext;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
if (window.AudioContext) {
|
|
1397
|
+
defineAudioGetter(AudioContext.prototype, "sampleRate");
|
|
1398
|
+
}
|
|
1399
|
+
` },
|
|
1400
|
+
{ name: '09_stealth_locale', source: `const locale = "en-US";
|
|
1401
|
+
const languages = [locale, "en"];
|
|
1402
|
+
|
|
1403
|
+
const sameLanguages = (value) => {
|
|
1404
|
+
if (!value || value.length !== languages.length) return false;
|
|
1405
|
+
for (let index = 0; index < languages.length; index += 1) {
|
|
1406
|
+
if (value[index] !== languages[index]) return false;
|
|
1407
|
+
}
|
|
1408
|
+
return true;
|
|
1409
|
+
};
|
|
1410
|
+
|
|
1411
|
+
const navigatorProto = Object_getPrototypeOf(navigator);
|
|
1412
|
+
const navigatorAccessors = {
|
|
1413
|
+
get language() {
|
|
1414
|
+
return locale;
|
|
1415
|
+
},
|
|
1416
|
+
get languages() {
|
|
1417
|
+
return [locale, "en"];
|
|
1418
|
+
},
|
|
1419
|
+
};
|
|
1420
|
+
|
|
1421
|
+
const defineNavigatorAccessor = (name) => {
|
|
1422
|
+
const owner = navigatorProto || navigator;
|
|
1423
|
+
const descriptor =
|
|
1424
|
+
(navigatorProto && Object_getOwnPropertyDescriptor(navigatorProto, name)) ||
|
|
1425
|
+
Object_getOwnPropertyDescriptor(navigator, name);
|
|
1426
|
+
if (descriptor && descriptor.configurable === false) return;
|
|
1427
|
+
|
|
1428
|
+
const accessor = Object_getOwnPropertyDescriptor(navigatorAccessors, name);
|
|
1429
|
+
if (!accessor || !accessor.get) return;
|
|
1430
|
+
patchToString(accessor.get, "get " + name);
|
|
1431
|
+
|
|
1432
|
+
Object_defineProperty(owner, name, {
|
|
1433
|
+
get: accessor.get,
|
|
1434
|
+
configurable: descriptor ? descriptor.configurable : true,
|
|
1435
|
+
enumerable: descriptor ? descriptor.enumerable : true,
|
|
1436
|
+
});
|
|
1437
|
+
|
|
1438
|
+
const ownDescriptor = Object_getOwnPropertyDescriptor(navigator, name);
|
|
1439
|
+
if (owner !== navigator && ownDescriptor && ownDescriptor.configurable !== false) {
|
|
1440
|
+
Reflect_deleteProperty(navigator, name);
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
|
|
1444
|
+
if (navigator.language !== locale) {
|
|
1445
|
+
defineNavigatorAccessor("language");
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
if (!sameLanguages(navigator.languages)) {
|
|
1449
|
+
defineNavigatorAccessor("languages");
|
|
1450
|
+
}
|
|
1451
|
+
` },
|
|
1452
|
+
{ name: '10_stealth_plugins', source: `const mimeTypesData = [
|
|
1453
|
+
{
|
|
1454
|
+
type: "application/pdf",
|
|
1455
|
+
suffixes: "pdf",
|
|
1456
|
+
description: "",
|
|
1457
|
+
__pluginName: "Chrome PDF Viewer",
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
type: "application/x-google-chrome-pdf",
|
|
1461
|
+
suffixes: "pdf",
|
|
1462
|
+
description: "Portable Document Format",
|
|
1463
|
+
__pluginName: "Chrome PDF Plugin",
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
type: "application/x-nacl",
|
|
1467
|
+
suffixes: "",
|
|
1468
|
+
description: "Native Client Executable",
|
|
1469
|
+
__pluginName: "Native Client",
|
|
1470
|
+
},
|
|
1471
|
+
{
|
|
1472
|
+
type: "application/x-pnacl",
|
|
1473
|
+
suffixes: "",
|
|
1474
|
+
description: "Portable Native Client Executable",
|
|
1475
|
+
__pluginName: "Native Client",
|
|
1476
|
+
},
|
|
1477
|
+
];
|
|
1478
|
+
|
|
1479
|
+
const pluginsData = [
|
|
1480
|
+
{
|
|
1481
|
+
name: "Chrome PDF Plugin",
|
|
1482
|
+
filename: "internal-pdf-viewer",
|
|
1483
|
+
description: "Portable Document Format",
|
|
1484
|
+
__mimeTypes: ["application/x-google-chrome-pdf"],
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
name: "Chrome PDF Viewer",
|
|
1488
|
+
filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai",
|
|
1489
|
+
description: "",
|
|
1490
|
+
__mimeTypes: ["application/pdf"],
|
|
1491
|
+
},
|
|
1492
|
+
{
|
|
1493
|
+
name: "Native Client",
|
|
1494
|
+
filename: "internal-nacl-plugin",
|
|
1495
|
+
description: "",
|
|
1496
|
+
__mimeTypes: ["application/x-nacl", "application/x-pnacl"],
|
|
1497
|
+
},
|
|
1498
|
+
];
|
|
1499
|
+
|
|
1500
|
+
const defineProp = (obj, prop, value) =>
|
|
1501
|
+
Object_defineProperty(obj, prop, {
|
|
1502
|
+
value,
|
|
1503
|
+
writable: false,
|
|
1504
|
+
enumerable: false,
|
|
1505
|
+
configurable: true,
|
|
1506
|
+
});
|
|
1507
|
+
|
|
1508
|
+
const generateFunctionMocks = (proto, itemMainProp, dataArray) => {
|
|
1509
|
+
const item = new Window_Proxy(proto.item, {
|
|
1510
|
+
apply(target, ctx, args) {
|
|
1511
|
+
if (!args.length) {
|
|
1512
|
+
throw new TypeError(
|
|
1513
|
+
\`Failed to execute 'item' on '\${proto[Symbol.toStringTag]}': 1 argument required, but only 0 present.\`,
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
const isInteger = args[0] && Number.isInteger(Number(args[0]));
|
|
1517
|
+
return (isInteger ? dataArray[Number(args[0])] : dataArray[0]) || null;
|
|
1518
|
+
},
|
|
1519
|
+
});
|
|
1520
|
+
const namedItem = new Window_Proxy(proto.namedItem, {
|
|
1521
|
+
apply(target, ctx, args) {
|
|
1522
|
+
if (!args.length) {
|
|
1523
|
+
throw new TypeError(
|
|
1524
|
+
\`Failed to execute 'namedItem' on '\${proto[Symbol.toStringTag]}': 1 argument required, but only 0 present.\`,
|
|
1525
|
+
);
|
|
1526
|
+
}
|
|
1527
|
+
return dataArray.find(item => item[itemMainProp] === args[0]) || null;
|
|
1528
|
+
},
|
|
1529
|
+
});
|
|
1530
|
+
patchToString(item, "item");
|
|
1531
|
+
patchToString(namedItem, "namedItem");
|
|
1532
|
+
const refresh = proto.refresh
|
|
1533
|
+
? new Window_Proxy(proto.refresh, {
|
|
1534
|
+
apply() {
|
|
1535
|
+
return undefined;
|
|
1536
|
+
},
|
|
1537
|
+
})
|
|
1538
|
+
: undefined;
|
|
1539
|
+
patchToString(refresh, "refresh");
|
|
1540
|
+
return { item, namedItem, refresh };
|
|
1541
|
+
};
|
|
1542
|
+
|
|
1543
|
+
const generateMagicArray = (dataArray, proto, itemProto, itemMainProp) => {
|
|
1544
|
+
const makeItem = (data) => {
|
|
1545
|
+
const item = {};
|
|
1546
|
+
for (const prop of Object_keys(data)) {
|
|
1547
|
+
if (prop.startsWith("__")) continue;
|
|
1548
|
+
defineProp(item, prop, data[prop]);
|
|
1549
|
+
}
|
|
1550
|
+
return patchItem(item, data);
|
|
1551
|
+
};
|
|
1552
|
+
|
|
1553
|
+
const patchItem = (item, data) => {
|
|
1554
|
+
let descriptor = Object_getOwnPropertyDescriptors(item);
|
|
1555
|
+
if (itemProto === Plugin.prototype) {
|
|
1556
|
+
descriptor = {
|
|
1557
|
+
...descriptor,
|
|
1558
|
+
length: {
|
|
1559
|
+
value: data.__mimeTypes.length,
|
|
1560
|
+
writable: false,
|
|
1561
|
+
enumerable: false,
|
|
1562
|
+
configurable: true,
|
|
1563
|
+
},
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
const obj = Object_create(itemProto, descriptor);
|
|
1568
|
+
const blacklist = [...Object_keys(data), "length", "enabledPlugin"];
|
|
1569
|
+
return new Window_Proxy(obj, {
|
|
1570
|
+
ownKeys(target) {
|
|
1571
|
+
return [...Object_getOwnPropertyNames(target), ...Object.getOwnPropertySymbols(target)].filter(
|
|
1572
|
+
key => !blacklist.includes(key),
|
|
1573
|
+
);
|
|
1574
|
+
},
|
|
1575
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
1576
|
+
if (blacklist.includes(prop)) return undefined;
|
|
1577
|
+
return Object_getOwnPropertyDescriptor(target, prop);
|
|
1578
|
+
},
|
|
1579
|
+
});
|
|
1580
|
+
};
|
|
1581
|
+
|
|
1582
|
+
const magicArray = [];
|
|
1583
|
+
dataArray.forEach(data => {
|
|
1584
|
+
magicArray.push(makeItem(data));
|
|
1585
|
+
});
|
|
1586
|
+
|
|
1587
|
+
magicArray.forEach(entry => {
|
|
1588
|
+
defineProp(magicArray, entry[itemMainProp], entry);
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
const magicArrayObj = Object_create(proto, {
|
|
1592
|
+
...Object_getOwnPropertyDescriptors(magicArray),
|
|
1593
|
+
length: {
|
|
1594
|
+
value: magicArray.length,
|
|
1595
|
+
writable: false,
|
|
1596
|
+
enumerable: false,
|
|
1597
|
+
configurable: true,
|
|
1598
|
+
},
|
|
1599
|
+
});
|
|
1600
|
+
|
|
1601
|
+
const functionMocks = generateFunctionMocks(proto, itemMainProp, magicArray);
|
|
1602
|
+
|
|
1603
|
+
return new Window_Proxy(magicArrayObj, {
|
|
1604
|
+
get(target, key = "") {
|
|
1605
|
+
if (key === "item") return functionMocks.item;
|
|
1606
|
+
if (key === "namedItem") return functionMocks.namedItem;
|
|
1607
|
+
if (proto === PluginArray.prototype && key === "refresh") return functionMocks.refresh;
|
|
1608
|
+
return Reflect_get(target, key);
|
|
1609
|
+
},
|
|
1610
|
+
ownKeys(target) {
|
|
1611
|
+
const keys = [];
|
|
1612
|
+
const typeProps = magicArray.map(entry => entry[itemMainProp]);
|
|
1613
|
+
typeProps.forEach((_, index) => keys.push(\`\${index}\`));
|
|
1614
|
+
typeProps.forEach(propName => keys.push(propName));
|
|
1615
|
+
return keys;
|
|
1616
|
+
},
|
|
1617
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
1618
|
+
if (prop === "length") return undefined;
|
|
1619
|
+
return Object_getOwnPropertyDescriptor(target, prop);
|
|
1620
|
+
},
|
|
1621
|
+
});
|
|
1622
|
+
};
|
|
1623
|
+
|
|
1624
|
+
const generateMimeTypeArray = (data) =>
|
|
1625
|
+
generateMagicArray(data, MimeTypeArray.prototype, MimeType.prototype, "type");
|
|
1626
|
+
const generatePluginArray = (data) =>
|
|
1627
|
+
generateMagicArray(data, PluginArray.prototype, Plugin.prototype, "name");
|
|
1628
|
+
|
|
1629
|
+
const mimeTypes = generateMimeTypeArray(mimeTypesData);
|
|
1630
|
+
const plugins = generatePluginArray(pluginsData);
|
|
1631
|
+
|
|
1632
|
+
for (const pluginData of pluginsData) {
|
|
1633
|
+
pluginData.__mimeTypes.forEach((type, index) => {
|
|
1634
|
+
plugins[pluginData.name][index] = mimeTypes[type];
|
|
1635
|
+
Object_defineProperty(plugins[pluginData.name], type, {
|
|
1636
|
+
value: mimeTypes[type],
|
|
1637
|
+
writable: false,
|
|
1638
|
+
enumerable: false,
|
|
1639
|
+
configurable: true,
|
|
1640
|
+
});
|
|
1641
|
+
Object_defineProperty(mimeTypes[type], "enabledPlugin", {
|
|
1642
|
+
value:
|
|
1643
|
+
type === "application/x-pnacl"
|
|
1644
|
+
? mimeTypes["application/x-nacl"].enabledPlugin
|
|
1645
|
+
: new Window_Proxy(plugins[pluginData.name], {}),
|
|
1646
|
+
writable: false,
|
|
1647
|
+
enumerable: false,
|
|
1648
|
+
configurable: true,
|
|
1649
|
+
});
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
const patchNavigator = (name, value) =>
|
|
1654
|
+
Object_defineProperty(Object_getPrototypeOf(navigator), name, {
|
|
1655
|
+
get() {
|
|
1656
|
+
return value;
|
|
1657
|
+
},
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1660
|
+
if (!("plugins" in navigator) || navigator.plugins.length === 0) {
|
|
1661
|
+
patchNavigator("plugins", plugins);
|
|
1662
|
+
patchNavigator("mimeTypes", mimeTypes);
|
|
1663
|
+
}
|
|
1664
|
+
` },
|
|
1665
|
+
{ name: '11_stealth_hardware', source: `const hardwareConcurrencyName = "hardwareConcurrency";
|
|
1666
|
+
let hardwareConcurrencyProto = Object_getPrototypeOf(navigator);
|
|
1667
|
+
let hardwareConcurrencyOwner;
|
|
1668
|
+
let hardwareConcurrencyDescriptor;
|
|
1669
|
+
|
|
1670
|
+
while (hardwareConcurrencyProto && !hardwareConcurrencyDescriptor) {
|
|
1671
|
+
hardwareConcurrencyDescriptor = Object_getOwnPropertyDescriptor(
|
|
1672
|
+
hardwareConcurrencyProto,
|
|
1673
|
+
hardwareConcurrencyName,
|
|
1674
|
+
);
|
|
1675
|
+
if (hardwareConcurrencyDescriptor) {
|
|
1676
|
+
hardwareConcurrencyOwner = hardwareConcurrencyProto;
|
|
1677
|
+
} else {
|
|
1678
|
+
hardwareConcurrencyProto = Object_getPrototypeOf(hardwareConcurrencyProto);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
const hardwareConcurrencyValue = 8;
|
|
1683
|
+
let shouldPatchHardwareConcurrency = false;
|
|
1684
|
+
|
|
1685
|
+
if (
|
|
1686
|
+
hardwareConcurrencyOwner &&
|
|
1687
|
+
hardwareConcurrencyDescriptor &&
|
|
1688
|
+
hardwareConcurrencyDescriptor.configurable &&
|
|
1689
|
+
typeof hardwareConcurrencyDescriptor.get === "function"
|
|
1690
|
+
) {
|
|
1691
|
+
shouldPatchHardwareConcurrency = true;
|
|
1692
|
+
try {
|
|
1693
|
+
shouldPatchHardwareConcurrency =
|
|
1694
|
+
Reflect_apply(hardwareConcurrencyDescriptor.get, navigator, []) !==
|
|
1695
|
+
hardwareConcurrencyValue;
|
|
1696
|
+
} catch {
|
|
1697
|
+
shouldPatchHardwareConcurrency = false;
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
if (shouldPatchHardwareConcurrency) {
|
|
1702
|
+
const hardwareConcurrencyAccessors = {
|
|
1703
|
+
get hardwareConcurrency() {
|
|
1704
|
+
Reflect_apply(hardwareConcurrencyDescriptor.get, this, []);
|
|
1705
|
+
return hardwareConcurrencyValue;
|
|
1706
|
+
},
|
|
1707
|
+
};
|
|
1708
|
+
const getHardwareConcurrency = Object_getOwnPropertyDescriptor(
|
|
1709
|
+
hardwareConcurrencyAccessors,
|
|
1710
|
+
hardwareConcurrencyName,
|
|
1711
|
+
).get;
|
|
1712
|
+
|
|
1713
|
+
if (typeof patchToString === "function") {
|
|
1714
|
+
patchToString(getHardwareConcurrency, "get hardwareConcurrency");
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
Object_defineProperty(hardwareConcurrencyOwner, hardwareConcurrencyName, {
|
|
1718
|
+
get: getHardwareConcurrency,
|
|
1719
|
+
set: hardwareConcurrencyDescriptor.set,
|
|
1720
|
+
enumerable: hardwareConcurrencyDescriptor.enumerable,
|
|
1721
|
+
configurable: hardwareConcurrencyDescriptor.configurable,
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
` },
|
|
1725
|
+
{ name: '12_stealth_codecs', source: `const parseInput = (arg) => {
|
|
1726
|
+
const [mime, codecStr] = arg.trim().split(";");
|
|
1727
|
+
let codecs = [];
|
|
1728
|
+
if (codecStr && codecStr.includes('codecs="')) {
|
|
1729
|
+
codecs = codecStr
|
|
1730
|
+
.trim()
|
|
1731
|
+
.replace('codecs="', "")
|
|
1732
|
+
.replace('"', "")
|
|
1733
|
+
.trim()
|
|
1734
|
+
.split(",")
|
|
1735
|
+
.filter(Boolean)
|
|
1736
|
+
.map(item => item.trim());
|
|
1737
|
+
}
|
|
1738
|
+
return { mime, codecStr, codecs };
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
const originalCanPlayType = HTMLMediaElement.prototype.canPlayType;
|
|
1742
|
+
const proxiedCanPlayType = new Window_Proxy(originalCanPlayType, {
|
|
1743
|
+
apply(target, ctx, args) {
|
|
1744
|
+
if (!args || !args.length) {
|
|
1745
|
+
return Reflect_apply(target, ctx, args);
|
|
1746
|
+
}
|
|
1747
|
+
const { mime, codecs } = parseInput(args[0]);
|
|
1748
|
+
if (mime === "video/mp4" && codecs.includes("avc1.42E01E")) {
|
|
1749
|
+
return "probably";
|
|
1750
|
+
}
|
|
1751
|
+
if (mime === "audio/x-m4a" && !codecs.length) {
|
|
1752
|
+
return "maybe";
|
|
1753
|
+
}
|
|
1754
|
+
if (mime === "audio/aac" && !codecs.length) {
|
|
1755
|
+
return "probably";
|
|
1756
|
+
}
|
|
1757
|
+
return Reflect_apply(target, ctx, args);
|
|
1758
|
+
},
|
|
1759
|
+
});
|
|
1760
|
+
patchToString(proxiedCanPlayType, "canPlayType");
|
|
1761
|
+
Object_defineProperty(HTMLMediaElement.prototype, "canPlayType", {
|
|
1762
|
+
value: proxiedCanPlayType,
|
|
1763
|
+
writable: true,
|
|
1764
|
+
configurable: true,
|
|
1765
|
+
enumerable: true,
|
|
1766
|
+
});
|
|
1767
|
+
` },
|
|
1768
|
+
{ name: '13_stealth_worker', source: `const patchWorkerConstructor = (name, OriginalWorker) => {
|
|
1769
|
+
if (typeof OriginalWorker !== "function") return;
|
|
1770
|
+
|
|
1771
|
+
const windowDescriptor = Object_getOwnPropertyDescriptor(window, name);
|
|
1772
|
+
if (windowDescriptor && windowDescriptor.configurable === false) return;
|
|
1773
|
+
|
|
1774
|
+
const NativeURL = window.URL;
|
|
1775
|
+
const URL_createObjectURL = NativeURL && NativeURL.createObjectURL;
|
|
1776
|
+
const URL_revokeObjectURL = NativeURL && NativeURL.revokeObjectURL;
|
|
1777
|
+
if (
|
|
1778
|
+
typeof NativeURL !== "function" ||
|
|
1779
|
+
typeof URL_createObjectURL !== "function" ||
|
|
1780
|
+
typeof URL_revokeObjectURL !== "function"
|
|
1781
|
+
) {
|
|
1782
|
+
return;
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
const sharedWorkerUrls = name === "SharedWorker" ? new Map() : undefined;
|
|
1786
|
+
|
|
1787
|
+
const revokeUrl = (url) => {
|
|
1788
|
+
try {
|
|
1789
|
+
Reflect_apply(URL_revokeObjectURL, NativeURL, [url]);
|
|
1790
|
+
} catch {}
|
|
1791
|
+
};
|
|
1792
|
+
|
|
1793
|
+
const scheduleWorkerUrlRevoke = (worker, url) => {
|
|
1794
|
+
let revoked = false;
|
|
1795
|
+
const revokeOnce = () => {
|
|
1796
|
+
if (revoked) return;
|
|
1797
|
+
revoked = true;
|
|
1798
|
+
revokeUrl(url);
|
|
1799
|
+
};
|
|
1800
|
+
try {
|
|
1801
|
+
if (typeof worker.addEventListener === "function") {
|
|
1802
|
+
Reflect_apply(worker.addEventListener, worker, ["error", revokeOnce, { once: true }]);
|
|
1803
|
+
}
|
|
1804
|
+
} catch {}
|
|
1805
|
+
Window_setTimeout(revokeOnce, 1000);
|
|
1806
|
+
};
|
|
1807
|
+
|
|
1808
|
+
if (sharedWorkerUrls) {
|
|
1809
|
+
try {
|
|
1810
|
+
window.addEventListener("pagehide", (event) => {
|
|
1811
|
+
if (event && event.persisted) return;
|
|
1812
|
+
for (const url of sharedWorkerUrls.values()) {
|
|
1813
|
+
revokeUrl(url);
|
|
1814
|
+
}
|
|
1815
|
+
sharedWorkerUrls.clear();
|
|
1816
|
+
});
|
|
1817
|
+
} catch {}
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
const canWrapArguments = (args) => {
|
|
1821
|
+
if (!args || args.length !== 1) {
|
|
1822
|
+
return name === "SharedWorker" && args && args.length === 2 && typeof args[1] === "string";
|
|
1823
|
+
}
|
|
1824
|
+
return true;
|
|
1825
|
+
};
|
|
1826
|
+
|
|
1827
|
+
const resolveWorkerUrl = (scriptURL) => {
|
|
1828
|
+
const baseUrl = document.baseURI || window.location.href;
|
|
1829
|
+
const scriptUrlString =
|
|
1830
|
+
typeof scriptURL === "string"
|
|
1831
|
+
? scriptURL
|
|
1832
|
+
: scriptURL instanceof NativeURL
|
|
1833
|
+
? scriptURL.href
|
|
1834
|
+
: undefined;
|
|
1835
|
+
if (scriptUrlString === undefined) return undefined;
|
|
1836
|
+
let absoluteUrl;
|
|
1837
|
+
try {
|
|
1838
|
+
absoluteUrl = new NativeURL(scriptUrlString, baseUrl);
|
|
1839
|
+
} catch {
|
|
1840
|
+
return undefined;
|
|
1841
|
+
}
|
|
1842
|
+
if (absoluteUrl.origin !== window.location.origin) return undefined;
|
|
1843
|
+
if (absoluteUrl.protocol !== "http:" && absoluteUrl.protocol !== "https:") return undefined;
|
|
1844
|
+
if (absoluteUrl.username || absoluteUrl.password) return undefined;
|
|
1845
|
+
return absoluteUrl.href;
|
|
1846
|
+
};
|
|
1847
|
+
|
|
1848
|
+
const buildWorkerPrelude = () => {
|
|
1849
|
+
const values = [];
|
|
1850
|
+
const addNavigatorString = (prop) => {
|
|
1851
|
+
try {
|
|
1852
|
+
const value = navigator[prop];
|
|
1853
|
+
if (typeof value === "string") values.push([prop, value]);
|
|
1854
|
+
} catch {}
|
|
1855
|
+
};
|
|
1856
|
+
addNavigatorString("userAgent");
|
|
1857
|
+
addNavigatorString("platform");
|
|
1858
|
+
if (!values.length) return "";
|
|
1859
|
+
|
|
1860
|
+
return \`(() => {
|
|
1861
|
+
try {
|
|
1862
|
+
const values = \${JSON.stringify(values)};
|
|
1863
|
+
const nav = self.navigator;
|
|
1864
|
+
if (!nav) return;
|
|
1865
|
+
const defineProperty = Object.defineProperty;
|
|
1866
|
+
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
1867
|
+
const getPrototypeOf = Object.getPrototypeOf;
|
|
1868
|
+
const Reflect_apply = Reflect.apply;
|
|
1869
|
+
const nativeFunctionToString = Function.prototype.toString;
|
|
1870
|
+
const nativeSources = new WeakMap();
|
|
1871
|
+
const WeakMap_get = WeakMap.prototype.get;
|
|
1872
|
+
const WeakMap_has = WeakMap.prototype.has;
|
|
1873
|
+
const WeakMap_set = WeakMap.prototype.set;
|
|
1874
|
+
const rememberNative = (fn, source) => {
|
|
1875
|
+
if (typeof fn === "function") Reflect_apply(WeakMap_set, nativeSources, [fn, source]);
|
|
1876
|
+
return fn;
|
|
1877
|
+
};
|
|
1878
|
+
const findDescriptor = (prop) => {
|
|
1879
|
+
let owner = nav;
|
|
1880
|
+
while (owner) {
|
|
1881
|
+
const descriptor = getOwnPropertyDescriptor(owner, prop);
|
|
1882
|
+
if (descriptor) return [owner, descriptor];
|
|
1883
|
+
owner = getPrototypeOf(owner);
|
|
1884
|
+
}
|
|
1885
|
+
return [getPrototypeOf(nav) || nav, undefined];
|
|
1886
|
+
};
|
|
1887
|
+
let patched = false;
|
|
1888
|
+
for (let i = 0; i < values.length; i += 1) {
|
|
1889
|
+
const prop = values[i][0];
|
|
1890
|
+
const value = values[i][1];
|
|
1891
|
+
try {
|
|
1892
|
+
if (nav[prop] === value) continue;
|
|
1893
|
+
} catch (_) {}
|
|
1894
|
+
const found = findDescriptor(prop);
|
|
1895
|
+
const owner = found[0];
|
|
1896
|
+
const descriptor = found[1];
|
|
1897
|
+
if (!owner || (descriptor && descriptor.configurable === false)) continue;
|
|
1898
|
+
const getterDescriptor = getOwnPropertyDescriptor({ get [prop]() { return value; } }, prop);
|
|
1899
|
+
const getter = getterDescriptor && getterDescriptor.get;
|
|
1900
|
+
if (typeof getter !== "function") continue;
|
|
1901
|
+
rememberNative(getter, "function get " + prop + "() { [native code] }");
|
|
1902
|
+
defineProperty(owner, prop, {
|
|
1903
|
+
get: getter,
|
|
1904
|
+
enumerable: descriptor ? descriptor.enumerable : true,
|
|
1905
|
+
configurable: true,
|
|
1906
|
+
});
|
|
1907
|
+
patched = true;
|
|
1908
|
+
}
|
|
1909
|
+
if (!patched) return;
|
|
1910
|
+
const toStringDescriptor = getOwnPropertyDescriptor(Function.prototype, "toString");
|
|
1911
|
+
if (toStringDescriptor && toStringDescriptor.configurable === false) return;
|
|
1912
|
+
const functionToString = new Proxy(nativeFunctionToString, {
|
|
1913
|
+
apply(target, thisArg, args) {
|
|
1914
|
+
if (Reflect_apply(WeakMap_has, nativeSources, [thisArg])) return Reflect_apply(WeakMap_get, nativeSources, [thisArg]);
|
|
1915
|
+
return Reflect_apply(target, thisArg, args);
|
|
1916
|
+
},
|
|
1917
|
+
});
|
|
1918
|
+
rememberNative(functionToString, "function toString() { [native code] }");
|
|
1919
|
+
defineProperty(Function.prototype, "toString", {
|
|
1920
|
+
value: functionToString,
|
|
1921
|
+
writable: toStringDescriptor ? toStringDescriptor.writable : true,
|
|
1922
|
+
configurable: toStringDescriptor ? toStringDescriptor.configurable : true,
|
|
1923
|
+
enumerable: toStringDescriptor ? toStringDescriptor.enumerable : false,
|
|
1924
|
+
});
|
|
1925
|
+
} catch (_) {}
|
|
1926
|
+
})();\`;
|
|
1927
|
+
};
|
|
1928
|
+
|
|
1929
|
+
const buildWrappedUrl = (scriptURL) => {
|
|
1930
|
+
const originalUrl = resolveWorkerUrl(scriptURL);
|
|
1931
|
+
if (!originalUrl) return undefined;
|
|
1932
|
+
if (sharedWorkerUrls) {
|
|
1933
|
+
const cachedUrl = sharedWorkerUrls.get(originalUrl);
|
|
1934
|
+
if (cachedUrl) return { url: cachedUrl, cacheKey: originalUrl };
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
const prelude = buildWorkerPrelude();
|
|
1938
|
+
if (!prelude) return undefined;
|
|
1939
|
+
|
|
1940
|
+
try {
|
|
1941
|
+
const blob = new Window_Blob(
|
|
1942
|
+
[prelude, "\\n", \`importScripts(\${JSON.stringify(originalUrl)});\`],
|
|
1943
|
+
{ type: "application/javascript" },
|
|
1944
|
+
);
|
|
1945
|
+
const url = Reflect_apply(URL_createObjectURL, NativeURL, [blob]);
|
|
1946
|
+
if (sharedWorkerUrls) sharedWorkerUrls.set(originalUrl, url);
|
|
1947
|
+
return { url, cacheKey: sharedWorkerUrls ? originalUrl : undefined };
|
|
1948
|
+
} catch {
|
|
1949
|
+
return undefined;
|
|
1950
|
+
}
|
|
1951
|
+
};
|
|
1952
|
+
|
|
1953
|
+
const handler = {
|
|
1954
|
+
construct(target, args, newTarget) {
|
|
1955
|
+
if (!canWrapArguments(args)) {
|
|
1956
|
+
return Reflect_construct(target, args || [], newTarget);
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
const wrapped = buildWrappedUrl(args[0]);
|
|
1960
|
+
if (!wrapped) {
|
|
1961
|
+
return Reflect_construct(target, args || [], newTarget);
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
const wrappedArgs = [wrapped.url];
|
|
1965
|
+
for (let i = 1; i < args.length; i += 1) {
|
|
1966
|
+
wrappedArgs[i] = args[i];
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
try {
|
|
1970
|
+
const worker = Reflect_construct(target, wrappedArgs, newTarget);
|
|
1971
|
+
if (!sharedWorkerUrls) {
|
|
1972
|
+
scheduleWorkerUrlRevoke(worker, wrapped.url);
|
|
1973
|
+
}
|
|
1974
|
+
return worker;
|
|
1975
|
+
} catch {
|
|
1976
|
+
if (wrapped.cacheKey && sharedWorkerUrls) sharedWorkerUrls.delete(wrapped.cacheKey);
|
|
1977
|
+
revokeUrl(wrapped.url);
|
|
1978
|
+
return Reflect_construct(target, args || [], newTarget);
|
|
1979
|
+
}
|
|
1980
|
+
},
|
|
1981
|
+
apply(target, thisArg, args) {
|
|
1982
|
+
return Reflect_apply(target, thisArg, args || []);
|
|
1983
|
+
},
|
|
1984
|
+
};
|
|
1985
|
+
|
|
1986
|
+
const proxied = new Window_Proxy(OriginalWorker, handler);
|
|
1987
|
+
patchToString(proxied, name);
|
|
1988
|
+
Object_defineProperty(window, name, {
|
|
1989
|
+
value: proxied,
|
|
1990
|
+
writable:
|
|
1991
|
+
windowDescriptor && "writable" in windowDescriptor
|
|
1992
|
+
? windowDescriptor.writable
|
|
1993
|
+
: true,
|
|
1994
|
+
configurable: windowDescriptor ? windowDescriptor.configurable : true,
|
|
1995
|
+
enumerable: windowDescriptor ? windowDescriptor.enumerable : false,
|
|
1996
|
+
});
|
|
1997
|
+
};
|
|
1998
|
+
|
|
1999
|
+
try {
|
|
2000
|
+
patchWorkerConstructor("Worker", window.Worker);
|
|
2001
|
+
patchWorkerConstructor("SharedWorker", window.SharedWorker);
|
|
2002
|
+
} catch {}
|
|
2003
|
+
` },
|
|
2004
|
+
];
|
|
2005
|
+
/** Run every stealth script in a freshly created page; call before navigation. */
|
|
2006
|
+
export async function installStealth(page) {
|
|
2007
|
+
for (const script of scripts)
|
|
2008
|
+
await page.addInitScript(script.source);
|
|
2009
|
+
}
|
|
2010
|
+
//# sourceMappingURL=stealth-scripts.js.map
|