猿人学web端爬虫攻防大赛赛题解析_第七题:动态字体,随风漂移

本文详细解析了一道动态字体反扒的爬虫难题,涉及字体加密和解密。通过分析字体映射关系和使用OCR识别,成功获取并匹配了胜点数据。文中介绍了两种解密方法,一种是建立字体编码与数字的映射关系,另一种是通过OCR技术识别数字。
摘要由CSDN通过智能技术生成

一、前言

上一道字体反扒的css加密题还是第四题,不过那道题主要还只是做了个数字的坐标偏移,逻辑分析起来不算难,这道动态字体的题算是真正的字体反扒题,做了一下感觉挺长见识的,对于了解同类型字体加密有不小帮助,闲言少叙,开搞!

二、分析过程

题目的要求是采集胜点列的数据,找出胜点最高的召唤师,那加密的数据肯定就是胜点数据了,核心应该是解析出胜点值,其次就是将这个值与对应的召唤师匹配,最后比较五十个玩家的胜点,找出胜点最高那一个。

这里仔细观察一下,可以发现胜点数字的字形都不是很规则,看着就像是图形渲染的,复制粘贴一下就会发现,这个值果然是图片符号。
在这里插入图片描述

打开开发者模式看一下请求的结果,不难发现这里的data应该是加密后的数值,而woff则是加密字体文件,而且这个字符串是base64加密后的。
在这里插入图片描述
往前查看一下请求调用栈,在网页源码这找到了对字体进行处理的部分代码:
在这里插入图片描述字体文件加载是通过这两行处理的,也就是通过这里加载得到字体的ttf文件,在页面渲染的过程中对data内的value值进行渲染从而得到最后的显示结果。

 ttf = data.woff;
 $('.font').text('').append('<style type="text/css">@font-face { font-family:"fonteditor";src: url(data:font/truetype;charset=utf-8;base64,' + ttf + '); }</style>');

从源代码这里看不出字体的加密字符和数值的对应关系,于是把woff文件逆编码后保存为ttf格式的字体文件:

b64_code="AAEAAAAKAIAAAwAgT1MvMgPtafAAAAEoAAAAYGNtYXBBft4uAAABpAAAAYpnbHlmPD0Wg.....省略部分字符"
with open('D:\\yuan_font.ttf', 'wb') as f:
    f.write(base64.b64decode(b64_code))

将保存的ttf字体文件在在线字体编辑器里打开,可以发现这里还原出了每个加密编码对应的数字字符:
在这里插入图片描述
这里验证了一下这个字体加密编码跟对应的数值关系,发现每次请求,返回的value值都是不一样的,以第一页为例,对于第一个胜点3236,每次value值都在变化,所以想靠固定编码关系来直接破解是不可能了:

第一次请求:
0: {
   value: "&#xf134 &#xc261 &#xf134 &#xa613 "}
第二次请求:
0: {
   value: "&#xf524 &#xa934 &#xf524 &#xc825 "}
第三次请求:
0: {
   value: "&#xf289 &#xa471 &#xf289 &#xa265 "}

只好进一步查看字体文件的详细信息,利用python的fontTools这个字体库的函数将ttf字体转换为xml格式:

font = TTFont('D:\\yuan_font.ttf')
font.saveXML('D:\\yuan_font.xml')

打开xml文件,这里的codename对应的就是0-9这十个数字的加密编码:
在这里插入图片描述
底下的TTGlyph内,contour标签中就是每个数字字形的坐标,全部绘制出来就构成了网页最后渲染出的字体效果,那么既然编码对应的是具体的字形坐标,无论每次返回的数字字符编码是多少,那每个字形的坐标总不会变吧。在这里插入图片描述

好家伙,我还是天真了,打开了两份文件对比了一下,发现即使是相同的数字,其字形竟然也不同,坐标值x,y会有会有小范围的浮动,这就导致即使同样的数字每次渲染出来都会有所不同,就比如底下这个2,虽然看着都一样,但是在轮廓上还是有明显差异:
在这里插入图片描述

如此看来,轻松简单的方法是不可能存在了,现在可能的解法有两种,一是虽然每个数字每次字形都有差异,但差异毕竟有限,可以尝试对同一个数字字形的两组坐标值进行对比,如果差别不超过某个阈值,那么就可以认为是同一个字;二是直接把字形文件的坐标绘制出来,生成字体图片,之后再ocr识别数字。

三、代码实现

3.1、方法一:字体映射关系识别数字

为了找到坐标跟对应字符间的映射关系,这里选了数字4的坐标来分析。可以发现,虽然每次返回的坐标值都不一样,但是同一个字体总的坐标对数量是一致的,而且pt内的on值是完全一致的,那么就可以尝试根据两个字体文件内所有的on值是否完全相等来判断是不是同一个数字。
在这里插入图片描述在这里插入图片描述

先随便找一个字体文件,手动分析出字符编码与数字的对应关系:
在这里插入图片描述在这里插入图片描述

以这个关系作为其他字符编码相比较的参考基准,即如果下一个字体文件中某一个数字对应的所有on值都跟标准对照表里的on值相等,那就确定是同一个数字

    #选定一个基准参照,确定flag和数字的关系
    font = TTFont("F:\\temp\yuan_font_1.ttf")
    gs = font.getGlyphSet()
    glyphNames = font.getGlyphNames()

    #这个数字顺序跟xml里的TTGlyph name顺序是一致的
    num_list = [4,6,9,0,2,8,1,5,3,7]
    map_dict={
   }

    for i,name in enumerate(glyphNames[1:]):
        g = gs[name]
        flag = list(g._glyph.flags)#读取每个坐标对应的on值
        # coord=g._glyph.coordinates#获取每个字符的坐标序列

        #map_dict字典里的键对应的数字,值则是on值构成的列表
        map_dict[num_list[i]]=flag

    print('标准对应关系为:',map_dict)


    #待解析文件
    font = TTFont("F:\\temp\yuan_font_2.ttf")
    gs = font.getGlyphSet()
    glyphNames = font.getGlyphNames()
    list2=[]
    for i,name in enumerate(glyphNames[1:]):
        g = gs[name]
        flag = list(g._glyph.flags)#读取每个坐标对应的on值

        for key,value in map_dict.items():
            if value==flag:
                list2.append((name,key))

    print('解析后的数字对应关系:')
    for m in list2:
        print(m)
        pass        

运行一下,输出结果为:

标准对应关系为: {
   4: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 6: [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0], 9: [1, 0, 0, 1, 0, 1, 0, 
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值