mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
14 KiB
377 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
debug.c
|
|
|
|
Abstract:
|
|
|
|
Main debug loop for callmon
|
|
|
|
Author:
|
|
|
|
Mark Lucovsky (markl) 26-Jan-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "callmonp.h"
|
|
|
|
DWORD
|
|
DebugEventHandler(
|
|
LPDEBUG_EVENT DebugEvent
|
|
);
|
|
|
|
DEBUG_EVENT DebugEvent;
|
|
|
|
VOID
|
|
DebugEventLoop( VOID )
|
|
{
|
|
DWORD ContinueStatus;
|
|
DWORD OldPriority;
|
|
|
|
//
|
|
// We want to process debug events quickly
|
|
//
|
|
|
|
OldPriority = GetPriorityClass( GetCurrentProcess() );
|
|
SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
|
|
|
|
do {
|
|
if (!WaitForDebugEvent( &DebugEvent, INFINITE )) {
|
|
fprintf(stderr,"CALLMON: WaitForDebugEvent Failed %d\n",GetLastError() );
|
|
ExitProcess( 1 );
|
|
}
|
|
if ( fVerbose ) {
|
|
if (DebugEvent.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) {
|
|
fprintf(stderr,"Debug exception event - Code: %x Address: %x Info: [%u] %x %x %x %x\n",
|
|
DebugEvent.u.Exception.ExceptionRecord.ExceptionCode,
|
|
DebugEvent.u.Exception.ExceptionRecord.ExceptionAddress,
|
|
DebugEvent.u.Exception.ExceptionRecord.NumberParameters,
|
|
DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 0 ],
|
|
DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 1 ],
|
|
DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 2 ],
|
|
DebugEvent.u.Exception.ExceptionRecord.ExceptionInformation[ 3 ]
|
|
);
|
|
}
|
|
else {
|
|
fprintf(stderr,"Debug %x event\n", DebugEvent.dwDebugEventCode);
|
|
}
|
|
}
|
|
|
|
ContinueStatus = DebugEventHandler( &DebugEvent );
|
|
|
|
if ( fVerbose ) {
|
|
fprintf(stderr,"Continue Status %x\n", ContinueStatus);
|
|
}
|
|
|
|
if (!ContinueDebugEvent( DebugEvent.dwProcessId,
|
|
DebugEvent.dwThreadId,
|
|
ContinueStatus
|
|
)
|
|
) {
|
|
fprintf(stderr,"CALLMON: Continue Debug Event failed %d\n", GetLastError() );
|
|
ExitProcess( 1 );
|
|
}
|
|
}
|
|
while (!IsListEmpty( &ProcessListHead ));
|
|
|
|
//
|
|
// Drop back to old priority to interact with user.
|
|
//
|
|
|
|
SetPriorityClass( GetCurrentProcess(), OldPriority );
|
|
}
|
|
|
|
DWORD
|
|
DebugEventHandler(
|
|
LPDEBUG_EVENT DebugEvent
|
|
)
|
|
{
|
|
DWORD ContinueStatus;
|
|
PPROCESS_INFO Process;
|
|
PTHREAD_INFO Thread;
|
|
CONTEXT Context;
|
|
PCONTEXT pContext;
|
|
PBREAKPOINT_INFO Breakpoint;
|
|
|
|
ContinueStatus = (DWORD)DBG_CONTINUE;
|
|
if (FindProcessAndThreadForEvent( DebugEvent, &Process, &Thread )) {
|
|
switch (DebugEvent->dwDebugEventCode) {
|
|
case CREATE_PROCESS_DEBUG_EVENT:
|
|
//
|
|
// Create process event includes first thread of process
|
|
// as well. Remember process and thread in our process tree
|
|
//
|
|
|
|
if (AddProcess( DebugEvent, &Process )) {
|
|
AddModule( DebugEvent );
|
|
AddThread( DebugEvent, Process, &Thread );
|
|
}
|
|
break;
|
|
|
|
case EXIT_PROCESS_DEBUG_EVENT:
|
|
//
|
|
// Exit process event includes last thread of process
|
|
// as well. Remove process and thread from our process tree
|
|
//
|
|
|
|
if (DeleteThread( Process, Thread )) {
|
|
DeleteProcess( Process );
|
|
}
|
|
break;
|
|
|
|
case CREATE_THREAD_DEBUG_EVENT:
|
|
//
|
|
// Create thread. Remember thread in our process tree.
|
|
//
|
|
|
|
AddThread( DebugEvent, Process, &Thread );
|
|
break;
|
|
|
|
case EXIT_THREAD_DEBUG_EVENT:
|
|
//
|
|
// Exit thread. Remove thread from our process tree.
|
|
//
|
|
|
|
DeleteThread( Process, Thread );
|
|
break;
|
|
|
|
case LOAD_DLL_DEBUG_EVENT:
|
|
AddModule( DebugEvent );
|
|
break;
|
|
|
|
case UNLOAD_DLL_DEBUG_EVENT:
|
|
break;
|
|
|
|
case OUTPUT_DEBUG_STRING_EVENT:
|
|
case RIP_EVENT:
|
|
//
|
|
// Ignore these
|
|
//
|
|
break;
|
|
|
|
case EXCEPTION_DEBUG_EVENT:
|
|
//
|
|
// Assume we wont handle this exception
|
|
//
|
|
|
|
ContinueStatus = (DWORD)DBG_CONTINUE;
|
|
switch (DebugEvent->u.Exception.ExceptionRecord.ExceptionCode) {
|
|
//
|
|
// Breakpoint exception.
|
|
//
|
|
|
|
case STATUS_BREAKPOINT:
|
|
|
|
EnterCriticalSection(&BreakTable);
|
|
if ( !fBreakPointsInitialized ) {
|
|
|
|
SetValidModuleFlags();
|
|
|
|
if ( fKernel32Valid ) EnableWindow(GetDlgItem(hwndDlg, ID_KERNEL32),TRUE);
|
|
if ( fWsock32Valid ) EnableWindow(GetDlgItem(hwndDlg, ID_WSOCK32),TRUE);
|
|
if ( fUser32Valid ) EnableWindow(GetDlgItem(hwndDlg, ID_USER32),TRUE);
|
|
if ( fGdi32Valid ) EnableWindow(GetDlgItem(hwndDlg, ID_GDI32),TRUE);
|
|
if ( fOle32Valid ) EnableWindow(GetDlgItem(hwndDlg, ID_OLE32),TRUE);
|
|
if ( fNtDllValid ) EnableWindow(GetDlgItem(hwndDlg, ID_NTDLL),TRUE);
|
|
|
|
CheckDlgButton(hwndDlg,ID_NTDLL,fNtDll ? 1 : 0);
|
|
CheckDlgButton(hwndDlg,ID_GDI32,fGdi32 ? 1 : 0);
|
|
CheckDlgButton(hwndDlg,ID_KERNEL32,fKernel32 ? 1 : 0);
|
|
CheckDlgButton(hwndDlg,ID_WSOCK32,fWsock32 ? 1 : 0);
|
|
CheckDlgButton(hwndDlg,ID_USER32,fUser32 ? 1 : 0);
|
|
CheckDlgButton(hwndDlg,ID_OLE32,fOle32 ? 1 : 0);
|
|
|
|
EnableWindow(GetDlgItem(hwndDlg, ID_START),TRUE);
|
|
EnableWindow(GetDlgItem(hwndDlg, ID_BREAKCONTROL),TRUE);
|
|
EnableWindow(GetDlgItem(hwndDlg, ID_CLEAR),TRUE);
|
|
EnableWindow(GetDlgItem(hwndDlg, ID_STARTBUTTON),TRUE);
|
|
EnableWindow(GetDlgItem(hwndDlg, ID_LOG),TRUE);
|
|
|
|
WaitForSingleObject(ReleaseDebugeeEvent,INFINITE);
|
|
|
|
InitializeBreakPoints(Process);
|
|
fBreakPointsInitialized = TRUE;
|
|
|
|
|
|
BaseTime = GetTickCount();
|
|
StartingTick = GetTickCount();
|
|
}
|
|
|
|
if (Thread->BreakpointToStepOver != NULL) {
|
|
//
|
|
// If this breakpoint was in place to step over an API entry
|
|
// point breakpoint, then deal with it by ending single
|
|
// step mode, resuming all threads in the process and
|
|
// reinstalling the API breakpoint we just stepped over.
|
|
// Finally return handled for this exception so the
|
|
// thread can proceed.
|
|
//
|
|
|
|
if (EndSingleStepBreakpoint( Process, Thread )) {
|
|
HandleThreadsForSingleStep( Process, Thread, FALSE );
|
|
|
|
InstallBreakpoint( Process, Thread->BreakpointToStepOver );
|
|
Thread->BreakpointToStepOver = NULL;
|
|
}
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Otherwise, see if this breakpoint is either an API entry
|
|
// point breakpoint for the process or a return address
|
|
// breakpoint for a thread in the process.
|
|
//
|
|
Breakpoint = FindBreakpoint( DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress,
|
|
Process
|
|
);
|
|
|
|
if (Breakpoint != NULL) {
|
|
|
|
TotalBreakPoints++;
|
|
RunningBreakPoints++;
|
|
Breakpoint->TotalApiCount++;
|
|
Breakpoint->ApiCount++;
|
|
NewCallmonData = TRUE;
|
|
|
|
//
|
|
// Now single step over this breakpoint, by removing it and
|
|
// setting a breakpoint at the next instruction (or using
|
|
// single stepmode if the processor supports it). We also
|
|
// suspend all the threads in the process except this one so
|
|
// we know the next breakpoint/single step exception we see
|
|
// for this process will be for this one.
|
|
//
|
|
|
|
RemoveBreakpoint( Process, Breakpoint );
|
|
if (BeginSingleStepBreakpoint(Process, Thread)) {
|
|
Thread->BreakpointToStepOver = Breakpoint;
|
|
HandleThreadsForSingleStep( Process, Thread, TRUE );
|
|
}
|
|
else {
|
|
Thread->BreakpointToStepOver = NULL;
|
|
}
|
|
}
|
|
else
|
|
//
|
|
// If not one of our breakpoints, assume it is a hardcoded
|
|
// breakpoint and skip over it. This will get by the one
|
|
// breakpoint in LdrInit triggered by the process being
|
|
// invoked with DEBUG_PROCESS.
|
|
//
|
|
|
|
if (SkipOverHardcodedBreakpoint( Process,
|
|
Thread,
|
|
DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress
|
|
)
|
|
) {
|
|
//
|
|
// If we successfully skipped over this hard-coded breakpoint
|
|
// then return handled for this exception.
|
|
//
|
|
|
|
}
|
|
}
|
|
LeaveCriticalSection(&BreakTable);
|
|
break;
|
|
|
|
case STATUS_SINGLE_STEP:
|
|
//
|
|
// We should only see this exception on processors that
|
|
// support a single step mode.
|
|
//
|
|
|
|
EnterCriticalSection(&BreakTable);
|
|
if (Thread->BreakpointToStepOver != NULL) {
|
|
EndSingleStepBreakpoint( Process, Thread );
|
|
HandleThreadsForSingleStep( Process, Thread, FALSE );
|
|
InstallBreakpoint( Process, Thread->BreakpointToStepOver );
|
|
Thread->BreakpointToStepOver = NULL;
|
|
}
|
|
LeaveCriticalSection(&BreakTable);
|
|
break;
|
|
|
|
default:
|
|
ContinueStatus = DBG_EXCEPTION_NOT_HANDLED;
|
|
if ( fVerbose ) {
|
|
DWORD Temp;
|
|
|
|
fprintf(stderr,"Unknown exception: %08x at %08x\n",
|
|
DebugEvent->u.Exception.ExceptionRecord.ExceptionCode,
|
|
DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress
|
|
);
|
|
if (ReadProcessMemory(Process->Handle,
|
|
DebugEvent->u.Exception.ExceptionRecord.ExceptionAddress,
|
|
&Temp,
|
|
sizeof(Temp),
|
|
NULL)) {
|
|
fprintf(stderr,"Instruction is %08lx\n",Temp);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return( ContinueStatus );
|
|
}
|
|
|
|
BOOLEAN
|
|
InstallBreakpoint(
|
|
PPROCESS_INFO Process,
|
|
PBREAKPOINT_INFO Breakpoint
|
|
)
|
|
{
|
|
if (!Breakpoint->SavedInstructionValid &&
|
|
!ReadProcessMemory( Process->Handle,
|
|
Breakpoint->Address,
|
|
&Breakpoint->SavedInstruction,
|
|
SizeofBreakpointInstruction,
|
|
NULL
|
|
)
|
|
) {
|
|
return FALSE;
|
|
}
|
|
else
|
|
if (!WriteProcessMemory( Process->Handle,
|
|
Breakpoint->Address,
|
|
BreakpointInstruction,
|
|
SizeofBreakpointInstruction,
|
|
NULL
|
|
)
|
|
) {
|
|
return FALSE;
|
|
}
|
|
else {
|
|
Breakpoint->SavedInstructionValid = TRUE;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
RemoveBreakpoint(
|
|
PPROCESS_INFO Process,
|
|
PBREAKPOINT_INFO Breakpoint
|
|
)
|
|
{
|
|
if (!Breakpoint->SavedInstructionValid ||
|
|
!WriteProcessMemory( Process->Handle,
|
|
Breakpoint->Address,
|
|
&Breakpoint->SavedInstruction,
|
|
SizeofBreakpointInstruction,
|
|
NULL
|
|
)
|
|
) {
|
|
return FALSE;
|
|
}
|
|
else {
|
|
return TRUE;
|
|
}
|
|
}
|