某网站采用了多种反破解手段,特此开贴记录分析破解过程。
工具和参考
- awesome-java-crawler - 我收集的爬虫相关工具和资料
- java-curl - 本人写的java HTTP库,可用来替换chrome网络后端,更方便控制底层行为,如缓存、代理、监控、修改请求和应答等
- cdp4j - java版的Chrome Devtools Protocol实现,用于控制Chrome浏览器,没有特征哟
- beautifier.io - js代码在线格式化
- 类似网站的破解 - 神箭手云的大佬写的
- 很早的一篇分析文 - 看特征是这种加密的早期版本
网站反破解手段
反动态分析(即反调试)
- Chrome中,F12打开开发者控制台,发现立即停在断点上了
- 断点处源码为:
(function() {var a = new Date(); debugger; return new Date() - a > 100;}())
-
此处可能有两种作用:
- 使用debugger调用显式设定断点,且使用定时器高频次调用此段代码,如果破解者不厌其烦,禁用调试,那么破解者等于自缚手脚,也无法设定断点进行动态分析了
- 此方法返回了debugger语句的执行时间是否大于100毫秒,意味着可以根据返回值判断出是否有破解者正在试图调试当前脚本
代码动态混淆
- 代码经过混淆,所有变量,方法都被更名为形似"_$xx",代码中所有字符串和部分常量也被替换,部分常用方法也被替换。因此基本上不可读
-
以上述方式加密的代码共分4部分。
-
一部份代码加密混淆后放在html文档内部,同时还在文档头部有大量高度混淆的纯数据,此部分估计也是加密的代码,类似
<meta id="9DhefwqGPrzGxEp9hPaoag" content="LotupvYp..."/>
- 一代码直接在页面加载js获取
- 还有两小段代码应是页面js执行过程中动态加载
-
- 以上述方式加密的4段js代码,每次刷新都会变化,不仅仅是变量名有变化,方法顺序也有变化,估计要么是服务端有一堆预先生成的js,或者是每次服务器端根据AST重新命名并乱序后生成等价的js
- 此手段导致很难动态跟踪分析,因为在pretty print后的某行代码上设定断点后重新刷新执行,由于js变化了,之前设的断点就失败了
- 对策,本地代理+自定义缓存,避免每次代码变动,这样既可进行动态跟踪分析
客户端生成Cookie
- 页面加载完成后,分析cookie,发现有一个FSSBBIl1UgzbN7N80T的值和前面应答中Set-Cookie的值不一致,说明客户端js对cookie进行了修改
- 此Cookie的值很长,估计包含浏览器特征等关键信息
- 常规手段无法对cookie的读写设定断点,不过github上找到几个可以增强js断点能力的库/插件
- 目前采用javascript-breakpoint-collection来在cookie修改处设定断点
- 具体方法是用Page.addScriptToEvaluateOnNewDocument()把上述库js代码注入,这样可以确保在其它脚本加载前注入
- 设定断点后,发现页面首次加载会设定一次cookie值,然后每隔1分钟左右还会更新该值
- 但是因为代码高度加密,尚无法解析cookie值生成算法
反PhantomJS, Selenium
- 通过动态分析调试,可知此种加密算法将全部字符串常量保存在全局对象中,其变量名特征为_$xx
- 因此,可以在控制台运行一小段程序遍历window对象中所有前缀为_$的变量,从而获取到字符串映射表
- 通过此方法导出的映射表中包含以下内容:
"_$iQ": "_Selenium_IDE_Recorder,_selenium,callSelenium","_$cv": "__driver_evaluate,__webdriver_evaluate,__selenium_evaluate,__fxdriver_evaluate,__driver_unwrapped,__webdriver_unwrapped,__selenium_unwrapped,__fxdriver_unwrapped,__webdriver_script_func,__webdriver_script_fn","_$a6": "webdriver-evaluate","_$bs": "callPhantom,_phantom",
- 由以上代码可知,前端代码对phantomjs, selenium等常用浏览器自动化框架的特征有判断或采集行为,并可采取针对性措施,比如应答错误数据等
-
因此,如果使用基于浏览器的破解方案,只能采取以下述方案之一:
- 自行修改自动化框架,修改特征
- 修改前端js代码,改掉特征检测逻辑
- 使用其它不基于webdriver (selenium), phantomjs的浏览器自动化框架
禁用桌面版浏览器
- 以前用cdp4j控制chrome模拟整个流程是ok的,但是某网最近一次更新后,彻底禁用了桌面版chrome,其表现为可以打开前几个页面,但是登录和计费提交时返回201,404等非正常应答
- 估计是在前面步骤收集浏览器特征,加密后放到cookie中,在若干关键步骤如登录和计费的服务端处理中,解析出浏览器特征,禁用掉桌面版浏览器
- 试过覆盖User-Agent, navigator.platform='Linux armv7l'和window.width/height等,但无效,在未能分析完整特征收集代码的情况下,很难猜测其逻辑
使用安卓版浏览器的坑
- 使用真实手机安装Chrome69版,确保adb连接后,使用这篇文档的方法,即可从桌面浏览器连接到手机Chrome并可使用inspector页面远程控制
- 但是安卓版inspector的地址在blogspot上,在墙外,因此还需要翻墙……
- 因为真实手机对整个程序的架构和成本有重大影响,因此尝试改用模拟器
- 测试过逍遥和夜神,后来选用逍遥
- 使用前面的chrome apk包可以正常安装,但打开页面空白
- 经搜索发现需要x86版的chrome,在apkmirror下载到69版,模拟器安装后可以正常使用
- 使用cdp4j连接,发现连接成功的几率非常低,Target.createTarget返回的targetId无法正常连接
- 在连接成功的一次中,发现模拟鼠标滑动失效,怀疑是桌面版和安卓版的坐标计算方法不同
- 可能需要综合分析inspector的api调用顺序后,自己编写cdp连接chrome的代码。需要借助chrome-protocol-proxy分析inspector的具体逻辑
- 或者验证下chrome-remote-interface能否正常连接chrome,如正常则可以参考node版本编写java版本
IP限制
- 注册用户/修改密码的SSO站点和主站地址不一样,估计是另一批人做的,这个站点对IP进行限制
-
目前发现的规则是:
- 所有的ISP(机房)IP 直接封掉,这一招干掉了所有用云主机和肉鸡做代理的
- 芝麻代理等几家的收费IP也大部分不可用,估计是同行用过被加入黑名单了
鼠标动作收集
- 使用注入js代码模拟点击按钮即可实现大部分页面的操作
- 但是在计费对话框页面上,直接js代码导致服务器返回错误
- 发现需要模拟真实鼠标点击,而且不能简单直接点击按钮,需要在页面的其它部分先点击一下再点击提交按钮才能生效,估计是js收集了鼠标动作,提交时将数据加密后放到"MmEwMD"参数中
目前已发现使用此手段加密的网站收集:
- 食品药品监督管理局
- 中国商标网
- 裁判文书网
- 咪咕阅读
- 神州租车
- 湖南移动
- 网上房地产