VirtualBox

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

Last change on this file since 107044 was 106379, checked in by vboxsync, 6 weeks ago

Debugger/DBGPlugInWinNt.cpp: Make it detect and extract symbols from Windows/ARM64 guests, bugref:10393

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