Python爬虫教程

第一章: 初学乍练-Python快速入门

第二章: 初窥门径-从全局把握网络爬虫

第三章: 爬虫数据-网页与JSON

第四章: 爬虫核心-HTTP协议

第五章: 手到擒来-数据的抓包

第六章: 利刃出鞘-HTTP请求库

第七章: 尘埃落定-数据的解析

第八章: 逆向初探-JS逆向

第九章: 爬虫进阶-Selenium, 中间人拦截

第十章:斗转星移-常用的反爬策略及应对方法

首页 > Python爬虫教程 > 第八章: 逆向初探-JS逆向 > 8.2节:浅析JS逆向

8.2节:浅析JS逆向

薯条老师 2021-03-15 10:52:03 236030 0

编辑 收藏

广州番禺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抓包案例所介绍的方法。下图所示为雪球的登录请求接口:

1.jpg 

从上图可知登录的请求url为/snowman/login。在Headers面板中鼠标下滑到底部,可以找到登录接口的请求参数:

 2.jpg

从上图可知给登录接口传递的参数为username,password,captcha等。

username表示用户名,password表示登录密码,captcha表示图片验证码。

在找到了请求url以后,怎么快速找到发起请求的js源代码呢? 此时可以在chrome开发者模式中按下快捷键CTRL+SHIFT+F全局搜索/snowman/login:

3.jpg 

点击搜索结果即可跳转到发起登录请求的源码位置:

1615776925(1).jpg

在出现的源码窗口中,可点击上图箭头所示的{}符号对代码进行格式化。继续在源码所在的视图窗口中使用快捷键CTRL+F搜索/snowman/login:

1615776970(1).jpg 

从上图可知,已经快速的定位到了发起登录请求的源码位置,为了进一步验证请求参数,可以在请求参数data所在的行设置断点(点击行号即可设置断点):

 1615776999(1).jpg

设置断点以后,退出再重新登录,然后在控制台执行console.log(n)输出请求参数(也可以在源码视窗中鼠标悬浮对应的参数来显示参数值)

1615777021(1).jpg 

调试出了请求参数以后,需要继续在当前源码窗口中进行搜索或按下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中:

1615777236(1).jpg 

然后按下CTRL+ALT+L快捷键对代码进行格式化:

1615777256(1).jpg 

格式化完毕以后就可以着手对代码进行调试。如需执行js代码来验证输出,也可以在视窗中右键点击run菜单来执行窗口中的js代码。下图所示为eval加密代码执行后的输出结果:

1615777272(1).jpg 

8.2.6 模拟JS代码的执行

做完逆向工作以后,python爬虫工程师最后需要做的是将与加密相关的核心代码用python模拟出来,然而实际情况是经过混淆、打包后的代码错综复杂,难以模拟。此时我们可以整段代码封装为单独的函数调用,再将js代码保存到js脚本文件中,最后通过Python中的第三方模块来执行文件中的js代码,获得js程序的输出。

Python社区比较流行的js执行工具有PyV8, PyExecJS等。本节以PyExecJS为例,来讲解在Python中模拟js代码执行的用法。

PyExecJS的安装很简单,直接在命令行中执行pip install PyExecJS。下图所示为未混淆的js代码:

1615777314(1).jpg 

该js代码定义了一个cipher函数,使用crypto-js对请求参数进行加密。

crypto-js的github地址:https://github.com/brix/crypto-js

在webstorm中执行窗口中的js脚本程序,来测试crypto-js的AES加密功能:

1615777346(1).jpg 

现在将该js代码使用eval进行混淆,下图所示为经过eval处理后的混淆代码:

1615777367(1).jpg 

继续点击窗口中的运行菜单:

1615777384(1).jpg 

输出结果与混淆前的输出是一样的,这说明,即便代码经过混淆,经过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,免费领取课程大纲

扫码免费领取学习资料:



欢迎 发表评论: