资源下载地址:https://download.csdn.net/download/sheziqiong/85795438
资源下载地址:https://download.csdn.net/download/sheziqiong/85795438
js 逆向之加密参数破解
背景说明
部分网站的 url 构成中含有加密参数,这些参数的加密方法写在了 JavaScript 中,而 js 通常经过了压缩,混淆和加密;本项目通过 AJAX 断点,hook 方法先寻找到加密入口,其次通过 playwright 将加密方法挂载到 window 对象中,达到利用浏览器环境执行 JavaScript 方法的目的,从而在不需要知道具体加密逻辑的情况下,模拟参数加密。
目标明确
- 获取 url:https://spa6.scrape.center/,网址中的 100 条电影数据的标题,评分和简介字段,并保存至 MySQL 中。
2.结果
方法分析
列表页 url 分析
这是一个 AJAX 请求,token 参数是加密参数,limit 参数表示每页电影展示数量,offset 根据观察,为(当前页数-1)*10;
为了观察 token 的构造,我们打一个 AJAX 断点,在发出 AJAX 请求的一瞬间停下,用调用栈寻找 token 的生成入口
按个查看调用栈,找到 token 生成入口,
在 169 行打上断点,刷新网页,并将 _0x2fa7bd['a']
和 this['$store']['state']['url']['index']
添加到 watch 监控中,
容易发现_0x2fa7bd[‘a’]是一个方法,传入的参数是 /api/movie
此时有 2 条路径可以走,1 是点击 chunk-4dec7ef0.e4c2b130.js:1
,具体的查看这个方法的构造逻辑,并用 python 模拟该方法,第二条路是直接将 _0x2fa7bd['a']
挂载到浏览器 window 对象中,具体选用哪种方法可以由该方法的复杂程度决定,这里我们先点进去查看该方法,
在 return 处打上断点,刷新,察看变量的变化
- 将当前的时间戳和
/api/movie
构造成一个列表 - 用
,
拼接这个列表形成字符串后,用SHA1
加密形成 40 个 16 进制数 - 再和时间戳构成成列表,并用,拼接成字符串
- 将字符串用 base64 方法加密成 64 个可打印字符
如果用 python 实现的话要导入 hashlib 调用 sha1 方法,导入 base64 调用 encode 方法,还是比较繁琐的,这里我们走挂载 window 对象方式
-
下载改加密方法所在的 js 文件
-
vscode 打开该文件,在该方法调用后的下一行加上如下一句
window.encrypt = Object(_0x2fa7bd['a']);
意思是将该方法变成 window 对象的一个属性 -
调用 playwright 库改写 js 加载路径
from playwright.sync_api import sync_playwright browser = sync_playwright().start().chromium.launch() page = browser.new_page() page.route('https://spa6.scrape.center/js/chunk-19c920f8.c3a1129d.js', lambda route: route.fulfill(path='./项目6/chunk.js')) page.route('https://spa6.scrape.center/js/chunk-4dec7ef0.e4c2b130.js', lambda route: route.fulfill(path='./项目6/chunk-id.js')) self.page.goto(self.BASEURL)
-
于是 playwright 的浏览器实例便具备了调用该加密方法的能力,紧接着我们写一个方法,让浏览器执行这个一个 JavaScript 方法,它的作用是让传入进来的参数,实现这个加密方法并返回输出结果
def get_token(self, params):
result = self.page.evaluate(
'()=>{return window.encrypt("%s")}' % params)
return result
于是列表页的 url 构造便完成了,接下来用 requests/aiohttp/scrapy 等方法请求该 url 即可
详情页 url 分析
还是一个 AJAX 请求,有 2 个加密参数,2 个都是 64 位的字符串,可能都用了 base64 加密方法,token 的构造方法可能和列表页的一样,前面那个,暂且叫 id 不清楚
-
先调查 token 的加密入口,看方法是否与列表页一致,如果一致,看加密参数是什么
可以看出 token 的加密方法是一样的,但是加密参数不一样,/api/movie/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
,在/api/movie/的基础上还拼接了 id,那么接下来方向就在 id 的加密构造方法上了,但 id 的构造方法通过查看调用栈的方式并没有找到,所以换一种方法,即然用了 base64 加密方法,那么很可能是用 btoa 方法实现的,我们可以借助tampermonkey
插件写一个简单的 JavaScript 脚本,进行 hook 捕获// ==UserScript== // @name hookbase64 # 脚本名称 // @namespace http://tampermonkey.net/ // @version 0.1 // @description try to take over the world! // @author You // @match https://spa6.scrape.center/ # 目标网址 // @icon https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org // @grant none // ==/UserScript== (function() { 'use strict'; function hook(object,attr){ var func=object[attr] # 先定义一个方法 object[attr]=function(){ # 随后改写这个方法 console.log('hooked',object,attr) # 控制台输出调用的对象和属性 var ret=func.apply(object,arguments) # 调用一开始的方法 console.log('ret',ret) # 控制台输出返回的结果 debugger # 重点:在此处进入调试 return ret # 返回结果 }} hook(window,'btoa') # 实例化这个方法,对象是window,方法是'btao' })();
取消网页所有断点,刷新
奇怪的是并没有触发 js 脚本的运行
可能原因是什么?可能是这个 id 并不是在这个网页包含的 AJAX 请求中生成的,可能是在列表页网址的 AJAX 请求中就生成了,验证一下果然debug住了,但这个参数重有时间戳,说明不是这次调用(这里是token的形成过程) 点击```继续执行脚本```
找到了,参数是一串无意义的字符,通过调用栈,一步步找到它的上层调用方法
最终我们找到了构造入口,进入查看其加密方法,为 2 个字符串拼凑后,用 base64 加密成 64 位的字符串,一个字符串是固定的,另外一个是传入的参数,而这个参数必然是区别每一步电影之间的唯一标识码,接下来去查看列表页的 JSON 文件
可以推断出 ID 字段便是这个传入进去的参数
那么接下来就沿用列表页的方式,将这个方法用 playwright 挂载到 window 对象中,构建 id 生成方法即可def get_id(self, params): result = self.page.evaluate( '()=>{return window.encrypt_id("%s")}' % params) return result
url 都构造好后,下面就是水到渠成了。
脚本结构
资源下载地址:https://download.csdn.net/download/sheziqiong/85795438
资源下载地址:https://download.csdn.net/download/sheziqiong/85795438