Disclosure Statement: This site contains affiliate links, which means that I may receive a commission if you make a purchase using these links. As an eBay Partner, I earn from qualifying purchases.
If you find these projects useful please consider becoming a sponsor with Patreon, GitHub or Liberapay.

Can't override canvas drawing correctly

sodlf159
Posts: 90
Joined: Thu Nov 09, 2023 1:55 pm

Re: Can't override canvas drawing correctly

Post by sodlf159 »

OK
Last edited by sodlf159 on Mon Oct 21, 2024 5:42 pm, edited 1 time in total.
Fvert
Posts: 19
Joined: Fri Oct 22, 2021 10:22 am

Re: Can't override canvas drawing correctly

Post by Fvert »

sodlf159 wrote: Thu Oct 17, 2024 3:06 pm ....
Something worked, only at Chromium1BeforeResourceLoad event, only after page refresh and only on this site https://pixelscan.net/ and only with this script.

Code: Select all

(function() {
    const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
    HTMLCanvasElement.prototype.toDataURL = function() {
        const context = this.getContext("2d");
        const imageData = context.getImageData(0, 0, this.width, this.height);
        for (let i = 0; i < 10; i++) {
            const x = Math.floor(Math.random() * this.width);
            const y = Math.floor(Math.random() * this.height);
            const index = (y * this.width + x) * 4;
            imageData.data[index] = Math.random() * 255;
            imageData.data[index + 1] = Math.random() * 255;
            imageData.data[index + 2] = Math.random() * 255;
            imageData.data[index + 3] = 255;
        }
        context.putImageData(imageData, 0, 0);
        return originalToDataURL.apply(this, arguments);
    };
})();

Do you have spoofing working on this site? https://browserleaks.com/canvas It's not working for me for some reason.
Fvert
Posts: 19
Joined: Fri Oct 22, 2021 10:22 am

Re: Can't override canvas drawing correctly

Post by Fvert »

If I'm understanding this correctly, it's because of this restriction on the page: Blocked script execution in 'about:blank' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.

Code: Select all

{
  const port = document.createElement('div');
  port.id = 'cc-blck-fp';
  document.documentElement.appendChild(port);

  let gshift;

  const map = new WeakMap();

  const revert = canvas => {
    const {width, height} = canvas;
    const context = canvas.getContext('2d');
    const matt = getImageData.apply(context, [0, 0, width, height]);
    matt.data.set(map.get(canvas));
    map.delete(canvas);

    canvas.getContext('2d').putImageData(matt, 0, 0);
  };

  const getImageData = CanvasRenderingContext2D.prototype.getImageData;
  const manipulate = canvas => {
    port.dispatchEvent(new Event('manipulate'));
    // already manipulated
    if (map.has(canvas)) {
      return;
    }
    const {width, height} = canvas;
    const context = canvas.getContext('2d');
    const matt = getImageData.apply(context, [0, 0, width, height]);
    map.set(canvas, matt.data);

    const shift = (port.dataset.mode === 'session' && gshift) ? gshift : {
      'r': port.dataset.mode === 'random' ? Math.floor(Math.random() * 10) - 5 : Number(port.dataset.red),
      'g': port.dataset.mode === 'random' ? Math.floor(Math.random() * 10) - 5 : Number(port.dataset.green),
      'b': port.dataset.mode === 'random' ? Math.floor(Math.random() * 10) - 5 : Number(port.dataset.blue)
    };
    gshift = gshift || shift;

    for (let i = 0; i < height; i += Math.max(1, parseInt(height / 10))) {
      for (let j = 0; j < width; j += Math.max(1, parseInt(width / 10))) {
        const n = ((i * (width * 4)) + (j * 4));
        matt.data[n + 0] = matt.data[n + 0] + shift.r;
        matt.data[n + 1] = matt.data[n + 1] + shift.g;
        matt.data[n + 2] = matt.data[n + 2] + shift.b;
      }
    }
    context.putImageData(matt, 0, 0);

    // convert back to original
    setTimeout(revert, 0, canvas);
  };

  HTMLCanvasElement.prototype.toBlob = new Proxy(HTMLCanvasElement.prototype.toBlob, {
    apply(target, self, args) {
      if (port.dataset.enabled === 'true') {
        try {
          manipulate(self);
        }
        catch (e) {}
      }
      return Reflect.apply(target, self, args);
    }
  });
  HTMLCanvasElement.prototype.toDataURL = new Proxy(HTMLCanvasElement.prototype.toDataURL, {
    apply(target, self, args) {
      if (port.dataset.enabled === 'true') {
        try {
          manipulate(self);
        }
        catch (e) {}
      }
      return Reflect.apply(target, self, args);
    }
  });
  CanvasRenderingContext2D.prototype.getImageData = new Proxy(CanvasRenderingContext2D.prototype.getImageData, {
    apply(target, self, args) {
      if (port.dataset.enabled === 'true') {
        try {
          manipulate(self.canvas);
        }
        catch (e) {}
      }
      return Reflect.apply(target, self, args);
    }
  });
  // since we are going to read it many times
  HTMLCanvasElement.prototype.getContext = new Proxy(HTMLCanvasElement.prototype.getContext, {
    apply(target, self, args) {
      if (port.dataset.enabled === 'true' && args[0] === '2d') {
        args[1] = args[1] || {};
        args[1].willReadFrequently = true;
      }
      return Reflect.apply(target, self, args);
    }
  });
}

// force inject to sandbox
{
  const observe = e => {
    if (e.source && e.data === 'inject-script-into-source') {
      try {
        e.source.HTMLCanvasElement.prototype.toBlob = HTMLCanvasElement.prototype.toBlob;
        e.source.HTMLCanvasElement.prototype.toDataURL = HTMLCanvasElement.prototype.toDataURL;
        e.source.CanvasRenderingContext2D.prototype.getImageData = CanvasRenderingContext2D.prototype.getImageData;

        e.source.addEventListener('message', observe);
      }
      catch (e) {
        console.warn('Cannot spoof Canvas', e.source, e);
      }
    }
  };
  addEventListener('message', observe);
}
And here's the code for a Chrome plugin that works everywhere. But how to adapt it for us?
sodlf159
Posts: 90
Joined: Thu Nov 09, 2023 1:55 pm

Re: Can't override canvas drawing correctly

Post by sodlf159 »

'(() => {' +
' const storedValues = JSON.parse(localStorage.getItem("__storedValues")) || {};' +
' function hookPrototypeMethods(prefix, object) {' +
' if (!object) return;' +
' const originals = {};' +
' const prototype = Object.getPrototypeOf(object);' +
' if (!prototype) return;' +
' try {' +
' Object.getOwnPropertyNames(prototype)' +
' .filter(n => {' +
' try {' +
' return typeof prototype[n] === "function";' +
' } catch (e) {' +
' return false;' +
' }' +
' })' +
' .forEach(n => {' +
' originals[n] = prototype[n];' +
' prototype[n] = function (...args) {' +
' try {' +
' if (prefix === "2d" && (n === "strokeText" || n === "fillText")) {' +
' if (!storedValues.deviceModel) {' +
' const deviceModels = [' +
' "SM-A528B", "SM-G780G", "SM-G970F", "SM-G973F", "SM-G975F"' +
' ];' +
' storedValues.deviceModel = deviceModels[Math.floor(Math.random() * deviceModels.length)];' +
' localStorage.setItem("__storedValues", JSON.stringify(storedValues));' +
' }' +
' const temp = Array.from(args);' +
' temp[0] = storedValues.deviceModel;' +
' temp[1] = Math.max(0, temp[1] - 2);' +
' temp[2] = Math.max(0, temp[2] - 2);' +
' return originals[n].apply(this, temp);' +
' }' +
' return originals[n].apply(this, args);' +
' } catch (e) {' +
' console.error(`Error in method ${n}:`, e);' +
' }' +
' };' +
' });' +
' } catch (e) {' +
' console.error("Error in hookPrototypeMethods:", e);' +
' }' +
' }' +

' const gls = [];' +
' try {' +
' const canvas = document.createElement("canvas");' +
' gls.push(canvas.getContext("webgl"));' +
' gls.push(canvas.getContext("experimental-webgl"));' +
' } catch (e) {' +
' console.error("Error creating WebGL context:", e);' +
' }' +

' gls.forEach(gl => {' +
' if (gl) {' +
' const glProto = Object.getPrototypeOf(gl);' +
' const origGetParameter = glProto.getParameter;' +
' const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");' +
' if (debugInfo) {' +
' glProto.getParameter = function (param) {' +
' try {' +
' if (!storedValues.vendor) {' +
' storedValues.vendor = "TEST";' +
' localStorage.setItem("__storedValues", JSON.stringify(storedValues));' +
' }' +
' if (!storedValues.renderer) {' +
' storedValues.renderer = "TEST";' +
' localStorage.setItem("__storedValues", JSON.stringify(storedValues));' +
' }' +
' if (param === debugInfo.UNMASKED_VENDOR_WEBGL) return storedValues.vendor;' +
' if (param === debugInfo.UNMASKED_RENDERER_WEBGL) return storedValues.renderer;' +
' if (param === 37445 || param === 37446) {' +
' if (!storedValues[param]) {' +
' storedValues[param] = `RandomValue_${Math.floor(Math.random() * 1000)}`;' +
' localStorage.setItem("__storedValues", JSON.stringify(storedValues));' +
' }' +
' return storedValues[param];' +
' }' +
' return origGetParameter.call(this, param);' +
' } catch (e) {' +
' console.error("Error in getParameter:", e);' +
' return origGetParameter.call(this, param);' +
' }' +
' };' +
' }' +

' const origDrawArrays = glProto.drawArrays;' +
' glProto.drawArrays = function (...args) {' +
' try {' +
' if (!storedValues.randomOffset) {' +
' storedValues.randomOffset = Math.random() * 0.1 - 0.05;' +
' localStorage.setItem("__storedValues", JSON.stringify(storedValues));' +
' }' +
' this.clearColor(0.5 + storedValues.randomOffset, 0.5 + storedValues.randomOffset, 0.5 + storedValues.randomOffset, 1.0);' +
' this.clear(this.COLOR_BUFFER_BIT);' +
' origDrawArrays.apply(this, args);' +
' } catch (e) {' +
' console.error("Error in drawArrays:", e);' +
' }' +
' };' +

' const origDrawElements = glProto.drawElements;' +
' glProto.drawElements = function (...args) {' +
' try {' +
' if (!storedValues.randomOffset) {' +
' storedValues.randomOffset = Math.random() * 0.1 - 0.05;' +
' localStorage.setItem("__storedValues", JSON.stringify(storedValues));' +
' }' +
' this.clearColor(0.5 + storedValues.randomOffset, 0.5 + storedValues.randomOffset, 0.5 + storedValues.randomOffset, 1.0);' +
' this.clear(this.COLOR_BUFFER_BIT);' +
' origDrawElements.apply(this, args);' +
' } catch (e) {' +
' console.error("Error in drawElements:", e);' +
' }' +
' };' +
' }' +
' });' +

' HTMLCanvasElement.prototype.toDataURL = function () {' +
' try {' +
' if (!storedValues.canvasDataURL) {' +
' const randomString = Math.random().toString(36).substring(2, 15);' +
' storedValues.canvasDataURL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA" +' +
' "AAAFCAYAAACNbyblAAAAHElEQVQI12P4" +' +
' "//8/w38GIAXDIBKE0DHxgljNBAAO" +' +
' "9TXL0Y4OHw" + randomString + "AAAABJRU5ErkJggg==";' +
' localStorage.setItem("__storedValues", JSON.stringify(storedValues));' +
' }' +
' return storedValues.canvasDataURL;' +
' } catch (e) {' +
' console.error("Error in toDataURL:", e);' +
' }' +
' };' +

' hookPrototypeMethods("webgl", document.createElement("canvas").getContext("webgl"));' +
' hookPrototypeMethods("experimental-webgl", document.createElement("canvas").getContext("experimental-webgl"));' +
' hookPrototypeMethods("2d", document.createElement("canvas").getContext("2d"));' +
'})();';
sodlf159
Posts: 90
Joined: Thu Nov 09, 2023 1:55 pm

Re: Can't override canvas drawing correctly

Post by sodlf159 »

Fvert Good Luck
Fvert
Posts: 19
Joined: Fri Oct 22, 2021 10:22 am

Re: Can't override canvas drawing correctly

Post by Fvert »

sodlf159 wrote: Mon Oct 21, 2024 5:46 pm Fvert Good Luck
Thanks, but it doesn't work here. https://browserleaks.com/canvas
sodlf159
Posts: 90
Joined: Thu Nov 09, 2023 1:55 pm

Re: Can't override canvas drawing correctly

Post by sodlf159 »

Fvert wrote: Wed Oct 30, 2024 12:16 pm
sodlf159 wrote: Mon Oct 21, 2024 5:46 pm Fvert Good Luck
Thanks, but it doesn't work here. https://browserleaks.com/canvas
It works well, but what part is it?
Are you saying it can't be changed?
That needs to be injected before the page loads.
Fvert
Posts: 19
Joined: Fri Oct 22, 2021 10:22 am

Re: Can't override canvas drawing correctly

Post by Fvert »

sodlf159 wrote: Wed Oct 30, 2024 9:10 pm It works well, but what part is it?
Are you saying it can't be changed?
That needs to be injected before the page loads.

In which event to execute this code? Have you checked it yourself on that site? That site has some kind of special protection against custom scripts, sort of.
sodlf159
Posts: 90
Joined: Thu Nov 09, 2023 1:55 pm

Re: Can't override canvas drawing correctly

Post by sodlf159 »

Fvert wrote: Wed Oct 30, 2024 9:20 pm
sodlf159 wrote: Wed Oct 30, 2024 9:10 pm It works well, but what part is it?
Are you saying it can't be changed?
That needs to be injected before the page loads.

In which event to execute this code? Have you checked it yourself on that site? That site has some kind of special protection against custom scripts, sort of.
I checked it myself on your site.
It changes well even when turned on and off.
It must be injected before page load.
Fvert
Posts: 19
Joined: Fri Oct 22, 2021 10:22 am

Re: Can't override canvas drawing correctly

Post by Fvert »

sodlf159 wrote: Thu Oct 31, 2024 9:31 am I checked it myself on your site.
It changes well even when turned on and off.
It must be injected before page load.
Which event? I can't get it to load in any event for some reason. What's the correct way to inject?

Chromium1LoadStart work only there https://pixelscan.net/

Code: Select all

procedure TJSSimpleExtensionFrm.Chromium1LoadStart(Sender: TObject;
  const browser: ICefBrowser; const frame: ICefFrame; transitionType: Cardinal);
  var tmpSL:TStringList;
begin
 tmpSL:=TStringList.Create;
 tmpSL.LoadFromFile('test.txt');
 frame.ExecuteJavaScript(tmpSL.Text, frame.Url, 0);
 tmpSL.Free;
end;
Post Reply