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 相关文章推荐
js实现ASP分页函数 HTML分页函数
Sep 22 Javascript
javascript 解析后的xml对象的读取方法细解
Jul 25 Javascript
js 有框架页面跳转(target)三种情况下的应用
Apr 09 Javascript
js unicode 编码解析关于数据转换为中文的两种方法
Apr 21 Javascript
js获取域名的方法
Jan 27 Javascript
js鼠标悬浮出现遮罩层的方法
Jan 28 Javascript
利用JS实现点击按钮后图片自动切换的简单方法
Oct 24 Javascript
Vue2.0实现将页面中表格数据导出excel的实例
Aug 09 Javascript
深入浅析Node环境和浏览器的区别
Aug 14 Javascript
JavaScript中创建原子的方法总结
Aug 26 Javascript
解决vue-cli项目打包出现空白页和路径错误的问题
Sep 04 Javascript
详解JavaScript中操作符和表达式
Sep 12 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仿博客园 个人博客(2) 数据库增添改删
2013/07/05 PHP
destoon整合ucenter后注册页面不跳转的解决方法
2014/06/21 PHP
JAVASCRIPT keycode总结
2009/02/04 Javascript
优化 JavaScript 代码的方法小结
2009/07/16 Javascript
『JavaScript』限制Input只能输入数字实现思路及代码
2013/04/22 Javascript
解析瀑布流布局:JS+绝对定位的实现
2013/05/08 Javascript
js使下拉列表框可编辑不止是选择
2013/12/12 Javascript
fixedBox固定div漂浮代码支持ie6以上大部分主流浏览器
2014/06/26 Javascript
JavaScript获取Url里的参数
2014/12/18 Javascript
利用jQuery实现漂亮的圆形进度条倒计时插件
2015/09/30 Javascript
Extjs4.0 ComboBox如何实现三级联动
2016/05/11 Javascript
基于javascript实现最简单的选项卡切换效果
2016/05/16 Javascript
Bootstrap基本组件学习笔记之导航(10)
2016/12/07 Javascript
纯js实现悬浮按钮组件
2016/12/17 Javascript
AngularJS中ng-class用法实例分析
2017/07/06 Javascript
JavaScript数组的5种迭代方法
2017/09/29 Javascript
input type=file 选择图片并且实现预览效果的实例
2017/10/26 Javascript
通过nodejs 服务器读取HTML文件渲染到页面的方法
2018/05/17 NodeJs
javascript中call,apply,callee,caller用法实例分析
2019/07/24 Javascript
解决vue自定义全局消息框组件问题
2019/11/22 Javascript
vue 出现data-v-xxx的原因及解决
2020/08/04 Javascript
python抓取京东价格分析京东商品价格走势
2014/01/09 Python
python应用程序在windows下不出现cmd窗口的办法
2014/05/29 Python
Python 获取新浪微博的最新公共微博实例分享
2014/07/03 Python
Python使用email模块对邮件进行编码和解码的实例教程
2016/07/01 Python
浅谈numpy数组中冒号和负号的含义
2018/04/18 Python
python 处理微信对账单数据的实例代码
2019/07/19 Python
HTML5 Canvas画线技巧——实现绘制一个像素宽的细线
2013/08/02 HTML / CSS
在购买印度民族服饰:Soch
2020/09/15 全球购物
创建索引时需要注意的事项
2013/05/13 面试题
教师档案管理制度
2014/01/23 职场文书
春节联欢晚会主持词范文
2014/03/24 职场文书
入党心得体会
2019/06/20 职场文书
教你用Python爬取英雄联盟皮肤原画
2021/06/13 Python
磁贴还没死, 微软Win11可修改注册表找回Win10开始菜单
2021/11/21 数码科技
Tomcat安装使用及部署Web项目的3种方法汇总
2022/08/14 Servers