diff --git a/reactos/ntoskrnl/dbg/kdb.c b/reactos/ntoskrnl/dbg/kdb.c index 9ead1ff917e..ff23cf566a6 100644 --- a/reactos/ntoskrnl/dbg/kdb.c +++ b/reactos/ntoskrnl/dbg/kdb.c @@ -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); +} + diff --git a/reactos/ntoskrnl/dbg/kdb.h b/reactos/ntoskrnl/dbg/kdb.h index a3380251c43..ac67e4f3926 100644 --- a/reactos/ntoskrnl/dbg/kdb.h +++ b/reactos/ntoskrnl/dbg/kdb.h @@ -72,6 +72,10 @@ KdbEnableProfiling(); VOID KdbProfileInterrupt(ULONG_PTR Eip); +VOID +KdbModuleLoaded(IN PUNICODE_STRING Name); + + struct KDB_BPINFO { DWORD Addr; DWORD Type; diff --git a/reactos/ntoskrnl/dbg/kdb_symbols.c b/reactos/ntoskrnl/dbg/kdb_symbols.c index efa60877b7e..be94348c09c 100644 --- a/reactos/ntoskrnl/dbg/kdb_symbols.c +++ b/reactos/ntoskrnl/dbg/kdb_symbols.c @@ -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\\"); diff --git a/reactos/ntoskrnl/ke/catch.c b/reactos/ntoskrnl/ke/catch.c index 9d54d4b61be..cda37261c12 100644 --- a/reactos/ntoskrnl/ke/catch.c +++ b/reactos/ntoskrnl/ke/catch.c @@ -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); diff --git a/reactos/ntoskrnl/ke/i386/exp.c b/reactos/ntoskrnl/ke/i386/exp.c index 93fc02f800e..44f2560093c 100644 --- a/reactos/ntoskrnl/ke/i386/exp.c +++ b/reactos/ntoskrnl/ke/i386/exp.c @@ -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); diff --git a/reactos/ntoskrnl/ke/i386/usertrap.c b/reactos/ntoskrnl/ke/i386/usertrap.c index b87b4fa79c7..eb582cb1fab 100644 --- a/reactos/ntoskrnl/ke/i386/usertrap.c +++ b/reactos/ntoskrnl/ke/i386/usertrap.c @@ -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); diff --git a/reactos/ntoskrnl/mm/virtual.c b/reactos/ntoskrnl/mm/virtual.c index b92ed3b0040..a012630cbab 100644 --- a/reactos/ntoskrnl/mm/virtual.c +++ b/reactos/ntoskrnl/mm/virtual.c @@ -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); }