前言
为了模拟实现navigator plugins与mimetyps,大致需要做四件事,调整数组类型和数组成员类型、补充缺失的函数、修改toString方法及对象替换。如下的总结虽未模拟完全,但可作为一种启发参考。其它操作与此类似。
1 调整数组类型和数组成员类型
正常navigator.plugins对象数组中成员类型是Plugin,数组类型为PluginArray。正常navigator.mimeTypes数组成员类型是MimeType,数组类型mimeTypes。
1 | PluginArray {0: Plugin, 1: Plugin, 2: Plugin, Chrome PDF Plugin: Plugin, Chrome PDF Viewer: Plugin, Native Client: Plugin, length: 3} |
1 | MimeTypeArray {0: MimeType, 1: MimeType, 2: MimeType, 3: MimeType, application/pdf: MimeType, application/x-google-chrome-pdf: MimeType, application/x-nacl: MimeType, application/x-pnacl: MimeType, length: 4} |
Plugin,MimeType,PluginsArray,MimeTypeArray均是浏览器内置构造函数。为了模拟改造,所创建模拟数据和数组成员也应是对应的类型,首先创建普通对象数组。
1 | let mimeTypes = [{ |
调整成员类型和数组类型,需要修改原型对象。
1 | mimeTypes.map(o => Object.setPrototypeOf(o,MimeType.prototype)); |
2 补充缺失的函数
这两个对象在原型链上还可以找到其它辅助函数。
1 | navigator.plugins.__proto__ |
1 | navigator.mimeTypes.__proto__ |
这个功能通过给对象直接赋值即可mimeTypes.namedItem = function() {}
3 修改toString方法
正常的toString方法如下。
1 | navigator.plugins.item.toString(); |
由于调用一个函数的toString方法,如果没提供覆盖实现,最终会通过原型链找到Function对象的toString方法。因此我们只要对Function的toString加一层代理。如果发现当前调用了我们自定义的方法,那么返回一个含有native的描述。如果调用的不是我们自定义的方法则放行。
- 如果调用了方法代理方法本身的String,那么返回一段toString描述。
- 如果是调用了我们自己实现的自定义方法,则返回一段自定义文本,并改变方法的名称。
- 如果调用的不是我们自定义方法,则放行,调原始方法。
1 | const makeFnsNative = (fns = []) => { |
4 对象替换
最后就是将我们修改的结果进行替换,即重新定时属性描述符的get方法。
1 | Object.defineProperty(navigator, 'plugins', { |
5 总结
以上介绍了组略介绍了修改的内容,还未模拟完全。但通过这些作为一个简单的启发,那么其它内容也可以以此方式分析进行修改。
6 参考
[1].plugin的模拟实现,https://github.com/berstend/puppeteer-extra/tree/master/packages/puppeteer-extra-plugin-stealth