omegafox/scripts/examples/webgl.html
daijro 02bc15161a feat: WebGL fingerprint spoofing
Experimental WebGL fingerprint injection.
- Allows the ability to set all WebGL parameters, supported extension list, shader precision formats, & context attributes.
- Added a demo website under scripts/examples/webgl.html that can be used to generate Camoufox config data
2024-10-14 02:12:13 -05:00

345 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WebGL Fingerprinting - Camoufox</title>
<style>
html,
body {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
body {
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
background-color: #1e1e1e;
color: #d4d4d4;
display: flex;
flex-direction: column;
}
.container {
flex: 1;
display: flex;
flex-direction: column;
padding: 20px;
overflow: hidden;
}
h1 {
color: #569cd6;
margin-top: 0;
}
#output {
flex: 1;
background-color: #252526;
border: 1px solid #3e3e42;
border-radius: 4px;
padding: 15px;
white-space: pre-wrap;
word-wrap: break-word;
font-family: "Consolas", "Courier New", monospace;
font-size: 14px;
overflow-y: auto;
}
.button-container {
display: flex;
gap: 10px;
margin-top: 5px;
}
.btn {
background-color: #0e639c;
color: white;
border: none;
padding: 3px 8px;
cursor: pointer;
border-radius: 4px;
font-size: 16px;
}
.btn:hover {
background-color: #1177bb;
}
.string {
color: #ce9178;
}
.number {
color: #b5cea8;
}
.boolean {
color: #569cd6;
}
.null {
color: #569cd6;
}
.key {
color: #9cdcfe;
}
#hash {
margin-bottom: 5px;
font-family: "Consolas", "Courier New", monospace;
font-size: 14px;
color: #569cd6;
}
#warning {
background-color: #ff9800;
color: #000;
margin-bottom: 10px;
border-radius: 4px;
display: none;
position: relative;
transition: opacity 0.3s ease-out, max-height 0.3s ease-out, padding 0.3s ease-out;
opacity: 1;
max-height: 100px;
overflow: hidden;
padding: 10px;
}
#warning.hide {
opacity: 0;
max-height: 0;
padding: 0;
}
#close-warning {
position: absolute;
top: 50%;
right: 5px;
transform: translateY(-50%);
background: none;
border: none;
color: #000;
font-size: 16px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>Your WebGL Information (in Camoufox format)</h1>
<div id="warning">Warning: This browser is not Firefox. The fingerprint below will not run properly on Camoufox.<button id="close-warning">&times;</button></div>
<div id="hash"></div>
<pre id="output"></pre>
<div class="button-container">
<button id="copy-btn" class="btn">Copy data</button>
<button id="copy-minified-btn" class="btn">Copy data (minified)</button>
</div>
</div>
<script>
const dumpWebGLCore = async (
webglContextId,
experimentalWebglContextId
) => {
function getWebGLContext() {
const canvas = new OffscreenCanvas(256, 256);
let result = null;
try {
result =
canvas.getContext(webglContextId, {
preserveDrawingBuffer: false,
}) ||
canvas.getContext(experimentalWebglContextId, {
preserveDrawingBuffer: false,
});
} catch (ex) {}
result || (result = null);
return result;
}
const webglContext = getWebGLContext();
if (!webglContext) {
return {};
}
function getMaxAnisotropy(ctx) {
if (ctx) {
const ext =
ctx.getExtension("EXT_texture_filter_anisotropic") ||
ctx.getExtension("WEBKIT_EXT_texture_filter_anisotropic") ||
ctx.getExtension("MOZ_EXT_texture_filter_anisotropic");
if (ext) {
return ctx.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
}
}
return null;
}
const glEnums = [
2849, 2884, 2885, 2886, 2928, 2929, 2930, 2931, 2932, 2960, 2961,
2962, 2963, 2964, 2965, 2966, 2967, 2968, 2978, 3024, 3042, 3088,
3089, 3106, 3107, 3317, 3333, 3379, 3386, 3408, 3410, 3411, 3412,
3413, 3414, 3415, 7936, 7937, 7938, 10752, 32773, 32777, 32823, 32824,
32873, 32883, 32936, 32937, 32938, 32939, 32968, 32969, 32970, 32971,
33170, 33901, 33902, 34016, 34024, 34045, 34047, 34068, 34076, 34467,
34816, 34817, 34818, 34819, 34852, 34877, 34921, 34930, 34964, 34965,
35071, 35076, 35077, 35371, 35373, 35374, 35375, 35376, 35377, 35379,
35380, 35657, 35658, 35659, 35660, 35661, 35724, 35725, 35968, 35978,
35979, 36003, 36004, 36005, 36006, 36007, 36063, 36183, 36347, 36348,
36349, 37154, 37157, 37440, 37441, 37443, 37444, 37445, 37446,
];
let rendererInfo = null;
const debug_ext = webglContext.getExtension(
"WEBGL_debug_renderer_info"
);
if (debug_ext) {
rendererInfo = {
webglVendor: webglContext.getParameter(
debug_ext.UNMASKED_VENDOR_WEBGL
),
webglRenderer: webglContext.getParameter(
debug_ext.UNMASKED_RENDERER_WEBGL
),
};
}
const result = {
[`${webglContextId}:renderer`]: rendererInfo
? rendererInfo.webglRenderer
: "Not available",
[`${webglContextId}:vendor`]: rendererInfo
? rendererInfo.webglVendor
: "Not available",
[`${webglContextId}:contextAttributes`]:
webglContext.getContextAttributes(),
[`${webglContextId}:supportedExtensions`]:
webglContext.getSupportedExtensions() || [],
[`${webglContextId}:parameters`]: {},
[`${webglContextId}:shaderPrecisionFormats`]: {},
};
for (const glEnum of glEnums) {
try {
const parmValue = webglContext.getParameter(glEnum);
if (
Array.isArray(parmValue) ||
[Float32Array, Int32Array, Uint32Array].some(
(type) => parmValue instanceof type
)
) {
const arrayValue = Array.from(parmValue);
result[`${webglContextId}:parameters`][glEnum] =
arrayValue.length > 0 ? arrayValue : null;
} else {
result[`${webglContextId}:parameters`][glEnum] = parmValue;
}
} catch (err) {
console.log(err);
}
}
const shaderTypes = [
webglContext.VERTEX_SHADER,
webglContext.FRAGMENT_SHADER,
];
const precisionTypes = [
webglContext.LOW_FLOAT,
webglContext.MEDIUM_FLOAT,
webglContext.HIGH_FLOAT,
webglContext.LOW_INT,
webglContext.MEDIUM_INT,
webglContext.HIGH_INT,
];
for (let shaderType of shaderTypes) {
for (let precisionType of precisionTypes) {
const r = webglContext.getShaderPrecisionFormat(
shaderType,
precisionType
);
const key = `${shaderType},${precisionType}`;
result[`${webglContextId}:shaderPrecisionFormats`][key] = {
rangeMin: r.rangeMin,
rangeMax: r.rangeMax,
precision: r.precision,
};
}
}
return result;
};
function syntaxHighlight(json) {
json = json
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
return json.replace(
/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
function (match) {
var cls = "number";
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = "key";
} else {
cls = "string";
}
} else if (/true|false/.test(match)) {
cls = "boolean";
} else if (/null/.test(match)) {
cls = "null";
}
return '<span class="' + cls + '">' + match + "</span>";
}
);
}
async function sha256(message) {
const msgBuffer = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest("SHA-256", msgBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
return hashHex;
}
document.addEventListener(
"DOMContentLoaded",
async () => {
// Check if the user agent is Firefox
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
if (!isFirefox) {
document.getElementById('warning').style.display = 'block';
}
// Add event listener for close button
document.getElementById('close-warning').addEventListener('click', function() {
document.getElementById('warning').classList.add('hide');
});
const [webglDetail, webgl2Detail] = await Promise.all([
dumpWebGLCore("webgl", "experimental-webgl"),
dumpWebGLCore("webgl2", "experimental-webgl2"),
]);
const combinedInfo = { ...webglDetail, ...webgl2Detail };
const jsonString = JSON.stringify(combinedInfo, null, 2);
const minifiedJson = JSON.stringify(combinedInfo);
const hashValue = await sha256(minifiedJson);
document.getElementById("hash").textContent = `SHA256: ${hashValue}`;
document.getElementById("output").innerHTML =
syntaxHighlight(jsonString);
document.getElementById("copy-btn").addEventListener("click", () => {
navigator.clipboard.writeText(jsonString).then(() => {
alert("Copied formatted JSON to clipboard!");
});
});
document
.getElementById("copy-minified-btn")
.addEventListener("click", () => {
navigator.clipboard.writeText(minifiedJson).then(() => {
alert("Copied minified JSON to clipboard!");
});
});
},
false
);
</script>
</body>
</html>