js逆向解密之网络爬虫


Posted in Javascript onMay 30, 2019

1 引言

数月前写过某网站(请原谅我的掩耳盗铃)的爬虫,这两天需要重新采集一次,用的是scrapy-redis框架,本以为二次爬取可以轻松完成的,可没想到爬虫启动没几秒,出现了大堆的重试提示,心里顿时就咯噔一下,悠闲时光估计要结束了。
仔细分析后,发现是获取店铺列表的请求出现问题,通过浏览器抓包,发现请求头参数中相比之前多了一个X-Shard和x-uab参数,如下图所示:

js逆向解密之网络爬虫

X-Shard倒是没什么问题,一看就是兴趣点的经纬度,但x-uab看过之后就让人心里苦了,js加密啊,只能去逆向解密了。

2 js逆向求解

最直接的思路是根据“x-uab”关键字在所有关键中查找(chrome浏览器-source中按ctrl + shift + F快捷键),结果如下所示:

js逆向解密之网络爬虫

接下来,打个断点调试一下:在数字那里点一下,数字位置出现蓝点,表示添加断点成功,然后刷新获取店铺列表的页面,程序会在断点处停下。如下所示:

js逆向解密之网络爬虫

在控制台调试o.getUA()函数,看一下输出:

js逆向解密之网络爬虫

果然是,证明猜测没错,就是这个o.getUA()函数负责生成请求头中的x-uab参数。
继续向下查看这个getUA()函数的引用(把光标放在要查看的函数上,就可以查看这个函数的引用),就是下图这个函数:

js逆向解密之网络爬虫

图中的s就是我们要的x-uab参数,下图在控制台输出可以证明:

js逆向解密之网络爬虫

所以,u-xab是这里的e生成的,而函数e传入的参数中,第一个是常量2,第二个参数a是undefined,呵,看起来没有传其它参数。继续向下找这个e(2,a)函数:

js逆向解密之网络爬虫

就是这个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() {
 var a;
 var param = e(2,a);
 return param
};

然后,开始撸Python代码吧:

import execjs
 
node = execjs.get()
file = 'eleme.js'
ctx = node.compile(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
os.environ["EXECJS_RUNTIME"] = "PhantomJS"
node = execjs.get()
file = 'eleme.js'
ctx = node.compile(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='chromedriver.exe')
with open('eleme.js', 'r') as f:
 js = f.read()
print(browser.execute_script(js))

这个方法也是可以获得加密之后的字符串。

最后,有必要说一下的是,如果需要获取大量的x-uab,采用方案二效率会高一下,因为采用方案二的话,可以自打开一个浏览器(都调用一个webdriver对象),然后快速执行js,返回加密字符串。

4 总结

一次js逆向解密,算是完成了吧。但是也留下了一些问题:

(1)使用chrome断点调试时,js脚本都是压缩混淆之后的,通过chrome的pretty print功能(也就是说那对花括号)可以格式美化,但是,有的时候却会失败,就像下图,格式化后,还是一团糟:

js逆向解密之网络爬虫

这个问题耽搁了我很长时间,没法调试啊!

(2)在下js基础不行,很困惑为什么运行时,先通过o.getUA()调用e函数内的嵌套函数,然后e函数内部嵌套函数中调用e方法本身,这是什么操作?函数调用不都应该先外层函数,然后再调用嵌套函数吗?

(3)如果不适用浏览器执行js的方法,就只能替换window对象,这该如何操作?

(4)这个e函数有近4万行,一个加密函数这么多代码,我可不信,里面肯定很多事混淆视听用的,但我尝试调试追踪过,只能说混淆之后让我无从追踪,头晕。怎么才能简化这段脚本呢?

如果哪位前辈可以解惑,请一定告知,不胜感激!拜谢!

Javascript 相关文章推荐
jquery下动态显示jqGrid以及jqGrid的属性设置容易出现问题的解决方法
Oct 22 Javascript
让低版本浏览器支持input的placeholder属性(js方法)
Apr 03 Javascript
jQuery选择器中含有空格的使用示例及注意事项
Aug 25 Javascript
js弹出确认是否删除对话框
Mar 27 Javascript
jquery自定义表单验证插件
Oct 12 Javascript
javascript实现获取指定精度的上传文件的大小简单实例
Oct 25 Javascript
基于KO+BootStrap+MVC实现的分页控件代码分享
Nov 07 Javascript
bootstrap实现图片自动轮播
Dec 21 Javascript
基于jQuery ztree实现表格风格的树状结构
Aug 31 jQuery
Vue2.X和Vue3.0数据响应原理变化的区别
Nov 07 Javascript
解决vue+elementui项目打包后样式变化问题
Aug 03 Javascript
Vue.js中的组件系统
May 30 #Javascript
Vue+Django项目部署详解
May 30 #Javascript
了解重排与重绘
May 29 #Javascript
小程序如何构建骨架屏
May 29 #Javascript
新手简单了解vue
May 29 #Javascript
详细讲解如何创建, 发布自己的 Vue UI 组件库
May 29 #Javascript
基于JS实现一个随机生成验证码功能
May 29 #Javascript
You might like
PHP数组操作――获取数组最后一个值的方法
2015/04/14 PHP
yii2.0实现验证用户名与邮箱功能
2015/12/22 PHP
Yii中的relations数据关联查询及统计功能用法详解
2016/07/14 PHP
匹配任意字符的正则表达式写法
2010/04/29 Javascript
理解Javascript_13_执行模型详解
2010/10/20 Javascript
一个页面放2段图片滚动代码出现冲突的问题如何解决
2012/12/21 Javascript
jQuery之日期选择器的深入解析
2013/06/19 Javascript
jquery map方法使用示例
2014/04/23 Javascript
javascript模拟订火车票和退票示例
2014/04/24 Javascript
js函数参数设置默认值的一种变通实现方法
2014/05/26 Javascript
究竟什么是Node.js?Node.js有什么好处?
2015/05/29 Javascript
Javascript中的return作用及javascript return关键字用法详解
2015/11/05 Javascript
js 动态给元素添加、移除事件的实现方法
2016/07/19 Javascript
JS正则表达式学习之贪婪和非贪婪模式实例总结
2016/12/26 Javascript
vuejs实现递归树型菜单组件
2018/01/13 Javascript
如何制作一个Node命令行图像识别工具
2018/12/12 Javascript
vue项目中使用particles实现粒子背景效果及遇到的坑(按钮没有点击响应)
2020/02/11 Javascript
angula中使用iframe点击后不执行变更检测的问题
2020/05/10 Javascript
一行JavaScript代码如何实现瀑布流布局
2020/12/11 Javascript
Python3学习urllib的使用方法示例
2017/11/29 Python
python 用正则表达式筛选文本信息的实例
2018/06/05 Python
简单了解python的break、continue、pass
2019/07/08 Python
python用类实现文章敏感词的过滤方法示例
2019/10/27 Python
python 实现list或string按指定分段
2019/12/25 Python
Python中的整除和取模实例
2020/06/03 Python
python是怎么被发明的
2020/06/15 Python
python连接手机自动搜集蚂蚁森林能量的实现代码
2021/02/24 Python
Python实现我的世界小游戏源代码
2021/03/02 Python
联想中国官方商城:Lenovo China
2017/10/18 全球购物
中科软测试工程师面试题
2012/06/16 面试题
分别介绍一下Session Bean和Entity Bean
2015/03/13 面试题
副总经理工作职责
2013/11/28 职场文书
网络信息安全承诺书
2014/03/26 职场文书
学校感恩节活动策划方案
2014/10/06 职场文书
2015年乡镇纪检工作总结
2015/04/22 职场文书
matlab xlabel位置的设置方式
2021/05/21 Python