1、加载恶意代码
- 直接加载 or 附加到进程
- 命令选项运行恶意代码
- 支持执行DLL中某个函数
- 打开一个可执行文件
- file->open:设置arguments可以在运行期间传入参数
- 默认在WinMain(程序入口点)停止执行,可以使用Options->Debugging Options修改启动选项(例:选择System Breakpoint在代码执行前立刻中断)
- 附加调试器到一个运行程序
- file->attach
- 进程当前执行线程的代码会被暂停
2、OllyDbg的接口
- 反汇编面板窗口:显示被调试程序的代码,修改按空格键
- 寄存器面板窗口:显示寄存器当前状态,修改处显示为红色
- 栈面板窗口:显示被调试线程堆栈在内存中的当前状态,总是显示给定线程的栈顶,栈单元上会显示一些有用的注释
- 内存转储面板窗口:显示被调试进程的实时内存转储,按Gtrl+G输入内存位置可以跳转到内存任意地址【也可以单击一个内存地址,选择Follow in Dump来转储那个内存地址】,想编辑这个窗口中的内存:右键Binary->Edit,也可以修改恶意代码存储在RAM中的全局变量及其他数据。
3、内存映射(View->Memory)
- 显示被调试程序分配的所有内存块
- 可以右击一段内存转储,选择View in Disassemble,将数据发送到反汇编窗口
- 基地址重定位
- 指Windows中一个模块没有被加载到其预定基地址时发生的情况
- 基地址
- 映像基地址,允许与被加载到内存中的实际地址不一致,大部分情况下是一致的,大部分执行程序都被预加载到0x00400000(windows平台下大多数编译器使用的默认地址)
- 开发者可以更改基地址,支持地址空间布局随机化(ASLR)安全增强特性的可执行程序
- 绝对地址与相对地址
- 使用绝对地址访问内存容易出错,多数DLL会在PE头的.reloc节打包一个修订位置的列表
- DLL在.exe载入后以任意顺序加载,基地址重定位后难以预测其在内存中定位的位置
- 在所有DLL被编译时,编译器会为他们选择一个默认的基地址,一般都会重定向
- 静态分析时不知道被重定向
4、查看线程和堆栈
- View->Threads:显示线程内存位置以及它们当前的活动状态
- 单击主工具栏的暂停按钮可以暂停所有活动的线程
- 线程的重要数据都保存在栈上,可以使用内存映射查看内存中栈的内容
5、执行代码
- Run&Pause:开始或暂停一个程序,但Pause很少用,因为可能会暂停到无用的位置,通常设断点暂停
- Run to Selection:在到达选择的指令之前一直运行,如果选择的指令不被执行,则被调试程序会一直运行下去。
- Execute till Return:在当前函数返回时暂停执行
- 调试时如果恶意代码程序执行迷失在代码库中,使用Debug->Execute till User Code,使被调试程序返回到编译的恶意代码中
- 单步跟踪
- 单步进入:F7
- 单步跳过:F8(要警惕函数没有ret的情况)
6、断点
- 默认使用软件断点
- 添加移除断点:F2
- **View->BreakPoints:看到一个程序中活跃的断点
- 软件断点:
- 在调试字符串解码函数时非常有用(混淆字符串->解码),在解码结束的位置处下断点
- 条件断点:
- 使用表达式设置断点
- 断点命中时,计算表达式的值,值不等于零断点生效程序运行中断
- 具体应用:在函数受到特定参数时断下
- 实例:探测Poison Ivy特定大小的内存分配情况,当接收shellcode时分配的内存大
- 分配内存大小为0x29,ESP寄存器存放栈顶指针,为了获取分配内存的大小,需要使用[ESP+8]引用内存(??)
- 设置条件断点Breakpoint->Conditional
- 硬件断点
- OD提供利用专门硬件寄存器设置硬件断点
- 硬件断点可以在不改变代码、堆栈以及任何目标资源的前提下进行调试(可以深究!!);但只能同时设置四个断点
- 使用硬件断点可以防御反调试技术(软件断点扫描)
- 内存断点
- 在内存块上设置,可以让被调试程序在访问这段内存时中断执行,还可以判断此内存块的权限
- 一次只能设置一个内存断点
- 应用:判断恶意代码何时使用了某个加载的dll
- 打开内存映射面板窗口,右键单击需要跟踪的dll的.text段
- 选择Set Memory Breakpoint on Access
- F9恢复运行
7、加载DLL
- OD使用loaddll.exe加载dll运行,一旦被加载,在DllMain中断
- 用参数调用被调试DLL中的导出函数:OD加载,运行到dll初始化完成,主菜单中选择Debug->Call Dll Export,使用参数调用dll导出的具体函数
- Hide on call:函数调用后隐藏窗口
- Follow in Disassembler:查看函数汇编代码
- Pause after call:代替断点
8、跟踪
- 标准回溯跟踪
- Step into&Step over:-可以回退操作
- 堆栈调用跟踪
- 查看给定函数的执行路径
- View->Call Stack:显示当前位置之前的调用序列
- 运行跟踪
- 在运行代码时,OD会保存所有运行过的指令以及运行过程中对寄存器和标志做的改变
- 使用方法:
- 反汇编窗口中高亮要跟踪的代码。右键单击选择Run Trace->Add Selection。代码运行后选择View->Run Trace查看运行过的指令,- +上下浏览
- 使用Trace Into & Trace Over
- Trace Into:单步执行并记录断点命中前所有指令
- Trace Over:仅记录当前正在运行的函数指令
- 例:跟踪Poison Ivy
- 当EIP指向Poison Ivy为shellcode指向的堆时,可以使用跟踪来捕获shellcode的运行
- 设置当EIP小于一个典型的映像位置时(0x400000,低于这个值是一些简单程序中的栈、堆和动态分配内存存放的位置),OD暂停。因为在正常程序运行中EIP不应该指向这些位置。
- 选择Trace Into使在shellcode运行之前整个程序被跟踪
9、异常处理
- 默认情况下产生异常时附加程序会停止运行,此时调试器开始接管控制权
- 调试器可以处理该异常们也可以将异常转到被调试的应用程序处理
- Shift+F7:进入异常
- Shift+F8:跳过异常
- Shift+F9:运行异常处理
- Debugging Options:告诉调试器忽略某些特定异常,并且将它们直接转到应用程序处理
10、修补
- 修改实时数据(如寄存器和标志),也可以将汇编形式的修补代码直接插入到一个程序,可以通过高亮选择某块区域来修改指令或内存
- 右键选择区域:Binary->Edit(可以使用00或NOP填充,JNZ时可以避免跳转,改变执行流程)
- 应用这种改变:右击反汇编面板窗口需要修补的代码,选择Copy to executable->All Modification->Save File保存
11、分析shellcode
- 将shellcode从一个十六进制编辑器复制到剪切板
- 在内存映射窗口中,选择类型为Priv的内存区域(分配给进程的私有内存,这个内存被多个进程共享)
- 双击内存映射窗口的某行,会弹出一个十六进制转储窗口,可以检查它的内容。
- 在内存映射面板窗口中右击被选择区域,选择Set Access->Full Access,赋予该区域读、写、运行的权限
- 返回内存转储窗口。0字节填充的高亮区域足以容纳整个shellcode,右键单击选择的内存区域,选择Binary->Binary Paste将复制的shellcode粘贴到选择的区域。
- 设置EIP寄存器,指向你修改的内存区域。(右键反汇编面板窗口的一条指令,选择New Origin Here设置EIP寄存器的值)
- 运行,调试和单步整个shellcode
12、协助功能
- 日志(Logging):View->Log 显示了加载的可执行模块、触发的断点等,状态记录
- 监视(Watches)窗口:查看生成表达式的值(??),按空格可以设置表达式
- 标注(Labeling):右键->Lable
13、插件
- 将插件dll放在安装根目录,OD自动识别
- OllyDump
- 将一个被调试的进程转储为一个PE文件
- 加载可执行文件时会尝试逆向这个进程(脱壳应用)
- 调试器隐藏插件
- 对探测者隐藏调试器的存在
- 防止恶意代码使用反调试技术
- 命令行
- Plugins->Command Line->Command Line
- Plugins->Command Line->Command Line
- 书签
- 将一个内存位置加到书签中
- 添加书签:Bookmark->Insert Bookmark
- 浏览书签:Plugins->Bookmarks->Bookmarks
14、脚本调试
- 需要扩展调试功能时,采用ImmDbg,使用Python脚本来扩展功能,有易于使用的API接口
- PyCommand:ImmDbg最常见的脚本类型,编写好脚本后放在ImmDbg安装目录下的PyCommands\目录,脚本从命令行运行,并且需要加上前缀!(例:!list:列出可用的PyCommand列表)
- 导入模块:通过immlib或immutils模块访问ImmDbg的功能
- 主函数:用来读取命令行参数(Python列表传递)
- PyCommand代码实现
- 返回包含字符串的值,脚本运行结束,主调试器会用这个返回字符串更新状态栏