Can't override canvas drawing correctly
Posted: Sun Oct 13, 2024 12:43 pm
I asked chatGPT how to make a random canvas fingerprint and he replied with this text. For some reason on the line context2D := CanvasPrototype.ExecuteFunction(OriginalGetContext, arguments); always returns nil, no matter what I put there (chatGPT wrote context2D := obl.ExecuteFunction(obj, arguments);). Can you tell me how to do it correctly? Thanks for your help.
Code: Select all
program ConsoleBrowser;
{$I cef.inc}
uses
// FastMM4,
Windows,
System.SysUtils,System.Classes, uCEFApplicationCore, uCEFInterfaces, uCEFTypes,
uCEFProcessMessage, uCEFConstants, uCEFv8Handler, uCEFv8Value;
{$IFDEF WIN32}
// CEF3 needs to set the LARGEADDRESSAWARE ($20) flag which allows 32-bit processes to use up to 3GB of RAM.
{$SetPEFlags $20}
{$ENDIF}
type
TCanvasFingerprintV8Handler = class(TCefv8HandlerOwn)
protected
function Execute(const name: ustring; const obj: ICefv8Value;
const arguments: TCefv8ValueArray; var retval: ICefv8Value; var exception: ustring): Boolean; override;
end;
var errmsg : string;
OriginalGetContext:ICefv8Value;
CanvasPrototype:ICefv8Value;
procedure AddSave(SaveS,fn:AnsiString;Er:boolean);
var fs:TFileStream; crl:AnsiString;
begin
crl:=#13#10; fs:=nil;
try
if not fileExists(fn) then begin
fs:=TFileStream.Create(fn,fmCreate or fmOpenWrite);
fs.WriteBuffer(SaveS[1], Length(SaveS));
end else begin
fs:=TFileStream.Create(fn,fmOpenWrite);
if (fs.Size>100000000) and Er then fs.Size:=0;
fs.Seek(0,soFromEnd);
if fs.Size>0 then
fs.WriteBuffer(crl[1],2);
fs.WriteBuffer(SaveS[1], Length(SaveS));
end;
finally freeandnil(fs) end;
end;
function TCanvasFingerprintV8Handler.Execute(const name: ustring; const obj: ICefv8Value;
const arguments: TCefv8ValueArray; var retval: ICefv8Value; var exception: ustring): Boolean;
var
context2D, GetImageDataFunc, OriginalGetImageDataFunc: ICefv8Value;
i, r, g, b, a: Integer;
sx, sy, sw, sh, pixelCount, randomPixelIndex1, randomPixelIndex2: Integer;
imageDataArray, pixelDataArray: ICefv8Value;
begin
Result := False;
// Intercept the getContext call
if name = 'getContext' then begin
if (Length(arguments) = 1) and (arguments[0].GetStringValue = '2d') then
begin
// Call the original getContext and get CanvasRenderingContext2D
context2D := CanvasPrototype.ExecuteFunction(OriginalGetContext, arguments);
if context2D=nil then
AddSave(DateToStr(Time)+' '+'nil','test4.txt',true);
if context2D.IsObject then begin
// Intercept getImageData on the CanvasRenderingContext2D instance
GetImageDataFunc := context2D.GetValueByKey('getImageData');
if GetImageDataFunc.IsFunction then
begin
// Save the original getImageData
OriginalGetImageDataFunc := GetImageDataFunc;
// Override getImageData
context2D.SetValueByKey('getImageData', TCefv8ValueRef.NewFunction('getImageData', Self), V8_PROPERTY_ATTRIBUTE_NONE);
// Return the modified context
retval := context2D;
Result := True;
end;
end;
end;
end
// Intercept the getImageData call
else if name = 'getImageData' then begin
// AddSave(DateToStr(Date)+' '+name,'test4.txt',true);
if Length(arguments) = 4 then
begin
// Getting parameters of call getImageData
sx := arguments[0].GetIntValue;
sy := arguments[1].GetIntValue;
sw := arguments[2].GetIntValue;
sh := arguments[3].GetIntValue;
// Call the original getImageData to get the real data
imageDataArray := obj.GetValueByKey('getImageData').ExecuteFunction(obj, arguments);
if (imageDataArray <> nil) and imageDataArray.IsObject then begin
// Get an array of pixels
pixelDataArray := imageDataArray.GetValueByKey('data');
if pixelDataArray.IsArray then
begin
pixelCount := pixelDataArray.GetArrayLength div 4; // Количество пикселей (каждый пиксель — это 4 значения: RGBA)
// Generate two random indices for pixels
randomPixelIndex1 := Random(pixelCount) * 4; // Multiply by 4 because each pixel consists of 4 values (R, G, B, A)
randomPixelIndex2 := Random(pixelCount) * 4;
// Modify the first random pixel
pixelDataArray.SetValueByIndex(randomPixelIndex1, TCefv8ValueRef.NewInt(255)); // Changing the red channel
pixelDataArray.SetValueByIndex(randomPixelIndex1 + 1, TCefv8ValueRef.NewInt(0)); // Changing the green channel
pixelDataArray.SetValueByIndex(randomPixelIndex1 + 2, TCefv8ValueRef.NewInt(0)); // Changing the blue channel
pixelDataArray.SetValueByIndex(randomPixelIndex1 + 3, TCefv8ValueRef.NewInt(255)); // Leave the alpha channel unchanged
// Let's modify the second random pixel
pixelDataArray.SetValueByIndex(randomPixelIndex2, TCefv8ValueRef.NewInt(0)); // Changing the red channel
pixelDataArray.SetValueByIndex(randomPixelIndex2 + 1, TCefv8ValueRef.NewInt(255)); // Changing the green channel
pixelDataArray.SetValueByIndex(randomPixelIndex2 + 2, TCefv8ValueRef.NewInt(0)); // Changing the blue channel
pixelDataArray.SetValueByIndex(randomPixelIndex2 + 3, TCefv8ValueRef.NewInt(255)); // Leave the alpha channel unchanged
// Return the modified data back
retval := imageDataArray;
Result := True;
end;
end;
end;
end;
end;
procedure GlobalCEFApp_OnContextCreated(const browser: ICefBrowser; const frame: ICefFrame; const context: ICefv8Context);
var
Global, GetContextFunc, GetImageDataFunc: ICefv8Value;
Handler: ICefv8Handler;
begin
if GlobalCEFApp.ProcessType = ptRenderer then begin
Global := context.GetGlobal;
// Hijacking the getContext method on the canvas prototype
CanvasPrototype := Global.GetValueByKey('HTMLCanvasElement').GetValueByKey('prototype');
if (CanvasPrototype <> nil) and CanvasPrototype.IsObject then
begin
Handler := TCanvasFingerprintV8Handler.Create;
// Intercept getContext
GetContextFunc := CanvasPrototype.GetValueByKey('getContext');
if GetContextFunc.IsFunction then begin
// Save the original getContext method
OriginalGetContext := GetContextFunc;
// Override getContext to capture CanvasRenderingContext2D
CanvasPrototype.SetValueByKey('getContext', TCefv8ValueRef.NewFunction('getContext', Handler), V8_PROPERTY_ATTRIBUTE_NONE);
end;
end;
end;
end;
begin
//try
GlobalCEFApp := TCefApplicationCore.Create;
GlobalCEFApp.SitePerProcess := True;
GlobalCEFApp.WindowlessRenderingEnabled := True;
GlobalCEFApp.EnableMediaStream := False;
GlobalCEFApp.EnableSpeechInput := False;
GlobalCEFApp.ShowMessageDlg := False;
GlobalCEFApp.BlinkSettings := 'hideScrollbars';
GlobalCEFApp.OnContextCreated:= GlobalCEFApp_OnContextCreated;
GlobalCEFApp.StartSubProcess;
DestroyGlobalCEFApp;
end.