注意:本示例僅供學習參考~
混淆原理
出於某種原因,明文資訊通過自定義字型進行渲染,達到混淆目的。
舉個例子:
網頁原始碼 <p>123</p>
在正常字型的渲染下,瀏覽者看到的是 123 這 3 個數字。
如果建立一種自定義字型,把 1 渲染成 5,那麼瀏覽者看到的便是 523 這 3 個數字。
這樣便達到混淆資訊的效果,常見於對付爬蟲之類的自動化工具。
破解方法
- 下載自定義字型檔案(通常在 css @font-face 中找到),儲存成 a.ttf 檔案。
-
安裝以下依賴專案
- tesseract 4.0 及以上版本,以及簡體中文(chi_sim)和英文(eng)資料檔案。
- python tesserocr 最新原始碼(github)版本。
- python fonttools 庫。
- python pillow 庫。
- 執行以下程式碼
# -*- coding: utf-8 -*-
本例採用 tesseract OCR 引擎,根據字型檔案自動生成密文與明文的字元對映表,實現解密功能。
@author: 李毅
```
from tesserocr import PyTessBaseAPI, PSM
from PIL import Image, ImageDraw, ImageFont
from fontTools.ttLib import TTFont
import string
class OCR(object):
default_config = {
# ocr engine
`data_path`: None,
`lang`: `chi_sim`,
`white_list`: None,
`black_list`: None,
# image
`font`: None,
`image_size`: (60, 60),
`font_size`: 30,
`text_offset`: (15, 15),
}
def __init__(self, config={}):
c = dict(self.default_config)
c.update(config)
self.api = PyTessBaseAPI(path=c[`data_path`], lang=c[`lang`], psm=PSM.SINGLE_CHAR)
self.img = Image.new(`RGB`, c[`image_size`], color=`white`)
self.draw = ImageDraw.Draw(self.img)
self.font = ImageFont.truetype(c[`font`], size=c[`font_size`])
self.text_offset = c[`text_offset`]
if c[`white_list`]:
self.api.SetVariable(`tessedit_char_whitelist`, c[`white_list`])
if c[`black_list`]:
self.api.SetVariable(`tessedit_char_blacklist`, c[`black_list`])
self.font_tool = TTFont(c[`font`])
self.empty_char = self._predict_empty_char()
def _predict_empty_char(self):
self.api.SetImage(self.img)
return self.api.GetUTF8Text().strip()
def is_char_in_font(self, char):
for t in self.font_tool[`cmap`].tables:
if t.isUnicode():
if ord(char) in t.cmap:
return True
return False
def predict(self, char):
``` 返回轉換後的字元,或空串`` ```
if not self.is_char_in_font(char):
return char # 若字型無法渲染該字元,則原樣返回。此處可酌情移除。
self.img.paste(`white`, (0, 0, self.img.size[0], self.img.size[1]))
self.draw.text(self.text_offset, char, fill=`black`, font=self.font)
self.api.SetImage(self.img)
c2 = self.api.GetUTF8Text().strip()
if c2 == self.empty_char:
return `` # 某些字元可能渲染成空白,此時返回空串。
return c2
class Decoder(object):
def __init__(self, data_path, font):
self.cache = {} # 快取已知的對映關係。
OCR.default_config.update(dict(data_path=data_path, font=font))
self.ocr_digit = OCR(dict(
lang=`eng`,
white_list=string.digits,
black_list=string.ascii_letters,
))
self.ocr_letter = OCR(dict(
lang=`eng`,
black_list=string.digits,
white_list=string.ascii_letters,
))
self.ocr_other = OCR()
def decode(self, char):
if char not in self.cache:
c2 = self._decode_when_cache_miss(char)
self.cache[char] = c2 or char
return self.cache[char]
def _decode_when_cache_miss(self, char):
ocr = self.ocr_other
if char in string.digits:
ocr = self.ocr_digit
elif char in string.ascii_letters:
ocr = self.ocr_letter
return ocr.predict(char)
if __name__ == `__main__`:
s = ```你好,青劃長務, 8175-13-79```
d = Decoder(`tessdata/`, `a.ttf`)
print(``.join(map(d.decode, s)))