前置知识
- 了解内存地址、寄存器、栈
- 了解 x86 的寻址方式
- 学习理解重要的汇编指令(可在逆向过程中学习)
逆向分析技术
静态分析技术
所谓静态分析技术,就是将程序反编译成汇编代码,手动对程序的逻辑结构、各模块的运行方式等进行分析。常用静态分析工具有 IDA 等。动态分析技术
通过静态分析可以了解各个模块的功能,以及整个软件的编程思路,但是不能真正了解软件各模块的技术细节。所以对软件分析来说,静态分析只是第一步,动态跟踪才是分析软件的关键。所谓动态分析就是利用 OllyDbg 、X64dbg、WinDbg 等工具将程序反编译,并一步一步地执行汇编指令,跟踪分析程序的运行原理及过程。
OllyDdbg 调试器
OllDbg(简称 “OD”) 是由 Oleh Yuschuk 编写的一款结合了动态调试和静态分析且具有可视化界面的用户调试器。
- 1:汇编代码对应的地址窗口
- 2:汇编代码对应的十六进制对应的机器码窗口
- 3:反汇编窗口
- 4:反汇编对应的注释信息窗口
- 5:寄存器信息窗口
- 6:当前执行到的反汇编代码的信息窗口
- 7~9:数据所在的内存地址,十六进制,ACSII码
- 10~12:栈地址、存放的数据、对应的说明信息
OD 基本快捷键及功能
快捷键 | 功能描述 |
---|---|
F2 | 下断点(指定断点地址) |
F3 | 加载一个可执行程序,进行调试分析 |
F4 | 程序执行到光标处 |
F5 | 缩小、还原当前窗口 |
F7 | 单步步入 |
F8 | 单步步过 |
F9 | 直接运行程序,遇到断点处,程序暂停 |
Ctrl+F2 | 重新运行程序到起始处,一般用于重新调试程序 |
Ctrl+F9 | 执行到函数返回处,用于跳出函数实现 |
Alt+F9 | 执行到用户代码处,用于快速跳出系统函数 |
Ctrl+G | 输入十六进制地址,快速定位到该地址处 |
Alt+b | 打开断点窗口(用户修改当前所设置的断点) |
F7 和 F8 的区别:遇到 call、loop等指令,按 “F7” 键调试器会跟进到函数内部单步执行,按 “F8” 键调试器会执行到直接执行完整个函数并跳到下一跳指令。
逆向一般有两种方式分别是爆破法、算法分析。
- 爆破法:通过反编译调试,锁定程序的关键跳转判断语句,通过修改该判断语句,使程序成功破解。
- 算法分析:通过反编译调试,详细分析程序的密钥算法,最后写出相应的秘钥或破解程序。
TraceMe 破解(爆破法)
- 按 “F3” 打开 TraceMe.exe 程序。
打开程序时,OllDbg 默认会指定到程序入口点。
- 按 “Ctrl+G” 输入函数名称,跳转到该函数入口点。
- 按 “F2” 设置断点,按 “Alt+b” 查看当前设置的断点。
- 按 “Ctrl+F2” 重新加载运行程序。
- 按 “F9” 运行程序到断点处,并输入任意用户名密码。
- 点击 “check” 程序进入 GetDlgltemTextA 函数入口。
- 一直按 “F8” 单步步过执行指令,找到 GetDlgltemTextA 函数获取到用户名的位置。
- 执行到 “test” 判断指令
- “F8” 继续执行 “je short 0040122E”,将标志位 “Z” 的值双击取反。
- 继续按 “F8” 执行程序发现,程序注册成功。
- 将跳转语句 “je short 0040122E” 填充为 nop
- 另存文件
右击 –> 复制到可执行文件 –> 选择 –> 在窗口中右击 –> 备份 –> 保存数据到文件
- 最后输入任意用户名,密码,程序破解成功。
CRACKME 破解(爆破)
- 找 GetMessage 函数入口
- 重新加载程序,并按 “F9” 运行程序到断点处
- 按 “f8” 运行程序,直到窗口有提示
- 在光标处 “call 0040137E” ,设置断点。然后禁用这个断点(主要是为了做标记)
- 重新加载程序,并按 “F7” 到 “call 0040137E” 查看子程序的内容。
- 经过多次尝试,发现这个子程序是用来判断用户名中是否有数字,如果有就输出失败的结果。
- 将用户名全部换成字母,再次进行调试,程序可以继续向下执行。
- 查看下面的跳转 和 call 子程序的内容
发现只要 “je short 0040124C” 跳转成功,程序就会注册正确。JE:等于则跳转,也就是“ZF” 标志位为 1,就会跳过。ZF(zero flag):零标志位。判断结果是不是0。如果结果为0,就置1;不为0,就置0。
- 将 “je short 0040124C” 改为 “jmp short 0040124C”
- 保存,并测试运行文件
技术总结
windows 消息循环机制
消息:事件可分为几种,由输入设备触发的,比如鼠标键盘等等。由窗体控件触发的,比如 button 控件,file 菜单等。还有就是来自 Windows 内部的事件。这三种称为事件。而消息,是由事件翻译而来的。事件产生消息。
消息队列:消息队列有两种,分为系统消息队列和应用程序消息队列。产生的消息首先由 Windows 系统捕获,放在系统消息队列,再拷贝到对应的应用程序消息队列。32/64 位系统为每一个应用程序维护一个消息队列。
消息循环:系统为每个应用程序维护一个消息循环,消息循环会不断检索自身的消息队列。每有一个消息,就用 GetMessage() 取出消息。
GetMessage 具有阻塞机制。当消息队列中没有消息时,程序非忙等,程序会一直停留在 GetMessage 内部,由此减少 CPU 的消耗。当收到 WM_QUIT 时,GetMessage 返回 false,循环停止,同时应用程序终止。
消息处理:DispatchMessage() 把取出来的消息分配给相应的窗口或线程,由窗口过程处理函数 DefWindowProc() 处理。
读取文本框内容的 API 函数:
16 位 | 32 位(ANSI 版) | 32 位(Unicode 版) |
---|---|---|
GetDlgltemText | GetDlgItemTextA | GetDlgItemTextW |
GetWindowsText | GetWindowsTextA | GetWindowsTextW |
在一般情况下,是不知道程序调用了什么函数来处理字符,因此,只能多试几次找出相关函数。
逆向中常见的指令
比较运算符
test指令:test 对两个参数进行 and 逻辑操作(1与0结果为0,1与1结果为1,0与0结果为0)。通常用来判断某个位是否为 0,或者判断寄存器的值是否为空(例如:
test eax, eax
)cmp 指令:cmp 对两个参数进行相减,通常用来判断两个值是否相等。
xor 指令:xor 对两个参数进行异或操作(1 xor 1=0,0 xor 0=0,1 xor 0=1,0 xor 1=1),通常用来将两个寄存器的值置 0,方便后续使用该寄存器(例如:
xor eax,eax
)。
跳转指令
- jmp 指令:无条件跳转(在修改程序时,可使用 jmp 指令进行强制跳转)
- je(jump When Equal) 指令:两个值相等就跳转,也就是 ZF 标志位等于 1 就跳转
- jnz(jump no Zero)/jne(jump no Equals) 指令:两个值不相等就跳转,也就是 ZF 标志位等于 0 就跳转。
- jb(jump not above and equal):低于且不等于就跳转,同常用在无符号数计算。也就是说 CF 等于 1 就跳转。
- jl(jump less):低于且不等于就跳转,同常用在有符号数计算。也就是说 CF 等于 1 就跳转。
其他
- lea 指令:取地址指令,主要用于动态内存定位
爆破操作流程
- 找到程序文本框入口 “GetDlgItemTextA” ,并使用 “F2” 进行软件断点。
- 使用 “F9” 运行程序到断点处,输入任意用户名和注册码,正常运行程序。
- 按 “F8” 依次执行汇编指令直到程序出错,在报错位置附近找到相应的判断、跳转指令,并在跳转指令处设置断点。如果程序在 call 指令中报错,需要使用 “F7” 进入 call 子程序,查看相应的跳转和报错。
- 按 “Ctrl+F2” 重新加载程序,按 “F9” 再次运行程序到断点处,并修改跳转指令相应的标志位,使程序以正确的方式运行。然后继续按步骤 3 继续执行程序。
- 重复上面的步骤 3 和 步骤 4 直到程序注册成功。
“Alt+b” 可以用来修改断点,可将之前的断点禁用或删除,也可以用来做地址标记。
点击汇编代码窗口, “Ctrl+G” 匹配查找 API 函数。
reverseMe 分析(算法分析)
- 先用爆破了解程序的结构
加载程序,一直按 “F8” 使程序发生错误。
稍微往上可以看到 “cmp” 和 “jnz” 跳转指令,认真看一下可以发现 “jnz” 不跳转就会报错 “Evaluation period out of date. Purchase new license”,所以这里一定要成功跳转。
“f2” 设置断点
“ctrl+f2” 重新加载程序,“f9” 程序运行到断点处,将标志位 ZF 设为 0,使程序成功跳转。
按 “f8” 继续向下执行,这里就不再重复操作,直接分析。“jnz” 如果不跳转,下面的“jmp” 就报错 “Keyfile is not valid. Sorry.”
所以这里阿静 ZF 位设为 0,使程序成功跳转。
最后测试在 “jl” 跳转后报错 “Keyfile is not valid. Sorry.”
修改 sf 标志位为 0 ,是程序成功跳转。
执行到最后程序完成爆破
- 对程序的算法结构进行分析
- 调用API Createfile 判断是否创建了 Keyfile.dat,如果没有报错“Evaluation period out of date. Purchase new license”
反汇编窗口最右边的注释窗口中,红色的字符串表示一个 API,要了解相应的API参数,可参考 win32 API 大全。
- 调用API Readfile 判断文件是否可读,如果不可读报错 “Evaluation period out of date. Purchase new license”
- 判断读的字节小于 “0x10” 个,就报错 “Keyfile is not valid. Sorry.”
前面的 xor ebx,ebx 和 xor esi,esi 的意思是将 ebx 和 esi 置 0,因为后面要用到这两个寄存器。
- 然后从 buffer 取得数据,进行循环判断,如果值为空就跳转,判断 esi 是否小于8,小于就报错 “Evaluation period out of date. Purchase new license”
- 如果值等于 0x47 (ebx+1);如果值等于 0x47 就不跳转(esi+1 和 ebx+1);如果不等于 0x47 就跳转(ebx+1)
最后从 ASCII 表中找到 0x47 对应的字母是 “G”,所以最后的密码文件如下:
技术总结
- 同样使用爆破法,找到程序运行的相应跳转的位置,并在附近找到程序的密钥算法部分。
- 对密钥算法部分依次执行理解,在执行过程中可以不停地修改密钥,写出满足一定条件的密钥来理解程序的算法。
- 最后写出正确的密钥。
程序领空(运行程序的内存空间):内存地址是以 40000(开始)
系统领空(运行系统dll等程序的内存空间):内存地址是以:6000、7000(开始)
OD 调试器打开程序后一直在 ntdll(系统领空),无法进入程序领空(程序能正常运行)。出现这种情况可能是插件问题,我这边将 srongOD 插件删除就好了。
- 本文标题:OD调试-基础篇
- 本文作者:9unk
- 创建时间:2020-11-03 11:00:07
- 本文链接:https://9unkk.github.io/2020/11/03/od-diao-shi-ji-chu-pian/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!