⽹络爬⾍之记⼀次js逆向解密经历
1 引⾔
数⽉前写过某⽹站(请原谅我的掩⽿盗铃)的爬⾍,这两天需要重新采集⼀次,⽤的是scrapy-redis框架,本以为⼆次爬取可以轻松完成的,可没想到爬⾍启动没⼏秒,出现了⼤堆的重试提⽰,⼼⾥顿时就咯噔⼀下,悠闲时光估计要结束了。
仔细分析后,发现是获取店铺列表的请求出现问题,通过浏览器抓包,发现请求头参数中相⽐之前多了⼀个X-Shard和x-uab参数,如下图所⽰:
X-Shard倒是没什么问题,⼀看就是兴趣点的经纬度,但x-uab看过之后就让⼈⼼⾥苦了,js加密啊,只能去逆向解密了。
2 js逆向求解
最直接的思路是根据“x-uab”关键字在所有关键中查(chrome浏览器-source中按ctrl + shift + F快捷键),结果如下所⽰:
接下来,打个断点调试⼀下:在数字那⾥点⼀下,数字位置出现蓝点,表⽰添加断点成功,然后刷新获取店铺列表的页⾯,程序会在断点处停下。如下所⽰:
在控制台调试o.getUA()函数,看⼀下输出:
果然是,证明猜测没错,就是这个o.getUA()函数负责⽣成请求头中的x-uab参数。
继续向下查看这个getUA()函数的引⽤(把光标放在要查看的函数上,就可以查看这个函数的引⽤),就是下图这个
函数:
图中的s就是我们要的x-uab参数,下图在控制台输出可以证明:
所以,u-xab是这⾥的e⽣成的,⽽函数e传⼊的参数中,第⼀个是常量2,第⼆个参数a是undefined,呵,看起来没有传其它参数。继续向下这个e(2,a)函数:
就是这个function e(r, i, n, h, p) ⽅法,直接运⾏可以获取加密后的参数。把这个function e(r, i, n, h, p) ⽅法全部代码取出来,另存为⼀个js⽂件。
3 撸代码
3.1 ⽅案⼀
你以为上⾯出⽣成x-uab的js代码,就⼤功告成了吗?少年,you are too young too simple!
怎么把这段js脚本运⾏起来,才是关(nan)键(dian)。
这个function e(r, i, n, h, p) 函数有近4万⾏代码,重新⽤Python实现难(jiu)度(shi)有(bu)点(ke)⼤(neng)。所以,我选择直接⽤Python来执⾏这段js脚本。
怎么⽤python执⾏js脚本,度娘会给你⼀堆资料,⾃⼰查吧。我这⾥选择的是execjs。
因为在上⾯复制出来的脚本中,只单单定义了⼀个e(r, i, n, h, p)⽅法,并没有调⽤这个⽅法,所以,我要要在js⽂件的末尾添加⼀些代码来调⽤:
function getParam() {
js代码加密软件
var a;
var param = e(2,a);
return param
};
然后,开始撸Python代码吧:
import execjs
node = ()
file = 'eleme.js'
ctx = nodepile(open(file).read())
js_encode = 'getParam()'
params = ctx.eval(js_encode)
print(params)
尝试执⾏,⼼凉,代码异常:
execjs._exceptions.ProgramError: TypeError: 'window'未定义
window对象估计是浏览器打开是创建的,蕴含浏览器的信息,所以⽤Python来执⾏这段代码时,没有这个对西乡。本来想尝试伪造window对象,但查之后发现js脚本中上百个地⽅⽤到window,这还没完,代码经过混淆,在下⽔平不够,没法追根溯源(这地⽅困扰了我许久,哪位前辈如果知道⽅法,请告知)。
后来,从⼀个前辈那⾥(感谢前辈)获知⼀个⽅法绕过去。这个前辈的⽅法是将execjs的引擎换成PhantomJS这个⽆头浏览器(之前⽤的引擎是node.js),换句话说就是⽤PhantomJS来执⾏js脚本,PhantomJS是⼀个浏览器,⾃然就会创建window对象。
使⽤PhantomJS之前,需要下载它的,然后放下Python代码统⼀⽬录下。对之前的Python代码也进⾏修改:
import execjs
import os
node = ()
file = 'eleme.js'
ctx = nodepile(open(file).read())
js_encode = 'getParam()'
params = ctx.eval(js_encode)
print(params)
果然,按照这个⽅法,成功获取加密字符串。
3.2 ⽅案⼆
事实上,这个⽅案⼆才是我在出现未定义window对象异常后⾸先尝试的⽅法,不过因为往js代码中添加的js脚本有问题,以为⾏不通,所以请教前辈,得到了⽅案⼀。
⽅案⼆的思路和⽅案⼀类似,不过更加粗暴⼀些。不是因为没在浏览器执⾏,造成没有window对象吗?那我就模拟浏览器来执⾏。
在执⾏之前,同样要修改js脚本,在js⽂件末尾调⽤e⽅法,添加如下代码:
var a;
var param = e(2,a);
return param;
切记:不要放在任何函数⾥⾯,我之前就是因为将这段代码放在函数⾥头强制执⾏,导致的结果就是在浏览器⾥可以获取加密字符串,但是在Python中获取到的却是None。
模拟浏览器⽤的selenium和chrome的webDriver,代码如下:
from selenium import webdriver
browser = webdriver.Chrome(executable_path='')
with open('eleme.js', 'r') as f:
js = f.read()
ute_script(js))
这个⽅法也是可以获得加密之后的字符串。
最后,有必要说⼀下的是,如果需要获取⼤量的x-uab,采⽤⽅案⼆效率会⾼⼀下,因为采⽤⽅案⼆的话,可以⾃打开⼀个浏览器(都调⽤⼀个webdriver对象),然后快速执⾏js,返回加密字符串。
4 总结
⼀次js逆向解密,算是完成了吧。但是也留下了⼀些问题:
(1)使⽤chrome断点调试时,js脚本都是压缩混淆之后的,通过chrome的pretty print功能(也就是说那对花括号)可以格式美化,但是,有的时候却会失败,就像下图,格式化后,还是⼀团糟:
这个问题耽搁了我很长时间,没法调试啊!
(2)在下js基础不⾏,很困惑为什么运⾏时,先通过o.getUA()调⽤e函数内的嵌套函数,然后e函数内部嵌套函数中调⽤e⽅法本⾝,这是什么操作?函数调⽤不都应该先外层函数,然后再调⽤嵌套函数吗?
(3)如果不适⽤浏览器执⾏js的⽅法,就只能替换window对象,这该如何操作?
(4)这个e函数有近4万⾏,⼀个加密函数这么多代码,我可不信,⾥⾯肯定很多事混淆视听⽤的,但我尝试调试追踪过,只能说混淆之后让我⽆从追踪,头晕。怎么才能简化这段脚本呢?
如果哪位前辈可以解惑,请⼀定告知,不胜感激!拜谢!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论