实验目的
- 理解计算机运行流程
- 理解汇编语言代码所代表的含义
- 会使用 IDA 看流程图
- 会使用 OllyICE 跟踪程序运行流程
- 学会基础的口令破解
实验环境:Windows 11 主机 + VMware 17 虚拟机(Windows XP)+ IDA + OllyICE
之所以要在 XP 虚拟机里跑,是因为 OllyICE 这类老调试器在新系统上兼容性很差。
第一步:用 IDA 摸清程序结构
把程序拖进 IDA,让它自动分析。
从流程图能看出来:程序首先判断密码长度。
校验的目标长度是 0x12,也就是十进制的 18 位。
这一步很关键——如果长度不对,程序会直接退出, 根本走不到后面的比对逻辑。所以我们得先输入一个 18 位的字符串才能继续。
继续往下看,找到负责校验的函数 sub_401090。双击进去,能看到完整的流程图。
这是一个循环:
- 第一个流程框把输入密码的某个字节放进寄存器
- 第二个流程框拿它和"正确密码"的对应字节比较
- 相等就继续循环;18 位全部通过才算成功
- 任何一位不对就跳出循环,提示密码错误
记下循环的起始地址:00401090。 下面要在 OllyICE 里用它下断点。
第二步:用 OllyICE 动态跟踪
打开 OllyICE,把程序载入。
下断点:
右键 → 转到 → 表达式 → 输入 00401090
按 F2 设置断点
F9 运行程序,随便输入 18 位字符,回车。
程序会在断点处停下。按 F8 单步执行,一步步走进循环内部。
关键指令逐条分析
走到循环里之后,会看到这样几条指令(这里只列我真正看懂的部分):
xor al, al ; al 清零,作为"当前处理到第几位"的计数器
movsx ecx, al ; 把 al 的值放进 ecx,此时 ecx = 0
mov dl, byte ptr [ecx+esi] ; 取「我输入的密码」的第 ecx 个字节 → dl
mov bl, byte ptr [ecx+407098] ; 取「正确密码」的第 ecx 个字节 → bl
这几行透露了两个极其重要的信息:
esi指向的是我们输入的字符串在内存中的地址 (可以在内存窗口右键 → 转到,定位到它)- 正确密码就存放在内存地址
407098处
于是直接去内存窗口:
右键 → 转到 → 表达式 → 输入 407098
一眼就能看到那个硬编码的字符串。这是整个实验的突破口。
接下来的比较逻辑
xor dl, al ; 把输入字节和 al 异或
cmp bl, dl ; 和正确密码的字节比较
jnz short 004010B5 ; 不相等 → 跳去"密码错误"分支
然后是循环控制:
inc al ; 计数器 +1
cmp al, 12 ; 和 0x12(18)比较
jl short 00401098 ; 小于就跳回循环开头
00401098 正是循环体的第一行,这就闭合了整个循环。
核心:这不是明文比较
这里是整个实验最容易掉进去的坑。
看这两行的顺序:
mov dl, byte ptr [ecx+esi] ; dl = 我输入的字节
mov bl, byte ptr [ecx+407098] ; bl = 内存里存的字节
xor dl, al ; ← 输入的字节先被异或了
cmp bl, dl ; 才做比较
比较之前,输入的字节会被异或上当前的下标 al。
所以:
- 内存
407098处存着的 并不是你该输入的密码,而是异或加密后的形式 - 真正要输入的每一位是:内存里的字节 ⊕ 当前下标
也就是:
明文[i] = 密文[i] ^ i
📌 说明:原始报告中记录了课程程序里那个具体的硬编码字符串, 但该字符串含有可识别院系的缩写,为避免暴露就读院校,这里不列出原值。 下面用一个自造的例子说明算法,原理完全一致。
假设内存里挖出来的密文是 ABC(对应下标 0、1、2),那么:
| i | 内存字节 | ASCII | ⊕ i | 结果 | 应输入 |
|---|---|---|---|---|---|
| 0 | A |
0x41 | 0x41 ^ 0 = 0x41 | A |
A |
| 1 | B |
0x42 | 0x42 ^ 1 = 0x43 | C |
C |
| 2 | C |
0x43 | 0x43 ^ 2 = 0x41 | A |
A |
所以该输入的是 ACA。
用同样的方式处理那 18 个字节,就能算出真正要输入的口令。
其实不用手算,写三行代码就出来了:
cipher = [0x41, 0x42, 0x43] # 从内存 407098 处抄下来的字节
plain = ''.join(chr(c ^ i) for i, c in enumerate(cipher))
print(plain)
把算出来的 18 位字符串输入程序,回车——跳出循环,口令破解成功。
实验总结
这次实验的难点不在工具,而在读懂每一条指令在干什么,以及最后那一步异或运算的理解。
真正让我卡住的地方是:一开始我以为内存里那个字符串就是答案, 直接抄下来输进去,结果当然是错的。
看懂 xor dl, al 出现在 cmp 之前,是这个实验的分水岭。
由此也得到一个更普遍的认知:
程序不会明文保存密码。 教材级的例子用"异或下标",真实系统用哈希加盐, 但思路是一样的——你看到的存储值,通常不是你能直接用的值。
另外几点收获:
- IDA 适合静态地看清程序结构(流程图、函数调用关系、循环边界)
- OllyICE 适合动态地看数据流动(寄存器变化、内存内容、断点命中)
- 两者配合,比只用其中一个效率高得多
📌 本文由大学时期的课程实验报告整理而来,已移除姓名、学号、班级、指导教师等个人信息。 原始报告中的截图因可能包含个人环境信息(文件路径、用户名)未一并收录。
