1843 lines
50 KiB
C
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 *)¤tdebugregs[4];
|
|
|
|
UINT_PTR LBR_Stack[16]; //max 16
|
|
// DebugReg7 _dr7=*(DebugReg7 *)¤tdebugregs[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 *)¤tdebugregs[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
|