破解字体加密解决思路

 之前做过爬取大众点评美团等电话号码遇到过字体反爬  ,故此处做个人记录

 例如:58同城、起点中文网、大众点评等

 

加密思路


原理:关于字体加密,其实是将一种特定的字体库来代替浏览器本身的字体库显示的过程

 

以58字体库加密为例


58同城中,无论是简历中的字体加密,还是房产信息中的加密都是有迹可循的;正如我们所知,加密的字体数量一般都不会太大且会随访问请求而变化,也就是说 ,字体加密的字体库每次都是变化的,我们需要在爬去网页的同时拿取加密的字体库,进行解密,

 

基本流程:

1、请求页面
2、获取加密的字体库
3、解析字体库,获取字体间的映射关系
4、获取加密的字体,获取字体间映射关系,一一对应


字体加密一般是网页修改了默认的字符编码集,在网页上加载的他们自己定义的字体文件作为字体的样式,可以正确地显示数字,但是在源码上同样的二进制数由于未加载自定义的字体文件就由计算机默认编码成了乱码。
网站url:https://zz.58.com/pinpaigongyu/?utm_source=sem-baidu-pc&spm=105916146708.26420796287&PGTID=0d100000-0015-63fb-169b-74ddef51914e&ClickID=2
在这里插入图片描述

一般来说,通用的解决办法是找到字体文件,分析文件中的映射关系。一般来说,字体文件都是作为样式加在加密字体的部位。

在样式中,我基本上都看完了, 从名字上看只有这个fangchan-secret最可能是字体加密文件。

 

在这里插入图片描述

 

在源码中Ctrl+F搜索fangchan-secret 寻找字体加密文件

在这里插入图片描述

在58的源码中,字体文件是通过base64加密之后放在js里面了。把其中加密的部分取出,第一次是分析,在代码中可使用正则将其中的内容取出来。
58的字体加密文件每次网页刷新,其中的映射顺序会变,所以在不刷新的情况下,再复制一份
这里取第一条799 及其对应的乱码。

base64_str = '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'
price_code = '鸺齤齤'

首先进行base64解码,转化成为二进制形式,在方法中同时我也将字体文件写入了otf字体文件中。

def make_font_file(base64_string: str):
    bin_data = base64.decodebytes(base64_string.encode())
    with open('text.otf','wb') as f:
        f.write(bin_data)
    return bin_data

使用fontCreator打开字体文件可以直观的看到每一个数字对应的编码

在这里插入图片描述

将字节文件转化为xml格式

def convert_font_to_xml(bin_data):
    # 由于TTFont接收一个文件类型
    # BytesIO(bin_data) 把二进制数据当作文件来操作
    font = TTFont(BytesIO(bin_data))
    font.saveXML("text.xml")
bin_data = make_font_file(base64_str)
convert_font_to_xml(bin_data)
# 获取对应关系
font = TTFont(BytesIO(make_font_file(base64_str)))
uniList = font['cmap'].tables[0].ttFont.getGlyphOrder()
c = font['cmap'].tables[0].ttFont.tables['cmap'].tables[0].cmap
# c = font.getBestCmap()
print('cmap is:::::', c)

打印出来是这样子:cmap is::::: {38006: 'glyph00007', 38287: 'glyph00009', 39228: 'glyph00004', 39499: 'glyph00005', 40506: 'glyph00008', 40611: 'glyph00006', 40804: 'glyph00010', 40850: 'glyph00001', 40868: 'glyph00003', 40869: 'glyph00002'}
该字典的键就是网页上显示的乱码的unicode编码,值就是该乱码对应的真正数字。

其中glyph00007是资源,每一个glyph0000x对应一个数字 、58的字体文件比较偷懒,根据其后缀就知道对应的数字是对少
又字体的xml文件中,下图部分开始可知(我没截取完),glyph00000没有意义,glyph00001对应0,glyph00001对应1以此类推。

在这里插入图片描述

那么,我们根据网页抓取的乱码的unicode编码,获取其对对应的字源,即可获取所对应的数字。

def get_num(string):
    ret_list = []
    for char in string:
        decode_num = ord(char)
        num = c[decode_num]
        num = int(num[-2:])-1
        ret_list.append(num)
    return ret_list

如果通过浏览器看到的是类似鸺齤齤这样的乱码,使用爬虫获取的数据是类似 鸺龒龒 十六进制的数字,可直接截取后面四位转化为十进制数后在通过映射表查找。

 

以下是详细代码

import base64
from io import BytesIO
from fontTools.ttLib import TTFont
import requests
import re
from lxml import etree
 
 
url = 'https://cd.58.com/wuhou/chuzu/b5j5'
res = requests.get(url)
bs64_str = re.findall("charset=utf-8;base64,(.*?)'\)", res.text)[0]
 
 
def get_page_show_ret(string):
    font = TTFont(BytesIO(base64.decodebytes(bs64_str.encode())))
    c = font['cmap'].tables[0].ttFont.tables['cmap'].tables[0].cmap
    ret_list = []
    for char in string:
        decode_num = ord(char)
        if decode_num in c:
            num = c[decode_num]
            num = int(num[-2:])-1
            ret_list.append(num)
        else:
            ret_list.append(char)
    ret_str_show = ''
    for num in ret_list:
        ret_str_show += str(num)
    return ret_str_show
 
 
page = etree.HTML(res.text)
li = page.xpath('.//ul[@class="listUl"]//li')[0:-1]
for each_li in li:
    title = each_li.xpath('.//div[@class="des"]/h2/a/text()')[0].strip()
    title = get_page_show_ret(title)
    price = each_li.xpath('.//div[@class="money"]/b/text()')[0]
    price = get_page_show_ret(price)
    print(title)
    print(price)
    print('='*20)

 

### Normal 加密方法的实现方案 Normal 加密可以理解为一种常规化的加密技术,具体取决于实际需求和场景。以下是基于对称加密(单密钥)和非对称加密两种常见模式下的正常化加密支持方式。 #### 对称加密 (Symmetric Encryption) 对称加密是一种常见的加密方式,其中同一个密钥用于数据的加密解密过程[^1]。这种方式的优点在于算法公开、执行速度较快且效率较高。然而,其主要挑战在于密钥的安全分发以及防止密钥泄露的风险。 ##### Python 实现 AES 加密示例 AES 是目前最广泛使用的对称加密标准之一。下面是一个简单的 AES 加密/解密实现: ```python from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from Crypto.Random import get_random_bytes def aes_encrypt(key, plaintext): cipher = AES.new(key, AES.MODE_CBC) ct_bytes = cipher.encrypt(pad(plaintext.encode('utf-8'), AES.block_size)) iv = cipher.iv return iv + ct_bytes def aes_decrypt(key, ciphertext): iv = ciphertext[:16] cipher = AES.new(key, AES.MODE_CBC, iv=iv) pt = unpad(cipher.decrypt(ciphertext[16:]), AES.block_size).decode('utf-8') return pt key = get_random_bytes(16) # 密钥长度应为16字节 plaintext = "This is a secret message" ciphertext = aes_encrypt(key, plaintext) decrypted_text = aes_decrypt(key, ciphertext) print(f"Ciphertext: {ciphertext}") print(f"Decrypted Text: {decrypted_text}") ``` 上述代码展示了如何使用 PyCryptodome 库来完成 AES 的加密解密操作。 --- #### 非对称加密 (Asymmetric Encryption) 非对称加密利用一对密钥——公钥和私钥分别负责加密解密的过程[^2]。这种机制解决了对称加密中密钥分发的问题,但在性能上不如对称加密高效。 ##### Python 中 RSA 的实现 尽管某些库可能未直接提供私钥加密的功能,但可以通过调整现有 API 来满足特定需求。以下是一段关于 RSA 加密/解密的简单例子: ```python from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_OAEP # 生成密钥对 key = RSA.generate(2048) private_key = key.export_key() public_key = key.publickey().export_key() # 加载密钥 rsa_public_key = RSA.import_key(public_key) rsa_private_key = RSA.import_key(private_key) cipher = PKCS1_OAEP.new(rsa_public_key) encrypted_message = cipher.encrypt(b"This is a top-secret message") decrypt_cipher = PKCS1_OAEP.new(rsa_private_key) decrypted_message = decrypt_cipher.decrypt(encrypted_message) print(f"Encrypted Message: {encrypted_message}") print(f"Decrypted Message: {decrypted_message.decode()}") ``` 此代码片段说明了如何在 Python 中使用 `PyCryptodome` 进行 RSA 的基本加解密流程。 --- #### 字体加密反爬虫解决方案 对于涉及字体加密的应用场景,比如网页中的动态文字加载防爬取措施,可参考以下思路[^3]。通过对特殊字符进行映射替换并结合自定义编码逻辑,能够有效提升安全性。 ```python encryption_dict = {'a': '!', 'b': '@', 'c': '#'} # 自定义映射表 text_to_encrypt = "abc" encrypted_text = ''.join([encryption_dict.get(char, char) for char in text_to_encrypt]) print(f"Encrypted Text: {encrypted_text}") # 解密部分逆向处理即可 decryption_dict = {v: k for k, v in encryption_dict.items()} decrypted_text = ''.join([decryption_dict.get(char, char) for char in encrypted_text]) print(f"Decrypted Text: {decrypted_text}") ``` 该案例仅作示意用途,在真实项目中建议设计更复杂的映射规则以增强防护效果。 --- #### PDF 文件综合保护策略 针对文档类资源如 PDF 的全面安全保障,则需要集成多种功能模块共同作用,例如电子签名验证身份真实性、加盖时间戳证明时效性等附加特性[^4]。Java 提供了一套完整的工具链帮助开发者快速搭建此类服务框架。 ```java // Java Keystore 参数配置实例 String storepass = "password"; // 存储密码 String keypass = "password"; // 私钥密码 String alias = "mycert"; // 别名 int keysize = 2048; // 密钥大小 String algorithm = "RSA"; // 公钥算法 int validity = 365; // 证书有效期天数 System.out.println("Keystore Configuration:"); System.out.printf("Store Pass:%s\n", storepass); System.out.printf("Key Pass:%s\n", keypass); System.out.printf("Alias:%s\n", alias); System.out.printf("Keysize:%d\n", keysize); System.out.printf("Algorithm:%s\n", algorithm); System.out.printf("Validity:%d days\n", validity); ``` 以上脚本体现了创建数字证书所需的关键要素及其含义解析。 --- ### 总结 根据不同的应用场景和技术背景,“normal”加密的具体实施方案会有所差异。无论是选用高效的对称加密还是兼顾灵活性的非对称加密,亦或是面向特定领域定制开发专属加密手段,都需权衡安全性和性能之间的关系。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值