第二章:编码与基础变换
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位十六进制数 |
多层编码嵌套的识别策略:
- 先观察最外层密文的特征,判断编码类型
- 解码后再观察结果,判断是否仍为编码后的数据
- 逐层解码,直到获得可读的明文或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)