• 记一次css字体反爬


    前段时间在看css反爬的时候,发现很多网站都做了css反爬,比如,设置字体反爬的(58同城租房版块,实习僧招聘https://www.shixiseng.com/等)设置雪碧图反爬的(自如租房http://gz.ziroom.com/)。

    还有一个网站本身是没有其他反爬措施的,只是设置了字体反爬,但是这个网站的反爬就有些扯淡,http://www.qiwen007.com/,我们随便点开一个文章,并打开开发者工具

     其中的文字并不是像其他字体反爬一样,是将某些文字转为了Unicode显示在源码中的

    首先来看一下破解流程及思路:

    """
    流程及思路:
    1. 通过requests请求获取响应数据,得到的就是源码中加密数据(杂乱的文章)
    2. 将加密数据的每一个字符转成Unicode编码,得到字符的Unicode列表
    3. 通过搜索源码中font-face关键字,找到字体库文件(ttf/woff文件)将当前域名拼接上/hansansjm.ttf,即 http://www.qiwen007.com/hansansjm.ttf,打开后即下载
    4. 使用FontCreator或者百度字体编辑器打开ttf文件,会看到里面每个字符
    5. 将ttf转为xml文件
    6. 同时使用pycharm打开xml文件,找到 glyf 标签下面的 TTGlyph 标签,里面的name 即是xml文件中的Unicode编码,
    里面contour下面的pt的x,y即是每个字符的坐标,计算坐标差(计算坐标差的时候,可以直接取第一个contour的前两组pt即可)
    (但是 TTGlyph name的顺序 有可能和ttf文件里面的字符的Unicode顺序对应不上,此时就要从GlyphOrder里面看是否对应,
    如果对应,就从GlyphOrder中获取每个xml文件中的每个字符的Unicode列表,然后遍历列表,从 glyf 中找到对应的坐标)
    7. 手动去组成汉字字符的列表,然后使用映射(dict(zip()))得到汉字和坐标差的映射
    8. 在第6步中我们可以获取Unicode和坐标差的映射
    9. 然后回到第2步,拿到加密数据的Unicode编码列表后,去Unicode和坐标差的映射中找到对应的坐标差,拿到坐标差后找到对应的汉字
    至此,破解过程结束
    """

    这里说明一下,为什么加密内容转Unicode之后不能直接用的原因,因为加密内容的Unicode和ttf文件中的Unicode不对应

     可以看到在ttf文件中万字的编码为uni2F00,而在Unicode在线编码中 为u4e07,uni 和 u 可忽略,直接看后四位,后面代码中有做转换

    获取文章加密内容,及将每个字符转为Unicode编码
    def get_source_article():
        """获取加密文章内容"""
        url = 'http://www.qiwen007.com/mb-db/pc-sg/zbwz/363609.html'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36',
        }
        resp = requests.get(url=url, headers=headers)
    
        html = etree.HTML(resp.text)
    
        p_list = html.xpath('//div[@class="article"]/p')
        for p in p_list:
            temp_article = p.xpath('./text()')
            if len(temp_article) > 0:
                print('temp_article', temp_article)
                result = to_unicode(temp_article[0])
                print('result', result)
                break
    
    
    def to_unicode(temp_article):
        """将汉字全部转为Unicode编码"""
        bytes_article = temp_article.encode(encoding='unicode-escape')
        str_article_li = str(bytes_article)[2:-1].split('\')
        uni_article_li = ['uni' + uni[1:].upper() for uni in str_article_li if uni != '']
    print(uni_article_li)
    
    

    将ttf文件或者woff文件转为xml文件

    # 将ttf/woff文件转换为xml文件(ttf/woff文件pycharm打开是乱码,xml可以打开)
    from fontTools.ttLib import TTFont
    font = TTFont('hansansjm.ttf')
    # 此时就将ttf/woff文件转换为了xml文件
    font.saveXML('hansansjm.xml')

    坑:TTGlyph name的顺序 有可能和ttf文件里面的字符的Unicode顺序对应不上

     

    此时就要找 GlyphOrder 中的name 

    组成 所有汉字和坐标差的映射,和 Unicode和坐标差的映射

    # 此汉字列表,需要通过百度字体编辑器(在线)打开ttf/woff文件,或者FontCreator,将全部汉字按照顺序写到列表中
    hans_list = ['', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '穿', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '西', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '',
                 ]
    
    
    def get_coordinate_hans_and__unicode_coordinate_map():
        """
        获取所有汉字和坐标差的映射,和 Unicode和坐标差的映射,
        接下来便可以通过映射找到Unicode所对应的坐标差,
        拿到坐标差之后,就可以再通过coordinate_hans_map,找到具体的汉字
        """
        # 存储该hansansjm.xml文件中所有的字符坐标差
        coordinate_diff_list = []
    
        # Unicode和坐标差的映射
        _unicode_coordinate_map = {}
    
        print('>>> 开始计算xml文件中所有字符的坐标差')
        content = parse('hansansjm.xml')
        # 此处有个坑,应当先获取GlyphOrder中的GlyphID对应的name即Unicode,这里的Unicode是和ttf文件中一一对应的
        # 而glyf中的TTGlyph对应的name(Unicode)不是和ttf文件中一一对应的
        # 应该拿到GlyphOrder 下面所有的Unicode编码后,再去 glyf中根据Unicode找对应的坐标
        GlyphID_list = content.getElementsByTagName('GlyphID')
        TTGlyph_list = content.getElementsByTagName('TTGlyph')
        # 由于xml文件中glyf下面的第一个TTGlyph 所对应的Unicode为.notdef ,要剔除掉,因此TTGlyph_list不能包含第一个元素
        # 注意:如果最后一个元素也不是Unicode编码的,应当也要剔除掉
    
        # 获取GlyphID下面所有的Unicode编码
        GlyphID_uni_list = []
        for GlyphID in GlyphID_list[1:]:
            # 获取Unicode
            _unicode = GlyphID.getAttribute('name')
            GlyphID_uni_list.append(_unicode)
        for uni in GlyphID_uni_list:
            for TTGlyph in TTGlyph_list[1:]:
                if uni == TTGlyph.getAttribute('name'):
                    # 获取第一个contour
                    # print(TTGlyph.getElementsByTagName('contour')[0])
                    first_contour = TTGlyph.getElementsByTagName('contour')[0]
    
                    # 获取第一个contour中的前两个pt元素,进一步获取这两个元素的x,y属性,便于计算坐标差
                    first_pt = first_contour.getElementsByTagName('pt')[0]
                    first_pt_x = int(first_pt.getAttribute('x'))
                    first_pt_y = int(first_pt.getAttribute('y'))
                    # print(first_pt_x, first_pt_y)
    
                    second_pt = first_contour.getElementsByTagName('pt')[1]
                    second_pt_x = int(second_pt.getAttribute('x'))
                    second_pt_y = int(second_pt.getAttribute('y'))
                    # print(second_pt_x, second_pt_y)
    
                    # 计算坐标差
                    coordinate_diff = (second_pt_x - first_pt_x, second_pt_y - first_pt_y)
                    # print(coordinate_diff)
                    coordinate_diff_list.append(coordinate_diff)
                    # break
                    _unicode_coordinate_map[uni] = coordinate_diff
    
                # print(coordinate_diff_list)
    
        # 将坐标差和汉字组成字典,完成映射
        coordinate_hans_map = dict(zip(coordinate_diff_list, hans_list))
        print(coordinate_hans_map)
        print(_unicode_coordinate_map)
        return coordinate_hans_map, _unicode_coordinate_map

    完整代码如下:

    import requests
    from lxml import etree
    from fontTools.ttLib import TTFont
    from xml.dom.minidom import parse
    
    """
    此案例是破解css反爬 网站:http://www.qiwen007.com
    经过查看网站页面源码后发现,tff文字库是 '/hansansjm.ttf',因此判定该网站的文字库不会自动变换
    
    woff/ttf文件样式查看(在线) http://fontstore.baidu.com/static/editor/index.html
    也可以使用FontCreator(下载地址) https://www.onlinedown.net/soft/88758.htm
    """
    
    """
    流程及思路:
    1. 通过requests请求获取响应数据,得到的就是源码中加密数据(杂乱的文章)
    2. 将加密数据的每一个字符转成Unicode编码,得到字符的Unicode列表
    3. 通过搜索源码中font-face关键字,找到字体库文件(ttf/woff文件)将当前域名拼接上/hansansjm.ttf,即 http://www.qiwen007.com/hansansjm.ttf,打开后即下载
    4. 使用FontCreator或者百度字体编辑器打开ttf文件,会看到里面每个字符
    5. 将ttf转为xml文件
    6. 同时使用pycharm打开xml文件,找到 glyf 标签下面的 TTGlyph 标签,里面的name 即是xml文件中的Unicode编码,
       里面contour下面的pt的x,y即是每个字符的坐标,计算坐标差(计算坐标差的时候,可以直接取第一个contour的前两组pt即可)
     (但是 TTGlyph name的顺序 有可能和ttf文件里面的字符的Unicode顺序对应不上,此时就要从GlyphOrder里面看是否对应,
      如果对应,就从GlyphOrder中获取每个xml文件中的每个字符的Unicode列表,然后遍历列表,从 glyf 中找到对应的坐标)
    7. 手动去组成汉字字符的列表,然后使用映射(dict(zip()))得到汉字和坐标差的映射
    8. 在第6步中我们可以获取Unicode和坐标差的映射
    9. 然后回到第2步,拿到加密数据的Unicode编码列表后,去Unicode和坐标差的映射中找到对应的坐标差,拿到坐标差后找到对应的汉字
    至此,破解过程结束
    """
    
    # 此汉字列表,需要通过百度字体编辑器(在线)打开ttf/woff文件,或者FontCreator,将全部汉字按照顺序写到列表中
    hans_list = ['', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '穿', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '西', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '', '',
                 '', '', '', '', '', '', '', '', '', '', '', '', '',
                 ]
    
    
    def get_result_article():
        """获取加密文章内容"""
        url = 'http://www.qiwen007.com/mb-db/pc-sg/zbwz/363609.html'
        headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36',
        }
        resp = requests.get(url=url, headers=headers)
    
        html = etree.HTML(resp.text)
    
        p_list = html.xpath('//div[@class="article"]/p')
        for p in p_list:
            temp_article = p.xpath('./text()')
            if len(temp_article) > 0:
                print('temp_article', temp_article)
                result = to_unicode(temp_article[0])
                print('result', result)
                break
    
    
    def to_unicode(temp_article):
        """将汉字全部转为Unicode编码"""
        bytes_article = temp_article.encode(encoding='unicode-escape')
        str_article_li = str(bytes_article)[2:-1].split('\')
        uni_article_li = ['uni' + uni[1:].upper() for uni in str_article_li if uni != '']
        # print(uni_article_li)
        return parse_uni(uni_article_li)
    
    
    def parse_uni(uni_article_li):
        # 获取坐标差汉字映射,和 Unicode 坐标差的映射
        get_map = get_coordinate_hans_and__unicode_coordinate_map
        coordinate_hans_map, _unicode_coordinate_map = get_map()
        result_article_content = ''
        for uni in uni_article_li:
            if uni in list(_unicode_coordinate_map.keys()):
                coordinate = _unicode_coordinate_map.get(uni)
                hans = coordinate_hans_map.get(coordinate)
                # print('1', hans)
                result_article_content += hans
            else:
                hans = chr(int(uni[3:], 16))
                # print('2', hans)
                result_article_content += hans
    
        return result_article_content
    
    
    # 将ttf/woff文件转换为xml文件(ttf/woff文件pycharm打开是乱码,xml可以打开)
    def ttf_to_xml():
        font = TTFont('hansansjm.ttf')
        # 此时就将ttf/woff文件转换为了xml文件
        font.saveXML('hansansjm.xml')
    
    
    def get_coordinate_hans_and__unicode_coordinate_map():
        """
        获取所有汉字和坐标差的映射,和 Unicode和坐标差的映射,
        接下来便可以通过映射找到Unicode所对应的坐标差,
        拿到坐标差之后,就可以再通过coordinate_hans_map,找到具体的汉字
        """
        # 存储该hansansjm.xml文件中所有的字符坐标差
        coordinate_diff_list = []
    
        # Unicode和坐标差的映射
        _unicode_coordinate_map = {}
    
        print('>>> 开始计算xml文件中所有字符的坐标差')
        content = parse('hansansjm.xml')
        # 此处有个坑,应当先获取GlyphOrder中的GlyphID对应的name即Unicode,这里的Unicode是和ttf文件中一一对应的
        # 而glyf中的TTGlyph对应的name(Unicode)不是和ttf文件中一一对应的
        # 应该拿到GlyphOrder 下面所有的Unicode编码后,再去 glyf中根据Unicode找对应的坐标
        GlyphID_list = content.getElementsByTagName('GlyphID')
        TTGlyph_list = content.getElementsByTagName('TTGlyph')
        # 由于xml文件中glyf下面的第一个TTGlyph 所对应的Unicode为.notdef ,要剔除掉,因此TTGlyph_list不能包含第一个元素
        # 注意:如果最后一个元素也不是Unicode编码的,应当也要剔除掉
    
        # 获取GlyphID下面所有的Unicode编码
        GlyphID_uni_list = []
        for GlyphID in GlyphID_list[1:]:
            # 获取Unicode
            _unicode = GlyphID.getAttribute('name')
            GlyphID_uni_list.append(_unicode)
        for uni in GlyphID_uni_list:
            for TTGlyph in TTGlyph_list[1:]:
                if uni == TTGlyph.getAttribute('name'):
                    # 获取第一个contour
                    # print(TTGlyph.getElementsByTagName('contour')[0])
                    first_contour = TTGlyph.getElementsByTagName('contour')[0]
    
                    # 获取第一个contour中的前两个pt元素,进一步获取这两个元素的x,y属性,便于计算坐标差
                    first_pt = first_contour.getElementsByTagName('pt')[0]
                    first_pt_x = int(first_pt.getAttribute('x'))
                    first_pt_y = int(first_pt.getAttribute('y'))
                    # print(first_pt_x, first_pt_y)
    
                    second_pt = first_contour.getElementsByTagName('pt')[1]
                    second_pt_x = int(second_pt.getAttribute('x'))
                    second_pt_y = int(second_pt.getAttribute('y'))
                    # print(second_pt_x, second_pt_y)
    
                    # 计算坐标差
                    coordinate_diff = (second_pt_x - first_pt_x, second_pt_y - first_pt_y)
                    # print(coordinate_diff)
                    coordinate_diff_list.append(coordinate_diff)
                    # break
                    _unicode_coordinate_map[uni] = coordinate_diff
    
                # print(coordinate_diff_list)
    
        # 将坐标差和汉字组成字典,完成映射
        coordinate_hans_map = dict(zip(coordinate_diff_list, hans_list))
        print(coordinate_hans_map)
        print(_unicode_coordinate_map)
        return coordinate_hans_map, _unicode_coordinate_map
    
    
    # ttf_to_xml()
    get_result_article()

    最后说明:在我测试的时候,发现最终结果还是有一些问题,通过解密,数据还是和页面上显示的不太一样,个别字符还是不对,搞不懂问题出在哪里,欢迎大佬们指正

  • 相关阅读:
    ICONS-图标库
    图形资源
    vue项目中,如果修改了组件名称,vscode编辑器会在引入修改组件的名字处提示红色波浪线 The file is in the program because:Imported via xxx Root file specified for compilation .
    接口在dev环境报跨域问题(has been blocked by CORS policy:Response to preflight request doesn't pass access control check:No 'Access-Control-Allow-Origin' header ispresent on the requested resource.),qa环境正常
    阿里云occ的图片文件URL用浏览器直接打开无法访问,提示This XML file does noe appear to have any style information associated with it. The document tree is shown below.
    vue 项目使用element ui 中tree组件 check-strictly 用法(父子不互相关联的反显情况)
    高德地图进行线路规划绘制标记点操作(vue)
    vue中实现拖拽调整顺序功能
    2021-01-22 浏览器相关知识
    2021-01-22 js 相关知识点
  • 原文地址:https://www.cnblogs.com/lishuaijiang/p/13381093.html
Copyright © 2020-2023  润新知