末泱花洺 - CTF
type: channel
link: https://ayfre.com/tag/CTF/
items: 将二进制数值转换成二维码
items: FFT 频域盲水印
items: Base系列混合编码爆破
items: 云影密码-01248密码-原理与加解密
items: 已知部分密码爆破压缩包
items: 培根密码-Baconian-Cipher
items: 栅栏密码-Rail-Fence-Cipher
items: 仿射密码-Affine-Cipher
items: RSA密码算法原理与CTF攻击

将二进制数值转换成二维码
type: item
date: 2023-04-20T15:11:00+08:00
description: AI摘要 本文介绍CTF MISC题目中，将01二进制数字串转换为二维码图像的方法：理解二进制与像素矩阵的映射关系，通过计算长度平方根确定边长，使用PIL库按行优先顺序将1映射为黑色、0映射为白色，生成图像后扫码获取flag。 以上内容由 AI 生成，仅供参考。背景在 CTF竞赛的 MISC（杂项） 类题目中，出题者经常会给出一段由 0 和 1 组成的数字串，要求选手将其还原为可视化的二维码图像，进而扫码获取 flag。这类题目的核心考察点是：理解二进制数据与像素矩阵之间的映射关系，以及如何利用编程手段完成图像重建。原理分析为什么 01 串能表示二维码？二维码（QR Code）本质上是一个二维像素矩阵。每个模块（module）只有两种状态：像素值二进制表示颜色深色模块1黑色(0, 0, 0)浅色模块0白色(255, 255, 255)当一段 01 数字串的长度恰好是某个整数 n 的平方时（即 len = n × n），它可以被排列成一个 n × n 的正方形矩阵——这正是二维码的像素布局方式。数据流转换过程01 数字串 → 一维数组 → 二维像素矩阵 → 二维码图像 625 bit [0,1,1,0,...] 25×25 矩阵 RGB 图像关键约束：数字串长度必须是完全平方数，否则无法构成正方形矩阵，也就不是合法的二维码。常见尺寸对照矩阵边长总像素数（bit）适用场景21441QR Version 1（最小）25625CTF 常见尺寸29841QR Version 3331089QR Version 4371369QR Version 5注意：CTF 题目中的矩阵不一定是标准 QR Code 的精确定位图案尺寸，但 25×25 是最常见的出题尺寸。解题代码完整脚本内容已锁定此处内容已被隐藏，请关注公众号后验证查看关注公众号验证代码逐行解析代码段说明Image.new("RGB", (MAX, MAX))创建 25×25 的 RGB 空白图像，默认全黑for y / for x双重循环按行优先（row-major）顺序遍历像素binary_str[i]逐字符读取，'1'→ 黑色，'0'→ 白色putpixel([x, y], color)将指定坐标像素设置为对应颜色pic.show()调用系统默认图片查看器显示结果pic.save("flag.png")保存为 PNG 文件，供扫码工具识别 扩展：另一种思路 — 使用 qrcode 库反向识别如果你拿到 01 串后不想手写转换逻辑，也可以先生成图像再用 pyzbar 等库自动解码：# 安装依赖: pip install Pillow pyzbar from pyzbar.pyzbar import decode from PIL import Image # 先用上面的方法生成图像，再解码 img = Image.open("flag.png") results = decode(img) for r in results: print(f"[+] 类型: {r.type}") print(f"[+] 内容: {r.data.decode()}")解题流程总结┌─────────────────────────────────────────┐ │ 1. 获取 01 数字串 │ │ ↓ │ │ 2. 计算长度，判断是否为完全平方数 │ │ ↓ │ │ 3. 求平方根 → 矩阵边长 n │ │ ↓ │ │ 4. 按行优先顺序，1→黑 0→白，填充 n×n 图像 │ │ ↓ │ │ 5. 保存图像，使用二维码扫描工具读取 flag │ └─────────────────────────────────────────┘常见问题Q: 数字串长度不是完全平方数怎么办？可能是以下情况：包含分隔符：检查数据中是否有空格、逗号、换行符需要清理多层编码：数据可能经过 Base64、Hex 等编码，需先解码非正方形：极少数题目可能是矩形（长 × 宽），需尝试不同因数组合Q: 生成的图像扫码失败？反转颜色：尝试将 1 和 0 对应的颜色互换（黑变白、白变黑）检查行列顺序：确认是行优先还是列优先添加白边：扫码器可能需要安静区（quiet zone），给图像加一圈白色边距小结本题属于 CTF MISC 中的图像隐写与还原类型，核心知识点：二进制与像素的映射：01 串本质上是像素矩阵的线性展开完全平方数约束：正方形矩阵要求数据长度为 n²PIL 图像操作：Image.new() 创建画布，putpixel() 逐像素绘制行优先遍历：(y, x) 的双重循环对应从左到右、从上到下的扫描顺序

FFT 频域盲水印
type: item
date: 2022-09-01T17:06:00+08:00
description: AI摘要 本文介绍基于FFT的频域水印技术。利用Python和OpenCV，通过随机种子置乱水印位置，在频域嵌入信息。虽为非盲水印，但难以破解。文章详细阐述了数学原理、代码实现、命令行用法及CTF解题策略。 以上内容由 AI 生成，仅供参考。概述盲水印（Blind Watermark）是一种信息隐藏技术，将水印数据嵌入到载体图像中，肉眼不可见，只有通过特定算法才能提取。概念辨析概念说明是否需原图盲水印 (Blind Watermark)提取水印时不需要原图和原始水印否非盲水印 (Non-Blind)提取时需要原图参与计算是（本文实现）可见水印图片上肉眼可见的叠加标记（如版权 LOGO）—本文实现的是频域非盲水印，但通过种子（seed）对水印位置进行随机置乱，使攻击者即使知道算法也无法在无种子时恢复可读水印，达到变相"盲"提取的效果。数学原理傅里叶频域变换图像在空间域（像素点阵）和频率域之间存在对应关系：低频分量：图像中变化缓慢的区域（大面积色块、背景）高频分量：图像中变化剧烈的区域（边缘、纹理、噪点）通过 二维快速傅里叶变换（FFT-2D），可以将 $M \times N$ 的图像矩阵从空间域转换到频率域：$$ F(u,v) = \sum_{x=0}^{M-1} \sum_{y=0}^{N-1} f(x,y) \cdot e^{-j2\pi(ux/M + vy/N)} $$逆变换（IFFT）可将频率域还原回空间域：$$ f(x,y) = \frac{1}{MN} \sum_{u=0}^{M-1} \sum_{v=0}^{N-1} F(u,v) \cdot e^{j2\pi(ux/M + vy/N)} $$频域嵌入原理在频率域中修改图像的傅里叶系数（尤其是中高频分量），不会在视觉上引起明显变化，但编码了信息：空间域图像 频率域 逆变换后 ┌──────┐ FFT ┌──────────┐ IFFT ┌──────┐ │ 原图 │ ────────→ │ F(u,v) │ ───────→ │ 图像 │ └──────┘ │ + │ └──────┘ │ α·W(u,v) │ └──────────┘ ↑ 水印频率分量嵌入公式：$$ F'(u,v) = F(u,v) + \alpha \cdot W(u,v) $$其中：$F(u,v)$ — 原始图像的第 $(u,v)$ 个频率系数$W(u,v)$ — 水印图像在频率域的系数$\alpha$ — 嵌入强度系数（默认 3.0），越大则水印越清晰但可见性越高$F'(u,v)$ — 嵌入水印后的频率系数提取公式：$$ W(u,v) = \frac{F'(u,v) - F(u,v)}{\alpha} $$从公式可见：提取需要已知 原始图像 $F(u,v)$ 和 alpha 参数。这是非盲水印的根本原因。攻击链路图嵌入过程 (Encode) ═══════════════════ 原图 img 水印 wm │ │ ▼ ▼ FFT(img) 随机置乱 (seed) │ │ │ ┌─────────────────┘ ▼ ▼ F' = F + α × wm_shuffled │ ▼ IFFT(F') → 含水印图像 │ ▼ 保存为 JPEG (有损压缩引入微量误差) 提取过程 (Decode) ═══════════════════ 原图 img 含水印图 img_encoded │ │ ▼ ▼ FFT(img) = F1 FFT(img_encoded) = F2 │ │ │ ┌─────────────────┘ ▼ ▼ W = (F2 - F1) / α → 得到置乱后的水印 │ ▼ 用相同 seed 逆置乱 → 原始水印图像代码实现环境依赖pip install opencv-python numpy matplotlib核心导入与参数import cv2 import numpy as np import random alpha = 3.0 # 嵌入强度：越大水印越清晰，但对原图像素影响越大 seed = 20160930 # 随机种子：作为水印置乱的"密钥"BGR → RGB 转换工具OpenCV 使用 BGR 通道顺序，Matplotlib 使用 RGB 顺序，显示时需转换：def bgr_to_rgb(img): b, g, r = cv2.split(img) return cv2.merge([r, g, b])编码（Encode）：嵌入水印# 1. 读取原图与水印 img = cv2.imread('hui.png') wm = cv2.imread('wm.png') h, w = img.shape[0], img.shape[1] # 2. 将水印置入半高画布 (h…

Base系列混合编码爆破
type: item
date: 2022-08-20T14:53:00+08:00
description: AI摘要 文章针对CTF中Flag被多层Base64/Base32随机混合编码的难题，提出利用Base16、Base32、Base64字符集互斥的特性，通过依次尝试解码来还原Flag。核心策略是先试Base16排除，再试Base32，最后试Base64，并提供了相关Python脚本。 以上内容由 AI 生成，仅供参考。概述CTF Crypto / MISC 中常见一类题目：将 Flag 用 Base64 / Base32 随机混合编码多层，要求选手解码还原。核心挑战在于——编码轮次未知、每轮用的是哪种 Base 也未知，需要"盲破"。Flag 原文 │ ▼ 随机选取 base64 或 base32 ┌──────────┐ │ Round 1 │ → 编码后 └────┬─────┘ │ ▼ 再次随机选取 ┌──────────┐ │ Round 2 │ → 编码后 └────┬─────┘ │ ▼ ... 重复 c 次 (c ∈ [20, 40]) │ ▼ 最终密文 (base2.txt)编码总轮次 $c \in [20, 40]$，每轮 base64 或 base32 二选一。理论上总组合数为 $2^{20} \sim 2^{40}$，无法穷举——必须用"试探解码"策略。Base 家族速查三种编码对比属性Base16 (Hex)Base32Base64字符表大小163264字符集0-9 A-FA-Z 2-7A-Z a-z 0-9 + /每组输入1 字节5 字节3 字节每组输出2 字符8 字符4 字符膨胀率200%160%133%= 填充无有 (=)有 (=)输出长度倍数2 的倍数8 的倍数4 的倍数三种编码的互斥识别最重要的特性：每种编码的合法输入字符集互不包含。Base16 合法字符: 0123456789ABCDEFabcdef ↑ 不含 G-Z，不含 +/= Base32 合法字符: ABCDEFGHIJKLMNOPQRSTUVWXYZ234567 = ↑ 不含 0 1 8 9 + / Base64 合法字符: A-Z a-z 0-9 + / = ↑ 几乎包含所有可打印字符利用这个互斥性：尝试 base16 解码 → 若报错 → 不是 base16 → 换 base32 → 若还报错 → 一定是 base64。 ┌─────────┐ │ 输入密文 │ └────┬────┘ ▼ ┌─ try base16_decode ──┐ │ 含 G/K/+ 等非法字符 │ ▼ 报错 ▼ 报错 非 base16 ┌─ try base32_decode ──┐ │ 含 0/1/8/9/+ 等 │ ▼ 报错 ▼ 报错 非 base32 try base64_decode │ ▼ 继续下一轮编码脚本import base64 import random a = random.randint(0, 10) # a ∈ [0, 10] b = random.randint(20, 30) # b ∈ [20, 30] rounds = a + b # 总轮次 ∈ [20, 40] print(f'编码轮次: {rounds}') flag = b'flag{lalala}' for _ in range(rounds): k = random.randint(0, 1) if k == 0: flag = base64.b64encode(flag) else: flag = base64.b32encode(flag) with open('base2.txt', 'wb') as f: f.write(flag) print('finish!')关键参数：变量范围说明a0~10随机因子一b20~30随机因子二rounds = a+b20~40总编码轮次k0 或 10 → base64; 1 → base32编码路径总数$2^{rounds}$约$10^6 \sim 10^{12}$（不可穷举）解密脚本import base64 with open('base2.txt', 'rb') as f: data = f.read() decoders = [base64.b16decode, base64.b32decode, base64.b64decode] while True: for decoder in decoders: try: data = decoder(data) break except Exception: continue else: print('[错误] 全部解码尝试失败，密文可能已损坏') break try: text = data.decode('utf-8') except UnicodeDeco…

云影密码-01248密码-原理与加解密
type: item
date: 2022-08-15T10:08:00+08:00
description: AI摘要 云影密码（01248）是一种利用0、1、2、4、8通过贪婪求和分解字母序号的古典替换密码。文章阐述了其原理、编码表、Python加解密实现及CTF实战技巧。 以上内容由 AI 生成，仅供参考。概述云影密码（又称 01248 密码）是一种古典替换密码，它仅使用数字 0、1、2、4、8 来编码完整的 A-Z 字母表。原理详解编码逻辑云影密码采用贪婪求和分解的方式：每个字母的序号 $n$ (1~26) 被拆分为 $\{1, 2, 4, 8\}$ 的允许重复的加权和，数字间用 0 分隔。核心规则：用 0 分割不同字母对应的编码组每组内的数字（1、2、4、8）可以重复出现组内所有数字之和 = 字母在字母表中的序号序号映射：1→A, 2→B, ..., 26→Z密文: 8842101220480224404014224202480122 │ │ │ │ ... ▼ ▼ ▼ ▼ 分割: 88421 122 8 ↓ ↓ ↓ 求和: 8+8+4+2+1=23 1+2+2=5 8=8 ↓ ↓ ↓ 字母: W E H完整编码表字母序号分解密文组A111B222C32+121D444E54+141F64+242G74+2+1421H888I98+181J108+282K118+2+1821L128+484M138+4+1841N148+4+2842O158+4+2+18421P168+888Q178+8+1881R188+8+2882S198+8+2+18821T208+8+4884U218+8+4+18841V228+8+4+28842W238+8+4+2+188421X248+8+8888Y258+8+8+18881Z268+8+8+28882编码使用贪婪策略：从大到小依次从 $\{8, 4, 2, 1\}$ 中选取，可重复。同一序号可以有多种合法编码（如 16 = 88 也可写成 4444 或 84211），上表采用的是"最少位数"贪婪编码。Python 加解密实现解密（密文 → 明文）与原文思路一致，用 0 分割、逐组求和、chr(sum+64) 映射字母：def decode_01248(cipher: str) -> str: """云影密码解密：密文 → 明文""" groups = cipher.split('0') result = [] for group in groups: if not group: continue total = sum(int(d) for d in group) result.append(chr(total + 64)) return ''.join(result) # 示例 print(decode_01248('8842101220480224404014224202480122')) # 输出: WELCOMETOCFF解密流程：步骤操作示例 (884210...)1cipher.split('0')['88421', '122', '8', ...]2逐组求和23, 5, 8, ...3chr(sum+64)23→W, 5→E, 8→H, ...加密（明文 → 密文）使用贪婪算法，从 $\{8, 4, 2, 1\}$ 中从大到小反复减去可用的最大值：def encode_01248(plain: str) -> str: """云影密码加密：明文 → 密文（贪婪策略）""" weights = [8, 4, 2, 1] # 从大到小，贪婪选取 code = [] for ch in plain.upper(): if not 'A' <= ch <= 'Z': raise ValueError(f'无效字符: {ch}') n = ord(ch) - ord('A') + 1 # 序号 1~26 group = '' for w in weights: while n >= w: # 关键：允许重复！ group += str(w) n -= w code.append(group) return '0'.join(code) # 示例 print(encode_01248('HELLO')) # 输出: 801404801248 print(encode_01248('WXYZ')) # 输出: 884210888108881088820加密流程（以 Z=26 为例）：轮次当前 n权重 8剩余 n126while 26>=8 → "8", n=18 218while 18>=8 → "88", n=10 310while 10>=8 → "888", n=2 428 > 2 跳过 → 4 > 2 跳过 → while 2>=2 → "8882", n=0 50结束，密文组 =88…

已知部分密码爆破压缩包
type: item
date: 2022-08-13T14:14:00+08:00
description: AI摘要 本文介绍利用已知密码片段（前缀/后缀）爆破加密 ZIP/RAR 压缩包的方法。内容涵盖暴力枚举原理、Python 单/多线程实现、字符集优化及 hashcat 工具加速，适用于 CTF 密码破解场景。 以上内容由 AI 生成，仅供参考。概述CTF MISC / Crypto 题目中常见一类题型：给出一个加密的 ZIP/RAR 压缩包，并提示密码的一部分（如前缀、后缀或已知片段），要求爆破剩余未知位得到完整密码。全空间爆破 $62^6 \approx 5.6 \times 10^{10}$ 不可行，但已知部分后剩余空间缩小到 $62^n$（$n$ 为未知位数），枚举即可。已知密码格式: ? ? ? ? LiHua ↑ ↑ 4个未知字符 (62种可能/位) 总尝试次数: 62^4 ≈ 1.48 × 10^7暴力枚举原理搜索空间计算未知位数字符集大小尝试次数单线程耗时估算2623,844< 1 秒362238,328~2 秒46214,776,336~2 分钟562916,132,832~2 小时662$5.68 \times 10^{10}$不可行瓶颈不在 CPU 而在 I/O：每尝试一次密码都需要打开压缩包 → 文件 I/O 是主要耗时。攻击链路已知部分密码: "LiHua" (后缀) 未知位: 前 4 位 │ ▼ ┌──────────────────────┐ │ 生成候选密码 │ │ itertools.product │ │ '????' + 'LiHua' │ └──────────┬───────────┘ │ 逐条生成 ▼ ┌──────────────────────┐ │ 尝试解压 │ │ zipfile.extractall │ │ / rarfile.extract │ └──────────┬───────────┘ ├─ 密码错误 → 下一条 │ (无异常或 RuntimeError) │ ▼ 解压成功！ ┌──────────────────────┐ │ 输出正确密码 │ │ 文件成功提取 │ └──────────────────────┘Python 实现原始脚本分析（MD5 匹配法）import string from hashlib import md5 a = string.ascii_letters + string.digits # 62 个字符: a-z, A-Z, 0-9 for a1 in a: for a2 in a: for a3 in a: for a4 in a: pwd = a1 + a2 + a3 + a4 + 'LiHua' h = md5(pwd.encode('ascii')).hexdigest() if '1a4fb3fb5ee' in h: print(pwd) break脚本解读：行说明string.ascii_letters + string.digits字符集 = 52 个字母 + 10 个数字 = 62四层嵌套for枚举所有 4 位组合，时间复杂度$O(62^4)$md5(...).hexdigest()对候选密码做 MD5 哈希'1a4fb3fb5ee' in h比对部分哈希值（CTF 常只给出哈希片段）break仅跳出最内层循环（Python 的 break 不穿透多层）此方法的适用场景： 题目只给了密码的 MD5 部分值（而非加密压缩包），只需匹配哈希片段即可。完整版：ZIP 密码爆破import zipfile import itertools import string def crack_zip(zip_path: str, known_suffix: str, unknown_len: int, charset: str = None, output_dir: str = '.') -> str: """ 已知密码后缀，爆破 ZIP 文件的前缀部分 参数: zip_path: 加密 ZIP 文件路径 known_suffix: 已知的密码后缀（如 'LiHua'） unknown_len: 未知前缀的位数 charset: 字符集（默认 a-z, A-Z, 0-9） output_dir: 解压输出目录 返回: 完整密码，或 None """ if charset is None: charset = string.ascii_letters + string.digits # 62 字符 total = len(charset) ** unknown_len print(f'字符集大小: {len(charset)}, 未知位: {unknown_len}') print(f'总尝试次数: {total:,}') with…

培根密码-Baconian-Cipher
type: item
date: 2022-08-04T14:41:00+08:00
description: AI摘要 培根密码是弗朗西斯·培根提出的隐写术，利用两种状态（如大小写）编码字母，每个字母对应5位二进制。文章介绍了其原理、码表、Python实现及CTF中的识别与解题方法。 以上内容由 AI 生成，仅供参考。概述培根密码（Baconian Cipher / Bacon's cipher）由英国哲学家弗朗西斯·培根（Francis Bacon）于 1605 年在《学术的进展》中提出。与凯撒、仿射等替换密码不同，它的本质是一种隐写术（steganography）：秘密不在于字母被替换成了什么，而在于信息被藏进了载体文本的"外在形式"——两种字体、两种大小写、两种符号……只要能区分出两种状态，就能承载信息。每个明文字母被编码成长度为 5 的 A/B 序列（因为 $2^5 = 32 \ge 26$，5 位足以覆盖 26 个字母）。在 CTF 中，培根密码常以"密文只含两种符号、且有效长度为 5 的倍数"这一特征出现。编码: 字母 → 5 位 A/B 序列 (2^5 = 32 ≥ 26) 隐写: A/B → 载体文本的两种"形态" (小写/大写、正体/斜体、两种符号…) 解码: 载体形态 → A/B 序列 → 每 5 位一组 → 查表还原字母 两套码表: 26 字母版 每个字母独立（现代 / CTF 常用，等价 5-bit 二进制） 24 字母版 I=J、U=V 合并（培根原版，1605）编码原理隐写而非替换：为什么叫"双字母密码"培根本人称之为 biliteral cipher（双字母密码）。"双字母"指整套系统只用两个基本符号（记作 a 和 b）就能表达全部字母表——这正是二进制编码思想的雏形，比莱布尼茨系统化二进制（1703）早了近一个世纪。它与普通替换密码的根本差异，不在算法强度，而在"秘密藏在哪里"：维度替换密码（凯撒 / 仿射）培根密码秘密藏在内容：字母被换成别的字母形式：字母的字体 / 大小写 / 符号密文外观一串明显的乱码字母一段"看似正常"的文本安全依赖敌人不知道密钥敌人没意识到存在密文学科归属密码学 Cryptography隐写术 Steganography信息论视角：编码一个英文字母的理论下限是 $\log_2 26 \approx 4.70$ bit，而至少需要 $\lceil 4.70 \rceil = 5$ 位（$2^4=16 < 26 \le 32 = 2^5$）才能一一区分，所以培根固定用 5 位。代价是每位只承载 1 bit——每个载体符号仅携带 1 bit，编码 1 个字母需要 5 个载体符号，载体文本因此膨胀 5 倍。这也是隐写容量的核心约束：要藏 $n$ 个字母，载体至少要有 $5n$ 个可区分单元。培根密码的精妙不在替换本身（替换极其简单），而在于它把 0/1 悄悄"焊"进了正常文本的排版里。理解了这一点，就理解了从零宽字符到图片 LSB 的一整条现代隐写脉络。编码对照表26 字母版（现代 / CTF 常用）——本质就是"字母序号的 5 位二进制"，a=0、b=1：字母序号二进制Bacon 码字母序号二进制Bacon 码[binary] 字母版（培根 1605 原版，I/J、U/V 各占同一码）：字母码字母码字母码字母码AaaaaaGaabbaNabbaaTbaabaBaaaabHaabbbOabbabU/VbaabbCaaabaI/JabaaaPabbbaWbabaaDaaabbKabaabQabbbbXbababEaabaaLababaRbaaaaYbabbaFaababMababbSbaaabZbabbb关键差异：24 版从 I/J 合并处起，后续每个字母的码值都比 26 版"错位"。例如 26 版 K=ababa，24 版 K=abaab。CTF 解题时若一版解出乱码，务必换另一版再试。编码 / 解码示意① 明文编码（26 版，把 BACON 转成 A/B 序列）：明文 B A C O N 序号 1 0 2 14 13 ↓ ↓ ↓ ↓ ↓ 二进制 00001 00000 00010 01110 01101 ↓ ↓ ↓ ↓ ↓ Bacon aaaab aaaaa aaaba abbba abbab 拼接: aaaab aaaaa aaaba abbba abbab = aaaabaaaaaaaabaabbbaabbab (25 位 = 5×5)② 隐写解码（大小写载体，小写=a、大写=b）——这是培根密码的"杀手锏"，密文伪装成一个普通单词：载体 c r Y P T o G r a m ← "crYPToGram" 看着只是大小写怪异 大小写 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ 小 小 大 大 大 小 大 小 小 小 A/B a a b b b a b a a a └──── aab…

栅栏密码-Rail-Fence-Cipher
type: item
date: 2022-08-02T01:11:00+08:00
description: AI摘要 栅栏密码是一种置换密码，分为W型锯齿和分组矩阵转置两种。文章阐述了其原理、Python实现及CTF中的暴力破解方法。 以上内容由 AI 生成，仅供参考。概述栅栏密码（Rail Fence Cipher）是一种典型的置换密码（transposition cipher）：它不改变任何字母本身，只打乱字母的位置。这与凯撒、仿射等"替换密码"（改变字母内容）以及培根密码（隐藏于形式）都属于不同的家族。中文语境下的"栅栏密码"实际上混指两种做法，务必先分清：W 型（锯齿 / Zigzag）栅栏：把明文沿"之"字形写在 N 条轨道（rail）上，再逐行读出——这是国际标准的 Rail Fence Cipher。分组（栏栅）栅栏：把明文每 N 个字符切成一组，再按"列"的顺序取字重排——本质是矩阵转置，中文 CTF 工具里最常见。本质: 密文是明文的一个「位置置换」π，字母集合完全不变 c[i] = p[π(i)] 解密 = 逆置换 π⁻¹ W 型 明文沿 N 条轨道锯齿排列 → 逐行读 (周期 2(N-1)) 分组 明文按 N 字一组填入矩阵 → 逐列读 (等价矩阵转置) 密钥 W 型: 轨道数 rails ∈ [2, L-1] 分组: 每组字数 key（多取 L 的因子）编码原理置换而非替换：为什么"字母没变，却读不通"栅栏密码的加密可以形式化为对下标集合 $\{0,1,\dots,L-1\}$ 的一个置换 $\pi$：密文第 $i$ 位等于明文第 $\pi(i)$ 位。解密只需应用逆置换 $\pi^{-1}$。因为置换是双射，解密天然唯一——这一点与仿射密码"必须 $\gcd(a,26)=1$ 才可逆"形成对照：置换密码的可逆性由结构保证，无需额外条件。三大古典密码家族的根本差异：家族代表操作对象字母本身单字母频率替换凯撒 / 仿射字母的值改变整体平移，分布形状保留置换栅栏 / 列置换字母的位置不变完全不变隐写培根字母的形式承载于载体——这正是识别置换密码的关键指纹：密文的单字母频率和正常英文几乎一致，读起来却毫无意义。因为只是换位，E 还是那么多、T 还是那么多——频率分析能一眼看出"这是置换而非替换"，但单字母频率对求解密钥毫无帮助，真正被破坏的是双字母 / N-gram 的相邻关系。W 型栅栏：锯齿排列与周期设轨道数为 $N$。字符在轨道间上下往返，形成周期为 $\text{cycle} = 2(N-1)$ 的三角波。第 $i$ 个字符所在的轨道号可由下式直接算出：$$ r = i \bmod 2(N-1), \qquad \text{rail}(i) = \min(r,\ 2(N-1) - r) $$以明文 FLAGISHERE、$N=3$（$\text{cycle}=4$）为例： 0 1 2 3 4 5 6 7 8 9 ← 明文位置 rail0: F . . . I . . . R . rail1: . L . G . S . E . E rail2: . . A . . . H . . . 逐行读出 → [FIR] [LGSEE] [AH] = FIRLGSEEAH轨道数越多，锯齿越"深"，相邻字母被拉得越开。但密钥空间仅 $N \in [2, L-1]$，即 $L-2$ 种，暴力枚举是平凡的。分组栅栏：矩阵转置视角分组栅栏等价于：把明文按行填进一个 列数 = key 的矩阵，再按列读出——也就是矩阵转置。以明文 WELCOMETOCTF、$key=3$ 为例：按行填入 (每行 key=3): 按列读出: W E L 列0: W C E C C O M → 列1: E O T T E T O 列2: L M O F C T F 密文: WCECEOTTLMOF解密时行列互换即可还原。它的密钥（每组字数 $key$）通常取明文长度 $L$ 的因子，否则末组不齐，需引入填充约定——因此分组栅栏的可用密钥比 W 型更少，破解更快。Python 完整实现W 型栅栏加解密def rail_fence_encrypt(text: str, rails: int) -> str: """W 型栅栏加密：沿 rails 条轨道锯齿排列后逐行读出""" if rails < 2: return text fence = [[] for _ in range(rails)] rail, step = 0, 1 for ch in text: fence[rail].append(ch) if rail == 0: step = 1 # 触底折返向下 elif rail == rails - 1: step = -1 # 触顶折返向上 rail += step return ''.join(''.join(row) for row i…

仿射密码-Affine-Cipher
type: item
date: 2022-07-31T15:20:00+08:00
description: AI摘要 仿射密码是基于模运算的单表替换密码，加密公式为y=(ax+b) mod 26。文章阐述了其数学原理（a需与26互质）、Python实现（含加密解密及爆破函数），并探讨了CTF中的已知明文攻击与频率分析破解方法。 以上内容由 AI 生成，仅供参考。概述仿射密码（Affine Cipher）是一种基于模运算的单表替换密码。其加密函数为线性变换加偏移，解密依赖模逆元。在 CTF 中常以"已知部分明文（如 flag 头）→ 爆破参数 a、b → 还原完整明文"的形式出现。加密: y = (a·x + b) mod m 解密: x = a⁻¹·(y - b) mod m x = 明文字母序号 (0~25) y = 密文字母序号 (0~25) m = 字母表大小 (26) a, b = 密钥 (a 必须与 m 互质)数学原理为什么 a 必须与 m 互质如果要解密唯一，加密必须是双射（一一对应）。加密函数 $E(x) = (ax + b) \bmod m$ 是双射的充分必要条件是 $\gcd(a, m) = 1$。证明：若 $\gcd(a, m) = d > 1$，则存在 $x_1 \neq x_2$ 使得：$$ ax_1 \equiv ax_2 \pmod{m} $$即 $a(x_1 - x_2) \equiv 0 \pmod{m}$。由于 $d \mid a$ 且 $d \mid m$，可取 $x_1 - x_2 = m/d < m$，使得 $a \cdot (m/d) \equiv 0 \pmod{m}$。此时 $E(x_1) = E(x_2)$，加密不再是一对一。$\gcd(a, 26)$a 的取值范围数量解密可行性11, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 2512唯一可逆> 1其余 14 个数14映射多对一，解密不唯一模逆元与解密解密需要求 $a$ 在模 $m$ 下的逆元 $a^{-1}$，满足：$$ a \cdot a^{-1} \equiv 1 \pmod{m} $$对于 $m=26$，各有效 $a$ 的逆元：| a | 1 | 3 | 5 | 7 | 9 | 11 | 15 | 17 | 19 | 21 | 23 | 25 || :-------: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: || a⁻¹ | 1 | 9 | 21 | 15 | 3 | 19 | 7 | 23 | 11 | 5 | 17 | 25 |# 计算模逆元（扩展欧几里得算法） def mod_inverse(a: int, m: int = 26) -> int: """返回 a 在模 m 下的逆元，若不互质则返回 None""" def egcd(a, b): if b == 0: return a, 1, 0 g, x1, y1 = egcd(b, a % b) return g, y1, x1 - (a // b) * y1 g, x, _ = egcd(a, m) if g != 1: return None return x % m # 示例 print(mod_inverse(5, 26)) # 输出: 21 print(mod_inverse(20, 26)) # 输出: None (gcd(20,26)=2)加密/解密示意以 a=5, b=8 加密 "HELLO" 为例 明文 H → 7 E → 4 L → 11 L → 11 O → 14 ↓ ↓ ↓ ↓ ↓ 加密 y=(5·7+8)%26 (5·4+8)%26 (5·11+8)%26 (5·11+8)%26 (5·14+8)%26 =43%26=17 =28%26=2 =63%26=11 =63%26=11 =78%26=0 ↓ ↓ ↓ ↓ ↓ 密文 R → 17 C → 2 L → 11 L → 11 A → 0 结果: "RCLLA"Python 完整实现加密类import math class AffineCipher: def __init__(self, a: int, b: int, m: int = 26): if math.gcd(a, m) != 1: raise ValueError( f'a={a} 与 m={m} 不互质 (gcd={math.gcd(a,m)})，' f'解密不唯一，请使用以下合法值之一: ' f'{[x for x in range(1,m) if math.gcd(x,m)==1]}' ) self.a = a self.b = b self.m = m self.a_inv = self.…

RSA密码算法原理与CTF攻击
type: item
date: 2022-07-15T15:54:00+08:00
description: AI摘要 本文解析了RSA算法原理，并详细列举了CTF中常见的RSA攻击方式，包括N分解、低加密指数、广播攻击、Wiener攻击及共模攻击等，附带代码实现。 以上内容由 AI 生成，仅供参考。算法核心选择两个大素数 $p, q$，令 $n = p \cdot q$，欧拉函数 $\varphi(n) = (p-1)(q-1)$。选取 $e$ 满足 $\gcd(e, \varphi(n)) = 1$，计算 $d \equiv e^{-1} \pmod{\varphi(n)}$。公钥：$(n, e)$私钥：$(n, d)$加密：$c \equiv m^e \pmod{n}$解密：$m \equiv c^d \pmod{n}$正确性证明：由 $ed \equiv 1 \pmod{\varphi(n)}$，存在整数 $k$ 使得 $ed = k\varphi(n) + 1$。$$ c^d \equiv (m^e)^d \equiv m^{ed} \equiv m^{k\varphi(n) + 1} \equiv (m^{\varphi(n)})^k \cdot m \pmod{n} $$根据欧拉定理，当 $\gcd(m, n) = 1$ 时 $m^{\varphi(n)} \equiv 1 \pmod{n}$，代入得 $c^d \equiv m \pmod{n}$。若 $\gcd(m, n) \neq 1$（即 $m$ 是 $p$ 或 $q$ 的倍数），可通过中国剩余定理分别验证模 $p$ 和模 $q$ 下成立，RSA 解密在该情况下仍然正确1。欧拉函数 $\varphi(n)$： 小于 $n$ 且与 $n$ 互质的正整数个数。积性：$\gcd(a, b) = 1$ 时 $\varphi(ab) = \varphi(a)\varphi(b)$。若 $p$ 为素数，$\varphi(p) = p - 1$由积性：$\varphi(pq) = (p-1)(q-1)$对于素数幂 $p^r$：$\varphi(p^r) = p^r - p^{r-1}$（区间 $[1, p^r]$ 中仅 $p, 2p, \dots, p^{r-1}p$ 共 $p^{r-1}$ 个数与 $p^r$ 不互素）几个 $\varphi(n)$ 的变形：情形$\varphi(n)$标准$n = pq$$(p-1)(q-1)$$n = p^r$$p^r - p^{r-1}$$n = pqr$（三素数）$(p-1)(q-1)(r-1)$模逆元： $d$ 满足 $e \cdot d \equiv 1 \pmod{\varphi(n)}$。可解性的充要条件是 $\gcd(e, \varphi(n)) = 1$——这正是密钥生成时选择 $e$ 的约束。求解使用扩展欧几里得算法（Extended Euclidean Algorithm），该算法在求出 $\gcd$ 的同时返回 Bézout 系数。d = libnum.invmod(e, phi_n) # libnum d = int(gmpy2.invert(e, phi_n)) # gmpy2 d = pow(e, -1, phi_n) # Python 3.8+基础加解密模板：import libnum p = libnum.generate_prime(1024) q = libnum.generate_prime(1024) m = libnum.s2n('flag{test}') n = p * q phi_n = (p - 1) * (q - 1) d = libnum.invmod(e, phi_n) c = pow(m, e, n) m = pow(c, d, n) print(libnum.n2s(m))N 的分解当 $p, q$ 选择不当，$n$ 可被分解，RSA 安全性直接瓦解。在线查询： factordb.com，常用的大数可能已有记录。yafu 本地分解：yafu-x64.exe factor(123456789) yafu-x64.exe "factor(@)" -batchfile n.txt$p, q$ 过于接近 — 费马分解：对于任意奇数 $n = pq$，可表示为平方差：$$ n = pq = \left(\frac{p+q}{2}\right)^2 - \left(\frac{p-q}{2}\right)^2 = a^2 - b^2 $$其中 $a = \frac{p+q}{2}$，$b = \frac{p-q}{2}$。而 $a = \sqrt{n + b^2} \ge \sqrt{n}$。当 $p, q$ 接近时，$b$ 很小，$a \approx \sqrt{n}$。从 $a = \lce…