• 网页→字体文件的base64码
  • base64码→字体文件
  • 字体文件→字符、字形图像
  • 字形图像→明文
  • 字符→明文

从网页获取字体文件的base64码

通过观察可知,“某通”有关于字体混淆加密的字体样式具有独特性(ID=”cxSecretStyle”)。因此,我们可以通过 CSS选择器 轻松定位此元素(CSS_SELECTOR=”#cxSecretStyle”),在获取它的 textContent 属性之后,再通过 “正则表达式” 来提取它的 base64码 。

Python
    def get_base64_selenium(selector):                           # 获取指定style标签中的base64值
       
       # 定位指定的<style>标签
        try: style_element = driver.find_element(By.CSS_SELECTOR, selector)
        except NoSuchElementException: return None
        style_content = style_element.get_attribute("textContent")

        # 提取Base64值(增强正则)
        base64_pattern = r"base64,([A-Za-z0-9+/=\s]+?)(?=['\";)])"
        matches = re.findall(base64_pattern, style_content, re.DOTALL)
        if matches:
        # 清理空白字符并选择最长的结果
            clean_base64 = max([m.replace(" ", "").replace("\n", "") for m in matches], key=len)
            return clean_base64
        return None
        
    @contextmanager                                                      # 构造上下文管理器
    def return_base64(selector):                                    # 切换到目标frame,调用 get_base64_via_selenium 获取ttf文件的base64值
        frame1 = driver.find_element(By.ID,'iframe')
        driver.switch_to.frame(frame1)
        frame2 = driver.find_element(By.CSS_SELECTOR,'#ext-gen1049 > div.wrap > div > p > div > iframe')
        driver.switch_to.frame(frame2)
        frame3 = driver.find_element(By.ID,'frame_content')
       driver.switch_to.frame(frame3)
        try:
            yield get_base64_selenium(selector)               # "通过ID定位构造CSS选择器"
        finally:
            driver.switch_to.default_content()                    #  最终切换回默认主框架
            

接下来我们要通过 base64码 生成相关的字体文件。

Python
import base64

def ttf_get(base64_string,opt_path):
    bytes_data = base64.b64decode(base64_string)                      # 从 base64字符串 中解码出二进制数据
    with open( opt_path , 'wb' ) as ttf_file:                                         # 以二进制写入模式打开 opt_path路径 指向的文件
        ttf_file.write(bytes_data)

接下来我们通过字体文件渲染出其包含的各个字符的 “字形图像” 。(此图像为 “明文图像” 但字符却是密文字符,相当于输入“密文字符”却能显示出“明文字符的字形”)

Python
import os
from PIL import ImageFont,Image,ImageDraw
from fontTools.ttLib import TTFont


def image_get(character,font_path,font_size=32,pad=10):	# 根据字体文件渲染出指定字符的图像
	font = ImageFont.truetype(font_path,font_size)					# 加载 truetype font,即 ttf 文件
	left,top,right,bottom = font.getbbox(character)					# 获取相应字符在字体文件中的字形坐标信息
	char_width = int(right - left)
	char_height = int(bottom - top)
	canvas_width = char_width + pad*2
	canvas_height = char_height + pad*2
	canvas = Image.new('RGB',(canvas_width,canvas_height),color='white') # new一个 RGB 通道的指定长宽的纯白“画布”
	draw = ImageDraw.Draw(canvas)														# 创建一个与“画布”绑定的“画笔”对象
	draw.text((pad-left,pad-top),character,font=font,fill='black') 		# 在画布上绘画出一个字形使其位于“正中心”,填充颜色为黑色
	return canvas # 返回“画布”

def plaintext_image_get(ttf_path,font_size=32,pad=10,opt='./font'):
	os.makedirs(opt)
	font = TTFont(ttf_path)			# 创建ttf字体对象
	cmap = font.getBestCmap()  # 获取判断最优的"字符字形映射表"
	unicodes = cmap.keys()         # 获取字典中的所有“键”	,这里对应“字符”的Unicode码点
	for unicode in unicodes:
		char = chr(unicode)				# 根据 Unicode码点 获得对应的字符
		img = image_get(char,ttf_path,font_size,pad) # 获取字形图像
		filepath = os.path.join(opt,f'{char}.png')
		img.save(filepath)
		

至于其中,为什么在绘画字形的时候,”draw.text()” 的 “xy” 参数,即 “字形原点” 要指定为 “(pad-left,pad-top)” 以使得字形位于“画布”中心呢?其实这是经过了相对严密的计算得来的结果。感兴趣的可以点开下面的链接,听我细细道来。

有关于 PIL 的字形坐标系的部分分析

我们现在需要通过字形图像获取”密文字符“所对应的明文,很简单还是要使用 OCR ,但是我们只需要对这几个 ”字符“ 单独使用 OCR ,然后建立一个 ”密文到明文的映射表“ ,我们就可以直接 ”爬取“ 网页的密文字符,然后利用映射表将其中的密文映射到明文,最终获得明文字符段。

OCR 市面上有很多相应的工具可以选择,我当时使用的主要是 PaddleOCR 。

值的一提的是, PaddleOCR 对中文数字一到十的单独识别效果不是很好,经常识别不出来,所以我之后又使用 pytorch 的VGG16网络自己训练了一个专门识别中文数字的OCR,但PaddleOCR与pytorch不兼容,所以我又将PaddleOCR进行了单独打包,然后通过Subprocess对其作为子进程调用……

这里不再赘述 OCR 部分,大家可以自行选择写一个 OCR 函数,用以传入图片并返回识别出的字符 。

Python
import json
import os

def plaintext_ciphertext_json(imgs_dir):
	ciphertext_plaintext = {}
	filenames = os.listdir(imgs_dir)
	for filename in filenames:
		ciphertext = filename.split('.' , 1)	# 因为我当时直接把密文字符设置为文件名了,就很方便
		img_path = os.path.join(imgs_dir,filename)
		plaintext = ocr(img_path)              # 这里假设有一个已经写好了的 ocr  函数
		ciphertext_plaintext[ciphertext] = plaintext or 'OCR未能正常识别成功'
	os.mkdir(./result,exist_ok=True) # 这里以当前目录下的 result 文件夹为储存文件夹创建,存在容忍
	with open('./result/decode.json','w',encoding = 'utf-8') as f:
		json.dump(ciphertext_plaintext,f,ensure_ascii=False,indent=4) # 将字典以 json 格式保存,非确保为ASCII以容忍Unicode中的非ASCII字符,设置缩进indent为4以求美观 

OK,现在万事俱备,只差一个”解密函数“了!

Python
import json



def decode(ciphertext,json_path='./result/decode.json'):
	with open(json_path,'r',encoding='utf-8')as f:
		ciphertext_plaintext = json.load(f)
	# 从密文中遍历字符,如果字符存在于映射表,则将其映射为明文字符,否则保持原字符。
	plaintext = [ciphertext_plaintext[char] for char in ciphertext if char in ciphertext_plaintext else char]
	return ''.join(plaintext) # 将明文字符连接并返回,因为是中文字符所以不需要空格分隔,英文字符可以使用空格分隔连接
		

完工!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

作者

3049874370@qq.com

相关文章

ssh的密钥认证以及通道保持

密钥验证登录 以下讨论皆以密钥在本地主机、公...

读出全部

Squid代理服务的搭建

前言 所谓的HTTP代理或者HTTPS代理指...

读出全部