mirror of
https://github.com/reactos/reactos.git
synced 2024-12-27 09:34:43 +00:00
Add possibility to make KDB break on module-loads. Fix handling of breakpoints in usermode with KDB. Set ExceptionRecord->ExceptionFlags to 0 for breakpoints/singlesteps and noncontinuable for everything else. Fix WriteProcessMemory.
svn path=/trunk/; revision=12973
This commit is contained in:
parent
030b0867f6
commit
d0a2f448f4
7 changed files with 210 additions and 69 deletions
|
@ -60,8 +60,9 @@ static KDB_ACTIVE_BREAKPOINT
|
|||
|
||||
static BOOLEAN KdbHandleUmode = FALSE;
|
||||
static BOOLEAN KdbHandleHandled = FALSE;
|
||||
static BOOLEAN KdbIgnoreNextSingleStep = FALSE;
|
||||
static BOOLEAN KdbBreakOnModuleLoad = FALSE;
|
||||
|
||||
static BOOLEAN KdbIgnoreNextSingleStep = FALSE;
|
||||
static ULONG KdbLastSingleStepFrom = 0xFFFFFFFF;
|
||||
static BOOLEAN KdbEnteredOnSingleStep = FALSE;
|
||||
|
||||
|
@ -75,6 +76,8 @@ DbgContCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf);
|
|||
ULONG
|
||||
DbgStopCondition(ULONG Aargc, PCH Argv[], PKTRAP_FRAME Tf);
|
||||
ULONG
|
||||
DbgModuleLoadedAction(ULONG Aargc, PCH Argv[], PKTRAP_FRAME Tf);
|
||||
ULONG
|
||||
DbgEchoToggle(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf);
|
||||
ULONG
|
||||
DbgRegsCommand(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf);
|
||||
|
@ -129,7 +132,8 @@ struct
|
|||
{"cont", "cont", "Exit the debugger", DbgContCommand},
|
||||
{"echo", "echo", "Toggle serial echo", DbgEchoToggle},
|
||||
{"condition", "condition [all|umode|kmode]", "Kdbg enter condition", DbgStopCondition},
|
||||
|
||||
{"module-loaded", "module-loaded [break|continue]", "Module-loaded action", DbgModuleLoadedAction},
|
||||
|
||||
{"regs", "regs", "Display general purpose registers", DbgRegsCommand},
|
||||
{"dregs", "dregs", "Display debug registers", DbgDRegsCommand},
|
||||
{"cregs", "cregs", "Display control registers", DbgCRegsCommand},
|
||||
|
@ -909,9 +913,8 @@ DbgPrintBackTrace(PULONG Frame, ULONG_PTR StackBase, ULONG_PTR StackLimit)
|
|||
{
|
||||
PVOID Address;
|
||||
|
||||
DbgPrint("Frames: ");
|
||||
while (Frame != NULL && (ULONG_PTR)Frame >= StackLimit &&
|
||||
(ULONG_PTR)Frame < StackBase) /* FIXME: why limit this to StackBase/StackLimit? */
|
||||
DbgPrint("Frames:\n");
|
||||
while (Frame != NULL)
|
||||
{
|
||||
if (!NT_SUCCESS(KdbpSafeReadMemory(&Address, Frame + 1, sizeof (Address))))
|
||||
{
|
||||
|
@ -1352,6 +1355,32 @@ DbgStopCondition(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf)
|
|||
return(TRUE);
|
||||
}
|
||||
|
||||
ULONG
|
||||
DbgModuleLoadedAction(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf)
|
||||
{
|
||||
if (Argc == 1)
|
||||
{
|
||||
if (KdbBreakOnModuleLoad)
|
||||
DbgPrint("Current setting: break\n");
|
||||
else
|
||||
DbgPrint("Current setting: continue\n");
|
||||
}
|
||||
else if (!strcmp(Argv[1], "break"))
|
||||
{
|
||||
KdbBreakOnModuleLoad = TRUE;
|
||||
}
|
||||
else if (!strcmp(Argv[1], "continue"))
|
||||
{
|
||||
KdbBreakOnModuleLoad = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
DbgPrint("Unknown setting: %s\n", Argv[1]);
|
||||
}
|
||||
|
||||
return(TRUE);
|
||||
}
|
||||
|
||||
ULONG
|
||||
DbgEchoToggle(ULONG Argc, PCH Argv[], PKTRAP_FRAME Tf)
|
||||
{
|
||||
|
@ -1656,23 +1685,24 @@ KdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
LONG BreakPointNr;
|
||||
ULONG ExpNr = (ULONG)TrapFrame->DebugArgMark;
|
||||
|
||||
/* Always handle beakpoints */
|
||||
if (ExpNr != 1 && ExpNr != 3)
|
||||
{
|
||||
DbgPrint(":KDBG:Entered:%s:%s\n",
|
||||
PreviousMode==KernelMode ? "kmode" : "umode",
|
||||
AlwaysHandle ? "always" : "if-unhandled");
|
||||
}
|
||||
|
||||
/* If we aren't handling umode exceptions then return */
|
||||
if (PreviousMode == UserMode && !KdbHandleUmode && !AlwaysHandle)
|
||||
{
|
||||
return kdContinue;
|
||||
}
|
||||
|
||||
/* If the exception would be unhandled (and we care) then handle it */
|
||||
if (PreviousMode == KernelMode && !KdbHandleHandled && !AlwaysHandle)
|
||||
{
|
||||
return kdContinue;
|
||||
/* If we aren't handling umode exceptions then return */
|
||||
if (PreviousMode == UserMode && !KdbHandleUmode && !AlwaysHandle)
|
||||
{
|
||||
return kdHandleException;
|
||||
}
|
||||
|
||||
/* If the exception would be unhandled (and we care) then handle it */
|
||||
if (PreviousMode == KernelMode && !KdbHandleHandled && !AlwaysHandle)
|
||||
{
|
||||
return kdHandleException;
|
||||
}
|
||||
}
|
||||
|
||||
/* Exception inside the debugger? Game over. */
|
||||
|
@ -1772,3 +1802,14 @@ KdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
return(kdContinue);
|
||||
}
|
||||
}
|
||||
|
||||
VOID
|
||||
KdbModuleLoaded(IN PUNICODE_STRING Name)
|
||||
{
|
||||
if (!KdbBreakOnModuleLoad)
|
||||
return;
|
||||
|
||||
DbgPrint("Module %wZ loaded.\n", Name);
|
||||
DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
|
||||
}
|
||||
|
||||
|
|
|
@ -72,6 +72,10 @@ KdbEnableProfiling();
|
|||
VOID
|
||||
KdbProfileInterrupt(ULONG_PTR Eip);
|
||||
|
||||
VOID
|
||||
KdbModuleLoaded(IN PUNICODE_STRING Name);
|
||||
|
||||
|
||||
struct KDB_BPINFO {
|
||||
DWORD Addr;
|
||||
DWORD Type;
|
||||
|
|
|
@ -816,6 +816,9 @@ KdbpSymLoadModuleSymbols(IN PUNICODE_STRING FileName,
|
|||
PSYMBOLFILE_HEADER SymbolFileHeader;
|
||||
PIMAGE_SYMBOL_INFO_CACHE CachedSymbolFile;
|
||||
|
||||
/* Allow KDB to break on module load */
|
||||
KdbModuleLoaded(FileName);
|
||||
|
||||
/* Get the path to the symbol store */
|
||||
wcscpy(TmpFileName, L"\\SystemRoot\\symbols\\");
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
|
||||
DPRINT("KiDispatchException() called\n");
|
||||
|
||||
|
||||
/* PCR->KeExceptionDispatchCount++; */
|
||||
|
||||
if (Context == NULL)
|
||||
|
@ -95,8 +96,12 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
NTSTATUS StatusOfCopy;
|
||||
|
||||
#ifdef KDBG
|
||||
KdbEnterDebuggerException (ExceptionRecord, PreviousMode,
|
||||
Context, Tf, FALSE);
|
||||
Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode,
|
||||
Context, Tf, FALSE);
|
||||
if (Action == kdContinue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* FIXME: Forward exception to user mode debugger */
|
||||
|
@ -141,8 +146,12 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
/* FIXME: Forward the exception to the process exception port */
|
||||
|
||||
#ifdef KDBG
|
||||
KdbEnterDebuggerException (ExceptionRecord, PreviousMode,
|
||||
Context, Tf, TRUE);
|
||||
Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode,
|
||||
Context, Tf, TRUE);
|
||||
if (Action == kdContinue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Terminate the offending thread */
|
||||
|
@ -153,8 +162,12 @@ KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
|
|||
{
|
||||
/* PreviousMode == KernelMode */
|
||||
#ifdef KDBG
|
||||
KdbEnterDebuggerException (ExceptionRecord, PreviousMode,
|
||||
Context, Tf, FALSE);
|
||||
Action = KdbEnterDebuggerException (ExceptionRecord, PreviousMode,
|
||||
Context, Tf, FALSE);
|
||||
if (Action == kdContinue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
Value = RtlpDispatchException (ExceptionRecord, Context);
|
||||
|
|
|
@ -190,9 +190,9 @@ KiKernelTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr, PVOID Cr2)
|
|||
Er.NumberParameters = 0;
|
||||
}
|
||||
|
||||
Er.ExceptionFlags = ((NTSTATUS) STATUS_SINGLE_STEP == (NTSTATUS) Er.ExceptionCode
|
||||
|| (NTSTATUS) STATUS_BREAKPOINT == (NTSTATUS) Er.ExceptionCode) ?
|
||||
EXCEPTION_NONCONTINUABLE : 0;
|
||||
Er.ExceptionFlags = (STATUS_SINGLE_STEP == Er.ExceptionCode ||
|
||||
STATUS_BREAKPOINT == Er.ExceptionCode) ?
|
||||
0 : EXCEPTION_NONCONTINUABLE;
|
||||
|
||||
KiDispatchException(&Er, 0, Tf, KernelMode, TRUE);
|
||||
|
||||
|
|
|
@ -132,9 +132,9 @@ KiUserTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr, PVOID Cr2)
|
|||
}
|
||||
|
||||
|
||||
Er.ExceptionFlags = ((NTSTATUS) STATUS_SINGLE_STEP == (NTSTATUS) Er.ExceptionCode ||
|
||||
(NTSTATUS) STATUS_BREAKPOINT == (NTSTATUS) Er.ExceptionCode) ?
|
||||
EXCEPTION_NONCONTINUABLE : 0;
|
||||
Er.ExceptionFlags = (STATUS_SINGLE_STEP == Er.ExceptionCode ||
|
||||
STATUS_BREAKPOINT == Er.ExceptionCode) ?
|
||||
0 : EXCEPTION_NONCONTINUABLE;
|
||||
|
||||
KiDispatchException(&Er, 0, Tf, UserMode, TRUE);
|
||||
return(0);
|
||||
|
|
|
@ -350,6 +350,56 @@ NtQueryVirtualMemory (IN HANDLE ProcessHandle,
|
|||
}
|
||||
|
||||
|
||||
NTSTATUS STDCALL
|
||||
MiProtectVirtualMemory(IN PEPROCESS Process,
|
||||
IN OUT PVOID *BaseAddress,
|
||||
IN OUT PULONG NumberOfBytesToProtect,
|
||||
IN ULONG NewAccessProtection,
|
||||
OUT PULONG OldAccessProtection OPTIONAL)
|
||||
{
|
||||
PMEMORY_AREA MemoryArea;
|
||||
PMADDRESS_SPACE AddressSpace;
|
||||
ULONG OldAccessProtection_;
|
||||
NTSTATUS Status;
|
||||
|
||||
*NumberOfBytesToProtect =
|
||||
PAGE_ROUND_UP((*BaseAddress) + (*NumberOfBytesToProtect)) -
|
||||
PAGE_ROUND_DOWN(*BaseAddress);
|
||||
*BaseAddress = (PVOID)PAGE_ROUND_DOWN(*BaseAddress);
|
||||
|
||||
AddressSpace = &Process->AddressSpace;
|
||||
|
||||
MmLockAddressSpace(AddressSpace);
|
||||
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, *BaseAddress);
|
||||
if (MemoryArea == NULL)
|
||||
{
|
||||
MmUnlockAddressSpace(AddressSpace);
|
||||
return STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
if (OldAccessProtection == NULL)
|
||||
OldAccessProtection = &OldAccessProtection_;
|
||||
|
||||
if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
|
||||
{
|
||||
Status = MmProtectAnonMem(AddressSpace, MemoryArea, *BaseAddress,
|
||||
*NumberOfBytesToProtect, NewAccessProtection,
|
||||
OldAccessProtection);
|
||||
}
|
||||
else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
|
||||
{
|
||||
Status = MmProtectSectionView(AddressSpace, MemoryArea, *BaseAddress,
|
||||
*NumberOfBytesToProtect,
|
||||
NewAccessProtection,
|
||||
OldAccessProtection);
|
||||
}
|
||||
|
||||
MmUnlockAddressSpace(AddressSpace);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
/* (tMk 2004.II.5)
|
||||
* FUNCTION:
|
||||
* Called from VirtualProtectEx (lib\kernel32\mem\virtual.c)
|
||||
|
@ -357,15 +407,13 @@ NtQueryVirtualMemory (IN HANDLE ProcessHandle,
|
|||
*/
|
||||
NTSTATUS STDCALL
|
||||
NtProtectVirtualMemory(IN HANDLE ProcessHandle,
|
||||
IN PVOID *UnsafeBaseAddress,
|
||||
IN ULONG *UnsafeNumberOfBytesToProtect,
|
||||
IN OUT PVOID *UnsafeBaseAddress,
|
||||
IN OUT ULONG *UnsafeNumberOfBytesToProtect,
|
||||
IN ULONG NewAccessProtection,
|
||||
OUT PULONG UnsafeOldAccessProtection)
|
||||
{
|
||||
PMEMORY_AREA MemoryArea;
|
||||
PEPROCESS Process;
|
||||
NTSTATUS Status;
|
||||
PMADDRESS_SPACE AddressSpace;
|
||||
ULONG OldAccessProtection;
|
||||
PVOID BaseAddress;
|
||||
ULONG NumberOfBytesToProtect;
|
||||
|
@ -377,18 +425,14 @@ NtProtectVirtualMemory(IN HANDLE ProcessHandle,
|
|||
if (!NT_SUCCESS(Status))
|
||||
return Status;
|
||||
|
||||
// (tMk 2004.II.5) in Microsoft SDK I read:
|
||||
// 'if this parameter is NULL or does not point to a valid variable, the function fails'
|
||||
/* (tMk 2004.II.5) in Microsoft SDK I read:
|
||||
* 'if this parameter is NULL or does not point to a valid variable, the function fails'
|
||||
*/
|
||||
if(UnsafeOldAccessProtection == NULL)
|
||||
{
|
||||
return(STATUS_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
NumberOfBytesToProtect =
|
||||
PAGE_ROUND_UP(BaseAddress + NumberOfBytesToProtect) -
|
||||
PAGE_ROUND_DOWN(BaseAddress);
|
||||
BaseAddress = (PVOID)PAGE_ROUND_DOWN(BaseAddress);
|
||||
|
||||
Status = ObReferenceObjectByHandle(ProcessHandle,
|
||||
PROCESS_VM_OPERATION,
|
||||
PsProcessType,
|
||||
|
@ -401,32 +445,12 @@ NtProtectVirtualMemory(IN HANDLE ProcessHandle,
|
|||
return(Status);
|
||||
}
|
||||
|
||||
AddressSpace = &Process->AddressSpace;
|
||||
Status = MiProtectVirtualMemory(Process,
|
||||
&BaseAddress,
|
||||
&NumberOfBytesToProtect,
|
||||
NewAccessProtection,
|
||||
&OldAccessProtection);
|
||||
|
||||
MmLockAddressSpace(AddressSpace);
|
||||
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, BaseAddress);
|
||||
if (MemoryArea == NULL)
|
||||
{
|
||||
MmUnlockAddressSpace(AddressSpace);
|
||||
ObDereferenceObject(Process);
|
||||
return(STATUS_UNSUCCESSFUL);
|
||||
}
|
||||
|
||||
if (MemoryArea->Type == MEMORY_AREA_VIRTUAL_MEMORY)
|
||||
{
|
||||
Status = MmProtectAnonMem(AddressSpace, MemoryArea, BaseAddress,
|
||||
NumberOfBytesToProtect, NewAccessProtection,
|
||||
&OldAccessProtection);
|
||||
}
|
||||
else if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
|
||||
{
|
||||
Status = MmProtectSectionView(AddressSpace, MemoryArea, BaseAddress,
|
||||
NumberOfBytesToProtect,
|
||||
NewAccessProtection,
|
||||
&OldAccessProtection);
|
||||
}
|
||||
|
||||
MmUnlockAddressSpace(AddressSpace);
|
||||
ObDereferenceObject(Process);
|
||||
|
||||
MmCopyToCaller(UnsafeOldAccessProtection, &OldAccessProtection, sizeof(ULONG));
|
||||
|
@ -590,12 +614,15 @@ NtWriteVirtualMemory(IN HANDLE ProcessHandle,
|
|||
IN PVOID BaseAddress,
|
||||
IN PVOID Buffer,
|
||||
IN ULONG NumberOfBytesToWrite,
|
||||
OUT PULONG NumberOfBytesWritten)
|
||||
OUT PULONG NumberOfBytesWritten OPTIONAL)
|
||||
{
|
||||
NTSTATUS Status;
|
||||
PMDL Mdl;
|
||||
PVOID SystemAddress;
|
||||
PEPROCESS Process;
|
||||
ULONG OldProtection = 0;
|
||||
PVOID ProtectBaseAddress;
|
||||
ULONG ProtectNumberOfBytes;
|
||||
|
||||
DPRINT("NtWriteVirtualMemory(ProcessHandle %x, BaseAddress %x, "
|
||||
"Buffer %x, NumberOfBytesToWrite %d)\n",ProcessHandle,BaseAddress,
|
||||
|
@ -612,23 +639,62 @@ NtWriteVirtualMemory(IN HANDLE ProcessHandle,
|
|||
return(Status);
|
||||
}
|
||||
|
||||
/* We have to make sure the target memory is writable.
|
||||
*
|
||||
* I am not sure if it is correct to do this in any case, but it has to be
|
||||
* done at least in some cases because you can use WriteProcessMemory to
|
||||
* write into the .text section of a module where memcpy() would crash.
|
||||
* -blight (2005/01/09)
|
||||
*/
|
||||
ProtectBaseAddress = BaseAddress;
|
||||
ProtectNumberOfBytes = NumberOfBytesToWrite;
|
||||
|
||||
/* Write memory */
|
||||
if (Process == PsGetCurrentProcess())
|
||||
{
|
||||
Status = MiProtectVirtualMemory(Process,
|
||||
&ProtectBaseAddress,
|
||||
&ProtectNumberOfBytes,
|
||||
PAGE_READWRITE,
|
||||
&OldProtection);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ObDereferenceObject(Process);
|
||||
return Status;
|
||||
}
|
||||
memcpy(BaseAddress, Buffer, NumberOfBytesToWrite);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create MDL describing the source buffer. */
|
||||
Mdl = MmCreateMdl(NULL,
|
||||
Buffer,
|
||||
NumberOfBytesToWrite);
|
||||
MmProbeAndLockPages(Mdl,
|
||||
UserMode,
|
||||
IoReadAccess);
|
||||
if(Mdl == NULL)
|
||||
{
|
||||
ObDereferenceObject(Process);
|
||||
return(STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
/* Make the target area writable. */
|
||||
Status = MiProtectVirtualMemory(Process,
|
||||
&ProtectBaseAddress,
|
||||
&ProtectNumberOfBytes,
|
||||
PAGE_READWRITE,
|
||||
&OldProtection);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
ObDereferenceObject(Process);
|
||||
ExFreePool(Mdl);
|
||||
return Status;
|
||||
}
|
||||
|
||||
/* Map the MDL. */
|
||||
MmProbeAndLockPages(Mdl,
|
||||
UserMode,
|
||||
IoReadAccess);
|
||||
|
||||
/* Copy memory from the mapped MDL into the target buffer. */
|
||||
KeAttachProcess(&Process->Pcb);
|
||||
|
||||
SystemAddress = MmGetSystemAddressForMdl(Mdl);
|
||||
|
@ -636,6 +702,7 @@ NtWriteVirtualMemory(IN HANDLE ProcessHandle,
|
|||
|
||||
KeDetachProcess();
|
||||
|
||||
/* Free the MDL. */
|
||||
if (Mdl->MappedSystemVa != NULL)
|
||||
{
|
||||
MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl);
|
||||
|
@ -644,9 +711,22 @@ NtWriteVirtualMemory(IN HANDLE ProcessHandle,
|
|||
ExFreePool(Mdl);
|
||||
}
|
||||
|
||||
/* Reset the protection of the target memory. */
|
||||
Status = MiProtectVirtualMemory(Process,
|
||||
&ProtectBaseAddress,
|
||||
&ProtectNumberOfBytes,
|
||||
OldProtection,
|
||||
&OldProtection);
|
||||
if (!NT_SUCCESS(Status))
|
||||
{
|
||||
DPRINT1("Failed to reset protection of the target memory! (Status 0x%x)\n", Status);
|
||||
/* FIXME: Should we bugcheck here? */
|
||||
}
|
||||
|
||||
ObDereferenceObject(Process);
|
||||
|
||||
*NumberOfBytesWritten = NumberOfBytesToWrite;
|
||||
if (NumberOfBytesWritten != NULL)
|
||||
MmCopyToCaller(NumberOfBytesWritten, &NumberOfBytesToWrite, sizeof(ULONG));
|
||||
|
||||
return(STATUS_SUCCESS);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue