Lazily wrote this small class that isn't really finished. As of now it can only set hardware breakpoints, it can't even remove them. I was going to implement software breakpoints, but who really cares about those anyway.
AwBreakPoint.h
[Highlight=C++]
#include <windows.h>
#include <tlhelp32.h>
typedef void(__cdecl* EXCEPTION_HANDLER)(EXCEPTION_POINTERS*);
class AwBreakPoint
{
public:
bool SetExceptionHandler(EXCEPTION_HANDLER);
bool SetHWBreakPoint(unsigned long,int);
static EXCEPTION_HANDLER GetHandler();
private:
void GetMainThreadFromProcessId();
HANDLE m_hThread;
static EXCEPTION_HANDLER Handler;
};
[/Highlight]
AwBreakPoint.cpp
[Highlight=C++]
#include "AwBreakPoint.h"
EXCEPTION_HANDLER AwBreakPoint::Handler;
LONG __stdcall Ex_handler(EXCEPTION_POINTERS* ep)
{
if(ep->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP)
{
EXCEPTION_HANDLER handler = AwBreakPoint::GetHandler();
handler(ep);
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
void AwBreakPoint::GetMainThreadFromProcessId()
{
unsigned long uProcessId = GetCurrentProcessId();
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,uProces sId);
if(!hSnapshot)
return;
THREADENTRY32 lpThread;
lpThread.dwSize = sizeof(THREADENTRY32);
if(Thread32First(hSnapshot,&lpThread))
{
do {
if(lpThread.th32OwnerProcessID == uProcessId)
{
break;
}
}while(Thread32Next(hSnapshot,&lpThread));
CloseHandle(hSnapshot);
AwBreakPoint::m_hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME,1,lpThread.th32ThreadID);
}
return;
}
bool AwBreakPoint::SetExceptionHandler(EXCEPTION_HANDLE R eHandler)
{
AwBreakPoint::Handler = eHandler;
AddVectoredExceptionHandler(1,Ex_handler);
return true;
}
EXCEPTION_HANDLER AwBreakPoint::GetHandler()
{
return AwBreakPoint::Handler;
}
bool AwBreakPoint::SetHWBreakPoint(unsigned long uAddress, int iIndex)
{
GetMainThreadFromProcessId();
CONTEXT c = {CONTEXT_DEBUG_REGISTERS};
SuspendThread(AwBreakPoint::m_hThread);
GetThreadContext(AwBreakPoint::m_hThread,&c);
switch(iIndex)
{
case 0:
c.Dr0 = uAddress;
c.Dr7 = ( 1 << 0); // set 0th bit
break;
case 1:
c.Dr1 = uAddress;
c.Dr7 |= ( 1 << 2); // set 2nd bit
break;
case 2:
c.Dr2 = uAddress;
c.Dr7 |= ( 1 << 4); // set 4th bit
break;
case 3:
c.Dr3 = uAddress;
c.Dr7 |= ( 1 << 6); // set 6th bit
break;
default:
return false;
}
c.Dr6 = 0;
SetThreadContext(AwBreakPoint::m_hThread,&c);
ResumeThread(AwBreakPoint::m_hThread);
return true;
}
[/Highlight]
I also went ahead and provided an example, since not every one knows how to use the instruction pointer to direct execution to your own function.
Main
[Highlight=C++]
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include "AwBreakPoint.h"
AwBreakPoint BP;
__declspec(naked) void hook(IDirect3DDevice9* pDevice)
{
__asm
{
mov edi,edi
push ebp
mov ebp,esp
}
pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,255,0), 1.0f, 0);
__asm
{
mov eax,0x4FE571A0
add eax,0x5
jmp eax
}
}
void Handle(EXCEPTION_POINTERS* ep)
{
if(ep->ContextRecord->Eip == 0x4FE571A0)
{
ep->ContextRecord->Eip = (unsigned long)&hook;
}
}
void SetBP()
{
BP.SetExceptionHandler(Handle);
BP.SetHWBreakPoint(0x4FE571A0,0);
}
bool __stdcall DllMain(HINSTANCE hInst,DWORD uReason,void* lpReserved)
{
if(uReason == DLL_PROCESS_ATTACH)
{
SetBP();
}
return true;
}
[/Highlight]
DR7 has certain bits that need to be set to for the breakpoints to be enabled. To enable them locally you set the 0th,2nd, 4th and 6th bit, depending on which debug register you're using. See more here:
Debug Control ( DR7 )
PS: The example provided demonstrates how to use breakpoints to redirect a function to your own. The example breaks at the first instruction of IDirect3DDevice9::EndScene and redirects execution to my function, my function calls 5 bytes passed the address the breakpoint is set on ( because we don't want to break again and the prologue is 5 bytes long ).