广州番禺Python爬虫小班周末班培训
第四期线下Python爬虫小班周末班已经开课了,授课详情请点击:http://chipscoco.com/?id=232
8.2.1 理解何为逆向
与逆向相对的是正向,正向即采用某种加密方式对数据进行加密,或对加密代码进行混淆,对请求过程进行多重防护,以增加反爬策略的复杂度。那么逆向就很好理解了:对加密的方式和请求过程进行破解、还原。
加密无非是采用8.1节所介绍的加密处理算法,难点在于js加密代码的定位,找出加密密钥(js代码中的密钥,或通过服务端接口、cookie返回的密钥)。
对于JS逆向的破解需保持较大的耐心,对请求链,请求参数进行梳理和分析,分析每一步的代码逻辑,然后用Python进行模拟,在Python中模拟js代码的执行,可以使用第三方模块,比如PyV8, PyExecJS等。现在分别从以下五个方面来浅析如何做JS逆向。
8.2.2 熟悉前端JavaScript
通常说的js,指的是javascript语言, 能看懂javascript代码是做js逆向的前提。编程语言在许多方面是相通的,比如直接量,变量,数据类型,控制结构等,同学们在学习javascript时,可以对照着Python语言来进行学习,以达到快速掌握的目的。
js逆向只需掌握基础的语法和数据结构,能看得懂代码、分析出代码的执行过程即可,无需深入学习。
8.2.3 快速定位请求源码
关于请求接口的分析,同学们可参照教程5.1节的chipscoco抓包案例所介绍的方法。下图所示为雪球的登录请求接口:
从上图可知登录的请求url为/snowman/login。在Headers面板中鼠标下滑到底部,可以找到登录接口的请求参数:
从上图可知给登录接口传递的参数为username,password,captcha等。
username表示用户名,password表示登录密码,captcha表示图片验证码。
在找到了请求url以后,怎么快速找到发起请求的js源代码呢? 此时可以在chrome开发者模式中按下快捷键CTRL+SHIFT+F全局搜索/snowman/login:
点击搜索结果即可跳转到发起登录请求的源码位置:
在出现的源码窗口中,可点击上图箭头所示的{}符号对代码进行格式化。继续在源码所在的视图窗口中使用快捷键CTRL+F搜索/snowman/login:
从上图可知,已经快速的定位到了发起登录请求的源码位置,为了进一步验证请求参数,可以在请求参数data所在的行设置断点(点击行号即可设置断点):
设置断点以后,退出再重新登录,然后在控制台执行console.log(n)输出请求参数(也可以在源码视窗中鼠标悬浮对应的参数来显示参数值)
调试出了请求参数以后,需要继续在当前源码窗口中进行搜索或按下CTRL+SHIFT+F进行全局搜索,然后分析参数值是如何生成的,再用Python代码进行模拟。比如雪球登录请求的geetest_challenge参数,该参数值是动态生成的,需要定位到该参数的源码位置,然后着手进行分析。
破解参数的加密方式也可以采用同样的办法,即全文搜索与加密相关的关键字,比如md5,hmac,aes, rsa,encrypt等。读者可以在网上搜罗与加密相关的其它关键字,然后在chrome中全局搜索,这是比较快捷的方法。
初学逆向的同学,需要熟练掌握chrome浏览器的使用方法:全局搜索,断点调试,查看接口的请求链等。站点也会不定期更新反爬策略,比如变更请求接口、请求参数,对代码进行混淆等。逆向并不是一门高深的技术,琐碎又乏味,但也并不简单,做逆向破解的人需要保持极大的耐心。
8.2.4 理解JS源码混淆
上节举的例子比较容易定位源码位置,但如果前端人员将请求url或其它参数名进行混淆,那么我们是搜索不出来的。举个简单的例子,前端可以将请求url/snowman/login混淆成以下形式:
"\x2f\x73\x6e\x6f\x77\x6d\x61\x6e\x2f\x6c\x6f\x67\x69\x6e"
经过混淆后的源码大幅增加了逆向破解的难度,在逆向过程中如果遇到这种情况,该怎么办呢?所谓知己知彼,百战不殆,同学们需要先掌握前端常用的代码混淆方法。
(1) 命名混淆
命名混淆主要指对直接量,变量名,函数名,参数名等变换下形式,以大幅降低源码的可读性。命名混淆很考验逆向人员的基础知识:JS语言特性,进制转换,字符编码等。以字符串直接量进行举例,JS提供了将数值对象转换为字符串对象的方法,利用这样的特性,前端人员可以先获取字符的数字编码,然后将数字编码转换为二进制或十六进制。
未混淆的JS代码:
// 为方便同学门理解,hmac的密钥只包含两个字符 var hmac_key = "A=";
混淆后的JS代码:
// 0x41是字符A的十六进制ASCII码,0x3d是字符=的十六进制ASCII码 // 通过fromCharCode方法将ascii码转换为字符,再通过+进行拼接 var hmac_key = String.fromCharCode(0x41)+String.fromCharCode(0x3d);
上文中的变量名比较明显,此时可以将可读性强的变量名hmac_key进行修改,或者使用JS中的对象来变换为更复杂的形式:
// 实际要获取的是对象e的k值 var e = {k:String.fromCharCode(0x41)+String.fromCharCode(0x3d)};
字符串对象的fromCharCode方法依然过于明显,此时又该怎么进一步混淆呢?这留给同学们进行思考。
(2) 逻辑混淆
所谓逻辑混淆,是指对代码的执行过程进行混淆,让本来一段简单的执行逻辑变得曲折陡峭。对代码逻辑进行混淆通常会使用到编程语言中的控制结构。
未混淆的JS代码:
// 前端使用jQuery框架发起ajax请求,仅需一行代码 $.ajax({type: "POST", url: "/snowman/login",data: n});
混淆后的JS代码:
// 定义函数r function r() { // 利用字符a,j,x来拼接成字符串ajax var a = ["j", "x", "a"]; // i中的元素为字符串ajax中的字符在a中的索引 var i= [2,0,2,1]; var _$r=""; // 下面的for循环看似复杂,其实逻辑很简单,就是将字符拼接为ajax for (_$i in i){ switch(i[_$i]){ case 0: _$r+=a[i[_$i]]; break; case 1: _$r+=a[1]; break; case 2: _$r+=a[i[_$i]]; break } } // 函数返回的实际是字符串"ajax" return _$r; } function c() { var _$r = {type: "POST"}; _$r["u"+"r"+"l"] = "/snowman/login"; // 调用r函数,返回字符串ajax,$[r()]等价于$["ajax"],$["ajax"]等价于$.ajax $[r()](_$r); } c();
从以上代码实例可看出,原本只需一行代码即可发起ajax请求,但经过混淆以后,代码的执行逻辑变得迂回曲折,如果继续对字符串直接量 、变量名等进行混淆,那么代码将变得更加难以阅读。
(3) 动态执行
JavaScript提供了动态执行js代码的机制,比如常见的eval函数。将一段源码字符串作为参数传递给eval,eval会将其解释为js代码并执行。
eval的代码实例:
// eval会将字符串解释为js代码并执行 eval("var a=1;var b=2;var c=a+b; console.log(c);");
利用这样的机制,可以先将源码字符串进行编码,然后在eval函数中解码再执行。编码的目的同样是增加源码阅读的难度。同学们可以在网上搜索eval加密工具,然后将上文中的字符串进行eval加密。加密后的eval代码如下:
// 原始代码为"var a=1;var b=2;var c=a+b;console.log(c);" eval(function(p,a,c,k,e,r){e=String;if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[0]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p} ('0 a=1;0 b=2;0 c=a+b;console.log(c);',[],1,'var'.split('|'),0,{}))
从上文的源码可知,该eval加密使用的方式主要为字符串替换以及正则表达式。
除了本节介绍的代码混淆方式,前端经常使用的还有源码压缩,js隐藏等技术,不会单一的只使用其中一种,会组合多种方式进行混淆。感兴趣的读者可以查找相关资料作进一步学习。
8.2.5 JS代码调试利器
js源码经过混淆以后,很难对代码的执行过程进行分析,这难倒了不少逆向人员。但同学们要知道的是,无论代码怎么混淆,最终还是要在浏览器中执行。做JS逆向最关键的是定位到参数生成或参数加密的核心代码。核心代码的定位,同学门需熟练掌握chrome浏览器的调试方法。
在这里薯条老师推荐一款开发神器webstorm, 同学们可以将待分析的JS代码全部复制到IDE中,然后在IDE中进行代码调试。
webstorm同时还支持对压缩后的代码重新格式化,以8.2.4节中的eval加密代码为例,将其复制到webstorm中:
然后按下CTRL+ALT+L快捷键对代码进行格式化:
格式化完毕以后就可以着手对代码进行调试。如需执行js代码来验证输出,也可以在视窗中右键点击run菜单来执行窗口中的js代码。下图所示为eval加密代码执行后的输出结果:
8.2.6 模拟JS代码的执行
做完逆向工作以后,python爬虫工程师最后需要做的是将与加密相关的核心代码用python模拟出来,然而实际情况是经过混淆、打包后的代码错综复杂,难以模拟。此时我们可以整段代码封装为单独的函数调用,再将js代码保存到js脚本文件中,最后通过Python中的第三方模块来执行文件中的js代码,获得js程序的输出。
Python社区比较流行的js执行工具有PyV8, PyExecJS等。本节以PyExecJS为例,来讲解在Python中模拟js代码执行的用法。
PyExecJS的安装很简单,直接在命令行中执行pip install PyExecJS。下图所示为未混淆的js代码:
该js代码定义了一个cipher函数,使用crypto-js对请求参数进行加密。
crypto-js的github地址:https://github.com/brix/crypto-js
在webstorm中执行窗口中的js脚本程序,来测试crypto-js的AES加密功能:
现在将该js代码使用eval进行混淆,下图所示为经过eval处理后的混淆代码:
继续点击窗口中的运行菜单:
输出结果与混淆前的输出是一样的,这说明,即便代码经过混淆,经过eval加密处理,依然可以执行。现在通过Python中的execjs来执行上文中的js加密代码:
// 导入execjs模块 import execjs with open(r'aes_encrypt.js', 'r', encoding='utf8') as f: js_code = f.read() # 执行compile方法将js源码编译为上下文处理对象 js_context = execjs.compile(js_code, # cwd指向的是通过npm安装的Node模块的路径 cwd=r'C:\Users\86188\AppData\Roaming\npm\node_modules') # 通过上下文对象调用源码中的cipher函数 print(js_context.call("cipher", '{"author": "薯条老师"}', "Ad12=/w#$N1?8=E&"))
(1) Python后端工程师高薪就业班,月薪10K-15K,免费领取课程大纲
(2) Python爬虫工程师高薪就业班,年薪十五万,免费领取课程大纲
(3) Java后端开发工程师高薪就业班,月薪10K-20K, 免费领取课程大纲
(4) Python大数据工程师就业班,月薪12K-25K,免费领取课程大纲
扫码免费领取学习资料: