JavaScript逆向爬虫——使用Python模拟执行JavaScript

使用Python模拟执行JavaScript

通过一些调试,我们发现加密参数token是由encrypt方法产生的。如果里面的逻辑相对简单的话,那么我们可以用Python完全重写一遍。但是现实情况往往不是这样的,一般来说,一些加密相关的方法通常会引用一些相关标准库,比如说JavaScript就有一个广泛使用的库,叫作crypto-js,这个库实现了很多主流的加密算法,包括对称加密、非对称加密、字符编码等。比如对于AES加密,通常我们需要输入待加密文本和加密密钥,实现如下:

const ciphertext = CryptoJS.AES.encrypt(message, key).toString();

对于这样的情况,我们其实没法很轻易地完全重写一遍,因为Python中并不一定有和JavaScript完全一样的类库。既然JavaScript已经实现好了,那么我用Python直接模拟执行这些JavaScript得到结果不就好了吗?

  1. 案例引入

案例网站链接https://spa7.scrape.center/,如图所示:

1

这是一个NBA球星网站,用卡片的形式展示了一些球星的基本信息。另外,每张卡片上其实都有一个加密字符串,这个加密字符串其实和球星的信息是有关联的,并且每个球星的加密字符串也是不同的。所以,要做的就是找出这个加密字符串的加密算法并用程序把加密字符串的生成过程模拟出来。

  1. 准备工作

我们需要使用Python模拟执行JavaScript,这里使用的库叫PyExecJS。我们使用pip3命令安装它,如下:

pip3 install pyexecjs

PyExecJS是用于执行JavaScript的,但执行JavaScript的功能需要依赖JavaScript运行环境,所以除了安装好这个库外,还需要安装一个JavaScript运行环境,首选Node.js。更加详细的安装和配置过程可以参考https://setup.scrape.center/pyexecjs。

PyExecJS库在运行时会检测本地JavaScript运行环境来实现JavaScript执行,运行代码检查一下运行环境:

import execjs
print(execjs.get().name)

运行结果如下:

Node.js (V8)

证明环境运行正常。

  1. 分析

接下来,我们就对这个网站稍作分析。打开Sources面板,我们可以非常轻易地找到加密字符串的生成逻辑,如图所示:

2

首先,声明一个球员相关的列表,如:

const players = [{name: '凯文-杜兰特',image: 'durant.png',birthday: '1988-09-29',height: '208cm',weight: '108.9KG'},....]

然后对于每一个球员,我们调用加密算法对其信息进行加密。我们可以添加断点看看,如图所示:

3

可以看到, getToken方法的输入就是单个球员的信息,就是上述列表的一个元素对象,然后this.key就是一个固定的字符串。整个加密逻辑就是提取球员的名字、生日、身高、体重、接着先进行Base64编码,然后进行DES加密,最后返回结果。

加密算法的实现就是依赖了crypto-js库,使用CryptoJS对象来实现的。这个网站就是直接引用了crypto-js库,如图所示:

4

执行crypto-js库对应的这个JavaScript文件之后,CryptoJS就被诸如浏览器全局环境下,因此我们就可以在别的方法里直接使用CryptoJS对象里的方法了。

  1. 模拟调用

首先,要模拟的其实就是这个getToken方法,输入球员相关的信息,得到最终的加密字符串。这里直接把key替换下,把getToken方法稍微改写一下,具体如下:

 function getToken(player) {let key = CryptoJS.enc.Utf8.parse("fipFfVsZsTda94hJNKJfLoaqyqMZFFimwLt")const {name, birthday, height, weight} = player;let base64Name = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(name));let encrypted = CryptoJS.DES.encrypt(`${base64Name}${birthday}${height}${weight}`, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7,});return encrypted.toString();}

因为这个方法的模拟执行需要CryptoJS这个对象,如果我们直接调用这个方法,肯定会报CryptoJS未定义的错误。所以只需要再模拟执行一下刚才看到的crypto-js.min.js就可以了。

  • 模拟运行crypto-js.min.js里面的JavaScript,用于声明CryptoJS对象。
  • 模拟运行getToken方法的定义,用于声明getToken方法。

把crypto-js.min.js里面的代码和上面getToken方法的代码复制一下,都粘贴到一个JavaScript文件里面,比如叫crypto.js。

接下来,就用PyExecJS模拟执行一下,代码如下:

import execjs
import jsonitem = {'name': '凯文-杜兰特','image': 'durant.png','birthday': '1988-09-29','height': '208cm','weight': '108.9KG'
}file = 'moni.js'
node = execjs.get()
ctx = node.compile(open(file).read())js = f"getToken({json.dumps(item,ensure_ascii=False)})"
print(js)
result = ctx.eval(js)
print(result)

这里单独定义了一位球员的信息,将其赋为item变量。然后使用execjs的get方法获取JavaScript执行环境,赋值为node。

接着,调用node的compile方法,这里给它传入刚才定义的crypto.js文件的文本内容。compile方法会返回一个JavaScript的上下文对象,我们将其赋给ctx。执行到这里,其实就可以理解为,ctx对象里面执行了过了crypto-js.min.js,CryptoJS就声明好了,然后紧接着getToken方法的声明代码也被执行,所以getToken方法也定义好了,相当于完成了一些初始化工作。

只需要定义我们想要执行的JavaScript代码,定义一个js变量,其实就是模拟调用了getToken方法并传入了球员信息。打印js变量的值,内容如下:

getToken({"name": "凯文-杜兰特", "image": "durant.png", "birthday": "1988-09-29", "height": "208cm", "weight": "108.9KG"})

其实这就是一个标准的JavaScript方法调用的写法而已。接着调用ctx对象的eval方法并传入js变量,其实就是模拟执行这句JavaScript代码,照理来说最终返回的就是加密字符串了。

然后,运行之后,我们可能看到这个报错:

execjs._exceptions.ProgramError: ReferenceError: CryptoJS is not defined

很奇怪,CryptoJS未定义?明明执行过crypto-js.min.js里面的内容了呀?

问题其实处在crypto-js.min.js,可以看到其中声明了一个JavaScript的自执行方法,如图所示。

5

什么是自执行方法呢?就是声明了一个方法,然后紧接着调用执行。可以看下这个例子:

!(function(a, b){console.log('result', a, b)})(1, 2)

这里先声明了一个function,它接收a和b两个参数,然后把内容输出出来,接着我们把这个function用小括号括起来。这其实就是一个方法,可以被直接调用,怎么调用呢?后面再跟上对应的参数就好了,比如传入1和2,执行结果如下:

result 1 2

可以看到,这个自执行方法就被执行了。

同理,crypto-js.min.js也符合这个格式,它接收t和e两个参数,t就是this,其实就是浏览器中的window对象,e就是一个function(用于定义CryptoJS的核心内容)。

我们再来观察下crypto-js.min.js开头的定义:

 "object" == typeof exports ? module.exports = exports = e() : "function" == typeof define && define([], e) : t.CryptoJS = e());

在Node.js中,其实exports用来将一些对象的定义导出,这里“object” == typeof exports的结果其实就是true,所以就执行了module.exports = exports = e()这段代码,这相当于把e()作为整体导出,而这个e()其实就对应后面的整个function。function里面定义了加密相关的各个实现,其实就指代整个加密算法库。

但是在浏览器中,结果就不一样了,浏览器中没有exports和define这两个对象。所以上述代码在浏览器中最后执行的就是t.CryptoJS = e()这段代码,其实这里就是把CryptoJS对象挂载到全局对象里面,所以后面我们再调用CryptoJS就自然出现未定义的错误了。

其实很简单解决这个问题,直接声明一个CryptoJS变量,然后手动声明一下它的初始化就好了,代码修改如下:

var CryptoJS; //声明变量
!function(t, e) {CryptoJS = e(); //初始化"object" == typeof exports ? module.exports = exports = e() : "function" == typeof define && define.amd ? define([], e) : t.CryptoJS = e()
}(this, function() {
//..
});

这里首先声明一个CryptoJS变量,然后给CryptoJS变量赋值e(),这样就完成了CryptoJS的初始化。这样再重新运行这个Python脚本,执行结果如下:

bruce_liu@localhost 模拟执行JavaScript % python3 pyexecjs.py
getToken({"name": "凯文-杜兰特", "image": "durant.png", "birthday": "1988-09-29", "height": "208cm", "weight": "108.9KG"})
DG1uMMq1M7OeHhds71HlSMHOoI2tFpWCB4ApP00cVFqptmlFKjFu9RluHo2w3mUw

这样我们就成功得到加密字符串了,和示例网站上显示的一模一样,成功模拟JavaScript的调用完成了某个加密算法的运行过程。

更多的体验访问小蜜蜂AI网站,网址:https://zglg.work

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/799255.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

摄像头校准漫反射板提高识别物体

摄像头校准漫反射板是一种用于摄像头校准的重要工具。在摄像头成像过程中,由于各种因素的影响,如光线、角度、镜头畸变等,会导致摄像头成像出现偏差。为了消除这些偏差,提高摄像头的成像质量,需要使用摄像头校准漫反射…

从头开发一个RISC-V的操作系统(四)嵌入式开发介绍

文章目录 前提嵌入式开发交叉编译GDB调试,QEMU,MAKEFILE练习 目标:通过这一个系列课程的学习,开发出一个简易的在RISC-V指令集架构上运行的操作系统。 前提 这个系列的大部分文章和知识来自于:[完结] 循序渐进&#x…

第十四讲:C语言字符函数和字符串函数

目录 1. 字符分类函数 2、字符转换函数 3. strlen的使⽤和模拟实现 4. strcpy 的使⽤和模拟实现 5. strcat 的使⽤和模拟实现 6. strcmp 的使⽤和模拟实现 7. strncpy 函数的使⽤ 8. strncat 函数的使⽤ 9. strncmp函数的使⽤ 10. strstr 的使⽤和模拟实现 11. strt…

mysql的索引类型与数据存储

mysql索引与类型 什么是索引? 索引(Index)是帮助MySQL高效获取数据的数据结构。我们可以简单理解为:快速查找排好序的一种数据结构。Mysql索引主要有两种结构:BTree索引和Hash索引。我们平常所说的索引,如…

校园圈子小程序,大学校园圈子,三段交付,源码交付,支持二开

介绍 在当今的数字化时代,校园社交媒体和在线论坛成为了学生交流思想、讨论问题以及分享信息的常用平台。特别是微信小程序,因其便捷性、用户基数庞大等特点,已逐渐成为构建校园社区不可或缺的一部分。以下是基于现有资料的校园小程序帖子发…

(已解决)引入本地bootstrap无效,bootstrap和jquery的引入

问题&#xff1a; 首先我是跟着张天宇老师下载的bootstrap文件&#xff0c;新建了一个css文件夹&#xff0c;但是这样子<link rel"stylesheet" type"text/css" src"./css/bootstrap.css">在index.html引入没有用。 解决办法: 1.把建立的…

【opencv】示例-dft.cpp 该程序演示了离散傅立叶变换 (dft) 的使用,获取图像的 dft 并显示其功率谱...

#include "opencv2/core.hpp" // 包含OpenCV核心功能头文件 #include "opencv2/core/utility.hpp" // 包含OpenCV实用程序头文件 #include "opencv2/imgproc.hpp" // 包含OpenCV图像处理头文件 #include "opencv2/imgcodecs.hpp" // 包…

CSS 学习笔记 总结

CSS 布局方式 • 表格布局 • 元素定位 • 浮动布局&#xff08;注意浮动的负效应&#xff09; • flex布局 • grid布局&#xff08;感兴趣的可以看下菜鸟教程&#xff09; 居中设置 元素水平居中 • 设置宽度后&#xff0c;margin设置为auto • 父容器设置text-alig…

积木报表Excel数据量大导出慢导不出问题、大量数据导不出问题优化方案和分析解决思路(优化前一万多导出失败,优化后支持百万级跨库表导出)

文章目录 积木报表Excel数据量大导出慢导不出问题、大量数据导不出问题优化方案和分析解决思路&#xff08;优化前一万多导出失败&#xff0c;优化后支持百万级跨库表导出&#xff09;优化结果需求背景和解决方案的思考解决方案流程描述&#xff1a;关键代码引入easy excel新建…

Linux云计算之Linux基础3——Linux系统基础2

1、终端 终端(terminal)&#xff1a;人和系统交互的必要设备&#xff0c;人机交互最后一个界面&#xff08;包含独立的输入输出设备&#xff09; 物理终端(console)&#xff1a;直接接入本机器的键盘设备和显示器虚拟终端(tty)&#xff1a;通过软件方式虚拟实现的终端。它可以…

Docker 引擎离线安装包采集脚本

文章目录 一、场景说明二、脚本职责三、参数说明四、操作示例五、注意事项 一、场景说明 本自动化脚本旨在为提高研发、测试、运维快速部署应用环境而编写。 脚本遵循拿来即用的原则快速完成 CentOS 系统各应用环境部署工作。 统一研发、测试、生产环境的部署模式、部署结构、…

基于SSM的宠物管理系统

点击以下链接获取源码: https://download.csdn.net/download/qq_64505944/89076676?spm=1001.2014.3001.5503 技术:SSM(Spring+SpringMVC+MyBatis)+LayUI+Echarts技术栈,分页采用pagehelper插件,EasyExcel进行Excel文件的导入导出。 宠物管理系统 1 CHINER-宠物管理系…

【MYSQL之进阶篇】视图、存储过程、存储函数以及触发器

&#x1f525;作者主页&#xff1a;小林同学的学习笔录 &#x1f525;mysql专栏&#xff1a;小林同学的专栏 1.视图 1.1 定义 视图是MySQL数据库中的虚拟表&#xff0c;它基于一个或多个实际表的查询结果。视图提供了一种简单的 方法来封装和重用复杂的查询&#xff0c;同时…

MySQL的基本查询

&#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;MySQL &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容介绍了mysql的基本查询部分的知识&#xff0c;包括Crea…

YOLOv9改进 | 一文带你了解全新的SOTA模型YOLOv9(论文阅读笔记,效果完爆YOLOv8)

官方论文地址&#xff1a; 官方论文地址点击即可跳转 官方代码地址&#xff1a; 官方代码地址点击即可跳转 图1. 在MS COCO数据集上实时对象检测器的比较。基于GELAN和PGI的对象检测方法在对象检测性能方面超越了所有以前的从头开始训练的方法。在准确性方面&#xff0c;新方法…

Go语言实现Redis分布式锁2

项目地址: https://github.com/liwook/Redislock 1.支持阻塞式等待获取锁 之前的是只尝试获取一次锁&#xff0c;要是获取失败就不再尝试了。现在修改为支持阻塞式等待获取锁。 添加LockOptions结构体 添加option.go文件。 在LockOptions中 isBlock表示是否是阻塞模式blo…

配置vscode用于STM32编译,Debug,github上传拉取

配置环境参考&#xff1a; Docs 用cubemx配置工程文件&#xff0c;用VScode打开工程文件。 编译的时候会有如下报错&#xff1a; vscode出现process_begin :CreateProcess failed 系统找不到指定文件 解决方案&#xff1a;在你的makefile中加上SHELLcmd.exe就可以了 参考…

java发送请求-cookie有关代码

在初始化后添加cookie的代码 用这个httpclients类调custom方法&#xff0c;进行代码定制化 找和cookie有关的方法&#xff0c;设置默认的cookie存储信息 入参是接口 将入参粘贴后找方法&#xff0c;用new实现这个接口 这个方法是无参空构造&#xff0c;可以使用 设置了cookie …

【C++】RapidJSON 设置支持 std::string,防止编译报错

问题 rapidjson 创建 json 数据&#xff0c;使用 std::string 字符串进行赋值&#xff0c;编译时&#xff0c;抱一堆错误 .... rapidjson/include/rapidjson/document.h:690:5: note: candidate expects 0 arguments, 1 provided [build] make[2]: *** [main/CMakeFiles/ma…

Betaflight 4.5RC3 AT32F435遇到的一些“怪”现象

Betaflight 4.5RC3 AT32F435遇到的一些“怪”现象 1. 源由2. “怪”现象2.1 电机#4没有RPM转速2.2 遥控器通道10接收机测试失败2.3 OSD 异常2.4 磁力计数据无法获取 3. 参考资料 1. 源由 升级下固件&#xff0c;追下“时髦”&#xff0c;赶下“潮流”&#xff0c;本着“活着就…