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);
 }