【Python】利用 Emoji 隐藏数据

背景介绍

Emoji 突变「U盘」?表情符号被曝能“走私”数据,程序员亲测:真的可以!-CSDN博客

Unicode使用代码点(Code Point)序列表示文本,每个代码点对应一个特定字符。通常表示为U+XXXXXXXX格式的十六进制数。对于拉丁字母等简单字符,代码点与显示字符一一对应。但在复杂书写系统中,单个显示字符可能由多个代码点组合而成。

Unicode定义了256个特殊代码点作为变体选择器(Variation Selectors,VS),范围从VS-1(U+FE00)到VS-256(U+E01EF)。它们不直接显示,而是修改前一个字符的样式。例如,g(U+0067)后跟VS-2(U+FE01)仍显示为g,但复制时会包含隐藏的选择器。

由于256个选择器正好对应一个字节(0-255),这提供了一种在Unicode文本中隐藏任意字节数据的方法。

编码实现

字节转变体选择器

def byte_to_variation_selector(byte: int) -> str:
    if byte < 16:
        return chr(0xFE00 + byte)
    else:
        return chr(0xE0100 + (byte - 16))

该函数将字节值映射到对应的变体选择器字符。前16个字节(0-15)对应U+FE00U+FE0F,后续字节(16-255)对应U+E0100U+E01EF

数据编码

def encode(base: str, data: bytes) -> str:
    encoded = [base]
    for byte in data:
        encoded.append(byte_to_variation_selector(byte))
    return ''.join(encoded)

将基础字符(如😊)与所有字节转换后的变体选择器拼接,生成最终字符串。例如:

data = b'hello'  # 字节值 [0x68, 0x65, 0x6c, 0x6c, 0x6f]
encoded = encode('😊', data)
print(encoded)  # 输出:😊󠅘󠅕󠅜󠅜󠅟
print(repr(encoded))  # 输出:'😊\U000e0158\U000e0155\U000e015c\U000e015c\U000e015f'

解码实现

变体选择器转字节

def variation_selector_to_byte(c: str) -> int | None:
    cp = ord(c)
    if 0xFE00 <= cp <= 0xFE0F:
        return cp - 0xFE00
    elif 0xE0100 <= cp <= 0xE01EF:
        return (cp - 0xE0100) + 16
    else:
        return None

通过判断字符的Unicode码点,反向解析出原始字节值。

数据解码

def decode(encoded: str) -> bytes:
    result = []
    for c in encoded:
        byte = variation_selector_to_byte(c)
        if byte is not None:
            result.append(byte)
        elif result:  # 遇到非变体选择符且已开始解码,终止
            break
    return bytes(result)

遍历字符串,跳过基础字符后的非变体选择符,遇到首个变体选择符开始解析,直至再次遇到无效字符停止。

decoded = decode(encoded)
print(decoded.decode('utf-8'))  # 输出:hello

面向对象封装

class UnicodeDataHider:
    def __init__(self, base: str):
        """
        初始化时设置基础字符,后续将基于该字符进行编码。
        :param base: 基础字符,例如 '😊'
        """
        self.base = base

    @staticmethod
    def char_to_variation_selector(char: str) -> str:
        """
        将字符转换为对应的Unicode变体选择器字符。
        :param char: 需要转换的字符
        :return: 对应的Unicode变体选择器字符
        """
        byte = ord(char)  # 获取字符的Unicode码点(字节值)
        if byte < 16:
            return chr(0xFE00 + byte)
        else:
            return chr(0xE0100 + (byte - 16))

    def encode(self, data: str) -> str:
        """
        将字符串编码为隐藏在基础字符后的Unicode变体选择器字符串。
        :param data: 需要编码的数据(普通字符串)
        :return: 编码后的字符串
        """
        encoded = [self.base]
        for char in data:
            encoded.append(self.char_to_variation_selector(char))
        return ''.join(encoded)

    @staticmethod
    def variation_selector_to_char(c: str) -> str | None:
        """
        将Unicode变体选择器字符反向转换为原始字符。
        :param c: 变体选择器字符
        :return: 对应的原始字符,或None(无效字符)
        """
        cp = ord(c)
        if 0xFE00 <= cp <= 0xFE0F:
            return chr(cp - 0xFE00)
        elif 0xE0100 <= cp <= 0xE01EF:
            return chr((cp - 0xE0100) + 16)
        else:
            return None

    def decode(self, encoded: str) -> str:
        """
        解码隐藏在Unicode变体选择器中的字符串数据。
        :param encoded: 编码后的字符串
        :return: 解码后的原始字符串
        """
        result = []
        for c in encoded:
            char = self.variation_selector_to_char(c)
            if char is not None:
                result.append(char)
            elif result:  # 遇到非变体选择符且已开始解码,终止
                break
        return ''.join(result)


# 使用示例
if __name__ == '__main__':
    base_char = '😊'
    data = 'hello'  # 普通字符串

    # 创建UnicodeDataHider实例
    hider = UnicodeDataHider(base_char)

    # 编码数据
    encoded = hider.encode(data)
    print(f'Encoded: {repr(encoded)}')

    # 解码数据
    decoded = hider.decode(encoded)
    print(f'Decoded: {decoded}')

应用场景与注意事项

  • 隐蔽传输:可在看似正常的文本中隐藏元数据或水印。
  • 兼容性:需确保处理程序保留变体选择器,部分环境可能丢弃不可识别字符。
  • 显示差异:某些渲染器可能忽略变体选择器,导致隐藏数据丢失。

通过巧妙利用Unicode特性,我们实现了数据的隐蔽存储与传输。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值