cheat-engine/DBKKernel/debugger.c
2021-05-14 17:31:36 +02:00

1843 lines
50 KiB
C

/*
debugger.c:
This unit will handle all debugging related code, from hooking, to handling interrupts
todo: this whole thing can be moved to a few simple lines in dbvm...
*/
#pragma warning( disable: 4100 4103 4189)
#include <ntifs.h>
#include <windef.h>
#include "DBKFunc.h"
#include "interruptHook.h"
#include "debugger.h"
#include "vmxhelper.h"
#ifdef AMD64
extern void interrupt1_asmentry( void ); //declared in debuggera.asm
#else
void interrupt1_asmentry( void );
#endif
volatile struct
{
BOOL isDebugging; //TRUE if a process is currently being debugged
BOOL stoppingTheDebugger;
DWORD debuggedProcessID; //The processID that is currently debugger
struct {
BOOL active;
UINT_PTR address; //Up to 4 addresses to break on
BreakType breakType; //What type of breakpoint for each seperate address
BreakLength breakLength; //How many bytes does this breakpoint look at
} breakpoint[4];
//...
BOOL globalDebug; //If set all threads of every process will raise an interrupt on taskswitch
//while debugging:
UINT_PTR *LastStackPointer;
UINT_PTR *LastRealDebugRegisters;
HANDLE LastThreadID;
BOOL CausedByDBVM;
BOOL handledlastevent;
//BOOL storeLBR;
//int storeLBR_max;
//UINT_PTR *LastLBRStack;
volatile struct {
UINT_PTR DR0;
UINT_PTR DR1;
UINT_PTR DR2;
UINT_PTR DR3;
UINT_PTR DR6;
UINT_PTR DR7;
UINT_PTR reserved;
volatile int inEpilogue; //if set the global debug bit does no faking
} FakedDebugRegisterState[256];
char b[1];
//volatile BYTE DECLSPEC_ALIGN(16) fxstate[512];
BOOL isSteppingTillClear; //when set the user has entered single stepping mode. This is a one thread only thing, so when it's active and another single step happens, discard it
} DebuggerState;
KEVENT debugger_event_WaitForContinue; //event for kernelmode. Waits till it's set by usermode (usermode function: DBK_Continue_Debug_Event sets it)
KEVENT debugger_event_CanBreak; //event for kernelmode. Waits till a break has been handled so a new one can enter
KEVENT debugger_event_WaitForDebugEvent; //event for usermode. Waits till it's set by a debugged event
DebugReg7 debugger_dr7_getValue(void);
void debugger_dr7_setValue(DebugReg7 value);
DebugReg6 debugger_dr6_getValue(void);
JUMPBACK Int1JumpBackLocation;
typedef struct _SavedStack
{
BOOL inuse;
QWORD stacksnapshot[600];
} SavedStack, *PSavedStack;
criticalSection StacksCS;
int StackCount;
PSavedStack *Stacks;
void debugger_dr7_setGD(int state)
{
DebugReg7 _dr7=debugger_dr7_getValue();
_dr7.GD=state; //usually 1
debugger_dr7_setValue(_dr7);
}
void debugger_dr0_setValue(UINT_PTR value)
{
__writedr(0,value);
}
UINT_PTR debugger_dr0_getValue(void)
{
return __readdr(0);
}
void debugger_dr1_setValue(UINT_PTR value)
{
__writedr(1,value);
}
UINT_PTR debugger_dr1_getValue(void)
{
return __readdr(1);
}
void debugger_dr2_setValue(UINT_PTR value)
{
__writedr(2,value);
}
UINT_PTR debugger_dr2_getValue(void)
{
return __readdr(2);
}
void debugger_dr3_setValue(UINT_PTR value)
{
__writedr(3,value);
}
UINT_PTR debugger_dr3_getValue(void)
{
return __readdr(3);
}
void debugger_dr6_setValue(UINT_PTR value)
{
__writedr(6,value);
}
void debugger_dr7_setValue(DebugReg7 value)
{
UINT_PTR temp=*(UINT_PTR *)&value;
__writedr(7,temp);
}
void debugger_dr7_setValueDword(UINT_PTR value)
{
__writedr(7,value);
}
UINT_PTR debugger_dr7_getValueDword(void) //I wonder why I couldn't just typecast the DebugReg7 to a dword...
{
return __readdr(7);
}
DebugReg7 debugger_dr7_getValue(void)
{
UINT_PTR temp=debugger_dr7_getValueDword();
return *(DebugReg7 *)&temp;
}
UINT_PTR debugger_dr6_getValueDword(void)
{
return __readdr(6);
}
DebugReg6 debugger_dr6_getValue(void)
{
UINT_PTR temp=debugger_dr6_getValueDword();
return *(DebugReg6 *)&temp;
}
void debugger_touchDebugRegister(UINT_PTR param)
{
//DbgPrint("Touching debug register. inepilogue=\n", DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue);
debugger_dr0_setValue(debugger_dr0_getValue());
}
void debugger_initialize(void)
{
//DbgPrint("Initializing debugger events\n");
KeInitializeEvent(&debugger_event_WaitForContinue, SynchronizationEvent, FALSE);
KeInitializeEvent(&debugger_event_CanBreak, SynchronizationEvent, TRUE); //true so the first can enter
KeInitializeEvent(&debugger_event_WaitForDebugEvent, SynchronizationEvent, FALSE);
//DbgPrint("DebuggerState.fxstate=%p\n",DebuggerState.fxstate);
StackCount = getCpuCount() * 4;
Stacks = (PSavedStack*)ExAllocatePool(NonPagedPool, StackCount*sizeof(PSavedStack));
int i;
for (i = 0; i < StackCount; i++)
{
Stacks[i] = (PSavedStack)ExAllocatePool(NonPagedPool, sizeof(SavedStack));
RtlZeroMemory(Stacks[i], sizeof(SavedStack));
}
}
void debugger_shutdown(void)
{
if (Stacks)
{
int i;
for (i = 0; i < StackCount; i++)
{
if (Stacks[i])
{
ExFreePool(Stacks[i]);
Stacks[i] = NULL;
}
}
ExFreePool(Stacks);
Stacks = NULL;
}
}
void debugger_growstack()
//called in passive mode
{
if (Stacks)
{
KIRQL oldIRQL=KeRaiseIrqlToDpcLevel();
csEnter(&StacksCS);
enableInterrupts(); //csEnter disables it, but we need it
int newStackCount = StackCount * 2;
int i;
PSavedStack *newStacks;
newStacks = (PSavedStack*)ExAllocatePool(NonPagedPool, newStackCount * sizeof(PSavedStack));
if (newStacks)
{
for (i = 0; i < StackCount; i++)
newStacks[i] = Stacks[i];
for (i = StackCount; i < newStackCount; i++)
{
newStacks[i] = (PSavedStack)ExAllocatePool(NonPagedPool, sizeof(SavedStack));
if (newStacks[i])
RtlZeroMemory(newStacks[i], sizeof(SavedStack));
else
{
ExFreePool(newStacks);
csLeave(&StacksCS);
KeLowerIrql(oldIRQL);
return;
}
}
ExFreePool(Stacks);
Stacks = newStacks;
}
csLeave(&StacksCS);
KeLowerIrql(oldIRQL);
}
}
void debugger_setInitialFakeState(void)
{
//DbgPrint("setInitialFakeState for cpu %d\n",cpunr());
DebuggerState.FakedDebugRegisterState[cpunr()].DR0=debugger_dr0_getValue();
DebuggerState.FakedDebugRegisterState[cpunr()].DR1=debugger_dr1_getValue();
DebuggerState.FakedDebugRegisterState[cpunr()].DR2=debugger_dr2_getValue();
DebuggerState.FakedDebugRegisterState[cpunr()].DR3=debugger_dr3_getValue();
DebuggerState.FakedDebugRegisterState[cpunr()].DR6=debugger_dr6_getValueDword();
DebuggerState.FakedDebugRegisterState[cpunr()].DR7=debugger_dr7_getValueDword();
}
VOID debugger_initHookForCurrentCPU_DPC(IN struct _KDPC *Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2)
{
debugger_initHookForCurrentCPU();
}
int debugger_removeHookForCurrentCPU(UINT_PTR params)
{
//DbgPrint("Unhooking int1 for this cpu\n");
return inthook_UnhookInterrupt(1);
}
int debugger_initHookForCurrentCPU(void)
/*
Must be called for each cpu
*/
{
int result=TRUE;
//DbgPrint("Hooking int1 for cpu %d\n", cpunr());
result=inthook_HookInterrupt(1,getCS() & 0xfff8, (ULONG_PTR)interrupt1_asmentry, &Int1JumpBackLocation);
#ifdef AMD64
if (result)
{
;//DbgPrint("hooked int1. Int1JumpBackLocation=%x:%llx\n", Int1JumpBackLocation.cs, Int1JumpBackLocation.eip);
}
else
{
//DbgPrint("Failed hooking interrupt 1\n");
return result;
}
#endif
if (DebuggerState.globalDebug)
{
//set the fake state
//debugger_setInitialFakeState();
//DbgPrint("Setting GD bit for cpu %d\n",cpunr());
debugger_dr7_setGD(1); //enable the GD flag
}
/*if (DebuggerState.storeLBR)
{
//DbgPrint("Enabling LBR logging. IA32_DEBUGCTL was %x\n", __readmsr(0x1d9));
__writemsr(0x1d9, __readmsr(0x1d9) | 1);
//DbgPrint("Enabling LBR logging. IA32_DEBUGCTL is %x\n", __readmsr(0x1d9));
}*/
return result;
}
void debugger_setStoreLBR(BOOL state)
{
return; //disabled for now
/*
//if (state)
// DbgPrint("Setting storeLBR to true\n");
//else
// DbgPrint("Setting storeLBR to false\n");
DebuggerState.storeLBR=state; //it's not THAT crucial to disable/enable it
DebuggerState.storeLBR_max=0;
switch (cpu_model)
{
case 0x2a:
case 0x1a:
case 0x1e:
case 0x1f:
case 0x2e:
case 0x25:
case 0x2c:
DebuggerState.storeLBR_max=16;
break;
case 0x17:
case 0x1d:
case 0x0f:
DebuggerState.storeLBR_max=4;
break;
case 0x1c:
DebuggerState.storeLBR_max=8;
break;
}
//DbgPrint("Because your cpu_model=%d I think that your storeLBR_max=%d\n", cpu_model, DebuggerState.storeLBR_max);
*/
}
int debugger_setGlobalDebugState(BOOL state)
//call this BEFORE debugging, if already debugging, the user must call this for each cpu
{
//DbgPrint("debugger_setGlobalDebugState(%d)\n",state);
if (state)
DebuggerState.globalDebug=state; //on enable set this first
if (inthook_isHooked(1))
{
int oldEpilogueState=DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue;
//DbgPrint("Int 1 is hooked,%ssetting GD\n",(state ? "":"un"));
//DbgPrint("oldEpilogueState=%d\n",oldEpilogueState);
//debugger_setInitialFakeState();
DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=TRUE;
DebuggerState.globalDebug=state;
debugger_dr7_setGD(state);
DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=oldEpilogueState;
DebuggerState.FakedDebugRegisterState[cpunr()].DR7=0x400;
debugger_dr7_setValueDword(0x400);
}
return TRUE;
}
int debugger_startDebugging(DWORD debuggedProcessID)
/*
Call this AFTER the interrupts are hooked
*/
{
//DbgPrint("debugger_startDebugging. Processid=%x\n",debuggedProcessID);
Int1JumpBackLocation.eip=inthook_getOriginalEIP(1);
Int1JumpBackLocation.cs=inthook_getOriginalCS(1);
#ifdef AMD64
//DbgPrint("Int1 jump back = %x:%llx\n", Int1JumpBackLocation.cs, Int1JumpBackLocation.eip);
#endif
DebuggerState.isDebugging=TRUE;
DebuggerState.debuggedProcessID=debuggedProcessID;
return TRUE;
}
int debugger_stopDebugging(void)
{
int i;
//DbgPrint("Stopping the debugger if it is running\n");
DebuggerState.stoppingTheDebugger=TRUE;
if (DebuggerState.globalDebug)
{
//touch the global debug for each debug processor
//DbgPrint("Touching the debug registers\n");
forEachCpuPassive(debugger_touchDebugRegister, 0);
}
DebuggerState.globalDebug=FALSE; //stop when possible, saves speed
DebuggerState.isDebugging=FALSE;
for (i=0; i<4; i++)
DebuggerState.breakpoint[i].active=FALSE;
//unhook all processors
forEachCpuPassive(debugger_removeHookForCurrentCPU, 0);
return TRUE;
}
int debugger_unsetGDBreakpoint(int breakpointnr)
{
int result=DebuggerState.breakpoint[breakpointnr].active;
DebuggerState.breakpoint[breakpointnr].active=FALSE;
return result; //returns true if it was active
}
int debugger_setGDBreakpoint(int breakpointnr, ULONG_PTR Address, BreakType bt, BreakLength bl)
/*
Will register a specific breakpoint. If global debug is used it'll set this debug register accordingly
*/
{
//DbgPrint("debugger_setGDBreakpoint(%d, %x, %d, %d)\n", breakpointnr, Address, bt, bl);
DebuggerState.breakpoint[breakpointnr].active=TRUE;
DebuggerState.breakpoint[breakpointnr].address=Address;
DebuggerState.breakpoint[breakpointnr].breakType=bt;
DebuggerState.breakpoint[breakpointnr].breakLength=bl;
return TRUE;
}
NTSTATUS debugger_waitForDebugEvent(ULONG timeout)
{
NTSTATUS r;
LARGE_INTEGER wait;
//DbgPrint("debugger_waitForDebugEvent with timeout of %d\n",timeout);
//-10000000LL=1 second
//-10000LL should be 1 millisecond
//-10000LL
wait.QuadPart=-10000LL * timeout;
if (timeout==0xffffffff) //infinite wait
r=KeWaitForSingleObject(&debugger_event_WaitForDebugEvent, UserRequest, KernelMode, TRUE, NULL);
else
r=KeWaitForSingleObject(&debugger_event_WaitForDebugEvent, UserRequest, KernelMode, TRUE, &wait);
if (r==STATUS_SUCCESS)
return r;
else
return STATUS_UNSUCCESSFUL;
}
NTSTATUS debugger_continueDebugEvent(BOOL handled)
/*
Only call this by one thread only, and only when there's actually a debug eevnt in progress
*/
{
//DbgPrint("debugger_continueDebugEvent\n");
DebuggerState.handledlastevent=handled;
KeSetEvent(&debugger_event_WaitForContinue, 0,FALSE);
return STATUS_SUCCESS;
}
UINT_PTR *debugger_getLastStackPointer(void)
{
return DebuggerState.LastStackPointer;
}
NTSTATUS debugger_getDebuggerState(PDebugStackState state)
{
//DbgPrint("debugger_getDebuggerState\n");
state->threadid=(UINT64)DebuggerState.LastThreadID;
state->causedbydbvm = (UINT64)DebuggerState.CausedByDBVM;
if (DebuggerState.LastStackPointer)
{
state->rflags=(UINT_PTR)DebuggerState.LastStackPointer[si_eflags];
state->rax=DebuggerState.LastStackPointer[si_eax];
state->rbx=DebuggerState.LastStackPointer[si_ebx];
state->rcx=DebuggerState.LastStackPointer[si_ecx];
state->rdx=DebuggerState.LastStackPointer[si_edx];
state->rsi=DebuggerState.LastStackPointer[si_esi];
state->rdi=DebuggerState.LastStackPointer[si_edi];
state->rbp=DebuggerState.LastStackPointer[si_ebp];
#ifdef AMD64
//fill in the extra registers
state->r8=DebuggerState.LastStackPointer[si_r8];
state->r9=DebuggerState.LastStackPointer[si_r9];
state->r10=DebuggerState.LastStackPointer[si_r10];
state->r11=DebuggerState.LastStackPointer[si_r11];
state->r12=DebuggerState.LastStackPointer[si_r12];
state->r13=DebuggerState.LastStackPointer[si_r13];
state->r14=DebuggerState.LastStackPointer[si_r14];
state->r15=DebuggerState.LastStackPointer[si_r15];
memcpy(state->fxstate, (void *)&DebuggerState.LastStackPointer[si_xmm], 512);
#endif
//generally speaking, NOTHING should touch the esp register, but i'll provide it anyhow
if ((DebuggerState.LastStackPointer[si_cs] & 3) == 3) //if usermode code segment
{
//priv level change, so the stack info was pushed as well
state->rsp=DebuggerState.LastStackPointer[si_esp];
state->ss=DebuggerState.LastStackPointer[si_ss];
}
else
{
//kernelmode stack, yeah, it's really useless here since changing it here only means certain doom, but hey...
state->rsp=(UINT_PTR)(DebuggerState.LastStackPointer)-4;
state->ss=getSS();; //unchangeble by the user
}
state->rip=DebuggerState.LastStackPointer[si_eip];
state->cs=DebuggerState.LastStackPointer[si_cs];
state->ds=DebuggerState.LastStackPointer[si_ds];
state->es=DebuggerState.LastStackPointer[si_es];
#ifdef AMD64
state->fs=0;
state->gs=0;
#else
state->fs=DebuggerState.LastStackPointer[si_fs];
state->gs=DebuggerState.LastStackPointer[si_gs];
#endif
state->dr0=DebuggerState.LastRealDebugRegisters[0];
state->dr1=DebuggerState.LastRealDebugRegisters[1];
state->dr2=DebuggerState.LastRealDebugRegisters[2];
state->dr3=DebuggerState.LastRealDebugRegisters[3];
state->dr6=DebuggerState.LastRealDebugRegisters[4];
state->dr7=DebuggerState.LastRealDebugRegisters[5];
/*if (DebuggerState.storeLBR)
{
//DbgPrint("Copying the LBR stack to usermode\n");
//DbgPrint("storeLBR_max=%d\n", DebuggerState.storeLBR_max);
for (state->LBR_Count=0; state->LBR_Count<DebuggerState.storeLBR_max; state->LBR_Count++ )
{
//DbgPrint("DebuggerState.LastLBRStack[%d]=%x\n", state->LBR_Count, DebuggerState.LastLBRStack[state->LBR_Count]);
state->LBR[state->LBR_Count]=DebuggerState.LastLBRStack[state->LBR_Count];
if (state->LBR[state->LBR_Count]==0) //no need to copy once a 0 has been reached
break;
}
}
else*/
state->LBR_Count=0;
return STATUS_SUCCESS;
}
else
{
//DbgPrint("debugger_getDebuggerState was called while DebuggerState.LastStackPointer was still NULL");
return STATUS_UNSUCCESSFUL;
}
}
NTSTATUS debugger_setDebuggerState(PDebugStackState state)
{
if (DebuggerState.LastStackPointer)
{
DebuggerState.LastStackPointer[si_eflags]=(UINT_PTR)state->rflags;
//DbgPrint("have set eflags to %x\n",DebuggerState.LastStackPointer[si_eflags]);
DebuggerState.LastStackPointer[si_eax]=(UINT_PTR)state->rax;
DebuggerState.LastStackPointer[si_ebx]=(UINT_PTR)state->rbx;
DebuggerState.LastStackPointer[si_ecx]=(UINT_PTR)state->rcx;
DebuggerState.LastStackPointer[si_edx]=(UINT_PTR)state->rdx;
DebuggerState.LastStackPointer[si_esi]=(UINT_PTR)state->rsi;
DebuggerState.LastStackPointer[si_edi]=(UINT_PTR)state->rdi;
DebuggerState.LastStackPointer[si_ebp]=(UINT_PTR)state->rbp;
//generally speaking, NOTHING should touch the esp register, but i'll provide it anyhow
if ((DebuggerState.LastStackPointer[si_cs] & 3) == 3) //if usermode code segment
{
//priv level change, so the stack info was pushed as well
DebuggerState.LastStackPointer[si_esp]=(UINT_PTR)state->rsp;
//don't mess with ss
}
else
{
//no change in kernelmode allowed
}
DebuggerState.LastStackPointer[si_eip]=(UINT_PTR)state->rip;
DebuggerState.LastStackPointer[si_cs]=(UINT_PTR)state->cs;
DebuggerState.LastStackPointer[si_ds]=(UINT_PTR)state->ds;
DebuggerState.LastStackPointer[si_es]=(UINT_PTR)state->es;
#ifndef AMD64
DebuggerState.LastStackPointer[si_fs]=(UINT_PTR)state->fs;
DebuggerState.LastStackPointer[si_gs]=(UINT_PTR)state->gs;
#else //don't touch fs or gs in 64-bit
DebuggerState.LastStackPointer[si_r8]=(UINT_PTR)state->r8;
DebuggerState.LastStackPointer[si_r9]=(UINT_PTR)state->r9;
DebuggerState.LastStackPointer[si_r10]=(UINT_PTR)state->r10;
DebuggerState.LastStackPointer[si_r11]=(UINT_PTR)state->r11;
DebuggerState.LastStackPointer[si_r12]=(UINT_PTR)state->r12;
DebuggerState.LastStackPointer[si_r13]=(UINT_PTR)state->r13;
DebuggerState.LastStackPointer[si_r14]=(UINT_PTR)state->r14;
DebuggerState.LastStackPointer[si_r15]=(UINT_PTR)state->r15;
memcpy((void *)&DebuggerState.LastStackPointer[si_xmm], state->fxstate, 512);
#endif
if (!DebuggerState.globalDebug)
{
//no idea why someone would want to use this method, but it's in (for NON globaldebug only)
//updating this array too just so the user can see it got executed. (it eases their state of mind...)
DebuggerState.LastRealDebugRegisters[0]=(UINT_PTR)state->dr0;
DebuggerState.LastRealDebugRegisters[1]=(UINT_PTR)state->dr1;
DebuggerState.LastRealDebugRegisters[2]=(UINT_PTR)state->dr2;
DebuggerState.LastRealDebugRegisters[3]=(UINT_PTR)state->dr3;
DebuggerState.LastRealDebugRegisters[4]=(UINT_PTR)state->dr6;
DebuggerState.LastRealDebugRegisters[5]=(UINT_PTR)state->dr7;
//no setting of the DebugRegs here
}
}
else
{
//DbgPrint("debugger_setDebuggerState was called while DebuggerState.LastStackPointer was still NULL");
return STATUS_UNSUCCESSFUL;
}
return STATUS_SUCCESS;
}
int breakpointHandler_kernel(UINT_PTR *stackpointer, UINT_PTR *currentdebugregs, UINT_PTR *LBR_Stack, int causedbyDBVM)
//Notice: This routine is called when interrupts are enabled and the GD bit has been set if globaL DEBUGGING HAS BEEN USED
//Interrupts are enabled and should be at passive level, so taskswitching is possible
{
NTSTATUS r=STATUS_UNSUCCESSFUL;
int handled=0; //0 means let the OS handle it
LARGE_INTEGER timeout;
timeout.QuadPart=-100000;
//DbgPrint("breakpointHandler for kernel breakpoints\n");
#ifdef AMD64
//DbgPrint("cs=%x ss=%x ds=%x es=%x fs=%x gs=%x\n",getCS(), getSS(), getDS(), getES(), getFS(), getGS());
//DbgPrint("fsbase=%llx gsbase=%llx gskernel=%llx\n", readMSR(0xc0000100), readMSR(0xc0000101), readMSR(0xc0000102));
//DbgPrint("rbp=%llx\n", getRBP());
//DbgPrint("gs:188=%llx\n", __readgsqword(0x188));
//DbgPrint("causedbyDBVM=%d\n", causedbyDBVM);
#endif
if (KeGetCurrentIrql()==0)
{
//crititical section here
if ((stackpointer[si_cs] & 3)==0)
{
//DbgPrint("Going to wait in a kernelmode routine\n");
}
//block other threads from breaking until this one has been handled
while (r != STATUS_SUCCESS)
{
r=KeWaitForSingleObject(&debugger_event_CanBreak,Executive, KernelMode, FALSE, NULL);
//check r and handle specific events
//DbgPrint("Woke up. r=%x\n",r);
}
if ((stackpointer[si_cs] & 3)==0)
{
//DbgPrint("Woke up in a kernelmode routine\n");
}
//We're here, let's notify the usermode debugger of our situation
//first store the stackpointer so it can be manipulated externally
DebuggerState.LastStackPointer=stackpointer;
DebuggerState.LastRealDebugRegisters=currentdebugregs;
/*DebuggerState.LastLBRStack=LBR_Stack;*/
DebuggerState.LastThreadID=PsGetCurrentThreadId();
DebuggerState.CausedByDBVM = causedbyDBVM;
//notify usermode app that this thread has halted due to a debug event
KeSetEvent(&debugger_event_WaitForDebugEvent,0,FALSE);
//wait for event from usermode that debgu event has been handled
//KeWaitForSingleObject();
//continue with state
while (1)
{
//LARGE_INTEGER wt;
NTSTATUS s=STATUS_UNSUCCESSFUL;
//wt.QuadPart=-10000000LL;
//s=KeDelayExecutionThread(KernelMode, FALSE, &wt);
//DbgPrint("Waiting...\n");
while (s != STATUS_SUCCESS)
{
s=KeWaitForSingleObject(&debugger_event_WaitForContinue, Executive, KernelMode, FALSE, NULL);
//DbgPrint("KeWaitForSingleObject=%x\n",s);
}
if (s==STATUS_SUCCESS)
{
if (DebuggerState.handledlastevent)
{
//DbgPrint("handledlastevent=TRUE");
handled=1;
}
else
handled=0;
break;
}
}
DebuggerState.LastStackPointer=NULL; //NULL the stackpointer so routines know it should not be called
//i'm done, let other threads catch it
KeSetEvent(&debugger_event_CanBreak, 0, FALSE);
//DbgPrint("Returning after a wait. handled=%d and eflags=%x\n",handled, stackpointer[si_eflags]);
if ((stackpointer[si_cs] & 3)==0) //check rpl of cs
{
//DbgPrint("and in kernelmode\n");
}
return handled;
}
else
{
//DbgPrint("Breakpoint wasn't at passive level. Screw this, i'm not going to break here\n");
return 1;
}
}
int interrupt1_handler(UINT_PTR *stackpointer, UINT_PTR *currentdebugregs)
{
HANDLE CurrentProcessID=PsGetCurrentProcessId();
UINT_PTR originaldr6=currentdebugregs[4];
DebugReg6 _dr6=*(DebugReg6 *)&currentdebugregs[4];
UINT_PTR LBR_Stack[16]; //max 16
// DebugReg7 _dr7=*(DebugReg7 *)&currentdebugregs[5];
int causedbyDBVM = vmxusable && vmx_causedCurrentDebugBreak();
/*
if (cpu_familyID==0x6)
{
if (DebuggerState.storeLBR)
{
//fetch the lbr stack
int MSR_LASTBRANCH_TOS=0x1c9;
int MSR_LASTBRANCH_0=0x40;
int i;
int count;
i=(int)__readmsr(MSR_LASTBRANCH_TOS);
count=0;
while (count<DebuggerState.storeLBR_max)
{
UINT64 x;
x=__readmsr(MSR_LASTBRANCH_0+i);
LBR_Stack[count]=(UINT_PTR)x;
__writemsr(MSR_LASTBRANCH_0+i,0); //it has been read out, so can be erased now
count++;
i++;
i=i % DebuggerState.storeLBR_max;
}
}
}
*/
//DbgPrint("interrupt1_handler(%p). DR6=%x (%x) DR7=%x %x:%p\n", interrupt1_handler, originaldr6, debugger_dr6_getValueDword(), debugger_dr7_getValueDword(), stackpointer[si_cs], (void*)(stackpointer[si_eip]));
//check if this break should be handled or not
if (DebuggerState.globalDebug)
{
//DbgPrint("DebuggerState.globalDebug=TRUE\n");
//global debugging is being used
if (_dr6.BD)
{
_dr6.BD = 0;
debugger_dr6_setValue(*(UINT_PTR *)&_dr6);
//The debug registers are being accessed, emulate it with DebuggerState.FakedDebugRegisterState[cpunr()].DRx
if ((stackpointer[si_cs] & 3)==0)
{
int instructionPointer;
#ifdef AMD64
int prefixpointer;
#endif
int currentcpunr = cpunr();
int debugregister;
int generalpurposeregister;
unsigned char *instruction = (unsigned char *)stackpointer[si_eip];
//unset this flag in DR6
_dr6.BD = 0;
debugger_dr6_setValue(*(UINT_PTR *)&_dr6);
if (DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue)
{
((EFLAGS *)&stackpointer[si_eflags])->RF = 1; //repeat this instruction and don't break
return 2;
}
//DbgPrint("handler: Setting fake dr6 to %x\n",*(UINT_PTR *)&_dr6);
DebuggerState.FakedDebugRegisterState[cpunr()].DR6 = *(UINT_PTR *)&_dr6;
for (instructionPointer = 0; instruction[instructionPointer] != 0x0f; instructionPointer++); //find the start of the instruction, skipping prefixes etc...
//we now have the start of the instruction.
//Find out which instruction it is, and which register is used
debugregister = (instruction[instructionPointer + 2] >> 3) & 7;
generalpurposeregister = instruction[instructionPointer + 2] & 7;
#ifdef AMD64
for (prefixpointer = 0; prefixpointer < instructionPointer; prefixpointer++)
{
//check for a REX.B prefix (0x40 + 0x1 : 0x41)
if ((instruction[prefixpointer] & 0x41) == 0x41)
{
//rex.b prefix is used, r8 to r15 are being accessed
generalpurposeregister += 8;
}
}
#endif
//DbgPrint("debugregister=%d, generalpurposeregister=%d\n",debugregister,generalpurposeregister);
if (instruction[instructionPointer + 1] == 0x21)
{
UINT_PTR drvalue = 0;
//DbgPrint("read opperation\n");
//21=read
switch (debugregister)
{
case 0:
drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR0;
//DbgPrint("Reading DR0 (returning %x real %x)\n", drvalue, currentdebugregs[0]);
break;
case 1:
drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR1;
break;
case 2:
drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR2;
break;
case 3:
drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR3;
break;
case 4:
case 6:
drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR6;
//DbgPrint("reading dr6 value:%x\n",drvalue);
break;
case 5:
case 7:
drvalue = DebuggerState.FakedDebugRegisterState[cpunr()].DR7;
break;
default:
//DbgPrint("Invalid debugregister\n");
drvalue = 0;
break;
}
switch (generalpurposeregister)
{
case 0:
stackpointer[si_eax] = drvalue;
break;
case 1:
stackpointer[si_ecx] = drvalue;
break;
case 2:
stackpointer[si_edx] = drvalue;
break;
case 3:
stackpointer[si_ebx] = drvalue;
break;
case 4:
if ((stackpointer[si_cs] & 3) == 3) //usermode dr access ?
stackpointer[si_esp] = drvalue;
else
stackpointer[si_stack_esp] = drvalue;
break;
case 5:
stackpointer[si_ebp] = drvalue;
break;
case 6:
stackpointer[si_esi] = drvalue;
break;
case 7:
stackpointer[si_edi] = drvalue;
break;
#ifdef AMD64
case 8:
stackpointer[si_r8] = drvalue;
break;
case 9:
stackpointer[si_r9] = drvalue;
break;
case 10:
stackpointer[si_r10] = drvalue;
break;
case 11:
stackpointer[si_r11] = drvalue;
break;
case 12:
stackpointer[si_r12] = drvalue;
break;
case 13:
stackpointer[si_r13] = drvalue;
break;
case 14:
stackpointer[si_r14] = drvalue;
break;
case 15:
stackpointer[si_r15] = drvalue;
break;
#endif
}
}
else
if (instruction[instructionPointer + 1] == 0x23)
{
//23=write
UINT_PTR gpvalue = 0;
//DbgPrint("Write operation\n");
switch (generalpurposeregister)
{
case 0:
gpvalue = stackpointer[si_eax];
break;
case 1:
gpvalue = stackpointer[si_ecx];
break;
case 2:
gpvalue = stackpointer[si_edx];
break;
case 3:
gpvalue = stackpointer[si_ebx];
break;
case 4:
if ((stackpointer[si_cs] & 3) == 3)
gpvalue = stackpointer[si_esp];
break;
case 5:
gpvalue = stackpointer[si_ebp];
break;
case 6:
gpvalue = stackpointer[si_esi];
break;
case 7:
gpvalue = stackpointer[si_edi];
break;
#ifdef AMD64
case 8:
gpvalue = stackpointer[si_r8];
break;
case 9:
gpvalue = stackpointer[si_r9];
break;
case 10:
gpvalue = stackpointer[si_r10];
break;
case 11:
gpvalue = stackpointer[si_r11];
break;
case 12:
gpvalue = stackpointer[si_r12];
break;
case 13:
gpvalue = stackpointer[si_r13];
break;
case 14:
gpvalue = stackpointer[si_r14];
break;
case 15:
gpvalue = stackpointer[si_r15];
break;
default:
//DbgPrint("Invalid register value\n");
break;
#endif
}
//gpvalue now contains the value to set the debug register
switch (debugregister)
{
case 0:
//DbgPrint("Writing DR0. Original value=%x new value=%x\n", currentdebugregs[0], gpvalue);
debugger_dr0_setValue(gpvalue);
DebuggerState.FakedDebugRegisterState[cpunr()].DR0 = debugger_dr0_getValue();
break;
case 1:
debugger_dr1_setValue(gpvalue);
DebuggerState.FakedDebugRegisterState[cpunr()].DR1 = debugger_dr1_getValue();
break;
case 2:
debugger_dr2_setValue(gpvalue);
DebuggerState.FakedDebugRegisterState[cpunr()].DR2 = debugger_dr2_getValue();
break;
case 3:
debugger_dr3_setValue(gpvalue);
DebuggerState.FakedDebugRegisterState[cpunr()].DR3 = debugger_dr3_getValue();
break;
case 4:
case 6:
//DbgPrint("Setting dr6 to %x (was %x)\n", gpvalue, DebuggerState.FakedDebugRegisterState[cpunr()].DR6);
_dr6 = *(DebugReg6 *)&gpvalue;
//if (_dr6.BD) DbgPrint("Some code wants to set the BD flag to 1\n");
debugger_dr6_setValue(gpvalue);
DebuggerState.FakedDebugRegisterState[cpunr()].DR6 = debugger_dr6_getValueDword();
if (_dr6.BD)
{
_dr6.BD = 0;
debugger_dr6_setValue(gpvalue);
}
break;
case 5:
case 7:
//make sure it doesn't set the GD flag here
//DbgPrint("DR7 write\n");
//if (generalpurposeregister == 15)
//{
// while (1); //patchguard
//}
//if (DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue)
//{
// DbgPrint("Was in epilogue\n");
//}
//check for invalid bits and raise a GPF if invalid
gpvalue = (gpvalue | 0x400) & (~(1 << 13)); //unset the GD value
//gpvalue=0xf0401;
debugger_dr7_setValueDword(gpvalue);
DebuggerState.FakedDebugRegisterState[cpunr()].DR7 = debugger_dr7_getValueDword();
break;
}
}
else
{
//DbgPrint("Some unknown instruction accessed the debug registers?\n");
//if (CurrentProcessID==(HANDLE)(UINT_PTR)DebuggerState.debuggedProcessID)
// DbgPrint("Happened inside the target process\n");
//DbgPrint("interrupt1_handler dr6=%x (original=%x) dr7=%d\n",_dr6, originaldr6, _dr7);
//DbgPrint("eip=%x\n",stackpointer[si_eip]);
}
//adjust eip to after this instruction
stackpointer[si_eip] += instructionPointer + 3; //0f xx /r
return 1; //don't tell windows about it
}
else
{
//DbgPrint("DR6.BD == 1 in USERMODE! WTF\n");
_dr6.BD = 0;
debugger_dr6_setValue(*(UINT_PTR *)&_dr6);
DebuggerState.FakedDebugRegisterState[cpunr()].DR6 = debugger_dr6_getValueDword();
}
}
}
if (DebuggerState.isSteppingTillClear) //this doesn't really work because when the state comes back to interruptable the system has a critical section lock on the GUI, so yeah... I really need a DBVM display driver for this
{
if ((((PEFLAGS)&stackpointer[si_eflags])->IF == 0) || (KeGetCurrentIrql() != PASSIVE_LEVEL))
{
((PEFLAGS)&stackpointer[si_eflags])->TF = 1;
((PEFLAGS)&stackpointer[si_eflags])->RF = 1;
debugger_dr6_setValue(0xffff0ff0);
return 1;
}
DebuggerState.isSteppingTillClear = FALSE;
}
if (DebuggerState.isDebugging)
{
//DbgPrint("DebuggerState.isDebugging\n");
//check if this should break
if (CurrentProcessID == (HANDLE)(UINT_PTR)DebuggerState.debuggedProcessID)
{
UINT_PTR originaldebugregs[6];
UINT64 oldDR7 = getDR7();
if ((((PEFLAGS)&stackpointer[si_eflags])->IF == 0) || (KeGetCurrentIrql() != PASSIVE_LEVEL))
{
//There's no way to display the state to the usermode part of CE
//DbgPrint("int1 at unstoppable location");
if (!KernelCodeStepping)
{
((PEFLAGS)&stackpointer[si_eflags])->TF = 0; //just give up stepping
// DbgPrint("Quitting this");
}
else
{
// DbgPrint("Stepping until valid\n");
((PEFLAGS)&stackpointer[si_eflags])->TF = 1; //keep going until a valid state
DebuggerState.isSteppingTillClear = TRUE; //Just in case a taskswitch happens right after enabling passive level with interrupts
}
((PEFLAGS)&stackpointer[si_eflags])->RF = 1;
debugger_dr6_setValue(0xffff0ff0);
return 1;
}
DebuggerState.isSteppingTillClear = FALSE;
//DbgPrint("CurrentProcessID==(HANDLE)(UINT_PTR)DebuggerState.debuggedProcessID\n");
if (DebuggerState.globalDebug)
{
originaldebugregs[0] = DebuggerState.FakedDebugRegisterState[cpunr()].DR0;
originaldebugregs[1] = DebuggerState.FakedDebugRegisterState[cpunr()].DR1;
originaldebugregs[2] = DebuggerState.FakedDebugRegisterState[cpunr()].DR2;
originaldebugregs[3] = DebuggerState.FakedDebugRegisterState[cpunr()].DR3;
originaldebugregs[4] = DebuggerState.FakedDebugRegisterState[cpunr()].DR6;
originaldebugregs[5] = DebuggerState.FakedDebugRegisterState[cpunr()].DR7;
}
//DbgPrint("BP in target process\n");
//no extra checks if it's caused by the debugger or not. That is now done in the usermode part
//if (*(PEFLAGS)(&stackpointer[si_eflags]).IF)
/*
if (((PEFLAGS)&stackpointer[si_eflags])->IF==0)
{
//DbgPrint("Breakpoint while interrupts are disabled: %x\n",stackpointer[si_eip]);
//An breakpoint happened while IF was 0. Step through the code untill IF is 1
((PEFLAGS)&stackpointer[si_eflags])->RF=1;
((PEFLAGS)&stackpointer[si_eflags])->TF=1; //keep going until IF=1
DbgPrint("IF==0\n");
return 1; //don't handle it but also don't tell windows
}*/
//set the real debug registers to what it is according to the guest (so taskswitches take over these values) .... shouldn't be needed as global debug is on which fakes that read...
if (DebuggerState.globalDebug)
{
//enable the GD flag for taskswitches that will occur as soon as interrupts are enabled
//this also means: DO NOT EDIT THE DEBUG REGISTERS IN GLOBAL DEBUG MODE at this point. Only in the epilogue
if (!DebuggerState.stoppingTheDebugger) //This is set when the driver is unloading. So do NOT set it back then
debugger_dr7_setGD(DebuggerState.globalDebug);
}
else
{
//unset ALL debug registers before enabling taskswitching. Just re-enable it when back when interrupts are disabled again
debugger_dr7_setValueDword(0x400);
debugger_dr0_setValue(0);
debugger_dr1_setValue(0);
debugger_dr2_setValue(0);
debugger_dr3_setValue(0);
debugger_dr6_setValue(0xffff0ff0);
}
//start the windows taskswitching mode
//if (1) return 1;
//save the state of the thread to a place that won't get overwritten
//todo: breaks 32-bit
//int i;
BOOL NeedsToGrowStackList = FALSE;
PSavedStack SelectedStackEntry = NULL;
/*
csEnter(&StacksCS);
for (i = 0; i < StackCount; i++)
{
if (Stacks[i]->inuse == FALSE)
{
SelectedStackEntry = Stacks[i];
SelectedStackEntry->inuse = TRUE;
RtlCopyMemory(SelectedStackEntry->stacksnapshot, stackpointer, 600 * 8);
if (i > StackCount / 2)
NeedsToGrowStackList = TRUE;
break;
}
}
csLeave(&StacksCS);
enableInterrupts();
//grow stack if needed
if (NeedsToGrowStackList)
debugger_growstack();
*/
{
int rs=1;
//DbgPrint("calling breakpointHandler_kernel\n");
if (SelectedStackEntry == NULL) //fuck
rs = breakpointHandler_kernel(stackpointer, currentdebugregs, LBR_Stack, causedbyDBVM);
else
rs = breakpointHandler_kernel((UINT_PTR *)(SelectedStackEntry->stacksnapshot), currentdebugregs, LBR_Stack, causedbyDBVM);
//DbgPrint("After handler\n");
/*
if (SelectedStackEntry) //restore the stack
{
RtlCopyMemory(stackpointer, SelectedStackEntry->stacksnapshot, 600 * 8);
SelectedStackEntry->inuse = FALSE;
}
*/
//DbgPrint("rs=%d\n",rs);
disableInterrupts();
//restore the
//we might be on a different CPU now
if (DebuggerState.globalDebug)
{
DebuggerState.FakedDebugRegisterState[cpunr()].DR0=originaldebugregs[0];
DebuggerState.FakedDebugRegisterState[cpunr()].DR1=originaldebugregs[1];
DebuggerState.FakedDebugRegisterState[cpunr()].DR2=originaldebugregs[2];
DebuggerState.FakedDebugRegisterState[cpunr()].DR3=originaldebugregs[3];
DebuggerState.FakedDebugRegisterState[cpunr()].DR6=originaldebugregs[4];
DebuggerState.FakedDebugRegisterState[cpunr()].DR7=originaldebugregs[5];
}
else
{
/*if (getDR7() != oldDR7)
{
DbgPrint("Something changed DR7. old=%llx new=%llx\n",oldDR7, getDR7());
}*/
//set the debugregisters to what they where set to before taskswitching was enable
//with global debug this is done elsewhere
debugger_dr0_setValue(currentdebugregs[0]);
debugger_dr1_setValue(currentdebugregs[1]);
debugger_dr2_setValue(currentdebugregs[2]);
debugger_dr3_setValue(currentdebugregs[3]);
debugger_dr6_setValue(currentdebugregs[4]);
if ((currentdebugregs[5] >> 13) & 1)
{
// DbgPrint("WTF? GD is 1 in currentdebugregs[5]: %llx\n", currentdebugregs[5]);
}
else
debugger_dr7_setValue(*(DebugReg7 *)&currentdebugregs[5]);
}
return rs;
}
}
else
{
//DbgPrint("Not the debugged process (%x != %x)\n",CurrentProcessID,DebuggerState.debuggedProcessID );
//check if this break is due to a breakpoint ce has set. (during global debug threadsurfing))
//do that by checking if the breakpoint condition exists in the FAKE dr7 registers
//if so, let windows handle it, if not, it is caused by ce, which then means, skip (so execute normally)
if (DebuggerState.globalDebug)
{
DebugReg6 dr6=debugger_dr6_getValue();
DebugReg7 dr7=*(DebugReg7 *)&DebuggerState.FakedDebugRegisterState[cpunr()].DR7;
//real dr6 //fake dr7
if ((dr6.B0) && (!(dr7.L0 || dr7.G0))) { /*DbgPrint("setting RF because of B0\n");*/ ((PEFLAGS)&stackpointer[si_eflags])->RF=1; return 1; } //break caused by DR0 and not expected by the current process, ignore this bp and continue
if ((dr6.B1) && (!(dr7.L1 || dr7.G1))) { /*DbgPrint("setting RF because of B1\n");*/ ((PEFLAGS)&stackpointer[si_eflags])->RF=1; return 1; } // ... DR1 ...
if ((dr6.B2) && (!(dr7.L2 || dr7.G2))) { /*DbgPrint("setting RF because of B2\n");*/ ((PEFLAGS)&stackpointer[si_eflags])->RF=1; return 1; } // ... DR2 ...
if ((dr6.B3) && (!(dr7.L3 || dr7.G3))) { /*DbgPrint("setting RF because of B3\n");*/ ((PEFLAGS)&stackpointer[si_eflags])->RF=1; return 1; } // ... DR3 ...
}
if (causedbyDBVM)
return 1; //correct PA, bad PID, ignore BP
if (DebuggerState.isSteppingTillClear) //shouldn't happen often
{
//DbgPrint("That thing that shouldn\'t happen often happened\n");
((PEFLAGS)&stackpointer[si_eflags])->TF = 0;
DebuggerState.isSteppingTillClear = 0;
return 1; //ignore
}
//DbgPrint("Returning unhandled. DR6=%x", debugger_dr6_getValueDword());
return 0; //still here, so let windows handle it
}
}
else
return 0; //Let windows handle it
//get the current processid
//is it being debugged
//if yes, check if the breakpoint is something done by me
//if no, exit
}
int interrupt1_centry(UINT_PTR *stackpointer) //code segment 8 has a 32-bit stackpointer
{
UINT_PTR before;//,after;
UINT_PTR currentdebugregs[6]; //used for determining if the current bp is caused by the debugger or not
int handled=0; //if 0 at return, the interupt will be passed down to the operating system
QWORD naddress;
//DbgPrint("interrupt1_centry cpunr=%d esp=%x\n",cpunr(), getRSP());
//bsod crashfix, but also disables kernelmode stepping
IDT idt;
GetIDT(&idt);
naddress = idt.vector[1].wLowOffset + (idt.vector[1].wHighOffset << 16);
#ifdef AMD64
naddress += ((UINT64)idt.vector[1].TopOffset << 32);
#endif
stackpointer[si_errorcode] = (UINT_PTR)naddress; //the errorcode is used as address to call the original function if needed
/*
if (Int1JumpBackLocation.eip != naddress) //no, just fucking no (patchguard will replace all inthandlers with invalid ones and then touch dr7)
{
//todo: the usual, but make sure not to use dbgprint or anything that could trigger a software int
if (DebuggerState.globalDebug)
{
debugger_dr7_setGD(DebuggerState.globalDebug);
stackpointer[si_eip] += 4;
return 1;
}
}
*/
before=getRSP();
//Fetch current debug registers
currentdebugregs[0]=debugger_dr0_getValue();
currentdebugregs[1]=debugger_dr1_getValue();
currentdebugregs[2]=debugger_dr2_getValue();
currentdebugregs[3]=debugger_dr3_getValue();
currentdebugregs[4]=debugger_dr6_getValueDword();
currentdebugregs[5]=debugger_dr7_getValueDword();
handled=interrupt1_handler(stackpointer, currentdebugregs);
//epilogue:
//At the end when returning:
//
//--------------------------------------------------------------------------
//--------------EPILOGUE (AFTER HAVING HANDLED THE BREAKPOINT)--------------
//--------------------------------------------------------------------------
//
disableInterrupts(); //just making sure..
DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=1;
debugger_dr7_setGD(0); //make sure the GD bit is disabled (int1 within int1, oooh the fun..., and yes, THIS itself will cause one too)
DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=1; //just be sure...
//if (inthook_isDBVMHook(1))
//{
//update the int1 return address, could have been changed
//DbgPrint("This was a dbvm hook. Changing if the interrupt return address is still valid\n");
// Int1JumpBackLocation.cs=idt.vector[1].wSelector;
// naddress=idt.vector[1].wLowOffset+(idt.vector[1].wHighOffset << 16);
#ifdef AMD64
// naddress+=((UINT64)idt.vector[1].TopOffset << 32);
#endif
//}
if (DebuggerState.globalDebug) //DR's are only accesses when there are DR's(no idea how it handles breakpoints in a different process...), so set them in each thread even those that don't belong original: && (PsGetCurrentProcessId()==(HANDLE)DebuggerState.debuggedProcessID))
{
//set the breakpoint in this thread.
DebugReg6 dr6=debugger_dr6_getValue();
//DebugReg7 dr7=debugger_dr7_getValue();
DebugReg6 _dr6=*(DebugReg6 *)&DebuggerState.FakedDebugRegisterState[cpunr()].DR6;
DebugReg7 _dr7=*(DebugReg7 *)&DebuggerState.FakedDebugRegisterState[cpunr()].DR7;
int debugregister=0, breakpoint=0;
//first clear the DR6 bits caused by the debugger
if (!handled)
{
//it's going to get sent to windows
if (dr6.BD && _dr7.GD) _dr6.BD=1; //should already have been done, but what the heck...
if (dr6.B0 && (_dr7.L0 || _dr7.G0)) _dr6.B0=1;
if (dr6.B1 && (_dr7.L1 || _dr7.G1)) _dr6.B1=1;
if (dr6.B2 && (_dr7.L2 || _dr7.G2)) _dr6.B2=1;
if (dr6.B3 && (_dr7.L3 || _dr7.G3)) _dr6.B3=1;
_dr6.BS=dr6.BS;
_dr6.BT=dr6.BT;
//DbgPrint("epilogue: Setting fake dr6 to %x (fake=%x)\n",*(DWORD *)&dr6, *(DWORD *)&_dr6);
}
debugger_dr6_setValue(0xffff0ff0);
//set the debug registers of active breakpoints. Doesn't have to be in the specified order. Just find an unused debug registers
//check DebuggerState.FakedDebugRegisterState[cpunumber].DR7 for unused breakpoints
//set state to what the guest thinks it is
debugger_dr0_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR0);
debugger_dr1_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR1);
debugger_dr2_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR2);
debugger_dr3_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR3);
debugger_dr6_setValue(DebuggerState.FakedDebugRegisterState[cpunr()].DR6);
for (breakpoint=0; breakpoint<4; breakpoint++)
{
if (DebuggerState.breakpoint[breakpoint].active)
{
int foundone=0;
// DbgPrint("Want to set breakpoint %d\n",breakpoint);
//find a usable debugregister
while ((debugregister<4) && (foundone==0))
{
if (DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue==0)
{
DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=1;
}
//check if this debugregister is usable
if (((DebuggerState.FakedDebugRegisterState[cpunr()].DR7 >> (debugregister*2)) & 3)==0) //DR7.Gx and DR7.Lx are 0
{
// DbgPrint("debugregister %d is free to be used\n",debugregister);
foundone=1;
//set address
switch (debugregister)
{
case 0:
debugger_dr0_setValue(DebuggerState.breakpoint[breakpoint].address);
_dr7.L0=1;
_dr7.LEN0=DebuggerState.breakpoint[breakpoint].breakLength;
_dr7.RW0=DebuggerState.breakpoint[breakpoint].breakType;
break;
case 1:
debugger_dr1_setValue(DebuggerState.breakpoint[breakpoint].address);
_dr7.L1=1;
_dr7.LEN1=DebuggerState.breakpoint[breakpoint].breakLength;
_dr7.RW1=DebuggerState.breakpoint[breakpoint].breakType;
break;
case 2:
debugger_dr2_setValue(DebuggerState.breakpoint[breakpoint].address);
_dr7.L2=1;
_dr7.LEN2=DebuggerState.breakpoint[breakpoint].breakLength;
_dr7.RW2=DebuggerState.breakpoint[breakpoint].breakType;
break;
case 3:
debugger_dr3_setValue(DebuggerState.breakpoint[breakpoint].address);
_dr7.L3=1;
_dr7.LEN3=DebuggerState.breakpoint[breakpoint].breakLength;
_dr7.RW3=DebuggerState.breakpoint[breakpoint].breakType;
break;
}
}
debugregister++;
}
}
}
debugger_dr7_setValue(_dr7);
//DbgPrint("after:\n");
//DbgPrint("after fake DR0=%x real DR0=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR0, debugger_dr0_getValue());
//DbgPrint("after fake DR1=%x real DR1=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR1, debugger_dr1_getValue());
//DbgPrint("after fake DR2=%x real DR2=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR2, debugger_dr2_getValue());
//DbgPrint("after fake DR3=%x real DR3=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR3, debugger_dr3_getValue());
//DbgPrint("after fake DR6=%x real DR6=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR6, debugger_dr6_getValueDword());
//DbgPrint("after fake DR7=%x real DR7=%x\n",DebuggerState.FakedDebugRegisterState[currentcpunr].DR7, debugger_dr7_getValueDword());
}
else
{
//not global debug, just clear all flags and be done with it
if (handled)
debugger_dr6_setValue(0xffff0ff0);
}
disableInterrupts();
if (handled == 2)
{
//DbgPrint("handled==2\n");
handled = 1; //epilogue = 1 Dr handler
}
else
{
//not handled by the epilogue set DR0, so the actual epilogue
//DbgPrint("handled==1\n");
if (DebuggerState.globalDebug)
{
DebuggerState.FakedDebugRegisterState[cpunr()].inEpilogue=0;
if (!DebuggerState.stoppingTheDebugger)
debugger_dr7_setGD(DebuggerState.globalDebug); //set it back to 1, if not unloading
}
}
//after=getRSP();
//DbgPrint("before=%llx after=%llx\n",before,after);
//DbgPrint("end of interrupt1_centry. eflags=%x", stackpointer[si_eflags]);
//if branch tracing set lbr back on (get's disabled on debug interrupts)
/*
if (DebuggerState.storeLBR)
__writemsr(0x1d9, __readmsr(0x1d9) | 1);
*/
return handled;
}
#ifndef AMD64
_declspec( naked ) void interrupt1_asmentry( void )
//This routine is called upon an interrupt 1, even before windows gets it
{
__asm{
//change the start of the stack so that instructions like setthreadcontext do not affect the stack it when it's frozen and waiting for input
//meaning the setting of debug registers will have to be done with the changestate call
//sub esp,4096
//push [esp+4096+0+16] //optional ss
//push [esp+4096+4+12] //optional esp
//push [esp+4096+8+8] //eflags
//push [esp+4096+12+4] //cs
//push [esp+4096+16+0] //eip
cld //reset the direction flag
//save stack position
push 0 //push an errorcode on the stack so the stackindex can stay the same
push ebp
mov ebp,esp
//save state
pushad
xor eax,eax
mov ax,ds
push eax
mov ax,es
push eax
mov ax,fs
push eax
mov ax,gs
push eax
//save fpu state
//save sse state
mov ax,0x23 //0x10 should work too, but even windows itself is using 0x23
mov ds,ax
mov es,ax
mov gs,ax
mov ax,0x30
mov fs,ax
push ebp
call interrupt1_centry
cmp eax,1 //set flag
//restore state
pop gs
pop fs
pop es
pop ds
popad
pop ebp
je skip_original_int1
add esp,4 //undo errorcode push (add effects eflags, so set it at both locations)
jmp far [Int1JumpBackLocation]
skip_original_int1:
add esp,4 //undo errorcode push
iretd
}
}
#endif