From 426049c437546260735911a475620dd62f145bf4 Mon Sep 17 00:00:00 2001 From: PoneyClairDeLune <88174309+PoneyClairDeLune@users.noreply.github.com> Date: Mon, 4 Jul 2022 06:21:19 +0000 Subject: [PATCH] Interception of screen capture. --- svc/minuette.js | 98 ++++++++++++++++++++++++++++++++++++-- svc/minuette/fakeStream.js | 21 ++++++++ 2 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 svc/minuette/fakeStream.js diff --git a/svc/minuette.js b/svc/minuette.js index 79bec1a..072b4c8 100644 --- a/svc/minuette.js +++ b/svc/minuette.js @@ -1,10 +1,12 @@ "use strict"; import {ics} from "./ics.js"; import {fakeNative} from "./minuette/fakeNative.js"; -import {getCSSSelector} from "./minuette/cssSelector.js" +import {getCSSSelector} from "./minuette/cssSelector.js"; import {getRandom} from "./minuette/getRandom.js"; -import {getEventFamily, getEventData} from "./minuette/eventData.js" +import {getEventFamily, getEventData} from "./minuette/eventData.js"; +import {fakeScreen, fakeCamera} from "./minuette/fakeStream.js"; self.blacklistEvent = ["visibilitychange"]; +self.fakeScreenVideo = undefined; { // Constants const selectorSkip = ["id", "class"], @@ -52,8 +54,39 @@ self.blacklistEvent = ["visibilitychange"]; fakeNative(HTMLElement.prototype.addEventListener); HTMLElement.prototype.addEventListener = fakeAddEl; document.addEventListener = fakeAddEl; - // Hijack several document APIs - let realDocument = self.document; + // Hijack screen reading + let realScreen = self.screen, fakeScreenWidth, fakeScreenHeight; + self.screen = new Proxy(self.screen, {get: function (obj, prop) { + switch (prop) { + case "availHeight": + case "height": { + return fakeScreenHeight; + break; + }; + case "availWidth": + case "width": { + return fakeScreenWidth; + break; + }; + case "orientation": { + return new Proxy(realScreen.orientation, {get: function (obj, prop) { + if (prop == "type") { + return (screen.width / screen.height < 1 ? "portrait" : "landscape"); + } else { + return obj[prop]; + }; + }}); + break; + }; + default: { + return obj[prop]; + }; + }; + }}); + /*screen.orientation = */ + let navMedDev = navigator.mediaDevices, + getDispMed = navMedDev.getDisplayMedia, + getUserMed = navMedDev.getUserMedia; // Extension channel message let extChannel = new BroadcastChannel("-ReplaceMeWithSomethingUnique-"); extChannel.onmessage = function (msg) { @@ -72,5 +105,62 @@ self.blacklistEvent = ["visibilitychange"]; }; }; }; + // Hijack display capture + navigator.mediaDevices.getDisplayMedia = async function () { + let stream; + try { + stream = await getDispMed.apply(navMedDev, arguments); + } catch (err) { + extChannel.postMessage({e: "getDisplay", success: false}); + throw(err); + }; + // Hijack stream + let inFocus = true, captureExhaust = 0; + let fakeVideoSource = document.createElement("video"); + fakeVideoSource.srcObject = stream; + fakeVideoSource.muted = true; + fakeVideoSource.autoplay = true; + fakeVideoSource.style = "display: none"; + addEL.apply(document, ["blur", function () { + inFocus = false; + captureExhaust = 2; + fakeVideoSource.pause(); + ics.debug("Window lost focus."); + }]); + addEL.apply(document, ["focus", function () { + inFocus = true; + fakeVideoSource.play(); + captureExhaust = 5; + ics.debug("Window regain focus."); + }]); + document.body.appendChild(fakeVideoSource); + let fakeVideoCanvas = document.createElement("canvas"); + fakeVideoCanvas.width = screen.width; + fakeVideoCanvas.height = screen.height; + fakeVideoCanvas.style = "display: none"; + document.body.appendChild(fakeVideoCanvas); + let fakeCanvasContext = fakeVideoCanvas.getContext("2d"); + fakeCanvasContext.fillStyle = "#777"; + fakeCanvasContext.fillRect(0, 0, screen.width, screen.height); + addEL.apply(fakeVideoSource, ["playing", function () { + fakeScreenWidth = fakeVideoSource.videoWidth, + fakeScreenHeight = fakeVideoSource.videoHeight; + fakeVideoCanvas.width = fakeVideoSource.videoWidth; + fakeVideoCanvas.height = fakeVideoSource.videoHeight; + fakeCanvasContext.drawImage(fakeVideoSource, 0, 0); + }]); + let proxyScreen = setInterval(function () { + if (!document.hidden && inFocus && !fakeVideoSource.paused) { + if (captureExhaust < 1) { + fakeCanvasContext.drawImage(fakeVideoSource, 0, 0); + } else { + captureExhaust --; + }; + }; + }, 50); + let newStream = fakeVideoCanvas.captureStream(60); + extChannel.postMessage({e: "getDisplay", success: true, stream: stream.id, task: proxyScreen}); + return newStream; + }; }; ics.debug("Minuette launched."); \ No newline at end of file diff --git a/svc/minuette/fakeStream.js b/svc/minuette/fakeStream.js new file mode 100644 index 0000000..3419832 --- /dev/null +++ b/svc/minuette/fakeStream.js @@ -0,0 +1,21 @@ +"use strict"; +let fakeScreenGetter = function (obj, prop) { + if (prop != "label") { + return obj[prop]; + } else { + return "Primary Monitor"; // This may change due to language or browser. This is on Firefox. + }; +}; +// I'm a bummer +let fakeScreen = function (stream) { + stream.forEach(function (e, i, a) { + a[i] = new Proxy(e, fakeScreenGetter); + }); + return stream; +}; +let fakeCamera = function (stream) {}; + +export { + fakeScreen, + fakeCamera +}; \ No newline at end of file