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. Modified: trunk/reactos/ntoskrnl/dbg/kdb.c Modified: trunk/reactos/ntoskrnl/dbg/kdb.h Modified: trunk/reactos/ntoskrnl/dbg/kdb_symbols.c Modified: trunk/reactos/ntoskrnl/ke/catch.c Modified: trunk/reactos/ntoskrnl/ke/i386/exp.c Modified: trunk/reactos/ntoskrnl/ke/i386/usertrap.c Modified: trunk/reactos/ntoskrnl/mm/virtual.c _____
Modified: trunk/reactos/ntoskrnl/dbg/kdb.c --- trunk/reactos/ntoskrnl/dbg/kdb.c 2005-01-12 16:09:12 UTC (rev 12972) +++ trunk/reactos/ntoskrnl/dbg/kdb.c 2005-01-12 19:04:06 UTC (rev 12973) @@ -60,8 +60,9 @@
static BOOLEAN KdbHandleUmode = FALSE; static BOOLEAN KdbHandleHandled = FALSE; +static BOOLEAN KdbBreakOnModuleLoad = FALSE; + static BOOLEAN KdbIgnoreNextSingleStep = FALSE; - static ULONG KdbLastSingleStepFrom = 0xFFFFFFFF; static BOOLEAN KdbEnteredOnSingleStep = FALSE;
@@ -75,6 +76,8 @@ 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 @@ {"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 @@ { 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)))) { @@ -1353,6 +1356,32 @@ }
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) { KbdEchoOn = !KbdEchoOn; @@ -1656,23 +1685,24 @@ 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 @@ return(kdContinue); } } + +VOID +KdbModuleLoaded(IN PUNICODE_STRING Name) +{ + if (!KdbBreakOnModuleLoad) + return; + + DbgPrint("Module %wZ loaded.\n", Name); + DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C); +} + _____
Modified: trunk/reactos/ntoskrnl/dbg/kdb.h --- trunk/reactos/ntoskrnl/dbg/kdb.h 2005-01-12 16:09:12 UTC (rev 12972) +++ trunk/reactos/ntoskrnl/dbg/kdb.h 2005-01-12 19:04:06 UTC (rev 12973) @@ -72,6 +72,10 @@
VOID KdbProfileInterrupt(ULONG_PTR Eip);
+VOID +KdbModuleLoaded(IN PUNICODE_STRING Name); + + struct KDB_BPINFO { DWORD Addr; DWORD Type; _____
Modified: trunk/reactos/ntoskrnl/dbg/kdb_symbols.c --- trunk/reactos/ntoskrnl/dbg/kdb_symbols.c 2005-01-12 16:09:12 UTC (rev 12972) +++ trunk/reactos/ntoskrnl/dbg/kdb_symbols.c 2005-01-12 19:04:06 UTC (rev 12973) @@ -816,6 +816,9 @@
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\");
_____
Modified: trunk/reactos/ntoskrnl/ke/catch.c --- trunk/reactos/ntoskrnl/ke/catch.c 2005-01-12 16:09:12 UTC (rev 12972) +++ trunk/reactos/ntoskrnl/ke/catch.c 2005-01-12 19:04:06 UTC (rev 12973) @@ -50,6 +50,7 @@
DPRINT("KiDispatchException() called\n");
+ /* PCR->KeExceptionDispatchCount++; */
if (Context == NULL) @@ -95,8 +96,12 @@ 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 @@ /* 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 @@ { /* 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); _____
Modified: trunk/reactos/ntoskrnl/ke/i386/exp.c --- trunk/reactos/ntoskrnl/ke/i386/exp.c 2005-01-12 16:09:12 UTC (rev 12972) +++ trunk/reactos/ntoskrnl/ke/i386/exp.c 2005-01-12 19:04:06 UTC (rev 12973) @@ -190,9 +190,9 @@
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);
_____
Modified: trunk/reactos/ntoskrnl/ke/i386/usertrap.c --- trunk/reactos/ntoskrnl/ke/i386/usertrap.c 2005-01-12 16:09:12 UTC (rev 12972) +++ trunk/reactos/ntoskrnl/ke/i386/usertrap.c 2005-01-12 19:04:06 UTC (rev 12973) @@ -132,9 +132,9 @@
}
- 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); _____
Modified: trunk/reactos/ntoskrnl/mm/virtual.c --- trunk/reactos/ntoskrnl/mm/virtual.c 2005-01-12 16:09:12 UTC (rev 12972) +++ trunk/reactos/ntoskrnl/mm/virtual.c 2005-01-12 19:04:06 UTC (rev 12973) @@ -350,6 +350,56 @@
}
+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 @@ */ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@
KeDetachProcess();
+ /* Free the MDL. */ if (Mdl->MappedSystemVa != NULL) { MmUnmapLockedPages(Mdl->MappedSystemVa, Mdl); @@ -644,9 +711,22 @@ 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); }