猫眼电影字体破解(图片转码方法)
问题
随便拿一篇电影做样例。我们发现猫眼的页面数据在预览窗口中全是小方框。在当我们拿到源码以后,数据全是加密后的。所以我们需要想办法破解加密,拿到数据。


破解过程
1.源码获取问题与破解
分析
在我们刚刚请求url的时候是可以得到数据的,但是过了一段时间后就无法获得数据。虽然状态码为200,但是却没有返回页面源码

一般这种应该是和时间戳有关系,在查看请求负载的时候我们发送,浏览器向这个url不仅发送了时间戳还有一个signKey的密钥。时间戳可以很容易得到,主要问题是如何获得signKey。

全局搜索signKey,我们发现一段js代码,它的返回值就是我们请求负载的内容。所以需要想办法还原这段js代码。

分析后发现:
- d:获取当前时间的函数
- r:随机数取整
- c:内容如下method=GET&timeStamp=1725264890773&User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0&index=8&channelId=40011&sVersion=1
- 可以发现就是多个信息进行拼接(时间戳+User-Agent+index+channelId+sVersion)。
- f:固定为&key=A013F70DB97834C0A5492378BD76C53A
分析图片如下:

![]()

同时我们还发现signKey是通过MD5加密(c+f)后得到的。因为1经过MD5加密后得到的内容就是c4ca4238a0b923820dcc509a6f75849b,所以我们可以猜测(0,a[i(_0x140e("0xe4"))])('c+f')就是一个MD5的加密。

js编写与调用
有了以上分析后,我们就可以拿页面原始的js代码进行适当的改动。修改后的js代码如下,我们直接返回网页负载需要的params。

添加首页cookie
在完成上面步骤后,我们调用js,虽然得到了params,但是还是无法获得到页面的源代码,这可能和cookie有关系,所以我们创建一个session,通过访问首页来保存首页的cookie,然后再来访问这个url看看结果。

我们发现浏览器请求了两次https://www.maoyan.com/,且第一次存在302跳转,跳转到https://www.maoyan.com/,所以是请求了两次。在python代码中,我们只需要请求有302跳转的链接即可,因为程序会自动进行第二次跳转。

添加cookie后,使用python程序调用js代码返回params,使用js生成的params去访问url地址运行结果如下:

2.字体破解
字体图片下载
在拿到页面源码以后,我们需要对数字进行获取。直接在返回的源码中搜索,获取.woff文件。得到url://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/e3dfe524.woff,因为每一次请求得到的源码中,woff文件的链接都不同,所以我们需要使用数据提取手段,提取每一次请求得到的woff文件链接并下载保存下来。


下载并保存woff文件,使用python代码识别woff文件,并保存为图片,识别代码如下,之后会整合到源码中:
from fontTools.ttLib import TTFont
from reportlab.graphics.shapes import Drawing, Path, Group
from reportlab.graphics import renderPM
from reportlab.lib import colors
from reportlab.graphics.shapes import Pathclass ReportLabPen(BasePen):def __init__(self, glyphSet, path=None):BasePen.__init__(self, glyphSet)if path is None:path = Path()self.path = pathdef _moveTo(self, p):(x, y) = pself.path.moveTo(x, y)def _lineTo(self, p):(x, y) = pself.path.lineTo(x, y)def _curveToOne(self, p1, p2, p3):(x1, y1) = p1(x2, y2) = p2(x3, y3) = p3self.path.curveTo(x1, y1, x2, y2, x3, y3)def closePath(self):self.path.closePath()def ttfToImage(fontName, imagePath, fmt="png"):font = TTFont(fontName) # 打开 WOFF 字体文件gs = font.getGlyphSet()glyphNames = font.getGlyphNames()[1:] # 排除第一个 .notdef 字形for i in glyphNames:g = gs[i] # 获取当前字形的 Glyph 对象pen = ReportLabPen(gs, Path(fillcolor=colors.red, strokeWidth=1)) # 创建 ReportLabPen 对象,并设置相关参数g.draw(pen) # 将当前字形通过 pen 绘制到 path 对象上# 字形的宽度和高度w, h = g.width, g.width + 300 g = Group(pen.path)g.translate(0, 100) # 将图形向下移动 100 个像素d = Drawing(w, h) # 创建 Drawing 对象,设置宽度和高度d.add(g) # 将 Group 对象添加到 Drawing 对象中# 定义输出图片路径和文件名imageFile = f"{imagePath}/{i}.{fmt}"# 将 Drawing 对象渲染成图像文件并保存renderPM.drawToFile(d, imageFile, fmt)# 示例用法:将 `mao.woff` 字体文件的字形保存为图像
ttfToImage(fontName="mao.woff", imagePath='images')
识别结果如下:

识别图片
识别代码如下,之后会整合到源码中:
import os
import ddddocr # 导入 ddddocr 库def orc():# 创建一个 ddddocr 的 OCR 对象ocr = ddddocr.DdddOcr()dicts = {} # 初始化一个空字典,用于存储识别结果lists = os.listdir('./images') # 获取 images 目录下的所有文件列表# 遍历每个图片文件for imgs in lists:# 以二进制模式读取图片文件with open('./images/' + imgs, 'rb') as f:img_bytes = f.read()# 使用 OCR 对象的 classification 方法识别图片内容res = ocr.classification(img_bytes)# 输出文件名中提取的 Unicode 代码print(222222222222222222, imgs[3:-4])try:# 将文件名中的 Unicode 代码转换为字符,并将识别结果存入字典dicts[eval('u\'\\u' + imgs[3:-4].lower() + '\'')] = resexcept:# 如果转换或存储过程中出错,则跳过pass# 打印当前的字典内容print(dicts)# 调用 orc 函数
orc()
字典输出结果如下:

字典替换
拿到页面加密的源码,然后根据字典的key来替换掉对应的数字

替换后的数字与原始页面一样

源码
py文件
import requests
import execjs
import re
import shutil
import os
import ddddocr
from fontTools.pens.basePen import BasePen
from fontTools.ttLib import TTFont
from reportlab.graphics.shapes import Drawing, Path, Group
from reportlab.graphics import renderPM
from reportlab.lib import colors
from reportlab.graphics.shapes import Pathclass ReportLabPen(BasePen):def __init__(self, glyphSet, path=None):BasePen.__init__(self, glyphSet)if path is None:path = Path()self.path = pathdef _moveTo(self, p):(x, y) = pself.path.moveTo(x, y)def _lineTo(self, p):(x, y) = pself.path.lineTo(x, y)def _curveToOne(self, p1, p2, p3):(x1, y1) = p1(x2, y2) = p2(x3, y3) = p3self.path.curveTo(x1, y1, x2, y2, x3, y3)def closePath(self):self.path.closePath()def ttfToImage(fontName, imagePath, fmt="png"):font = TTFont(fontName) # 打开 WOFF 字体文件gs = font.getGlyphSet()glyphNames = font.getGlyphNames()[1:] # 排除第一个 .notdef 字形for i in glyphNames:g = gs[i] # 获取当前字形的 Glyph 对象pen = ReportLabPen(gs, Path(fillcolor=colors.red, strokeWidth=1)) # 创建 ReportLabPen 对象,并设置相关参数g.draw(pen) # 将当前字形通过 pen 绘制到 path 对象上# 字形的宽度和高度w, h = g.width, g.width + 300g = Group(pen.path)g.translate(0, 100) # 将图形向下移动 100 个像素d = Drawing(w, h) # 创建 Drawing 对象,设置宽度和高度d.add(g) # 将 Group 对象添加到 Drawing 对象中# 定义输出图片路径和文件名imageFile = f"{imagePath}/{i}.{fmt}"# 将 Drawing 对象渲染成图像文件并保存renderPM.drawToFile(d, imageFile, fmt)def download_woff():with open('猫眼.js','r',encoding='utf-8') as f:ctx = execjs.compile(f.read())headers_home = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","Cache-Control": "max-age=0","Connection": "keep-alive","Sec-Fetch-Dest": "document","Sec-Fetch-Mode": "navigate","Sec-Fetch-Site": "none","Sec-Fetch-User": "?1","Upgrade-Insecure-Requests": "1","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0","sec-ch-ua": "\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\", \"Microsoft Edge\";v=\"128\"","sec-ch-ua-mobile": "?0","sec-ch-ua-platform": "\"Windows\""}cookies_home = {"_lxsdk_s": "191b2c23b90-602-526-0ba%7C%7C1"}url = "https://www.maoyan.com/"s = requests.session()# 访问首页,保存cookier = s.get(url, headers=headers_home, cookies=cookies_home)headers = {"Accept": "*/*","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","Connection": "keep-alive","Referer": "https://www.maoyan.com/films/1464004","Sec-Fetch-Dest": "empty","Sec-Fetch-Mode": "cors","Sec-Fetch-Site": "same-origin","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0","X-Requested-With": "XMLHttpRequest","sec-ch-ua": "\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\", \"Microsoft Edge\";v=\"128\"","sec-ch-ua-mobile": "?0","sec-ch-ua-platform": "\"Windows\""}url = "https://www.maoyan.com/ajax/films/1464004"params = ctx.call("get_params")response = s.get(url, headers=headers, params=params).text# 保存woffwoff_url = "https:" + re.findall(r',url.*?woff', response)[0].split('"')[1]woff_res = s.get(woff_url).contentwith open('mao.woff', 'wb') as f:f.write(woff_res)f.close()result = re.findall('<span class="stonefont">(.*?)</span>', response)return resultdef clear_folder(folder_path):# 确保指定路径是一个文件夹if os.path.isdir(folder_path):# 遍历文件夹中的所有文件和子文件夹for filename in os.listdir(folder_path):file_path = os.path.join(folder_path, filename)try:# 如果是文件则删除if os.path.isfile(file_path) or os.path.islink(file_path):os.unlink(file_path)# 如果是文件夹则删除整个文件夹elif os.path.isdir(file_path):shutil.rmtree(file_path)except Exception as e:print(f"删除 {file_path} 时出错: {e}")print("删除完成")def orc():# 创建一个 ddddocr 的 OCR 对象ocr = ddddocr.DdddOcr()dicts = {} # 初始化一个空字典,用于存储识别结果lists = os.listdir('./images') # 获取 images 目录下的所有文件列表# 遍历每个图片文件for imgs in lists:# 以二进制模式读取图片文件with open('./images/' + imgs, 'rb') as f:img_bytes = f.read()# 使用 OCR 对象的 classification 方法识别图片内容res = ocr.classification(img_bytes)# 输出文件名中提取的 Unicode 代码print(222222222222222222, imgs[3:-4])try:# 将文件名中的 Unicode 代码转换为字符,并将识别结果存入字典dicts[eval('u\'\\u' + imgs[3:-4].lower() + '\'')] = resexcept:# 如果转换或存储过程中出错,则跳过pass# 返回字典内容return dictsif __name__ == '__main__':data = download_woff()# 指定要清空的文件夹路径folder_path = './images'clear_folder(folder_path)# 转换 TTF 字体并将字形转换为 PNG 图片ttfToImage(fontName="mao.woff", imagePath='images')# 使用ocr识别图片,返回字典res = orc()print(data)print(res)# 遍历字典并将识别结果输出for i in data:# 首先去掉所有的 &#x 和 ;cleaned_str = i.replace('&#x', '').replace(';', '')# 然后进行字符替换for key, value in res.items():cleaned_str = cleaned_str.replace(key.encode('unicode_escape').decode('ascii').replace('\\u', ''), value)print(cleaned_str)
js文件
const CryptoJS = require('crypto-js')var r = Math["ceil"](10 * Math["random"]())
var d = (new Date)["getTime"]()
var c = "method=GET&timeStamp="+d+'&User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0&index='+r+'&channelId=40011&sVersion=1'
var f = "&key=A013F70DB97834C0A5492378BD76C53A"function get_params(){return{"timeStamp": d,"index": r,"signKey": CryptoJS.MD5(c+f).toString(),"channelId": "40011","sVersion": "1","webdriver": "false"}
}
相关文章:
猫眼电影字体破解(图片转码方法)
问题 随便拿一篇电影做样例。我们发现猫眼的页面数据在预览窗口中全是小方框。在当我们拿到源码以后,数据全是加密后的。所以我们需要想办法破解加密,拿到数据。 破解过程 1.源码获取问题与破解 分析 在我们刚刚请求url的时候是可以得到数据的ÿ…...
flink wordcount
Maven配置pom文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/P…...
组合模式(Composite Pattern)
使用组合模式(Composite Pattern)是一个更优雅的方式来表示菜单和菜单项。组合模式允许我们将单个对象(如菜单项)和组合对象(如菜单)以相同的方式处理。 解决方案: 创建组合结构:我…...
教你制作一本加密的样本册
在这个信息的时代,保护自己的隐私和知识产权变得尤为重要。你有没有想过,如何将自己珍贵的样本资料变成一本只有自己才能查看的加密宝典?今天,我就来教你制作一本加密的样本册 第一步,打开浏览器,搜索FLBOO…...
C语言进阶【1】--字符函数和字符串函数【1】
本章概述 字符分类函数字符转换函数strlen的使用和模拟实现strcpy的使用和模拟实现strcat的使用和模拟实现strcmp的使用和模拟实现彩蛋时刻!!! 字符分类函数 字符: 这个概念,我们在以前的文章中讲过了。我们键盘输入的…...
git提交自动带上 Signed-off-by信息
为了确保在使用 Signed-off-by 签名的同时保留你的提交消息,你需要修改 prepare-commit-msg 钩子脚本,以便它不会丢失原始的提交信息。 增加prepare-commit-msg 钩子以保留提交消息 prepare-commit-msg 钩子的目的是在提交信息文件中插入额外的内容&am…...
图论(2)
一、度 度统计的是一个节点上又多少条边 度出度入度 出度:统计以该节点为起始点箭头指向外面的边的条数 入度:统计箭头指向该节点的边数 度为1的节点为悬挂节点,边为悬挂边 用矩阵计算节点的度 二、握手定理 比如这里第一个集合里面有三…...
ASP.NET Core 入门教学十九 依赖注入ioc
ASP.NET Core内置了对依赖注入(Dependency Injection,简称DI)的支持,这是一种设计模式,用于实现控制反转(Inversion of Control,简称IoC),从而使得应用程序组件之间的耦合…...
omm kill 内存碎片化
内存频繁 OOM(Out of Memory)会导致内存碎片化,并进一步加剧无可用内存分配的问题。碎片化是内存管理中常见的问题,当系统频繁分配和释放内存时,内存空间会被分割成许多小块,虽然内存总量可能足够,但这些小块无法满足较大进程或数据的内存需求,最终导致系统无法找到足够…...
JS中给元素添加事件监听器的各种方法详解(包含比较和应用场景)
JavaScript 中给元素添加事件监听器的各种方法详解 在 JavaScript 中,事件处理是前端开发的一个重要部分。无论是点击按钮、提交表单,还是鼠标悬停,都涉及到事件监听。本文中,我将详细讲解各种给元素添加事件监听器的方法&#x…...
Python基本数据类型之复数complex
来源: “码农不会写诗”公众号 链接:Python基本数据类型之复数complex 文章目录 01 基本概念02 基本运算03 拓展1复数与向量 复数complex Python基本数据之复数(complex)即包含实部和虚部的数字。 01 基本概念 即包含实部和虚部的数字。 在Python中&am…...
第六届机器人与智能制造技术国际会议 (ISRIMT 2024)
目录 会议详情 主题 会议官网 会议详情 第六届机器人与智能制造技术国际研讨会(ISRIMT 2024)计划于2024年9月20-22日在常州举行。会议主要聚焦“机器人”和“智能制造技术”的研究领域,旨在为机器人和智能制造技术领域的专家学者、工程技术…...
鸿蒙轻内核M核源码分析系列十九 Musl LibC
往期知识点记录: 鸿蒙(HarmonyOS)应用层开发(北向)知识点汇总 轻内核M核源码分析系列一 数据结构-双向循环链表 轻内核M核源码分析系列二 数据结构-任务就绪队列 鸿蒙轻内核M核源码分析系列三 数据结构-任务排序链表 轻…...
mysqldump备份恢复数据库
mysqldump程序可以用来备份和恢复数据库 ,默认情况mysqldump会创建drop table, create table,和insert into的sql语句. 语法 > mysqldump [options] db_name [tbl_name ...] > mysqldump [options] --databases db_name ... > mysqldump [options] --all-databases备…...
路径规划——RRT算法
路径规划——RRT算法 算法原理 RRT算法的全称是快速扩展随机树算法(Rapidly Exploring Random Tree),它的思想是选取一个初始点作为根节点,通过随机采样,增加叶子节点的方式,生成一个随机扩展树,当随机树中的叶子节点…...
OPCUA-PLC
下载opcua服务器(有PLC可以直连),UaAnsiCServer下载路径 双击运行如下,Endpoint显示opcua服务路径 opc.tcp://DESKTOP-9SD7K4B:48020 下载opcua客户端(类似编写代码连接操作),UaExpert下载路径 如果连接失败,有一个授权认证,点击同意就行 java代码实现连接opcUA操作 pom.…...
在Windows系统上部署PPTist并实现远程访问
在Windows系统上部署PPTist并实现远程访问 前言PPTist简介本地部署PPTist步骤1:获取PPTist步骤2:安装依赖步骤3:运行PPTist 使用PPTist远程访问PPTist步骤1:安装Cpolar步骤2:配置公网地址步骤3:配置固定公网…...
【Grafana】Prometheus结合Grafana打造智能监控可视化平台
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
隐私计算实训营:SplitRec:当拆分学习遇上推荐系统
拆分学习的概念 拆分学习的核心思想是拆分网络结构。每一个参与方拥有模型结构的一部分,所有参与方的模型合在一起形成一个完整的模型。训练过程中,不同参与方只对本地模型进行正向或者反向传播计算,并将计算结果传递给下一个参与方。多个参…...
存在nginx版本信息泄露(请求头中存在nginx中间件版本信息)
在Nginx的配置文件中,server_tokens指令用于控制Nginx在HTTP响应头中包含的服务器版本信息,默认为true,开启状态。当设置为off时,Nginx将不会在响应头中包含任何服务器版本信息,仅显示“Server: nginx”这一行…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
计算机基础知识解析:从应用到架构的全面拆解
目录 前言 1、 计算机的应用领域:无处不在的数字助手 2、 计算机的进化史:从算盘到量子计算 3、计算机的分类:不止 “台式机和笔记本” 4、计算机的组件:硬件与软件的协同 4.1 硬件:五大核心部件 4.2 软件&#…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
