reverse-skills-max: initial public release (restored, redacted, hardened)

This commit is contained in:
baradok609hecl1pq6qi
2026-10-20 02:17:15 +03:00
commit da24c2bd20
2227 changed files with 443931 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
const MODULE_PRIORITY = [
"prototype-builder",
"descriptor-guard",
"native-protector",
"navigator-module",
"document-module",
"document-all-module",
"storage-module",
"fingerprint-module",
"performance-module",
"crypto-module",
"audio-fingerprint-module",
"webrtc-module",
"worker-module",
"stack-clean-module",
];
function toArray(value) {
if (Array.isArray(value)) return value;
if (value && Array.isArray(value.logs)) return value.logs;
return [];
}
function unique(values) {
return Array.from(new Set(values));
}
function extractPathList(input, key, type) {
if (Array.isArray(input[key])) return unique(input[key]);
return unique(
toArray(input)
.filter((item) => item && item.type === type && typeof item.path === "string")
.map((item) => item.path)
);
}
function inferModulesFromPath(path) {
const modules = [];
const value = String(path || "");
if (/__proto__|prototype|constructor/.test(value)) modules.push("prototype-builder");
if (/getOwnPropertyDescriptor|defineProperty|descriptor|ownKeys/.test(value)) modules.push("descriptor-guard");
if (/toString|name|length|caller|arguments/.test(value)) modules.push("native-protector");
if (/navigator|plugins|mimeTypes|webdriver|getBattery|userAgentData/.test(value)) {
modules.push("navigator-module");
if (/getBattery/.test(value)) modules.push("fingerprint-module");
}
if (/document\.all/.test(value)) {
modules.push("document-all-module");
modules.push("document-module");
} else if (/document|createElement|querySelector|cookie/.test(value)) {
modules.push("document-module");
}
if (/localStorage|sessionStorage|indexedDB|cookieStore/.test(value)) modules.push("storage-module");
if (/canvas|webgl|screen|devicePixelRatio|getContext|toDataURL/.test(value)) modules.push("fingerprint-module");
if (/performance|Performance/.test(value)) modules.push("performance-module");
if (/crypto|msCrypto|getRandomValues|subtle/.test(value)) modules.push("crypto-module");
if (/AudioContext|OfflineAudioContext|Oscillator|Analyser/.test(value)) modules.push("audio-fingerprint-module");
if (/RTCPeerConnection|RTCDataChannel|RTCIceCandidate|RTCSessionDescription/.test(value)) modules.push("webrtc-module");
if (/Worker|SharedWorker|MessagePort|BroadcastChannel/.test(value)) modules.push("worker-module");
if (/Error|stack|prepareStackTrace/.test(value)) modules.push("stack-clean-module");
return unique(modules);
}
function scoreEvidence(entry) {
if (!entry || !entry.type) return 0;
if (entry.type === "invoke-error") return 5;
if (entry.type === "missing") return 4;
if (entry.type === "descriptor" || entry.type === "prototype") return 3;
return 1;
}
function analyzeGapLog(input = {}) {
const logs = toArray(input);
const missingPaths = extractPathList(input, "missingPaths", "missing");
const descriptorAccess = extractPathList(input, "descriptorAccess", "descriptor");
const prototypeAccess = extractPathList(input, "prototypeAccess", "prototype");
const invocationErrors = extractPathList(input, "invocationErrors", "invoke-error");
const groupedByModule = Object.create(null);
for (const entry of logs) {
const modules = inferModulesFromPath(entry.path);
if (!modules.length) continue;
for (const moduleName of modules) {
if (!groupedByModule[moduleName]) {
groupedByModule[moduleName] = { score: 0, evidence: [] };
}
groupedByModule[moduleName].score += scoreEvidence(entry);
groupedByModule[moduleName].evidence.push(entry);
}
}
const recommendedModules = Object.keys(groupedByModule).sort((left, right) => {
const leftPriority = MODULE_PRIORITY.indexOf(left);
const rightPriority = MODULE_PRIORITY.indexOf(right);
const leftScore = groupedByModule[left].score;
const rightScore = groupedByModule[right].score;
if (rightScore !== leftScore) return rightScore - leftScore;
return leftPriority - rightPriority;
});
const nextActions = [];
if (recommendedModules.includes("prototype-builder")) {
nextActions.push("先修复构造器、原型链和 instanceof 关系,再处理对象值。");
}
if (recommendedModules.includes("descriptor-guard")) {
nextActions.push("把 getOwnPropertyDescriptor/defineProperty/propertyIsEnumerable 的结果对齐。");
}
if (recommendedModules.includes("native-protector")) {
nextActions.push("统一接管 Function.prototype.toString,并补齐 getter/setter/name/length 保护。");
}
if (recommendedModules.includes("document-all-module")) {
nextActions.push("单独评估 document.all,明确是 native-addon、V8 API 还是 fallback 路线。");
}
if (recommendedModules.includes("storage-module")) {
nextActions.push("把 cookie/localStorage/sessionStorage 的真实状态同步进运行时。");
}
if (recommendedModules.includes("fingerprint-module")) {
nextActions.push("把 canvas/webgl/screen/battery 等指纹点单独建模,不要和普通 navigator 属性混补。");
}
return {
totalLogs: logs.length,
missingPaths,
descriptorAccess,
prototypeAccess,
invocationErrors,
recommendedModules,
groupedByModule,
nextActions: unique(nextActions),
};
}
module.exports = {
analyzeGapLog,
inferModulesFromPath,
};

View File

@@ -0,0 +1,180 @@
const { scaffoldModule } = require("./scaffold-module");
const DEFAULT_SUBTLE_METHODS = [
"digest",
"generateKey",
"importKey",
"exportKey",
"sign",
"verify",
"encrypt",
"decrypt",
];
function toFiniteNumber(value, fallback) {
return Number.isFinite(value) ? value : fallback;
}
function normalizeCryptoSeed(seed = {}) {
const cryptoSeed = seed.crypto && typeof seed.crypto === "object" ? seed.crypto : seed;
return {
exposeCrypto: cryptoSeed.exposeCrypto !== false,
installMsCrypto: cryptoSeed.installMsCrypto !== false,
maxGetRandomValuesLength: toFiniteNumber(cryptoSeed.maxGetRandomValuesLength, 65536),
hasSubtle: cryptoSeed.hasSubtle !== false,
subtleMethods: Array.isArray(cryptoSeed.subtleMethods) && cryptoSeed.subtleMethods.length
? cryptoSeed.subtleMethods.slice()
: DEFAULT_SUBTLE_METHODS.slice(),
randomValues: Array.isArray(cryptoSeed.randomValues)
? cryptoSeed.randomValues.map((item) => Number(item) & 255)
: [],
};
}
function renderCryptoPatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installCryptoModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
const integerArrayTags = new Set([
"[object Int8Array]",
"[object Uint8Array]",
"[object Uint8ClampedArray]",
"[object Int16Array]",
"[object Uint16Array]",
"[object Int32Array]",
"[object Uint32Array]"
]);
const randomPool = Array.isArray(seed.randomValues) ? seed.randomValues.slice() : [];
function defineValue(target, key, value) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
writable: true,
value: value
});
}
function createQuotaError(message) {
if (typeof DOMException === "function") {
return new DOMException(message, "QuotaExceededError");
}
const error = new Error(message);
error.name = "QuotaExceededError";
return error;
}
function assertIntegerTypedArray(view) {
const tag = Object.prototype.toString.call(view);
if (!integerArrayTags.has(tag)) {
throw new TypeError("Failed to execute 'getRandomValues' on 'Crypto': parameter 1 is not an integer array view.");
}
if (view.byteLength > seed.maxGetRandomValuesLength) {
throw createQuotaError("Failed to execute 'getRandomValues' on 'Crypto': The ArrayBufferView's byte length exceeds the number of bytes of entropy available via this API.");
}
return view;
}
function nextByte(index) {
if (randomPool.length) {
return randomPool[index % randomPool.length] & 255;
}
return (index * 73 + 19) & 255;
}
function getRandomValues(view) {
assertIntegerTypedArray(view);
for (let index = 0; index < view.length; index += 1) {
view[index] = nextByte(index);
}
return view;
}
const subtle = {};
for (const methodName of seed.subtleMethods) {
subtle[methodName] = function subtlePlaceholder() {
return Promise.reject(new Error("SubtleCrypto method not implemented: " + methodName));
};
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(subtle[methodName], { name: methodName, length: 0 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(subtle[methodName], methodName);
}
}
const cryptoObject = {};
defineValue(cryptoObject, "getRandomValues", getRandomValues);
if (seed.hasSubtle) {
defineValue(cryptoObject, "subtle", subtle);
}
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(getRandomValues, { name: "getRandomValues", length: 1 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(getRandomValues, "getRandomValues");
}
if (seed.exposeCrypto) {
Object.defineProperty(globalObject, "crypto", {
configurable: true,
enumerable: false,
writable: true,
value: cryptoObject
});
}
if (seed.installMsCrypto) {
Object.defineProperty(globalObject, "msCrypto", {
configurable: true,
enumerable: false,
writable: true,
value: cryptoObject
});
}
return cryptoObject;
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildCryptoModule(seed = {}, options = {}) {
const module = scaffoldModule("crypto-module");
const normalizedSeed = normalizeCryptoSeed(seed);
module.patchPlan = [
"挂载 window.crypto 与可选的 window.msCrypto。",
"按种子构建 getRandomValues,并复刻整数 TypedArray 校验和长度上限。",
"按模块契约补出 subtle 对象的基础方法占位,并接入 native 保护接口。",
];
module.patchCode = renderCryptoPatch(normalizedSeed, options);
module.runtimeState = {
crypto: normalizedSeed,
};
module.validation = [
"调用 crypto.getRandomValues(new Uint8Array(16)),确认返回原数组实例。",
"调用 crypto.getRandomValues(new Uint16Array(8)) 与 new Uint32Array(4)。",
"传入 Float32Array 时应抛出 TypeError。",
"传入超出 maxGetRandomValuesLength 的视图时应抛出 QuotaExceededError。",
"检查 crypto.getRandomValues.toString() 与 crypto.getRandomValues.name/length。",
];
module.residualRisk = [
"subtle 仍为占位实现,只适合绕过存在性检测,不适合真实密码流程。",
"如果目标站点会比对 DOMException message/code,仍需依据真实浏览器日志继续收敛。",
];
if (options.includeLegacyAlias === false) {
module.runtimeState.crypto.installMsCrypto = false;
}
return module;
}
module.exports = {
buildCryptoModule,
normalizeCryptoSeed,
};

View File

@@ -0,0 +1,332 @@
const { scaffoldModule } = require("./scaffold-module");
function clone(value) {
return JSON.parse(JSON.stringify(value || {}));
}
function normalizeDocumentSeed(seed = {}) {
const documentSeed = seed.document && typeof seed.document === "object" ? seed.document : seed;
return {
document: {
URL: documentSeed.URL || "https://example.invalid/",
referrer: documentSeed.referrer || "",
documentURI: documentSeed.documentURI || documentSeed.URL || "https://example.invalid/",
compatMode: documentSeed.compatMode || "CSS1Compat",
dir: documentSeed.dir || "",
title: documentSeed.title || "",
designMode: documentSeed.designMode || "off",
readyState: documentSeed.readyState || "complete",
contentType: documentSeed.contentType || "text/html",
inputEncoding: documentSeed.inputEncoding || "UTF-8",
domain: documentSeed.domain || "example.invalid",
characterSet: documentSeed.characterSet || "UTF-8",
charset: documentSeed.charset || "UTF-8",
hidden: documentSeed.hidden === true,
visibilityState: documentSeed.visibilityState || "visible",
cookie: typeof documentSeed.cookie === "string" ? documentSeed.cookie : (typeof seed.cookie === "string" ? seed.cookie : ""),
},
queryMap: clone(seed.queryMap || {}),
ids: clone(seed.ids || {}),
tags: clone(seed.tags || {}),
specialCases: clone(seed.specialCases || {}),
};
}
function renderDocumentPatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installDocumentModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
function defineValue(target, key, value) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
writable: true,
value: value
});
}
function defineGetter(target, key, getter, setter) {
const descriptor = {
configurable: true,
enumerable: false,
get: getter
};
if (typeof setter === "function") {
descriptor.set = setter;
}
Object.defineProperty(target, key, descriptor);
if (protector && protector.protectDescriptor) {
protector.protectDescriptor(target, key);
}
}
function applyEventTarget(target) {
defineValue(target, "__listeners__", Object.create(null));
defineValue(target, "addEventListener", function addEventListener(type, listener) {
if (!this.__listeners__[type]) {
this.__listeners__[type] = [];
}
this.__listeners__[type].push(listener);
});
defineValue(target, "removeEventListener", function removeEventListener(type, listener) {
const list = this.__listeners__[type] || [];
this.__listeners__[type] = list.filter(function filterItem(item) {
return item !== listener;
});
});
defineValue(target, "dispatchEvent", function dispatchEvent(event) {
const list = (this.__listeners__[event.type] || []).slice();
for (const listener of list) {
listener.call(this, event);
}
const handler = this["on" + event.type];
if (typeof handler === "function") {
handler.call(this, event);
}
return true;
});
}
function createElementNode(tagName) {
const normalized = String(tagName || "div").toUpperCase();
if (normalized === "CANVAS" && typeof globalObject.__envCreateCanvasElement === "function") {
return globalObject.__envCreateCanvasElement();
}
const element = {
nodeType: 1,
nodeName: normalized,
tagName: normalized,
ownerDocument: null,
style: {},
dataset: {},
children: [],
childNodes: []
};
applyEventTarget(element);
defineValue(element, "appendChild", function appendChild(child) {
this.children.push(child);
this.childNodes.push(child);
child.parentNode = this;
return child;
});
defineValue(element, "setAttribute", function setAttribute(name, value) {
this[name] = String(value);
});
defineValue(element, "getAttribute", function getAttribute(name) {
return Object.prototype.hasOwnProperty.call(this, name) ? this[name] : null;
});
return element;
}
function buildNodeMap(source, kind) {
const out = Object.create(null);
Object.keys(source || {}).forEach(function eachKey(key) {
const value = source[key];
if (Array.isArray(value)) {
out[key] = value.map(function mapTag(tagName) {
const node = createElementNode(tagName);
node.ownerDocument = documentInstance;
return node;
});
} else {
const node = createElementNode(value || kind || "div");
node.ownerDocument = documentInstance;
out[key] = node;
}
});
return out;
}
function Document() {}
function HTMLDocument() {}
HTMLDocument.prototype = Object.create(Document.prototype);
defineValue(HTMLDocument.prototype, "constructor", HTMLDocument);
const state = seed.document || {};
const documentInstance = Object.create(HTMLDocument.prototype);
let cookieValue = state.cookie || "";
applyEventTarget(documentInstance);
const queryNodes = buildNodeMap(seed.queryMap || {}, "div");
const idNodes = buildNodeMap(seed.ids || {}, "div");
const tagNodes = buildNodeMap(seed.tags || {}, "div");
const htmlNode = createElementNode("html");
const headNode = createElementNode("head");
const bodyNode = createElementNode("body");
htmlNode.ownerDocument = documentInstance;
headNode.ownerDocument = documentInstance;
bodyNode.ownerDocument = documentInstance;
htmlNode.appendChild(headNode);
htmlNode.appendChild(bodyNode);
defineGetter(Document.prototype, "URL", function getURL() {
return state.URL;
});
defineGetter(Document.prototype, "referrer", function getReferrer() {
return state.referrer;
});
defineGetter(Document.prototype, "documentURI", function getDocumentURI() {
return state.documentURI;
});
defineGetter(Document.prototype, "compatMode", function getCompatMode() {
return state.compatMode;
});
defineGetter(Document.prototype, "dir", function getDir() {
return state.dir;
});
defineGetter(Document.prototype, "title", function getTitle() {
return state.title;
}, function setTitle(value) {
state.title = String(value);
});
defineGetter(Document.prototype, "designMode", function getDesignMode() {
return state.designMode;
}, function setDesignMode(value) {
state.designMode = String(value);
});
defineGetter(Document.prototype, "readyState", function getReadyState() {
return state.readyState;
});
defineGetter(Document.prototype, "contentType", function getContentType() {
return state.contentType;
});
defineGetter(Document.prototype, "inputEncoding", function getInputEncoding() {
return state.inputEncoding;
});
defineGetter(Document.prototype, "domain", function getDomain() {
return state.domain;
});
defineGetter(Document.prototype, "characterSet", function getCharacterSet() {
return state.characterSet;
});
defineGetter(Document.prototype, "charset", function getCharset() {
return state.charset;
});
defineGetter(Document.prototype, "hidden", function getHidden() {
return !!state.hidden;
});
defineGetter(Document.prototype, "visibilityState", function getVisibilityState() {
return state.visibilityState;
});
defineGetter(Document.prototype, "cookie", function getCookie() {
return cookieValue;
}, function setCookie(value) {
cookieValue = cookieValue ? cookieValue + "; " + String(value) : String(value);
});
defineGetter(Document.prototype, "head", function getHead() {
return headNode;
});
defineGetter(Document.prototype, "body", function getBody() {
return bodyNode;
});
defineGetter(Document.prototype, "documentElement", function getDocumentElement() {
return htmlNode;
});
defineGetter(Document.prototype, "defaultView", function getDefaultView() {
return globalObject;
});
defineValue(Document.prototype, "createElement", function createElement(tagName) {
const element = createElementNode(tagName);
element.ownerDocument = documentInstance;
return element;
});
defineValue(Document.prototype, "querySelector", function querySelector(selector) {
return queryNodes[selector] || null;
});
defineValue(Document.prototype, "getElementById", function getElementById(id) {
return idNodes[id] || null;
});
defineValue(Document.prototype, "getElementsByTagName", function getElementsByTagName(tagName) {
const key = String(tagName || "").toLowerCase();
const list = tagNodes[key] || [];
return list.slice();
});
const documentAllFallback = {
length: 0,
item: function item() {
return null;
},
namedItem: function namedItem() {
return null;
}
};
defineGetter(Document.prototype, "all", function getAll() {
return documentAllFallback;
});
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(Document, { name: "Document", length: 0 });
protector.repairFunctionMeta(HTMLDocument, { name: "HTMLDocument", length: 0 });
protector.repairFunctionMeta(Document.prototype.createElement, { name: "createElement", length: 1 });
protector.repairFunctionMeta(Document.prototype.querySelector, { name: "querySelector", length: 1 });
protector.repairFunctionMeta(Document.prototype.getElementById, { name: "getElementById", length: 1 });
protector.repairFunctionMeta(Document.prototype.getElementsByTagName, { name: "getElementsByTagName", length: 1 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(Document, "Document");
protector.protectFunction(HTMLDocument, "HTMLDocument");
protector.protectFunction(Document.prototype.createElement, "createElement");
protector.protectFunction(Document.prototype.querySelector, "querySelector");
protector.protectFunction(Document.prototype.getElementById, "getElementById");
protector.protectFunction(Document.prototype.getElementsByTagName, "getElementsByTagName");
}
Object.defineProperty(globalObject, "Document", {
configurable: true,
enumerable: false,
writable: true,
value: Document
});
Object.defineProperty(globalObject, "HTMLDocument", {
configurable: true,
enumerable: false,
writable: true,
value: HTMLDocument
});
Object.defineProperty(globalObject, "document", {
configurable: true,
enumerable: false,
writable: true,
value: documentInstance
});
return documentInstance;
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildDocumentModule(seed = {}) {
const module = scaffoldModule("document-module");
const normalizedSeed = normalizeDocumentSeed(seed);
module.patchPlan = [
"构建 Document / HTMLDocument 构造器、prototype 和 document 实例。",
"把 document.cookie、URL、readyState 等高频字段补成访问器描述符。",
"补出 createElement/querySelector/getElementById/getElementsByTagName,并为 canvas 入口预留到 fingerprint 模块的接线点。",
];
module.patchCode = renderDocumentPatch(normalizedSeed);
module.runtimeState = normalizedSeed;
module.validation = [
"检查 document.URL / document.documentURI / document.readyState。",
"检查 document.cookie getter/setter 是否正常工作。",
"检查 document.createElement('canvas') 是否能接到 fingerprint 模块。",
"检查 querySelector / getElementById / getElementsByTagName 的基本返回。",
"检查 document.all 仍被标注为特殊兼容风险,而不是宣称完全等价。",
];
module.residualRisk = [
"这里只覆盖静态 DOM 壳和常见查询入口,未实现完整 Node/Element/MutationObserver/CSSStyleDeclaration 系统。",
"document.all 仅提供降级 fallback,无法在纯 JS 中完整等价原生特殊对象语义。",
];
return module;
}
module.exports = {
buildDocumentModule,
normalizeDocumentSeed,
};

View File

@@ -0,0 +1,228 @@
const { scaffoldModule } = require("./scaffold-module");
function clone(value) {
return JSON.parse(JSON.stringify(value || {}));
}
function normalizeFingerprintSeed(seed = {}) {
return {
fingerprint: clone(seed.fingerprint || {}),
screen: clone(seed.screen || {}),
windowMetrics: clone(seed.windowMetrics || {}),
battery: clone(seed.battery || {}),
navigator: clone(seed.navigator || {}),
};
}
function renderFingerprintPatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installFingerprintModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
function defineValue(target, key, value) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
writable: true,
value: value
});
}
function defineGetter(target, key, getter) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
get: getter
});
if (protector && protector.protectDescriptor) {
protector.protectDescriptor(target, key);
}
}
function Screen() {}
const screenState = Object.assign({
width: 1920,
height: 1080,
availWidth: 1920,
availHeight: 1040,
colorDepth: 24,
pixelDepth: 24
}, seed.screen || {});
const screenInstance = Object.create(Screen.prototype);
["width", "height", "availWidth", "availHeight", "colorDepth", "pixelDepth", "availLeft", "availTop"].forEach(function eachKey(key) {
defineGetter(Screen.prototype, key, function getScreenValue() {
return screenState[key];
});
});
function createCanvas2DContext() {
return {
fillRect: function fillRect() {},
clearRect: function clearRect() {},
fillText: function fillText() {},
measureText: function measureText(text) {
return { width: String(text || "").length * 7.25 };
}
};
}
function WebGLRenderingContext() {}
defineValue(WebGLRenderingContext.prototype, "getParameter", function getParameter(param) {
if (param === 37445) {
return seed.fingerprint.webglVendor || "Google Inc.";
}
if (param === 37446) {
return seed.fingerprint.webglRenderer || "ANGLE";
}
return null;
});
function createWebGLContext() {
return Object.create(WebGLRenderingContext.prototype);
}
function HTMLCanvasElement() {
this.width = 300;
this.height = 150;
this.__lastContextType__ = "";
}
defineValue(HTMLCanvasElement.prototype, "getContext", function getContext(type) {
this.__lastContextType__ = String(type || "");
if (type === "2d") {
return createCanvas2DContext();
}
if (type === "webgl" || type === "experimental-webgl") {
return createWebGLContext();
}
return null;
});
defineValue(HTMLCanvasElement.prototype, "toDataURL", function toDataURL() {
if (this.__lastContextType__ === "webgl" || this.__lastContextType__ === "experimental-webgl") {
return seed.fingerprint.canvasWebglDataUrl || seed.fingerprint.canvas2dDataUrl || "data:image/png;base64,";
}
return seed.fingerprint.canvas2dDataUrl || "data:image/png;base64,";
});
defineValue(HTMLCanvasElement.prototype, "toBlob", function toBlob(callback) {
if (typeof callback === "function") {
callback({
type: "image/png",
size: (seed.fingerprint.canvas2dDataUrl || "").length
});
}
});
defineValue(globalObject, "__envCreateCanvasElement", function __envCreateCanvasElement() {
return new HTMLCanvasElement();
});
if (globalObject.navigator) {
const navigatorProto = Object.getPrototypeOf(globalObject.navigator);
if (navigatorProto && !Object.getOwnPropertyDescriptor(navigatorProto, "getBattery")) {
defineValue(navigatorProto, "getBattery", function getBattery() {
return Promise.resolve(Object.assign({
charging: true,
chargingTime: 0,
dischargingTime: Infinity,
level: 1
}, seed.battery || {}));
});
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(navigatorProto.getBattery, { name: "getBattery", length: 0 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(navigatorProto.getBattery, "getBattery");
}
}
}
Object.keys(seed.windowMetrics || {}).forEach(function eachMetric(key) {
Object.defineProperty(globalObject, key, {
configurable: true,
enumerable: false,
writable: true,
value: seed.windowMetrics[key]
});
});
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(Screen, { name: "Screen", length: 0 });
protector.repairFunctionMeta(WebGLRenderingContext, { name: "WebGLRenderingContext", length: 0 });
protector.repairFunctionMeta(HTMLCanvasElement, { name: "HTMLCanvasElement", length: 0 });
protector.repairFunctionMeta(HTMLCanvasElement.prototype.getContext, { name: "getContext", length: 1 });
protector.repairFunctionMeta(HTMLCanvasElement.prototype.toDataURL, { name: "toDataURL", length: 0 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(Screen, "Screen");
protector.protectFunction(WebGLRenderingContext, "WebGLRenderingContext");
protector.protectFunction(HTMLCanvasElement, "HTMLCanvasElement");
protector.protectFunction(HTMLCanvasElement.prototype.getContext, "getContext");
protector.protectFunction(HTMLCanvasElement.prototype.toDataURL, "toDataURL");
}
Object.defineProperty(globalObject, "Screen", {
configurable: true,
enumerable: false,
writable: true,
value: Screen
});
Object.defineProperty(globalObject, "screen", {
configurable: true,
enumerable: false,
writable: true,
value: screenInstance
});
Object.defineProperty(globalObject, "WebGLRenderingContext", {
configurable: true,
enumerable: false,
writable: true,
value: WebGLRenderingContext
});
Object.defineProperty(globalObject, "HTMLCanvasElement", {
configurable: true,
enumerable: false,
writable: true,
value: HTMLCanvasElement
});
return {
screen: screenInstance,
HTMLCanvasElement: HTMLCanvasElement
};
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildFingerprintModule(seed = {}) {
const module = scaffoldModule("fingerprint-module");
const normalizedSeed = normalizeFingerprintSeed(seed);
module.patchPlan = [
"构建 Screen、HTMLCanvasElement、WebGLRenderingContext 等高频指纹对象壳。",
"把 canvas 2d / webgl 的入口统一到 getContext/toDataURL/getParameter。",
"把 screen 尺寸、windowMetrics 和 battery Promise 一起收敛到统一指纹种子。",
];
module.patchCode = renderFingerprintPatch(normalizedSeed);
module.runtimeState = normalizedSeed;
module.validation = [
"检查 screen.width / height / availWidth / availHeight。",
"检查 document.createElement('canvas').getContext('2d') / getContext('webgl')。",
"检查 canvas.toDataURL() 与 webgl getParameter(37445/37446)。",
"检查 navigator.getBattery() 返回 Promise 且值来自种子。",
"检查 devicePixelRatio / innerWidth / outerWidth 等窗口尺寸指标。",
];
module.residualRisk = [
"这里只覆盖常见 canvas/WebGL/battery/screen 检测点,未实现音频指纹、字体指纹和 OffscreenCanvas。",
"如果目标站点会校验扩展列表、着色器精度或更复杂的渲染链,仍需继续收集真实浏览器结果。",
];
return module;
}
module.exports = {
buildFingerprintModule,
normalizeFingerprintSeed,
};

View File

@@ -0,0 +1,261 @@
const { scaffoldModule } = require("./scaffold-module");
const { buildPluginGraph } = require("./build-plugin-graph");
function clone(value) {
return JSON.parse(JSON.stringify(value || {}));
}
function normalizeNavigatorSeed(seed = {}) {
const navigatorSeed = seed.navigator && typeof seed.navigator === "object" ? seed.navigator : seed;
const plugins = Array.isArray(seed.plugins) ? clone(seed.plugins) : [];
const mimeTypes = Array.isArray(seed.mimeTypes) ? clone(seed.mimeTypes) : [];
return {
navigator: {
userAgent: navigatorSeed.userAgent || "",
platform: navigatorSeed.platform || "",
vendor: navigatorSeed.vendor || "",
language: navigatorSeed.language || "",
languages: Array.isArray(navigatorSeed.languages) ? navigatorSeed.languages.slice() : [],
webdriver: typeof navigatorSeed.webdriver === "boolean" ? navigatorSeed.webdriver : false,
hardwareConcurrency: Number.isFinite(navigatorSeed.hardwareConcurrency)
? navigatorSeed.hardwareConcurrency
: 8,
deviceMemory: Number.isFinite(navigatorSeed.deviceMemory) ? navigatorSeed.deviceMemory : 8,
maxTouchPoints: Number.isFinite(navigatorSeed.maxTouchPoints) ? navigatorSeed.maxTouchPoints : 0,
cookieEnabled: navigatorSeed.cookieEnabled !== false,
pdfViewerEnabled: navigatorSeed.pdfViewerEnabled !== false,
appCodeName: navigatorSeed.appCodeName || "Mozilla",
appName: navigatorSeed.appName || "Netscape",
appVersion: navigatorSeed.appVersion || navigatorSeed.userAgent || "",
userAgentData: clone(seed.userAgentData || navigatorSeed.userAgentData || null),
connection: clone(seed.connection || navigatorSeed.connection || null),
},
plugins,
mimeTypes,
battery: clone(seed.battery || null),
};
}
function renderNavigatorPatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installNavigatorModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
const pluginSeed = Array.isArray(seed.plugins) ? seed.plugins.slice() : [];
const mimeSeed = Array.isArray(seed.mimeTypes) ? seed.mimeTypes.slice() : [];
function defineValue(target, key, value) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
writable: true,
value: value
});
}
function defineGetter(target, key, getter) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
get: getter
});
if (protector && protector.protectDescriptor) {
protector.protectDescriptor(target, key);
}
}
function createIndexedCollection(items, namedKey) {
const collection = {};
for (let index = 0; index < items.length; index += 1) {
Object.defineProperty(collection, index, {
configurable: true,
enumerable: false,
writable: false,
value: items[index]
});
if (items[index] && items[index][namedKey]) {
Object.defineProperty(collection, items[index][namedKey], {
configurable: true,
enumerable: false,
writable: false,
value: items[index]
});
}
}
defineValue(collection, "length", items.length);
defineValue(collection, "item", function item(index) {
return items[index] || null;
});
defineValue(collection, "namedItem", function namedItem(name) {
return items.find(function findItem(entry) {
return entry && entry[namedKey] === name;
}) || null;
});
defineValue(collection, "refresh", function refresh() {});
return collection;
}
const pluginArray = pluginSeed.map(function mapPlugin(plugin) {
return {
name: plugin.name || "",
filename: plugin.filename || "",
description: plugin.description || "",
length: 0
};
});
const mimeTypeArray = mimeSeed.map(function mapMime(mime) {
return {
type: mime.type || "",
suffixes: mime.suffixes || "",
description: mime.description || "",
enabledPlugin: null
};
});
for (let index = 0; index < mimeSeed.length; index += 1) {
const pluginName = mimeSeed[index].enabledPluginName || "";
const plugin = pluginArray.find(function findPlugin(entry) {
return entry.name === pluginName;
}) || null;
mimeTypeArray[index].enabledPlugin = plugin;
if (plugin) {
const itemIndex = plugin.length;
Object.defineProperty(plugin, itemIndex, {
configurable: true,
enumerable: false,
writable: false,
value: mimeTypeArray[index]
});
plugin.length += 1;
}
}
const plugins = createIndexedCollection(pluginArray, "name");
const mimeTypes = createIndexedCollection(mimeTypeArray, "type");
function Navigator() {}
const navigatorState = seed.navigator || {};
const navigatorInstance = Object.create(Navigator.prototype);
const scalarKeys = [
"userAgent",
"platform",
"vendor",
"language",
"webdriver",
"hardwareConcurrency",
"deviceMemory",
"maxTouchPoints",
"cookieEnabled",
"pdfViewerEnabled",
"appCodeName",
"appName",
"appVersion"
];
scalarKeys.forEach(function defineScalar(key) {
defineGetter(Navigator.prototype, key, function getScalar() {
return navigatorState[key];
});
});
defineGetter(Navigator.prototype, "languages", function getLanguages() {
return Array.isArray(navigatorState.languages) ? navigatorState.languages.slice() : [];
});
defineGetter(Navigator.prototype, "plugins", function getPlugins() {
return plugins;
});
defineGetter(Navigator.prototype, "mimeTypes", function getMimeTypes() {
return mimeTypes;
});
defineGetter(Navigator.prototype, "userAgentData", function getUserAgentData() {
return navigatorState.userAgentData || null;
});
defineGetter(Navigator.prototype, "connection", function getConnection() {
return navigatorState.connection || null;
});
defineValue(Navigator.prototype, "javaEnabled", function javaEnabled() {
return false;
});
defineValue(Navigator.prototype, "getBattery", function getBattery() {
return Promise.resolve(seed.battery || {
charging: true,
chargingTime: 0,
dischargingTime: Infinity,
level: 1
});
});
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(Navigator, { name: "Navigator", length: 0 });
protector.repairFunctionMeta(Navigator.prototype.javaEnabled, { name: "javaEnabled", length: 0 });
protector.repairFunctionMeta(Navigator.prototype.getBattery, { name: "getBattery", length: 0 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(Navigator, "Navigator");
protector.protectFunction(Navigator.prototype.javaEnabled, "javaEnabled");
protector.protectFunction(Navigator.prototype.getBattery, "getBattery");
}
defineValue(navigatorInstance, Symbol.toStringTag, "Navigator");
Object.defineProperty(globalObject, "Navigator", {
configurable: true,
enumerable: false,
writable: true,
value: Navigator
});
Object.defineProperty(globalObject, "navigator", {
configurable: true,
enumerable: false,
writable: true,
value: navigatorInstance
});
return navigatorInstance;
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildNavigatorModule(seed = {}) {
const module = scaffoldModule("navigator-module");
const normalizedSeed = normalizeNavigatorSeed(seed);
const pluginGraph = buildPluginGraph({
plugins: normalizedSeed.plugins,
mimeTypes: normalizedSeed.mimeTypes,
});
module.patchPlan = [
"构建 Navigator 构造器、prototype 和 navigator 实例。",
"把 userAgent/language/webdriver 等高频字段补成 prototype getter,而不是直接灌到实例上。",
"把 plugins/mimeTypes 构造成类数组图结构,并补 getBattery/userAgentData 等高频检测点。",
];
module.patchCode = renderNavigatorPatch(normalizedSeed);
module.runtimeState = {
navigator: normalizedSeed.navigator,
plugins: pluginGraph.pluginArray,
mimeTypes: pluginGraph.mimeTypeArray,
battery: normalizedSeed.battery,
};
module.validation = [
"检查 navigator.webdriver === false。",
"检查 navigator.plugins.length 与 navigator.mimeTypes.length。",
"检查 navigator.plugins.item(0) / namedItem(name) 与 mimeTypes.enabledPlugin 关系。",
"检查 navigator.getBattery() 返回 Promise。",
"检查 Navigator.prototype 上 getter 的描述符与 toString 保护是否生效。",
];
module.residualRisk = [
"这里只覆盖常见字段与图结构,未实现 permissions、mediaDevices、bluetooth 等更深层能力对象。",
"如果目标脚本会校验 brands/mobile/platformVersion 等 userAgentData 细节,仍需真实浏览器种子。",
];
return module;
}
module.exports = {
buildNavigatorModule,
normalizeNavigatorSeed,
};

View File

@@ -0,0 +1,159 @@
const { scaffoldModule } = require("./scaffold-module");
function toFiniteNumber(value, fallback) {
return Number.isFinite(value) ? value : fallback;
}
function cloneRecord(value) {
return Object.assign({}, value || {});
}
function normalizePerformanceSeed(seed = {}) {
const performanceSeed = seed.performance && typeof seed.performance === "object" ? seed.performance : seed;
const navigationStart = toFiniteNumber(performanceSeed.navigationStart, Date.now());
const timeOrigin = toFiniteNumber(performanceSeed.timeOrigin, navigationStart);
const nowOffset = toFiniteNumber(performanceSeed.nowOffset, 12.345);
const timing = cloneRecord(performanceSeed.timing);
if (!Number.isFinite(timing.navigationStart)) timing.navigationStart = navigationStart;
if (!Number.isFinite(timing.fetchStart)) timing.fetchStart = timing.navigationStart;
if (!Number.isFinite(timing.domainLookupStart)) timing.domainLookupStart = timing.fetchStart + 1;
if (!Number.isFinite(timing.domainLookupEnd)) timing.domainLookupEnd = timing.domainLookupStart + 1;
if (!Number.isFinite(timing.connectStart)) timing.connectStart = timing.domainLookupEnd;
if (!Number.isFinite(timing.connectEnd)) timing.connectEnd = timing.connectStart + 2;
if (!Number.isFinite(timing.requestStart)) timing.requestStart = timing.connectEnd + 1;
if (!Number.isFinite(timing.responseStart)) timing.responseStart = timing.requestStart + 4;
if (!Number.isFinite(timing.responseEnd)) timing.responseEnd = timing.responseStart + 4;
if (!Number.isFinite(timing.domLoading)) timing.domLoading = timing.responseEnd + 2;
if (!Number.isFinite(timing.domInteractive)) timing.domInteractive = timing.domLoading + 5;
if (!Number.isFinite(timing.domContentLoadedEventStart)) timing.domContentLoadedEventStart = timing.domInteractive + 1;
if (!Number.isFinite(timing.domContentLoadedEventEnd)) timing.domContentLoadedEventEnd = timing.domContentLoadedEventStart + 1;
if (!Number.isFinite(timing.domComplete)) timing.domComplete = timing.domContentLoadedEventEnd + 5;
if (!Number.isFinite(timing.loadEventStart)) timing.loadEventStart = timing.domComplete + 1;
if (!Number.isFinite(timing.loadEventEnd)) timing.loadEventEnd = timing.loadEventStart + 1;
return {
timeOrigin,
navigationStart,
nowOffset,
timing,
memory: cloneRecord(performanceSeed.memory),
};
}
function renderPerformancePatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installPerformanceModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
const baseEpoch = Date.now();
const monotonicStart = seed.nowOffset;
let tick = 0;
function nextNow() {
tick += 1;
return monotonicStart + (Date.now() - baseEpoch) + tick * 0.01;
}
function now() {
return Number(nextNow().toFixed(3));
}
function clearMarks() {
return undefined;
}
function clearMeasures() {
return undefined;
}
function getEntries() {
return [];
}
function getEntriesByType() {
return [];
}
function getEntriesByName() {
return [];
}
const performanceObject = {
now: now,
timeOrigin: seed.timeOrigin,
timing: seed.timing,
clearMarks: clearMarks,
clearMeasures: clearMeasures,
getEntries: getEntries,
getEntriesByType: getEntriesByType,
getEntriesByName: getEntriesByName,
mark: function mark() {},
measure: function measure() {}
};
if (seed.memory && Object.keys(seed.memory).length) {
performanceObject.memory = seed.memory;
}
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(now, { name: "now", length: 0 });
protector.repairFunctionMeta(clearMarks, { name: "clearMarks", length: 0 });
protector.repairFunctionMeta(clearMeasures, { name: "clearMeasures", length: 0 });
protector.repairFunctionMeta(getEntries, { name: "getEntries", length: 0 });
protector.repairFunctionMeta(getEntriesByType, { name: "getEntriesByType", length: 1 });
protector.repairFunctionMeta(getEntriesByName, { name: "getEntriesByName", length: 1 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(now, "now");
protector.protectFunction(clearMarks, "clearMarks");
protector.protectFunction(clearMeasures, "clearMeasures");
protector.protectFunction(getEntries, "getEntries");
protector.protectFunction(getEntriesByType, "getEntriesByType");
protector.protectFunction(getEntriesByName, "getEntriesByName");
}
Object.defineProperty(globalObject, "performance", {
configurable: true,
enumerable: false,
writable: true,
value: performanceObject
});
return performanceObject;
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildPerformanceModule(seed = {}) {
const module = scaffoldModule("performance-module");
const normalizedSeed = normalizePerformanceSeed(seed);
module.patchPlan = [
"构建 timeOrigin/navigationStart/timing 基础树。",
"补出单调递增的 performance.now()。",
"为 getEntries/getEntriesByType/getEntriesByName 等常见检测点提供稳定返回。",
];
module.patchCode = renderPerformancePatch(normalizedSeed);
module.runtimeState = {
performance: normalizedSeed,
};
module.validation = [
"连续两次调用 performance.now(),确认返回值递增。",
"检查 performance.timeOrigin 与 performance.timing.navigationStart 的相对关系。",
"检查 performance.getEntries()/getEntriesByType()/getEntriesByName() 返回数组。",
"检查 performance.now.toString() 以及 name/length 元信息。",
];
module.residualRisk = [
"未实现 PerformanceObserver、resource timing、navigation entries 等复杂链路。",
"如果目标脚本会校验 performance.memory 或多阶段时间戳分布,仍需采集真实浏览器种子。",
];
return module;
}
module.exports = {
buildPerformanceModule,
normalizePerformanceSeed,
};

View File

@@ -0,0 +1,47 @@
function buildPluginGraph(seed = {}) {
const plugins = Array.isArray(seed.plugins) ? seed.plugins : [];
const mimeTypes = Array.isArray(seed.mimeTypes) ? seed.mimeTypes : [];
const pluginArray = [];
const mimeTypeArray = [];
for (const pluginSeed of plugins) {
const plugin = {
name: pluginSeed.name || "",
filename: pluginSeed.filename || "",
description: pluginSeed.description || "",
length: 0,
items: [],
};
pluginArray.push(plugin);
}
for (const mimeSeed of mimeTypes) {
const mime = {
type: mimeSeed.type || "",
suffixes: mimeSeed.suffixes || "",
description: mimeSeed.description || "",
enabledPluginName: mimeSeed.enabledPluginName || "",
enabledPlugin: null,
};
mimeTypeArray.push(mime);
}
for (const mime of mimeTypeArray) {
const plugin = pluginArray.find((item) => item.name === mime.enabledPluginName);
if (plugin) {
mime.enabledPlugin = plugin;
plugin.items.push(mime);
plugin.length = plugin.items.length;
}
}
return {
pluginArray,
mimeTypeArray,
};
}
module.exports = {
buildPluginGraph,
};

View File

@@ -0,0 +1,151 @@
const { scaffoldModule } = require("./scaffold-module");
const { mergeStorageState } = require("./merge-storage-state");
function normalizeStorageSeed(seed = {}) {
const merged = mergeStorageState(seed);
return {
cookie: seed.cookie || "",
cookies: merged.cookies,
localStorage: merged.localStorage,
sessionStorage: merged.sessionStorage,
};
}
function renderStoragePatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installStorageModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
function defineValue(target, key, value) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
writable: true,
value: value
});
}
function Storage() {}
defineValue(Storage.prototype, "key", function key(index) {
const keys = Object.keys(this.__store__);
return typeof keys[index] === "string" ? keys[index] : null;
});
defineValue(Storage.prototype, "getItem", function getItem(name) {
const key = String(name);
return Object.prototype.hasOwnProperty.call(this.__store__, key) ? this.__store__[key] : null;
});
defineValue(Storage.prototype, "setItem", function setItem(name, value) {
this.__store__[String(name)] = String(value);
this.length = Object.keys(this.__store__).length;
});
defineValue(Storage.prototype, "removeItem", function removeItem(name) {
delete this.__store__[String(name)];
this.length = Object.keys(this.__store__).length;
});
defineValue(Storage.prototype, "clear", function clear() {
this.__store__ = Object.create(null);
this.length = 0;
});
function createStorage(initialState) {
const storage = Object.create(Storage.prototype);
defineValue(storage, "__store__", Object.assign(Object.create(null), initialState || {}));
defineValue(storage, "length", Object.keys(storage.__store__).length);
Object.keys(storage.__store__).forEach(function defineEntry(key) {
Object.defineProperty(storage, key, {
configurable: true,
enumerable: true,
get: function getEntry() {
return storage.getItem(key);
},
set: function setEntry(value) {
storage.setItem(key, value);
}
});
});
return storage;
}
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(Storage, { name: "Storage", length: 0 });
protector.repairFunctionMeta(Storage.prototype.key, { name: "key", length: 1 });
protector.repairFunctionMeta(Storage.prototype.getItem, { name: "getItem", length: 1 });
protector.repairFunctionMeta(Storage.prototype.setItem, { name: "setItem", length: 2 });
protector.repairFunctionMeta(Storage.prototype.removeItem, { name: "removeItem", length: 1 });
protector.repairFunctionMeta(Storage.prototype.clear, { name: "clear", length: 0 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(Storage, "Storage");
protector.protectFunction(Storage.prototype.key, "key");
protector.protectFunction(Storage.prototype.getItem, "getItem");
protector.protectFunction(Storage.prototype.setItem, "setItem");
protector.protectFunction(Storage.prototype.removeItem, "removeItem");
protector.protectFunction(Storage.prototype.clear, "clear");
}
const localStorage = createStorage(seed.localStorage || {});
const sessionStorage = createStorage(seed.sessionStorage || {});
Object.defineProperty(globalObject, "Storage", {
configurable: true,
enumerable: false,
writable: true,
value: Storage
});
Object.defineProperty(globalObject, "localStorage", {
configurable: true,
enumerable: false,
writable: true,
value: localStorage
});
Object.defineProperty(globalObject, "sessionStorage", {
configurable: true,
enumerable: false,
writable: true,
value: sessionStorage
});
if (globalObject.document && typeof globalObject.document.cookie === "string" && seed.cookie) {
globalObject.document.cookie = seed.cookie;
}
return {
localStorage: localStorage,
sessionStorage: sessionStorage
};
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildStorageModule(seed = {}) {
const module = scaffoldModule("storage-module");
const normalizedSeed = normalizeStorageSeed(seed);
module.patchPlan = [
"构建 Storage 构造器与原型方法。",
"按真实种子预置 localStorage / sessionStorage 的 key/value 运行态。",
"把 cookie 字符串与 document.cookie 入口联动,而不是把 storage 状态和 cookie 割裂开。",
];
module.patchCode = renderStoragePatch(normalizedSeed);
module.runtimeState = normalizedSeed;
module.validation = [
"检查 localStorage.length / sessionStorage.length。",
"检查 getItem/setItem/removeItem/clear/key 的返回值。",
"检查 localStorage.device_id 这类同名属性访问是否与 getItem 一致。",
"检查 document 已存在时 cookie 字符串能否同步注入。",
];
module.residualRisk = [
"未实现配额异常、跨 tab 事件同步与 storage event。",
"如果站点依赖历史态的更新时间、写入顺序或事件广播,仍需继续扩展。",
];
return module;
}
module.exports = {
buildStorageModule,
normalizeStorageSeed,
};

View File

@@ -0,0 +1,191 @@
const { scaffoldModule } = require("./scaffold-module");
function normalizeRtcCandidate(candidate = {}) {
return {
candidate: candidate.candidate || "candidate:842163049 1 udp 1677729535 0.0.0.0 9 typ host generation 0 ufrag env network-id 1",
sdpMid: candidate.sdpMid || "0",
sdpMLineIndex: Number.isFinite(candidate.sdpMLineIndex) ? candidate.sdpMLineIndex : 0,
usernameFragment: candidate.usernameFragment || "env",
};
}
function normalizeWebrtcSeed(seed = {}) {
const webrtcSeed = seed.webrtc && typeof seed.webrtc === "object" ? seed.webrtc : seed;
return {
allowCandidateLeak: webrtcSeed.allowCandidateLeak === true,
iceTransportPolicy: webrtcSeed.iceTransportPolicy || "all",
bundlePolicy: webrtcSeed.bundlePolicy || "balanced",
rtcpMuxPolicy: webrtcSeed.rtcpMuxPolicy || "require",
defaultOfferSdp: webrtcSeed.defaultOfferSdp || "v=0\\r\\no=- 0 0 IN IP4 127.0.0.1\\r\\ns=env-codex-offer\\r\\nt=0 0\\r\\n",
defaultAnswerSdp: webrtcSeed.defaultAnswerSdp || "v=0\\r\\no=- 0 0 IN IP4 127.0.0.1\\r\\ns=env-codex-answer\\r\\nt=0 0\\r\\n",
iceCandidates: Array.isArray(webrtcSeed.iceCandidates) && webrtcSeed.iceCandidates.length
? webrtcSeed.iceCandidates.map(normalizeRtcCandidate)
: [normalizeRtcCandidate()],
};
}
function renderWebrtcPatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installWebrtcModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
function defineValue(target, key, value) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
writable: true,
value: value
});
}
function applyEventTarget(target) {
target._listeners = Object.create(null);
target.addEventListener = function addEventListener(type, listener) {
if (!this._listeners[type]) this._listeners[type] = [];
this._listeners[type].push(listener);
};
target.removeEventListener = function removeEventListener(type, listener) {
const list = this._listeners[type] || [];
this._listeners[type] = list.filter(function filterItem(item) {
return item !== listener;
});
};
target.dispatchEvent = function dispatchEvent(event) {
const list = (this._listeners[event.type] || []).slice();
for (const listener of list) listener.call(this, event);
const handler = this["on" + event.type];
if (typeof handler === "function") handler.call(this, event);
return true;
};
}
function RTCSessionDescription(init) {
init = init || {};
this.type = init.type || "offer";
this.sdp = init.sdp || "";
}
function RTCIceCandidate(init) {
init = init || {};
this.candidate = init.candidate || "";
this.sdpMid = init.sdpMid || null;
this.sdpMLineIndex = typeof init.sdpMLineIndex === "number" ? init.sdpMLineIndex : null;
this.usernameFragment = init.usernameFragment || null;
}
function RTCDataChannel(label, dataChannelDict) {
applyEventTarget(this);
this.label = label || "";
this.ordered = !dataChannelDict || dataChannelDict.ordered !== false;
this.readyState = "open";
}
RTCDataChannel.prototype.send = function send(data) {
this.lastPayload = data;
};
RTCDataChannel.prototype.close = function close() {
this.readyState = "closed";
};
function RTCPeerConnection(configuration) {
applyEventTarget(this);
this.configuration = Object.assign({
iceTransportPolicy: seed.iceTransportPolicy,
bundlePolicy: seed.bundlePolicy,
rtcpMuxPolicy: seed.rtcpMuxPolicy
}, configuration || {});
this.localDescription = null;
this.remoteDescription = null;
this.connectionState = "new";
this.iceConnectionState = "new";
this.signalingState = "stable";
}
RTCPeerConnection.prototype.createOffer = function createOffer() {
return Promise.resolve(new RTCSessionDescription({ type: "offer", sdp: seed.defaultOfferSdp }));
};
RTCPeerConnection.prototype.createAnswer = function createAnswer() {
return Promise.resolve(new RTCSessionDescription({ type: "answer", sdp: seed.defaultAnswerSdp }));
};
RTCPeerConnection.prototype.setLocalDescription = function setLocalDescription(description) {
this.localDescription = description || null;
this.signalingState = "have-local-offer";
if (seed.allowCandidateLeak) {
for (const candidateSeed of seed.iceCandidates) {
this.dispatchEvent({ type: "icecandidate", candidate: new RTCIceCandidate(candidateSeed) });
}
}
return Promise.resolve();
};
RTCPeerConnection.prototype.setRemoteDescription = function setRemoteDescription(description) {
this.remoteDescription = description || null;
this.signalingState = "stable";
return Promise.resolve();
};
RTCPeerConnection.prototype.addIceCandidate = function addIceCandidate(candidate) {
this.lastIceCandidate = candidate || null;
return Promise.resolve();
};
RTCPeerConnection.prototype.createDataChannel = function createDataChannel(label, dataChannelDict) {
return new RTCDataChannel(label, dataChannelDict);
};
RTCPeerConnection.prototype.close = function close() {
this.connectionState = "closed";
this.iceConnectionState = "closed";
};
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(RTCSessionDescription, { name: "RTCSessionDescription", length: 1 });
protector.repairFunctionMeta(RTCIceCandidate, { name: "RTCIceCandidate", length: 1 });
protector.repairFunctionMeta(RTCPeerConnection, { name: "RTCPeerConnection", length: 1 });
protector.repairFunctionMeta(RTCDataChannel, { name: "RTCDataChannel", length: 2 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(RTCSessionDescription, "RTCSessionDescription");
protector.protectFunction(RTCIceCandidate, "RTCIceCandidate");
protector.protectFunction(RTCPeerConnection, "RTCPeerConnection");
protector.protectFunction(RTCDataChannel, "RTCDataChannel");
}
defineValue(globalObject, "RTCSessionDescription", RTCSessionDescription);
defineValue(globalObject, "RTCIceCandidate", RTCIceCandidate);
defineValue(globalObject, "RTCPeerConnection", RTCPeerConnection);
defineValue(globalObject, "webkitRTCPeerConnection", RTCPeerConnection);
defineValue(globalObject, "RTCDataChannel", RTCDataChannel);
return { RTCPeerConnection: RTCPeerConnection, RTCDataChannel: RTCDataChannel };
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildWebrtcModule(seed = {}) {
const module = scaffoldModule("webrtc-module");
const normalizedSeed = normalizeWebrtcSeed(seed);
module.patchPlan = [
"补出 RTCPeerConnection / RTCSessionDescription / RTCIceCandidate / RTCDataChannel 构造器。",
"为 createOffer/createAnswer/setLocalDescription/addIceCandidate/createDataChannel 提供 Promise 型基础实现。",
"将 ICE candidate 是否泄露做成显式策略,而不是默认暴露本地网络信息。",
];
module.patchCode = renderWebrtcPatch(normalizedSeed);
module.runtimeState = { webrtc: normalizedSeed };
module.validation = [
"检查 typeof RTCPeerConnection === 'function'。",
"执行 new RTCPeerConnection().createOffer(),确认返回 Promise。",
"执行 createDataChannel('env'),确认返回对象且包含 send/close。",
"在 allowCandidateLeak=false 时确认不会主动抛出真实候选信息。",
];
module.residualRisk = [
"这里只覆盖存在性检测和轻量行为检测,不提供真实音视频协商能力。",
"若目标站点会校验 stats/transceiver/sender/receiver/track,仍需按真实浏览器继续扩展。",
];
return module;
}
module.exports = {
buildWebrtcModule,
normalizeWebrtcSeed,
};

View File

@@ -0,0 +1,195 @@
const { scaffoldModule } = require("./scaffold-module");
function normalizeWorkerSeed(seed = {}) {
const workerSeed = seed.worker && typeof seed.worker === "object" ? seed.worker : seed;
return {
scriptBaseUrl: workerSeed.scriptBaseUrl || "https://example.invalid/assets/",
inlineScripts: Object.assign({}, workerSeed.inlineScripts || {}),
sharedWorkerName: workerSeed.sharedWorkerName || "env-shared-worker",
broadcastChannels: Array.isArray(workerSeed.broadcastChannels)
? workerSeed.broadcastChannels.slice()
: ["env-sync"],
};
}
function renderWorkerPatch(seed) {
const serializedSeed = JSON.stringify(seed, null, 2);
return `(
function installWorkerModule(globalObject) {
const seed = ${serializedSeed};
const protector = globalObject.__envNativeProtector || null;
const sharedChannels = Object.create(null);
function applyEventTarget(target) {
target._listeners = Object.create(null);
target.addEventListener = function addEventListener(type, listener) {
if (!this._listeners[type]) this._listeners[type] = [];
this._listeners[type].push(listener);
};
target.removeEventListener = function removeEventListener(type, listener) {
const list = this._listeners[type] || [];
this._listeners[type] = list.filter(function filterItem(item) {
return item !== listener;
});
};
target.dispatchEvent = function dispatchEvent(event) {
const list = (this._listeners[event.type] || []).slice();
for (const listener of list) listener.call(this, event);
const handler = this["on" + event.type];
if (typeof handler === "function") handler.call(this, event);
return true;
};
}
function createMessageEvent(data) {
return { type: "message", data: data };
}
function MessagePort() {
applyEventTarget(this);
this._entangledPort = null;
this._started = false;
}
MessagePort.prototype.postMessage = function postMessage(data) {
if (this._entangledPort) this._entangledPort.dispatchEvent(createMessageEvent(data));
};
MessagePort.prototype.start = function start() {
this._started = true;
};
MessagePort.prototype.close = function close() {
this._entangledPort = null;
this._started = false;
};
function createMessageChannel() {
const port1 = new MessagePort();
const port2 = new MessagePort();
port1._entangledPort = port2;
port2._entangledPort = port1;
return { port1: port1, port2: port2 };
}
function Worker(scriptUrl) {
applyEventTarget(this);
this.scriptURL = scriptUrl || "";
this.baseUrl = seed.scriptBaseUrl;
this.inlineSource = seed.inlineScripts[scriptUrl] || "";
}
Worker.prototype.postMessage = function postMessage(data) {
this.dispatchEvent(createMessageEvent(data));
};
Worker.prototype.terminate = function terminate() {
this._terminated = true;
};
function SharedWorker(scriptUrl, name) {
applyEventTarget(this);
this.scriptURL = scriptUrl || "";
this.name = name || seed.sharedWorkerName;
const channel = createMessageChannel();
this.port = channel.port1;
this._workerPort = channel.port2;
}
function BroadcastChannel(name) {
applyEventTarget(this);
this.name = name || "";
if (!sharedChannels[this.name]) sharedChannels[this.name] = [];
sharedChannels[this.name].push(this);
}
BroadcastChannel.prototype.postMessage = function postMessage(data) {
const peers = sharedChannels[this.name] || [];
for (const peer of peers) {
if (peer !== this) peer.dispatchEvent(createMessageEvent(data));
}
};
BroadcastChannel.prototype.close = function close() {
const peers = sharedChannels[this.name] || [];
sharedChannels[this.name] = peers.filter(function filterPeer(item) {
return item !== this;
}, this);
};
if (protector && protector.repairFunctionMeta) {
protector.repairFunctionMeta(MessagePort, { name: "MessagePort", length: 0 });
protector.repairFunctionMeta(Worker, { name: "Worker", length: 1 });
protector.repairFunctionMeta(SharedWorker, { name: "SharedWorker", length: 2 });
protector.repairFunctionMeta(BroadcastChannel, { name: "BroadcastChannel", length: 1 });
}
if (protector && protector.protectFunction) {
protector.protectFunction(MessagePort, "MessagePort");
protector.protectFunction(Worker, "Worker");
protector.protectFunction(SharedWorker, "SharedWorker");
protector.protectFunction(BroadcastChannel, "BroadcastChannel");
}
Object.defineProperty(globalObject, "MessagePort", {
configurable: true,
enumerable: false,
writable: true,
value: MessagePort
});
Object.defineProperty(globalObject, "MessageChannel", {
configurable: true,
enumerable: false,
writable: true,
value: function MessageChannel() {
return createMessageChannel();
}
});
Object.defineProperty(globalObject, "Worker", {
configurable: true,
enumerable: false,
writable: true,
value: Worker
});
Object.defineProperty(globalObject, "SharedWorker", {
configurable: true,
enumerable: false,
writable: true,
value: SharedWorker
});
Object.defineProperty(globalObject, "BroadcastChannel", {
configurable: true,
enumerable: false,
writable: true,
value: BroadcastChannel
});
return { Worker: Worker, SharedWorker: SharedWorker, BroadcastChannel: BroadcastChannel };
})(typeof globalThis !== "undefined" ? globalThis : window);
`;
}
function buildWorkerModule(seed = {}) {
const module = scaffoldModule("worker-module");
const normalizedSeed = normalizeWorkerSeed(seed);
module.patchPlan = [
"补出 Worker/SharedWorker/MessagePort/MessageChannel/BroadcastChannel 构造器。",
"用最小事件系统模拟 postMessage/onmessage 通路。",
"把 worker 脚本路径、inline source、广播频道名称外置为种子,而不是写死在补丁里。",
];
module.patchCode = renderWorkerPatch(normalizedSeed);
module.runtimeState = { worker: normalizedSeed };
module.validation = [
"检查 typeof Worker/SharedWorker/BroadcastChannel === 'function'。",
"执行 new Worker('/worker.js').postMessage({ping:1}),确认 message 事件能被触发。",
"执行 new SharedWorker('/shared.js').port.postMessage('x'),确认端口存在。",
"执行两个同名 BroadcastChannel 互发消息,确认消息广播通路正常。",
];
module.residualRisk = [
"这里只模拟消息通路,不提供真实线程、importScripts、URL.createObjectURL 等完整能力。",
"若目标站点依赖 Worker 全局作用域、自定义事件循环或二进制消息通路,仍需继续扩展。",
];
return module;
}
module.exports = {
buildWorkerModule,
normalizeWorkerSeed,
};

View File

@@ -0,0 +1,146 @@
/*
Browser-side environment collector template.
Reconstructed from public env-harvesting patterns, especially boda_jsEnv's
"changeDom.js", but normalized into a structured JSON exporter.
*/
(function collectBrowserEnv() {
function safeCall(fn, fallback) {
try {
return fn();
} catch (err) {
return fallback;
}
}
function dumpStorage(storage) {
const out = {};
if (!storage) {
return out;
}
for (let i = 0; i < storage.length; i += 1) {
const key = storage.key(i);
out[key] = storage.getItem(key);
}
return out;
}
function dumpNavigator() {
return {
userAgent: safeCall(() => navigator.userAgent, ""),
platform: safeCall(() => navigator.platform, ""),
vendor: safeCall(() => navigator.vendor, ""),
language: safeCall(() => navigator.language, ""),
languages: safeCall(() => Array.from(navigator.languages || []), []),
webdriver: safeCall(() => navigator.webdriver, undefined),
hardwareConcurrency: safeCall(() => navigator.hardwareConcurrency, undefined),
deviceMemory: safeCall(() => navigator.deviceMemory, undefined),
maxTouchPoints: safeCall(() => navigator.maxTouchPoints, undefined),
cookieEnabled: safeCall(() => navigator.cookieEnabled, undefined),
pdfViewerEnabled: safeCall(() => navigator.pdfViewerEnabled, undefined),
userAgentData: safeCall(() => navigator.userAgentData, null),
connection: safeCall(() => navigator.connection, null),
webkitPersistentStorage: safeCall(() => navigator.webkitPersistentStorage, null),
webkitTemporaryStorage: safeCall(() => navigator.webkitTemporaryStorage, null),
};
}
function dumpDocument() {
return {
URL: safeCall(() => document.URL, ""),
referrer: safeCall(() => document.referrer, ""),
documentURI: safeCall(() => document.documentURI, ""),
compatMode: safeCall(() => document.compatMode, ""),
dir: safeCall(() => document.dir, ""),
title: safeCall(() => document.title, ""),
designMode: safeCall(() => document.designMode, ""),
readyState: safeCall(() => document.readyState, ""),
contentType: safeCall(() => document.contentType, ""),
inputEncoding: safeCall(() => document.inputEncoding, ""),
domain: safeCall(() => document.domain, ""),
characterSet: safeCall(() => document.characterSet, ""),
charset: safeCall(() => document.charset, ""),
hidden: safeCall(() => document.hidden, undefined),
cookie: safeCall(() => document.cookie, ""),
};
}
function dumpScreen() {
return {
width: safeCall(() => screen.width, undefined),
height: safeCall(() => screen.height, undefined),
availWidth: safeCall(() => screen.availWidth, undefined),
availHeight: safeCall(() => screen.availHeight, undefined),
availLeft: safeCall(() => screen.availLeft, undefined),
availTop: safeCall(() => screen.availTop, undefined),
colorDepth: safeCall(() => screen.colorDepth, undefined),
pixelDepth: safeCall(() => screen.pixelDepth, undefined),
isExtended: safeCall(() => screen.isExtended, undefined),
orientation: safeCall(() => screen.orientation, null),
};
}
function dumpWindowMetrics() {
return {
devicePixelRatio: safeCall(() => window.devicePixelRatio, undefined),
innerWidth: safeCall(() => window.innerWidth, undefined),
innerHeight: safeCall(() => window.innerHeight, undefined),
outerWidth: safeCall(() => window.outerWidth, undefined),
outerHeight: safeCall(() => window.outerHeight, undefined),
isSecureContext: safeCall(() => window.isSecureContext, undefined),
};
}
function collectCanvasFingerprint() {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
if (!ctx) {
return { canvas2d: null, webgl: null };
}
ctx.textBaseline = "top";
ctx.font = "14px Arial";
ctx.fillStyle = "#f60";
ctx.fillRect(10, 10, 120, 40);
ctx.fillStyle = "#069";
ctx.fillText("env-codex", 14, 16);
const canvas2d = safeCall(() => canvas.toDataURL(), null);
let webgl = null;
const gl = safeCall(() => canvas.getContext("webgl") || canvas.getContext("experimental-webgl"), null);
if (gl) {
webgl = {
vendor: safeCall(() => gl.getParameter(37445), null),
renderer: safeCall(() => gl.getParameter(37446), null),
};
}
return { canvas2d, webgl };
}
const payload = {
timestamp: new Date().toISOString(),
location: {
href: safeCall(() => location.href, ""),
origin: safeCall(() => location.origin, ""),
protocol: safeCall(() => location.protocol, ""),
host: safeCall(() => location.host, ""),
hostname: safeCall(() => location.hostname, ""),
pathname: safeCall(() => location.pathname, ""),
search: safeCall(() => location.search, ""),
hash: safeCall(() => location.hash, ""),
},
history: {
length: safeCall(() => history.length, undefined),
scrollRestoration: safeCall(() => history.scrollRestoration, undefined),
},
navigator: dumpNavigator(),
document: dumpDocument(),
screen: dumpScreen(),
windowMetrics: dumpWindowMetrics(),
localStorage: dumpStorage(window.localStorage),
sessionStorage: dumpStorage(window.sessionStorage),
fingerprint: collectCanvasFingerprint(),
};
console.log(JSON.stringify(payload, null, 2));
return payload;
})();

View File

@@ -0,0 +1,32 @@
function parseCookieString(cookieString) {
const out = {};
if (!cookieString) {
return out;
}
for (const item of String(cookieString).split(";")) {
const trimmed = item.trim();
if (!trimmed) {
continue;
}
const idx = trimmed.indexOf("=");
if (idx === -1) {
out[trimmed] = "";
continue;
}
out[trimmed.slice(0, idx)] = trimmed.slice(idx + 1);
}
return out;
}
function mergeStorageState(input = {}) {
return {
cookies: parseCookieString(input.cookie || ""),
localStorage: Object.assign({}, input.localStorage || {}),
sessionStorage: Object.assign({}, input.sessionStorage || {}),
};
}
module.exports = {
mergeStorageState,
parseCookieString,
};

View File

@@ -0,0 +1,155 @@
/*
Runtime observer template.
Derived from qxVm/catvm proxy ideas, but rewritten into a reusable logger that
aggregates missing paths, descriptor probes, and invocation traces.
*/
function createRuntimeObserver(config = {}) {
const logs = [];
const seen = new WeakMap();
const maxDepth = config.maxDepth || 4;
const logOpen = config.logOpen !== false;
const skip = new Set(config.skip || ["toString", "__proto__"]);
function record(type, path, detail) {
const entry = { type, path, detail };
logs.push(entry);
if (logOpen) {
console.log("[observer]", type, path, detail);
}
return entry;
}
function summarize() {
const missingPaths = [];
const descriptorAccess = [];
const invocationErrors = [];
const prototypeAccess = [];
for (const log of logs) {
if (log.type === "missing") {
missingPaths.push(log.path);
} else if (log.type === "descriptor") {
descriptorAccess.push(log.path);
} else if (log.type === "invoke-error") {
invocationErrors.push(log.path);
} else if (log.type === "prototype") {
prototypeAccess.push(log.path);
}
}
return {
total: logs.length,
missingPaths,
descriptorAccess,
invocationErrors,
prototypeAccess,
logs,
};
}
function wrap(target, name = "root", depth = 0) {
if (!target || (typeof target !== "object" && typeof target !== "function")) {
return target;
}
if (seen.has(target)) {
return seen.get(target);
}
if (depth > maxDepth) {
return target;
}
const proxy = new Proxy(target, {
get(t, key, receiver) {
const path = name + "." + String(key);
let value;
try {
value = Reflect.get(t, key, receiver);
} catch (err) {
record("invoke-error", path, err && err.message ? err.message : String(err));
throw err;
}
if (!skip.has(String(key))) {
if (typeof value === "undefined") {
record("missing", path, "undefined");
} else {
record("get", path, typeof value);
}
}
if (value && (typeof value === "object" || typeof value === "function")) {
return wrap(value, path, depth + 1);
}
return value;
},
set(t, key, value, receiver) {
const path = name + "." + String(key);
record("set", path, typeof value);
return Reflect.set(t, key, value, receiver);
},
has(t, key) {
const path = name + "." + String(key);
record("has", path, Reflect.has(t, key));
return Reflect.has(t, key);
},
deleteProperty(t, key) {
const path = name + "." + String(key);
record("delete", path, true);
return Reflect.deleteProperty(t, key);
},
getOwnPropertyDescriptor(t, key) {
const path = name + "." + String(key);
const result = Reflect.getOwnPropertyDescriptor(t, key);
record("descriptor", path, result ? Object.keys(result) : null);
return result;
},
defineProperty(t, key, attrs) {
const path = name + "." + String(key);
record("define", path, Object.keys(attrs || {}));
return Reflect.defineProperty(t, key, attrs);
},
getPrototypeOf(t) {
record("prototype", name, "getPrototypeOf");
return Reflect.getPrototypeOf(t);
},
setPrototypeOf(t, proto) {
record("prototype", name, "setPrototypeOf");
return Reflect.setPrototypeOf(t, proto);
},
ownKeys(t) {
record("ownKeys", name, Reflect.ownKeys(t));
return Reflect.ownKeys(t);
},
isExtensible(t) {
record("isExtensible", name, Reflect.isExtensible(t));
return Reflect.isExtensible(t);
},
preventExtensions(t) {
record("preventExtensions", name, true);
return Reflect.preventExtensions(t);
},
apply(t, thisArg, args) {
record("apply", name, args);
return Reflect.apply(t, thisArg, args);
},
construct(t, args, newTarget) {
record("construct", name, args);
return Reflect.construct(t, args, newTarget);
},
});
seen.set(target, proxy);
return proxy;
}
return {
wrap,
summarize,
logs,
};
}
module.exports = {
createRuntimeObserver,
};

View File

@@ -0,0 +1,94 @@
/*
Native protection template.
Derived from qxVm/catvm safe-function patterns and normalized into a reusable
module for protecting functions, getters, and setters.
*/
function installNativeProtector() {
const rawToString = Function.toString;
const nativeSymbol = Symbol("native_toString_" + Math.random().toString(36).slice(2));
function defineHidden(target, key, value) {
Object.defineProperty(target, key, {
configurable: true,
enumerable: false,
writable: true,
value,
});
}
function markNative(fn, displayName) {
if (typeof fn !== "function") {
return fn;
}
const name = displayName || fn.name || "";
defineHidden(fn, nativeSymbol, "function " + name + "() { [native code] }");
return fn;
}
function protectedToString() {
if (typeof this === "function" && this[nativeSymbol]) {
return this[nativeSymbol];
}
return rawToString.call(this);
}
defineHidden(Function.prototype, "toString", protectedToString);
defineHidden(Function, "toString", protectedToString);
markNative(Function.prototype.toString, "toString");
function protectFunction(fn, displayName) {
return markNative(fn, displayName);
}
function protectDescriptor(target, key) {
const desc = Object.getOwnPropertyDescriptor(target, key);
if (!desc) {
return false;
}
if (typeof desc.get === "function") {
markNative(desc.get, "get " + key);
}
if (typeof desc.set === "function") {
markNative(desc.set, "set " + key);
}
if (typeof desc.value === "function") {
markNative(desc.value, desc.value.name || key);
}
return true;
}
function repairFunctionMeta(fn, meta = {}) {
if (typeof fn !== "function") {
return fn;
}
if (Object.prototype.hasOwnProperty.call(meta, "name")) {
Object.defineProperty(fn, "name", {
configurable: true,
enumerable: false,
writable: false,
value: meta.name,
});
}
if (Object.prototype.hasOwnProperty.call(meta, "length")) {
Object.defineProperty(fn, "length", {
configurable: true,
enumerable: false,
writable: false,
value: meta.length,
});
}
return fn;
}
return {
nativeSymbol,
protectFunction,
protectDescriptor,
repairFunctionMeta,
};
}
module.exports = {
installNativeProtector,
};

View File

@@ -0,0 +1,14 @@
function scaffoldModule(name) {
return {
name,
patchPlan: [],
patchCode: "",
runtimeState: {},
validation: [],
residualRisk: [],
};
}
module.exports = {
scaffoldModule,
};