admin管理员组

文章数量:1565281

背景

利用浏览器进行数据抓取需要改在环境,反爬虫中chrome无头浏览器的几种检测与绕过方式介绍了修改属性,本文总结修改方法。

在浏览中包含各种浏览器对象与网页元素对象,环境修改就是对这两类对象进行修改。Js中属性修改要么是修改该对象的自身属性,要么是修改该对象原型属性。当访问一个属性时,如果对象不存在该属性,那么在原型中去查找。因此修改就很明确了,改对象属性或原型属性。如果通过构造器修改,那么重新定义构造器prototype上的方法属性,如果通过实例对象修改那么修改实例对象的__proto__属性,实例的该属性指向原型对象[1] 。

页面元素标签属性修改

有时封会通过元素标签上的方法进行封禁,这时就需要进行改造。Javascript通过文档对象模型(DOM)来操作网页[1]。DOM中最小组成单位是节点(node),节点类型如下:

  • Document:整个文档树的顶层节点
  • DocumentType:doctype标签(比如)
  • Element:网页的各种HTML标签(比如< body>等)
  • Attribute:网页元素的属性(比如class=“right”)
  • Text:标签之间或标签包含的文本
  • Comment:注释
  • DocumentFragment:文档的片段

就是说页面中的每个标签都有一个具体的元素对象与之对应。例如页面标签元素都是Element的子元素。

HTMLCanvasElement 对应 标签,该对象有toDataURL()与toBlob()等方法 [3]
HTMLTextAreaElement 对应了 标签,该对象有focus()等方法。[4]


以textarea的focus方法为例创建一个textarea元素对象。

ta = document.createElement("textarea");

修改该元素构造器对象(HTMLTextAreaElement)原型对象的方法focus

Object.defineProperty(HTMLTextAreaElement.prototype,"focus",{
     get : () => function() {alert("modifyTest")}
});

接着我们调用focus方法,会弹出一个对话框。因此该标签环境改造完毕。那么反爬在检测时我们就可以绕过。

ta.focus();

浏览器对象修改

首先通过navigator.hasOwnProperty("webdriver”) 返回false可知webdriver不是直接定义在navigator对象上的属性,因此我们可以直接重新定义。

Object.defineProperty(navigator,"webdriver",{
     get:() => false
});

接着运行navigator.webdriver,返回false。如前所述,属性不是自身属性,那么就是原型属性。navigator实例对象,我们通过__proto__获得原型对象。

接着检测属性navigator.proto.hasOwnProperty("webdriver”); 返回true 那么直接删除原型上的属性达到修改的目的。并且更切合真实浏览器的状态。

delete navigator.__proto__.webdriver

接着运行navigator.webdriver,返回 undefined 修改完毕。

当通过Object.defineProperty通过修改属性描述对象时,除了修改其值get或set方法,其它的枚举属性也要修改。例如网站在收集用户浏览器connection信息时有如下代码收集connection对象属性。

for(let a in navigator.connection) {
    console.log(a);
}

VM207:2 effectiveType
VM207:2 rtt
VM207:2 downlink
VM207:2 saveData
VM207:2 addEventListener
VM207:2 removeEventListener
VM207:2 dispatchEvent

接着希望修改connection的rtt属性获取方式。

Reflect.defineProperty(navigator.connection,"rtt",{
    get:()=> 101
});

若直接修改后,收集代码在遍历connection时,将无法获取rtt属性[7]。因为该属性不可枚举。因此修改时,也要修改可枚举属性。这样收集代码最终将获取该属性。

Reflect.defineProperty(navigator.connection,"rtt",{
    get:()=> 101,
    enumerable:true
});

通过这个例子说明修改属性描述对象要关注每个属性。

验证网站

https://fingerprintjs/zh/ 这个网站点击生成指纹,可以在控制台打印出检测的数据,然后根据这些数据调整自己的浏览器信息。

总结

环境改造就是对属性或方法修改,利用上述方法基本能完成对环境的重定义。上述内容设计的知识点总结如下。

  • DOM模型

  • 标签元素对象继承关系

  • 原型对象

  • 原型链__proto__

参考

[1]Javascript面向对象编程指南(第2版) 5.1.2,p176
[2]https://wangdoc/javascript/dom/general.html
[3]https://developer.mozilla/en-US/docs/Web/API/HTMLCanvasElement
[4] https://developer.mozilla/en-US/docs/Web/API/HTMLTextAreaElement
[5] 原生只读对象劫持,https://zhuanlan.zhihu/p/24342684
[6] 指纹在线检测,https://fingerprintjs/zh/
[7] 重定义属性描述符后对象不可遍历的问题,https://stackoverflow/questions/55757089/strange-behavior-of-object-defineproperty-in-javascript

附录

1 常见属性获取

         let deviceInfo = {
            "window.speed":!window.speed?(typeof window.speed) : window.speed,
            "window.deviceorientation":!window.deviceorientation?(typeof window.deviceorientation):window.deviceorientation,
            "connectionInfo":connectionInfo(),
            "eval.toString().length":eval.toString().length,
            "'ontouchstart' in window":('ontouchstart' in window),
            "history" : {
                "history.length":history.length
            },
            "navigatorData": {
                "navigator.maxTouchPoints":1,
                "navigator.mimeTypes":navigator.mimeTypes.length,
                "navigator.vibrate":(typeof navigator.vibrate),
                "navigator.onLine":navigator.onLine,
                "navigator.userAgent": navigator.userAgent,
                "navigator.languages":navigator.languages,
                "navigator.language":navigator.language,
                "navigator.vendor":navigator.vendor,
                "navigator.appVersion":navigator.appVersion,
                "navigator.hardwareConcurrency":navigator.hardwareConcurrency,
                "navigator.devceMemory": !navigator.devceMemory ? "" : navigator.devceMemory,
                "navigator.appName":navigator.appName,
                "navigator.appCodeName":navigator.appCodeName,
                "navigator.cookieEnabled":navigator.cookieEnabled,
                "navigator.cpuClass": !navigator.cpuClass ? "" : navigator.cpuClass,
                "navigator.doNotTrack":(navigator.doNotTrack ? navigator.doNotTrack : navigator.msDoNotTrack ? navigator.msDoNotTrack : window.doNotTrack ? window.doNotTrack : undefined),
                "navigator.platform":navigator.platform,
                "navigator.product":navigator.product,
                "navigator.productSub":navigator.productSub,
                "navigator.vendorSub":navigator.vendorSub,
                "navigator.buildID":!navigator.buildID? "" : navigator.buildID,
                "navigator.connection":navigator.connection,
                "navigator.connection.effectiveType":navigator.connection ? navigator.connection.effectiveType : "",
                "navigator.plugins":navigator.plugins
            },
            "ClientTimezoneOffset":(new Date).getTimezoneOffset(),
            "window.chrome":!!window.chrome,
            "window.chrome.webstore": (!!window.chrome ? !!window.chrome.webstore : null),
            "window.CSS":!!window.CSS,
            "screenData": {
                "screen.width":screen.width,
                "screen.height":screen.height,
                "screen.availWidth":screen.availWidth,
                "screen.availHeight":screen.availHeight,
                "screen.colorDepth":screen.colorDepth,
                "screen.pixelDepth":screen.pixelDepth,
                "window.devicePixelRatio":window.devicePixelRatio,
                "screen.width * (window.devicePixelRatio || 1)":screen.width * (window.devicePixelRatio || 1),
                "screen.height * (window.devicePixelRatio || 1)":screen.height * (window.devicePixelRatio || 1)
            },
            "navigator.hardwareConcurrency":navigator.hardwareConcurrency,
            "webglInfo":webglInfo(),
            "adblockTest":adblockTest()
        }

        console.log(JSON.stringify(deviceInfo));
        

    
        navigator.getBattery().then(function(battery) {
            deviceInfo.batteryInfo = {
                "battery.level":battery.level,
                "battery.charging":battery.charging,
                "battery.dischargingTime":battery.dischargingTime
            }
            //document.writeln(JSON.stringify(deviceInfo));
        });

        function connectionInfo() {
                let nav = window.navigator;
                if (!nav.connection)
                    return "";
                var e = {};
                if (typeof nav.connection == "object") {
                    for (var t in nav.connection) {
                        if (typeof nav.connection[t] !== "function") {
                            e[t] = nav.connection[t];
                        }
                    }
                }
                return e;
        }

        function adblockTest() {
            var e = document.createElement("div");
            e.innerHTML = "&nbsp;",
            e.className = "adsbox";
            var t = !1;
            try {
                document.body && (document.body.appendChild(e),
                t = 0 === document.getElementsByClassName("adsbox")[0].offsetHeight,
                document.body.removeChild(e))
            } catch (n) {
                t = !1
            }
            return t
        }

        function webglInfo() {
            let canvasEle = document.createElement("canvas");
            let webglCtx = canvasEle.getContext("experimental-webgl");
            let webglDebugRenderInfo = webglCtx.getExtension("WEBGL_debug_renderer_info");
            let anisotropic = webglCtx.getExtension("EXT_texture_filter_anisotropic") || webglCtx.getExtension("WEBKIT_EXT_texture_filter_anisotropic") || webglCtx.getExtension("MOZ_EXT_texture_filter_anisotropic");
            let anisotropicExt = webglCtx.getParameter(anisotropic.MAX_TEXTURE_MAX_ANISOTROPY_EXT);
            let maxVertexTextureImageUnits = webglCtx.getShaderPrecisionFormat ? webglCtx.getShaderPrecisionFormat(webglCtx.VERTEX_SHADER, webglCtx.MEDIUM_FLOAT).precision : 0;
            let fragmentShaderBestPrecision = webglCtx.getShaderPrecisionFormat ? webglCtx.getShaderPrecisionFormat(webglCtx.FRAGMENT_SHADER, webglCtx.MEDIUM_FLOAT).precision : 0;
            let fragmentShaderFloatIntPrecision = (webglCtx.getShaderPrecisionFormat(webglCtx.FRAGMENT_SHADER, webglCtx.HIGH_FLOAT).precision ? "highp/" : "mediump/") + (webglCtx.getShaderPrecisionFormat(webglCtx.FRAGMENT_SHADER, webglCtx.HIGH_INT).rangeMax ? "highp" : "lowp")

            return {
                "RENDERER": webglCtx.getParameter(webglCtx.RENDERER),
                "VENDOR": webglCtx.getParameter(webglCtx.VENDOR),
                "VERSION": webglCtx.getParameter(webglCtx.VERSION),
                // 浏览器和手机一致不考虑
                "UNMASKED_RENDERER_WEBGL": webglCtx.getParameter(webglDebugRenderInfo.UNMASKED_RENDERER_WEBGL),
                "UNMASKED_VENDOR_WEBGL": webglCtx.getParameter(webglDebugRenderInfo.UNMASKED_VENDOR_WEBGL),
                "STENCIL_TEST": webglCtx.isEnabled(webglCtx.STENCIL_TEST),
                "SHADING_LANGUAGE_VERSION": webglCtx.getParameter(webglCtx.SHADING_LANGUAGE_VERSION),
                "RED_BITS": webglCtx.getParameter(webglCtx.RED_BITS),
                "GREEN_BITS": webglCtx.getParameter(webglCtx.GREEN_BITS),
                "BLUE_BITS": webglCtx.getParameter(webglCtx.BLUE_BITS),
                "ALPHA_BITS": webglCtx.getParameter(webglCtx.ALPHA_BITS),
                "MAX_RENDERBUFFER_SIZE": webglCtx.getParameter(webglCtx.MAX_RENDERBUFFER_SIZE),
                "MAX_COMBINED_TEXTURE_IMAGE_UNITS": webglCtx.getParameter(webglCtx.MAX_COMBINED_TEXTURE_IMAGE_UNITS),
                "MAX_CUBE_MAP_TEXTURE_SIZE": webglCtx.getParameter(webglCtx.MAX_CUBE_MAP_TEXTURE_SIZE),
                "MAX_FRAGMENT_UNIFORM_VECTORS": webglCtx.getParameter(webglCtx.MAX_FRAGMENT_UNIFORM_VECTORS),
                "MAX_TEXTURE_IMAGE_UNITS": webglCtx.getParameter(webglCtx.MAX_TEXTURE_IMAGE_UNITS),
                "MAX_TEXTURE_SIZE": webglCtx.getParameter(webglCtx.MAX_TEXTURE_SIZE),
                "MAX_VARYING_VECTORS": webglCtx.getParameter(webglCtx.MAX_VARYING_VECTORS),
                "MAX_VERTEX_ATTRIBS": webglCtx.getParameter(webglCtx.MAX_VERTEX_ATTRIBS),
                "MAX_VERTEX_UNIFORM_VECTORS": webglCtx.getParameter(webglCtx.MAX_VERTEX_UNIFORM_VECTORS),
                "ALIASED_LINE_WIDTH_RANGE": webglCtx.getParameter(webglCtx.ALIASED_LINE_WIDTH_RANGE),
                "ALIASED_POINT_SIZE_RANGE": webglCtx.getParameter(webglCtx.ALIASED_POINT_SIZE_RANGE),
                "MAX_VIEWPORT_DIMS": webglCtx.getParameter(webglCtx.MAX_VIEWPORT_DIMS),
                //t.getParameter(n.MAX_DRAW_BUFFERS_WEBGL) // 手机也返回null
                "anisotropicExt": anisotropicExt,
                "maxVertexTextureImageUnits": maxVertexTextureImageUnits,
                "MAX_VERTEX_TEXTURE_IMAGE_UNITS": webglCtx.getParameter(webglCtx.MAX_VERTEX_TEXTURE_IMAGE_UNITS),
                "fragmentShaderBestPrecision": fragmentShaderBestPrecision,
                "DEPTH_BITS": webglCtx.getParameter(webglCtx.DEPTH_BITS),
                "STENCIL_BITS": webglCtx.getParameter(webglCtx.STENCIL_BITS),
                "getSupportedExtensions": webglCtx.getSupportedExtensions(),
                "fragmentShaderFloatIntPrecision": fragmentShaderFloatIntPrecision
            };
        }

本文标签: 自定义属性浏览器数据