VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGPlugInWinNt.cpp

Last change on this file was 105532, checked in by vboxsync, 7 weeks ago

Debugger/DBGPlugInWinNt: Detect winload.exe and the dll part of the bootmgr, also be able to locate the NT kernel after winload has loaded it. bugref:10727

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.3 KB
Line 
1/* $Id: DBGPlugInWinNt.cpp 105532 2024-07-27 00:55:53Z vboxsync $ */
2/** @file
3 * DBGPlugInWindows - Debugger and Guest OS Digger Plugin For Windows NT.
4 */
5
6/*
7 * Copyright (C) 2009-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DBGF /// @todo add new log group.
33#include "DBGPlugIns.h"
34#include <VBox/vmm/dbgf.h>
35#include <VBox/vmm/cpumctx.h>
36#include <VBox/vmm/mm.h>
37#ifdef VBOX_DEBUGGER_WITH_WIN_DBG_PRINT_HOOKING
38# include <VBox/vmm/vmapi.h>
39# include <VBox/dis.h>
40#endif
41#include <VBox/vmm/vmmr3vtable.h>
42#include <VBox/err.h>
43#include <VBox/param.h>
44#include <iprt/ctype.h>
45#include <iprt/ldr.h>
46#include <iprt/mem.h>
47#include <iprt/path.h>
48#include <iprt/stream.h>
49#include <iprt/string.h>
50#include <iprt/utf16.h>
51#include <iprt/formats/pecoff.h>
52#include <iprt/formats/mz.h>
53#include <iprt/nt/nt-structures.h>
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59
60/** @name Internal WinNT structures
61 * @{ */
62/**
63 * PsLoadedModuleList entry for 32-bit NT aka LDR_DATA_TABLE_ENTRY.
64 * Tested with XP.
65 *
66 * See comments in NTMTE64.
67 */
68typedef struct NTMTE32
69{
70 struct
71 {
72 uint32_t Flink;
73 uint32_t Blink;
74 } InLoadOrderLinks,
75 InMemoryOrderModuleList,
76 InInitializationOrderModuleList;
77 uint32_t DllBase;
78 uint32_t EntryPoint;
79 /** @note This field is not a size in NT 3.1. It's NULL for images loaded by the
80 * boot loader, for other images it looks like some kind of pointer. */
81 uint32_t SizeOfImage;
82 struct
83 {
84 uint16_t Length;
85 uint16_t MaximumLength;
86 uint32_t Buffer;
87 } FullDllName,
88 BaseDllName;
89 uint32_t Flags; /**< NTMTE_F_XXX */
90 uint16_t LoadCount;
91 uint16_t TlsIndex;
92 /* ... there is more ... */
93} NTMTE32;
94typedef NTMTE32 *PNTMTE32;
95
96/**
97 * PsLoadedModuleList entry for 64-bit NT aka LDR_DATA_TABLE_ENTRY.
98 *
99 * Starting with XP it it's specialized for the kernel as KLDR_DATA_TABLE_ENTRY
100 * with compatible layout up to a point, the non-K version is used in user land.
101 * The Flags values probably differs a bit by now.
102 */
103typedef struct NTMTE64
104{
105 struct
106 {
107 uint64_t Flink;
108 uint64_t Blink;
109 } InLoadOrderLinks, /**< 0x00 */
110 InMemoryOrderModuleList, /**< 0x10 */
111 InInitializationOrderModuleList; /**< 0x20 */
112 uint64_t DllBase; /**< 0x30 */
113 uint64_t EntryPoint; /**< 0x38 */
114 uint32_t SizeOfImage; /**< 0x40 */
115 uint32_t Alignment; /**< 0x44 */
116 struct
117 {
118 uint16_t Length; /**< 0x48,0x58 */
119 uint16_t MaximumLength; /**< 0x4a,0x5a */
120 uint32_t Alignment; /**< 0x4c,0x5c */
121 uint64_t Buffer; /**< 0x50,0x60 */
122 } FullDllName, /**< 0x48 */
123 BaseDllName; /**< 0x58 */
124 uint32_t Flags; /**< 0x68 NTMTE_F_XXX */
125 uint16_t LoadCount; /**< 0x6c */
126 uint16_t TlsIndex; /**< 0x6e */
127 /* ... there is more ... */
128} NTMTE64;
129typedef NTMTE64 *PNTMTE64;
130
131#define NTMTE_F_FORCE_INTEGRITY 0x20 /* copy of IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY */
132
133/** MTE union. */
134typedef union NTMTE
135{
136 NTMTE32 vX_32;
137 NTMTE64 vX_64;
138} NTMTE;
139typedef NTMTE *PNTMTE;
140
141
142/**
143 * The essential bits of the KUSER_SHARED_DATA structure.
144 */
145typedef struct NTKUSERSHAREDDATA
146{
147 uint32_t TickCountLowDeprecated;
148 uint32_t TickCountMultiplier;
149 struct
150 {
151 uint32_t LowPart;
152 int32_t High1Time;
153 int32_t High2Time;
154
155 } InterruptTime,
156 SystemTime,
157 TimeZoneBias;
158 uint16_t ImageNumberLow;
159 uint16_t ImageNumberHigh;
160 RTUTF16 NtSystemRoot[260];
161 uint32_t MaxStackTraceDepth;
162 uint32_t CryptoExponent;
163 uint32_t TimeZoneId;
164 uint32_t LargePageMinimum;
165 uint32_t Reserved2[6];
166 uint32_t NtBuildNumber;
167 uint32_t NtProductType;
168 uint8_t ProductTypeIsValid;
169 uint8_t abPadding[3];
170 uint32_t NtMajorVersion;
171 uint32_t NtMinorVersion;
172 /* uint8_t ProcessorFeatures[64];
173 ...
174 */
175} NTKUSERSHAREDDATA;
176typedef NTKUSERSHAREDDATA *PNTKUSERSHAREDDATA;
177
178/** KI_USER_SHARED_DATA for i386 */
179#define NTKUSERSHAREDDATA_WINNT32 UINT32_C(0xffdf0000)
180/** KI_USER_SHARED_DATA for AMD64 */
181#define NTKUSERSHAREDDATA_WINNT64 UINT64_C(0xfffff78000000000)
182
183/** NTKUSERSHAREDDATA::NtProductType */
184typedef enum NTPRODUCTTYPE
185{
186 kNtProductType_Invalid = 0,
187 kNtProductType_WinNt = 1,
188 kNtProductType_LanManNt,
189 kNtProductType_Server
190} NTPRODUCTTYPE;
191
192
193/** NT image header union. */
194typedef union NTHDRSU
195{
196 IMAGE_NT_HEADERS32 vX_32;
197 IMAGE_NT_HEADERS64 vX_64;
198} NTHDRS;
199/** Pointer to NT image header union. */
200typedef NTHDRS *PNTHDRS;
201/** Pointer to const NT image header union. */
202typedef NTHDRS const *PCNTHDRS;
203
204
205/**
206 * NT KD version block.
207 */
208typedef struct NTKDVERSIONBLOCK
209{
210 uint16_t MajorVersion;
211 uint16_t MinorVersion;
212 uint8_t ProtocolVersion;
213 uint8_t KdSecondaryVersion;
214 uint16_t Flags;
215 uint16_t MachineType;
216 uint8_t MaxPacketType;
217 uint8_t MaxStateChange;
218 uint8_t MaxManipulate;
219 uint8_t Simulation;
220 uint16_t Unused;
221 uint64_t KernBase;
222 uint64_t PsLoadedModuleList;
223 uint64_t DebuggerDataList;
224} NTKDVERSIONBLOCK;
225/** Pointer to an NT KD version block. */
226typedef NTKDVERSIONBLOCK *PNTKDVERSIONBLOCK;
227/** Pointer to a const NT KD version block. */
228typedef const NTKDVERSIONBLOCK *PCNTKDVERSIONBLOCK;
229
230/** @} */
231
232
233
234typedef enum DBGDIGGERWINNTVER
235{
236 DBGDIGGERWINNTVER_UNKNOWN,
237 DBGDIGGERWINNTVER_3_1,
238 DBGDIGGERWINNTVER_3_5,
239 DBGDIGGERWINNTVER_4_0,
240 DBGDIGGERWINNTVER_5_0,
241 DBGDIGGERWINNTVER_5_1,
242 DBGDIGGERWINNTVER_6_0
243} DBGDIGGERWINNTVER;
244
245/**
246 * WinNT guest OS digger instance data.
247 */
248typedef struct DBGDIGGERWINNT
249{
250 /** Whether the information is valid or not.
251 * (For fending off illegal interface method calls.) */
252 bool fValid;
253 /** 32-bit (true) or 64-bit (false) */
254 bool f32Bit;
255 /** Set if NT 3.1 was detected.
256 * This implies both Misc.VirtualSize and NTMTE32::SizeOfImage are zero. */
257 bool fNt31;
258
259 /** The NT version. */
260 DBGDIGGERWINNTVER enmVer;
261 /** NTKUSERSHAREDDATA::NtProductType */
262 NTPRODUCTTYPE NtProductType;
263 /** NTKUSERSHAREDDATA::NtMajorVersion */
264 uint32_t NtMajorVersion;
265 /** NTKUSERSHAREDDATA::NtMinorVersion */
266 uint32_t NtMinorVersion;
267 /** NTKUSERSHAREDDATA::NtBuildNumber */
268 uint32_t NtBuildNumber;
269
270 /** The address of the bootmgr image if early during boot. */
271 DBGFADDRESS BootMgrAddr;
272 /** The address of the winload.exe image if early during boot.
273 * When this is set, f32Bit has also been determined. */
274 DBGFADDRESS WinLoadAddr;
275
276 /** The address of the ntoskrnl.exe image. */
277 DBGFADDRESS KernelAddr;
278 /** The address of the ntoskrnl.exe module table entry. */
279 DBGFADDRESS KernelMteAddr;
280 /** The address of PsLoadedModuleList. */
281 DBGFADDRESS PsLoadedModuleListAddr;
282
283 /** Array of detected KPCR addresses for each vCPU. */
284 PDBGFADDRESS paKpcrAddr;
285 /** Array of detected KPCRB addresses for each vCPU. */
286 PDBGFADDRESS paKpcrbAddr;
287
288 /** The Windows NT specifics interface. */
289 DBGFOSIWINNT IWinNt;
290
291#ifdef VBOX_DEBUGGER_WITH_WIN_DBG_PRINT_HOOKING
292 /** Breakpoint owner handle for the DbgPrint/vDbgPrint{,Ex}... interception. */
293 DBGFBPOWNER hBpOwnerDbgPrint;
294 /** Breakpoint handle for the DbgPrint/vDbgPrint{,Ex}... interception. */
295 DBGFBP hBpDbgPrint;
296#endif
297} DBGDIGGERWINNT;
298/** Pointer to the linux guest OS digger instance data. */
299typedef DBGDIGGERWINNT *PDBGDIGGERWINNT;
300
301
302/**
303 * The WinNT digger's loader reader instance data.
304 */
305typedef struct DBGDIGGERWINNTRDR
306{
307 /** The VM handle (referenced). */
308 PUVM pUVM;
309 /** The image base. */
310 DBGFADDRESS ImageAddr;
311 /** The image size. */
312 uint32_t cbImage;
313 /** The file offset of the SizeOfImage field in the optional header if it
314 * needs patching, otherwise set to UINT32_MAX. */
315 uint32_t offSizeOfImage;
316 /** The correct image size. */
317 uint32_t cbCorrectImageSize;
318 /** Number of entries in the aMappings table. */
319 uint32_t cMappings;
320 /** Mapping hint. */
321 uint32_t iHint;
322 /** Mapping file offset to memory offsets, ordered by file offset. */
323 struct
324 {
325 /** The file offset. */
326 uint32_t offFile;
327 /** The size of this mapping. */
328 uint32_t cbMem;
329 /** The offset to the memory from the start of the image. */
330 uint32_t offMem;
331 } aMappings[1];
332} DBGDIGGERWINNTRDR;
333/** Pointer a WinNT loader reader instance data. */
334typedef DBGDIGGERWINNTRDR *PDBGDIGGERWINNTRDR;
335
336
337/*********************************************************************************************************************************
338* Defined Constants And Macros *
339*********************************************************************************************************************************/
340/** Validates a 32-bit Windows NT kernel address */
341#define WINNT32_VALID_ADDRESS(Addr) ((Addr) > UINT32_C(0x80000000) && (Addr) < UINT32_C(0xfffff000))
342/** Validates a 64-bit Windows NT kernel address */
343#define WINNT64_VALID_ADDRESS(Addr) ((Addr) > UINT64_C(0xffff800000000000) && (Addr) < UINT64_C(0xfffffffffffff000))
344/** Validates a kernel address. */
345#define WINNT_VALID_ADDRESS(pThis, Addr) ((pThis)->f32Bit ? WINNT32_VALID_ADDRESS(Addr) : WINNT64_VALID_ADDRESS(Addr))
346/** Versioned and bitness wrapper. */
347#define WINNT_UNION(pThis, pUnion, Member) ((pThis)->f32Bit ? (pUnion)->vX_32. Member : (pUnion)->vX_64. Member )
348
349/** The length (in chars) of the kernel file name (no path). */
350#define WINNT_KERNEL_BASE_NAME_LEN 12
351
352/** WindowsNT on little endian ASCII systems. */
353#define DIG_WINNT_MOD_TAG UINT64_C(0x54696e646f774e54)
354
355
356/*********************************************************************************************************************************
357* Internal Functions *
358*********************************************************************************************************************************/
359static DECLCALLBACK(int) dbgDiggerWinNtInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData);
360
361
362/*********************************************************************************************************************************
363* Global Variables *
364*********************************************************************************************************************************/
365/** Kernel names. */
366static const RTUTF16 g_wszKernelNames[][WINNT_KERNEL_BASE_NAME_LEN + 1] =
367{
368 { 'n', 't', 'o', 's', 'k', 'r', 'n', 'l', '.', 'e', 'x', 'e' }
369};
370
371
372#ifdef VBOX_DEBUGGER_WITH_WIN_DBG_PRINT_HOOKING
373
374/**
375 * Queries the string from guest memory with the pointer in the given register, sanitizing it.
376 *
377 * @returns VBox status code.
378 * @param pUVM The user mode VM handle.
379 * @param idCpu The CPU ID.
380 * @param enmReg The register to query the string pointer from.
381 * @param pszBuf Where to store the sanitized string.
382 * @param cbBuf Size of the buffer in number of bytes.
383 */
384static int dbgDiggerWinNtDbgPrintQueryStringFromReg(PUVM pUVM, VMCPUID idCpu, DBGFREG enmReg, char *pszBuf, size_t cbBuf)
385{
386 uint64_t u64RegPtr = 0;
387 int rc = DBGFR3RegCpuQueryU64(pUVM, idCpu, enmReg, &u64RegPtr);
388 if ( rc == VINF_SUCCESS
389 || rc == VINF_DBGF_ZERO_EXTENDED_REGISTER) /* Being strict about what we expect here. */
390 {
391 DBGFADDRESS AddrStr;
392 DBGFR3AddrFromFlat(pUVM, &AddrStr, u64RegPtr);
393 rc = DBGFR3MemRead(pUVM, idCpu, &AddrStr, pszBuf, cbBuf);
394 if (RT_SUCCESS(rc))
395 {
396 /* Check that there is a zero terminator and purge invalid encoding (expecting UTF-8 here). */
397 size_t idx = 0;
398 for (idx = 0; idx < cbBuf; idx++)
399 if (pszBuf[idx] == '\0')
400 break;
401
402 if (idx == cbBuf)
403 pszBuf[cbBuf - 1] = '\0'; /* Force terminator, truncating the string. */
404 else
405 memset(&pszBuf[idx], 0, cbBuf - idx); /* Clear everything afterwards. */
406
407 /* Purge the string encoding. */
408 RTStrPurgeEncoding(pszBuf);
409 }
410 }
411 else if (RT_SUCCESS(rc))
412 rc = VERR_INVALID_STATE;
413
414 return rc;
415}
416
417
418/**
419 * @copydoc{FNDBGFBPHIT, Breakpoint callback for the DbgPrint interception.}
420 */
421static DECLCALLBACK(VBOXSTRICTRC) dbgDiggerWinNtDbgPrintHit(PVM pVM, VMCPUID idCpu, void *pvUserBp, DBGFBP hBp, PCDBGFBPPUB pBpPub, uint16_t fFlags)
422{
423 RT_NOREF(hBp, pBpPub, fFlags);
424 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvUserBp;
425 PUVM pUVM = VMR3GetUVM(pVM);
426
427 /*
428 * The worker prototype looks like the following:
429 * vDbgPrintExWorker(PCCH Prefix, ULONG ComponentId, ULONG Level, PCCH Format, va_list arglist, BOOL fUnknown)
430 *
431 * Depending on the bitness the parameters are grabbed from the appropriate registers and stack locations.
432 * For amd64 reading the following is recommended:
433 * https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=vs-2019
434 * https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=vs-2019
435 * https://docs.microsoft.com/en-us/cpp/build/stack-usage?view=vs-2019
436 *
437 * @todo 32bit
438 */
439 int rc = VINF_SUCCESS;
440 uint32_t idComponent = 0;
441 uint32_t iLevel = 0;
442 char aszPrefixStr[128]; /* Restricted size. */
443 char aszFmtStr[_1K]; /* Restricted size. */
444 DBGFADDRESS AddrVaList;
445 if (!pThis->f32Bit)
446 {
447 /*
448 * Grab the prefix, component, level, format string pointer from the registers and the argument list from the
449 * stack (mind the home area for the register arguments).
450 */
451 rc = dbgDiggerWinNtDbgPrintQueryStringFromReg(pUVM, idCpu, DBGFREG_RCX, &aszPrefixStr[0], sizeof(aszPrefixStr));
452 if (RT_SUCCESS(rc))
453 rc = DBGFR3RegCpuQueryU32(pUVM, idCpu, DBGFREG_RDX, &idComponent);
454 if (RT_SUCCESS(rc))
455 rc = DBGFR3RegCpuQueryU32(pUVM, idCpu, DBGFREG_R8, &iLevel);
456 if (RT_SUCCESS(rc))
457 rc = dbgDiggerWinNtDbgPrintQueryStringFromReg(pUVM, idCpu, DBGFREG_R9, &aszFmtStr[0], sizeof(aszFmtStr));
458 if (RT_SUCCESS(rc))
459 {
460 /* Grabbing the pointer to the va list. The stack layout when we are here looks like (each entry is 64bit):
461 * +-------------+
462 * | ... |
463 * | VA list ptr |
464 * | (arg3/r9) |
465 * | (arg2/r8) |
466 * | (arg1/rdx) |
467 * | (arg0/rcx) |
468 * | return RIP |
469 * +-------------+ <- RSP
470 */
471 uint64_t uRegRsp = 0;
472 rc = DBGFR3RegCpuQueryU64(pUVM, idCpu, DBGFREG_RSP, &uRegRsp);
473 if (rc == VINF_SUCCESS)
474 {
475 DBGFADDRESS AddrVaListPtr;
476 RTGCUINTPTR GCPtrVaList = 0;
477
478 DBGFR3AddrFromFlat(pUVM, &AddrVaListPtr, uRegRsp + 5 * sizeof(RTGCUINTPTR));
479 rc = DBGFR3MemRead(pUVM, idCpu, &AddrVaListPtr, &GCPtrVaList, sizeof(GCPtrVaList));
480 if (RT_SUCCESS(rc))
481 DBGFR3AddrFromFlat(pUVM, &AddrVaList, GCPtrVaList);
482 }
483 else
484 rc = VERR_INVALID_STATE;
485 }
486 }
487 else
488 rc = VERR_NOT_IMPLEMENTED; /** @todo */
489
490 if (RT_SUCCESS(rc))
491 {
492 LogRel(("DigWinNt/DbgPrint: Queried arguments %s %#x %u %s %RGv\n", &aszPrefixStr[0], idComponent, iLevel, &aszFmtStr[0], AddrVaList.FlatPtr));
493 /** @todo Continue here. */
494 }
495 else
496 LogRel(("DigWinNt/DbgPrint: Failed to query all arguments with rc=%Rrc\n", rc));
497
498 return VINF_SUCCESS;
499}
500
501
502/**
503 * Disassembles the given instruction and checks whether it is a call with a fixed address.
504 *
505 * @returns Flag whether the insturction at the given address is a call.
506 * @param pThis The instance data.
507 * @param pUVM The user mode VM handle.
508 * @param pAddrInsn Guest address of the instruction.
509 * @param pAddrCall Where to store the destination if the instruction is a call.
510 */
511static bool dbgDiggerWinNtDbgPrintWrapperInsnIsCall(PDBGDIGGERWINNT pThis, PUVM pUVM, PCDBGFADDRESS pAddrInsn, PDBGFADDRESS pAddrCall)
512{
513 DISSTATE DisState;
514 RT_ZERO(DisState);
515
516 /* Prefetch the instruction. */
517 uint8_t abInstr[32];
518 int rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, pAddrInsn, &abInstr[0], sizeof(abInstr));
519 if (RT_SUCCESS(rc))
520 {
521 uint32_t cbInsn = 0;
522 rc = DISInstr(&abInstr[0], pThis->f32Bit ? DISCPUMODE_32BIT : DISCPUMODE_64BIT, &DisState, &cbInsn);
523 if ( RT_SUCCESS(rc)
524 && DisState.pCurInstr->uOpcode == OP_CALL
525 && DisState.Param1.fUse & DISUSE_IMMEDIATE)
526 {
527 if (DisState.Param1.fUse & (DISUSE_IMMEDIATE32 | DISUSE_IMMEDIATE64))
528 DBGFR3AddrFromFlat(pUVM, pAddrCall, DisState.Param1.uValue);
529 else if (DisState.Param1.fUse & (DISUSE_IMMEDIATE32_REL | DISUSE_IMMEDIATE64_REL))
530 {
531 *pAddrCall = *pAddrInsn;
532 DBGFR3AddrAdd(pAddrCall, DisState.Param1.uValue + cbInsn);
533 }
534
535 return true;
536 }
537 }
538
539 return false;
540}
541
542
543/**
544 * Tries to find the single call instruction of the DbgPrint/etc. worker in the given control flow graph
545 * (single basic block assumed).
546 *
547 * @returns VBox status code.
548 * @param pThis The instance data.
549 * @param pUVM The user mode VM handle.
550 * @param hFlow The control flow graph handle.
551 * @param pAddr Where to store the worker address on success.
552 */
553static int dbgDiggerWinNtDbgPrintResolveWorker(PDBGDIGGERWINNT pThis, PUVM pUVM, DBGFFLOW hFlow, PDBGFADDRESS pAddr)
554{
555 DBGFFLOWBB hBb;
556 int rc = DBGFR3FlowQueryStartBb(hFlow, &hBb);
557 if (RT_SUCCESS(rc))
558 {
559 bool fCallFound = false;
560
561 for (uint32_t i = 0; i < DBGFR3FlowBbGetInstrCount(hBb) && RT_SUCCESS(rc); i++)
562 {
563 DBGFADDRESS AddrInsn;
564 uint32_t cbInsn;
565 rc = DBGFR3FlowBbQueryInstr(hBb, i, &AddrInsn, &cbInsn, NULL);
566 if (RT_SUCCESS(rc))
567 {
568 DBGFADDRESS AddrCall;
569 if (dbgDiggerWinNtDbgPrintWrapperInsnIsCall(pThis, pUVM, &AddrInsn, &AddrCall))
570 {
571 if (!fCallFound)
572 {
573 *pAddr = AddrCall;
574 fCallFound = true;
575 }
576 else
577 {
578 LogRel(("DigWinNt/DbgPrint: nt!vDbgPrintEx contains multiple call instructions!\n"));
579 rc = VERR_ALREADY_EXISTS;
580 }
581 }
582 }
583 }
584
585 DBGFR3FlowBbRelease(hBb);
586 }
587
588 return rc;
589}
590
591
592/**
593 * Tries to resolve and hook into the worker for all the DbgPrint like wrappers to be able
594 * to gather debug information from the system.
595 *
596 * @param pThis The instance data.
597 * @param pUVM The user mode VM handle.
598 */
599static void dbgDiggerWinNtDbgPrintHook(PDBGDIGGERWINNT pThis, PUVM pUVM)
600{
601 /*
602 * This is a multi step process:
603 * 1. Try to resolve the address of vDbgPrint() (available since XP).
604 * 2. Create a control flow graph from the code and verify the following assumptions:
605 * 1. Only a single basic block.
606 * 2. Just one call instruction.
607 * @todo More?
608 * 3. Get the address from the called worker
609 * 4. Set a hardware breakpoint with our callback.
610 */
611 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
612 if (hAs != NIL_RTDBGAS)
613 {
614 RTDBGSYMBOL SymInfo;
615 int rc = RTDbgAsSymbolByName(hAs, "nt!vDbgPrintEx", &SymInfo, NULL /*phMod*/);
616 if (RT_SUCCESS(rc))
617 {
618 DBGFADDRESS Addr;
619 DBGFR3AddrFromFlat(pUVM, &Addr, (RTGCPTR)SymInfo.Value);
620
621 LogRel(("DigWinNt/DbgPrint: nt!vDbgPrintEx resolved to %RGv\n", SymInfo.Value));
622
623 DBGFFLOW hCfg;
624 rc = DBGFR3FlowCreate(pUVM, 0 /*idCpu*/, &Addr, 512 /*cbDisasmMax*/,
625 0 /*fFlagsFlow*/, DBGF_DISAS_FLAGS_UNPATCHED_BYTES | DBGF_DISAS_FLAGS_ANNOTATE_PATCHED | DBGF_DISAS_FLAGS_DEFAULT_MODE,
626 &hCfg);
627 if (RT_SUCCESS(rc))
628 {
629 /* Verify assumptions. */
630 if (DBGFR3FlowGetBbCount(hCfg) == 1)
631 {
632 rc = dbgDiggerWinNtDbgPrintResolveWorker(pThis, pUVM, hCfg, &Addr);
633 if (RT_SUCCESS(rc))
634 {
635 /* Try to hook the worker. */
636 LogRel(("DigWinNt/DbgPrint: Worker for nt!vDbgPrintEx resolved to %RGv\n", Addr.FlatPtr));
637 rc = DBGFR3BpOwnerCreate(pUVM, dbgDiggerWinNtDbgPrintHit, NULL /*pfnBpIoHit*/, &pThis->hBpOwnerDbgPrint);
638 if (RT_SUCCESS(rc))
639 {
640 rc = DBGFR3BpSetInt3Ex(pUVM, pThis->hBpOwnerDbgPrint, pThis, 0 /*idCpu*/, &Addr, DBGF_BP_F_DEFAULT,
641 0 /*iHitTrigger*/, 0 /*iHitDisable*/, &pThis->hBpDbgPrint);
642 if (RT_SUCCESS(rc))
643 LogRel(("DigWinNt/DbgPrint: Hooked nt!vDbgPrintEx worker hBp=%#x\n", pThis->hBpDbgPrint));
644 else
645 {
646 LogRel(("DigWinNt/DbgPrint: Setting hardware breakpoint for nt!vDbgPrintEx worker failed with rc=%Rrc\n", rc));
647 int rc2 = DBGFR3BpOwnerDestroy(pUVM, pThis->hBpOwnerDbgPrint);
648 pThis->hBpOwnerDbgPrint = NIL_DBGFBPOWNER;
649 AssertRC(rc2);
650 }
651 }
652 }
653 /* else LogRel() already done */
654 }
655 else
656 LogRel(("DigWinNt/DbgPrint: Control flow graph for nt!vDbgPrintEx has more than one basic block (%u)\n",
657 DBGFR3FlowGetBbCount(hCfg)));
658
659 DBGFR3FlowRelease(hCfg);
660 }
661 else
662 LogRel(("DigWinNt/DbgPrint: Failed to create control flow graph from nt!vDbgPrintEx rc=%Rrc\n", rc));
663 }
664 else
665 LogRel(("DigWinNt/DbgPrint: Failed to resolve nt!vDbgPrintEx -> rc=%Rrc\n", rc));
666 RTDbgAsRelease(hAs);
667 }
668 else
669 LogRel(("DigWinNt/DbgPrint: Failed to resolve kernel address space handle\n"));
670}
671
672#endif /* VBOX_DEBUGGER_WITH_WIN_DBG_PRINT_HOOKING */
673
674/**
675 * Tries to resolve the KPCR and KPCRB addresses for each vCPU.
676 *
677 * @param pThis The instance data.
678 * @param pUVM The user mode VM handle.
679 * @param pVMM The VMM function table.
680 */
681static void dbgDiggerWinNtResolveKpcr(PDBGDIGGERWINNT pThis, PUVM pUVM, PCVMMR3VTABLE pVMM)
682{
683 /*
684 * Getting at the KPCR and KPCRB is explained here:
685 * https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kpcr.htm
686 * Together with the available offsets from:
687 * https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ksamd64.inc#L883
688 * we can verify that the found addresses are valid by cross checking that the GDTR and self reference
689 * match what we expect.
690 */
691 VMCPUID cCpus = pVMM->pfnDBGFR3CpuGetCount(pUVM);
692 pThis->paKpcrAddr = (PDBGFADDRESS)RTMemAllocZ(cCpus * 2 * sizeof(DBGFADDRESS));
693 if (RT_LIKELY(pThis->paKpcrAddr))
694 {
695 pThis->paKpcrbAddr = &pThis->paKpcrAddr[cCpus];
696
697 /* Work each CPU, unexpected values in each CPU make the whole thing fail to play safe. */
698 int rc = VINF_SUCCESS;
699 for (VMCPUID idCpu = 0; (idCpu < cCpus) && RT_SUCCESS(rc); idCpu++)
700 {
701 PDBGFADDRESS pKpcrAddr = &pThis->paKpcrAddr[idCpu];
702 PDBGFADDRESS pKpcrbAddr = &pThis->paKpcrbAddr[idCpu];
703
704 if (pThis->f32Bit)
705 {
706 /* Read FS base */
707 uint32_t GCPtrKpcrBase = 0;
708
709 rc = pVMM->pfnDBGFR3RegCpuQueryU32(pUVM, idCpu, DBGFREG_FS_BASE, &GCPtrKpcrBase);
710 if ( RT_SUCCESS(rc)
711 && WINNT32_VALID_ADDRESS(GCPtrKpcrBase))
712 {
713 /*
714 * Read the start of the KPCR (@todo Probably move this to a global header)
715 * and verify its content.
716 */
717 struct
718 {
719 uint8_t abOoi[28]; /* Out of interest */
720 uint32_t GCPtrSelf;
721 uint32_t GCPtrCurrentPrcb;
722 uint32_t u32Irql;
723 uint32_t u32Iir;
724 uint32_t u32IirActive;
725 uint32_t u32Idr;
726 uint32_t GCPtrKdVersionBlock;
727 uint32_t GCPtrIdt;
728 uint32_t GCPtrGdt;
729 uint32_t GCPtrTss;
730 } Kpcr;
731
732 LogFlow(("DigWinNt/KPCR[%u]: GS Base %RGv\n", idCpu, GCPtrKpcrBase));
733 pVMM->pfnDBGFR3AddrFromFlat(pUVM, pKpcrAddr, GCPtrKpcrBase);
734
735 rc = pVMM->pfnDBGFR3MemRead(pUVM, idCpu, pKpcrAddr, &Kpcr, sizeof(Kpcr));
736 if (RT_SUCCESS(rc))
737 {
738 uint32_t GCPtrGdt = 0;
739 uint32_t GCPtrIdt = 0;
740
741 rc = pVMM->pfnDBGFR3RegCpuQueryU32(pUVM, idCpu, DBGFREG_GDTR_BASE, &GCPtrGdt);
742 if (RT_SUCCESS(rc))
743 rc = pVMM->pfnDBGFR3RegCpuQueryU32(pUVM, idCpu, DBGFREG_IDTR_BASE, &GCPtrIdt);
744 if (RT_SUCCESS(rc))
745 {
746 if ( Kpcr.GCPtrGdt == GCPtrGdt
747 && Kpcr.GCPtrIdt == GCPtrIdt
748 && Kpcr.GCPtrSelf == pKpcrAddr->FlatPtr)
749 {
750 pVMM->pfnDBGFR3AddrFromFlat(pUVM, pKpcrbAddr, Kpcr.GCPtrCurrentPrcb);
751 LogRel(("DigWinNt/KPCR[%u]: KPCR=%RGv KPCRB=%RGv\n", idCpu, pKpcrAddr->FlatPtr, pKpcrbAddr->FlatPtr));
752
753 /*
754 * Try to extract the NT build number from the KD version block if it exists,
755 * the shared user data might have set it to 0.
756 *
757 * @todo We can use this method to get at the kern base and loaded module list if the other detection
758 * method fails (seen with Windows 10 x86).
759 * @todo On 32bit Windows the debugger data list is also always accessible this way contrary to
760 * the amd64 version where it is only available with "/debug on" set.
761 */
762 if (!pThis->NtBuildNumber)
763 {
764 NTKDVERSIONBLOCK KdVersBlock;
765 DBGFADDRESS AddrKdVersBlock;
766
767 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrKdVersBlock, Kpcr.GCPtrKdVersionBlock);
768 rc = pVMM->pfnDBGFR3MemRead(pUVM, idCpu, &AddrKdVersBlock, &KdVersBlock, sizeof(KdVersBlock));
769 if (RT_SUCCESS(rc))
770 pThis->NtBuildNumber = KdVersBlock.MinorVersion;
771 }
772 }
773 else
774 LogRel(("DigWinNt/KPCR[%u]: KPCR validation error GDT=(%RGv vs %RGv) KPCR=(%RGv vs %RGv)\n", idCpu,
775 Kpcr.GCPtrGdt, GCPtrGdt, Kpcr.GCPtrSelf, pKpcrAddr->FlatPtr));
776 }
777 else
778 LogRel(("DigWinNt/KPCR[%u]: Getting GDT or IDT base register failed with %Rrc\n", idCpu, rc));
779 }
780 }
781 else
782 LogRel(("DigWinNt/KPCR[%u]: Getting FS base register failed with %Rrc (%RGv)\n", idCpu, rc, GCPtrKpcrBase));
783 }
784 else
785 {
786 /* Read GS base which points to the base of the KPCR for each CPU. */
787 RTGCUINTPTR GCPtrTmp = 0;
788 rc = pVMM->pfnDBGFR3RegCpuQueryU64(pUVM, idCpu, DBGFREG_GS_BASE, &GCPtrTmp);
789 if ( RT_SUCCESS(rc)
790 && !WINNT64_VALID_ADDRESS(GCPtrTmp))
791 {
792 /*
793 * Could be a user address when we stopped the VM right in usermode,
794 * read the GS kernel base MSR instead.
795 */
796 rc = pVMM->pfnDBGFR3RegCpuQueryU64(pUVM, idCpu, DBGFREG_MSR_K8_KERNEL_GS_BASE, &GCPtrTmp);
797 }
798
799 if ( RT_SUCCESS(rc)
800 && WINNT64_VALID_ADDRESS(GCPtrTmp))
801 {
802 LogFlow(("DigWinNt/KPCR[%u]: GS Base %RGv\n", idCpu, GCPtrTmp));
803 pVMM->pfnDBGFR3AddrFromFlat(pUVM, pKpcrAddr, GCPtrTmp);
804
805 rc = pVMM->pfnDBGFR3RegCpuQueryU64(pUVM, idCpu, DBGFREG_GDTR_BASE, &GCPtrTmp);
806 if (RT_SUCCESS(rc))
807 {
808 /*
809 * Read the start of the KPCR (@todo Probably move this to a global header)
810 * and verify its content.
811 */
812 struct
813 {
814 RTGCUINTPTR GCPtrGdt;
815 RTGCUINTPTR GCPtrTss;
816 RTGCUINTPTR GCPtrUserRsp;
817 RTGCUINTPTR GCPtrSelf;
818 RTGCUINTPTR GCPtrCurrentPrcb;
819 } Kpcr;
820
821 rc = pVMM->pfnDBGFR3MemRead(pUVM, idCpu, pKpcrAddr, &Kpcr, sizeof(Kpcr));
822 if (RT_SUCCESS(rc))
823 {
824 if ( Kpcr.GCPtrGdt == GCPtrTmp
825 && Kpcr.GCPtrSelf == pKpcrAddr->FlatPtr
826 /** @todo && TSS */ )
827 {
828 pVMM->pfnDBGFR3AddrFromFlat(pUVM, pKpcrbAddr, Kpcr.GCPtrCurrentPrcb);
829 LogRel(("DigWinNt/KPCR[%u]: KPCR=%RGv KPCRB=%RGv\n", idCpu, pKpcrAddr->FlatPtr, pKpcrbAddr->FlatPtr));
830 }
831 else
832 LogRel(("DigWinNt/KPCR[%u]: KPCR validation error GDT=(%RGv vs %RGv) KPCR=(%RGv vs %RGv)\n", idCpu,
833 Kpcr.GCPtrGdt, GCPtrTmp, Kpcr.GCPtrSelf, pKpcrAddr->FlatPtr));
834 }
835 else
836 LogRel(("DigWinNt/KPCR[%u]: Reading KPCR start at %RGv failed with %Rrc\n", idCpu, pKpcrAddr->FlatPtr, rc));
837 }
838 else
839 LogRel(("DigWinNt/KPCR[%u]: Getting GDT base register failed with %Rrc\n", idCpu, rc));
840 }
841 else
842 LogRel(("DigWinNt/KPCR[%u]: Getting GS base register failed with %Rrc\n", idCpu, rc));
843 }
844 }
845
846 if (RT_FAILURE(rc))
847 {
848 LogRel(("DigWinNt/KPCR: Failed to detmine KPCR and KPCRB rc=%Rrc\n", rc));
849 RTMemFree(pThis->paKpcrAddr);
850 pThis->paKpcrAddr = NULL;
851 pThis->paKpcrbAddr = NULL;
852 }
853 }
854 else
855 LogRel(("DigWinNt/KPCR: Failed to allocate %u entries for the KPCR/KPCRB addresses\n", cCpus * 2));
856}
857
858
859/**
860 * Checks if the given headers are for the BootMgr image loaded very early
861 * during the BIOS boot process.
862 */
863static bool dbgDiggerWinNtIsBootMgr(PUVM pUVM, PCVMMR3VTABLE pVMM, PCDBGFADDRESS pAddr,
864 uint8_t const *pbHdrs, size_t cbHdrs, uint32_t *pcbImage)
865{
866 if (pcbImage)
867 *pcbImage = 0;
868
869 /*
870 * Check and skip the DOS header.
871 */
872 AssertReturn(sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS32) < cbHdrs, false);
873 IMAGE_DOS_HEADER const * const pMzHdr = (IMAGE_DOS_HEADER const *)pbHdrs;
874 if ( pMzHdr->e_magic != IMAGE_DOS_SIGNATURE
875 || pMzHdr->e_lfanew < 0x40
876 || pMzHdr->e_lfanew > 0x400)
877 return false;
878
879 /*
880 * Check the NT headers.
881 */
882 AssertReturn(pMzHdr->e_lfanew + sizeof(IMAGE_NT_HEADERS32) <= cbHdrs, false);
883 IMAGE_NT_HEADERS32 const * const pHdrs = (PCIMAGE_NT_HEADERS32)&pbHdrs[pMzHdr->e_lfanew];
884 if (pHdrs->Signature != IMAGE_NT_SIGNATURE)
885 return false;
886 /* Not so many sections here, typically 3: */
887 if ( pHdrs->FileHeader.NumberOfSections <= 1
888 || pHdrs->FileHeader.NumberOfSections >= 6)
889 return false;
890 if ( pHdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_I386
891 || pHdrs->FileHeader.SizeOfOptionalHeader != sizeof(pHdrs->OptionalHeader))
892 return false;
893 if (pHdrs->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC)
894 return false;
895 if (pHdrs->OptionalHeader.NumberOfRvaAndSizes != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
896 return false;
897 /* Special subsystem with version 1.0: */
898 /** @todo which subsystem type does pre win11 use? (this is win11/amd64) */
899 if (pHdrs->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
900 return false;
901 if ( pHdrs->OptionalHeader.MajorSubsystemVersion != 1
902 || pHdrs->OptionalHeader.MinorSubsystemVersion != 0)
903 return false;
904 /* Image OS version and windows version value are all zero: */
905 if ( pHdrs->OptionalHeader.MajorOperatingSystemVersion != 0
906 || pHdrs->OptionalHeader.MinorOperatingSystemVersion != 0
907 || pHdrs->OptionalHeader.Win32VersionValue != 0)
908 return false;
909 /* DLL image with 32-bit machine code: */
910 if ( (pHdrs->FileHeader.Characteristics & (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL | IMAGE_FILE_32BIT_MACHINE))
911 != (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL | IMAGE_FILE_32BIT_MACHINE))
912 return false;
913 /* No imports: */
914 if ( pHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size != 0
915 || pHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size != 0)
916 return false;
917 /* Has a bunch of exports ('BootLib.dll'), for win11/amd64 it's 2630 bytes: */
918 IMAGE_DATA_DIRECTORY const ExpRvaSize = pHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; /* cache for later */
919 if (ExpRvaSize.Size < 1024)
920 return false;
921 if ((uint64_t)ExpRvaSize.VirtualAddress + ExpRvaSize.Size > pHdrs->OptionalHeader.SizeOfImage)
922 return false;
923 /* Check the image size is reasonable (win11/amd64 is 872448): */
924 /** @todo adjust for older windows versions... */
925 if ( pHdrs->OptionalHeader.SizeOfImage < _64K
926 || pHdrs->OptionalHeader.SizeOfImage > _4M)
927 return false;
928 /* The image base is 0x00400000 (relocations typically stripped): */
929 if (pHdrs->OptionalHeader.ImageBase != UINT32_C(0x00400000))
930 return false;
931
932 /*
933 * Final check is that export directory name is 'BootLib.dll'.
934 */
935 static char const s_szBootLibDll[] = "BootLib.dll";
936 DBGFADDRESS Addr2;
937 IMAGE_EXPORT_DIRECTORY ExpDir;
938 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0,
939 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr2, pAddr->FlatPtr + ExpRvaSize.VirtualAddress),
940 &ExpDir, sizeof(ExpDir));
941 if (RT_FAILURE(rc))
942 return false;
943 /* There ought to be a few named exports here (win11 has 94): */
944 if (ExpDir.NumberOfNames < 48 || ExpDir.NumberOfNames > _4K)
945 return false;
946
947 if ( ExpDir.Name < pMzHdr->e_lfanew + pHdrs->OptionalHeader.SizeOfHeaders
948 || (uint64_t)ExpDir.Name + sizeof(s_szBootLibDll) > pHdrs->OptionalHeader.SizeOfImage)
949 return false;
950
951 char szActualName[sizeof(s_szBootLibDll)];
952 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr2, pAddr->FlatPtr + ExpDir.Name),
953 &szActualName, sizeof(szActualName));
954 if (RT_FAILURE(rc))
955 return false;
956 if (szActualName[sizeof(s_szBootLibDll) - 1] != '\0')
957 return false;
958 if (RTStrICmpAscii(szActualName, s_szBootLibDll) != 0)
959 return false;
960
961 if (pcbImage)
962 *pcbImage = pHdrs->OptionalHeader.SizeOfImage;
963 return true;
964}
965
966
967/**
968 * Checks if the given headers are for the WinLoad.exe/efi image loaded very
969 * early during the BIOS or EFI boot process.
970 */
971static bool dbgDiggerWinNtIsWinLoad(PUVM pUVM, PCVMMR3VTABLE pVMM, PCDBGFADDRESS pAddr, uint8_t const *pbHdrs, size_t cbHdrs,
972 bool *pf32Bit, uint32_t *pcbImage, uint32_t *puNtMajorVersion, uint32_t *puNtMinorVersion)
973{
974 if (pf32Bit)
975 *pf32Bit = false;
976 if (pcbImage)
977 *pcbImage = 0;
978 if (puNtMajorVersion)
979 *puNtMajorVersion = 0;
980 if (puNtMinorVersion)
981 *puNtMinorVersion = 0;
982
983 /*
984 * Check and skip the DOS header.
985 */
986 AssertReturn(sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS64) < cbHdrs, false);
987 IMAGE_DOS_HEADER const * const pMzHdr = (IMAGE_DOS_HEADER const *)pbHdrs;
988 if ( pMzHdr->e_magic != IMAGE_DOS_SIGNATURE
989 || pMzHdr->e_lfanew < 0x40
990 || pMzHdr->e_lfanew > 0x400)
991 return false;
992
993 /*
994 * Check the NT headers.
995 *
996 * ASSUMES that the 64-bit and 32-bit optional headers match up to
997 * SizeOfStackReserve, if you exclude ImageBase and BaseOfData.
998 */
999 AssertReturn(pMzHdr->e_lfanew + sizeof(IMAGE_NT_HEADERS64) <= cbHdrs, false);
1000 IMAGE_NT_HEADERS32 const * const pHdrs32 = (PCIMAGE_NT_HEADERS32)&pbHdrs[pMzHdr->e_lfanew];
1001 IMAGE_NT_HEADERS64 const * const pHdrs64 = (PCIMAGE_NT_HEADERS64)&pbHdrs[pMzHdr->e_lfanew];
1002 if (pHdrs32->Signature != IMAGE_NT_SIGNATURE)
1003 return false;
1004 /* There are a few extra sections here, but not too many: */
1005 if ( pHdrs32->FileHeader.NumberOfSections <= 4
1006 || pHdrs32->FileHeader.NumberOfSections >= 15)
1007 return false;
1008 bool f32Bit;
1009 if (pHdrs32->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
1010 f32Bit = true;
1011 else if (pHdrs32->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
1012 f32Bit = false;
1013 else
1014 return false;
1015 if (pHdrs32->FileHeader.SizeOfOptionalHeader != (f32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64)))
1016 return false;
1017 if (pHdrs32->OptionalHeader.Magic != (f32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
1018 return false;
1019 /* Special subsystem with version 6.0 or later: */
1020 /** @todo which subsystem type does pre win11 use? (this is win11/amd64) */
1021 if (pHdrs32->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
1022 return false;
1023 if (pHdrs32->OptionalHeader.MajorSubsystemVersion < 6)
1024 return false;
1025 /* Image OS version and windows version value are all zero: */
1026 if ( pHdrs32->OptionalHeader.MajorOperatingSystemVersion != 0
1027 || pHdrs32->OptionalHeader.MinorOperatingSystemVersion != 0
1028 || pHdrs32->OptionalHeader.Win32VersionValue != 0)
1029 return false;
1030 /* DLL image: */
1031 if ( (pHdrs32->FileHeader.Characteristics & (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL))
1032 != (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL))
1033 return false;
1034 /* Check the image size is reasonable (win11/amd64 is 1769472 for BIOS and ~2MB for EFI): */
1035 /** @todo adjust for older windows versions... */
1036 if ( pHdrs32->OptionalHeader.SizeOfImage < _1M
1037 || pHdrs32->OptionalHeader.SizeOfImage > _8M)
1038 return false;
1039
1040 /* The rest of the fields differs in placement between 32-bit and 64-bit. */
1041
1042 if ( (f32Bit ? pHdrs32->OptionalHeader.NumberOfRvaAndSizes : pHdrs64->OptionalHeader.NumberOfRvaAndSizes)
1043 != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
1044 return false;
1045
1046 /* No imports: */
1047 IMAGE_DATA_DIRECTORY const * const paDataDirs = f32Bit
1048 ? pHdrs32->OptionalHeader.DataDirectory : pHdrs64->OptionalHeader.DataDirectory;
1049 if ( paDataDirs[IMAGE_DIRECTORY_ENTRY_IMPORT].Size != 0
1050 || paDataDirs[IMAGE_DIRECTORY_ENTRY_IAT].Size != 0)
1051 return false;
1052 /* Has a bunch of exports ('winload.sys'), for win11/amd64 it's 11734 bytes: */
1053 IMAGE_DATA_DIRECTORY const ExpRvaSize = paDataDirs[IMAGE_DIRECTORY_ENTRY_EXPORT]; /* cache for later */
1054 if (ExpRvaSize.Size < 1024)
1055 return false;
1056 if ((uint64_t)ExpRvaSize.VirtualAddress + ExpRvaSize.Size > pHdrs32->OptionalHeader.SizeOfImage)
1057 return false;
1058
1059 /*
1060 * Final check is that export directory name is 'winload.sys'.
1061 */
1062 static char const s_szWinLoadSys[] = "winload.sys";
1063 DBGFADDRESS Addr2;
1064 IMAGE_EXPORT_DIRECTORY ExpDir;
1065 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0,
1066 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr2, pAddr->FlatPtr + ExpRvaSize.VirtualAddress),
1067 &ExpDir, sizeof(ExpDir));
1068 if (RT_FAILURE(rc))
1069 return false;
1070 /* There ought to be a few named exports here (win11 has 373): */
1071 if (ExpDir.NumberOfNames < 128 || ExpDir.NumberOfNames > _4K)
1072 return false;
1073
1074 if ( ExpDir.Name < pMzHdr->e_lfanew + pHdrs32->OptionalHeader.SizeOfHeaders
1075 || (uint64_t)ExpDir.Name + sizeof(s_szWinLoadSys) > pHdrs32->OptionalHeader.SizeOfImage)
1076 return false;
1077
1078 char szActualName[sizeof(s_szWinLoadSys)];
1079 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr2, pAddr->FlatPtr + ExpDir.Name),
1080 &szActualName, sizeof(szActualName));
1081 if (RT_FAILURE(rc))
1082 return false;
1083 if (szActualName[sizeof(s_szWinLoadSys) - 1] != '\0')
1084 return false;
1085 if (RTStrICmpAscii(szActualName, s_szWinLoadSys) != 0)
1086 return false;
1087
1088 if (pf32Bit)
1089 *pf32Bit = f32Bit;
1090 if (pcbImage)
1091 *pcbImage = pHdrs32->OptionalHeader.SizeOfImage;
1092 /* Note! We could get more accurate version info from the resource section if we wanted to... */
1093 if (puNtMajorVersion)
1094 *puNtMajorVersion = pHdrs32->OptionalHeader.MajorSubsystemVersion;
1095 if (puNtMinorVersion)
1096 *puNtMinorVersion = pHdrs32->OptionalHeader.MinorSubsystemVersion;
1097
1098 return true;
1099}
1100
1101
1102/**
1103 * Process a PE image found in guest memory.
1104 *
1105 * @param pThis The instance data.
1106 * @param pUVM The user mode VM handle.
1107 * @param pVMM The VMM function table.
1108 * @param pszName The module name.
1109 * @param pszFilename The image filename.
1110 * @param pImageAddr The image address.
1111 * @param cbImage The size of the image.
1112 * @param enmArch Module architecture override. Default is determined
1113 * by DBGDIGGERWINNT::f32Bit.
1114 */
1115static void dbgDiggerWinNtProcessImage(PDBGDIGGERWINNT pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, const char *pszName,
1116 const char *pszFilename, PCDBGFADDRESS pImageAddr, uint32_t cbImage,
1117 RTLDRARCH enmArch = RTLDRARCH_WHATEVER)
1118{
1119 LogFlow(("DigWinNt: %RGp %#x %s\n", pImageAddr->FlatPtr, cbImage, pszName));
1120
1121 /*
1122 * Do some basic validation first.
1123 */
1124 if ( (cbImage < sizeof(IMAGE_NT_HEADERS64) && !pThis->fNt31)
1125 || cbImage >= _1M * 256)
1126 {
1127 Log(("DigWinNt: %s: Bad image size: %#x\n", pszName, cbImage));
1128 return;
1129 }
1130
1131 /*
1132 * Use the common in-memory module reader to create a debug module.
1133 */
1134 if (enmArch == RTLDRARCH_WHATEVER)
1135 enmArch = pThis->f32Bit ? RTLDRARCH_X86_32 : RTLDRARCH_AMD64;
1136 RTERRINFOSTATIC ErrInfo;
1137 RTDBGMOD hDbgMod = NIL_RTDBGMOD;
1138 int rc = pVMM->pfnDBGFR3ModInMem(pUVM, pImageAddr, pThis->fNt31 ? DBGFMODINMEM_F_PE_NT31 : 0, pszName, pszFilename,
1139 enmArch, cbImage, &hDbgMod, RTErrInfoInitStatic(&ErrInfo));
1140 if (RT_SUCCESS(rc))
1141 {
1142 /*
1143 * Tag the module.
1144 */
1145 rc = RTDbgModSetTag(hDbgMod, DIG_WINNT_MOD_TAG);
1146 AssertRC(rc);
1147
1148 /*
1149 * Link the module.
1150 */
1151 RTDBGAS hAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
1152 if (hAs != NIL_RTDBGAS)
1153 rc = RTDbgAsModuleLink(hAs, hDbgMod, pImageAddr->FlatPtr, RTDBGASLINK_FLAGS_REPLACE /*fFlags*/);
1154 else
1155 rc = VERR_INTERNAL_ERROR;
1156 if (RT_FAILURE(rc))
1157 LogRel(("DigWinNt: %s: Linking module into address space failed with %Rrc\n", pszName, rc));
1158 RTDbgModRelease(hDbgMod);
1159 RTDbgAsRelease(hAs);
1160 }
1161 else if (RTErrInfoIsSet(&ErrInfo.Core))
1162 Log(("DigWinNt: %s: DBGFR3ModInMem failed: %Rrc - %s\n", pszName, rc, ErrInfo.Core.pszMsg));
1163 else
1164 Log(("DigWinNt: %s: DBGFR3ModInMem failed: %Rrc\n", pszName, rc));
1165}
1166
1167
1168/**
1169 * Generate a debugger compatible module name from a filename.
1170 *
1171 * @returns Pointer to module name (doesn't need to be pszName).
1172 * @param pszFilename The source filename.
1173 * @param pszName Buffer to put the module name in.
1174 * @param cbName Buffer size.
1175 */
1176static const char *dbgDiggerWintNtFilenameToModuleName(const char *pszFilename, char *pszName, size_t cbName)
1177{
1178 /* Skip to the filename part of the filename. :-) */
1179 pszFilename = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);
1180
1181 /* We try use 'nt' for the kernel. */
1182 if ( RTStrICmpAscii(pszFilename, "ntoskrnl.exe") == 0
1183 || RTStrICmpAscii(pszFilename, "ntkrnlmp.exe") == 0)
1184 return "nt";
1185
1186
1187 /* Drop the extension if .dll or .sys. */
1188 size_t cchFilename = strlen(pszFilename);
1189 if ( cchFilename > 4
1190 && pszFilename[cchFilename - 4] == '.')
1191 {
1192 if ( RTStrICmpAscii(&pszFilename[cchFilename - 4], ".sys") == 0
1193 || RTStrICmpAscii(&pszFilename[cchFilename - 4], ".dll") == 0)
1194 cchFilename -= 4;
1195 }
1196
1197 /* Copy and do replacements. */
1198 if (cchFilename >= cbName)
1199 cchFilename = cbName - 1;
1200 size_t off;
1201 for (off = 0; off < cchFilename; off++)
1202 {
1203 char ch = pszFilename[off];
1204 if (!RT_C_IS_ALNUM(ch))
1205 ch = '_';
1206 pszName[off] = ch;
1207 }
1208 pszName[off] = '\0';
1209 return pszName;
1210}
1211
1212
1213/**
1214 * @interface_method_impl{DBGFOSIWINNT,pfnQueryVersion}
1215 */
1216static DECLCALLBACK(int) dbgDiggerWinNtIWinNt_QueryVersion(struct DBGFOSIWINNT *pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
1217 uint32_t *puVersMajor, uint32_t *puVersMinor,
1218 uint32_t *puBuildNumber, bool *pf32Bit)
1219{
1220 PDBGDIGGERWINNT pData = RT_FROM_MEMBER(pThis, DBGDIGGERWINNT, IWinNt);
1221 RT_NOREF(pUVM, pVMM);
1222
1223 if (puVersMajor)
1224 *puVersMajor = pData->NtMajorVersion;
1225 if (puVersMinor)
1226 *puVersMinor = pData->NtMinorVersion;
1227 if (puBuildNumber)
1228 *puBuildNumber = pData->NtBuildNumber;
1229 if (pf32Bit)
1230 *pf32Bit = pData->f32Bit;
1231 return VINF_SUCCESS;
1232}
1233
1234
1235/**
1236 * @interface_method_impl{DBGFOSIWINNT,pfnQueryKernelPtrs}
1237 */
1238static DECLCALLBACK(int) dbgDiggerWinNtIWinNt_QueryKernelPtrs(struct DBGFOSIWINNT *pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
1239 PRTGCUINTPTR pGCPtrKernBase, PRTGCUINTPTR pGCPtrPsLoadedModuleList)
1240{
1241 PDBGDIGGERWINNT pData = RT_FROM_MEMBER(pThis, DBGDIGGERWINNT, IWinNt);
1242 RT_NOREF(pUVM, pVMM);
1243
1244 *pGCPtrKernBase = pData->KernelAddr.FlatPtr;
1245 *pGCPtrPsLoadedModuleList = pData->PsLoadedModuleListAddr.FlatPtr;
1246 return VINF_SUCCESS;
1247}
1248
1249
1250/**
1251 * @interface_method_impl{DBGFOSIWINNT,pfnQueryKpcrForVCpu}
1252 */
1253static DECLCALLBACK(int) dbgDiggerWinNtIWinNt_QueryKpcrForVCpu(struct DBGFOSIWINNT *pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
1254 VMCPUID idCpu, PRTGCUINTPTR pKpcr, PRTGCUINTPTR pKpcrb)
1255{
1256 PDBGDIGGERWINNT pData = RT_FROM_MEMBER(pThis, DBGDIGGERWINNT, IWinNt);
1257
1258 if (!pData->paKpcrAddr)
1259 return VERR_NOT_SUPPORTED;
1260
1261 AssertReturn(idCpu < pVMM->pfnDBGFR3CpuGetCount(pUVM), VERR_INVALID_CPU_ID);
1262
1263 if (pKpcr)
1264 *pKpcr = pData->paKpcrAddr[idCpu].FlatPtr;
1265 if (pKpcrb)
1266 *pKpcrb = pData->paKpcrbAddr[idCpu].FlatPtr;
1267 return VINF_SUCCESS;
1268}
1269
1270
1271/**
1272 * @interface_method_impl{DBGFOSIWINNT,pfnQueryCurThrdForVCpu}
1273 */
1274static DECLCALLBACK(int) dbgDiggerWinNtIWinNt_QueryCurThrdForVCpu(struct DBGFOSIWINNT *pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
1275 VMCPUID idCpu, PRTGCUINTPTR pCurThrd)
1276{
1277 PDBGDIGGERWINNT pData = RT_FROM_MEMBER(pThis, DBGDIGGERWINNT, IWinNt);
1278
1279 if (!pData->paKpcrAddr)
1280 return VERR_NOT_SUPPORTED;
1281
1282 AssertReturn(idCpu < pVMM->pfnDBGFR3CpuGetCount(pUVM), VERR_INVALID_CPU_ID);
1283
1284 DBGFADDRESS AddrCurThrdPtr = pData->paKpcrbAddr[idCpu];
1285 pVMM->pfnDBGFR3AddrAdd(&AddrCurThrdPtr, 0x08); /** @todo Make this prettier. */
1286 return pVMM->pfnDBGFR3MemRead(pUVM, idCpu, &AddrCurThrdPtr, pCurThrd, sizeof(*pCurThrd));
1287}
1288
1289
1290/**
1291 * @copydoc DBGFOSREG::pfnStackUnwindAssist
1292 */
1293static DECLCALLBACK(int) dbgDiggerWinNtStackUnwindAssist(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, VMCPUID idCpu,
1294 PDBGFSTACKFRAME pFrame, PRTDBGUNWINDSTATE pState, PCCPUMCTX pInitialCtx,
1295 RTDBGAS hAs, uint64_t *puScratch)
1296{
1297 Assert(pInitialCtx);
1298
1299 /*
1300 * We want to locate trap frames here. The trap frame structure contains
1301 * the 64-bit IRET frame, so given unwind information it's easy to identify
1302 * using the return type and frame address.
1303 */
1304 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_64BIT)
1305 {
1306 /*
1307 * Is this a trap frame? If so, try read the trap frame.
1308 */
1309 if ( pFrame->enmReturnType == RTDBGRETURNTYPE_IRET64
1310 && !(pFrame->AddrFrame.FlatPtr & 0x7)
1311 && WINNT64_VALID_ADDRESS(pFrame->AddrFrame.FlatPtr) )
1312 {
1313 KTRAP_FRAME_AMD64 TrapFrame;
1314 RT_ZERO(TrapFrame);
1315 uint64_t const uTrapFrameAddr = pFrame->AddrFrame.FlatPtr
1316 - RT_UOFFSETOF(KTRAP_FRAME_AMD64, ErrCdOrXcptFrameOrS);
1317 int rc = pState->pfnReadStack(pState, uTrapFrameAddr, sizeof(TrapFrame), &TrapFrame);
1318 if (RT_SUCCESS(rc))
1319 {
1320 /* Valid? Not too much else we can check here (EFlags isn't
1321 reliable in manually construct frames). */
1322 if (TrapFrame.ExceptionActive <= 2)
1323 {
1324 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_TRAP_FRAME;
1325
1326 /*
1327 * Add sure 'register' information from the frame to the frame.
1328 *
1329 * To avoid code duplication, we do this in two steps in a loop.
1330 * The first iteration only figures out how many registers we're
1331 * going to save and allocates room for them. The second iteration
1332 * does the actual adding.
1333 */
1334 uint32_t cRegs = pFrame->cSureRegs;
1335 PDBGFREGVALEX paSureRegs = NULL;
1336#define ADD_REG_NAMED(a_Type, a_ValMemb, a_Value, a_pszName) do { \
1337 if (paSureRegs) \
1338 { \
1339 paSureRegs[iReg].pszName = a_pszName;\
1340 paSureRegs[iReg].enmReg = DBGFREG_END; \
1341 paSureRegs[iReg].enmType = a_Type; \
1342 paSureRegs[iReg].Value.a_ValMemb = (a_Value); \
1343 } \
1344 iReg++; \
1345 } while (0)
1346#define MAYBE_ADD_GREG(a_Value, a_enmReg, a_idxReg) do { \
1347 if (!(pState->u.x86.Loaded.s.fRegs & RT_BIT(a_idxReg))) \
1348 { \
1349 if (paSureRegs) \
1350 { \
1351 pState->u.x86.Loaded.s.fRegs |= RT_BIT(a_idxReg); \
1352 pState->u.x86.auRegs[a_idxReg] = (a_Value); \
1353 paSureRegs[iReg].Value.u64 = (a_Value); \
1354 paSureRegs[iReg].enmReg = a_enmReg; \
1355 paSureRegs[iReg].enmType = DBGFREGVALTYPE_U64; \
1356 paSureRegs[iReg].pszName = NULL; \
1357 } \
1358 iReg++; \
1359 } \
1360 } while (0)
1361 for (unsigned iLoop = 0; iLoop < 2; iLoop++)
1362 {
1363 uint32_t iReg = pFrame->cSureRegs;
1364 ADD_REG_NAMED(DBGFREGVALTYPE_U64, u64, uTrapFrameAddr, "TrapFrame");
1365 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, TrapFrame.ExceptionActive, "ExceptionActive");
1366 if (TrapFrame.ExceptionActive == 0)
1367 {
1368 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, TrapFrame.PreviousIrql, "PrevIrql");
1369 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, (uint8_t)TrapFrame.ErrCdOrXcptFrameOrS, "IntNo");
1370 }
1371 else if ( TrapFrame.ExceptionActive == 1
1372 && TrapFrame.FaultIndicator == ((TrapFrame.ErrCdOrXcptFrameOrS >> 1) & 0x9))
1373 ADD_REG_NAMED(DBGFREGVALTYPE_U64, u64, TrapFrame.FaultAddrOrCtxRecOrTS, "cr2-probably");
1374 if (TrapFrame.SegCs & X86_SEL_RPL)
1375 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, 1, "UserMode");
1376 else
1377 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, 1, "KernelMode");
1378 if (TrapFrame.ExceptionActive <= 1)
1379 {
1380 MAYBE_ADD_GREG(TrapFrame.Rax, DBGFREG_RAX, X86_GREG_xAX);
1381 MAYBE_ADD_GREG(TrapFrame.Rcx, DBGFREG_RCX, X86_GREG_xCX);
1382 MAYBE_ADD_GREG(TrapFrame.Rdx, DBGFREG_RDX, X86_GREG_xDX);
1383 MAYBE_ADD_GREG(TrapFrame.R8, DBGFREG_R8, X86_GREG_x8);
1384 MAYBE_ADD_GREG(TrapFrame.R9, DBGFREG_R9, X86_GREG_x9);
1385 MAYBE_ADD_GREG(TrapFrame.R10, DBGFREG_R10, X86_GREG_x10);
1386 MAYBE_ADD_GREG(TrapFrame.R11, DBGFREG_R11, X86_GREG_x11);
1387 }
1388 else if (TrapFrame.ExceptionActive == 2)
1389 {
1390 MAYBE_ADD_GREG(TrapFrame.Rbx, DBGFREG_RBX, X86_GREG_xBX);
1391 MAYBE_ADD_GREG(TrapFrame.Rsi, DBGFREG_RSI, X86_GREG_xSI);
1392 MAYBE_ADD_GREG(TrapFrame.Rdi, DBGFREG_RDI, X86_GREG_xDI);
1393 }
1394 // MAYBE_ADD_GREG(TrapFrame.Rbp, DBGFREG_RBP, X86_GREG_xBP); - KiInterrupt[Sub]Dispatch* may leave this invalid.
1395
1396 /* Done? */
1397 if (iLoop > 0)
1398 {
1399 Assert(cRegs == iReg);
1400 break;
1401 }
1402
1403 /* Resize the array, zeroing the extension. */
1404 if (pFrame->cSureRegs)
1405 paSureRegs = (PDBGFREGVALEX)pVMM->pfnMMR3HeapRealloc(pFrame->paSureRegs, iReg * sizeof(paSureRegs[0]));
1406 else
1407 paSureRegs = (PDBGFREGVALEX)pVMM->pfnMMR3HeapAllocU(pUVM, MM_TAG_DBGF_STACK, iReg * sizeof(paSureRegs[0]));
1408 AssertReturn(paSureRegs, VERR_NO_MEMORY);
1409
1410 pFrame->paSureRegs = paSureRegs;
1411 RT_BZERO(&paSureRegs[pFrame->cSureRegs], (iReg - pFrame->cSureRegs) * sizeof(paSureRegs[0]));
1412 cRegs = iReg;
1413 }
1414#undef ADD_REG_NAMED
1415#undef MAYBE_ADD_GREG
1416
1417 /* Commit the register update. */
1418 pFrame->cSureRegs = cRegs;
1419 }
1420 }
1421 }
1422 }
1423
1424 RT_NOREF(pUVM, pVMM, pvData, idCpu, hAs, pInitialCtx, puScratch);
1425 return VINF_SUCCESS;
1426}
1427
1428
1429/**
1430 * @copydoc DBGFOSREG::pfnQueryInterface
1431 */
1432static DECLCALLBACK(void *) dbgDiggerWinNtQueryInterface(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, DBGFOSINTERFACE enmIf)
1433{
1434 RT_NOREF(pUVM, pVMM);
1435 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
1436
1437 switch (enmIf)
1438 {
1439 case DBGFOSINTERFACE_WINNT:
1440 return &pThis->IWinNt;
1441 default:
1442 return NULL;
1443 }
1444}
1445
1446
1447/**
1448 * @copydoc DBGFOSREG::pfnQueryVersion
1449 */
1450static DECLCALLBACK(int) dbgDiggerWinNtQueryVersion(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData,
1451 char *pszVersion, size_t cchVersion)
1452{
1453 RT_NOREF(pUVM, pVMM);
1454 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
1455 Assert(pThis->fValid);
1456
1457 const char *pszNtProductType;
1458 switch (pThis->NtProductType)
1459 {
1460 case kNtProductType_WinNt: pszNtProductType = "-WinNT"; break;
1461 case kNtProductType_LanManNt: pszNtProductType = "-LanManNT"; break;
1462 case kNtProductType_Server: pszNtProductType = "-Server"; break;
1463 default: pszNtProductType = ""; break;
1464 }
1465
1466 RTStrPrintf(pszVersion, cchVersion, "%u.%u-%s%s (BuildNumber %u)", pThis->NtMajorVersion, pThis->NtMinorVersion,
1467 pThis->f32Bit ? "x86" : "AMD64", pszNtProductType, pThis->NtBuildNumber);
1468 return VINF_SUCCESS;
1469}
1470
1471
1472/**
1473 * @copydoc DBGFOSREG::pfnTerm
1474 */
1475static DECLCALLBACK(void) dbgDiggerWinNtTerm(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1476{
1477 RT_NOREF1(pUVM);
1478 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
1479 Assert(pThis->fValid);
1480
1481#ifdef VBOX_DEBUGGER_WITH_WIN_DBG_PRINT_HOOKING
1482 if (pThis->hBpDbgPrint != NIL_DBGFBP)
1483 {
1484 int rc = DBGFR3BpClear(pUVM, pThis->hBpDbgPrint);
1485 AssertRC(rc);
1486 pThis->hBpDbgPrint = NIL_DBGFBP;
1487 }
1488
1489 if (pThis->hBpOwnerDbgPrint != NIL_DBGFBPOWNER)
1490 {
1491 int rc = DBGFR3BpOwnerDestroy(pUVM, pThis->hBpOwnerDbgPrint);
1492 AssertRC(rc);
1493 pThis->hBpOwnerDbgPrint = NIL_DBGFBPOWNER;
1494 }
1495#endif
1496
1497 /*
1498 * As long as we're using our private LDR reader implementation,
1499 * we must unlink and ditch the modules we created.
1500 */
1501 RTDBGAS hDbgAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
1502 if (hDbgAs != NIL_RTDBGAS)
1503 {
1504 uint32_t iMod = RTDbgAsModuleCount(hDbgAs);
1505 while (iMod-- > 0)
1506 {
1507 RTDBGMOD hMod = RTDbgAsModuleByIndex(hDbgAs, iMod);
1508 if (hMod != NIL_RTDBGMOD)
1509 {
1510 if (RTDbgModGetTag(hMod) == DIG_WINNT_MOD_TAG)
1511 {
1512 int rc = RTDbgAsModuleUnlink(hDbgAs, hMod);
1513 AssertRC(rc);
1514 }
1515 RTDbgModRelease(hMod);
1516 }
1517 }
1518 RTDbgAsRelease(hDbgAs);
1519 }
1520
1521 if (pThis->paKpcrAddr)
1522 RTMemFree(pThis->paKpcrAddr);
1523 /* pThis->paKpcrbAddr comes from the same allocation as pThis->paKpcrAddr. */
1524
1525 pThis->paKpcrAddr = NULL;
1526 pThis->paKpcrbAddr = NULL;
1527
1528 pThis->fValid = false;
1529}
1530
1531
1532/**
1533 * @copydoc DBGFOSREG::pfnRefresh
1534 */
1535static DECLCALLBACK(int) dbgDiggerWinNtRefresh(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1536{
1537 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
1538 NOREF(pThis);
1539 Assert(pThis->fValid);
1540
1541 /*
1542 * For now we'll flush and reload everything.
1543 */
1544 dbgDiggerWinNtTerm(pUVM, pVMM, pvData);
1545
1546 return dbgDiggerWinNtInit(pUVM, pVMM, pvData);
1547}
1548
1549
1550/**
1551 * @copydoc DBGFOSREG::pfnInit
1552 */
1553static DECLCALLBACK(int) dbgDiggerWinNtInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1554{
1555 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
1556 Assert(!pThis->fValid);
1557
1558 union
1559 {
1560 uint8_t au8[GUEST_PAGE_SIZE * 2];
1561 RTUTF16 wsz[GUEST_PAGE_SIZE];
1562 NTKUSERSHAREDDATA UserSharedData;
1563 } u;
1564 DBGFADDRESS Addr;
1565 int rc;
1566
1567 /*
1568 * Load the bootmgr module if it was detected and is still present.
1569 * The boot manager is always 32-bit on x86.
1570 */
1571 if (DBGFADDRESS_IS_VALID(&pThis->BootMgrAddr))
1572 {
1573 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->BootMgrAddr, &u, sizeof(u));
1574 if (RT_SUCCESS(rc))
1575 {
1576 uint32_t cbImage = 0;
1577 if (dbgDiggerWinNtIsBootMgr(pUVM, pVMM, &pThis->BootMgrAddr, &u.au8[0], sizeof(u), &cbImage))
1578 dbgDiggerWinNtProcessImage(pThis, pUVM, pVMM, "BootMgr", "BootMgr.exe",
1579 &pThis->BootMgrAddr, cbImage, RTLDRARCH_X86_32);
1580 }
1581 }
1582
1583 /*
1584 * Load the winload module if it was detected and still present.
1585 */
1586 if (DBGFADDRESS_IS_VALID(&pThis->WinLoadAddr))
1587 {
1588 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &pThis->WinLoadAddr, &u, sizeof(u));
1589 if (RT_SUCCESS(rc))
1590 {
1591 uint32_t cbImage = 0;
1592 uint32_t uNtMajorVersion = 0;
1593 uint32_t uNtMinorVersion = 0;
1594 if (dbgDiggerWinNtIsWinLoad(pUVM, pVMM, &pThis->WinLoadAddr, &u.au8[0], sizeof(u),
1595 NULL /*pf32Bit*/, &cbImage, &uNtMajorVersion, &uNtMinorVersion))
1596 {
1597 dbgDiggerWinNtProcessImage(pThis, pUVM, pVMM, "WinLoad", "WinLoad.exe", &pThis->WinLoadAddr, cbImage);
1598 pThis->NtMajorVersion = uNtMajorVersion;
1599 pThis->NtMinorVersion = uNtMinorVersion;
1600 }
1601 }
1602 }
1603
1604 /*
1605 * Try figure the NT version.
1606 */
1607 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, pThis->f32Bit ? NTKUSERSHAREDDATA_WINNT32 : NTKUSERSHAREDDATA_WINNT64);
1608 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &u, GUEST_PAGE_SIZE);
1609 if (RT_SUCCESS(rc))
1610 {
1611 pThis->NtProductType = u.UserSharedData.ProductTypeIsValid && u.UserSharedData.NtProductType <= kNtProductType_Server
1612 ? (NTPRODUCTTYPE)u.UserSharedData.NtProductType
1613 : kNtProductType_Invalid;
1614 pThis->NtMajorVersion = u.UserSharedData.NtMajorVersion;
1615 pThis->NtMinorVersion = u.UserSharedData.NtMinorVersion;
1616 pThis->NtBuildNumber = u.UserSharedData.NtBuildNumber;
1617 }
1618 else if (pThis->fNt31)
1619 {
1620 pThis->NtProductType = kNtProductType_WinNt;
1621 pThis->NtMajorVersion = 3;
1622 pThis->NtMinorVersion = 1;
1623 pThis->NtBuildNumber = 0;
1624 }
1625 else
1626 {
1627 Log(("DigWinNt: Error reading KUSER_SHARED_DATA: %Rrc\n", rc));
1628 if ( !DBGFADDRESS_IS_VALID(&pThis->KernelAddr)
1629 || !DBGFADDRESS_IS_VALID(&pThis->KernelMteAddr)
1630 || !DBGFADDRESS_IS_VALID(&pThis->PsLoadedModuleListAddr))
1631 return DBGFADDRESS_IS_VALID(&pThis->BootMgrAddr) || DBGFADDRESS_IS_VALID(&pThis->WinLoadAddr) ? VINF_SUCCESS : rc;
1632 }
1633
1634 /*
1635 * Dig out the module chain.
1636 */
1637 DBGFADDRESS AddrPrev = pThis->PsLoadedModuleListAddr;
1638 Addr = pThis->KernelMteAddr;
1639 do
1640 {
1641 /* Read the validate the MTE. */
1642 NTMTE Mte;
1643 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &Mte, pThis->f32Bit ? sizeof(Mte.vX_32) : sizeof(Mte.vX_64));
1644 if (RT_FAILURE(rc))
1645 break;
1646 if (WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Blink) != AddrPrev.FlatPtr)
1647 {
1648 Log(("DigWinNt: Bad Mte At %RGv - backpointer\n", Addr.FlatPtr));
1649 break;
1650 }
1651 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Flink)) )
1652 {
1653 Log(("DigWinNt: Bad Mte at %RGv - forward pointer\n", Addr.FlatPtr));
1654 break;
1655 }
1656 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer)))
1657 {
1658 Log(("DigWinNt: Bad Mte at %RGv - BaseDllName=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer)));
1659 break;
1660 }
1661 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, FullDllName.Buffer)))
1662 {
1663 Log(("DigWinNt: Bad Mte at %RGv - FullDllName=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, FullDllName.Buffer)));
1664 break;
1665 }
1666 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, DllBase)))
1667 {
1668 Log(("DigWinNt: Bad Mte at %RGv - DllBase=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, DllBase) ));
1669 break;
1670 }
1671
1672 uint32_t const cbImageMte = !pThis->fNt31 ? WINNT_UNION(pThis, &Mte, SizeOfImage) : 0;
1673 if ( !pThis->fNt31
1674 && ( cbImageMte > _256M
1675 || WINNT_UNION(pThis, &Mte, EntryPoint) - WINNT_UNION(pThis, &Mte, DllBase) > cbImageMte) )
1676 {
1677 Log(("DigWinNt: Bad Mte at %RGv - EntryPoint=%llx SizeOfImage=%x DllBase=%llx\n",
1678 Addr.FlatPtr, WINNT_UNION(pThis, &Mte, EntryPoint), cbImageMte, WINNT_UNION(pThis, &Mte, DllBase)));
1679 break;
1680 }
1681
1682 /* Read the full name. */
1683 DBGFADDRESS AddrName;
1684 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrName, WINNT_UNION(pThis, &Mte, FullDllName.Buffer));
1685 uint16_t cbName = WINNT_UNION(pThis, &Mte, FullDllName.Length);
1686 if (cbName < sizeof(u))
1687 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrName, &u, cbName);
1688 else
1689 rc = VERR_OUT_OF_RANGE;
1690 if (RT_FAILURE(rc))
1691 {
1692 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrName, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer));
1693 cbName = WINNT_UNION(pThis, &Mte, BaseDllName.Length);
1694 if (cbName < sizeof(u))
1695 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrName, &u, cbName);
1696 else
1697 rc = VERR_OUT_OF_RANGE;
1698 }
1699 if (RT_SUCCESS(rc))
1700 {
1701 u.wsz[cbName / 2] = '\0';
1702
1703 char *pszFilename;
1704 rc = RTUtf16ToUtf8(u.wsz, &pszFilename);
1705 if (RT_SUCCESS(rc))
1706 {
1707 char szModName[128];
1708 const char *pszModName = dbgDiggerWintNtFilenameToModuleName(pszFilename, szModName, sizeof(szModName));
1709
1710 /* Read the start of the PE image and pass it along to a worker. */
1711 DBGFADDRESS ImageAddr;
1712 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &ImageAddr, WINNT_UNION(pThis, &Mte, DllBase));
1713 dbgDiggerWinNtProcessImage(pThis, pUVM, pVMM, pszModName, pszFilename, &ImageAddr, cbImageMte);
1714 RTStrFree(pszFilename);
1715 }
1716 }
1717
1718 /* next */
1719 AddrPrev = Addr;
1720 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Flink));
1721 } while ( Addr.FlatPtr != pThis->KernelMteAddr.FlatPtr
1722 && Addr.FlatPtr != pThis->PsLoadedModuleListAddr.FlatPtr);
1723
1724 /* Try resolving the KPCR and KPCRB addresses for each vCPU. */
1725 dbgDiggerWinNtResolveKpcr(pThis, pUVM, pVMM);
1726
1727#ifdef VBOX_DEBUGGER_WITH_WIN_DBG_PRINT_HOOKING
1728 /* Try to hook into the DbgPrint/vDbgPrint... code so we can gather information from the drivers. */
1729 dbgDiggerWinNtDbgPrintHook(pThis, pUVM);
1730#endif
1731
1732 pThis->fValid = true;
1733 return VINF_SUCCESS;
1734}
1735
1736
1737/**
1738 * @copydoc DBGFOSREG::pfnProbe
1739 */
1740static DECLCALLBACK(bool) dbgDiggerWinNtProbe(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1741{
1742 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
1743 bool fRet = false;
1744 DBGFADDRESS Addr;
1745 union
1746 {
1747 uint8_t au8[8192];
1748 uint16_t au16[8192/2];
1749 uint32_t au32[8192/4];
1750 IMAGE_DOS_HEADER MzHdr;
1751 RTUTF16 wsz[8192/2];
1752 X86DESCGATE a32Gates[X86_XCPT_PF + 1];
1753 X86DESC64GATE a64Gates[X86_XCPT_PF + 1];
1754 } u;
1755
1756 union
1757 {
1758 NTMTE32 v32;
1759 NTMTE64 v64;
1760 } uMte, uMte2, uMte3;
1761
1762 /*
1763 * Reset the state (paranoia)
1764 */
1765 RT_ZERO(pThis->BootMgrAddr);
1766 RT_ZERO(pThis->WinLoadAddr);
1767 RT_ZERO(pThis->KernelAddr);
1768 RT_ZERO(pThis->KernelMteAddr);
1769 RT_ZERO(pThis->PsLoadedModuleListAddr);
1770 pThis->f32Bit = false;
1771 pThis->fNt31 = false;
1772
1773 /*
1774 * Look for the BootMgr/BootLib.dll module at 0x400000.
1775 * This is only relevant when booting via BIOS.
1776 */
1777 if (1/** @todo BIOS + x86/amd64 only */)
1778 {
1779 int rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1780 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, UINT32_C(0x400000)),
1781 &u.au8[0], sizeof(u));
1782 if (RT_SUCCESS(rc) && dbgDiggerWinNtIsBootMgr(pUVM, pVMM, &Addr, &u.au8[0], sizeof(u), NULL))
1783 {
1784 pThis->BootMgrAddr = Addr;
1785 fRet = true;
1786 }
1787 }
1788
1789 /*
1790 * Look for the winload.exe/winload.sys module that's, from the looks of it,
1791 * responsible for loading the kernel and boot drivers.
1792 */
1793 if (DBGFADDRESS_IS_VALID(&pThis->BootMgrAddr))
1794 {
1795 static const char s_szNeedle[] = "PAGER32C"; /* Section name. No terminator. */
1796 uint32_t const uEndAddr = _16M + _1M;
1797 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, pThis->BootMgrAddr.FlatPtr + _64K);
1798 do
1799 {
1800 int rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &Addr, uEndAddr - Addr.FlatPtr,
1801 8 /*uAlign*/, s_szNeedle, sizeof(s_szNeedle) - 1, &Addr);
1802 if (RT_FAILURE(rc))
1803 break;
1804
1805 DBGFADDRESS AddrMz;
1806 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1807 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrMz,
1808 Addr.FlatPtr & ~(RTGCUINTPTR)GUEST_PAGE_OFFSET_MASK),
1809 &u.au8[0], sizeof(u));
1810 bool f32Bit = false;
1811 if (RT_SUCCESS(rc) && dbgDiggerWinNtIsWinLoad(pUVM, pVMM, &AddrMz, &u.au8[0], sizeof(u), &f32Bit, NULL, NULL, NULL))
1812 {
1813 pThis->WinLoadAddr = AddrMz;
1814 pThis->f32Bit = f32Bit;
1815 fRet = true;
1816 break;
1817 }
1818 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, AddrMz.FlatPtr + GUEST_PAGE_SIZE);
1819 } while (Addr.FlatPtr + _64K < uEndAddr);
1820 }
1821
1822 /*
1823 * NT only runs in protected or long mode.
1824 */
1825 CPUMMODE const enmMode = pVMM->pfnDBGFR3CpuGetMode(pUVM, 0 /*idCpu*/);
1826 if (enmMode != CPUMMODE_PROTECTED && enmMode != CPUMMODE_LONG)
1827 return fRet;
1828 bool const f64Bit = enmMode == CPUMMODE_LONG
1829 || (DBGFADDRESS_IS_VALID(&pThis->WinLoadAddr) && !pThis->f32Bit);
1830 uint64_t const uStart = f64Bit ? UINT64_C(0xffff800000000000) : UINT32_C(0x80001000);
1831 uint64_t const uEnd = f64Bit ? UINT64_C(0xffffffffffff0000) : UINT32_C(0xffff0000);
1832
1833 /*
1834 * To approximately locate the kernel we examine the IDTR handlers.
1835 *
1836 * The exception/trap/fault handlers are all in NT kernel image, we pick
1837 * KiPageFault here.
1838 */
1839 uint64_t uIdtrBase = 0;
1840 uint16_t uIdtrLimit = 0;
1841 int rc = pVMM->pfnDBGFR3RegCpuQueryXdtr(pUVM, 0, DBGFREG_IDTR, &uIdtrBase, &uIdtrLimit);
1842 AssertRCReturn(rc, fRet);
1843
1844 const uint16_t cbMinIdtr = (X86_XCPT_PF + 1) * (f64Bit ? sizeof(X86DESC64GATE) : sizeof(X86DESCGATE));
1845 if (uIdtrLimit < cbMinIdtr)
1846 return fRet;
1847
1848 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, uIdtrBase), &u, cbMinIdtr);
1849 if (RT_FAILURE(rc))
1850 return fRet;
1851
1852 uint64_t uKrnlStart = uStart;
1853 uint64_t uKrnlEnd = uEnd;
1854 if (f64Bit)
1855 {
1856 uint64_t uHandler = u.a64Gates[X86_XCPT_PF].u16OffsetLow
1857 | ((uint32_t)u.a64Gates[X86_XCPT_PF].u16OffsetHigh << 16)
1858 | ((uint64_t)u.a64Gates[X86_XCPT_PF].u32OffsetTop << 32);
1859 if (uHandler >= uStart && uHandler <= uEnd)
1860 {
1861 uKrnlStart = (uHandler & ~(uint64_t)_4M) - _512M;
1862 uKrnlEnd = (uHandler + (uint64_t)_4M) & ~(uint64_t)_4M;
1863 }
1864 else if (!DBGFADDRESS_IS_VALID(&pThis->WinLoadAddr))
1865 return fRet;
1866 }
1867 else
1868 {
1869 uint32_t uHandler = RT_MAKE_U32(u.a32Gates[X86_XCPT_PF].u16OffsetLow, u.a32Gates[X86_XCPT_PF].u16OffsetHigh);
1870 if (uHandler >= uStart && uHandler <= uEnd)
1871 {
1872 uKrnlStart = (uHandler & ~(uint64_t)_4M) - _64M;
1873 uKrnlEnd = (uHandler + (uint64_t)_4M) & ~(uint64_t)_4M;
1874 }
1875 else if (!DBGFADDRESS_IS_VALID(&pThis->WinLoadAddr))
1876 return fRet;
1877 }
1878
1879 /*
1880 * Look for the PAGELK section name that seems to be a part of all kernels.
1881 * Then try find the module table entry for it. Since it's the first entry
1882 * in the PsLoadedModuleList we can easily validate the list head and report
1883 * success.
1884 *
1885 * Note! We ASSUME the section name is 8 byte aligned.
1886 */
1887 DBGFADDRESS KernelAddr;
1888 for (pVMM->pfnDBGFR3AddrFromFlat(pUVM, &KernelAddr, uKrnlStart);
1889 KernelAddr.FlatPtr < uKrnlEnd;
1890 KernelAddr.FlatPtr += GUEST_PAGE_SIZE)
1891 {
1892 bool fNt31 = false;
1893 DBGFADDRESS const RetryAddress = KernelAddr;
1894 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, uEnd - KernelAddr.FlatPtr,
1895 8, "PAGELK\0", sizeof("PAGELK\0"), &KernelAddr);
1896 if ( rc == VERR_DBGF_MEM_NOT_FOUND
1897 && !f64Bit)
1898 {
1899 /* NT3.1 didn't have a PAGELK section, so look for _TEXT instead. The
1900 following VirtualSize is zero, so check for that too. */
1901 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &RetryAddress, uEnd - RetryAddress.FlatPtr,
1902 8, "_TEXT\0\0\0\0\0\0", sizeof("_TEXT\0\0\0\0\0\0"), &KernelAddr);
1903 fNt31 = true;
1904 }
1905 if (RT_FAILURE(rc))
1906 break;
1907 pVMM->pfnDBGFR3AddrSub(&KernelAddr, KernelAddr.FlatPtr & GUEST_PAGE_OFFSET_MASK);
1908
1909 /* MZ + PE header. */
1910 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &KernelAddr, &u, sizeof(u));
1911 if ( RT_SUCCESS(rc)
1912 && u.MzHdr.e_magic == IMAGE_DOS_SIGNATURE
1913 && !(u.MzHdr.e_lfanew & 0x7)
1914 && u.MzHdr.e_lfanew >= 0x080
1915 && u.MzHdr.e_lfanew <= 0x400) /* W8 is at 0x288*/
1916 {
1917 if (!f64Bit)
1918 {
1919 IMAGE_NT_HEADERS32 const *pHdrs = (IMAGE_NT_HEADERS32 const *)&u.au8[u.MzHdr.e_lfanew];
1920 if ( pHdrs->Signature == IMAGE_NT_SIGNATURE
1921 && pHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_I386
1922 && pHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pHdrs->OptionalHeader)
1923 && pHdrs->FileHeader.NumberOfSections >= 10 /* the kernel has lots */
1924 && (pHdrs->FileHeader.Characteristics & (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL)) == IMAGE_FILE_EXECUTABLE_IMAGE
1925 && pHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
1926 && pHdrs->OptionalHeader.NumberOfRvaAndSizes == IMAGE_NUMBEROF_DIRECTORY_ENTRIES
1927 )
1928 {
1929 /* Find the MTE. */
1930 RT_ZERO(uMte);
1931 uMte.v32.DllBase = KernelAddr.FlatPtr;
1932 uMte.v32.EntryPoint = KernelAddr.FlatPtr + pHdrs->OptionalHeader.AddressOfEntryPoint;
1933 uMte.v32.SizeOfImage = !fNt31 ? pHdrs->OptionalHeader.SizeOfImage : 0; /* NT 3.1 didn't set the size. */
1934 DBGFADDRESS HitAddr;
1935 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, uEnd - KernelAddr.FlatPtr,
1936 4 /*align*/, &uMte.v32.DllBase, 3 * sizeof(uint32_t), &HitAddr);
1937 while (RT_SUCCESS(rc))
1938 {
1939 /* check the name. */
1940 DBGFADDRESS MteAddr = HitAddr;
1941 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1942 pVMM->pfnDBGFR3AddrSub(&MteAddr, RT_OFFSETOF(NTMTE32, DllBase)),
1943 &uMte2.v32, sizeof(uMte2.v32));
1944 if ( RT_SUCCESS(rc)
1945 && uMte2.v32.DllBase == uMte.v32.DllBase
1946 && uMte2.v32.EntryPoint == uMte.v32.EntryPoint
1947 && uMte2.v32.SizeOfImage == uMte.v32.SizeOfImage
1948 && WINNT32_VALID_ADDRESS(uMte2.v32.InLoadOrderLinks.Flink)
1949 && WINNT32_VALID_ADDRESS(uMte2.v32.BaseDllName.Buffer)
1950 && WINNT32_VALID_ADDRESS(uMte2.v32.FullDllName.Buffer)
1951 && uMte2.v32.BaseDllName.Length <= 128
1952 && uMte2.v32.FullDllName.Length <= 260
1953 )
1954 {
1955 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1956 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, uMte2.v32.BaseDllName.Buffer),
1957 u.wsz, uMte2.v32.BaseDllName.Length);
1958 u.wsz[uMte2.v32.BaseDllName.Length / 2] = '\0';
1959 if ( RT_SUCCESS(rc)
1960 && ( !RTUtf16ICmp(u.wsz, g_wszKernelNames[0])
1961 /* || !RTUtf16ICmp(u.wsz, g_wszKernelNames[1]) */
1962 )
1963 )
1964 {
1965 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1966 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr,
1967 uMte2.v32.InLoadOrderLinks.Blink),
1968 &uMte3.v32, RT_SIZEOFMEMB(NTMTE32, InLoadOrderLinks));
1969 if ( RT_SUCCESS(rc)
1970 && uMte3.v32.InLoadOrderLinks.Flink == MteAddr.FlatPtr
1971 && WINNT32_VALID_ADDRESS(uMte3.v32.InLoadOrderLinks.Blink) )
1972 {
1973 Log(("DigWinNt: MteAddr=%RGv KernelAddr=%RGv SizeOfImage=%x &PsLoadedModuleList=%RGv (32-bit)\n",
1974 MteAddr.FlatPtr, KernelAddr.FlatPtr, uMte2.v32.SizeOfImage, Addr.FlatPtr));
1975 pThis->KernelAddr = KernelAddr;
1976 pThis->KernelMteAddr = MteAddr;
1977 pThis->PsLoadedModuleListAddr = Addr;
1978 pThis->f32Bit = true;
1979 pThis->fNt31 = fNt31;
1980 return true;
1981 }
1982 }
1983 else if (RT_SUCCESS(rc))
1984 {
1985 Log2(("DigWinNt: Wrong module: MteAddr=%RGv ImageAddr=%RGv SizeOfImage=%#x '%ls'\n",
1986 MteAddr.FlatPtr, KernelAddr.FlatPtr, uMte2.v32.SizeOfImage, u.wsz));
1987 break; /* Not NT kernel */
1988 }
1989 }
1990
1991 /* next */
1992 pVMM->pfnDBGFR3AddrAdd(&HitAddr, 4);
1993 if (HitAddr.FlatPtr < uEnd)
1994 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, uEnd - HitAddr.FlatPtr,
1995 4 /*align*/, &uMte.v32.DllBase, 3 * sizeof(uint32_t), &HitAddr);
1996 else
1997 rc = VERR_DBGF_MEM_NOT_FOUND;
1998 }
1999 }
2000 }
2001 else
2002 {
2003 IMAGE_NT_HEADERS64 const *pHdrs = (IMAGE_NT_HEADERS64 const *)&u.au8[u.MzHdr.e_lfanew];
2004 if ( pHdrs->Signature == IMAGE_NT_SIGNATURE
2005 && pHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64
2006 && pHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pHdrs->OptionalHeader)
2007 && pHdrs->FileHeader.NumberOfSections >= 10 /* the kernel has lots */
2008 && (pHdrs->FileHeader.Characteristics & (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL))
2009 == IMAGE_FILE_EXECUTABLE_IMAGE
2010 && pHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC
2011 && pHdrs->OptionalHeader.NumberOfRvaAndSizes == IMAGE_NUMBEROF_DIRECTORY_ENTRIES
2012 )
2013 {
2014 /* Find the MTE. */
2015 RT_ZERO(uMte.v64);
2016 uMte.v64.DllBase = KernelAddr.FlatPtr;
2017 uMte.v64.EntryPoint = KernelAddr.FlatPtr + pHdrs->OptionalHeader.AddressOfEntryPoint;
2018 uMte.v64.SizeOfImage = pHdrs->OptionalHeader.SizeOfImage;
2019 DBGFADDRESS ScanAddr;
2020 DBGFADDRESS HitAddr;
2021 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &ScanAddr, uStart),
2022 uEnd - uStart, 8 /*align*/, &uMte.v64.DllBase, 5 * sizeof(uint32_t), &HitAddr);
2023 while (RT_SUCCESS(rc))
2024 {
2025 /* Read the start of the MTE and check some basic members. */
2026 DBGFADDRESS MteAddr = HitAddr;
2027 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
2028 pVMM->pfnDBGFR3AddrSub(&MteAddr, RT_OFFSETOF(NTMTE64, DllBase)),
2029 &uMte2.v64, sizeof(uMte2.v64));
2030 if ( RT_SUCCESS(rc)
2031 && uMte2.v64.DllBase == uMte.v64.DllBase
2032 && uMte2.v64.EntryPoint == uMte.v64.EntryPoint
2033 && uMte2.v64.SizeOfImage == uMte.v64.SizeOfImage
2034 && WINNT64_VALID_ADDRESS(uMte2.v64.InLoadOrderLinks.Flink)
2035 && WINNT64_VALID_ADDRESS(uMte2.v64.BaseDllName.Buffer)
2036 && WINNT64_VALID_ADDRESS(uMte2.v64.FullDllName.Buffer)
2037 && uMte2.v64.BaseDllName.Length <= 128
2038 && uMte2.v64.FullDllName.Length <= 260
2039 )
2040 {
2041 /* Try read the base name and compare with known NT kernel names. */
2042 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
2043 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, uMte2.v64.BaseDllName.Buffer),
2044 u.wsz, uMte2.v64.BaseDllName.Length);
2045 u.wsz[uMte2.v64.BaseDllName.Length / 2] = '\0';
2046 if ( RT_SUCCESS(rc)
2047 && ( !RTUtf16ICmp(u.wsz, g_wszKernelNames[0])
2048 /* || !RTUtf16ICmp(u.wsz, g_wszKernelNames[1]) */
2049 )
2050 )
2051 {
2052 /* Read the link entry of the previous entry in the list and check that its
2053 forward pointer points at the MTE we've found. */
2054 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
2055 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr,
2056 uMte2.v64.InLoadOrderLinks.Blink),
2057 &uMte3.v64, RT_SIZEOFMEMB(NTMTE64, InLoadOrderLinks));
2058 if ( RT_SUCCESS(rc)
2059 && uMte3.v64.InLoadOrderLinks.Flink == MteAddr.FlatPtr
2060 && WINNT64_VALID_ADDRESS(uMte3.v64.InLoadOrderLinks.Blink) )
2061 {
2062 Log(("DigWinNt: MteAddr=%RGv KernelAddr=%RGv SizeOfImage=%x &PsLoadedModuleList=%RGv (32-bit)\n",
2063 MteAddr.FlatPtr, KernelAddr.FlatPtr, uMte2.v64.SizeOfImage, Addr.FlatPtr));
2064 pThis->KernelAddr = KernelAddr;
2065 pThis->KernelMteAddr = MteAddr;
2066 pThis->PsLoadedModuleListAddr = Addr;
2067 pThis->f32Bit = false;
2068 pThis->fNt31 = false;
2069 return true;
2070 }
2071 }
2072 else if (RT_SUCCESS(rc))
2073 {
2074 Log2(("DigWinNt: Wrong module: MteAddr=%RGv ImageAddr=%RGv SizeOfImage=%#x '%ls'\n",
2075 MteAddr.FlatPtr, KernelAddr.FlatPtr, uMte2.v64.SizeOfImage, u.wsz));
2076 break; /* Not NT kernel */
2077 }
2078 }
2079
2080 /* next */
2081 pVMM->pfnDBGFR3AddrAdd(&HitAddr, 8);
2082 if (HitAddr.FlatPtr < uEnd)
2083 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, uEnd - HitAddr.FlatPtr,
2084 8 /*align*/, &uMte.v64.DllBase, 3 * sizeof(uint32_t), &HitAddr);
2085 else
2086 rc = VERR_DBGF_MEM_NOT_FOUND;
2087 }
2088 }
2089 }
2090 }
2091 }
2092
2093 return fRet;
2094}
2095
2096
2097/**
2098 * @copydoc DBGFOSREG::pfnDestruct
2099 */
2100static DECLCALLBACK(void) dbgDiggerWinNtDestruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
2101{
2102 RT_NOREF(pUVM, pVMM, pvData);
2103}
2104
2105
2106/**
2107 * @copydoc DBGFOSREG::pfnConstruct
2108 */
2109static DECLCALLBACK(int) dbgDiggerWinNtConstruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
2110{
2111 RT_NOREF(pUVM, pVMM);
2112 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
2113 pThis->fValid = false;
2114 pThis->f32Bit = false;
2115 pThis->enmVer = DBGDIGGERWINNTVER_UNKNOWN;
2116
2117 pThis->IWinNt.u32Magic = DBGFOSIWINNT_MAGIC;
2118 pThis->IWinNt.pfnQueryVersion = dbgDiggerWinNtIWinNt_QueryVersion;
2119 pThis->IWinNt.pfnQueryKernelPtrs = dbgDiggerWinNtIWinNt_QueryKernelPtrs;
2120 pThis->IWinNt.pfnQueryKpcrForVCpu = dbgDiggerWinNtIWinNt_QueryKpcrForVCpu;
2121 pThis->IWinNt.pfnQueryCurThrdForVCpu = dbgDiggerWinNtIWinNt_QueryCurThrdForVCpu;
2122 pThis->IWinNt.u32EndMagic = DBGFOSIWINNT_MAGIC;
2123
2124#ifdef VBOX_DEBUGGER_WITH_WIN_DBG_PRINT_HOOKING
2125 pThis->hBpDbgPrint = NIL_DBGFBP;
2126 pThis->hBpOwnerDbgPrint = NIL_DBGFBPOWNER;
2127#endif
2128
2129 return VINF_SUCCESS;
2130}
2131
2132
2133const DBGFOSREG g_DBGDiggerWinNt =
2134{
2135 /* .u32Magic = */ DBGFOSREG_MAGIC,
2136 /* .fFlags = */ 0,
2137 /* .cbData = */ sizeof(DBGDIGGERWINNT),
2138 /* .szName = */ "WinNT",
2139 /* .pfnConstruct = */ dbgDiggerWinNtConstruct,
2140 /* .pfnDestruct = */ dbgDiggerWinNtDestruct,
2141 /* .pfnProbe = */ dbgDiggerWinNtProbe,
2142 /* .pfnInit = */ dbgDiggerWinNtInit,
2143 /* .pfnRefresh = */ dbgDiggerWinNtRefresh,
2144 /* .pfnTerm = */ dbgDiggerWinNtTerm,
2145 /* .pfnQueryVersion = */ dbgDiggerWinNtQueryVersion,
2146 /* .pfnQueryInterface = */ dbgDiggerWinNtQueryInterface,
2147 /* .pfnStackUnwindAssist = */ dbgDiggerWinNtStackUnwindAssist,
2148 /* .u32EndMagic = */ DBGFOSREG_MAGIC
2149};
2150
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette