中国商标网JS调试 - 动态代码注入

  • 中国商标网JS调试 - 动态代码注入
    • 前言
      • 背景
      • 工具
      • 知识点
    • 正文
      • 了解 Fiddler Script
        • Session 处理函数
      • 反调试策略
        • 问题分析
        • 解决思路
        • 注入代码
        • 关于 7cLOtPi5wrHA.5780574.js
          • 如何调试``while(1)``内的执行流程?
  • 最后
  • 补充说明
  • 参考
  • 相关
    • 作者
    • 文章

中国商标网JS调试 - 动态代码注入

前言

  1. 中国商标网地址:http://wcjs.sbj.cnipa.gov.cn/txnT01.do
  2. 本文的主要目的并不是对中国商标网的爬虫实现,而是对其反爬机制的核心突破。由于反爬策略的更新频繁,不保证本文中的代码的时效性。
  3. 本文不会对JS脚本从头开始逆向分析,而是有选择性的挑选一些有趣反爬机制进行描述其解决思路。
  4. 本文主要是记录中国商标网的调试过程,不会提供任何关于爬虫实现的代码。!!!!!!!!!别问我要商标网的爬虫代码!!!,因为我也没有。

背景

  1. 中国商标网在反爬安全上是属于标杆的存在。其反爬技术提供者是瑞数信息,反爬机制是属于动态安全
  2. 对于动态反爬机制最核心的地方不是token加密算法,而是其反调试策略。
  3. 任何动态的事物都是基于静态产生的,所以只要找到静态点,那么动态就会坍缩为静态。

工具

  • Fiddler: 尽可能使用新版本,如果必要,至少要带Fiddler Script功能。
  • 谷歌浏览器:字面意思,也是尽可能使用新版本。

知识点

  • JavaScript
  • Fiddler Script
  • 谷歌浏览器开发者工具

正文

在背景里面我有说过中国商标网的反爬机制是动态反爬,那么根据定理:

必须用魔法打败魔法。

我们必须要用动态打破动态。

本文章的核心思路是代码注入。代码注入是字面意思,其作用就是在脚本执行之前插入自己的代码,以实现对代码的查看修改替换等各种操作。通过 Fiddler 可以实现代码注入,下面会对其实现原理进行简单的介绍。如对该知识点了解请跳过。

了解 Fiddler Script

众所周知Fiddler是一个很强大的抓包工具,但是大多数人对它的印象主要是抓包,但其实其最强大的功能实属FiddlerScript,下面引用《Fiddler调试权威指南 - Debugging with Fiddler》FiddlerScript的描述。

Fiddler在处理每个Session时,脚本文件CostomRules.js中的方法都会运行,该脚本使得你可以隐藏、表示或任意修改复杂的Session。规则脚本在运行状态下就可以修改并重新编译,不需要重新启动Fiddler

上面一句话简单的来说就是Fiddler作为一个中间者(代理服务器),其FiddlerScript可以实现在客户端与服务器之间的数据请求转发过程中进行修改。当然作为规则脚本还可以对Fiddler的业务功能进行拓展。

所以,根据上面所说,我们就可以理解代码注入就是在服务器返回请求响应之后,Fiddler作为中间者根据预先编写的FiddlerScript来将代码注入到响应中,之后再将响应转发给 客户端(本文所说的客户端主要是浏览器)。以上的描述的过程体现在下面的流程图中。

Session 处理函数

编译FiddlerScript时,Fiddler保留了某些关键静态函数的引用,这些函数都能在Handlers类中找到。

除了OnReturningError外,以下的处理函数Handlers按照了其执行顺序进行描述:

  • OnPeekAtRequestHeaders: 在客户端发送请求头(Request Header)之后,中间者接收到其请求头后进入该处理函数。
  • OnBeforeRequest: 在客户端发送请求体(Request Body)之后,中间者接收到请求体后进入该处理函数。之后将新的请求头和请求体转发给服务器。
  • OnPeekAtResponseHeaders: 在服务器返回响应头(Response Headers)之后,中间者接收到响应头后进入该处理函数,之后将请求头转发给客户端。
  • OnBeforeResponse: 在服务器返回响应体(Response Body)之后,中间者接收到响应体后进入该处理函数,之后将请求体转发给客户端。
  • OnReturningError: 在Fiddler生成的错误信息(如“DNS Lookup Failed”)返回给客户端时被调用。通过这个函数可以定制客户端应用看到的错误信息。

下图描述了Session处理函数的流程

在本文中,代码注入是在OnBeforeResponse处理函数中进行。

为了加深对代码注入方法的理解,我们不妨拿中国商标网来练练手。

反调试策略

浏览器打开商标网,刚打开开发者工具(F12),发现调试工具触发了中断,无一例外都停在了debugger处。

debugger指令是调试器断点,在未进入调试器的情况下,JS引擎会忽略任何debugger指令。

有很多人其实看到这种情况就已经束手无策,这反调试机制是建立在动态脚本的前提下的,这在传统方法来看确实是没有任何招架之力。如非要逆向这些脚本,就必须要硬啃代码。

问题分析

事实上,存在两处debugger指令,一处是鼠标事件(MouseEvent)触发的,另一处是定时器(setInterval)触发的,在debugger指令的前后进行了时间差new Date().getTime()方式判断,通常只要时间差大于几百毫秒或者更低时间差内就会触发反调试防御,在这几百毫秒内一般来说无法当然只要你不继续执行代码,就不会被检测到。

既然这是为调试器准备的反调试策略,那么我们直接把他删掉或者注释掉就能避免调试器中断。透过Fiddler这个中间者,我们就能动态注入代码。

有一个问题是,要注入什么地方,要修改什么地方?没错,只要删掉debugger指令就能卸除debugger反调试防御。但是debugger指令在哪里?

我们可以注意到上图中的标签页的名称是VM+数字,这种组合的名称通常这是为了区别原网页的JS脚本,是由eval()方法产生的或者是ajax方法获取的。商标网的debugger所处的脚本是通过eval()动态执行的脚本,在本文我们不讨论如何一步一步找eval()执行的位置1。这里直接说结果,eval()在下图中的下红框中执行。

解决思路

如果debugger是在原网页的JS脚本中,即使是动态脚本,也可以直接修改脚本代码以删除debugger。但是这里经过了在脚本中eval(string)来代理执行的代码,debugger存在于string中,所以只能通过将代码注入到string才能修改删除debugger。所以我们需要在脚本中的eval(string)的执行之前通过注入修改string的代码来达到修改删除debugger,之后执行的eval(new_string)就是经过了去debugger的代码,这样反调试防御就能卸下来了。

这里面的一个技巧是,在eval(string)执行的刚好之前,string必然是已经经过了解密了的代码字符串,这样在这恰好之前注入代码可以直接跳过脚本解密这个过程,完全可以不用在意解密算法。

如下代码,既然这是动态的脚本,那么怎么定位呢?一个最简单的办法其实就是找特征信息,然后使用正则匹配就行了。

...
} else if (_$71 < 75) {  // 注意,这里的75和除了ret外的所有变量名也是动态生成的,不应作为特性值ret = _$Az.call(_$j1, _$8t);} else {...} ...

既然有这么多特征值,那么__/ret\s*=\s*[\w\$]+\.call\([\w\$]+,\s*([\w\$]+)\)/__,这一正则就能定位。

注入代码

既然是通过Fiddler来实现代码注入,我们肯定要先搞清楚如何进行编写FiddlerScript来实现代码注入,关于FiddlerScript的介绍可以参考书《Fiddler调试权威指南 - Debugging with Fiddler》和ModifyRequestOrResponse。

通过 Rule - Customize Rules 进入Fiddler脚本编辑器 Fiddler ScriptEditor

FiddlerScript的脚本编写语言是JScript.NET,这是微软自己开发的IE脚本执行引擎,也是按照ECMAScript标准的,所以一般我直接把它当JavaScript来用,当然其中存在一定的使用差异。

好了,我们进入Fiddler ScriptEditor,找到OnBeforeResponse处理函数。加入如下代码。

static function OnBeforeResponse(oSession: Session) {if (m_Hide304s && oSession.responseCode == 304) {oSession["ui-hide"] = "true";}// 以下为商标网的代码注入// 注意的是,对Fiddler来说,所有经过Fiddler的请求都会经过该处理函数,这就要求你自己对请求进行筛选,否则将会对所有请求都执行对应的操作。我这里仅仅是对域名为 "wcjs.sbj.cnipa.gov.cn" 和其请求头中"Content-Type" 包含 "html"的请求进行响应的处理。关于oSession对象,可以参见参考处的链接或书籍。if (oSession.HostnameIs("wcjs.sbj.cnipa.gov.cn") && oSession.oResponse.headers.ExistsAndContains("Content-Type", "html")){// 这一步我们将响应将Body作为字符串解析。var oBody = oSession.GetResponseBodyAsString();var newBody = oBody;var oRegEx = /\bret\s*=\s*[\w\$]+\.call\([\w\$]+,\s*([\w\$]+)\)/;var oEvalLineRes = oBody.match(oRegEx);if(oEvalLineRes !== null){var oLine = oEvalLineRes[0];var oCodeVar = oEvalLineRes[1];// 创建被注入的代码,通过修改脚本代码string来实现取出debugger指令。var rmDbg1Code = "var rmDbg1Res = " + oCodeVar + ".replace(/\\bdebugger\\s*;/, '') ;";var rmDbg2Code = "var dbg2RegEx = /\\{\\s*\\bvar\\s*([\\w\\$]+)\\s*=\\s*[\\w\\$]+\\[[\\w\\$]+\\[\\d+\\]\\]\\([\\w\\$]+\\(.*?\\)\\);\\s*\\}/;" + "var dbg2assignVarName = rmDbg1Res.match(dbg2RegEx)[1];" + "var rmDbg2Res = rmDbg1Res.replace(dbg2RegEx, '{var ' + dbg2assignVarName + '=false;}');";// 替换成经过处理后的代码所在的变量var newEvalCode = oLine.replace(oCodeVar, "rmDbg2Res");// 注入代码到脚本。newBody = newBody.replace(oLine, rmDbg1Code + rmDbg2Code + newEvalCode);}// 替换响应体oSession.utilSetResponseBody(newBody);}
}

将以上代码添加完成后,保存即可生效。这时再尝试在商标网内打开F12就会发现不再发生中断,因为debugger已被移除。如下图就是注入代码后的新响应。

到目前为止,我们无法被debugger指令所限制,但是我们真的就能随心所欲的进行调试了吗?不是的,还不能,前面说了这是动态生成的脚本2,我们无法由谷歌浏览器的开发工具帮我们记录断点位置,这样我们就不能反复的进行调试。

既然调试器无法帮助我们准确的记录断点位置,我们只能另寻他法。既然反调试防御是通过debugger指令实现的,我们要以牙还牙,通过正则表达式定位并注入debugger进行准确的动态断点调试。

为了让文章尽量简洁易懂,这里不通过举大量例子来演示,不然容易让人产生困惑而且写起来累人。。只要你能领悟上面的代码注入示例,后续注入debugger不会是问题,所以这里不再进行演示。

在本文中我们都是通过正则表达式进行定位,如果有必要,可以通过构建RPC调用任何你想要实现的第三方程序处理,进行语义树解析定位什么的。

事实上,掌握以上的方法后,只要你够__肝__,你完全可以逆向整个提交流程。

如果是通过Selenium实现可能会更简单点,可以直接注入代码,将Selenium等自动化测试软件的判断屏蔽掉,什么无头检测之类的,还有鼠标事件动态产生的Cookie算法。如果不打算进行模拟鼠标则需要逆向其算法,所以工作量也不会少太多。

关于 7cLOtPi5wrHA.5780574.js

(function() {var _$l5 = 0, _$Q1 = $_ts.scj, _$_d = $_ts.aebi;// 下面这类函数属于事件或定时器触发函数function _$WM() {var _$UC = [459];Array.prototype.push.apply(_$UC, arguments);return _$Tb.apply(this, _$UC);}// ...忽略多个同类... //var _$Jv = [], _$X9 = String.fromCharCode;// 下列函数主要用于生成对象的混淆属性名数组,每一个元素对应一个属性。_$30('rtrc0ccavodcr`ch}r`.`uars`,`}a|c|ch}r`b.........'); // 下列一堆变量经过二次代理内置函数/方法,也属于混淆的一种。var _$Sp, _$0x = null;var _$uR = window, _$8J = String;var _$Xc = _$uR["XMLHttpRequest"];var _$1q = _$uR["ActiveXObject"];// ...忽略多个同类... //// 以下是简单的通过简单的浏览器端判断等操作,同时启动了准备了一个反调试判断的时间戳_$iU();// ...// 由下面该字符串通过charCodeAt()进行多种操作后生成的数组,将参与后续很多算法运算。 var _$2H = _$ZH["call"]("qrcklmDoExthWJiHAp1sVYKU3RFMQw8IGfPO92bvLNj.7zXBaSnu0TC6gy_4Ze5d{}|~ !#$%()*+,-;=?@[]^", '');_$S3();// 以下调用将<meta>元素的content进行解密,并且将meta节点删掉,这一节点的内容将影响后续生成的input#__onload__节点,并用于生成/?kmcmNx0Q=..._$Vt(_$9k());_$Tb(768, \d); // Cookie 加密处理// 后面的太多了,不分析了

注意:以上代码我注入了属性名反混淆的代码,所以显示的是原属性名。其实还可以将内置函数/方法进行复原对象,可读性更高。

如何调试while(1)内的执行流程?

对于上面这些流程走向明显的都很好分析,但是对于while(1)或者for(;;)没有调用栈记录的,我们该怎么办?怎么去分析调试呢?相信看过 爱奇艺视频cmd5x解析算法的移植分析和实现.2019-08的应该知道,当时我在调试for(;;)过程中使用的是对做决策的变量进行记录,也就是说,通过记录做决策的变量的走向来判断其执行流程。那么对于现在这个情况,我们需要通过注入代码到while(1)内的决策变量来进行分析流程。

最后

没了

补充说明

[1] eval(string)方法中,大家可以想一下string从何而来?可以发现的是网页就引入的JS脚本不多,其中一个脚本比较可疑 - 7cLOtPi5wrHA.5780574.js中的$_ts['5780574']变量,可以看到该变量的字符串内容明显存在不合语法的内容,有理由猜测是首先经过一层解密后再进行的eval(),如果你不是瞎调的一派,其实可以尝试下写入破坏性数据进入该字符串,然后在eval()过程中就会发现语法错误并抛出异常就能发现是哪一步执行的。事实上这里在调用栈图1的调用栈就能追溯到eval()的位置,看调用栈所对应的文件就能找到。

[2] 这里所说的动态生成的脚本事实上是这样的,7cLOtPi5wrHA.5780574.js脚本是静态的这毫无疑问。为什么最后说是动态脚本呢?因为真正的动态只有原网页txnT01.do的源代码,其中包括<meta>再内的content,里面的内容很奇怪对吧?是的,它经过解密后用于生成临时表单等东西,它也是动态生成的。第二行的5维数组的变量包括下面的if else决策树等也是动态产生的,这里面的数组里面的数值决定着后续的执行顺序,这非常重要,那怎么办?到底静态点在哪里?发现第二级数组的长度是不变的吗?是的,数组的值是动态的,但位置的作用其实是固定的。比如变量的[1][14]位置开始就是用来链接一个动态生成的字符串,并且由该字符串构建静态脚本7cLOtPi5wrHA.5780574.js的变量名,这样,一个静态脚本由动态脚本转成了动态。

参考

[1] Fiddler调试权威指南(Debugging with Fiddler), Eric Lawrence, 祝洪凯 / 李妹芳

[2] FiddlerScript CookBook, http://www.fiddlerbook.com/Fiddler/dev/ScriptSamples.asp

[3] ModifyRequestOrResponse, https://docs.telerik.com/fiddler/KnowledgeBase/FiddlerScript/ModifyRequestOrResponse

相关

作者

【1】 GitHub

【2】GitHub Pages

【3】 CSDN

文章

【1】 爱奇艺视频cmd5x解析算法的移植分析和实现.2019-08

【2】 腾讯视频cKey9.1的生成分析和实现

【3】爱奇艺视频H5解析分析过程

中国商标网JS调试 - 动态代码注入相关推荐

  1. [免费专栏] Android安全之利用JDB调试Android应用程序(动态代码注入技术)

    也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大 少走了弯路,也就错过了风景,无论如何,感谢经历 Android安全付费专栏长期更新,本篇最新内容请前往: [ ...

  2. 2019-04-07我破解了中国商标网

    2019-04-07我破解了中国商标网 历时2天,接口还是没搞明白,先用selenium搞了下,只不过比较慢. 历时2天,接口还是没搞明白,先用selenium搞了下,只不过比较慢. 太累了,明天把代 ...

  3. 前端信息查询与显示_中国商标网查询显示的信息都是正确的吗

    说起商标查询,虽然市场上有各种各样的第三方查询工具,但"中国商标网"一定中夜空中最闪亮的那颗星.因为中国商标网是国家知识产权局商标局的官方查询网站,上面的商标信息及时又权威,实乃商 ...

  4. 中国土地市场网-js解密

    一.背景 目标链接:http://www.landchina.com/default.aspx?tabid=226 ,中国土地市场网,获取相应的行政区代码.标题.详情页链接和发布时间 二.过程 1.确 ...

  5. 知名Node.js组件存在代码注入漏洞

    喜欢就关注我们吧! 日前,一个被大量下载的 Node.js 组件被发现其含有一个高危的代码注入漏洞. 该漏洞被追踪为 CVE-2021-21315,影响了「systeminformation」npm ...

  6. 最新中国土地市场网JS逆向分析

    目标网址:中国土地市场网 重要说明:文章教程仅供参考学习,请勿用于非法用途,否则后果自负. 目录 一.接口参数分析 二.程序代码编写

  7. 中国天气网接口、城市代码、解析

    中国天气网提供的天气信息对国内用户来说,还是非常全面的,以南京为例,通过访问 http://m.weather.com.cn/data/101190101.html 返回的数据是: {"we ...

  8. 爬虫:破解同花顺网js加密动态生成请求中所需要的cookie

    看了半天帖子都是都是通过selenium破解js加密的,个人感觉用selenium破解js加密效率太低,而且繁琐,根据目前业务需求就自己研究了一下同花顺的js加密. 通过接口测试工具直接请求接口发现获 ...

  9. 商标局、中国商标网爬虫

    如题,商标目前商标数据有6千万左右,并且定时更新中,有问题直接站内私信即可.

最新文章

  1. 地方弱势运营商如何发展宽带业务?
  2. mysql的innodb数据库引擎详解
  3. osx doc to html,macos – 在OSX上安装Git HTML帮助
  4. “知识图谱+”系列:知识图谱+图神经网络
  5. qt中使用QStringLiteral宏来实现带参数的输出
  6. 2012年最佳30款免费 WordPress 主题
  7. ros简版Action通讯SimpleAction
  8. 【CCCC】L3-006 迎风一刀斩 (30分),几何关系,找规律 (拼合多边形==斜边等价)
  9. Core 提交返回500 问题 记录 来自网上文章
  10. Java 并发编程常识 —— by 梁飞
  11. itext-asian-5.2.0
  12. 互联网国家缩写代码一览表
  13. 录入数学公式至mark down文档的方法
  14. OpenCV-RGB转HSV
  15. python 拦截windows弹窗广告_Win10如何拦截桌面弹窗广告?流氓软件怎么彻底清除?...
  16. 设计模式——仲裁模式
  17. java close 方法,close()方法的用法(Java初学者)
  18. 大数据——DBT:数据治理、血缘关系DBT的安装及测试(基础使用篇)
  19. 二级路由器的设置上网
  20. cad.net 图层隐藏 IsHidden 用法 eDuplicateRecordName 报错

热门文章

  1. linux教程第五版课后答案第六章,linux基础及应用第六章练习题
  2. 如何增强云端医疗健康数据的隐私保护
  3. echarts地图设置区块点击后颜色不改变
  4. 计算机通过网线连接不到网络,电脑有网线连不上网怎么解决
  5. python matplotlib searbon 设置画版颜色 热力图固定颜色等级 固定比例尺寸大小
  6. 利用 @media screen 实现网页布局的自适应,@media screen and
  7. 【回溯法】机器零件加工-最优加工顺序
  8. best time to cooldown
  9. 《自己动手写嵌入式操作系统》阅读笔记之操作系统小知识
  10. linux课程总结范文,大学课程学习心得体会5篇.doc