Windows软件调试学习笔记(六)—— 硬件断点

硬件断点设置硬件断点触发硬件断点处理硬件断点实验:硬件断点的设置与处理

硬件断点

描述:

与软件断点与内存断点不同,硬件断点不依赖被调试程序,而是依赖于CPU中的调试寄存器。调试寄存器有8个,分别为Dr0~Dr7。用户最多能够设置4个硬件断点,这是由于只有Dr0~Dr3用于存储线性地址。其中,Dr4和Dr5是保留的。

思考:假如在Dr0寄存器中写入线性地址,是否所有线程都会受影响? 答案:不对,每个线程都拥有一份独立的寄存器,切换线程时,寄存器的值也会被切换。

设置硬件断点

1)Dr0~Dr3用于设置硬件断点,由于只有4个断点寄存器,所以最多只能设置4个硬件调试断点。 2)Dr7是最重要的寄存器:

L0/G0 ~ L3/G3:控制Dr0~Dr3是否有效,局部还是全局;每次异常后,Lx都被清零,Gx不清零。断点长度(LENx):00(1字节)、01(2字节)、11(4字节)断点类型(R/Wx):00(执行断点)、01(写入断点)、11(访问断点)

触发硬件断点

被调试进程: 1)CPU执行时检测当前线性地址与调试寄存器(Dr0~Dr3)中的线性地址相等。 2)查IDT表找到对应的中断处理函数(nt!_KiTrap01) 3)CommonDispatchException 4)KiDispatchException 5)DbgkForwardException收集并发送调试事件

最终调用DbgkpSendApiMessage(x, x) 第一个参数:消息类型 第二个参数:是否挂起其它线程

调试器进程: 1)循环判断 2)取出调试事件 3)列出信息:寄存器、内存 4)用户处理

处理硬件断点

1)硬件调试断点产生的异常是 STATUS_SINGLE_STEP(单步异常) 2)检测Dr6寄存器的B0~B3:哪个寄存器触发的异常

实验:硬件断点的设置与处理

实验附件:https://pan.baidu.com/s/16p7PDlSsBJeTnIttBGaXlQ 密码: 9lro 1)编译并运行以下代码:

#include

#include

#include

#define DEBUGGEE "C:\\helloworld.exe"

//被调试进程ID,进程句柄,OEP

DWORD dwDebuggeePID = 0;

//被调试线程句柄

HANDLE hDebuggeeThread = NULL;

HANDLE hDebuggeeProcess = NULL;

//系统断点

BOOL bIsSystemInt3 = TRUE;

//被INT 3覆盖的数据

CHAR OriginalCode = 0;

//线程上下文

CONTEXT Context;

typedef HANDLE (__stdcall *FnOpenThread) (DWORD, BOOL, DWORD);

VOID InitDebuggeeInfo(DWORD dwPID, HANDLE hProcess)

{

dwDebuggeePID = dwPID;

hDebuggeeProcess = hProcess;

}

DWORD GetProcessId(LPTSTR lpProcessName)

{

HANDLE hProcessSnap = NULL;

PROCESSENTRY32 pe32 = {0};

hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if(hProcessSnap == (HANDLE)-1)

{

return 0;

}

pe32.dwSize = sizeof(PROCESSENTRY32);

if(Process32First(hProcessSnap, &pe32))

{

do

{

if(!strcmp(lpProcessName, pe32.szExeFile))

return (int)pe32.th32ProcessID;

} while (Process32Next(hProcessSnap, &pe32));

}

else

{

CloseHandle(hProcessSnap);

}

return 0;

}

BOOL WaitForUserCommand()

{

BOOL bRet = FALSE;

CHAR command;

printf("COMMAND>");

command = getchar();

switch(command)

{

case 't':

bRet = TRUE;

break;

case 'p':

bRet = TRUE;

break;

case 'g':

bRet = TRUE;

break;

}

getchar();

return bRet;

}

VOID SetHardBreakPoint(PVOID pAddress)

{

//1. 获取线程上下文

Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;

GetThreadContext(hDebuggeeThread, &Context);

//2. 设置断点位置

Context.Dr0 = (DWORD)pAddress;

Context.Dr7 |= 1;

//3. 设置断点长度和类型

Context.Dr7 &= 0xfff0ffff; //执行断点(16、17位 置0) 1字节(18、19位 置0)

//5. 设置线程上下文

SetThreadContext(hDebuggeeThread, &Context);

}

BOOL Int3ExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo)

{

BOOL bRet = FALSE;

//1. 将INT 3修复为原来的数据(如果是系统断点,不用修复)

if(bIsSystemInt3)

{

bIsSystemInt3 = FALSE;

return TRUE;

}

else

{

WriteProcessMemory(hDebuggeeProcess, pExceptionInfo->ExceptionRecord.ExceptionAddress, &OriginalCode, 1, NULL);

}

//2. 显示断点位置

printf("Int 3断点:0x%p \r\n", pExceptionInfo->ExceptionRecord.ExceptionAddress);

//3. 获取线程上下文

Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;

GetThreadContext(hDebuggeeThread, &Context);

//4. 修正EIP

Context.Eip--;

SetThreadContext(hDebuggeeThread, &Context);

//5. 显示反汇编代码、寄存器等

/*

硬件断点需要设置在被调试进程的的线程上下文中。

因此当被调试程序触发调试器设置的INT 3断点时,此时设置硬件断点较为合理。

*/

SetHardBreakPoint((PVOID)((DWORD)pExceptionInfo->ExceptionRecord.ExceptionAddress+1));

//6. 等待用户命令

while(bRet == FALSE)

{

bRet = WaitForUserCommand();

}

return bRet;

}

BOOL AccessExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo)

{

BOOL bRet = TRUE;

return bRet;

}

BOOL SingleStepExceptionProc(EXCEPTION_DEBUG_INFO *pExceptionInfo)

{

BOOL bRet = FALSE;

//1. 获取线程上下文

Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;

GetThreadContext(hDebuggeeThread, &Context);

//2. 判断是否是硬件断点导致的异常

if(Context.Dr6 & 0xF) //B0~B3不为空 硬件断点

{

//2.1 显示断点信息

printf("硬件断点:%x 0x%p \n", Context.Dr7&0x00030000, Context.Dr0);

//2.2 将断点去除

Context.Dr0 = 0;

Context.Dr7 &= 0xfffffffe;

}

else //单步异常

{

//2.1 显示断点信息

printf("单步:0x%p \n", Context.Eip);

//2.2 将断点去除

Context.Dr7 &= 0xfffffeff;

}

SetThreadContext(hDebuggeeThread, &Context);

//6. 等待用户命令

while(bRet == FALSE)

{

bRet = WaitForUserCommand();

}

return bRet;

}

BOOL ExceptionHandler(DEBUG_EVENT *pDebugEvent)

{

BOOL bRet = TRUE;

EXCEPTION_DEBUG_INFO *pExceptionInfo = NULL;

pExceptionInfo = &pDebugEvent->u.Exception;

//得到线程句柄,后面要用

FnOpenThread MyOpenThread = (FnOpenThread)GetProcAddress(LoadLibrary("kernel32.dll"), "OpenThread");

hDebuggeeThread = MyOpenThread(THREAD_ALL_ACCESS, FALSE, pDebugEvent->dwThreadId);

switch(pExceptionInfo->ExceptionRecord.ExceptionCode)

{

//INT 3异常

case EXCEPTION_BREAKPOINT:

bRet = Int3ExceptionProc(pExceptionInfo);

break;

//访问异常

case EXCEPTION_ACCESS_VIOLATION:

bRet = AccessExceptionProc(pExceptionInfo);

break;

//单步执行

case EXCEPTION_SINGLE_STEP:

bRet = SingleStepExceptionProc(pExceptionInfo);

break;

}

return bRet;

}

VOID SetInt3BreakPoint(LPVOID addr)

{

CHAR int3 = 0xCC;

//1. 备份

ReadProcessMemory(hDebuggeeProcess, addr, &OriginalCode, 1, NULL);

//2. 修改

WriteProcessMemory(hDebuggeeProcess, addr, &int3, 1, NULL);

}

int main(int argc, char* argv[])

{

BOOL nIsContinue = TRUE;

DEBUG_EVENT debugEvent = {0};

BOOL bRet = TRUE;

DWORD dwContinue = DBG_CONTINUE;

//1.创建调试进程

STARTUPINFO startupInfo = {0};

PROCESS_INFORMATION pInfo = {0};

GetStartupInfo(&startupInfo);

bRet = CreateProcess(DEBUGGEE, NULL, NULL, NULL, TRUE, DEBUG_PROCESS || DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &startupInfo, &pInfo);

if(!bRet)

{

printf("CreateProcess error: %d \n", GetLastError());

return 0;

}

hDebuggeeProcess = pInfo.hProcess;

//2.调试循环

while(nIsContinue)

{

bRet = WaitForDebugEvent(&debugEvent, INFINITE);

if(!bRet)

{

printf("WaitForDebugEvent error: %d \n", GetLastError());

return 0;

}

switch(debugEvent.dwDebugEventCode)

{

//1.异常

case EXCEPTION_DEBUG_EVENT:

bRet = ExceptionHandler(&debugEvent);

if(!bRet)

dwContinue = DBG_EXCEPTION_NOT_HANDLED;

break;

//2.

case CREATE_THREAD_DEBUG_EVENT:

break;

//3.创建进程

case CREATE_PROCESS_DEBUG_EVENT:

//设置INT 3断点

SetInt3BreakPoint((PCHAR)debugEvent.u.CreateProcessInfo.lpStartAddress);

break;

//4.

case EXIT_THREAD_DEBUG_EVENT:

break;

//5.

case EXIT_PROCESS_DEBUG_EVENT:

break;

//6.

case LOAD_DLL_DEBUG_EVENT:

break;

//7.

case UNLOAD_DLL_DEBUG_EVENT:

break;

//8.

case OUTPUT_DEBUG_STRING_EVENT:

break;

}

bRet = ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);

}

return 0;

}

运行结果: 2)输入g并回车,触发硬件断点 3)再次输入g并回车,使程序继续运行

Copyright © 2088 世界杯欧洲区_世界杯中国 - rd508.com All Rights Reserved.
友情链接