数字转字体加解密,字体转数字解密,mutationobserver监听dom变化

将以下代码放入到油猴,新建脚本,保存。也可以F12,放入控制台执行,但是可能会没有效果,除非是一直在变的dom。

//将需要监听的dom selector 替换掉 span.title-follownum 
new MutationObserver((mutations, observer) => {
  const el = document.querySelector("span.Title-followNum")
  if (el != null) {
    observer.disconnect()
    new MutationObserver((mutations, observer) => {
      debugger
    }).observe(el, {childList: true, subtree: true})
  }
}).observe(document, {childList: true, subtree: true})



二、前端可能使用的加密算法

base64、rot13 


三、sdl渲染字体到图片

#include <stdio.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_image.h>

int
main(void)
{
  if(TTF_Init() == -1) {
    printf("error: %s\n", TTF_GetError());
    return 1;
  }

  TTF_Font *font = TTF_OpenFont("test.woff", 50);
  if(font == NULL) {
    printf("error: %s\n", TTF_GetError());
    return 1;
  }

  SDL_Color black = { 0x00, 0x00, 0x00 };
  SDL_Surface *surface = TTF_RenderText_Solid(font, "0123456789", black);
  if(surface == NULL) {
    printf("error: %s\n", TTF_GetError());
    return 1;
  }

  IMG_SavePNG(surface, "test.png");
  return 0;
}


四、将数字加密为字体

常见的字体格式有 ttfotfwoff。其中 woff 是一个包装格式,里面的字体不是 ttf 就是 otf 的,所以真正的存储格式只有两种,ttf 和 otf

这两种格式很明显都是二进制格式,没法直接打开看。但是,幸运的是,字体有一个格式叫做 ttx,是一个 XML 的可读格式。

我们的基本思路是:

  • 裁剪字体:根据一个基础字体裁剪掉我们不需要的字符,比如斗鱼这种情况,我们只需要数字即可

  • 将字体转换成 ttx 格式打开

  • 找到字符和图形的映射

  • 修改这个映射

  • 再导出字体为 ttf

这里我们使用 fonttools 这个强大的 Python 库来进行后续的操作。


4.1裁剪字体

$ pyftsubset hack.ttf --text="0123456789" 
WARNING: TTFA NOT subset; don't know how to subset; dropped

上面的 warning 不用介意,运行完毕之后我们得到了 hack.subset.ttf,这个便是裁剪后的字体,只支持渲染 0 ~ 9。


4.2转换字体为可读的 ttx 格式

$ ttx hack.subset.ttf
Dumping "hack.subset.ttf" to "hack.subset.ttx"...
Dumping 'GlyphOrder' table...
Dumping 'head' table...
Dumping 'hhea' table...
Dumping 'maxp' table...
Dumping 'OS/2' table...
Dumping 'hmtx' table...
Dumping 'cmap' table...
Dumping 'fpgm' table...
Dumping 'prep' table...
Dumping 'cvt ' table...
Dumping 'loca' table...
Dumping 'glyf' table...
Dumping 'name' table...
Dumping 'post' table...
Dumping 'gasp' table...
Dumping 'GSUB' table...


4.3

我们会发现目录下多了一个 hack.subset.ttx 文件,打开观察一下。

很容易就可以发现,cmap 标签中定义了字符和图形的映射。

<cmap>
  <tableVersion version="0"/>
  <cmap_format_4 platformID="0" platEncID="3" language="0">
    <map code="0x30" name="zero"/><!-- DIGIT ZERO -->
    <map code="0x31" name="one"/><!-- DIGIT ONE -->
    <map code="0x32" name="two"/><!-- DIGIT TWO -->
    <map code="0x33" name="three"/><!-- DIGIT THREE -->
    <map code="0x34" name="four"/><!-- DIGIT FOUR -->
    <map code="0x35" name="five"/><!-- DIGIT FIVE -->
    <map code="0x36" name="six"/><!-- DIGIT SIX -->
    <map code="0x37" name="seven"/><!-- DIGIT SEVEN -->
    <map code="0x38" name="eight"/><!-- DIGIT EIGHT -->
    <map code="0x39" name="nine"/><!-- DIGIT NINE -->
  </cmap_format_4>
  ...
</cmap>

0x30 也就是字符 0 对应 name="zero" 的 TTGlyph,TTGlyph 中定义了渲染要用的数据,也就是一些坐标。

<TTGlyph name="zero" xMin="123" yMin="-29" xMax="1110" yMax="1520">
  <contour>
    <pt x="617" y="-29" on="1"/>
    <pt x="369" y="-29" on="0"/>
    <pt x="246" y="165" on="1"/>
    <pt x="123" y="358" on="0"/>
    <pt x="123" y="745" on="1"/>
    <pt x="123" y="1134" on="0"/>
    <pt x="246" y="1327" on="1"/>
    <pt x="369" y="1520" on="0"/>
    <pt x="616" y="1520" on="1"/>
    <pt x="864" y="1520" on="0"/>
    <pt x="987" y="1327" on="1"/>
    <pt x="1110" y="1134" on="0"/>
    <pt x="1110" y="745" on="1"/>
    <pt x="1110" y="-29" on="0"/>
  </contour>
  ...
</TTGlyph>

那么怎么制作混淆字体的方法就不言而喻了,我们修改一下这个 XML,把 TTGlyph(name="zero") 标签的 zero 换成 eight 然后把 TTGlyph(name="eight") 标签的 eight 换成 zero,保存文件为 fake.ttx

导出 ttx 到 ttf 依然是使用 ttx 工具。

$ ttx -o fake.ttf fake.ttx
Compiling "fake.ttx" to "fake.ttf"...
Parsing 'GlyphOrder' table...
Parsing 'head' table...
Parsing 'hhea' table...
Parsing 'maxp' table...
Parsing 'OS/2' table...
Parsing 'hmtx' table...
Parsing 'cmap' table...
Parsing 'fpgm' table...
Parsing 'prep' table...
Parsing 'cvt ' table...
Parsing 'loca' table...
Parsing 'glyf' table...
Parsing 'name' table...
Parsing 'post' table...
Parsing 'gasp' table...
Parsing 'GSUB' table...

使用上文提到的 HTML 使用 fake.ttf 渲染 0 ~ 9,可以看到,我们成功地制作了一个混淆字体。


附脚本

./genfont.py hack.subset.ttf 20
#!/usr/bin/env python
# 生成用于数字混淆的字体文件用于反爬
# 即字体对于数字的渲染是错误的,例如数字 1 会渲染成 5
# ./genfont.py <font-file> <count>
# 生成字体在 result/generated 目录中

import sys
import os
import subprocess
from pathlib import Path
import random
from bs4 import BeautifulSoup
import copy
import hashlib

names = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]

# must contain glyphs with name "zero" "one" .. "nine"
def check_font(ttx):
  for name in names:
    if ttx.find("TTGlyph", attrs={"name": name}) is None:
      return False

  return True

def gen(ttx):
  mapping = names[:]
  random.shuffle(mapping)

  target = copy.copy(ttx)

  for name in names:
    target.find("TTGlyph", {"name": name})["id"] = name

  for idx, name in enumerate(names):
    tmp = target.find("TTGlyph", attrs={"id": mapping[idx]})

    tmp.attrs = {}

    for k, v in ttx.find("TTGlyph", attrs={"name": name}).attrs.items():
      tmp[k] = v

  content = target.prettify()
  name = hashlib.md5(content.encode("utf8")).hexdigest()[:10] + "." + "".join([str(names.index(x)) for x in mapping])

  print(f"Generate temporary ttx: {name}.ttx")
  target_ttx_path = os.path.join("result", "tmp", f"{name}.ttx")
  with open(target_ttx_path, "w") as f:
    f.write(content)

  target_ttf_path = os.path.join("result", "generated", f"{name}.ttf")
  print(f"Generate target ttf: {target_ttf_path}")
  subprocess.run(f"ttx -o {target_ttf_path} {target_ttx_path}", shell=True, check=True)

def run(font_file, count):
  ttx_name = os.path.splitext(font_file)[0] + ".ttx"
  ttx_path = os.path.join("result", "tmp", ttx_name)

  if not Path(ttx_path).exists():
    print("Convert ttf to ttx..")
    subprocess.run(f"ttx -o {ttx_path} {font_file}", shell=True, check=True)

  with open(ttx_path) as f:
    ttx = BeautifulSoup(f, "xml")

    if not check_font(ttx):
      print("font must contain glyphs with name 'zero', 'one', 'two' .. 'nine'")
      exit(1)

    for _ in range(count):
      gen(ttx)

if __name__ == "__main__":
  if len(sys.argv) < 3:
    print(f"usage: ./genfont.py <font-file> <count>")
    exit(1)

  # create necessary dirs
  os.makedirs(os.path.join("result", "generated"), exist_ok=True)
  os.makedirs(os.path.join("result", "tmp"), exist_ok=True)

  run(sys.argv[1], int(sys.argv[2]))



原文

https://cjting.me/2020/07/01/douyu-crawler-and-font-anti-crawling/

打赏

看恩吧
网站不承担任何有关评论的责任
  • 最新评论
  • 总共条评论
取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦