猫眼电影字体破解(图片转码方法)

news2024/9/17 7:09:16

问题

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

破解过程 

        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 Path

class ReportLabPen(BasePen):
    def __init__(self, glyphSet, path=None):
        BasePen.__init__(self, glyphSet)
        if path is None:
            path = Path()
        self.path = path

    def _moveTo(self, p):
        (x, y) = p
        self.path.moveTo(x, y)

    def _lineTo(self, p):
        (x, y) = p
        self.path.lineTo(x, y)

    def _curveToOne(self, p1, p2, p3):
        (x1, y1) = p1
        (x2, y2) = p2
        (x3, y3) = p3
        self.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() + '\'')] = res
        except:
            # 如果转换或存储过程中出错,则跳过
            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 Path


class ReportLabPen(BasePen):
    def __init__(self, glyphSet, path=None):
        BasePen.__init__(self, glyphSet)
        if path is None:
            path = Path()
        self.path = path

    def _moveTo(self, p):
        (x, y) = p
        self.path.moveTo(x, y)

    def _lineTo(self, p):
        (x, y) = p
        self.path.lineTo(x, y)

    def _curveToOne(self, p1, p2, p3):
        (x1, y1) = p1
        (x2, y2) = p2
        (x3, y3) = p3
        self.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)


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()

    # 访问首页,保存cookie
    r = 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

    # 保存woff
    woff_url = "https:" + re.findall(r',url.*?woff', response)[0].split('"')[1]
    woff_res = s.get(woff_url).content
    with open('mao.woff', 'wb') as f:
        f.write(woff_res)
    f.close()

    result = re.findall('<span class="stonefont">(.*?)</span>', response)
    return result


def 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() + '\'')] = res
        except:
            # 如果转换或存储过程中出错,则跳过
            pass

    # 返回字典内容
    return dicts


if __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"
    }
}

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

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

相关文章

halcon 由离散点云生成3d模型(2步)

一&#xff0c;创建立方体的3d坐标&#xff0c;定义X,Y,Z坐标 dev_open_window (0, 0, 512, 512, black, WindowHandle) x:[0,0,1,1,0,0,1,1] y:[0,1,1,0,0,1,1,0] z:[0,0,0,0,2,2,2,2] 二&#xff0c;由3创建3d模型&#xff08;在这里是将所有的点集合为一体&#xff09; ge…

多路转接之select(fd_set介绍,参数详细介绍),实现非阻塞式网络通信

目录 多路转接之select 引入 介绍 fd_set 函数原型 nfds readfds / writefds / exceptfds readfds 总结 fd_set操作接口 timeout timevalue 结构体 传入值 返回值 代码 注意点 -- 调用函数 select的参数填充 获取新连接 注意点 -- 通信时的调用函数 添…

JavaScript使用高德API显示地图

前言 在JavaScript中&#xff0c;使用Leaflet库显示地图是一种常见的做法。Leaflet是一个开源的JavaScript库&#xff0c;用于在Web应用程序中创建互动地图。它非常轻量级&#xff0c;易于使用&#xff0c;并且提供了多种功能&#xff0c;使开发者能够轻松地将地图集成到他们的…

【Python报错已解决】 SyntaxError: positional argument follows keyword argument

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一&#xff1a;调整参数顺序2.2 步骤二…

银行贷款产品

1、对公贷款 1.1 一般贷款 按贷款期限可分为短期贷款和中长期贷款。短期贷款是指金融企业根据有关规定发放的,期限在1年(含1年)以下的各种贷款。中长期贷款是指金融企业发放的贷款期限在1年以上的各种贷款。 按提供方式不同可分为信用贷款、担保贷款等。信用贷款是指金融企…

springboot实战学习(1)(开发模式与环境)

目录 一、实战学习的引言 &#xff08;1&#xff09;前后端的大致学习模块 &#xff08;2&#xff09;后端 &#xff08;3&#xff09;前端 二、开发模式 一、实战学习的引言 &#xff08;1&#xff09;前后端的大致学习模块 &#xff08;2&#xff09;后端 Validation&#xf…

ubuntu上通过openvswitch卸载实现roce over vxlan

环境 操作系统&#xff1a; uname -a Linux 5.4.0-187-generic #207-Ubuntu SMP Mon Jun 10 08:16:10 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux Mellanox网卡&#xff1a; ethtool -i ens6np0 driver: mlx5_core version: 23.10-2.1.3 firmware-version: 20.39.3004 (MT_0…

在Linux下查看HBA卡的速率和状态

平时在Linux下映射存储&#xff0c;都是映射哪台就给哪台插线&#xff0c;然后在存储端扫描WWPN&#xff0c;简单粗暴&#xff0c;没技术含量。当然&#xff0c;光交下也可以看。 1&#xff0c;查看当前卡的品牌&#xff0c;常用的卡有两种&#xff0c;Emulex和Qlogic。 lspc…

C语言 - 预处理详解(一)#预定义符号 ##define #undef

文章目录 前言 一、预定义符号 二、#define (一)、#define 定义的标识符 (二)、#define 定义的宏 (三)、#define 替换规则 (四)、# 和 ## 1、 # 的作用 2、## 的作用 (五)、带副作用的宏参数 (六)、宏和函数的对比 (七)、命名约定 三、#undef 总结 前言 路漫漫其修远兮&#…

C语言深度剖析--不定期更新的第五弹

const关键字 来看一段代码&#xff1a; #include <stdio.h> int main() {int a 10;a 20;printf("%d\n", a);return 0; }运行结果如下&#xff1a; 接下来我们在上面的代码做小小的修改&#xff1a; #include <stdio.h> int main() {const int a 1…

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口 int main(int argc, char *argv[]) {//…

IDA的安装和使用

IDA Pro&#xff08;简称 IDA&#xff0c;官网地址为 https://www.hex-rays.com/products/ida/&#xff09;是一个反编译器&#xff0c;同时具备调试器的功能。IDA Pro 的功能非常强大&#xff0c;几乎所有的逆向题目都需要用到它&#xff0c;因而也被称为「逆向神器」 IDA安装…

MySQL入门到精通

一、创建数据库 CREATE DATABASE 数据库名称; 如果数据库存在&#xff0c;则会提示报错。 二、选择数据库 USE 数据库名称; 三、创建数据表 CREATE TABLE 数据表名称; 四、MySQL数据类型 MySQL支持多种类型&#xff0c;大致可以分为三类&#xff1a;数值、日期/时间和字符串…

Data Filtering Network论文浅析

time2023-09paperhttps://arxiv.org/abs/2309.17425codehttps://huggingface.co/apple/DFN5B-CLIP-ViT-H-14-378org.Apple个人博客地址http://myhz0606.com/article/dfn Motivation 训练一个好的CLIP模型依赖大规模&#xff0c;高质量的训练数据。通过爬虫&#xff0c;可以很…

S7-1200与G120变频器CU240E-2控制单元通过353报文实现PN通信的基本方法

S7-1200与G120变频器CU240E-2控制单元通过353报文实现PN通信的基本方法 西门子报文353 PKW+PZD-2/2, 6个字中前4个字是PKW用,后2个字是PZD用, 结合以上内容, 可以知道第5个字是STW1控制字,第6个字是转速给定值(PLC输出);第5个字是ZSW1是状态字,第6个字是当前转速值(P…

LeetCode:快乐数(202)

目录 题目 代码思路 双指针 代码实现 题目 202. 快乐数 - 力扣&#xff08;LeetCode&#xff09; 编写一个算法来判断一个数 n 是不是快乐数。 [ 快乐数 ] 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程…

ThinkPHP5 5.0.23-rce远程代码执行漏洞复现

漏洞描述 ThinkPHP是一款运用极广的PHP开发框架。其版本5中&#xff0c;由于框架对控制器名没有进行足够的检测&#xff0c;会导致在没有开启强制路由的情况下可执行任意方法&#xff0c;从而导致远程命令执行漏洞。 启动容器 docker-compose up -d 查看端口 docker ps 端口为…

【C++进阶】hash表的封装

文章目录 hash表哈希表的关键组成部分哈希表的优缺点优点&#xff1a;缺点&#xff1a; 常见应用场景 开放定址法实现hash表负载因子 (Load Factor)负载因子的意义负载因子的影响再散列 (Rehashing)示例 整体框架insertFinderasehash桶封装框架insertfinderase~HashTable() 总结…

从路径优化学习FastPlanner之B样条曲线平滑(二):FastPlanner中B样条曲线代码理解与解读

参考别人的博客学习 根据之前一章只是大致了解了B样条数学原理&#xff0c;实际读代码还有疑惑。 控制点是什么&#xff1f;和规划出的路径点什么关系&#xff1f; 控制点可以说我们规划出的路径点&#xff0c;即n等于轨迹点个数。也可以不是轨迹点&#xff0c;通过线性方程反解…

Einsum(Einstein summation convention)

Einsum&#xff08;Einstein summation convention&#xff09; 笔记来源&#xff1a; Permute和Reshape嫌麻烦&#xff1f;einsum来帮忙&#xff01; The Einstein summation convention is a notational shorthand used in tensor calculus, particularly in the fields of …