不完美笔记
首页项目归档照片墙音乐说说杂谈友链关于
封面

计算机原理实验:用 IDA + OllyICE 破解一个登录校验

写作时间:2026-09-29 11:05:00
# 计算机原理
# 逆向
# OllyICE
# IDA
# 口令破解
# 实验笔记

实验目的

  • 理解计算机运行流程
  • 理解汇编语言代码所代表的含义
  • 会使用 IDA 看流程图
  • 会使用 OllyICE 跟踪程序运行流程
  • 学会基础的口令破解

实验环境:Windows 11 主机 + VMware 17 虚拟机(Windows XP)+ IDA + OllyICE

之所以要在 XP 虚拟机里跑,是因为 OllyICE 这类老调试器在新系统上兼容性很差。


第一步:用 IDA 摸清程序结构

把程序拖进 IDA,让它自动分析。

从流程图能看出来:程序首先判断密码长度。 校验的目标长度是 0x12,也就是十进制的 18 位。

这一步很关键——如果长度不对,程序会直接退出, 根本走不到后面的比对逻辑。所以我们得先输入一个 18 位的字符串才能继续。

继续往下看,找到负责校验的函数 sub_401090。双击进去,能看到完整的流程图。

这是一个循环:

  1. 第一个流程框把输入密码的某个字节放进寄存器
  2. 第二个流程框拿它和"正确密码"的对应字节比较
  3. 相等就继续循环;18 位全部通过才算成功
  4. 任何一位不对就跳出循环,提示密码错误

记下循环的起始地址: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

这几行透露了两个极其重要的信息:

  1. esi 指向的是我们输入的字符串在内存中的地址 (可以在内存窗口右键 → 转到,定位到它)
  2. 正确密码就存放在内存地址 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 适合动态地看数据流动(寄存器变化、内存内容、断点命中)
  • 两者配合,比只用其中一个效率高得多

📌 本文由大学时期的课程实验报告整理而来,已移除姓名、学号、班级、指导教师等个人信息。 原始报告中的截图因可能包含个人环境信息(文件路径、用户名)未一并收录。

avatar

Winter21c

暗处提灯,静处守门;功成不必在我,安宁自在人间

RECOMMENDED

我是如何修改她的 blog 的

2026-09-29 10:45:00

红米 K20 Pro 的 Arch Linux 移植手记

2026-10-02 17:25:00

计算机原理作业:直接映射高速缓存的两道题

2026-09-29 11:15:00

Table of Contents