下面介绍的知识性信息来自intel IA-32手册(可以在intel的开发手册或者官方网站查到),提示和补充来自学习调试器实现时的总结。
希望能给你带去有用的信息。
(DRx对应任意的一个调试寄存器。LENn对应任意一个长度。Ln对应任意一个局部置位)
DR0-DR7可以直接被读写操作(MOV 指令之类的,DRx可以是源操作数也可以是目的操作数)
但是,DRx的访问是需要一定权限的。比如你用MOV操作的话,你需要在实地址模式,系统管理模式(smm)或者在保护模式(CPL设0).如果权限不够,将会在访问DRx的时候尝产生#GP(general-protection)异常
现在来看看DRx可以干些什么?
1.设置发生断点的地址(线性地址)
2.设置断点的长度(1,2,4个字节,但是执行断点只能是1)
3.设置在调试异常产生的地址执行的操作
4.设置断点是否可用
5.在调试异常产生时,调试条件是否是可用
(以上直接翻译自"Intel 64 and IA-32 Architectures Software Developer’s Manual" volume 3。
以下来自个人的总结。当然,也是参考intel官方资料得来的)
我们来看看调试寄存器的一些细节信息。
下图很重要,后面的介绍都是针对这个图说的。
(当然不是我画的,是来自intel ia-32系统结构开发手册18章2节)。
最后需要提醒一个小问题:数据写入断点设置后。是在原数据被修改后,才产生调试异常。所以,返回异常时,原有数据已经被修改。如果想保留原有数据,需要自己提前保存对应地址的数据。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
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,这个异常不是我的,找别人处理去
}
其实这个文章我也不知道源头是哪,因为我看到它的时候,它也是转的,但是他也没写源头,据图片推测,可能是CSDN哪的。