这也是对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)
逆向步骤
- 令
t = cipher - 97(密文在小写字母表中的偏移,0~25) - 移项:
flag ≡ t + 39 + key - 97 (mod 26) - 因为
flag = 65 + x,且65 % 26 = 13,所以:1
2
313 + x ≡ t + 39 + key - 97 (mod 26)
x ≡ t + key + (39 - 97 - 13) (mod 26)
x ≡ t + key + 7 (mod 26) - 最终:
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 | def decrypt_mod(cipher, key, base_cipher=97, base_flag=65, mod=26, const=0): |
八、易错点总结
- 忘记加回起始值:
% 26只给 0~25,必须+65或+97。 - 大小写混用:大写用 65,小写用 97,不能混。
- 常数合并错误:移项后常数要正确合并,并取模。
- Python 与 C 负数取模差异:Python 自动正,C 可能负。
- 密钥是否转小写:看原程序是否对 key 做了
+32或lower()。 - 多解问题:模运算可能多解,需用字母范围或已知格式筛选。
一句话:含模语句的逆向,核心就是“归一化 → 移项 → 取模 → 复原”。搞清起始值、大小写、负数取模,就能通杀一类题。
