在某网站发现一个字体加密,今天来尝试破解
一、查找代码看到一个日期 我尝试复制,发现复制结果是乱码的
驋龤驋龤-龒驋-驋驋
查看源码发现是这样的
驋龤驋龤-龒驋-驋驋
感觉应该是字体加密了,看到这个类名上有一个特殊的字体cyzone-secret
.strongbox {
font-family: 'cyzone-secret','Hiragino Sans GB','Microsoft yahei',Arial,sans-serif,'宋体'!important;
}
点击源码文件,看到了一段字体样式,
/* 省略了很长的内容*/
@font-face{
font-family:'cyzone-secret';
src:url('data:application/font-ttf;charset=utf-8;base64,AAEAAAALAIAAA... AAAAAAAAAAAAAAAAAAAAA') format('truetype')
}
.strongbox{
font-family:'cyzone-secret','Hiragino Sans GB','Microsoft yahei',Arial,sans-serif,'宋体'!important
}
查看网页源码,没有看到相关代码,断定是动态生成的样式片段。找遍网页所有文件,都没有发现这个字体cyzone-secret
的代码
经过观察,发现页面里边有这么一段自执行的代码
! function (w, d) {
if (!w.ActiveXObject) {
// 省略了很长的内容
let code = unescape("%64%2E%77%72%69%...%65%3E%22%29");
console.log(code); // 打印语句是我自己加入的
eval(code);
}
}(window, document);
单独放入一个html文件中,控制台打印出了如下代码 所以这段字体就是这个
eval
函数生成的了。
将base64的数据,提取后保存为ttf文件
# -*- coding: utf-8 -*-
import base64
# 省略了很长的...
b64_code = 'AAEAAAALAIAAAwAwR1NV...kBCgELAQwAAAAAAAAAAAAAAAAAAAAA...'
with open('font.ttf', 'wb') as f:
f.write(base64.decodebytes(b64_code.encode()))
查看ttf文件字体对应的编码: http://fontstore.baidu.com/static/editor/index.html
安装依赖
pip3 install fonttools
将ttf文件保存成xml文件
from fontTools.ttLib import TTFont # 导包
font = TTFont('font.ttf')
font.saveXML('font.xml')
可以再font.xml中看到字体编码对应的文字 字体坐标,就是字体显示出来的样子
再刷新页面,再生成一组字体文件ttf和xml文件。
观察发现:
- 发现字体文件【code编码 - name】的对应关系发生了变化
- 【name-TTGlyph坐标】没有发生变化,而TTGlyph坐标就是显示的文字轮廓,所以可以确定,无论文件怎么变,name所显示的内容不变
所需要做的就是每次确定【code编码 - name】之间的关系
code编码 name - TTGlyph坐标
完整的代码
# -*- coding: utf-8 -*-
"""
字体反爬测试文件
https://data.cyzone.cn/event/list-0-1-0-0-0-0-1/0?clear=1
"""
import base64
import re
from io import BytesIO
from urllib.parse import unquote
import requests
from fontTools.ttLib import TTFont # 导包
from scrapy.selector import Selector
def get_page():
"""获取页面内容"""
headers = {
'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.9',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7',
'cache-control': 'no-cache',
'pragma': 'no-cache',
'sec-ch-ua': '"Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"',
'sec-ch-ua-mobile': '?0',
'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 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
}
url = 'https://data.cyzone.cn/event/list-0-1-0-0-0-0-1/0?clear=1'
response = requests.get(url, headers=headers)
return response.text
def get_ttf_base64(text):
"""通过正则表达式,查找到表示字体文件的编码"""
result = re.search(r'let code = unescape\((.*)\);', text)
code = result.group(1)
# url解码
unquote_code = unquote(code)
result2 = re.search(r"base64,(.*?)'\)", unquote_code)
b64_code = result2.group(1)
return b64_code
def parse_time(text):
"""找到页面中所需要的文字"""
sel = Selector(text=text)
string = sel.css(".list-table3 .table-plate3 .strongbox::text").extract()[-1]
return string
def get_mapping(b64_code):
"""找到字体新的映射关系"""
text_font = TTFont(BytesIO(base64.decodebytes(b64_code.encode())))
text_font.saveXML("text.xml")
text_mapping = text_font['cmap'].tables[0].ttFont.tables['cmap'].tables[0].cmap
# 通过比对发现, glyph* 对应的坐标没有变化,即数字值没有变化
_mapping = {
'glyph00009': '8',
'glyph00005': '4',
'glyph00001': '0',
'glyph00008': '7',
'glyph00010': '9',
'glyph00002': '1',
'glyph00006': '5',
'glyph00003': '2',
'glyph00004': '3',
'glyph00007': '6',
}
__mapping = {}
for key, val in text_mapping.items():
__mapping[key] = _mapping[val]
# print(text_mapping)
return __mapping
def decode_text(mapping, string):
ret_list = []
for char in string:
value = mapping.get(ord(char), char)
ret_list.append(value)
return ''.join(ret_list)
def main():
page = get_page()
print(page)
b64_code = get_ttf_base64(page)
mapping = get_mapping(b64_code)
string = parse_time(page)
decode_string = decode_text(mapping, string)
print(decode_string)
if __name__ == '__main__':
main()
参考文章
- 破解字体加密解决思路
- 反反爬技术,破解猫眼网加密数字