这也是对CTF2中Simplerev这道题目,关键部分的总结

一、常用字母的 ASCII 码

字符 ASCII 说明
'A' 65 大写字母起点
'Z' 90 大写字母终点
'a' 97 小写字母起点
'z' 122 小写字母终点
'0' 48 数字起点
'9' 57 数字终点

字母表长度:

  • 大写:90 - 65 + 1 = 26
  • 小写:122 - 97 + 1 = 26

二、模运算在逆向中的作用

模运算 % 的核心作用是 把任意整数映射到一个固定范围。

最常见的是 % 26,把数映射到 0 ~ 25,正好对应 26 个英文字母的序号。

1. 归一化到 0~25

  • 大写字母:ord(c) - 65 → 'A' 得 0,'Z' 得 25。
  • 小写字母:ord(c) - 97 → 'a' 得 0,'z' 得 25。

2. 循环移位

加密时经常写成:

1
new = (old + shift) % 26;

这样 old + shift 超过 25 时自动绕回 0~25。
例如凯撒密码、ROT13、仿射密码。

3. 复原成字母

得到 0~25 的序号后,必须加回起始值:

  • 变大写:+ 65
  • 变小写:+ 97

三、大小写必须分开处理

因为大写和小写的起始值不同:

类型 起始值 映射公式
大写 65 x = ord(c) - 65
小写 97 x = ord(c) - 97

逆向时,先判断原字符是大写还是小写,再用对应的起始值。
如果题目没有明确,可以两种都试,看哪种能得到有意义的结果。


四、正向加密与逆向推导

典型正向形式

1
cipher = (flag - 39 - key + 97) % 26 + 97;

其中:

  • flag 是大写字母 ASCII(65~90)
  • key 是密钥(题目中常转为小写,ASCII 97~122)
  • cipher 是小写字母 ASCII(97~122)

逆向步骤

  1. 令 t = cipher - 97(密文在小写字母表中的偏移,0~25)
  2. 移项:flag ≡ t + 39 + key - 97 (mod 26)
  3. 因为 flag = 65 + x,且 65 % 26 = 13,所以:
    1
    2
    3
    13 + x ≡ t + 39 + key - 97 (mod 26)
    x ≡ t + key + (39 - 97 - 13) (mod 26)
    x ≡ t + key + 7 (mod 26)
  4. 最终:
    1
    flag = 65 + (t + key + 7) % 26

关键:移项得到的是 flag mod 26,不是 flag 本身。必须根据原字符的字母表范围加回 65 或 97。


五、Python 与 C/C++ 负数取模差异

这是逆向含模语句时最容易踩的坑。

语言 -1 % 26 特点
Python 25 结果永远非负,符号跟除数
C / C++ -1 结果符号跟被除数,可能为负

影响

  • 原程序如果是 C/C++,(负数) % 26 可能得到负数,导致后续映射出错。
    所以 C 代码里常写成:
    1
    ((x % 26) + 26) % 26
    来强制结果为正。
  • 你在 Python 里逆向时,直接用 % 26 就会自动得到正数,无需额外处理。
  • 但分析 C 代码时,要意识到它为什么要加 +26 再 %26,这正是为了消除负数。

例子

pyre 题中:

1
num = ((input1[i] + i) % 128 + 128) % 128

这里 + 128 再 % 128 就是为了保证结果在 0~127,因为 C 里负数取模可能为负。
逆向时直接 (num - i) % 128 即可,Python 自动处理正数。


六、BUUCTF 题目例子

题目 含模语句 考点
SimpleRev str2[i] = (flag[i] - 39 - key[i] + 97) % 26 + 97 模 26 循环移位、大小写转换、移项推导
pyre num = ((input1[i] + i) % 128 + 128) % 128 模 128、负数取模、异或、pyc 逆向
reverse1 无模,纯字符替换 对比理解
reverse2 无模,字符替换 对比理解
reverse3 Base64 编码 编码识别
[GXYCTF2019]simple CPP 大量位运算 + 取模 Z3 约束求解
CrackRTF MD5 + 模运算 哈希爆破

SimpleRev 逆向要点

  • 密钥 key 在加密前转为小写。
  • 密文 text 是小写字母。
  • 原始 flag 是大写字母。
  • 枚举大写字母 'A'~'Z',代入加密公式验证,可唯一确定每个字符。
  • 或用移项公式直接计算。

pyre 逆向要点

  • 先解包 PyInstaller,修复 pyc 文件头。
  • 反编译得到 Python 源码。
  • 核心是 num = ((input1[i] + i) % 128 + 128) % 128。
  • 逆向:input1[i] = (num - i) % 128。
  • 注意 Python 的 % 自动返回正数。

七、通用逆向模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def decrypt_mod(cipher, key, base_cipher=97, base_flag=65, mod=26, const=0):
"""
cipher: 密文字符串
key: 密钥字符串(已转为小写)
base_cipher: 密文字母表起始值(小写 97)
base_flag: 原文字母表起始值(大写 65)
mod: 模数(26)
const: 移项后合并的常数(根据推导调整)
"""
flag = []
for i, c in enumerate(cipher):
t = ord(c) - base_cipher # 密文偏移
k = ord(key[i % len(key)]) # 密钥 ASCII(按题目要求)
x = (t + k + const) % mod # 核心公式
flag.append(chr(base_flag + x))
return ''.join(flag)

八、易错点总结

  1. 忘记加回起始值:% 26 只给 0~25,必须 +65 或 +97。
  2. 大小写混用:大写用 65,小写用 97,不能混。
  3. 常数合并错误:移项后常数要正确合并,并取模。
  4. Python 与 C 负数取模差异:Python 自动正,C 可能负。
  5. 密钥是否转小写:看原程序是否对 key 做了 +32 或 lower()。
  6. 多解问题:模运算可能多解,需用字母范围或已知格式筛选。

一句话:含模语句的逆向,核心就是“归一化 → 移项 → 取模 → 复原”。搞清起始值、大小写、负数取模,就能通杀一类题。

站内搜索

没有找到内容!