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

news2024/11/29 6:39:29

使用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 json

item = {
    '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.coloradmin.cn/o/1576640.html

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

相关文章

2024年妈妈杯数学建模MathorCup数学建模思路B题思路解析+参考成品

1 赛题思路 (赛题出来以后第一时间在群内分享,点击下方群名片即可加群) 2 比赛日期和时间 报名截止时间:2024年4月11日(周四)12:00 比赛开始时间:2024年4月12日(周五)8:00 比赛结束时间&…

数据库相关知识总结

一、数据库三级模式 三个抽象层次: 1. 视图层:最高层次的抽象,描述整个数据库的某个部分的数据 2. 逻辑层:描述数据库中存储的数据以及这些数据存在的关联 3. 物理层:最低层次的抽象,描述数据在存储器中时如…

docker 部署 Epusdt - 独角数卡 dujiaoka 的 usdt 支付插件

部署 部署说明 部署之前必须注意的几点事项,该教程不一定适合所有用户: 本教程主要是使用 docker 部署,宝塔用户或宿主机直接安装的用户请直接参考官网教程.本教程是独立部署 epusdt,使用独立的mysql和redis,与dujiaoka项目分开. 在研究的过程中发现 epusdt 也需要用到 mys…

【Java】maven是什么?

先看一下基本概念: ①Maven 翻译为"专家","内行"是跨平台的项目管理工具。 主要服务于基于Java平台的项目构建,依赖管理和项目信息管理。 ②项目构建 项目构建过程包括【清理项目】→【编译项目】→【测试项目】→【生成测试报…

3D目标检测跟踪 | 基于kitti+waymo数据集的自动驾驶场景的3D目标检测+跟踪渲染可视化

项目应用场景 面向自动驾驶场景的 3D 目标检测目标跟踪,基于kittiwaymo数据集的自动驾驶场景的3D目标检测跟踪渲染可视化查看。 项目效果 项目细节 > 具体参见项目 README.md (1) Kitti detection 数据集结构 # For Kitti Detection Dataset └── k…

解决前端性能瓶颈:高效处理大量数据渲染与复杂交互的策略与优化方法

✨✨祝屏幕前的小伙伴们每天都有好运相伴左右,一定要天天开心!✨✨ 🎈🎈作者主页: 喔的嘛呀🎈🎈 目录 引言 一、分页加载数据 二、虚拟滚动 三、懒加载 四、数据缓存 五、减少重绘和回流 …

如何在 Windows上安装 Python

系列文章目录 作者:i阿极 作者简介:数据分析领域优质创作者、多项比赛获奖者:博主个人首页 😊😊😊如果觉得文章不错或能帮助到你学习,可以点赞👍收藏📁评论📒…

循环双链表算法库构建

学习贺老师数据结构数据结构之自建算法库——循环双链表_数据结构编写一个程序linklist.cpp-CSDN博客 模仿单链表逻辑,实现双链表, 大差不差 v1.0: 实现基本功能 V1.0 1.主要功能: //(1)头插法建立循环双链表 void Create_Double_CyclicList_Head(DoubleLinkList_Cyclic *&am…

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

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

云原生之旅第一课(2站搜索K8s成神之路)

自己动手搭建Kubernetes集群,学习如何自定义CRD,以及使用Kubebuilder快速搭建Operator项目,云原生之旅第一课。从一开始准备录制课程,到如今已经有了500位忠实粉丝,我感到无比欣慰。这门课程完全开源,每一集…

C++【组合模式】

简单介绍 组合模式是一种结构型设计模式, 只有在可以将对象拆分为【树状结构】的情况下使用。并且像使用独立对象一样使用它们。 常用于表示与图形打交道的用户界面组件或代码的层次结构。 基础理解 Q:为什么要用组合模式 ? A:在…

JavaScript - 你知道数组去重都有哪些实现方案吗

难度级别:初级及以上 提问概率:70% 数组去重是一道非常经典而又高频的面试题,这里我们提出6种解决方案: 目录 1 第一种 2 第二种 3 第三种 4 第四种

Tokenize Anything via Prompting

SAM的延续,把SAM输出的token序列用来进行分类,分割和一个自然语言的decoder处理,但其实现在多模态的图像的tokenizer也几乎都是用VIT来实现的。一开始认为这篇文章可能是关于tokenize的,tokenize还是很重要的,后来看完…

MUX VLAN

目录 原理概述 实验目的 实验内容 实验拓扑 1.基本配置 2.使用Hybrid端口实现网络需求 3.使用Mux VLAN实现网络需求 原理概述 在实际的企业网络环境中,往往需要所有的终端用户都能够访问某些特定的服务器,而用户之间的访问控制规则则比较复杂。在…

Mysql启动报错:本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止

Mysql启动报错:本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止 文章目录 Mysql启动报错:本地计算机上的mysql服务启动后停止,某些服务在未由其他服务或程序使用时将自动停止1. 备份mysql的data文件夹2. 重新构建 Wind…

helm与k8s

文章目录 一、helm二、K8S/K3S1.K8S基本组件1.1 资源对象1.2 核心组件1.3典型的创建 Pod 的流程1.4 Kubernetes 多组件之间的通信原理 2. YAML 文件2.1 Maps2.2 Lists2.3 使用 YAML 创建 Pod2.4 创建 Deployment 4.静态pod4.1 配置文件4.2 通过 HTTP 创建静态 Pods4.3 静态pods…

【Linux系列】如何确定当前运行的是 RHEL 9 还是 RHEL 8?

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

zheng项目:从零到一打造全方位J2EE企业级开发解决方案

zheng项目:从零到一打造全方位J2EE企业级开发解决方案 摘要: 在当今快速发展的企业级应用开发领域,一套高效、稳定且可扩展的解决方案对于企业的成功至关重要。zheng项目旨在提供一套全面的J2EE企业级开发解决方案,从前端模板到自…

学习人工智能:为何PyTorch深度学习框架不可或缺

在人工智能(AI)的浩瀚领域中,深度学习作为其核心分支,正以其强大的数据处理能力、模式识别能力和预测能力引领着科技的飞速发展。而在深度学习的众多工具与框架中,PyTorch无疑是一颗璀璨的明星。本文将从PyTorch的特点…

单片机为什么还在用C语言编程?

单片机产品的成本是非常敏感的。因此对于单片机开发来说,最重要的是在极其有限的ROM和RAM中实现最多产品的功能。或者反过来说,实现相同的产品功能,所需要的ROM和RAM越小越好,在开始前我有一些资料,是我根据网友给的问…