调试的本质:
- 想办法让被调试程序触发异常
- 触发异常后就会向DEBUG_OBJECT里发送调试事件
- 调试器接管异常的过程
一般调试器都会有软件断点,内存断点,硬件断点… 其实这些无非就是想让被调试程序触发异常再让调试器来接管。
在OD中随便一个地址按下F2后程序执行到那就会被断下来,这时调试器将会拥有被调试程序的控制权,这个过程我们称为“中断到调试器”。
当我们在OD中按下F2其实就是将那个地址的第一个机器码修改为0xCC对应的汇编就是 int 3 。
被调试进程:
- CPU检测到 INT 3 指令
- 查IDT表找到对应的中断处理函数
- CommonDispatchException
- KiDispatchException
- DbgkForwardException收集并发送调试事件
DbgkpSendApillessage (x, x)
1)参数1:消息结构 每种消息都有自己的消息结构共有7种类型
2)参数2:要不要把本进程内除了自己之外的其他线程挂起.有些消息需要把其他线程挂起,比如异常
调试器进程
- 循环判断
- 取出调试事件
- 列出信息寄存器内存
寄存器
内存
- 等待调试者处理
int 3流程图
IDT中进入3号中断
我们现在只关注3环。
如果有内核调试器你3环的调试器是没有办法调试了,我们现在是没有内核调试器的
DbgkpSendApillessage (x, x)
一旦产生异常会先将被调试进程挂起,然后向调试对象的链表插入调试事件,后面就是调试器的事了…
代码
typedef HANDLE(__stdcall *pMyOpenThread)(DWORD dwDesiredAccess, // access right
BOOL bInheritHandle, // handle inheritance option
DWORD dwThreadId // thread identifier
);
//被调试进程ID,进程句柄,OEP
DWORD dwDebuggeePID = 0;
HANDLE hDebuggeeProcess = NULL;
//被调试线程句柄
HANDLE hDebuggeeThread;
//系统断点
BOOL bIsSystemInt3 = TRUE;
//被INT 3覆盖的数据
char OriginalCode = 0;
//线程上下文
CONTEXT Context = {
0 };
BOOL WaitForUserCommand()
{
BOOL bRet = FALSE