diff --git a/xilriws-proxy/background.js b/xilriws-proxy/background.js index e1c92e4..846b9d5 100644 --- a/xilriws-proxy/background.js +++ b/xilriws-proxy/background.js @@ -1,4 +1,3 @@ -const STEALTH_JS = "" const ws = new WebSocket('ws://127.0.0.1:9091'); let currentProxyCreds = { diff --git a/xilriws-targetfp/background.js b/xilriws-targetfp/background.js index 241da44..f2057ed 100644 --- a/xilriws-targetfp/background.js +++ b/xilriws-targetfp/background.js @@ -7,4 +7,4 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { sendResponse(sender.tab.id) } return true -}) \ No newline at end of file +}) diff --git a/xilriws-targetfp/contentScript.js b/xilriws-targetfp/contentScript.js index eecdace..a93f9f1 100644 --- a/xilriws-targetfp/contentScript.js +++ b/xilriws-targetfp/contentScript.js @@ -2,5 +2,10 @@ const tabId = await chrome.runtime.sendMessage("getTabId") const div = document.createElement("div") div.setAttribute("data-xil-tab-id", tabId) + + while (!document.body) { + console.log("body is null, waiting 0.1s") + await new Promise(resolve => setTimeout(resolve, 100)) + } document.body.appendChild(div) })() \ No newline at end of file diff --git a/xilriws-targetfp/general.js b/xilriws-targetfp/general.js index 05954e3..ddf9d49 100644 --- a/xilriws-targetfp/general.js +++ b/xilriws-targetfp/general.js @@ -28,8 +28,45 @@ const englishes = [ "en-GB" ] +const timezones = [ + 0, 1, 2, 3, 4, 5, 6, 8, 9, -4, -5, -6, -7 +] + export function block() { utils.overwriteProp(navigator, "platform", "Win32") + utils.overwriteProp(navigator, "doNotTrack", utils.randomChoose(["unknown", "unknown", "1"])) + utils.overwriteProp(navigator, "maxTouchPoints", utils.randomChoose([0, 5, 10, 20])) + utils.overwriteProp(navigator, "productSub", "20030107") + utils.overwriteProp(navigator.connection, "rtt", utils.randomChoose([undefined, 0, 50, 100])) + utils.overwriteProp(navigator, "hardwareConcurrency", utils.randomChoose([4, 8, 12, 16, 24, 32])) + + utils.overwriteProp(window.history, "length", utils.randomNumber(1, 5)) + + const timezone = utils.randomChoose(timezones) * -60 + utils.overwriteProp(Date.prototype, "getTimezoneOffset", () => timezone) + utils.overwriteProp(Navigator.prototype, "mimeTypes", { + 0: { + suffixes: "pdf", + type: "application/pdf", + enabledPlugin: { filename: "internal-pdf-viewer" }, + }, + 1: { + suffixes: "pdf", + type: "text/pdf", + enabledPlugin: { filename: "internal-pdf-viewer" }, + }, + "application/pdf": { + suffixes: "pdf", + type: "application/pdf", + enabledPlugin: { filename: "internal-pdf-viewer" }, + }, + "text/pdf": { + suffixes: "pdf", + type: "text/pdf", + enabledPlugin: { filename: "internal-pdf-viewer" }, + }, + + }) // language const baseLanguage = utils.randomChoose(baseLanguages) @@ -45,7 +82,6 @@ export function block() { } console.log("languages are " + languages.join(",")) - // TODO: seed Math.random using the tabId (or the url possibly) - the content script will be reloaded by the iframe. in some cases (like the language) this can be detcted by the script utils.overwriteProp(navigator, "language", baseLanguage) utils.overwriteProp(navigator, "languages", languages) } \ No newline at end of file diff --git a/xilriws-targetfp/inject.js b/xilriws-targetfp/inject.js index 0791f00..39e1dca 100644 --- a/xilriws-targetfp/inject.js +++ b/xilriws-targetfp/inject.js @@ -1,19 +1,48 @@ (async () => { "use strict" + HTMLIFrameElement.prototype.addEventListener = async function (eventType, callback) { + if (eventType !== "load") { + return + } + + let fpLoaded = false + while (!fpLoaded) { + console.log("waiting for plugin load") + await new Promise(resolve => setTimeout(resolve, 200)) + if (this.contentDocument) { + fpLoaded = this.contentDocument.fpLoaded + } + } + callback() + } + const utils = await import("./utils.js") const screen = await import("./screen.js") const general = await import("./general.js") const canvas = await import("./canvas.js") + const webgl = await import("./webgl.js") - const div = document.querySelector("[data-xil-tab-id]") + let div = null + while (!div) { + div = document.querySelector("[data-xil-tab-id]") + if (!div) { + console.log("didn't find seed element, waiting 0.1s") + await new Promise(resolve => setTimeout(resolve, 100)) + } + } const seed = div.getAttribute("data-xil-tab-id") document.body.removeChild(div) utils.setSeed(seed) - screen.block() - general.block() - canvas.block() + utils.sendWs("debug:seed", seed) + + // screen.block() + // general.block() + // canvas.block() + // webgl.block() + + document.fpLoaded = true })() diff --git a/xilriws-targetfp/manifest.json b/xilriws-targetfp/manifest.json index a6c7c8b..bccb3da 100644 --- a/xilriws-targetfp/manifest.json +++ b/xilriws-targetfp/manifest.json @@ -92,6 +92,14 @@ "matches": [ "" ] + }, + { + "resources": [ + "webgl.js" + ], + "matches": [ + "" + ] } ] } diff --git a/xilriws-targetfp/screen.js b/xilriws-targetfp/screen.js index 9cd5996..6f7a379 100644 --- a/xilriws-targetfp/screen.js +++ b/xilriws-targetfp/screen.js @@ -66,4 +66,94 @@ export function block() { utils.overwriteProp(window, "screenX", 0) utils.overwriteProp(window, "screenY", 0) utils.overwriteProp(window, "devicePixelRatio", 1) + + utils.overwriteProp(window.visualViewport, "scale", 1) + utils.overwriteProp(window.visualViewport, "width", utils.randomNumber(150, screenWidth - 100)) + utils.overwriteProp(window.visualViewport, "height", utils.randomNumber(150, screenHeight - 100)) + + // mouse events + let mouseEventsActive = true + let currentMouseX = utils.randomNumber(1, screenWidth) + let currentMouseY = utils.randomNumber(1, screenHeight - 60) + let mouseOutCallback = null + let mouseOverCallback = null + + async function randomMouseOver() { + console.log("faking mouseover & mouseout") + let max = 2 + + while (mouseEventsActive && max > 0) { + max -= 1 + await new Promise(resolve => setTimeout(resolve, utils.randomNumber(50, 150))) + const eventData = { + clientX: currentMouseX, + clientY: currentMouseY, + screenX: currentMouseX, + screenY: currentMouseY - 13 + } + + mouseOutCallback(new MouseEvent("mouseout", eventData)) + mouseOverCallback(new MouseEvent("mouseover", eventData)) + } + } + + /** + * @param {(event: MouseEvent) => {}} callback + */ + async function randomMouseMove(callback) { + console.log("faking mousemove") + let max = 4 + + while (mouseEventsActive && max > 0) { + max -= 1 + await new Promise(resolve => setTimeout(resolve, utils.randomNumber(50, 200))) + + currentMouseX += utils.randomNumber(2, 20) + currentMouseY += utils.randomNumber(4, 27) + callback(new MouseEvent("mousemove", { + clientX: currentMouseX, + clientY: currentMouseY, + screenX: currentMouseX, + screenY: currentMouseY - 13 + })) + } + } + + const anyMouseEvents = utils.randomNumber(0, 10) > 2 + const anyMouseOver = utils.randomNumber(0, 10) > 2 + + const originalAddEventListener = Document.prototype.addEventListener + const originalRemoveEventListener = Document.prototype.removeEventListener + + Document.prototype.addEventListener = function(eventType, callback) { + if (!anyMouseEvents) { + return + } + + if (eventType === "mousemove") { + randomMouseMove(callback).then() + } + + if (anyMouseOver && eventType === "mouseout") { + mouseOutCallback = callback + + if (mouseOverCallback) { + randomMouseOver().then() + } + } + if (anyMouseOver && eventType === "mouseover") { + mouseOverCallback = callback + + if (mouseOutCallback) { + randomMouseOver().then() + } + } + + return originalAddEventListener.bind(this, eventType, callback)() + } + + Document.prototype.removeEventListener = function(eventType, callback) { + mouseEventsActive = false + return originalRemoveEventListener.bind(this, eventType, callback)() + } } \ No newline at end of file diff --git a/xilriws-targetfp/utils.js b/xilriws-targetfp/utils.js index 1446a2d..56a5c30 100644 --- a/xilriws-targetfp/utils.js +++ b/xilriws-targetfp/utils.js @@ -1,5 +1,13 @@ +const ws = new WebSocket('ws://127.0.0.1:9091'); + let random = null +export function sendWs(action, detail = null) { + try { + ws.send(JSON.stringify({action: action, detail: detail})) + } catch (e) {} +} + export function setSeed(newSeed) { random = seededRandom(newSeed) } @@ -29,11 +37,6 @@ export function overwriteProp(object, propName, propValue) { }); } - -export function overwriteFunc(object, funcName, func) { - object[funcName] = func -} - /** * @template T * @param {T[]} array diff --git a/xilriws-targetfp/webgl.js b/xilriws-targetfp/webgl.js new file mode 100644 index 0000000..f2637db --- /dev/null +++ b/xilriws-targetfp/webgl.js @@ -0,0 +1,56 @@ +import * as utils from "./utils.js" +import {randomChoose} from "./utils.js"; + +export function block() { + const glProto = WebGLRenderingContext.prototype + + utils.overwriteProp(glProto, "getSupportedExtensions", () => ["ANGLE_instanced_arrays","EXT_blend_minmax","EXT_clip_control","EXT_color_buffer_half_float","EXT_depth_clamp","EXT_disjoint_timer_query","EXT_float_blend","EXT_frag_depth","EXT_polygon_offset_clamp","EXT_shader_texture_lod","EXT_texture_compression_bptc","EXT_texture_compression_rgtc","EXT_texture_filter_anisotropic","EXT_texture_mirror_clamp_to_edge","EXT_sRGB","KHR_parallel_shader_compile","OES_element_index_uint","OES_fbo_render_mipmap","OES_standard_derivatives","OES_texture_float","OES_texture_float_linear","OES_texture_half_float","OES_texture_half_float_linear","OES_vertex_array_object","WEBGL_blend_func_extended","WEBGL_color_buffer_float","WEBGL_compressed_texture_s3tc","WEBGL_compressed_texture_s3tc_srgb","WEBGL_debug_renderer_info","WEBGL_debug_shaders","WEBGL_depth_texture","WEBGL_draw_buffers","WEBGL_lose_context","WEBGL_multi_draw","WEBGL_polygon_mode"]) + + const originalGetParameter = glProto.getParameter + glProto.getParameter = function (parameter) { + if (parameter === glProto.MAX_VERTEX_UNIFORM_VECTORS) { + return utils.randomChoose([127, 128, 255, 256, 511, 512, 1023, 1024, 2047, 2048, 4095, 4096]) + } else if (parameter === glProto.MAX_VIEWPORT_DIMS) { + return utils.randomChoose([[16384, 16384], [32767, 32767], [65536, 65536]]) + } else if (parameter === glProto.RENDERER) { + return utils.randomChoose(["WebKit WebGL", "WebKit WebGL", "WebKit WebGL", "WebKit WebGL", "ANGLE (Microsoft, Microsoft Basic Render Driver Direct3D11 vs_5_0 ps_5_0), or similar", "ANGLE (Intel, Intel(R) HD Graphics Direct3D11 vs_5_0 ps_5_0), or similar", "Adreno (TM) 650, or similar"]) + } else { + const debug = this.getExtension("WEBGL_debug_renderer_info") + if (debug) { + if (parameter === debug.UNMASKED_VENDOR_WEBGL) { + return utils.randomChoose([ + "Google Inc. (Microsoft)", "Google Inc. (Intel)", "Google Inc. (NVIDIA Corporation)", "Google Inc. (ARM)", "Google Inc. (NVIDIA)", "Google Inc. (AMD)" + ]) + } else if (parameter === debug.UNMASKED_RENDERER_WEBGL) { + let randomHex = "" + for (let i = 0; i < 4; i++) { + randomHex += utils.randomChoose(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E"]) + } + const graphicsCard = utils.randomChoose([ + "NVIDIA, NVIDIA GeForce MX450", + "NVIDIA, NVIDIA GeForce 710M", + "NVIDIA, NVIDIA GeForce RTX 2050", + "NVIDIA, NVIDIA GeForce GTX 950M", + "Intel, Intel(R) UHD Graphics 620", + "Intel, Intel(R) HD Graphics 630", + "Intel, Intel(R) UHD Graphics", + "Intel, Intel(R) Iris(R) Xe Graphics", + "AMD, Radeon RX 570 Series", + "AMD, Radeon R9 380 Series", + "AMD, Radeon X800 Series", + ]) + + return "ANGLE(" + graphicsCard + " (0x0000" + randomHex + ") Direct3D11 vs_5_0 ps_5_0, D3D11)" + } + } + } + return originalGetParameter.bind(this, parameter)() + } + + utils.overwriteProp(glProto.getParameter, "name", "getParameter") + // utils.overwriteProp(glProto.getParameter, "toString", () => "function getParameter() { [native code] }") + + Function.prototype.toString = (func) => { + return "function () { [native code] }" + } +} \ No newline at end of file