调试寄存器 原理与使用:DR0-DR7
下面介绍的知识性信息来自intel IA-32手册(可以在intel的开发手册或者官方网站查到),提示和补充来自学习调试器实现时的总结。
希望能给你带去有用的信息。最后需要提醒一个小问题:数据写入断点设置后。是在原数据被修改后,才产生调试异常。所以,返回异常时,原有数据已经被修改。如果想保留原有数据,需要自己提前保存对应地址的数据。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VC利用调试寄存器实现硬件断点,处理断点异常
/************************************************************************ SetHardWareBP: 设置线程硬件断点 hThread: 线程句柄 dwAddr: 断点地址 dwDrIndex: 硬件寄存器(0~3) nType: 断点类型(0:执行,1:读取,2:写入) nLen: 读写断点数据长度(1,2,4) /************************************************************************/ BOOL SetHardWareBP(HANDLE hThread,DWORD dwAddr,DWORD dwDrIndex=0,UINT nType=0,UINT nLen=1) { BOOL bResult=FALSE; CONTEXT context = {0}; context.ContextFlags=CONTEXT_DEBUG_REGISTERS; if(::GetThreadContext(hThread,&context)) { DWORD dwDrFlags=context.Dr7; //将断点地址复制进入对应Dr寄存器(参考CONTEXT结构) memcpy(((BYTE *)&context)+4+dwDrIndex*4,&dwAddr,4); //决定使用哪个寄存器 dwDrFlags|=(DWORD)0x1<<(2*dwDrIndex); //见OD读写断点时 这个置位了,执行没有(置位也正常-_-) dwDrFlags|=0x100; //先将对应寄存器对应4个控制位清零(先或,再异或,还有其它好方法吗) =.= 悲催的小学生 dwDrFlags|=(DWORD)0xF<<(16+4*dwDrIndex); dwDrFlags^=(DWORD)0xF<<(16+4*dwDrIndex); //设置断点类型,执行:00 读取:11 写入:01 //(不知何故,测试时发现不论是11还是01,读写数据时均会断下来) if (nType==1) dwDrFlags|=(DWORD)0x3<<(16+4*dwDrIndex); //读取 else if(nType==2) dwDrFlags|=(DWORD)0x1<<(16+4*dwDrIndex); //写入 //else if(nType==0) //dwDrFlags=dwDrFlags //执行 //设置读写断点时数据长度 if (nType!=0) { if(nLen==2 && dwAddr%2==0) dwDrFlags|=(DWORD)0x1<<(18+4*dwDrIndex); //2字节 else if(nLen==4 && dwAddr%4==0) dwDrFlags|=(DWORD)0x3<<(18+4*dwDrIndex); //4字节 } context.Dr7=dwDrFlags; if (::SetThreadContext(hThread,&context)) bResult=TRUE; } return bResult; }
//异常处理 //直接从工程中拷出来的 typedef ULONG (WINAPI *pfnRtlDispatchException)(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext); static pfnRtlDispatchException m_fnRtlDispatchException=NULL; BOOL RtlDispatchException(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext); ULONG WINAPI CSysHook::_RtlDispatchException( PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext ) { if(RtlDispatchException(pExcptRec,pContext)) return 1; return m_fnRtlDispatchException(pExcptRec,pContext); } //Hook程序异常处理,当程序发生异常时,由ring0转回ring3时调用的第一个函数:KiUserExceptionDispatcher BOOL CSysHook::HookSystemSEH() { BOOL bResult=FALSE; BYTE *pAddr=(BYTE *)::GetProcAddress(::GetModuleHandleA("ntdll"),"KiUserExceptionDispatcher"); if (pAddr) { while (*pAddr!=0xE8)pAddr++; //XP~Win7正常,Win8尚无缘得见 m_fnRtlDispatchException=(pfnRtlDispatchException)((*(DWORD *)(pAddr+1))+5+(DWORD)pAddr); //得到原函数地址 DWORD dwNewAddr=(DWORD)_RtlDispatchException-(DWORD)pAddr-5; //计算新地址 CMemory::WriteMemory((DWORD)pAddr+1,(BYTE *)&dwNewAddr,4); //这个写内存的自己改造吧 bResult=TRUE; } return bResult; } //异常处理函数 BOOL RtlDispatchException(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext) { 返回TRUE,这个异常我已经处理好了,继续运行程序 返回FALSE,这个异常不是我的,找别人处理去 }