发表于 2019-7-31 12:12

申请会员ID: randompath【申请通过】

1、申 请 I D :randompath
2、个人邮箱:manwu91@gmail.com
3、原创技术文章:这是之前在freebuf投稿的两篇文章
关于行为验证分析的文章:http://www.freebuf.com/web/140693.html
关于hook微信的文章: http://www.freebuf.com/web/156944.html
个人首页:




Hmily 发表于 2019-7-31 16:02

请看好申请规则,不要只发一个url,把内容发布于论坛审核。

发表于 2019-8-1 09:50

Hmily 发表于 2019-7-31 16:02
请看好申请规则,不要只发一个url,把内容发布于论坛审核。

好的,那两篇文章是两年前的了,这里贴个去年写的关于破解字体加密的。这篇文章之前发布在我的博客上 http://www.wisedream.net/2018/10/10/spider/crack-qxb-font/

在抓取某信宝数据时,发现有几个字段的值与真实值不符,分析发现源码中由class qxb-num修饰的标签数据都是错乱的。
![由qxb-num修饰的标签](http://www.wisedream.net/res/img/spider/qxb_font1.png)
查看qxb-num样式,发现特殊字体
![](http://www.wisedream.net/res/img/spider/qxb_font2.png)
由此断定开发人员是在字体库上动的手脚。

## 字体的绘制与ttf
在查阅[相关文档](https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html)后,总结字体的绘制过程为:
1. 根据字符的unicode编码找到glyph名称
2. 根据glyph名称找到glyph
3. 使用glyph进行绘制

A TrueType font file consists of a sequence of concatenated tables. A table is a sequence of words. Each table must be long aligned and padded with zeroes if necessary.

一个TrueType Font字体库包含几个table。这里需要用到的两个table如下(tag为table的名称)

| tag | table |
| --- | --- |
| cmap        | character to glyph mapping |
| glyf | glyph data |

## 破解过程

根据字体的绘制过程,可以猜测有两种方式实现字体加密
1. 打乱字符编码与glyph映射(即cmap table)
2. 打乱glyph名称与glyph数据(即glyf table)

利用fonttools,使用如下代码将字体转为xml
```py
from fontTools.ttLib import TTFont
from io import BytesIO
import requests

font_content = requests.get('https://cache.qixin.com/pcweb/font-awesome-qxb-1bd55e43.woff2').content
font_file = BytesIO(font_content)
font = TTFont(font_file)
font.saveXML('font.xml')
```

查看生成的xml文件,发现cmap节点部分数据
```
      <map code="0x30" name="icon-number_9"/><!-- DIGIT ZERO -->
      <map code="0x31" name="icon-number_3"/><!-- DIGIT ONE -->
      <map code="0x32" name="icon-number_7"/><!-- DIGIT TWO -->
      <map code="0x33" name="icon-number_2"/><!-- DIGIT THREE -->
      <map code="0x34" name="icon-number_0"/><!-- DIGIT FOUR -->
      <map code="0x35" name="icon-number_1"/><!-- DIGIT FIVE -->
      <map code="0x36" name="icon-number_5"/><!-- DIGIT SIX -->
      <map code="0x37" name="icon-number_8"/><!-- DIGIT SEVEN -->
      <map code="0x38" name="icon-number_6"/><!-- DIGIT EIGHT -->
      <map code="0x39" name="icon-number_4"/><!-- DIGIT NINE -->
      <map code="0x41" name="icon-upper_S"/><!-- LATIN CAPITAL LETTER A -->
      <map code="0x42" name="icon-upper_Q"/><!-- LATIN CAPITAL LETTER B -->
      <map code="0x43" name="icon-upper_K"/><!-- LATIN CAPITAL LETTER C -->
```
由此可以断定这个字体库通过打乱cmap table实现实体加密。

## 破解
对于打乱的cmap,只要找到字符对应的glyph名称就可以了
```
"""
企信宝字体加密: cmap table应对方案
"""

def decrypt(ss, font_file):
    """
    根据字体文件解密字符串
    :param ss: str or list of str
    :param font_file: file like object or file path
    :return:
    """
    with TTFont(font_file) as font:
      cmap = font['cmap'].getBestCmap()

    def _decrypt(s):
      predict = ''
      for c in s:
            if c in PLAIN_CHARS:
                predict += cmap[-1]
            else:
                predict += c
      return predict

    if isinstance(ss, str):
      return _decrypt(ss)
    else:
      return
```

对于打乱的glyf,先标记glyph数据与真实字符,之后通过比对glyph数据找到对应的真实字符就可以了。
```

"""
企信宝字体加密: glyf table应对方案
"""

from fontTools.ttLib import TTFont
import string


def _get_glyph_name(c: str, font):
    return font.getBestCmap()


PLAIN_BOOK = string.ascii_uppercase + string.ascii_lowercase + '1234567890'


def _load_refer_glyph_data():
    """
    加载已知宋体库,取得真实的glyphdata与name字典
    :return: {glyphdata->bytes: char_name}
    """
    import os.path
    font_file = os.path.join(os.path.dirname(__file__), 'font-awesome-qxb-5ffe2d46.woff2')
    with TTFont(font_file) as font:
      cipherbook = 'XSQRTWFCZHDIN' \
                     'LAKEUGBMPOVJY' \
                     'rpfcbtdnajuhg' \
                     'zyikxovqleswm' \
                     '7658419203'
      glyphset = font['glyf'].glyphs
      return {glyphset.data: c for p, c in zip(PLAIN_BOOK, cipherbook)}


refer_glyph_data = _load_refer_glyph_data()


def decrypt(ss, font_file):
    """
    根据字体文件解密字符串
    :param ss: str or list of str
    :param font_file: file like object or file path
    :return:
    """
    font = TTFont(font_file)
    glyphs = font['glyf'].glyphs

    def _decrypt(s):
      predict = ''
      for c in s:
            if c in PLAIN_BOOK:
                glyph_data = glyphs.data
                predict += refer_glyph_data
            else:
                predict += c
      return predict

    if isinstance(ss, str):
      return _decrypt(ss)
    else:
      return


def _test():
    import os
    cipher = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'
    plain = 'XSQRTWFCZHDINLAKEUGBMPOVJYrpfcbtdnajuhgzyikxovqleswm7658419203'
    predict = decrypt(cipher, os.path.join(os.path.dirname(__file__), 'font-awesome-qxb-5ffe2d46.woff2'))
    print(predict)
    print(predict == plain)


if __name__ == '__main__':
    _test()

```

Hmily 发表于 2019-8-1 14:31

I D:randompath
邮箱:manwu91@gmail.com

申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。

randompath 发表于 2019-8-1 15:35

randompath报到
页: [1]
查看完整版本: 申请会员ID: randompath【申请通过】