CTF密码学综合教学指南--第二章

第二章:编码与基础变换

2.1 常见编码方式详解

2.1.1 Base64编码

原理: 将二进制数据每6位一组,映射到一个由64个可打印ASCII字符(A-Z, a-z, 0-9, +, /)组成的字符集中。若输入字节数不是3的倍数,则末尾用 = 填充。

编码示例: "flag" → "ZmxhZw=="

复制代码
import base64
import binascii

class Base64Tool:
    """
    CTF 专用 Base64 工具类
    支持:标准编解码、URL安全编解码、自动填充修复、文件处理
    """

    @staticmethod
    def encode(data, url_safe=False):
        """
        Base64 编码
        参数:
            data: 待编码的数据 (str 或 bytes)
            url_safe: 是否使用 URL 安全模式 (替换 + 为 -, / 为 _)
        返回:
            str: 编码后的字符串
        """
        # 统一转换为 bytes
        if isinstance(data, str):
            data = data.encode('utf-8')
        
        if url_safe:
            encoded = base64.urlsafe_b64encode(data)
        else:
            encoded = base64.b64encode(data)
            
        return encoded.decode('utf-8')

    @staticmethod
    def decode(data, url_safe=False, fix_padding=True):
        """
        Base64 解码
        参数:
            data: 待解码的数据 (str 或 bytes)
            url_safe: 是否使用 URL 安全模式
            fix_padding: 是否自动修复缺失的 '=' 填充符 (CTF 常见坑)
        返回:
            bytes: 解码后的原始字节
        """
        # 统一转换为字符串处理
        if isinstance(data, bytes):
            data = data.decode('utf-8')
        
        # 自动修复填充
        if fix_padding:
            # 计算缺失的等号数量
            missing_padding = len(data) % 4
            if missing_padding:
                data += '=' * (4 - missing_padding)
        
        try:
            if url_safe:
                return base64.urlsafe_b64decode(data)
            else:
                return base64.b64decode(data)
        except binascii.Error as e:
            return f"解码错误: {e}"

    @staticmethod
    def file_to_base64(file_path):
        """将文件转换为 Base64 字符串 (Misc 常用)"""
        with open(file_path, "rb") as f:
            return base64.b64encode(f.read()).decode('utf-8')

    @staticmethod
    def base64_to_file(b64_str, output_path):
        """将 Base64 字符串保存为文件"""
        # 尝试自动修复填充
        try:
            data = Base64Tool.decode(b64_str)
            with open(output_path, "wb") as f:
                f.write(data)
            print(f"文件已保存至: {output_path}")
        except Exception as e:
            print(f"保存失败: {e}")

# ==========================================
# 使用示例
# ==========================================
if __name__ == "__main__":
    tool = Base64Tool()
    
    # --- 场景 1: 标准编解码 (你的原始代码场景) ---
    print(f"--- 场景1: 标准模式 ---")
    plaintext = "flag{hello_crypto}"
    encoded = tool.encode(plaintext)
    decoded_bytes = tool.decode(encoded)
    print(f"原文: {plaintext}")
    print(f"编码: {encoded}")
    print(f"解码: {decoded_bytes.decode('utf-8')}")
    print()

    # --- 场景 2: URL 安全模式 (Web/JWT 常见) ---
    # 标准 Base64 包含 + 和 /,在 URL 中需要转义
    print(f"--- 场景2: URL 安全模式 ---")
    data_with_special = b"data+with/slash" # 包含 + 和 /
    standard_b64 = tool.encode(data_with_special)
    url_safe_b64 = tool.encode(data_with_special, url_safe=True)
    
    print(f"原始数据: {data_with_special}")
    print(f"标准 Base64: {standard_b64}")       # 包含 + /
    print(f"URL 安全 Base64: {url_safe_b64}")   # 包含 - _
    print()

    # --- 场景 3: 修复缺失的填充符 (CTF 常见坑) ---
    # 很多题目会去掉末尾的 '=' 来增加难度
    print(f"--- 场景3: 自动修复填充 ---")
    broken_b64 = "SGVsbG8gV29ybGQ"  # 正常是 SGVsbG8gV29ybGQ=
    fixed_data = tool.decode(broken_b64)
    print(f"残缺密文: {broken_b64}")
    print(f"修复解码: {fixed_data.decode('utf-8')}")
    print()

    # --- 场景 4: 生成 Data URL (用于前端隐写) ---
    # 格式: data:image/png;base64,iVBOR...
    print(f"--- 场景4: Data URL 生成 ---")
    # 假设这是一个图片的 Base64
    img_b64 = tool.encode(b"Fake Image Data") 
    data_url = f"data:image/png;base64,{img_b64}"
    print(f"Data URL: {data_url[:50]}...")

2.1.2 Base32与Base16(Hex)编码

Base32 使用32个字符(A-Z, 2-7),每5位一组。常用 = 填充。

Base16 Hex ): 即十六进制编码,使用16个字符(0-9, A-F),每4位一组。

复制代码
import base64
import binascii

class Base32And16Tool:
    """
    CTF 专用 Base32 和 Base16 (Hex) 工具类
    """

    # ================= Base32 相关 =================
    @staticmethod
    def base32_encode(data):
        """
        Base32 编码
        特征:只包含 A-Z 和 2-7,末尾通常有 = 填充
        """
        if isinstance(data, str):
            data = data.encode('utf-8')
        return base64.b32encode(data).decode('utf-8')

    @staticmethod
    def base32_decode(data):
        """
        Base32 解码
        注意:Base32 对填充符 '=' 要求严格,这里增加了自动补全逻辑
        """
        if isinstance(data, bytes):
            data = data.decode('utf-8')
        
        # 自动修复填充:Base32 长度必须是 8 的倍数
        missing_padding = len(data) % 8
        if missing_padding:
            data += '=' * (8 - missing_padding)
            
        try:
            return base64.b32decode(data).decode('utf-8')
        except binascii.Error as e:
            return f"解码错误: {e}"

    # ================= Base16 (Hex) 相关 =================
    @staticmethod
    def hex_encode(data, method='lib'):
        """
        Hex 编码
        method: 'lib' 使用 base64 库, 'native' 使用 bytes.hex() (推荐)
        """
        if isinstance(data, str):
            data = data.encode('utf-8')
            
        if method == 'lib':
            # base64.b16encode 返回的是大写字母
            return base64.b16encode(data).decode('utf-8')
        else:
            # Python 原生方法,返回小写字母,更常用
            return data.hex()

    @staticmethod
    def hex_decode(data, method='native'):
        """
        Hex 解码
        method: 'lib' 使用 base64 库, 'native' 使用 bytes.fromhex() (推荐)
        """
        if isinstance(data, bytes):
            data = data.decode('utf-8')
            
        # 移除可能的空格或换行
        data = data.replace(" ", "").replace("\n", "")
        
        try:
            if method == 'lib':
                return base64.b16decode(data.upper()).decode('utf-8')
            else:
                # Python 原生方法,容错性更好
                return bytes.fromhex(data).decode('utf-8')
        except (binascii.Error, ValueError) as e:
            return f"解码错误: {e}"

# ==========================================
# 使用示例
# ==========================================
if __name__ == "__main__":
    tool = Base32And16Tool()
    original_text = "flag{base_encoding}"

    # --- 场景 1: Base32 编解码 ---
    print(f"--- 场景1: Base32 ---")
    print(f"原文: {original_text}")
    
    b32_encoded = tool.base32_encode(original_text)
    print(f"编码: {b32_encoded}") 
    # 输出示例: MZWGCZBAORUXIIDCMYQGSZ3XNFXGC3TEON2HE2LOM4======
    
    b32_decoded = tool.base32_decode(b32_encoded)
    print(f"解码: {b32_decoded}")
    print()

    # --- 场景 2: Hex (Base16) 编解码 ---
    print(f"--- 场景2: Hex (Base16) ---")
    print(f"原文: {original_text}")
    
    # 使用原生方法 (推荐,因为通常是小写)
    hex_encoded = tool.hex_encode(original_text, method='native')
    print(f"编码 (Native): {hex_encoded}")
    # 输出示例: 666c61677b626173655f656e636f64696e677d
    
    hex_decoded = tool.hex_decode(hex_encoded, method='native')
    print(f"解码 (Native): {hex_decoded}")
    print()

    # --- 场景 3: 混合处理 ---
    # 有时候题目是 Hex -> Base32
    print(f"--- 场景3: 嵌套编码 (Hex -> Base32) ---")
    step1_hex = tool.hex_encode("flag")
    step2_b32 = tool.base32_encode(step1_hex) # 对 Hex 字符串再次 Base32
    print(f"Hex: {step1_hex}")
    print(f"Base32(Hex): {step2_b32}")

2.1.3 URL编码(Percent Encoding)

原理: 将非ASCII字符或特殊字符转换为 %XX 的形式,其中XX是字符的十六进制ASCII值。

复制代码
from urllib.parse import quote, unquote, urlencode, parse_qs

class URLTool:
    """
    CTF 专用 URL 编码工具类
    支持:标准编解码、自定义保留字符、空格处理、字典转查询串
    """

    @staticmethod
    def encode(data, safe='', space_to_plus=False):
        """
        URL 编码
        参数:
            data: 待编码数据 (str)
            safe: 不需要编码的字符 (例如 '/' 或 '{}')
            space_to_plus: 是否将空格转为 + 号 (默认 False 转为 %20)
        返回:
            str: 编码后的字符串
        """
        if isinstance(data, bytes):
            data = data.decode('utf-8')
            
        if space_to_plus:
            # 使用 quote_plus 处理表单数据 (空格变+)
            return quote_plus(data, safe=safe)
        else:
            # 使用 quote 处理普通 URL 路径 (空格变%20)
            return quote(data, safe=safe)

    @staticmethod
    def decode(data, plus_to_space=False):
        """
        URL 解码
        参数:
            data: 待解码数据 (str)
            plus_to_space: 是否将 + 号还原为空格
        返回:
            str: 解码后的字符串
        """
        if isinstance(data, bytes):
            data = data.decode('utf-8')
            
        if plus_to_space:
            return unquote_plus(data)
        else:
            # 标准 unquote 也会把 + 解码为空格,除非是 quote_plus 生成的
            # 这里为了保险,通常 unquote 即可
            return unquote(data)

    @staticmethod
    def dict_to_query(data_dict):
        """
        将字典转换为查询字符串 (例如: {'a': 1} -> 'a=1')
        常用于构造 POST 请求体或 URL 参数
        """
        return urlencode(data_dict)

    @staticmethod
    def query_to_dict(query_string):
        """
        将查询字符串解析回字典
        注意:返回的 value 是列表形式,因为同一个 key 可能有多个值
        """
        return parse_qs(query_string)

# ==========================================
# 使用示例
# ==========================================
if __name__ == "__main__":
    tool = URLTool()
    
    # --- 场景 1: 基础编解码 (你的原始代码场景) ---
    print(f"--- 场景1: 基础编解码 ---")
    text = "flag{hello world!}"
    encoded = tool.encode(text)
    print(f"原文: {text}")
    print(f"编码: {encoded}") 
    # 输出: flag%7Bhello%20world%21%7D
    print(f"解码: {tool.decode(encoded)}")
    print()

    # --- 场景 2: 保留特殊字符 (CTF 常用) ---
    # 比如我们要编码一个 URL,但不想把 / 和 : 编码掉
    print(f"--- 场景2: 保留字符 (Safe Mode) ---")
    url = "http://example.com/flag{test}"
    # 默认会把 / 和 : 都编码
    full_encoded = tool.encode(url)
    # 指定保留 / 和 :
    safe_encoded = tool.encode(url, safe='/:{}') 
    print(f"全编码: {full_encoded}")
    print(f"保留编码: {safe_encoded}")
    # 输出: http://example.com/flag%7Btest%7D
    print()

    # --- 场景 3: 空格处理差异 ---
    # 在 GET 请求中空格通常是 %20,但在 POST 表单中通常是 +
    print(f"--- 场景3: 空格处理 ---")
    text_with_space = "hello world"
    standard = tool.encode(text_with_space) # %20
    form_data = tool.encode(text_with_space, space_to_plus=True) # +
    print(f"标准 (%20): {standard}")
    print(f"表单 (+): {form_data}")
    print()

    # --- 场景 4: 字典转查询串 ---
    print(f"--- 场景4: 构造查询参数 ---")
    params = {"id": "123", "flag": "flag{test}"}
    query_str = tool.dict_to_query(params)
    print(f"字典: {params}")
    print(f"查询串: {query_str}")
    # 输出: id=123&flag=flag%7Btest%7D

2.1.4 ASCII编码与Unicode

ASCII 7位编码,覆盖128个字符(0-127)。常见操作:字符与ASCII码互转。

复制代码
class ASCIIUtil:
    """
    CTF 专用 ASCII 码工具类
    支持:字符串与数值列表互转、Hex 互转、进制转换
    """

    @staticmethod
    def str_to_list(text):
        """字符串转 ASCII 码列表"""
        return [ord(c) for c in text]

    @staticmethod
    def list_to_str(num_list):
        """ASCII 码列表转字符串"""
        return ''.join(chr(x) for x in num_list)

    @staticmethod
    def str_to_hex(text, prefix=False):
        """
        字符串转十六进制字符串
        prefix: 是否添加 0x 前缀
        """
        hex_str = text.encode('utf-8').hex()
        if prefix:
            return '0x' + hex_str
        return hex_str

    @staticmethod
    def hex_to_str(hex_str):
        """
        十六进制字符串转字符串
        支持带 0x 前缀或不带前缀
        """
        clean_hex = hex_str.replace('0x', '').replace(' ', '')
        return bytes.fromhex(clean_hex).decode('utf-8')

    @staticmethod
    def str_to_decimal_str(text, separator=' '):
        """字符串转十进制数值字符串 (例如: "102 108 97 103")"""
        return separator.join(str(ord(c)) for c in text)

    @staticmethod
    def decimal_str_to_str(decimal_str, separator=' '):
        """十进制数值字符串转字符串"""
        nums = [int(x) for x in decimal_str.split(separator)]
        return ASCIIUtil.list_to_str(nums)

    @staticmethod
    def to_binary(text):
        """字符串转二进制流 (每8位一组)"""
        return ' '.join(format(ord(c), '08b') for c in text)

# ==========================================
# 使用示例
# ==========================================
if __name__ == "__main__":
    util = ASCIIUtil()
    text = "flag"
    
    # --- 场景 1: 基础列表互转 (你的原始代码场景) ---
    print(f"--- 场景1: 基础列表互转 ---")
    print(f"原文: {text}")
    ascii_list = util.str_to_list(text)
    print(f"转列表: {ascii_list}") # [102, 108, 97, 103]
    print(f"还原: {util.list_to_str(ascii_list)}")
    print()

    # --- 场景 2: 十六进制互转 (Misc/Web 常用) ---
    print(f"--- 场景2: Hex 互转 ---")
    hex_val = util.str_to_hex(text)
    print(f"转 Hex: {hex_val}") # 666c6167
    print(f"还原: {util.hex_to_str(hex_val)}")
    print()

    # --- 场景 3: 十进制字符串 (常见混淆) ---
    print(f"--- 场景3: 十进制字符串 ---")
    # 题目可能给出一串数字: 102 108 97 103
    dec_str = util.str_to_decimal_str(text)
    print(f"转十进制串: {dec_str}")
    print(f"还原: {util.decimal_str_to_str(dec_str)}")
    print()

    # --- 场景 4: 二进制查看 ---
    print(f"--- 场景4: 二进制查看 ---")
    print(f"转二进制: {util.to_binary(text)}")
    # 输出: 01100110 01101100 01100001 01100111

2.1.5 摩尔斯电码(Morse Code)

原理: 使用点(.)和划(-)的组合表示字母和数字。

复制代码
class MorseCodeTool:
    """
    CTF 专用摩尔斯电码工具类
    支持:加解密、标点符号、自动分隔符处理
    """
    
    # 标准摩尔斯电码表 (包含常用标点)
    MORSE_DICT = {
        'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.', 'F': '..-.',
        'G': '--.', 'H': '....', 'I': '..', 'J': '.---', 'K': '-.-', 'L': '.-..',
        'M': '--', 'N': '-.', 'O': '---', 'P': '.--.', 'Q': '--.-', 'R': '.-.',
        'S': '...', 'T': '-', 'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-',
        'Y': '-.--', 'Z': '--..',
        '0': '-----', '1': '.----', '2': '..---', '3': '...--', '4': '....-',
        '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.',
        '.': '.-.-.-', ',': '--..--', '?': '..--..', "'": '.----.', '!': '-.-.--',
        '/': '-..-.', '(': '-.--.', ')': '-.--.-', '&': '.-...', ':': '---...',
        ';': '-.-.-.', '=': '-...-', '+': '.-.-.', '-': '-....-', '_': '..--.-',
        '"': '.-..-.', '$': '...-..-', '@': '.--.-.'
    }

    # 反向字典用于解码 (自动生成)
    REVERSE_DICT = {v: k for k, v in MORSE_DICT.items()}

    @staticmethod
    def encode(text, separator=' '):
        """
        将文本转换为摩尔斯电码
        参数:
            text: 待加密文本
            separator: 字母之间的分隔符 (默认为空格)
        """
        morse_chars = []
        for char in text.upper():
            if char == ' ':
                morse_chars.append('/')  # 单词之间用 / 分隔
            elif char in MorseCodeTool.MORSE_DICT:
                morse_chars.append(MorseCodeTool.MORSE_DICT[char])
            else:
                morse_chars.append('?') # 未知字符
        
        return separator.join(morse_chars)

    @staticmethod
    def decode(morse_text, separator=' '):
        """
        将摩尔斯电码解码为文本
        参数:
            morse_text: 摩尔斯电码字符串
            separator: 字母之间的分隔符 (默认为空格)
        """
        result = []
        # 处理单词分隔符,将 / 替换为特殊标记以便后续处理,或者直接按 / 分割
        # 这里采用按 / 分割单词,按空格分割字母的逻辑
        
        words = morse_text.split('/')
        decoded_words = []
        
        for word in words:
            letters = word.strip().split(separator)
            decoded_word = ""
            for code in letters:
                if code: # 忽略空字符串
                    decoded_word += MorseCodeTool.REVERSE_DICT.get(code, '?')
            decoded_words.append(decoded_word)
            
        return ' '.join(decoded_words)

# ==========================================
# 使用示例
# ==========================================
if __name__ == "__main__":
    tool = MorseCodeTool()
    
    # --- 场景 1: 基础解码 (你的原始代码场景) ---
    print(f"--- 场景1: 基础解码 ---")
    cipher = ".. .-.. --- ...- . / -.-. - ..-."
    # 注意:这里假设字母间是空格,单词间是 /
    plain = tool.decode(cipher)
    print(f"密文: {cipher}")
    print(f"明文: {plain}") 
    # 输出: I LOVE CTF
    print()

    # --- 场景 2: 编码 (加密) ---
    print(f"--- 场景2: 编码 (加密) ---")
    text = "FLAG{MORSE}"
    encoded = tool.encode(text)
    print(f"明文: {text}")
    print(f"密文: {encoded}")
    # 输出: ..-. .-.. .- --. / -- --- .-. ... .
    print()

    # --- 场景 3: 包含标点符号 ---
    print(f"--- 场景3: 包含标点 ---")
    text_punct = "HELP!"
    encoded_punct = tool.encode(text_punct)
    decoded_punct = tool.decode(encoded_punct)
    print(f"明文: {text_punct}")
    print(f"密文: {encoded_punct}")
    print(f"还原: {decoded_punct}")

2.1.6 进制转换

复制代码
class RadixConverter:
    """
    CTF 专用进制转换工具类
    支持:2/8/10/16/32/36/62/64/85 进制互转,自动去前缀,补零
    """

    @staticmethod
    def to_binary(n, fill=8):
        """
        十进制转二进制字符串
        fill: 自动补零的位数 (默认8位,设为0则不补零)
        """
        if fill:
            return format(n, f'0{fill}b')
        return bin(n)[2:]

    @staticmethod
    def to_hex(n, prefix=False):
        """
        十进制转十六进制字符串
        prefix: 是否保留 0x 前缀
        """
        if prefix:
            return hex(n)
        return hex(n)[2:]

    @staticmethod
    def to_oct(n, prefix=False):
        """
        十进制转八进制字符串
        prefix: 是否保留 0o 前缀
        """
        if prefix:
            return oct(n)
        return oct(n)[2:]

    @staticmethod
    def to_decimal(data, base):
        """
        任意进制转十进制
        data: 字符串或整数
        base: 原进制 (2-36)
        """
        if isinstance(data, int):
            return data
        return int(str(data), base)

    @staticmethod
    def convert(data, from_base, to_base):
        """
        通用进制转换器
        data: 输入数据
        from_base: 原进制
        to_base: 目标进制
        """
        # 1. 先转十进制
        decimal_val = RadixConverter.to_decimal(data, from_base)
        
        # 2. 再转目标进制
        if to_base == 2:
            return RadixConverter.to_binary(decimal_val, fill=0)
        elif to_base == 8:
            return RadixConverter.to_oct(decimal_val, prefix=False)
        elif to_base == 16:
            return RadixConverter.to_hex(decimal_val, prefix=False)
        elif to_base == 10:
            return str(decimal_val)
        else:
            # 对于非标准进制,使用 Python 原生转换逻辑(仅支持到36进制)
            return RadixConverter._custom_base_convert(decimal_val, to_base)

    @staticmethod
    def _custom_base_convert(n, base):
        """处理 2-36 进制的转换 (使用 0-9, a-z)"""
        if n < 0:
            return '-' + RadixConverter._custom_base_convert(-n, base)
        if n < base:
            return RadixConverter._digit_to_char(n)
        return RadixConverter._custom_base_convert(n // base, base) + RadixConverter._digit_to_char(n % base)

    @staticmethod
    def _digit_to_char(d):
        if d < 10:
            return str(d)
        return chr(ord('a') + d - 10)

# ==========================================
# 使用示例
# ==========================================
if __name__ == "__main__":
    converter = RadixConverter()
    n = 255
    
    # --- 场景 1: 基础转换 (你的原始代码场景) ---
    print(f"--- 场景1: 基础转换 ---")
    print(f"十进制: {n}")
    print(f"二进制: {converter.to_binary(n)}")       # 11111111 (自动补8位)
    print(f"八进制: {converter.to_oct(n)}")          # 377 (自动去前缀)
    print(f"十六进制: {converter.to_hex(n)}")        # ff (自动去前缀)
    print()

    # --- 场景 2: 逆向转换 ---
    print(f"--- 场景2: 逆向转换 ---")
    print(f"二进制 '11111111' -> 十进制: {converter.to_decimal('11111111', 2)}")
    print(f"十六进制 'ff' -> 十进制: {converter.to_decimal('ff', 16)}")
    print()

    # --- 场景 3: 任意进制互转 ---
    print(f"--- 场景3: 任意进制互转 ---")
    # 将 16进制的 'ff' 转换为 2进制
    print(f"Hex 'ff' -> Bin: {converter.convert('ff', 16, 2)}")
    # 将 10进制的 100 转换为 3进制
    print(f"Dec 100 -> Base 3: {converter.convert(100, 10, 3)}")
    # 将 36进制的 'zz' 转换为 10进制
    print(f"Base 36 'zz' -> Dec: {converter.to_decimal('zz', 36)}") # 1295
    print()

    # --- 场景 4: 补零处理 (CTF 常见) ---
    print(f"--- 场景4: 补零处理 ---")
    small_num = 10
    print(f"数字 10 转二进制 (不补零): {converter.to_binary(small_num, fill=0)}") # 1010
    print(f"数字 10 转二进制 (补8位): {converter.to_binary(small_num, fill=8)}") # 00001010
    print(f"数字 10 转二进制 (补16位): {converter.to_binary(small_num, fill=16)}") # 0000000000001010

2.2 编码识别技巧

在CTF比赛中,快速识别编码类型是解题的第一步。以下是常见编码的特征总结:

编码类型 特征字符集 识别要点
Base64 A-Z, a-z, 0-9, +, / 末尾可能有1-2个 = 填充;长度是4的倍数
Base32 A-Z, 2-7 全大写字母;末尾可能有多个 = 填充
Hex (Base16) 0-9, a-f (或A-F) 只有十六进制字符;长度为偶数
URL编码 含 %XX 格式 出现 %20, %7B 等百分号序列
二进制 0, 1 只有0和1;通常8位一组表示一个字节
摩尔斯电码 ., -, 空格, / 由点和划组成,空格分隔字母,/ 分隔单词
Unicode转义 \uXXXX 格式 以 \u 开头后跟4位十六进制数

多层编码嵌套的识别策略:

  1. 先观察最外层密文的特征,判断编码类型
  2. 解码后再观察结果,判断是否仍为编码后的数据
  3. 逐层解码,直到获得可读的明文或flag
  4. 善用CyberChef的"Magic"功能,可自动检测并建议解码步骤

2.3 实战案例:多层编码解密

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 📋 题目 你截获了一段神秘信息,经过情报分析,已知该信息经过了多层编码处理。请逐层解码,恢复出隐藏的flag。 密文:2d2d2d202e2e2e202d2d2d202e2e2e2e202d2d2d202d2d2d202d2d2d202e2e2e2e202d2d2d202d2d2d 编码顺序提示:原文 → Morse → 逆序 → Hex |

解题过程:

第一步: Hex 解码 (最外层是十六进制编码)

第一步:Hex解码 cipher_hex = "2d2d2d202e2e2e202d2d2d202e2e2e2e202d2d2d202d2d2d202d2d2d202e2e2e2e202d2d2d202d2d2d" step1 = bytes.fromhex(cipher_hex).decode() print(f"Hex解码: {step1}") # 结果: --- ... --- .... --- --- --- .... --- ---

第二步:逆序还原

第二步:逆序 step2 = step1[::-1] print(f"逆序: {step2}") # 结果: --- --- .... --- --- --- .... --- ... ---

第三步: Morse 解码

第三步:Morse解码 MORSE_DECODE = { '.-': 'A', '-...': 'B', '-.-.': 'C', '-..': 'D', '.': 'E', '..-.': 'F', '--.': 'G', '....': 'H', '..': 'I', '.---': 'J', '-.-': 'K', '.-..': 'L', '--': 'M', '-.': 'N', '---': 'O', '.--.': 'P', '--.-': 'Q', '.-.': 'R', '...': 'S', '-': 'T', '..-': 'U', '...-': 'V', '.--': 'W', '-..-': 'X', '-.--': 'Y', '--..': 'Z' } morse_text = step2 letters = morse_text.split(' ') flag = ''.join(MORSE_DECODE.get(l, '?') for l in letters) print(f"Morse解码: {flag}") # 结果: OOHOOOHSO → 根据实际内容获取flag

通过逐层解码,最终恢复出了隐藏的明文信息。在实际CTF比赛中,编码嵌套的层数和类型可能更加复杂,关键是掌握每种编码的特征识别方法。

2.4 图片与特殊编码

在Misc或Crypto题目中,有时候"编码"不仅仅存在于文本中,还可能隐藏在图片像素或特殊的Unicode字符里。

2.4.1 零宽字符编码

原理:利用Unicode中不可见的"零宽字符"(如零宽空格、零宽非连接符等)来隐藏信息。这些字符在普通文本编辑器中不可见,但可以通过脚本提取。

常见字符

  • 零宽空格:\u200b
  • 零宽非连接符:\u200c
  • 零宽连接符:\u200d

实战脚本:提取隐藏在字符串中的二进制信息

复制代码
def decode_zwc(text):
    binary = ""
    for char in text:
        if char == '\u200b': binary += '0'
        elif char == '\u200c': binary += '1'
        # 也可以根据题目定义的规则调整,比如用\u200d代表1
    return binary

# 假设密文中隐藏了零宽字符
secret_text = "H\u200be\u200bl\u200cl\u200bo" 
print(decode_zwc(secret_text)) # 输出: 0101

2.4.2 盲文编码

原理:使用2x3的点阵来表示字母,通常表现为特殊的Unicode符号。

特征:看起来像是一堆凸起的小点(如 ⠁⠃⠉)。

工具 :可以使用在线盲文转换器,或者Python的 braille 库。

2.4.3 二维码与条形码

原理:CTF中常出现破损、模糊或被部分遮挡的二维码。

解题技巧

  • 修复:使用Photoshop调整对比度、反色。

  • 暴力修补 :如果二维码缺失定位点,可以使用 qrecovery 等工具尝试修复。

  • 脚本生成:有时候题目给的是二维码的矩阵数据(01串),需要自己用Python生成二维码。

    import qrcode

    假设题目给出了01矩阵数据

    data = "111111100101..."

    将其转换为图片并保存,再用扫码工具识别


2.5 编码层面的"加密"与混淆

有时候出题人不会直接给你标准的Base64,而是会对编码表进行修改,这被称为"魔改编码"。

2.5.1 自定义Base64表

原理 :标准的Base64表是 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/。出题人可能会打乱这个顺序,或者替换其中的字符(例如把 + 换成 -,把 / 换成 _,这其实是Base64的URL安全变体,但也可能是完全随机的乱序)。

解题思路

  1. 观察密文字符集,确定使用的字符范围。
  2. 如果有明文-密文对,尝试推导映射关系。
  3. 如果没有,尝试根据文件头(如 PK 对应 zip文件,flag 对应常见开头)进行爆破或推导。

Python实现自定义解码逻辑

复制代码
import base64

def custom_b64_decode(ciphertext, custom_table):
    # 1. 建立自定义表与标准表的映射
    # 假设 standard_table 是标准Base64表
    standard_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    
    # 2. 将密文根据自定义表还原为标准Base64
    # 这里演示一种简单的替换思路:将自定义字符替换回标准字符
    trans_table = str.maketrans(custom_table, standard_table)
    standard_b64 = ciphertext.translate(trans_table)
    
    # 3. 使用标准库解码
    try:
        return base64.b64decode(standard_b64)
    except Exception as e:
        return f"解码失败: {e}"

# 示例:假设出题人把 'A' 换成了 'Z','B' 换成了 'Y'...
# 实际题目中需要根据具体字符集构建 custom_table

2.5.2 键盘密码

原理:利用电脑键盘的布局(QWERTY)来进行编码。

常见形式

  • QWE解码 :密文是 qwe,对应键盘上的位置,可能映射为 abc(如果按字母表顺序排列键盘)。
  • 图形轨迹 :密文是一串方向键(如 ↑↑↓↓←→←→BA),或者是键盘上连线的形状(如画出一个字母)。

解题工具:通常使用在线的"键盘密码解密工具"或编写简单的字典映射脚本。


2.6 编码与加密的边界:异或

虽然异或属于加密,但在CTF入门阶段,单字节异或常与编码混淆,且常作为编码后的下一步处理。

2.6.1 单字节异或

原理:明文与一个单字节密钥进行异或运算。

特征:解码后的数据看起来像乱码,但具有周期性。

攻击方法:暴力破解(0-255),结合字符频率分析(英文中e, t, a出现频率高)。

复制代码
def xor_brute_force(ciphertext):
    for key in range(256):
        result = ''.join(chr(byte ^ key) for byte in ciphertext)
        # 简单判断是否包含常见字符
        if "flag" in result or "CTF" in result:
            print(f"Key: {key}, Result: {result}")

# 假设密文是字节流
cipher_bytes = bytes.fromhex("6c6c62636e") 
xor_brute_force(cipher_bytes)
相关推荐
jimy16 小时前
C 语言的 static 关键字作用
c语言·开发语言·算法
22信通小白6 小时前
北邮现代通信技术实验——网络基础综合实验
网络
七颗糖很甜6 小时前
基于IRI-2016模型计算电子密度、TEC、foF2等参数的技术原理与代码实现
大数据·python·算法
枫叶丹46 小时前
【HarmonyOS 6.0】Camera Kit白平衡API深度解析:让三方应用真正“掌控”色彩
开发语言·华为·harmonyos·视频编解码
xyq20246 小时前
C# 运算符重载
开发语言
echome8887 小时前
Python 生成器与 yield 关键字实战:5 个节省内存的高级用法与性能优化技巧
开发语言·python
starvapour7 小时前
CUDA_VISIBLE_DEVICES与nvidia-smi显卡序号不一致的问题
python·ai·cuda
艾莉丝努力练剑7 小时前
【Linux网络】Linux 网络编程入门:UDP Socket 编程(上)
linux·运维·服务器·网络·c++·udp
码界筑梦坊7 小时前
112-基于Flask的游戏行业销售数据可视化分析系统
开发语言·python·游戏·信息可视化·flask·毕业设计·echarts