Files
XBDM/xbdm/dmbreak.c
2025-11-06 15:36:24 -05:00

1425 lines
31 KiB
C

// Copyright (c) 2013 Nathan LeRoux
// Project started early 2011
// If you want to get in touch, shoot me an email <confettimancer@gmail.com>
// I'm currently seeking employment, and figured 'what the hell, its free advertising'
/*
Copyright (c) 2013 Nathan LeRoux
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "dmincludes.h"
DWORD DmpBreakSpinLock;
CRITICAL_SECTION csExceptionStack, csExceptionStackTitle, csDebugMonitorData, csExecState;
PVOID ExceptionStack, ExceptionStackTitle;
LIST_ENTRY leDebugMonitorData, leBreaks;
DMHB HardwareBreakpoint;
DWORD dwExecState = DMN_EXEC_PENDING;
DWORD __declspec(naked) DebugRtlAssert(PANSI_STRING Message)
{
__asm
{
lhz r4, 0(r3)
lwz r3, 4(r3)
twi 31, r0, 26
blr
}
}
VOID __declspec(naked) DbgBreakPoint()
{
__asm
{
twi 31, r0, 22
blr
}
}
VOID __declspec(naked) DebugPrint(PANSI_STRING String)
{
__asm
{
lhz r4, 0(r3)
lwz r3, 4(r3)
twi 31, r0, 20
blr
}
}
VOID __stdcall RtlAssert(LPCSTR FailedAssertion, LPCSTR FileName, ULONG LineNumber, PCHAR Message)
{
char result;
char sz[0x200];
ANSI_STRING as;
_snprintf(sz, sizeof(sz), "\n*** Assertion failed: %s%s\n*** Source File: %s, line %ld\n\n", Message, FailedAssertion, FileName, LineNumber);
RtlInitAnsiString(&as, sz);
for(;;)
{
result = (BYTE)DebugRtlAssert(&as);
if(result == 'b' || result == 'B')
DbgBreakPoint();
else if(result == 'i' || result == 'I')
return;
else if(result == 'T' || result == 't')
ExTerminateThread(STATUS_UNSUCCESSFUL);
}
}
DMHRAPI DwChangeExecState(DWORD dwState, BOOL fAlterThreads, BOOL fTell, BOOL bStop)
{
// dwState = DMN_EXEC_XXX
// fAlterThreads = TRUE to start/stop threads
// fTell = TRUE to tell others, FALSE to hide (notifications)
// bStop = TRUE to add a DM_STOPTHREAD flag to the notification
PLIST_ENTRY ple;
PKTHREAD pthr;
DMTD *pdmtd;
BYTE irql;
int i;
if(dwExecState == dwState)
{
if(dwState == DMN_EXEC_START)
return XBDM_NOTSTOPPED;
else
return XBDM_ALREADYSTOPPED;
}
if(dwState > DMN_EXEC_PENDING_TITLE)
return E_INVALIDARG;
KeEnterCriticalRegion();
dwExecState = dwState;
if(fAlterThreads)
{
irql = KfAcquireSpinLock(&g_dmDebug.KeTitleProcess->ThreadListLock);
ple = g_dmDebug.KeTitleProcess->ThreadListHead.Flink;
for(i = 0;i < 2;i++)
{
while(ple != &g_dmDebug.KeTitleProcess->ThreadListHead
&& ple != &g_dmDebug.KeSystemProcess->ThreadListHead)
{
pthr = CONTAINING_RECORD(ple, KTHREAD, ThreadListEntry);
#if 0
pdmtd = DmGetThreadDmtd((DWORD)pthr->ThreadId);
#else
pdmtd = (DMTD*)pthr->DebugMonitorData;
#endif
if(!pdmtd)
{
FInitThreadDebugData(pthr);
pdmtd = (DMTD*)pthr->DebugMonitorData;
}
ple = ple->Flink;
if(!pdmtd || (pdmtd->DebugFlags & DMFLAG_DEBUGTHREAD)
|| (PsGetCurrentThread()->ThreadId == pthr->ThreadId))
continue;
if(dwState == DMN_EXEC_START)
KeSetEvent(pdmtd->DebugEvent, TRUE, FALSE);
if((dwState == DMN_EXEC_START) & (pdmtd->DebugFlags & DMFLAG_STOPPED))
{
KeResumeThread(pthr);
pdmtd->DebugFlags &= ~DMFLAG_STOPPED;
}
else if((dwState != DMN_EXEC_START) && !(pdmtd->DebugFlags & DMFLAG_STOPPED))
{
KeSuspendThread(pthr);
pdmtd->DebugFlags |= DMFLAG_STOPPED;
}
}
if(i == 0)
{
KfReleaseSpinLock(&g_dmDebug.KeTitleProcess->ThreadListLock, irql);
irql = KfAcquireSpinLock(&g_dmDebug.KeSystemProcess->ThreadListLock);
ple = g_dmDebug.KeSystemProcess->ThreadListHead.Flink;
}
else
KfReleaseSpinLock(&g_dmDebug.KeSystemProcess->ThreadListLock, irql);
}
}
if(fTell)
FNotify(bStop ? (DM_EXEC | DM_STOPTHREAD) : DM_EXEC, dwState);
KeLeaveCriticalRegion();
return XBDM_NOERR;
}
DMHRAPI DmGetThreadInfoEx(DWORD dwThreadId, PDM_THREADINFOEX pdmti)
{
PKTHREAD pthr;
BYTE irql;
DMTD *pdmtd;
if(!pdmti || pdmti->Size < sizeof(DM_THREADINFOEX)) // Kill everything compiled before a certain date? WHY CERTAINLY I SHALL
return E_INVALIDARG;
if(FAILED(ObLookupAnyThreadByThreadId(dwThreadId, &pthr)))
return XBDM_NOTHREAD;
irql = KfAcquireSpinLock(pthr->Process);
ZeroMemory(pdmti + 4, pdmti->Size - 4);
#if 0
pdmtd = DmGetThreadDmtd(dwThreadId);
#else
pdmtd = (DMTD*)pthr->DebugMonitorData;
#endif
pdmti->SuspendCount = pthr->SuspendCount;
pdmti->Priority = pthr->Priority;
pdmti->TlsBase = pthr->TlsData;
pdmti->StartAddress = pthr->StartAddress;
pdmti->StackBase = pthr->StackBase;
pdmti->StackLimit = pthr->StackLimit;
pdmti->CreateTime.dwHighDateTime = pthr->CreateTime.HighPart;
pdmti->CreateTime.dwLowDateTime = pthr->CreateTime.LowPart;
pdmti->StackSlackSpace = 0x540;
if(pdmtd)
{
if(pdmtd->DebugFlags & DMFLAG_STOPPED)
pdmti->SuspendCount--;
pdmti->ThreadNameAddress = pdmtd->ThreadName;
if(pdmti->ThreadNameAddress)
pdmti->ThreadNameLength = strlen(pdmtd->ThreadName) + 1;
else
pdmti->ThreadNameLength = 0;
}
pdmti->CurrentProcessor = pthr->CurrentProcessor;
pdmti->LastError = pthr->LastWin32ErrorCode;
KfReleaseSpinLock(pthr->Process, irql);
ObDereferenceObject(pthr);
return XBDM_NOERR;
}
VOID VSetThreadName(DWORD dwThreadId, LPCSTR szName)
{
DMTD *pdmtd;
PKTHREAD pthr = NULL;
int i;
if(dwThreadId == -1)
pdmtd = DmGetCurrentDmtd();
else
{
if(NT_SUCCESS(ObLookupAnyThreadByThreadId(dwThreadId, &pthr)))
{
#if 0
if(!(pdmtd = DmGetThreadDmtd(dwThreadId)))
#else
if(!(pdmtd = (DMTD*)pthr->DebugMonitorData))
#endif
{
FInitThreadDebugData(pthr);
#if 0
pdmtd = DmGetThreadDmtd(dwThreadId);
#else
pdmtd = (DMTD*)pthr->DebugMonitorData;
#endif
}
}
}
if(pdmtd)
{
if(pdmtd->ThreadName)
DmFreePool(pdmtd->ThreadName);
if(szName)
{
i = strlen(szName) + 1;
pdmtd->ThreadName = (char*)DmAllocatePoolTypeWithTag(i, 'Dmtd', 2);
if(pdmtd->ThreadName)
strcpy_s(pdmtd->ThreadName, i, szName);
}
else
pdmtd->ThreadName = NULL;
}
if(pthr)
ObDereferenceObject(pthr);
}
BOOL FInitThreadDebugData(PKTHREAD pthr)
{
DMTD *pdmtd = (DMTD*)DmAllocatePoolTypeWithTag(sizeof(DMTD), 'Dmtd', 2);
if(!pdmtd)
return FALSE;
ZeroMemory(pdmtd, sizeof(DMTD));
if(pthr->CreateOptions & 0x400)
pdmtd->DebugFlags |= DMFLAG_DEBUGTHREAD;
KeInitializeEvent(&pdmtd->DebugEventData, 0, FALSE);
KeSetEvent(&pdmtd->DebugEventData, TRUE, FALSE);
pdmtd->DebugEvent = &pdmtd->DebugEventData;
pdmtd->dwThreadId = (DWORD)pthr->ThreadId;
#if 0
// Add the data to the list
EnterCriticalSection(&csDebugMonitorData);
pdmtd->Link.Flink = leDebugMonitorData.Flink;
pdmtd->Link.Blink = &leDebugMonitorData;
leDebugMonitorData.Flink->Blink = &pdmtd->Link;
leDebugMonitorData.Flink = &pdmtd->Link;
LeaveCriticalSection(&csDebugMonitorData);
#else
pthr->DebugMonitorData = pdmtd;
#endif
return TRUE;
}
VOID DmFreeThreadData(DMTD *pdmtd)
{
#if 0
// Patch up the hole
EnterCriticalSection(&csDebugMonitorData);
pdmtd->Link.Flink->Blink = pdmtd->Link.Blink;
pdmtd->Link.Blink->Flink = pdmtd->Link.Flink;
LeaveCriticalSection(&csDebugMonitorData);
#endif
if(!pdmtd)
return;
if(pdmtd->ThreadName)
DmFreePool(pdmtd->ThreadName);
DmFreePool(pdmtd);
}
#if 0
DMTD *DmGetThreadDmtd(DWORD dwThreadId)
{
DMTD *pdmtd = NULL;
PLIST_ENTRY ple;
EnterCriticalSection(&csDebugMonitorData);
ple = leDebugMonitorData.Flink;
while(ple != &leDebugMonitorData)
{
pdmtd = CONTAINING_RECORD(ple, DMTD, Link);
ple = ple->Flink;
if(pdmtd->dwThreadId == dwThreadId)
break;
else
pdmtd = NULL;
}
LeaveCriticalSection(&csDebugMonitorData);
return pdmtd;
}
#endif
DMTD *DmGetCurrentDmtd()
{
#if 0
DMTD *pdmtd = NULL;
PLIST_ENTRY ple;
EnterCriticalSection(&csDebugMonitorData);
ple = leDebugMonitorData.Flink;
while(ple != &leDebugMonitorData)
{
pdmtd = CONTAINING_RECORD(ple, DMTD, Link);
ple = ple->Flink;
if(pdmtd->dwThreadId == (DWORD)PsGetCurrentThread()->ThreadId)
break;
else
pdmtd = NULL;
}
LeaveCriticalSection(&csDebugMonitorData);
return pdmtd;
#else
return (DMTD*)PsGetCurrentThread()->DebugMonitorData;
#endif
}
VOID DbgOut(LPCSTR fmt, ...)
{
char sz[0x200];
va_list va;
ANSI_STRING as;
va_start(va, fmt);
RtlVsnprintf(sz, sizeof(sz), fmt, va);
va_end(va);
if(KeGetCurrentIrql() == PASSIVE_LEVEL)
{
RtlInitAnsiString(&as, sz);
DebugPrint(&as);
}
}
BOOL ResumeAfterBreakpoint(PCONTEXT pcr)
{
BOOL fRet = FALSE;
PDMBP bp = FFindBreak((PVOID)pcr->Iar, TRUE);
if(bp)
{
fRet = TRUE;
DoDisableBreak(bp);
// Single step exceptions
pcr->Msr |= 0x400;
}
return fRet;
}
VOID PrepareToStop()
{
DMTD *pdmtd = DmGetCurrentDmtd();
if(!pdmtd)
ASSERT(FALSE);
KeResetEvent(pdmtd->DebugEvent);
DwChangeExecState(DMN_EXEC_STOP, TRUE, TRUE, FALSE);
}
BOOL FStopAtException()
{
BOOL fRet = TRUE;
DMTD *pdmtd = DmGetCurrentDmtd();
NTSTATUS st;
if(!pdmtd)
ASSERT(FALSE);
pdmtd->DebugFlags |= DMFLAG_CONTINUEABLE;
st = KeWaitForSingleObject(pdmtd->DebugEvent, 3, 1, 0, NULL);
ASSERT(NT_SUCCESS(st));
pdmtd->DebugFlags &= ~DMFLAG_CONTINUEABLE;
fRet = pdmtd->DebugFlags & DMFLAG_EXCEPTION;
pdmtd->DebugFlags &= ~DMFLAG_EXCEPTION;
return fRet; // True means that the debugger did not handle the exception
}
BOOL FTrapNotify(DWORD dwMask, ULONG_PTR ulpParam, BOOL fStop, BOOL *pPassAlong)
{
BOOL fRet = FALSE;
DMTD *pdmtd = DmGetCurrentDmtd();
if(!pdmtd)
return FALSE;
if((KeGetCurrentIrql() != PASSIVE_LEVEL) || (pdmtd->DebugFlags & DMFLAG_DEBUGTHREAD))
fStop = FALSE;
else if(!g_dmGlobals.bDebugging)
fStop = FALSE; // TODO: get kd working so i can check if kd is connected before passing to it
if(fStop)
PrepareToStop();
pdmtd->StopReason = dwMask & DM_NOTIFICATIONMASK;
FNotify(fStop ? (dwMask | DM_STOPTHREAD) : dwMask, ulpParam);
if(fStop)
{
fRet = FStopAtException();
if(!fRet && pPassAlong) // Debugger did not handle, thus pass it along
*pPassAlong = FALSE;
}
pdmtd->StopReason = 0;
return fRet;
}
VOID DoModuleWait()
{
DWORD dwFlags;
LARGE_INTEGER li;
PLARGE_INTEGER pli;
DMTD* pdmtd = DmGetCurrentDmtd();
dwFlags = g_dmGlobals.dwBootFlags & (DMBOOT_WAIT | DMBOOT_STOP);
g_dmGlobals.dwBootFlags &= ~(DMBOOT_WAIT | DMBOOT_STOP);
g_dmGlobals.bDirty = TRUE;
if(!dwFlags || !pdmtd || (KeGetCurrentIrql() != PASSIVE_LEVEL))
return;
if(dwFlags == DMBOOT_STOP)
pli = NULL;
else
{
pli = &li;
li.QuadPart = -150000000;
}
KeResetEvent(pdmtd->DebugEvent);
DwChangeExecState(DMN_EXEC_PENDING_TITLE, TRUE, TRUE, TRUE);
if(KeWaitForSingleObject(pdmtd->DebugEvent, 3, 1, FALSE, pli) == STATUS_TIMEOUT)
DwChangeExecState(DMN_EXEC_START, TRUE, TRUE, FALSE);
}
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
BOOL DmTrapHandlerImp(PKTRAP_FRAME ptf, PKEXCEPTION_FRAME pef,
PCONTEXT pcr, BOOL fSecondChance, PVOID Unused)
{
BOOL fPassAlong = TRUE;
BOOL fRet = TRUE;
DMTD *pdmtd = DmGetCurrentDmtd();
PIMAGE_NT_HEADERS pinh;
PANSI_STRING pas;
BOOL fSkip = TRUE;
pdmtd = DmGetCurrentDmtd();
if(pdmtd)
{
pdmtd->Context = pcr;
pdmtd->Exception = pef;
if(pef->Status == STATUS_BREAKPOINT)
{
if(pef->ExceptionInformation[0] == BREAKPOINT_PRINT)
{
DMN_DEBUGSTR dmds;
dmds.ThreadId = (DWORD)PsGetCurrentThread()->ThreadId;
dmds.String = (LPCSTR)pcr->Gpr3;
dmds.Length = (DWORD)pcr->Gpr4;
FTrapNotify(DM_DEBUGSTR, (ULONG_PTR)&dmds, g_dmGlobals.dwStopFlags & DMSTOP_DEBUGSTR, NULL);
}
else if(pef->ExceptionInformation[0] == BREAKPOINT_LOAD_SYMBOLS)
{
if(pcr->Gpr3 && pcr->Gpr4)
{
DMN_MODLOAD_EX dmmd;
LPDWORD lp;
ZeroMemory(&dmmd, sizeof(dmmd));
dmmd.SizeOfStruct = sizeof(dmmd);
pas = (PANSI_STRING)pcr->Gpr3;
lp = (LPDWORD)pcr->Gpr4;
pinh = RtlImageNtHeader((PVOID)lp[0]);
strncpy_s(dmmd.Name, 260, pas->Buffer, pas->Length);
dmmd.BaseAddress = (PVOID)lp[0];
dmmd.Size = lp[3];
dmmd.CheckSum = _byteswap_ulong(pinh->OptionalHeader.CheckSum);
dmmd.Flags = 0;
dmmd.OriginalSize = _byteswap_ulong(pinh->OptionalHeader.SizeOfImage);
dmmd.PDataAddress = (LPDWORD)RtlImageDirectoryEntryToData(dmmd.BaseAddress, TRUE, 3, &dmmd.PDataSize);
dmmd.ThreadId = (DWORD)PsGetCurrentThread()->ThreadId;
if((lp[0] & 0xEFFFFFFF) == 0x82000000)
DoModuleWait();
FTrapNotify(DM_MODLOAD, (ULONG_PTR)&dmmd, g_dmGlobals.dwStopFlags & DMSTOP_MODLOAD, NULL);
}
}
else if(pef->ExceptionInformation[0] == BREAKPOINT_UNLOAD_SYMBOLS)
{
if(pcr->Gpr3 && pcr->Gpr4)
{
DMN_MODLOAD_EX dmmd;
LPDWORD lp;
pas = (PANSI_STRING)pcr->Gpr3;
lp = (LPDWORD)pcr->Gpr4;
strcpy_s(dmmd.Name, 260, pas->Buffer);
dmmd.BaseAddress = (PVOID)lp[0];
dmmd.Size = lp[3];
dmmd.ThreadId = (DWORD)PsGetCurrentThread()->ThreadId;
pas = (PANSI_STRING)pcr->Gpr3;
FTrapNotify(DM_MODUNLOAD, (ULONG_PTR)&dmmd, FALSE, NULL);
}
}
else if(pef->ExceptionInformation[0] == BREAKPOINT_BREAK)
{
DMN_BREAK dmb;
dmb.Address = (PVOID)pcr->Iar;
dmb.ThreadId = (DWORD)PsGetCurrentThread()->ThreadId;
if(FFindBreak(dmb.Address, TRUE))
fSkip = FALSE;
FTrapNotify(DM_BREAK, (ULONG_PTR)&dmb, TRUE, &fPassAlong);
if(g_dmGlobals.bDebugging)
fPassAlong = FALSE; // we dont pass along breakpoints while debugging
if(ResumeAfterBreakpoint(pcr))
pdmtd->BreakpointAddress = (DWORD)dmb.Address;
}
else if(pef->ExceptionInformation[0] == BREAKPOINT_ASSERT)
{
DMN_DEBUGSTR dmds;
dmds.String = (LPCSTR)pcr->Gpr3;
dmds.Length = (DWORD)pcr->Gpr4;
dmds.ThreadId = (DWORD)PsGetCurrentThread()->ThreadId;
FTrapNotify(DM_ASSERT, (ULONG_PTR)&dmds, TRUE, &fPassAlong);
}
else if(pef->ExceptionInformation[0] == BREAKPOINT_RIP)
{
// TODO: this thing here
ASSERT(FALSE);
}
}
else if(pef->Status == STATUS_SINGLE_STEP)
{
// We dont want to skip the current instruction in a single step exception
fSkip = FALSE;
if(pef->ExceptionInformation[0])
{
BOOL fStep = pdmtd->DebugFlags & DMFLAG_SINGLESTEP;
pdmtd->DebugFlags &= ~DMFLAG_SINGLESTEP;
pcr->Msr &= ~0x400;
// Single step exception
if(pdmtd->BreakpointAddress)
{
PDMBP bp = FFindBreak((PVOID)pdmtd->BreakpointAddress, TRUE);
if(bp)
DoEnableBreak(bp);
pdmtd->BreakpointAddress = 0;
fPassAlong = FALSE;
}
else if(pdmtd->DebugFlags & DMFLAG_DATABREAK)
{
// Set the breakpoint again
DmSetDataBreakpoint((PVOID)HardwareBreakpoint.dwAddr, HardwareBreakpoint.dwType, HardwareBreakpoint.dwSize);
pdmtd->DebugFlags &= ~DMFLAG_DATABREAK;
fPassAlong = FALSE;
}
else
fStep = TRUE;
if(fStep)
{
// we have to stop
DMN_BREAK dmb;
dmb.Address = (PVOID)pcr->Iar;
dmb.ThreadId = (DWORD)PsGetCurrentThread()->ThreadId;
FTrapNotify(DM_SINGLESTEP, (ULONG_PTR)&dmb, TRUE, &fPassAlong);
if(g_dmGlobals.bDebugging)
fPassAlong = FALSE;
}
}
else
{
// Data breakpoint
if(FMatchDataBreak((PVOID)pef->ExceptionInformation[1]))
{
DMN_DATABREAK dmdb;
dmdb.Address = (PVOID)pcr->Iar;
dmdb.DataAddress = (PVOID)pef->ExceptionInformation[1];
pdmtd->DebugFlags |= DMFLAG_DATABREAK;
KeIpiGenericCall(DmpSetHardwareBreakpoint, 0);
FTrapNotify(DM_DATABREAK, (ULONG_PTR)&dmdb, TRUE, &fPassAlong);
// Single step
pcr->Msr |= 0x400;
}
else
{
DMN_BREAK dmb;
dmb.Address = (PVOID)pcr->Iar;
dmb.ThreadId = (DWORD)PsGetCurrentThread()->ThreadId;
FTrapNotify(DM_DATABREAK, (ULONG_PTR)&dmb, TRUE, &fPassAlong);
}
if(g_dmGlobals.bDebugging)
fPassAlong = FALSE;
}
}
else if(pef->Status == 0x406D1388)
{
if(pef->ExceptionInformation[0] == 0x1000)
{
VSetThreadName(pef->ExceptionInformation[2], (LPCSTR)pef->ExceptionInformation[1]);
}
}
else
{
DMN_EXCEPTION dme;
BOOL fStop = TRUE;
dme.Address = (PVOID)pcr->Iar;
dme.Code = pef->Status;
dme.ThreadId = (DWORD)PsGetCurrentThread()->ThreadId;
if(!fSecondChance)
{
dme.Flags = DM_EXCEPT_FIRSTCHANCE;
pdmtd->DebugFlags |= DMFLAG_FIRSTCHANCE;
fStop = (g_dmGlobals.dwStopFlags & DMSTOP_FCE);
}
else
dme.Flags = 0;
if(pef->Status == STATUS_ACCESS_VIOLATION)
{
dme.Information[0] = pef->ExceptionInformation[0];
dme.Information[1] = pef->ExceptionInformation[1];
}
else
{
dme.Information[0] = pef->Continuable;
dme.Information[1] = (DWORD)&pef->ExceptionInformation[0];
}
fRet = !FTrapNotify(DM_EXCEPTION, (ULONG_PTR)&dme, fStop, &fPassAlong);
pdmtd->DebugFlags &= ~DMFLAG_FIRSTCHANCE;
if(g_dmGlobals.bDebugging && (KeGetCurrentIrql() == PASSIVE_LEVEL))
{
fPassAlong = FALSE; // We don't pass along if we are debugging
if(fSecondChance)
fRet = TRUE; // continue second chance if we are debugging no matter what
}
fSkip = FALSE;
}
pdmtd->Context = NULL;
pdmtd->Exception = NULL;
}
if(fPassAlong)
return g_dmGlobals.PreviousTrap(ptf, pef, pcr, fSecondChance);
if(fSkip)
pcr->Iar += 4;
return fRet;
}
BOOL __declspec(naked) DmTrapOnStack(PKTRAP_FRAME ptf, PKEXCEPTION_FRAME pef,
PCONTEXT pcr, BOOL fSecondChance, PVOID Unused, PVOID stack)
{
__asm
{
// Store LR
mflr r12
stw r12, -8(sp)
// Get some stack
stwu sp, -0x60(sp)
// Get some breathing room
addi r8, r8, -0x50
// r8 = sp - r8
subf r8, sp, r8
// r9 = sp + 0x60
addi r9, sp, 0x60
// temp = sp + r8
// *r9 = temp
// sp = temp
stwux r9, sp, r8
// Branch
bl DmTrapHandlerImp
// Get the stack pointer
lwz sp, 0(sp)
// Get the link register
lwz r12, -8(sp)
mtlr r12
// Return
blr
}
}
BOOL DmTrapHandler(PKTRAP_FRAME ptf, PKEXCEPTION_FRAME pef,
PCONTEXT pcr, BOOL fSecondChance)
{
BOOL fRet;
DMTD *pdmtd = DmGetCurrentDmtd();
PCRITICAL_SECTION cs;
PVOID stack;
if((KeGetCurrentIrql() == PASSIVE_LEVEL) && pdmtd && !(pdmtd->DebugFlags & DMFLAG_TRAPPED) && !(pdmtd->DebugFlags & DMFLAG_DEBUGTHREAD)) // Prevent trapception
{
if(KeGetCurrentProcessType() == 2)
{
cs = &csExceptionStack;
stack = ExceptionStack;
}
else
{
cs = &csExceptionStackTitle;
stack = ExceptionStackTitle;
}
EnterCriticalSection(cs);
pdmtd->DebugFlags |= DMFLAG_TRAPPED;
// swap stack and trap
fRet = DmTrapOnStack(ptf, pef, pcr, fSecondChance, NULL, stack);
pdmtd->DebugFlags &= ~DMFLAG_TRAPPED;
LeaveCriticalSection(cs);
}
else
fRet = DmTrapHandlerImp(ptf, pef, pcr, fSecondChance, NULL);
return fRet;
}
VOID LpTitleTerminateRoutine()
{
// TODO: free up stuff used by titles (notifications, command handlers, etc)
DwChangeExecState(DMN_EXEC_REBOOT_TITLE, FALSE, TRUE, FALSE);
}
VOID LpThreadNotificationRoutine(PEX_THREAD_REGISTRATION ThisRegistration, PKTHREAD Thread, BOOL Creating)
{
DMTD *pdmtd;
BOOL bBreak = FALSE;
BOOL bStop = FALSE;
if(Creating)
{
DMN_CREATETHREAD dmct;
FInitThreadDebugData(Thread);
pdmtd = (DMTD*)Thread->DebugMonitorData;
if(Thread->CreateOptions & 0x100)
{
// Title launched, initial breakpoint
if(g_dmGlobals.dwStopFlags & DMSTOP_TITLELAUNCH)
{
g_dmGlobals.dwStopFlags &= ~DMSTOP_TITLELAUNCH;
bBreak = TRUE;
}
else
DwChangeExecState(DMN_EXEC_START, FALSE, TRUE, FALSE);
}
dmct.StartAddress = Thread->StartAddress;
dmct.ThreadId = (DWORD)Thread->ThreadId;
dmct.ThreadNameAddress = NULL;
bStop = g_dmGlobals.dwStopFlags & DMSTOP_CREATETHREAD;
if(!pdmtd || !(pdmtd->DebugFlags & DMFLAG_DEBUGTHREAD))
{
if(bStop)
PrepareToStop();
FNotify(bStop ? (DM_CREATETHREAD | DM_STOPTHREAD) : DM_CREATETHREAD, (ULONG_PTR)&dmct);
if(bStop)
FStopAtException();
if(bBreak)
__emit(BREAKPOINT_BREAK);
}
}
else
{
#if 0
pdmtd = DmGetThreadDmtd((DWORD)Thread->ThreadId);
#else
pdmtd = (DMTD*)Thread->DebugMonitorData;
#endif
KeEnterCriticalRegion();
FNotify(DM_DESTROYTHREAD, (ULONG_PTR)Thread->ThreadId);
if(pdmtd)
DmFreeThreadData(pdmtd);
KeLeaveCriticalRegion();
}
}
BOOL FGetMemory(DWORD dwAddr, PBYTE pb)
{
BYTE irql;
BOOL fRet = TRUE;
PBYTE pAddr;
PDMBP bp;
irql = KfRaiseIrql(0x7C);
KeAcquireSpinLockAtRaisedIrql(&DmpBreakSpinLock);
bp = FFindBreak((PVOID)dwAddr, FALSE);
if(bp)
dwAddr = (DWORD)bp->Code + (dwAddr & 3);
pAddr = (PBYTE)PvDbgReadCheck((PVOID)dwAddr);
if(pAddr)
*pb = *pAddr;
else
fRet = FALSE;
KeReleaseSpinLockFromRaisedIrql(&DmpBreakSpinLock);
KfLowerIrql(irql);
return fRet;
}
BOOL FSetMemory(DWORD dwAddr, BYTE b)
{
BYTE irql;
BOOL fRet = TRUE;
PBYTE pAddr;
HANDLE h;
PDMBP bp;
irql = KfRaiseIrql(0x7C);
KeAcquireSpinLockAtRaisedIrql(&DmpBreakSpinLock);
bp = FFindBreak((PVOID)dwAddr, FALSE);
if(bp)
dwAddr = (DWORD)bp->Code + (dwAddr & 3);
pAddr = (PBYTE)PvDbgWriteCheck((PVOID)dwAddr, &h);
if(pAddr)
{
*pAddr = b;
KeSweepIcacheRange((PVOID)pAddr, 1);
MmDbgReleaseAddress((PVOID)pAddr, &h);
}
else
fRet = FALSE;
KeReleaseSpinLockFromRaisedIrql(&DmpBreakSpinLock);
KfLowerIrql(irql);
return fRet;
}
// Breakpoints
DMHRAPI DmSetBreakpoint(PVOID addr)
{
BYTE irql;
HRESULT hr = XBDM_NOERR;
PDMBP bp = (PDMBP)DmAllocatePoolTypeWithTag(sizeof(DMBP), 'dmbp', 2);
irql = KfRaiseIrql(0x7C);
KeAcquireSpinLockAtRaisedIrql(&DmpBreakSpinLock);
if(FFindBreak(addr, FALSE))
hr = E_FAIL;
else
{
if(bp == NULL)
hr = E_FAIL;
else
{
InsertHeadList(&leBreaks, &bp->Link);
bp->bEnabled = FALSE;
bp->dwAddr = (DWORD)addr;
DoEnableBreak(bp);
}
}
KeReleaseSpinLockFromRaisedIrql(&DmpBreakSpinLock);
KfLowerIrql(irql);
if(FAILED(hr))
DmFreePool(bp);
return hr;
}
DMHRAPI DmRemoveBreakpoint(PVOID addr)
{
BYTE irql;
HRESULT hr = XBDM_NOERR;
PDMBP bp;
irql = KfRaiseIrql(0x7C);
KeAcquireSpinLockAtRaisedIrql(&DmpBreakSpinLock);
bp = FFindBreak(addr, FALSE);
if(bp)
{
DoDisableBreak(bp);
RemoveEntryList(&bp->Link);
}
else
hr = E_FAIL;
KeReleaseSpinLockFromRaisedIrql(&DmpBreakSpinLock);
KfLowerIrql(irql);
if(NT_SUCCESS(hr))
DmFreePool(bp);
return hr;
}
DMHRAPI DmIsBreakpoint(PVOID addr, LPDWORD dwType)
{
if(FFindBreak(addr, TRUE))
*dwType = DMBREAK_FIXED;
else
{
// TODO: check hardware breakpoint
*dwType = DMBREAK_NONE;
}
return XBDM_NOERR;
}
DMHRAPI DmSetInitialBreakpoint()
{
if(dwExecState != DMN_EXEC_PENDING_TITLE
&& dwExecState != DMN_EXEC_PENDING)
return XBDM_NOTSTOPPED;
g_dmGlobals.dwStopFlags |= DMSTOP_TITLELAUNCH;
return XBDM_NOERR;
}
VOID DoDisableBreak(PDMBP bp)
{
PVOID addr;
HANDLE h;
if(!bp->bEnabled)
return;
if(addr = PvDbgWriteCheck((PVOID)bp->dwAddr, &h))
{
*(LPDWORD)addr = *(LPDWORD)bp->Code;
KeSweepIcacheRange((PVOID)bp->dwAddr, 4);
MmDbgReleaseAddress(addr, &h);
bp->bEnabled = FALSE;
}
}
VOID DoEnableBreak(PDMBP bp)
{
PVOID addr;
HANDLE h;
if(bp->bEnabled)
return;
if(addr = PvDbgWriteCheck((PVOID)bp->dwAddr, &h))
{
*(LPDWORD)bp->Code = *(LPDWORD)addr;
*(LPDWORD)addr = BREAKPOINT_BREAK;
KeSweepIcacheRange((PVOID)bp->dwAddr, 4);
MmDbgReleaseAddress(addr, &h);
bp->bEnabled = TRUE;
}
}
DMHRAPI DmRemoveAllBreakpoints()
{
BYTE irql;
PLIST_ENTRY ple;
PDMBP bp;
irql = KfAcquireSpinLock(&DmpBreakSpinLock);
ple = leBreaks.Flink;
while(ple != &leBreaks)
{
bp = CONTAINING_RECORD(ple, DMBP, Link);
ple = ple->Flink;
DoDisableBreak(bp);
RemoveEntryList(&bp->Link);
DmFreePool(bp);
}
KfReleaseSpinLock(&DmpBreakSpinLock, irql);
return E_FAIL;
}
BOOL FMatchDataBreak(PVOID addr)
{
DWORD dw = (DWORD)addr;
if(dw >= HardwareBreakpoint.dwAddr && dw < (HardwareBreakpoint.dwAddr + HardwareBreakpoint.dwSize))
return TRUE;
return FALSE;
}
ULONG_PTR DmpSetHardwareBreakpoint(DWORD Context)
{
#ifdef _DEBUG
if(!Context)
KeSetSpecialPurposeRegister(SPR_DABRX, 0); // break disabled
KeSetSpecialPurposeRegister(SPR_DABR, Context); // set context
if(Context)
KeSetSpecialPurposeRegister(SPR_DABRX, 3); // break enabled
#else
if(!Context)
HvPokeSPR(SPR_DABRX, 0); // break disabled
HvPokeSPR(SPR_DABR, Context); // set context
if(Context)
HvPokeSPR(SPR_DABRX, 3); // break enabled
#endif
return Context;
}
DMHRAPI DmSetDataBreakpoint(PVOID addr, DWORD dwType, DWORD dwSize)
{
BYTE irql;
DWORD dw = (DWORD)addr;
DWORD dwMask = 0;
HRESULT hr = XBDM_NOERR;
irql = KfAcquireSpinLock(&DmpBreakSpinLock);
if(dwType == DMBREAK_NONE)
{
HardwareBreakpoint.dwAddr = 0;
HardwareBreakpoint.dwType = 0;
HardwareBreakpoint.dwSize = 0;
}
else
{
if((dw & 3) + dwSize > 8)
hr = E_INVALIDARG;
else
{
if(dwType == DMBREAK_WRITE)
dwMask = HWBP_WRITE;
else if(dwType == DMBREAK_READ)
dwMask = HWBP_READ;
else if(dwType == DMBREAK_READWRITE)
dwMask = HWBP_READWRITE;
else if(dwType == DMBREAK_EXECUTE)
dwMask = HWBP_EXECUTE;
HardwareBreakpoint.dwType = dwMask;
HardwareBreakpoint.dwAddr = dw;
HardwareBreakpoint.dwSize = dwSize;
dwMask |= (dw & 0xFFFFFFF8) | 4;
}
KeIpiGenericCall(DmpSetHardwareBreakpoint, dwMask);
}
KfReleaseSpinLock(&DmpBreakSpinLock, irql);
return hr;
}
PDMBP FFindBreak(PVOID addr, BOOL lock)
{
BYTE irql;
PDMBP bp = NULL, pbp;
PLIST_ENTRY ple;
if(lock)
{
irql = KfRaiseIrql(0x7C);
KeAcquireSpinLockAtRaisedIrql(&DmpBreakSpinLock);
}
ple = leBreaks.Flink;
while(ple != &leBreaks)
{
pbp = CONTAINING_RECORD(ple, DMBP, Link);
ple = ple->Flink;
if(pbp->dwAddr == ((DWORD)addr & 0xFFFFFFFC))
{
bp = pbp;
break;
}
}
if(lock)
{
KeReleaseSpinLockFromRaisedIrql(&DmpBreakSpinLock);
KfLowerIrql(irql);
}
return bp;
}
BOOL FIsBreakpoint(PVOID addr)
{
return (BOOL)FFindBreak(addr, TRUE);
}
const DWORD MMIORangeTable[] =
{
0x7FC80000, 0x7FC9FFFF,
0x7FD00000, 0x7FDFFFFF,
0x7FEA0000, 0x7FEAFFFF,
0x7FED0000, 0x7FEDFFFF,
0x7FED0000, 0x7FEDFFFF,
0x8FFF0000, 0x8FFF0FFF,
0x8FFF1000, 0x8FFF1FFF,
0x00000000, 0x00000000,
};
BOOL FIsMmIoAddress(PVOID addr)
{
int i;
for(i = 0;MMIORangeTable[i];i += 2)
{
if(((DWORD)addr > MMIORangeTable[i])
&& ((DWORD)addr < MMIORangeTable[i + 1]))
return TRUE;
}
return FALSE;
}
PVOID PvDbgWriteCheck(PVOID Address, PHANDLE Handle)
{
PVOID addr;
#ifndef _DEBUG
if(!FIsMmIoAddress(Address) && MmIsAddressValid(Address))
addr = Address;
else
addr = NULL;
#else
if(addr = MmDbgWriteCheck(Address, Handle))
{
if(FIsMmIoAddress(addr))
{
MmDbgReleaseAddress(addr, Handle);
addr = NULL;
}
}
#endif
return addr;
}
PVOID PvDbgReadCheck(PVOID Address)
{
PVOID addr;
if(addr = MmDbgReadCheck(Address))
{
if(FIsMmIoAddress(addr))
addr = NULL;
}
return addr;
}
#define MAKE_CONTEXT(x, y) { #x, y, (DWORD)&((XCONTEXT*)0)->##x, sizeof(((XCONTEXT*)0)->##x) },
#define MV(a) \
MAKE_CONTEXT(a##0, CONTEXT_VECTOR) MAKE_CONTEXT(a##1, CONTEXT_VECTOR) MAKE_CONTEXT(a##2, CONTEXT_VECTOR) \
MAKE_CONTEXT(a##3, CONTEXT_VECTOR) MAKE_CONTEXT(a##4, CONTEXT_VECTOR) MAKE_CONTEXT(a##5, CONTEXT_VECTOR) \
MAKE_CONTEXT(a##6, CONTEXT_VECTOR) MAKE_CONTEXT(a##7, CONTEXT_VECTOR) MAKE_CONTEXT(a##8, CONTEXT_VECTOR) \
MAKE_CONTEXT(a##9, CONTEXT_VECTOR)
DMREG rgbregs[] =
{
MAKE_CONTEXT(Msr, CONTEXT_CONTROL)
MAKE_CONTEXT(Iar, CONTEXT_CONTROL)
MAKE_CONTEXT(Lr, CONTEXT_CONTROL)
MAKE_CONTEXT(Ctr, CONTEXT_CONTROL)
MAKE_CONTEXT(Gpr0, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr1, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr2, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr3, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr4, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr5, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr6, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr7, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr8, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr9, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr10, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr11, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr12, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr13, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr14, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr15, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr16, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr17, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr18, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr19, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr20, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr21, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr22, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr23, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr24, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr25, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr26, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr27, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr28, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr29, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr30, CONTEXT_INTEGER)
MAKE_CONTEXT(Gpr31, CONTEXT_INTEGER)
MAKE_CONTEXT(Cr, CONTEXT_INTEGER)
MAKE_CONTEXT(Xer, CONTEXT_INTEGER)
MAKE_CONTEXT(Fpscr, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr0, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr1, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr2, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr3, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr4, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr5, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr6, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr7, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr8, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr9, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr10, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr11, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr12, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr13, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr14, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr15, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr16, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr17, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr18, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr19, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr20, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr21, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr22, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr23, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr24, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr25, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr26, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr27, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr28, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr29, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr30, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Fpr31, CONTEXT_FLOATING_POINT)
MAKE_CONTEXT(Vscr, CONTEXT_VECTOR)
MV(Vr)
MV(Vr1)
MV(Vr2)
MV(Vr3)
MV(Vr4)
MV(Vr5)
MV(Vr6)
MV(Vr7)
MV(Vr8)
MV(Vr9)
MV(Vr10)
MV(Vr11)
MAKE_CONTEXT(Vr120, CONTEXT_VECTOR)
MAKE_CONTEXT(Vr121, CONTEXT_VECTOR)
MAKE_CONTEXT(Vr122, CONTEXT_VECTOR)
MAKE_CONTEXT(Vr123, CONTEXT_VECTOR)
MAKE_CONTEXT(Vr124, CONTEXT_VECTOR)
MAKE_CONTEXT(Vr125, CONTEXT_VECTOR)
MAKE_CONTEXT(Vr126, CONTEXT_VECTOR)
MAKE_CONTEXT(Vr127, CONTEXT_VECTOR)
{ 0, 0, 0}
};
int cchregs = (sizeof(rgbregs) / sizeof(DMREG));
#undef MV
#undef MAKE_CONTEXT