reactos/ntoskrnl/kd/kdmain.c

489 lines
16 KiB
C
Raw Normal View History

KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
/*
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
* PROJECT: ReactOS Kernel
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Kernel Debugger Initialization
* COPYRIGHT: Copyright 2005 Alex Ionescu <alex.ionescu@reactos.org>
* Copyright 2020 Hervé Poussineau <hpoussin@reactos.org>
* Copyright 2023 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
*/
#include <ntoskrnl.h>
#include "kd.h"
#include "kdterminal.h"
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
#define NDEBUG
#include <debug.h>
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
#undef KdD0Transition
#undef KdD3Transition
#undef KdSave
#undef KdRestore
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
/* PUBLIC FUNCTIONS *********************************************************/
static VOID
KdpGetTerminalSettings(
_In_ PCSTR p1)
{
#define CONST_STR_LEN(x) (sizeof(x)/sizeof(x[0]) - 1)
while (p1 && *p1)
{
/* Skip leading whitespace */
while (*p1 == ' ') ++p1;
if (!_strnicmp(p1, "KDSERIAL", CONST_STR_LEN("KDSERIAL")))
{
p1 += CONST_STR_LEN("KDSERIAL");
KdbDebugState |= KD_DEBUG_KDSERIAL;
KdpDebugMode.Serial = TRUE;
}
else if (!_strnicmp(p1, "KDNOECHO", CONST_STR_LEN("KDNOECHO")))
{
p1 += CONST_STR_LEN("KDNOECHO");
KdbDebugState |= KD_DEBUG_KDNOECHO;
}
/* Move on to the next option */
p1 = strchr(p1, ' ');
}
}
static PCHAR
2023-01-09 11:31:03 +00:00
KdpGetDebugMode(
_In_ PCHAR Currentp2)
{
PCHAR p1, p2 = Currentp2;
ULONG Value;
/* Check for Screen Debugging */
if (!_strnicmp(p2, "SCREEN", 6))
{
/* Enable It */
p2 += 6;
KdpDebugMode.Screen = TRUE;
}
/* Check for Serial Debugging */
else if (!_strnicmp(p2, "COM", 3))
{
/* Check for a valid Serial Port */
p2 += 3;
if (*p2 != ':')
{
Value = (ULONG)atol(p2);
if (Value > 0 && Value < 5)
{
/* Valid port found, enable Serial Debugging */
KdpDebugMode.Serial = TRUE;
/* Set the port to use */
SerialPortNumber = Value;
}
}
else
{
Value = strtoul(p2 + 1, NULL, 0);
if (Value)
{
KdpDebugMode.Serial = TRUE;
SerialPortInfo.Address = UlongToPtr(Value);
SerialPortNumber = 0;
}
}
}
/* Check for Debug Log Debugging */
else if (!_strnicmp(p2, "FILE", 4))
{
/* Enable It */
p2 += 4;
KdpDebugMode.File = TRUE;
if (*p2 == ':')
{
p2++;
p1 = p2;
while (*p2 != '\0' && *p2 != ' ') p2++;
KdpLogFileName.MaximumLength = KdpLogFileName.Length = p2 - p1;
KdpLogFileName.Buffer = p1;
}
}
return p2;
}
NTSTATUS
NTAPI
KdDebuggerInitialize0(
2023-01-09 11:31:03 +00:00
_In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)
{
PCHAR CommandLine, Port = NULL;
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
ULONG i;
BOOLEAN Success = FALSE;
if (LoaderBlock)
{
/* Check if we have a command line */
CommandLine = LoaderBlock->LoadOptions;
if (CommandLine)
{
/* Upcase it */
_strupr(CommandLine);
/* Get terminal settings */
KdpGetTerminalSettings(CommandLine);
/* Get the port */
Port = strstr(CommandLine, "DEBUGPORT");
}
}
/* Check if we got the /DEBUGPORT parameter(s) */
while (Port)
{
/* Move past the actual string, to reach the port*/
Port += sizeof("DEBUGPORT") - 1;
/* Now get past any spaces and skip the equal sign */
while (*Port == ' ') Port++;
Port++;
/* Get the debug mode and wrapper */
Port = KdpGetDebugMode(Port);
Port = strstr(Port, "DEBUGPORT");
}
/* Use serial port then */
if (KdpDebugMode.Value == 0)
KdpDebugMode.Serial = TRUE;
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/* Call the providers at Phase 0 */
for (i = 0; i < RTL_NUMBER_OF(DispatchTable); i++)
{
DispatchTable[i].InitStatus = InitRoutines[i](&DispatchTable[i], 0);
Success = (Success || NT_SUCCESS(DispatchTable[i].InitStatus));
}
/* Return success if at least one of the providers succeeded */
return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
}
/**
* @brief Reinitialization routine.
* DRIVER_REINITIALIZE
*
* Calls each registered provider for reinitialization at Phase >= 2.
* I/O is now set up for disk access, at different phases.
**/
static VOID
NTAPI
KdpDriverReinit(
_In_ PDRIVER_OBJECT DriverObject,
_In_opt_ PVOID Context,
_In_ ULONG Count)
{
PLIST_ENTRY CurrentEntry;
PKD_DISPATCH_TABLE CurrentTable;
PKDP_INIT_ROUTINE KdpInitRoutine;
ULONG BootPhase = (Count + 1); // Do BootPhase >= 2
BOOLEAN ScheduleReinit = FALSE;
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
DPRINT("*** KD %sREINITIALIZATION - Phase %d ***\n",
Context ? "" : "BOOT ", BootPhase);
/* Call the registered providers */
for (CurrentEntry = KdProviders.Flink;
CurrentEntry != &KdProviders; NOTHING)
{
/* Get the provider */
CurrentTable = CONTAINING_RECORD(CurrentEntry,
KD_DISPATCH_TABLE,
KdProvidersList);
/* Go to the next entry (the Init routine may unlink us) */
CurrentEntry = CurrentEntry->Flink;
/* Call it if it requires a reinitialization */
if (CurrentTable->KdpInitRoutine)
{
/* Get the initialization routine and reset it */
KdpInitRoutine = CurrentTable->KdpInitRoutine;
CurrentTable->KdpInitRoutine = NULL;
CurrentTable->InitStatus = KdpInitRoutine(CurrentTable, BootPhase);
DPRINT("KdpInitRoutine(%p) returned 0x%08lx\n",
CurrentTable, CurrentTable->InitStatus);
/* Check whether it needs to be reinitialized again */
ScheduleReinit = (ScheduleReinit || CurrentTable->KdpInitRoutine);
}
}
DPRINT("ScheduleReinit: %s\n", ScheduleReinit ? "TRUE" : "FALSE");
if (ScheduleReinit)
{
/*
* Determine when to reinitialize.
* If Context == NULL, we are doing a boot-driver reinitialization.
* It is initially done once (Count == 1), and is rescheduled once
* after all other boot drivers get loaded (Count == 2).
* If further reinitialization is needed, switch to system-driver
* reinitialization and do it again, not more than twice.
*/
if (Count <= 1)
{
IoRegisterBootDriverReinitialization(DriverObject,
KdpDriverReinit,
(PVOID)FALSE);
}
else if (Count <= 3)
{
IoRegisterDriverReinitialization(DriverObject,
KdpDriverReinit,
(PVOID)TRUE);
}
else
{
/* Too late, no more reinitializations! */
DPRINT("Cannot reinitialize anymore!\n");
ScheduleReinit = FALSE;
}
}
if (!ScheduleReinit)
{
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/* All the necessary reinitializations are done,
* the driver object is not needed anymore. */
ObMakeTemporaryObject(DriverObject);
IoDeleteDriver(DriverObject);
}
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
}
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/**
* @brief Entry point for the auxiliary driver.
* DRIVER_INITIALIZE
**/
static NTSTATUS
NTAPI
KdpDriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
/* Register for reinitialization after the other drivers are loaded */
IoRegisterBootDriverReinitialization(DriverObject,
KdpDriverReinit,
(PVOID)FALSE);
/* Set the driver as initialized */
DriverObject->Flags |= DRVO_INITIALIZED;
return STATUS_SUCCESS;
}
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/**
* @brief Hooked HalInitPnpDriver() callback.
* It is initially set by the HAL when HalInitSystem(0, ...)
* is called earlier on.
**/
static pHalInitPnpDriver orgHalInitPnpDriver = NULL;
/**
* @brief
* HalInitPnpDriver() callback hook installed by KdDebuggerInitialize1().
*
* It is called during initialization of the I/O manager and is where
* the auxiliary driver is created. This driver is needed for receiving
* reinitialization callbacks in KdpDriverReinit() later.
* This hook must *always* call the original HalInitPnpDriver() function
* and return its returned value, or return STATUS_SUCCESS.
**/
static NTSTATUS
NTAPI
KdpInitDriver(VOID)
{
static BOOLEAN InitCalled = FALSE;
NTSTATUS Status;
UNICODE_STRING DriverName = RTL_CONSTANT_STRING(L"\\Driver\\KdDriver");
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
/* Ensure we are not called more than once */
if (_InterlockedCompareExchange8((char*)&InitCalled, TRUE, FALSE) != FALSE)
return STATUS_SUCCESS;
/* Create the driver */
Status = IoCreateDriver(&DriverName, KdpDriverEntry);
if (!NT_SUCCESS(Status))
DPRINT1("IoCreateDriver failed: 0x%08lx\n", Status);
/* Ignore any failure from IoCreateDriver(). If it fails, no I/O-related
* initialization will happen (no file log debugging, etc.). */
/* Finally, restore and call the original HalInitPnpDriver() */
InterlockedExchangePointer((PVOID*)&HalInitPnpDriver, orgHalInitPnpDriver);
return (HalInitPnpDriver ? HalInitPnpDriver() : STATUS_SUCCESS);
}
NTSTATUS
NTAPI
KdDebuggerInitialize1(
2023-01-09 11:31:03 +00:00
_In_opt_ PLOADER_PARAMETER_BLOCK LoaderBlock)
{
PLIST_ENTRY CurrentEntry;
PKD_DISPATCH_TABLE CurrentTable;
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
PKDP_INIT_ROUTINE KdpInitRoutine;
BOOLEAN Success = FALSE;
BOOLEAN ReinitForPhase2 = FALSE;
/* Make space for the displayed providers' signons */
HalDisplayString("\r\n");
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/* Call the registered providers */
for (CurrentEntry = KdProviders.Flink;
CurrentEntry != &KdProviders; NOTHING)
{
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/* Get the provider */
CurrentTable = CONTAINING_RECORD(CurrentEntry,
KD_DISPATCH_TABLE,
KdProvidersList);
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/* Go to the next entry (the Init routine may unlink us) */
CurrentEntry = CurrentEntry->Flink;
/* Get the initialization routine and reset it */
ASSERT(CurrentTable->KdpInitRoutine);
KdpInitRoutine = CurrentTable->KdpInitRoutine;
CurrentTable->KdpInitRoutine = NULL;
/* Call it */
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
CurrentTable->InitStatus = KdpInitRoutine(CurrentTable, 1);
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/* Check whether it needs to be reinitialized for Phase 2 */
Success = (Success || NT_SUCCESS(CurrentTable->InitStatus));
ReinitForPhase2 = (ReinitForPhase2 || CurrentTable->KdpInitRoutine);
}
/* Make space for the displayed providers' signons */
HalDisplayString("\r\n");
NtGlobalFlag |= FLG_STOP_ON_EXCEPTION;
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/* If we don't need to reinitialize providers for Phase 2, we are done */
if (!ReinitForPhase2)
{
/* Return success if at least one of them succeeded */
return (Success ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL);
}
/**
* We want to be able to perform I/O-related initialization (starting a
* logger thread for file log debugging, loading KDBinit file for KDBG,
* etc.). A good place for this would be as early as possible, once the
* I/O Manager has started the storage and the boot filesystem drivers.
*
* Here is an overview of the initialization steps of the NT Kernel and
* Executive:
* ----
* KiSystemStartup(KeLoaderBlock)
* if (Cpu == 0) KdInitSystem(0, KeLoaderBlock);
* KiSwitchToBootStack() -> KiSystemStartupBootStack()
* -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock)
*
* (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); )
* KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock);
*
* ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0;
* HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback.
* ...
* PsInitSystem(LoaderBlock)
* PsCreateSystemThread(Phase1Initialization)
*
* Phase1Initialization(Discard): ExpInitializationPhase = 1;
* HalInitSystem(1, KeLoaderBlock);
* ...
* Early initialization of Ob, Ex, Ke.
* KdInitSystem(1, KeLoaderBlock);
* ...
* KdDebuggerInitialize1(LoaderBlock);
* ...
* IoInitSystem(LoaderBlock);
* ...
* ----
* As we can see, KdDebuggerInitialize1() is the last KD initialization
* routine the kernel calls, and is called *before* the I/O Manager starts.
* Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also,
* we want to do the I/O initialization as soon as possible. There does
* not seem to be any exported way to be notified about the I/O manager
* initialization steps... that is, unless we somehow become a driver and
* insert ourselves in the flow!
*
* Since we are not a regular driver, we need to invoke IoCreateDriver()
* to create one. However, remember that we are currently running *before*
* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this,
* calling IoCreateDriver(), much like any other IO functions, would lead
* to a crash, because it calls
* ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType
* is non-initialized yet (it's NULL).
*
* The chosen solution is to hook a "known" exported callback: namely, the
* HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver").
* It is set very early on by the HAL via the HalInitSystem(0, ...) call,
* and is called early on by IoInitSystem() before any driver is loaded,
* but after the I/O Manager has been minimally set up so that new drivers
* can be created.
* When the hook: KdpInitDriver() is called, we create our driver with
* IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then
* restore and call the original HalInitPnpDriver() callback.
*
* Another possible unexplored alternative, could be to insert ourselves
* in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the
* KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while
* we may be able to do this, because boot-drivers are resident in memory,
* much like we are, we cannot insert ourselves in the system-driver list
* however, since those drivers are expected to come from PE image files.)
*
* Once the KdpDriverEntry() driver entrypoint is called, we register
* KdpDriverReinit() for re-initialization with the I/O Manager, in order
* to provide more initialization points. KdpDriverReinit() calls the KD
* providers at BootPhase >= 2, and schedules further reinitializations
* (at most 3 more) if any of the providers request so.
**/
orgHalInitPnpDriver =
InterlockedExchangePointer((PVOID*)&HalInitPnpDriver, KdpInitDriver);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
KdD0Transition(VOID)
{
/* Nothing to do */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
KdD3Transition(VOID)
{
/* Nothing to do */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
KdSave(
_In_ BOOLEAN SleepTransition)
{
/* Nothing to do */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
KdRestore(
_In_ BOOLEAN SleepTransition)
{
/* Nothing to do */
return STATUS_SUCCESS;
}
[NTOS:KD][KDBG] Rework the BootPhase >= 2 initialization of the KD/KDBG kernel debugger. (#4892) CORE-17470 + KdpDebugLogInit: Add resources cleanup in failure code paths. Fix, in an NT-compatible manner, how (and when) the KD/KDBG BootPhase >=2 initialization steps are performed. These are necessary for any functionality KDBG needs, that would depend on the NT I/O Manager and the storage and filesystem stacks to be running. This includes, creating the debug log file, and for KDBG, loading its KDBinit initialization file. As a result, file debug logging is fixed. The old ReactOS-specific (NT-incompatible) callback we did in the middle of IoInitSystem() is removed, in favor of a runtime mechanism that should work on Windows as well. The idea for this new mechanism is loosely inspired by the TDL4 rootkit, see http://blog.w4kfu.com/public/tdl4_article/draft_tdl4article.html but contrary to it, a specific hook is used instead, as well as the technique of driver reinitialization: https://web.archive.org/web/20211021050515/https://driverentry.com.br/en/blog/?p=261 Its rationale is as follows: We want to be able to perform I/O-related initialization (starting a logger thread for file log debugging, loading KDBinit file for KDBG, etc.). A good place for this would be as early as possible, once the I/O Manager has started the storage and the boot filesystem drivers. Here is an overview of the initialization steps of the NT Kernel and Executive: ---- KiSystemStartup(KeLoaderBlock) if (Cpu == 0) KdInitSystem(0, KeLoaderBlock); KiSwitchToBootStack() -> KiSystemStartupBootStack() -> KiInitializeKernel() -> ExpInitializeExecutive(Cpu, KeLoaderBlock) (NOTE: Any unexpected debugger break will call KdInitSystem(0, NULL); ) KdInitSystem(0, LoaderBlock) -> KdDebuggerInitialize0(LoaderBlock); ExpInitializeExecutive(Cpu == 0): ExpInitializationPhase = 0; HalInitSystem(0, KeLoaderBlock); <-- Sets HalInitPnpDriver callback. ... PsInitSystem(LoaderBlock) PsCreateSystemThread(Phase1Initialization) Phase1Initialization(Discard): ExpInitializationPhase = 1; HalInitSystem(1, KeLoaderBlock); ... Early initialization of Ob, Ex, Ke. KdInitSystem(1, KeLoaderBlock); ... KdDebuggerInitialize1(LoaderBlock); ... IoInitSystem(LoaderBlock); ... ---- As we can see, KdDebuggerInitialize1() is the last KD initialization routine the kernel calls, and is called *before* the I/O Manager starts. Thus, direct Nt/ZwCreateFile ... calls done there would fail. Also, we want to do the I/O initialization as soon as possible. There does not seem to be any exported way to be notified about the I/O manager initialization steps... that is, unless we somehow become a driver and insert ourselves in the flow! Since we are not a regular driver, we need to invoke IoCreateDriver() to create one. However, remember that we are currently running *before* IoInitSystem(), the I/O subsystem is not initialized yet. Due to this, calling IoCreateDriver(), much like any other IO functions, would lead to a crash, because it calls ObCreateObject(..., IoDriverObjectType, ...), and IoDriverObjectType is non-initialized yet (it's NULL). The chosen solution is to hook a "known" exported callback: namely, the HalInitPnpDriver() callback (it initializes the "HAL Root Bus Driver"). It is set very early on by the HAL via the HalInitSystem(0, ...) call, and is called early on by IoInitSystem() before any driver is loaded, but after the I/O Manager has been minimally set up so that new drivers can be created. When the hook: KdpInitDriver() is called, we create our driver with IoCreateDriver(), specifying its entrypoint KdpDriverEntry(), then restore and call the original HalInitPnpDriver() callback. Another possible unexplored alternative, could be to insert ourselves in the KeLoaderBlock->LoadOrderListHead boot modules list, or in the KeLoaderBlock->BootDriverListHead boot-driver list. (Note that while we may be able to do this, because boot-drivers are resident in memory, much like we are, we cannot insert ourselves in the system-driver list however, since those drivers are expected to come from PE image files.) Once the KdpDriverEntry() driver entrypoint is called, we register KdpDriverReinit() for re-initialization with the I/O Manager, in order to provide more initialization points. KdpDriverReinit() calls the KD providers at BootPhase >= 2, and schedules further reinitializations (at most 3 more) if any of the providers request so.
2023-01-09 18:35:18 +00:00
/* EOF */