From 681a7a0072881c72eb24639a44e8e2f769712c39 Mon Sep 17 00:00:00 2001 From: Libor Polčák Date: Aug 11 2021 14:55:15 +0000 Subject: Merge wrapper insertion into iframes, workers --- diff --git a/chrome/manifest.json b/chrome/manifest.json index c86ed8d..7fc7281 100644 --- a/chrome/manifest.json +++ b/chrome/manifest.json @@ -43,6 +43,7 @@ "match_origin_as_fallback": true, "js": [ "nscl/lib/browser-polyfill.js", + "nscl/common/uuid.js", "nscl/content/patchWindow.js", "nscl/lib/sha256.js", "alea.js", diff --git a/common/code_builders.js b/common/code_builders.js index a181b2f..214f25d 100644 --- a/common/code_builders.js +++ b/common/code_builders.js @@ -23,7 +23,7 @@ * Create IIFE to wrap the code in closure */ function enclose_wrapping(code, ...args) { - return `try{(function(...args) {${code}})(${args});} catch {}`; + return `try{(function(...args) {${code}})(${args});} catch (e) {console.error(e)}`; } /** @@ -43,8 +43,14 @@ function enclose_wrapping2(code, name, params, call_with_window) { * a function in the page context. */ function define_page_context_function(wrapper) { - var originalF = wrapper["original_function"] || `${wrapper.parent_object}.${wrapper.parent_object_property}`; - return enclose_wrapping2(`var originalF = ${originalF}; + let {parent_object, parent_object_property, original_function, replace_original_function} = wrapper; + if (replace_original_function) { + let lastDot = original_function.lastIndexOf("."); + parent_object = original_function.substring(0, lastDot); + parent_object_property = original_function.substring(lastDot + 1); + } + let originalF = original_function || `${parent_object}.${parent_object_property}`; + return enclose_wrapping2(`let originalF = ${originalF}; var replacementF = function(${wrapper.wrapping_function_args}) { // This comment is needed to correctly differentiate wrappers with the same body // by the toString() wrapper @@ -53,7 +59,7 @@ function define_page_context_function(wrapper) { // ${gen_random32()} ${wrapper.wrapping_function_body} }; - ${wrapper.replace_original_function ? wrapper.original_function : `${wrapper.parent_object}.${wrapper.parent_object_property}`} = replacementF; + exportFunction(replacementF, ${parent_object}, {defineAs: '${parent_object_property}'}); original_functions[replacementF.toString()] = originalF.toString(); ${wrapper.post_replacement_code || ''} `, wrapper.wrapping_code_function_name, wrapper.wrapping_code_function_params, wrapper.wrapping_code_function_call_window); @@ -63,12 +69,14 @@ function define_page_context_function(wrapper) { * This function creates code that assigns an already defined function to given property. */ function generate_assign_function_code(code_spec_obj) { - return `${code_spec_obj.parent_object}.${code_spec_obj.parent_object_property} = ${code_spec_obj.export_function_name}; - ` + return `exportFunction(${code_spec_obj.export_function_name}, + ${code_spec_obj.parent_object}, + {defineAs: '${code_spec_obj.parent_object_property}'}); + `; } /** - * This function wraps object properties using Object.defineProperties. + * This function wraps object properties using ObjForPage.defineProperties(). */ function generate_object_properties(code_spec_obj) { var code = ` @@ -79,7 +87,7 @@ function generate_object_properties(code_spec_obj) { } `; for (assign of code_spec_obj.wrapped_objects) { - code += `var ${assign.wrapped_name} = ${assign.original_name};`; + code += `var ${assign.wrapped_name} = window.${assign.original_name};`; } code += `descriptor = Object.getOwnPropertyDescriptor( ${code_spec_obj.parent_object}, "${code_spec_obj.parent_object_property}"); @@ -102,7 +110,7 @@ function generate_object_properties(code_spec_obj) { } `; } - code += `Object.defineProperty(${code_spec_obj.parent_object}, + code += `ObjForPage.defineProperty(${code_spec_obj.parent_object}, "${code_spec_obj.parent_object_property}", descriptor); `; return code; @@ -119,7 +127,7 @@ function generate_delete_properties(code_spec_obj) { if ("${prop}" in ${code_spec_obj.parent_object}) { // Delete only properties that are available. // The if should be safe to be deleted but it can possibly reduce fingerprintability - Object.defineProperty( + ObjForPage.defineProperty( ${code_spec_obj.parent_object}, "${prop}", {get: undefined, set: undefined, configurable: false, enumerable: false} ); @@ -150,7 +158,7 @@ var build_code = function(wrapper, ...args) { var code = `try {if (${wrapper.parent_object} === undefined) {return;}} catch (e) {return; /* It seems that the parent object does not exist */ }`; for (wrapped of wrapper.wrapped_objects) { code += ` - var ${wrapped.wrapped_name} = ${wrapped.original_name}; + var ${wrapped.wrapped_name} = window.${wrapped.original_name}; if (${wrapped.wrapped_name} === undefined) { // Do not wrap an object that is not defined, e.g. because it is experimental feature. // This should reduce fingerprintability. @@ -174,9 +182,11 @@ var build_code = function(wrapper, ...args) { } } if (wrapper["wrapper_prototype"] !== undefined) { - code += `Object.setPrototypeOf(${wrapper.parent_object}.${wrapper.parent_object_property}, - ${wrapper.wrapper_prototype}); - ` + let target = `${wrapper.parent_object}.${wrapper.parent_object_property}`; + let source = wrapper.wrapper_prototype; + code += `if (${target.prototype} !== ${source.prototype}) { // prevent cyclic __proto__ errors on Proxy + Object.setPrototypeOf(${target}, ${source}); + }`; } code += ` if (!${wrapper.nofreeze}) { @@ -195,20 +205,78 @@ function wrap_code(wrappers) { if (wrappers.length === 0) { return; // Nothing to wrap } - var code = `(function() { - var original_functions = {}; - `; - for (tobewrapped of wrappers) { + + let build = wrapper => { try { - code += build_code(build_wrapping_code[tobewrapped[0]], tobewrapped.slice(1)); - } - catch (e) { + return build_code(build_wrapping_code[wrapper[0]], wrapper.slice(1)); + } catch (e) { console.log(e); + return ""; } - } - code += ` - var originalToStringF = Function.prototype.toString; - var originalToStringStr = Function.prototype.toString(); + }; + + let code = (w => { + let original_functions = {}; + let xrayWindow = window; + let ObjForPage, forPage; + { + + let pageStuff = new WeakSet(); + + forPage = obj => { + if (obj === null || pageStuff.has(obj)) return obj; + let ret = cloneInto(obj, unwrappedWindow, {cloneFunctions: true, wrapReflectors: true}); + try { + pageStuff.add(ret); + } catch (e) { + // non-reference type? + } + return ret; + } + + let fixProp = (d, prop, obj) => { + for (let accessor of ["set", "get"]) { + if (typeof d[accessor] === "function") { + d[accessor] = exportFunction(d[accessor], obj, {defineAs: `${accessor} ${prop}`}); + } + } + if (typeof d.value === "object") d.value = forPage(d.value); + return d; + }; + + ObjForPage = { + make: obj => forPage(obj), + promise: obj => xrayWindow.Promise.resolve(forPage(obj)), + defineProperty(obj, prop, descriptor, ...args) { + if (obj.wrappedJSObject) obj = obj.wrappedJSObject; + return Object.defineProperty(obj, prop, fixProp(descriptor, prop, obj), ...args); + }, + defineProperties(obj, descriptors, ...args) { + if (obj.wrappedJSObject) obj = obj.wrappedJSObject; + for (let [prop, d] of Object.entries(descriptors)) { + fixProp(d, prop, obj); + } + return Object.defineProperties(obj.wrappedJSObject || obj, descriptors, ...args); + }, + create(proto, descriptor) { + let obj = forPage(Object.create(proto.wrappedJSObject || proto)); + return descriptor ? this.defineProperties(obj, descriptors) && obj : obj; + } + }; + } + + with(unwrappedWindow) { + let window = unwrappedWindow; + + try { + // WRAPPERS // + + } finally { + // cleanup environment if necessary + } + + let originalToStringF = Function.prototype.toString; + let originalToStringStr = Function.prototype.toString(); Function.prototype.toString = function() { var currentString = originalToStringF.call(this); var originalStr = original_functions[currentString]; @@ -220,7 +288,12 @@ function wrap_code(wrappers) { } }; original_functions[Function.prototype.toString.toString()] = originalToStringStr; - })();`; - return code; + } + }).toString().replace('// WRAPPERS //', + wrappers.map(build) + .join("\n") + .replace(/\bObject\.(create|definePropert)/g, "ObjForPage.$1")); + + return `(${code})();`; } diff --git a/common/document_start.js b/common/document_start.js index 1243e13..4c988da 100644 --- a/common/document_start.js +++ b/common/document_start.js @@ -22,7 +22,9 @@ // along with this program. If not, see . // -function configureInjection({code, wrappers, ffbug1267027, domainHash}) { +var wrappersPort; + +function configureInjection({code, wrappers, ffbug1267027, domainHash, sessionHash}) { console.debug("configureInjection", new Error().stack, document.readyState); configureInjection = () => false; // one shot if(browser.extension.inIncognitoContext){ @@ -37,7 +39,7 @@ function configureInjection({code, wrappers, ffbug1267027, domainHash}) { ${code} })()`; - injectScript(aleaCode, wrappers, ffbug1267027); + wrappersPort = patchWindow(aleaCode); return true; } if ("configuration" in window) { diff --git a/common/wrappingS-AJAX.js b/common/wrappingS-AJAX.js index 30deed7..2af745a 100644 --- a/common/wrappingS-AJAX.js +++ b/common/wrappingS-AJAX.js @@ -40,7 +40,7 @@ wrapping_function_body: ` var currentXMLHttpRequestObject = new originalXMLHttpRequest(); var originalXMLHttpRequestOpenFunction = currentXMLHttpRequestObject.open; - currentXMLHttpRequestObject.open = function(...args) { + currentXMLHttpRequestObject.open = exportFunction(function(...args) { if (blockEveryXMLHttpRequest || (confirmEveryXMLHttpRequest && !confirm('There is a XMLHttpRequest on URL ' + args[1] + '. Do you want to continue?'))) { currentXMLHttpRequestObject.send = function () {}; // Prevents throwing an exception return undefined; @@ -48,7 +48,7 @@ else { return originalXMLHttpRequestOpenFunction.call(this, ...args); } - }; + }, currentXMLHttpRequestObject, {defineAs: "open"}); return currentXMLHttpRequestObject; `, }, diff --git a/common/wrappingS-ECMA-ARRAY.js b/common/wrappingS-ECMA-ARRAY.js index 6a29547..b1d4be5 100644 --- a/common/wrappingS-ECMA-ARRAY.js +++ b/common/wrappingS-ECMA-ARRAY.js @@ -170,7 +170,7 @@ function packF32(v) { function constructDecorator(wrapped) { return function () { - const res = wrapped.apply(originalF, arguments); + const res = forPage(wrapped).apply(originalF, arguments); return replacementF(res); } } @@ -192,7 +192,7 @@ function offsetDecorator(wrapped, type, proxyRef, offsetF) { if (type === 3) { res = new this.__proto__.constructor(this)[wrapped.name.split(' ')[1]]() } else { - res = wrapped.apply(this, arguments); + res = forPage(wrapped).apply(this, arguments); } // Create copy of new arr let secArr = []; @@ -248,16 +248,18 @@ var proxyHandler = `{ var random_idx = Math.floor(Math.random() * target['length']); // Load random index from array var rand_val = target[random_idx]; + /* let proto_keys = ['buffer', 'byteLength', 'byteOffset', 'length']; if (proto_keys.indexOf(key) >= 0) { return target[key]; } + */ // offsetF argument needs to be in array range if (typeof key !== 'symbol' && Number(key) >= 0 && Number(key) < target.length) { key = offsetF(key) } - let value = Reflect.get(...arguments); - return typeof value == 'function' ? value.bind(target) : value; + let value = target[key] + return typeof value == 'function' ? forPage(value.bind(forPage(target))) : typeof value === "object" ? forPage(value) : value; }, set(target, key, value) { var random_idx = Math.floor(Math.random() * (target['length'])); @@ -267,7 +269,7 @@ var proxyHandler = `{ if (typeof key !== 'symbol' && Number(key) >= 0 && Number(key) < target.length) { key = offsetF(key) } - return Reflect.set(...arguments); + return target[key] = value; } }`; @@ -552,7 +554,7 @@ function redefineDataViewFunctions(target, offsetF, doMapping) { } } let _target = target; - var proxy = new newProxy(_data, ${proxyHandler}); + var proxy = new newProxy(forPage(_data), forPage(${proxyHandler})); // Proxy has to support all methods, original object supports. ${offsetDecorator}; ${redefineNewArrayFunctions}; @@ -634,7 +636,7 @@ function redefineDataViewFunctions(target, offsetF, doMapping) { wrapped_objects: [], helping_code:` let doMapping = args[0]; - var proxyHandler = ${proxyHandler}; + var proxyHandler = forPage(${proxyHandler}); function gcd(x, y) { while(y) { var t = y; @@ -646,16 +648,16 @@ function redefineDataViewFunctions(target, offsetF, doMapping) { const is_proxy = Symbol('is_proxy'); const originalProxy = Proxy; - var proxyHandler = { + var proxyHandler = forPage({ has (target, key) { return (is_proxy === key) || (key in target); } - }; - let newProxy = new Proxy(Proxy, { + }); + let newProxy = new Proxy(Proxy, forPage({ construct(target, args) { - return new originalProxy(new target(...args), proxyHandler); + return new originalProxy(new target(...args), forPage(proxyHandler)); } - }); + })); `, wrapping_function_args: `target`, wrapping_function_body: common_function_body, diff --git a/common/wrappingS-ECMA-DATE.js b/common/wrappingS-ECMA-DATE.js index fffb5d6..6d1cf6a 100644 --- a/common/wrappingS-ECMA-DATE.js +++ b/common/wrappingS-ECMA-DATE.js @@ -68,7 +68,7 @@ parent_object: "window.Date", parent_object_property: "now", wrapping_function_args: "", - wrapping_function_body: "return func(originalDateConstructor.now.call(Date), precision);", + wrapping_function_body: "return func(originalF.call(Date), precision);", }, { code_type: "function_export", diff --git a/common/wrappingS-MCS.js b/common/wrappingS-MCS.js index 2c04267..5571297 100644 --- a/common/wrappingS-MCS.js +++ b/common/wrappingS-MCS.js @@ -66,7 +66,7 @@ return devices; } else if(args[0] == 2){ - return new Promise((resolve) => resolve([])); + return ObjForPage.promise([]); } } /** @@ -122,7 +122,7 @@ wrapped_name: "origEnumerateDevices", }], helping_code: farbleEnumerateDevices+shuffleArray+deviceRandomString+randomString+fakeDevice+` - if(args[0]==0){ + if(args[0]==0){ var devices = origEnumerateDevices.call(navigator.mediaDevices); devices.then(function(result) { shuffleArray(result); diff --git a/common/wrappingS-NP.js b/common/wrappingS-NP.js index 29babf2..eaa072b 100644 --- a/common/wrappingS-NP.js +++ b/common/wrappingS-NP.js @@ -182,6 +182,7 @@ } } } + Object.defineProperty(ret, 'length', { value: counter }); diff --git a/common/wrappingS-WEBGL.js b/common/wrappingS-WEBGL.js index fa87be9..c4a4693 100644 --- a/common/wrappingS-WEBGL.js +++ b/common/wrappingS-WEBGL.js @@ -238,7 +238,7 @@ */ function farbleNullArray(name, ctx, ...fcarg) { if(args[0]===1) { - return []; + return new window.Array; } else if(args[0]===0) { return name.call(ctx, ...fcarg); diff --git a/firefox/manifest.json b/firefox/manifest.json index 3beade5..120d05f 100644 --- a/firefox/manifest.json +++ b/firefox/manifest.json @@ -40,6 +40,7 @@ "all_frames": true, "match_about_blank": true, "js": [ + "nscl/common/uuid.js", "nscl/content/patchWindow.js", "nscl/lib/sha256.js", "alea.js", diff --git a/nscl b/nscl index c4458ef..dfc0868 160000 --- a/nscl +++ b/nscl @@ -1 +1 @@ -Subproject commit c4458ef291e12c35fd458ea7d0cd519238efcd3e +Subproject commit dfc08684def491e3a4c88f2aab29af01ba2081d0