目录
简介
WebGL(Web图形库)是一个JavaScript API,无需使用插件可在任何兼容的Web浏览器中渲染高性能的交互式3D和2D图形。WebGL通过引入一个与OpenGL ES 2.0非常一致的API来做到这一点,该API可以在HTML5 <canvas>元素中使用。 这种一致性使API可以利用用户设备提供的硬件图形加速。WebGL 2 API引入了对大部分的OpenGL ES 3.0功能集的支持; 它是通过WebGL2RenderingContext界面提供的。与其相关的主要是2种指纹,一个是报告指纹,另外一个是图片指纹。
图像指纹
由于硬件的差异,特定图像的渲染的会有少许不同,以此可以作为webGL的图像指纹。与canvas指纹大同小异。不过获取哈希的方法仅涉及WebGL API,不依赖于toDataURL PNG导出的浏览器差异,因此该指纹在单个设备内的浏览器中可能是相似的。
核心代码
function getGLDrawHash(){
var e;
try {
var t = document.createElement("canvas");
t.width = 256,
t.height = 128,
e = t.getContext("webgl2", {
preserveDrawingBuffer: !0
}) || t.getContext("experimental-webgl2", {
preserveDrawingBuffer: !0
}) || t.getContext("webgl", {
preserveDrawingBuffer: !0
}) || t.getContext("experimental-webgl", {
preserveDrawingBuffer: !0
}) || t.getContext("moz-webgl", {
preserveDrawingBuffer: !0
})
} catch (e) {}
try {
var a = e.createBuffer()
, n = (e.bindBuffer(e.ARRAY_BUFFER, a),
new Float32Array([-.2, -.9, 0, .4, -.26, 0, 0, .7321, 0]))
, i = (e.bufferData(e.ARRAY_BUFFER, n, e.STATIC_DRAW),
a.itemSize = 3,
a.numItems = 3,
e.createProgram())
, o = e.createShader(e.VERTEX_SHADER)
, s = (e.shaderSource(o, "attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}"),
e.compileShader(o),
e.createShader(e.FRAGMENT_SHADER));
e.shaderSource(s, "precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}"),
e.compileShader(s),
e.attachShader(i, o),
e.attachShader(i, s),
e.linkProgram(i),
e.useProgram(i),
i.vertexPosAttrib = e.getAttribLocation(i, "attrVertex"),
i.offsetUniform = e.getUniformLocation(i, "uniformOffset"),
e.enableVertexAttribArray(i.vertexPosArray),
e.vertexAttribPointer(i.vertexPosAttrib, a.itemSize, e.FLOAT, !1, 0, 0),
e.uniform2f(i.offsetUniform, 1, 1),
e.drawArrays(e.TRIANGLE_STRIP, 0, a.numItems);
} catch (e) {
console.log(e)
return ''
}
let r = "";
try {
var _ = new Uint8Array(131072);
if (e.readPixels(0, 0, 256, 128, e.RGBA, e.UNSIGNED_BYTE, _),
"" == (r = JSON.stringify(_).replace(/,?"[0-9]+":/g, "")).replace(/^{[0]+}$/g, ""))
throw 1;
r = hash(r);
} catch (e) {
r = "n/a"
}
return r
}
let I64BIT_TABLE =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'.split('');
function hash(input){
var hash = 5381;
var i = input.length - 1;
if(typeof input == 'string'){
for (; i > -1; i--)
hash += (hash << 5) + input.charCodeAt(i);
}
else{
for (; i > -1; i--)
hash += (hash << 5) + input[i];
}
var value = hash & 0x7FFFFFFF;
var retValue = '';
do{
retValue += I64BIT_TABLE[value & 0x3F];
}
while(value >>= 6);
return retValue;
}
WebGL Fingerprint Defender 干扰方法
其思路是给bufferData增加噪音
var proto = target.prototype ? target.prototype : target.__proto__;
const bufferData = proto.bufferData;
Object.defineProperty(proto, "bufferData", {
"value": function () {
var index = Math.floor(config.random.value() * arguments[1].length);
var noise = arguments[1][index] !== undefined ? 0.1 * config.random.value() * arguments[1][index] : 0;
arguments[1][index] = arguments[1][index] + noise;
window.top.postMessage("webgl-fingerprint-defender-alert", '*');
return bufferData.apply(this, arguments);
}
});
报告指纹
通过webGL Api,获取当前浏览器支持的功能,扩展以及参数的信息,并以此作为指纹。在browserleaks.com中是以WebGL最高版本作为报告指纹。
核心代码
支持上下文类型
function getSupportedContextName() {
var e, t,
r = ["webgl2", "experimental-webgl2", "webgl", "experimental-webgl", "moz-webgl", "fake-webgl"],
a = [], n = !1;
for (t in r)
try {
(e = document.createElement("canvas").getContext(r[t], {
stencil: !0
})) && (n = e,
a.push(r[t]))
} catch (e) {
}
return !!n && {
name: a,
gl: n
}
}
渲染引擎和显卡信息
var canvas = document.createElement('canvas');
var gl = canvas.getContext('webgl');
var debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
var vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL);
var renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
console.log(debugInfo);
console.log(vendor);
console.log(renderer);
WebGL 2 新函数支持情况
(function getNewFunctionsImplemented(){
var F = document.createElement("canvas").getContext('webgl2', {stencil: !0})
var Y = [],N = []
for (var c = ["copyBufferSubData", "getBufferSubData", "blitFramebuffer", "framebufferTextureLayer", "getInternalformatParameter", "invalidateFramebuffer", "invalidateSubFramebuffer", "readBuffer", "renderbufferStorageMultisample", "texStorage2D", "texStorage3D", "texImage3D", "texSubImage3D", "copyTexSubImage3D", "compressedTexImage3D", "compressedTexSubImage3D", "getFragDataLocation", "uniform1ui", "uniform2ui", "uniform3ui", "uniform4ui", "uniform1uiv", "uniform2uiv", "uniform3uiv", "uniform4uiv", "uniformMatrix2x3fv", "uniformMatrix3x2fv", "uniformMatrix2x4fv", "uniformMatrix4x2fv", "uniformMatrix3x4fv", "uniformMatrix4x3fv", "vertexAttribI4i", "vertexAttribI4iv", "vertexAttribI4ui", "vertexAttribI4uiv", "vertexAttribIPointer", "vertexAttribDivisor", "drawArraysInstanced", "drawElementsInstanced", "drawRangeElements", "drawBuffers", "clearBufferiv", "clearBufferuiv", "clearBufferfv", "clearBufferfi", "createQuery", "deleteQuery", "isQuery", "beginQuery", "endQuery", "getQuery", "getQueryParameter", "createSampler", "deleteSampler", "isSampler", "bindSampler", "samplerParameteri", "samplerParameterf", "getSamplerParameter", "fenceSync", "isSync", "deleteSync", "clientWaitSync", "waitSync", "getSyncParameter", "createTransformFeedback", "deleteTransformFeedback", "isTransformFeedback", "bindTransformFeedback", "beginTransformFeedback", "endTransformFeedback", "transformFeedbackVaryings", "getTransformFeedbackVarying", "pauseTransformFeedback", "resumeTransformFeedback", "bindBufferBase", "bindBufferRange", "getIndexedParameter", "getUniformIndices", "getActiveUniforms", "getUniformBlockIndex", "getActiveUniformBlockParameter", "getActiveUniformBlockName", "uniformBlockBinding", "createVertexArray", "deleteVertexArray", "isVertexArray", "bindVertexArray"], E = 0, u = 0; u < 88; u++) {
var m = c[u];
F[m] ? (E++,Y.push(m)):N.push(m);
}
return {
Y:Y,
c:Y.length,
E:E
}
})()
webgl2 参数
(function getParameter(){
function h(e) {
return null == e ? "null" : "[" + e[0] + ", " + e[1] + "]"
}
var Parameter = {};
var F = document.createElement("canvas").getContext('webgl2', {stencil: !0})
var g = ["VERSION", "SHADING_LANGUAGE_VERSION", "VENDOR", "RENDERER", "MAX_VERTEX_ATTRIBS", "MAX_VERTEX_UNIFORM_VECTORS", "MAX_VERTEX_TEXTURE_IMAGE_UNITS", "MAX_VARYING_VECTORS", "ALIASED_LINE_WIDTH_RANGE", "ALIASED_POINT_SIZE_RANGE", "MAX_FRAGMENT_UNIFORM_VECTORS", "MAX_TEXTURE_IMAGE_UNITS", ["RED_BITS", "GREEN_BITS", "BLUE_BITS", "ALPHA_BITS"], ["DEPTH_BITS", "STENCIL_BITS"], "MAX_RENDERBUFFER_SIZE", "MAX_VIEWPORT_DIMS", "MAX_TEXTURE_SIZE", "MAX_CUBE_MAP_TEXTURE_SIZE", "MAX_COMBINED_TEXTURE_IMAGE_UNITS"];
for (u in g = g.concat(["MAX_VERTEX_UNIFORM_COMPONENTS", "MAX_VERTEX_UNIFORM_BLOCKS", "MAX_VERTEX_OUTPUT_COMPONENTS", "MAX_VARYING_COMPONENTS", "MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", "MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", "MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", "MAX_FRAGMENT_UNIFORM_COMPONENTS", "MAX_FRAGMENT_UNIFORM_BLOCKS", "MAX_FRAGMENT_INPUT_COMPONENTS", "MIN_PROGRAM_TEXEL_OFFSET", "MAX_PROGRAM_TEXEL_OFFSET", "MAX_DRAW_BUFFERS", "MAX_COLOR_ATTACHMENTS", "MAX_SAMPLES", "MAX_3D_TEXTURE_SIZE", "MAX_ARRAY_TEXTURE_LAYERS", "MAX_TEXTURE_LOD_BIAS", "MAX_UNIFORM_BUFFER_BINDINGS", "MAX_UNIFORM_BLOCK_SIZE", "UNIFORM_BUFFER_OFFSET_ALIGNMENT", "MAX_COMBINED_UNIFORM_BLOCKS", "MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", "MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS"])) {
var A = "";
if (g[u]instanceof Array) {
for (var T in g[u])
A.length && (A += ", "),
A += F.getParameter(F[g[u][T]]);
A = "[" + A + "]"
} else{
null === (A = F.getParameter(F[g[u]])) ? A = "n/a" : "object" == typeof A && null != A && (A = h(A));
}
Parameter[g[u]] = A
}
return Parameter
})()
webgl2 支持扩展
( document.createElement("canvas").getContext('webgl2', {stencil: !0})).getSupportedExtensions()
WebGL Fingerprint Defender 干扰方法
其思路是给getParameter 增加噪音
const getParameter = proto.getParameter;
Object.defineProperty(proto, "getParameter", {
"value": function () {
if (arguments[0] === 3415) return 0;
else if (arguments[0] === 3414) return 24;
else if (arguments[0] === 36348) return 30;
else if (arguments[0] === 7936) return "WebKit";
else if (arguments[0] === 37445) return "Google Inc.";
else if (arguments[0] === 7937) return "WebKit WebGL";
else if (arguments[0] === 3379) return config.random.number([14, 15]);
else if (arguments[0] === 36347) return config.random.number([12, 13]);
else if (arguments[0] === 34076) return config.random.number([14, 15]);
else if (arguments[0] === 34024) return config.random.number([14, 15]);
else if (arguments[0] === 3386) return config.random.int([13, 14, 15]);
else if (arguments[0] === 3413) return config.random.number([1, 2, 3, 4]);
else if (arguments[0] === 3412) return config.random.number([1, 2, 3, 4]);
else if (arguments[0] === 3411) return config.random.number([1, 2, 3, 4]);
else if (arguments[0] === 3410) return config.random.number([1, 2, 3, 4]);
else if (arguments[0] === 34047) return config.random.number([1, 2, 3, 4]);
else if (arguments[0] === 34930) return config.random.number([1, 2, 3, 4]);
else if (arguments[0] === 34921) return config.random.number([1, 2, 3, 4]);
else if (arguments[0] === 35660) return config.random.number([1, 2, 3, 4]);
else if (arguments[0] === 35661) return config.random.number([4, 5, 6, 7, 8]);
else if (arguments[0] === 36349) return config.random.number([10, 11, 12, 13]);
else if (arguments[0] === 33902) return config.random.float([0, 10, 11, 12, 13]);
else if (arguments[0] === 33901) return config.random.float([0, 10, 11, 12, 13]);
else if (arguments[0] === 37446) return config.random.item(["Graphics", "HD Graphics", "Intel(R) HD Graphics"]);
else if (arguments[0] === 7938) return config.random.item(["WebGL 1.0", "WebGL 1.0 (OpenGL)", "WebGL 1.0 (OpenGL Chromium)"]);
else if (arguments[0] === 35724) return config.random.item(["WebGL", "WebGL GLSL", "WebGL GLSL ES", "WebGL GLSL ES (OpenGL Chromium"]);
return getParameter.apply(this, arguments);
}
});
参考
https://developer.mozilla.org/zh-CN/docs/Web/API/WebGL_API/Constants
https://browserleaks.com/webgl