一种基于浏览器内核修改的复杂网站layout/渲染bug的HTTP重放调试系统
缘起
做移动浏览器开发/浏览器内核定制修改的,总会遇到这么一些诡异的问题:某网站A,出现奇怪的渲染显示错误、UI界面阻塞、乃至突然崩溃。一般来说,我们有下面几种解决问题的思路:
- 在浏览器内核代码中打log,前提是你已经大概估计到了问题原因的大概范围
- 使用远程调试,即使用桌面版Chrome的DevTools来调试手机浏览器的JS代码执行
- 将网站A离线保存,然后进行代码裁剪,以制作一个最小testcase
- 不幸的是,大部分现实世界中的网站都使用大量复杂的JS类库,甚至JS代码被混淆过
- 同时还有分散到不同子网站的资源(JS/图片/CSS),以及Ajax请求
- 使用特殊的Windows Debug版在调试环境下运行,这通常用于解决某类UI线程阻塞卡死、异常崩溃的问题
- 用C++调试器及其“暂停功能”可以快速定位到问题发生时的Call Stack现场
- 或者想办法触发一个手工的CRASH(kill?),然后将backtrace信息导出来,通过符号解码得到崩溃栈
解决方案
我们需要能够将一次在线的交互式页面浏览过程转换为离线的自动Replay过程。但是我们不想为了达到这一目标而执行太多手工的活。How?
思路如下:
- 修改浏览器内核,对每个对网站A的在线HTTP请求接受到的数据,在完全接收到数据之后,暂停接下来的Parse-Layout过程,而是先同步地将此HTTP响应数据发送到内网本地服务器(SOA)
- 接下来,重新发起请求,这次我们不是访问原始的服务器A,而是访问之前保存下来的数据,How?2种方法:
- 配置代理服务器,通过HTTP Proxy协议,架设一个Hacking代理服务器,浏览器以为它现在仅仅是通过代理访问A网站,它错了,实际上现在它收到的数据是本地服务器缓存的(这里可以进一步修改定制这些数据!)
- 使用透明代理,即指定一个特殊的网关G,将手机连接到一个定制的WiFi网络,使得所有IP请求都会首先经过G转发,G随后配置特殊的网络应用层框架和Hacking修改代码,以使得浏览器接下来收到的数据都是本地缓存的
- 上面2个方法都没有解决HTTPS的问题,其实这个问题可以通过修改浏览器内核来做到(当然,要关闭安全性检查等模块)只使用HTTP来访问,因为我们的目标是调查解决内核引擎的bug;或者也可以配置网络环境(DNS和自签名证书),让内网服务器权当原始网站A使用
- 如果配置了DNS欺骗的话,实际上倒没有必要保存所有的响应数据,修改也可以是根据字符串替换规则JIT的
- 下一步就是修改网站的原始JS代码,插入console.log语句,这些log最终会在手机的终端上输出,从而至少可以定位到发生问题的前一刻执行的JS代码到哪里了
主要技术难点
- 内核修改:使得可以同步地发送HTTP(TCP)大数据包请求;
- 使用脚本语言编写一个基本的HTTP代理服务器,同时还要允许对特定URL的模式匹配和响应BODY字符串的修改
实际上这类问题最好是应该由这些网站来解决,不幸的是,作为浏览器内核研发人员,似乎我们的工作主要是修改这类诡异的兼容性Bug。基本上等于是在做Web反向工程的活。