跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

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 比赛中,快速识别编码类型是解题的第一步。

以下是常见编码的特征总结: 编码类型特征字符集识别要点 Base64A-Z, a-z, 0-9, +, /末尾可能有1-2个=填充;长度是4的倍数Base32A-Z, 2-7全大写字母;末尾可能有多个=填充Hex (Base16)0-9, a-f (或A-F)只有十六进制字符;长度为偶数URL编码含%XX格式出现%20,%7B等百分号序列二进制0, 1只有0和1;通常8位一组表示一个字节摩尔斯电码., -,空格, /由点和划组成,空格分隔字母,/分隔单词Unicode转义\uXXXX格式以\u开头后跟4位十六进制数 多层编码嵌套的识别策略: 先观察最外层密文的特征,判断编码类型 解码后再观察结果,判断是否仍为编码后的数据 逐层解码,直到获得可读的明文或 flag 善用 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安全变体,但也可能是完全随机的乱序)。

解题思路 : 观察密文字符集,确定使用的字符范围。

如果有明文-密文对,尝试推导映射关系。

如果没有,尝试根据文件头(如

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)

相关文章