调试寄存器
硬件断点的原理是使用DR0~DR3设置地址,使用DR7设置状态,因此最多设置4个。
<1> L0/G0 ~ L3/G3: L0/G0对应DR0,L1/G1对应DR1,以此类推…,用于控制Dr0~Dr3是否有效,Lx是局部的仅影响当前线程,Gx全局的影响当前进程中所有线程。
每次异常后,Lx都被清零,Gx不清零。
<2> 断点长度(LENx):x的下标为几就对应那个控制寄存器,00(1字节) 01(2字节) 11(4字节)
<3> 断点类型(R/Wx):x的下标为几就对应那个控制寄存器,00(执行断点) 01(写入断点) 11(访问断点)
DbgkpSendApillessage (x, x)
1)第一个参数:消息结构每种消息都有自己的消息结构共有7种类型
2)第二个参数,要不要把本进程内除了自己之外的其他线程挂起.有些消息需要把其他线程挂起,比如异常
这个过程到第3步时,所有断点都一样执行流程都一样。
设置硬件断点要设置到所属线程的上下文。
设置单步运行:
TF位置1
处理单步异常:
单步产生的异常是 STATUS_SINGLE_STEP(单步异常)
TF位置1的时候每走完一步就会触发单步异常。
单步步入:
遇到CALL指令会跟进去
单步步过:
遇到CALL不会跟进去,实现原理是计算当指令长度,当前指令长度+当前eip的地方下断(也就是call指令的下一行)。
下面代码只有单步步入
//被调试进程句柄
HANDLE hDebuggeeProcess = NULL;
//被调试线程句柄
HANDLE hDebuggeeThread;
//系统断点
BOOL bIsSystemInt3 = TRUE;
//被INT 3覆盖的数据
char OriginalCode = 0;
//线程上下文
CONTEXT Context = { 0 };
BOOL WaitForUserCommand()
{
BOOL bRet = FALSE;
char cContext = 0;
printf("COMMAND> ");
scanf("%c",&cContext);
switch (cContext)
{
case 't':
bRet = TRUE;
//1.获取线程上下文
Context.ContextFlags = CONTEXT_FULL || CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hDebuggeeThread, &Context);
//2.设置单步执行
Context.EFlags |= 0x100;
//3.设置线程上下文
SetThreadContext(hDebuggeeThread, &Context);
break;
case 'p':
bRet = TRUE;
break;
case 'g':
bRet = TRUE;
break;
}
return bRet;
}
VOID setHardBreakPoint(PVOID addr)
{
//1.获取线程上下文
Context.ContextFlags = CONTEXT_ALL;
SuspendThread(hDebuggeeThread);//挂起线程
GetThreadContext(hDebuggeeThread,&Context);
//2.设置断点位置
Context.Dr0 = (DWORD)addr;
Context.Dr7 |= 1;
//3.设置断点长度
Context.Dr7 &= 0xfff0ffff;
//4.设置线程上下文
BOOL a = SetThreadContext(hDebuggeeThread, &Context);
ResumeThread(hDebuggeeThread);//恢复线程
}
BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO* excp)
{
BOOL bRet = FALSE;
//1将INT 3修复为原来的数据(如果是系统断点不用修复)
if (bIsSystemInt3)
{
bIsSystemInt3 = FALSE;
return TRUE;
}
else
{
//还原
WriteProcessMemory(hDebuggeeProcess, excp->ExceptionRecord.ExceptionAddress, &OriginalCode, 1, NULL);
}
//2.显示断点位置
printf("int3 断点 0x%p\n", excp->ExceptionRecord.ExceptionAddress);
//3.获取线程上下文
Context.ContextFlags = CONTEXT_FULL || CONTEXT_DEBUG_REGISTERS;
bool a = GetThreadContext(hDebuggeeThread, &Context);
//4.修正EIP(0xcc断下已经走了一个字节,Eip要-1)
Context.Eip --;
SetThreadContext(hDebuggeeThread, &Context);
//5.显示反汇编代码
//省略..
//硬件断点
setHardBreakPoint((PVOID)((DWORD)excp->ExceptionRecord.ExceptionAddress+1));
//6.等待调试者命令
while (bRet == FALSE)
{
bRet = WaitForUserCommand();
}
return bRet;
}
BOOL SingleStepExceptionProc(EXCEPTION_DEBUG_INFO* excp)
{
BOOL bRet = FALSE;
//1.取线程上下文
Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
GetThreadContext(hDebuggeeThread, &Context);
//2.判断是否是硬件断点导致的异常
if (Context.Dr6 & 0xf)//B0-B3不为NULL
{
//2.1显示断点信息
printf("硬件断点:%X %X\n", Context.Dr7 & 0x0003000, Context.Dr0);
//2.2将断点去除
Context.Dr0 = 0;
Context.Dr7 = 0xfffffffe;
}
else
{
//2.1显示断点信息
printf("单步->0x%p\n", Context.Eip);
//2.2将单步去除
Context.EFlags &= 0xfffffeff;
}
//3.设置线程上下文
SetThreadContext(hDebuggeeThread, &Context);
//等待用户命令
while (bRet == FALSE)
{
bRet = WaitForUserCommand();
}
return bRet;
}
BOOL ExceptionHandler(DEBUG_EVENT* pDebugEvent)
{
BOOL bRet = TRUE;
EXCEPTION_DEBUG_INFO* pExceptionInfo = NULL;
pExceptionInfo = &pDebugEvent->u.Exception;
//得到线程句柄 后面要用
pMyOpenThread MyOpenThread = (pMyOpenThread)GetProcAddress(LoadLibrary(L"Kernel32.dll"),"OpenThread");
hDebuggeeThread = MyOpenThread(THREAD_ALL_ACCESS,FALSE,pDebugEvent->dwThreadId);
if (pExceptionInfo->ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
{
bRet = Int3ExceptionProc(pExceptionInfo);
return bRet;
}
if (pExceptionInfo->ExceptionRecord.ExceptionCode == EXCEPTION_SINGLE_STEP)
{
bRet = SingleStepExceptionProc(pExceptionInfo);
return bRet;
}
return bRet;
}
void SetInt3BreakPoint(LPVOID addr)
{
ReadProcessMemory(hDebuggeeProcess, addr, &OriginalCode, 1, NULL);
BYTE int3[1] = { 0xcc };
WriteProcessMemory(hDebuggeeProcess, addr, int3, 1, NULL);
}
void CALL()
{
BOOL nIsConinue = TRUE;
DEBUG_EVENT debugEvent = { 0 };
DWORD dwContext = DBG_CONTINUE;
//1.创建调试进程
STARTUPINFO startupInfo = { 0 };
PROCESS_INFORMATION pInfo = { 0 };
GetStartupInfo(&startupInfo);
WCHAR* _ProcessName = { L"C:\\Users\\Administrator\\Desktop\\控制台测试\\Debug\\0000.exe" };
BOOL bRet = CreateProcess(_ProcessName, NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &pInfo);
if (bRet == FALSE)
{
printf("CreateProcess error:%d\n", GetLastError());
return;
}
hDebuggeeProcess = pInfo.hProcess;
//调试循环
while (nIsConinue)
{
BOOL bRet = WaitForDebugEvent(&debugEvent, INFINITE);//取DEBUG_EVENT
if (!bRet)
{
printf("WaitForDebugEvent error:%d\n", GetLastError());
return;
}
switch (debugEvent.dwDebugEventCode)
{
case EXCEPTION_DEBUG_EVENT:
bRet = ExceptionHandler(&debugEvent);
if (!bRet)
dwContext = DBG_EXCEPTION_NOT_HANDLED;
break;
case CREATE_THREAD_DEBUG_EVENT:
break;
case CREATE_PROCESS_DEBUG_EVENT:
//进程创建的时候 在DEP处设置断点就是将OEP处的一个字节改为0xCC(INT 3)
SetInt3BreakPoint((PCHAR)debugEvent.u.CreateProcessInfo.lpStartAddress);
break;
case EXIT_THREAD_DEBUG_EVENT:
break;
case EXIT_PROCESS_DEBUG_EVENT:
break;
case LOAD_DLL_DEBUG_EVENT:
break;
case UNLOAD_DLL_DEBUG_EVENT:
break;
}
bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
}
}