VirtualBox

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

Last change on this file since 96860 was 96407, checked in by vboxsync, 22 months ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 61.0 KB
Line 
1/* $Id: DBGPlugInWinNt.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * DBGPlugInWindows - Debugger and Guest OS Digger Plugin For Windows NT.
4 */
5
6/*
7 * Copyright (C) 2009-2022 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#include <VBox/vmm/vmmr3vtable.h>
38#include <VBox/err.h>
39#include <VBox/param.h>
40#include <iprt/ctype.h>
41#include <iprt/ldr.h>
42#include <iprt/mem.h>
43#include <iprt/path.h>
44#include <iprt/stream.h>
45#include <iprt/string.h>
46#include <iprt/utf16.h>
47#include <iprt/formats/pecoff.h>
48#include <iprt/formats/mz.h>
49#include <iprt/nt/nt-structures.h>
50
51
52/*********************************************************************************************************************************
53* Structures and Typedefs *
54*********************************************************************************************************************************/
55
56/** @name Internal WinNT structures
57 * @{ */
58/**
59 * PsLoadedModuleList entry for 32-bit NT aka LDR_DATA_TABLE_ENTRY.
60 * Tested with XP.
61 */
62typedef struct NTMTE32
63{
64 struct
65 {
66 uint32_t Flink;
67 uint32_t Blink;
68 } InLoadOrderLinks,
69 InMemoryOrderModuleList,
70 InInitializationOrderModuleList;
71 uint32_t DllBase;
72 uint32_t EntryPoint;
73 /** @note This field is not a size in NT 3.1. It's NULL for images loaded by the
74 * boot loader, for other images it looks like some kind of pointer. */
75 uint32_t SizeOfImage;
76 struct
77 {
78 uint16_t Length;
79 uint16_t MaximumLength;
80 uint32_t Buffer;
81 } FullDllName,
82 BaseDllName;
83 uint32_t Flags;
84 uint16_t LoadCount;
85 uint16_t TlsIndex;
86 /* ... there is more ... */
87} NTMTE32;
88typedef NTMTE32 *PNTMTE32;
89
90/**
91 * PsLoadedModuleList entry for 64-bit NT aka LDR_DATA_TABLE_ENTRY.
92 */
93typedef struct NTMTE64
94{
95 struct
96 {
97 uint64_t Flink;
98 uint64_t Blink;
99 } InLoadOrderLinks, /**< 0x00 */
100 InMemoryOrderModuleList, /**< 0x10 */
101 InInitializationOrderModuleList; /**< 0x20 */
102 uint64_t DllBase; /**< 0x30 */
103 uint64_t EntryPoint; /**< 0x38 */
104 uint32_t SizeOfImage; /**< 0x40 */
105 uint32_t Alignment; /**< 0x44 */
106 struct
107 {
108 uint16_t Length; /**< 0x48,0x58 */
109 uint16_t MaximumLength; /**< 0x4a,0x5a */
110 uint32_t Alignment; /**< 0x4c,0x5c */
111 uint64_t Buffer; /**< 0x50,0x60 */
112 } FullDllName, /**< 0x48 */
113 BaseDllName; /**< 0x58 */
114 uint32_t Flags; /**< 0x68 */
115 uint16_t LoadCount; /**< 0x6c */
116 uint16_t TlsIndex; /**< 0x6e */
117 /* ... there is more ... */
118} NTMTE64;
119typedef NTMTE64 *PNTMTE64;
120
121/** MTE union. */
122typedef union NTMTE
123{
124 NTMTE32 vX_32;
125 NTMTE64 vX_64;
126} NTMTE;
127typedef NTMTE *PNTMTE;
128
129
130/**
131 * The essential bits of the KUSER_SHARED_DATA structure.
132 */
133typedef struct NTKUSERSHAREDDATA
134{
135 uint32_t TickCountLowDeprecated;
136 uint32_t TickCountMultiplier;
137 struct
138 {
139 uint32_t LowPart;
140 int32_t High1Time;
141 int32_t High2Time;
142
143 } InterruptTime,
144 SystemTime,
145 TimeZoneBias;
146 uint16_t ImageNumberLow;
147 uint16_t ImageNumberHigh;
148 RTUTF16 NtSystemRoot[260];
149 uint32_t MaxStackTraceDepth;
150 uint32_t CryptoExponent;
151 uint32_t TimeZoneId;
152 uint32_t LargePageMinimum;
153 uint32_t Reserved2[6];
154 uint32_t NtBuildNumber;
155 uint32_t NtProductType;
156 uint8_t ProductTypeIsValid;
157 uint8_t abPadding[3];
158 uint32_t NtMajorVersion;
159 uint32_t NtMinorVersion;
160 /* uint8_t ProcessorFeatures[64];
161 ...
162 */
163} NTKUSERSHAREDDATA;
164typedef NTKUSERSHAREDDATA *PNTKUSERSHAREDDATA;
165
166/** KI_USER_SHARED_DATA for i386 */
167#define NTKUSERSHAREDDATA_WINNT32 UINT32_C(0xffdf0000)
168/** KI_USER_SHARED_DATA for AMD64 */
169#define NTKUSERSHAREDDATA_WINNT64 UINT64_C(0xfffff78000000000)
170
171/** NTKUSERSHAREDDATA::NtProductType */
172typedef enum NTPRODUCTTYPE
173{
174 kNtProductType_Invalid = 0,
175 kNtProductType_WinNt = 1,
176 kNtProductType_LanManNt,
177 kNtProductType_Server
178} NTPRODUCTTYPE;
179
180
181/** NT image header union. */
182typedef union NTHDRSU
183{
184 IMAGE_NT_HEADERS32 vX_32;
185 IMAGE_NT_HEADERS64 vX_64;
186} NTHDRS;
187/** Pointer to NT image header union. */
188typedef NTHDRS *PNTHDRS;
189/** Pointer to const NT image header union. */
190typedef NTHDRS const *PCNTHDRS;
191
192
193/**
194 * NT KD version block.
195 */
196typedef struct NTKDVERSIONBLOCK
197{
198 uint16_t MajorVersion;
199 uint16_t MinorVersion;
200 uint8_t ProtocolVersion;
201 uint8_t KdSecondaryVersion;
202 uint16_t Flags;
203 uint16_t MachineType;
204 uint8_t MaxPacketType;
205 uint8_t MaxStateChange;
206 uint8_t MaxManipulate;
207 uint8_t Simulation;
208 uint16_t Unused;
209 uint64_t KernBase;
210 uint64_t PsLoadedModuleList;
211 uint64_t DebuggerDataList;
212} NTKDVERSIONBLOCK;
213/** Pointer to an NT KD version block. */
214typedef NTKDVERSIONBLOCK *PNTKDVERSIONBLOCK;
215/** Pointer to a const NT KD version block. */
216typedef const NTKDVERSIONBLOCK *PCNTKDVERSIONBLOCK;
217
218/** @} */
219
220
221
222typedef enum DBGDIGGERWINNTVER
223{
224 DBGDIGGERWINNTVER_UNKNOWN,
225 DBGDIGGERWINNTVER_3_1,
226 DBGDIGGERWINNTVER_3_5,
227 DBGDIGGERWINNTVER_4_0,
228 DBGDIGGERWINNTVER_5_0,
229 DBGDIGGERWINNTVER_5_1,
230 DBGDIGGERWINNTVER_6_0
231} DBGDIGGERWINNTVER;
232
233/**
234 * WinNT guest OS digger instance data.
235 */
236typedef struct DBGDIGGERWINNT
237{
238 /** Whether the information is valid or not.
239 * (For fending off illegal interface method calls.) */
240 bool fValid;
241 /** 32-bit (true) or 64-bit (false) */
242 bool f32Bit;
243 /** Set if NT 3.1 was detected.
244 * This implies both Misc.VirtualSize and NTMTE32::SizeOfImage are zero. */
245 bool fNt31;
246
247 /** The NT version. */
248 DBGDIGGERWINNTVER enmVer;
249 /** NTKUSERSHAREDDATA::NtProductType */
250 NTPRODUCTTYPE NtProductType;
251 /** NTKUSERSHAREDDATA::NtMajorVersion */
252 uint32_t NtMajorVersion;
253 /** NTKUSERSHAREDDATA::NtMinorVersion */
254 uint32_t NtMinorVersion;
255 /** NTKUSERSHAREDDATA::NtBuildNumber */
256 uint32_t NtBuildNumber;
257
258 /** The address of the ntoskrnl.exe image. */
259 DBGFADDRESS KernelAddr;
260 /** The address of the ntoskrnl.exe module table entry. */
261 DBGFADDRESS KernelMteAddr;
262 /** The address of PsLoadedModuleList. */
263 DBGFADDRESS PsLoadedModuleListAddr;
264
265 /** Array of detected KPCR addresses for each vCPU. */
266 PDBGFADDRESS paKpcrAddr;
267 /** Array of detected KPCRB addresses for each vCPU. */
268 PDBGFADDRESS paKpcrbAddr;
269
270 /** The Windows NT specifics interface. */
271 DBGFOSIWINNT IWinNt;
272} DBGDIGGERWINNT;
273/** Pointer to the linux guest OS digger instance data. */
274typedef DBGDIGGERWINNT *PDBGDIGGERWINNT;
275
276
277/**
278 * The WinNT digger's loader reader instance data.
279 */
280typedef struct DBGDIGGERWINNTRDR
281{
282 /** The VM handle (referenced). */
283 PUVM pUVM;
284 /** The image base. */
285 DBGFADDRESS ImageAddr;
286 /** The image size. */
287 uint32_t cbImage;
288 /** The file offset of the SizeOfImage field in the optional header if it
289 * needs patching, otherwise set to UINT32_MAX. */
290 uint32_t offSizeOfImage;
291 /** The correct image size. */
292 uint32_t cbCorrectImageSize;
293 /** Number of entries in the aMappings table. */
294 uint32_t cMappings;
295 /** Mapping hint. */
296 uint32_t iHint;
297 /** Mapping file offset to memory offsets, ordered by file offset. */
298 struct
299 {
300 /** The file offset. */
301 uint32_t offFile;
302 /** The size of this mapping. */
303 uint32_t cbMem;
304 /** The offset to the memory from the start of the image. */
305 uint32_t offMem;
306 } aMappings[1];
307} DBGDIGGERWINNTRDR;
308/** Pointer a WinNT loader reader instance data. */
309typedef DBGDIGGERWINNTRDR *PDBGDIGGERWINNTRDR;
310
311
312/*********************************************************************************************************************************
313* Defined Constants And Macros *
314*********************************************************************************************************************************/
315/** Validates a 32-bit Windows NT kernel address */
316#define WINNT32_VALID_ADDRESS(Addr) ((Addr) > UINT32_C(0x80000000) && (Addr) < UINT32_C(0xfffff000))
317/** Validates a 64-bit Windows NT kernel address */
318#define WINNT64_VALID_ADDRESS(Addr) ((Addr) > UINT64_C(0xffff800000000000) && (Addr) < UINT64_C(0xfffffffffffff000))
319/** Validates a kernel address. */
320#define WINNT_VALID_ADDRESS(pThis, Addr) ((pThis)->f32Bit ? WINNT32_VALID_ADDRESS(Addr) : WINNT64_VALID_ADDRESS(Addr))
321/** Versioned and bitness wrapper. */
322#define WINNT_UNION(pThis, pUnion, Member) ((pThis)->f32Bit ? (pUnion)->vX_32. Member : (pUnion)->vX_64. Member )
323
324/** The length (in chars) of the kernel file name (no path). */
325#define WINNT_KERNEL_BASE_NAME_LEN 12
326
327/** WindowsNT on little endian ASCII systems. */
328#define DIG_WINNT_MOD_TAG UINT64_C(0x54696e646f774e54)
329
330
331/*********************************************************************************************************************************
332* Internal Functions *
333*********************************************************************************************************************************/
334static DECLCALLBACK(int) dbgDiggerWinNtInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData);
335
336
337/*********************************************************************************************************************************
338* Global Variables *
339*********************************************************************************************************************************/
340/** Kernel names. */
341static const RTUTF16 g_wszKernelNames[][WINNT_KERNEL_BASE_NAME_LEN + 1] =
342{
343 { 'n', 't', 'o', 's', 'k', 'r', 'n', 'l', '.', 'e', 'x', 'e' }
344};
345
346
347
348/**
349 * Tries to resolve the KPCR and KPCRB addresses for each vCPU.
350 *
351 * @returns nothing.
352 * @param pThis The instance data.
353 * @param pUVM The user mode VM handle.
354 * @param pVMM The VMM function table.
355 */
356static void dbgDiggerWinNtResolveKpcr(PDBGDIGGERWINNT pThis, PUVM pUVM, PCVMMR3VTABLE pVMM)
357{
358 /*
359 * Getting at the KPCR and KPCRB is explained here:
360 * https://www.geoffchappell.com/studies/windows/km/ntoskrnl/structs/kpcr.htm
361 * Together with the available offsets from:
362 * https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ksamd64.inc#L883
363 * we can verify that the found addresses are valid by cross checking that the GDTR and self reference
364 * match what we expect.
365 */
366 VMCPUID cCpus = pVMM->pfnDBGFR3CpuGetCount(pUVM);
367 pThis->paKpcrAddr = (PDBGFADDRESS)RTMemAllocZ(cCpus * 2 * sizeof(DBGFADDRESS));
368 if (RT_LIKELY(pThis->paKpcrAddr))
369 {
370 pThis->paKpcrbAddr = &pThis->paKpcrAddr[cCpus];
371
372 /* Work each CPU, unexpected values in each CPU make the whole thing fail to play safe. */
373 int rc = VINF_SUCCESS;
374 for (VMCPUID idCpu = 0; (idCpu < cCpus) && RT_SUCCESS(rc); idCpu++)
375 {
376 PDBGFADDRESS pKpcrAddr = &pThis->paKpcrAddr[idCpu];
377 PDBGFADDRESS pKpcrbAddr = &pThis->paKpcrbAddr[idCpu];
378
379 if (pThis->f32Bit)
380 {
381 /* Read FS base */
382 uint32_t GCPtrKpcrBase = 0;
383
384 rc = pVMM->pfnDBGFR3RegCpuQueryU32(pUVM, idCpu, DBGFREG_FS_BASE, &GCPtrKpcrBase);
385 if ( RT_SUCCESS(rc)
386 && WINNT32_VALID_ADDRESS(GCPtrKpcrBase))
387 {
388 /*
389 * Read the start of the KPCR (@todo Probably move this to a global header)
390 * and verify its content.
391 */
392 struct
393 {
394 uint8_t abOoi[28]; /* Out of interest */
395 uint32_t GCPtrSelf;
396 uint32_t GCPtrCurrentPrcb;
397 uint32_t u32Irql;
398 uint32_t u32Iir;
399 uint32_t u32IirActive;
400 uint32_t u32Idr;
401 uint32_t GCPtrKdVersionBlock;
402 uint32_t GCPtrIdt;
403 uint32_t GCPtrGdt;
404 uint32_t GCPtrTss;
405 } Kpcr;
406
407 LogFlow(("DigWinNt/KPCR[%u]: GS Base %RGv\n", idCpu, GCPtrKpcrBase));
408 pVMM->pfnDBGFR3AddrFromFlat(pUVM, pKpcrAddr, GCPtrKpcrBase);
409
410 rc = pVMM->pfnDBGFR3MemRead(pUVM, idCpu, pKpcrAddr, &Kpcr, sizeof(Kpcr));
411 if (RT_SUCCESS(rc))
412 {
413 uint32_t GCPtrGdt = 0;
414 uint32_t GCPtrIdt = 0;
415
416 rc = pVMM->pfnDBGFR3RegCpuQueryU32(pUVM, idCpu, DBGFREG_GDTR_BASE, &GCPtrGdt);
417 if (RT_SUCCESS(rc))
418 rc = pVMM->pfnDBGFR3RegCpuQueryU32(pUVM, idCpu, DBGFREG_IDTR_BASE, &GCPtrIdt);
419 if (RT_SUCCESS(rc))
420 {
421 if ( Kpcr.GCPtrGdt == GCPtrGdt
422 && Kpcr.GCPtrIdt == GCPtrIdt
423 && Kpcr.GCPtrSelf == pKpcrAddr->FlatPtr)
424 {
425 pVMM->pfnDBGFR3AddrFromFlat(pUVM, pKpcrbAddr, Kpcr.GCPtrCurrentPrcb);
426 LogRel(("DigWinNt/KPCR[%u]: KPCR=%RGv KPCRB=%RGv\n", idCpu, pKpcrAddr->FlatPtr, pKpcrbAddr->FlatPtr));
427
428 /*
429 * Try to extract the NT build number from the KD version block if it exists,
430 * the shared user data might have set it to 0.
431 *
432 * @todo We can use this method to get at the kern base and loaded module list if the other detection
433 * method fails (seen with Windows 10 x86).
434 * @todo On 32bit Windows the debugger data list is also always accessible this way contrary to
435 * the amd64 version where it is only available with "/debug on" set.
436 */
437 if (!pThis->NtBuildNumber)
438 {
439 NTKDVERSIONBLOCK KdVersBlock;
440 DBGFADDRESS AddrKdVersBlock;
441
442 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrKdVersBlock, Kpcr.GCPtrKdVersionBlock);
443 rc = pVMM->pfnDBGFR3MemRead(pUVM, idCpu, &AddrKdVersBlock, &KdVersBlock, sizeof(KdVersBlock));
444 if (RT_SUCCESS(rc))
445 pThis->NtBuildNumber = KdVersBlock.MinorVersion;
446 }
447 }
448 else
449 LogRel(("DigWinNt/KPCR[%u]: KPCR validation error GDT=(%RGv vs %RGv) KPCR=(%RGv vs %RGv)\n", idCpu,
450 Kpcr.GCPtrGdt, GCPtrGdt, Kpcr.GCPtrSelf, pKpcrAddr->FlatPtr));
451 }
452 else
453 LogRel(("DigWinNt/KPCR[%u]: Getting GDT or IDT base register failed with %Rrc\n", idCpu, rc));
454 }
455 }
456 else
457 LogRel(("DigWinNt/KPCR[%u]: Getting FS base register failed with %Rrc (%RGv)\n", idCpu, rc, GCPtrKpcrBase));
458 }
459 else
460 {
461 /* Read GS base which points to the base of the KPCR for each CPU. */
462 RTGCUINTPTR GCPtrTmp = 0;
463 rc = pVMM->pfnDBGFR3RegCpuQueryU64(pUVM, idCpu, DBGFREG_GS_BASE, &GCPtrTmp);
464 if ( RT_SUCCESS(rc)
465 && !WINNT64_VALID_ADDRESS(GCPtrTmp))
466 {
467 /*
468 * Could be a user address when we stopped the VM right in usermode,
469 * read the GS kernel base MSR instead.
470 */
471 rc = pVMM->pfnDBGFR3RegCpuQueryU64(pUVM, idCpu, DBGFREG_MSR_K8_KERNEL_GS_BASE, &GCPtrTmp);
472 }
473
474 if ( RT_SUCCESS(rc)
475 && WINNT64_VALID_ADDRESS(GCPtrTmp))
476 {
477 LogFlow(("DigWinNt/KPCR[%u]: GS Base %RGv\n", idCpu, GCPtrTmp));
478 pVMM->pfnDBGFR3AddrFromFlat(pUVM, pKpcrAddr, GCPtrTmp);
479
480 rc = pVMM->pfnDBGFR3RegCpuQueryU64(pUVM, idCpu, DBGFREG_GDTR_BASE, &GCPtrTmp);
481 if (RT_SUCCESS(rc))
482 {
483 /*
484 * Read the start of the KPCR (@todo Probably move this to a global header)
485 * and verify its content.
486 */
487 struct
488 {
489 RTGCUINTPTR GCPtrGdt;
490 RTGCUINTPTR GCPtrTss;
491 RTGCUINTPTR GCPtrUserRsp;
492 RTGCUINTPTR GCPtrSelf;
493 RTGCUINTPTR GCPtrCurrentPrcb;
494 } Kpcr;
495
496 rc = pVMM->pfnDBGFR3MemRead(pUVM, idCpu, pKpcrAddr, &Kpcr, sizeof(Kpcr));
497 if (RT_SUCCESS(rc))
498 {
499 if ( Kpcr.GCPtrGdt == GCPtrTmp
500 && Kpcr.GCPtrSelf == pKpcrAddr->FlatPtr
501 /** @todo && TSS */ )
502 {
503 pVMM->pfnDBGFR3AddrFromFlat(pUVM, pKpcrbAddr, Kpcr.GCPtrCurrentPrcb);
504 LogRel(("DigWinNt/KPCR[%u]: KPCR=%RGv KPCRB=%RGv\n", idCpu, pKpcrAddr->FlatPtr, pKpcrbAddr->FlatPtr));
505 }
506 else
507 LogRel(("DigWinNt/KPCR[%u]: KPCR validation error GDT=(%RGv vs %RGv) KPCR=(%RGv vs %RGv)\n", idCpu,
508 Kpcr.GCPtrGdt, GCPtrTmp, Kpcr.GCPtrSelf, pKpcrAddr->FlatPtr));
509 }
510 else
511 LogRel(("DigWinNt/KPCR[%u]: Reading KPCR start at %RGv failed with %Rrc\n", idCpu, pKpcrAddr->FlatPtr, rc));
512 }
513 else
514 LogRel(("DigWinNt/KPCR[%u]: Getting GDT base register failed with %Rrc\n", idCpu, rc));
515 }
516 else
517 LogRel(("DigWinNt/KPCR[%u]: Getting GS base register failed with %Rrc\n", idCpu, rc));
518 }
519 }
520
521 if (RT_FAILURE(rc))
522 {
523 LogRel(("DigWinNt/KPCR: Failed to detmine KPCR and KPCRB rc=%Rrc\n", rc));
524 RTMemFree(pThis->paKpcrAddr);
525 pThis->paKpcrAddr = NULL;
526 pThis->paKpcrbAddr = NULL;
527 }
528 }
529 else
530 LogRel(("DigWinNt/KPCR: Failed to allocate %u entries for the KPCR/KPCRB addresses\n", cCpus * 2));
531}
532
533
534/**
535 * Process a PE image found in guest memory.
536 *
537 * @param pThis The instance data.
538 * @param pUVM The user mode VM handle.
539 * @param pVMM The VMM function table.
540 * @param pszName The module name.
541 * @param pszFilename The image filename.
542 * @param pImageAddr The image address.
543 * @param cbImage The size of the image.
544 */
545static void dbgDiggerWinNtProcessImage(PDBGDIGGERWINNT pThis, PUVM pUVM, PCVMMR3VTABLE pVMM, const char *pszName,
546 const char *pszFilename, PCDBGFADDRESS pImageAddr, uint32_t cbImage)
547{
548 LogFlow(("DigWinNt: %RGp %#x %s\n", pImageAddr->FlatPtr, cbImage, pszName));
549
550 /*
551 * Do some basic validation first.
552 */
553 if ( (cbImage < sizeof(IMAGE_NT_HEADERS64) && !pThis->fNt31)
554 || cbImage >= _1M * 256)
555 {
556 Log(("DigWinNt: %s: Bad image size: %#x\n", pszName, cbImage));
557 return;
558 }
559
560 /*
561 * Use the common in-memory module reader to create a debug module.
562 */
563 RTERRINFOSTATIC ErrInfo;
564 RTDBGMOD hDbgMod = NIL_RTDBGMOD;
565 int rc = pVMM->pfnDBGFR3ModInMem(pUVM, pImageAddr, pThis->fNt31 ? DBGFMODINMEM_F_PE_NT31 : 0, pszName, pszFilename,
566 pThis->f32Bit ? RTLDRARCH_X86_32 : RTLDRARCH_AMD64, cbImage,
567 &hDbgMod, RTErrInfoInitStatic(&ErrInfo));
568 if (RT_SUCCESS(rc))
569 {
570 /*
571 * Tag the module.
572 */
573 rc = RTDbgModSetTag(hDbgMod, DIG_WINNT_MOD_TAG);
574 AssertRC(rc);
575
576 /*
577 * Link the module.
578 */
579 RTDBGAS hAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
580 if (hAs != NIL_RTDBGAS)
581 rc = RTDbgAsModuleLink(hAs, hDbgMod, pImageAddr->FlatPtr, RTDBGASLINK_FLAGS_REPLACE /*fFlags*/);
582 else
583 rc = VERR_INTERNAL_ERROR;
584 RTDbgModRelease(hDbgMod);
585 RTDbgAsRelease(hAs);
586 }
587 else if (RTErrInfoIsSet(&ErrInfo.Core))
588 Log(("DigWinNt: %s: DBGFR3ModInMem failed: %Rrc - %s\n", pszName, rc, ErrInfo.Core.pszMsg));
589 else
590 Log(("DigWinNt: %s: DBGFR3ModInMem failed: %Rrc\n", pszName, rc));
591}
592
593
594/**
595 * Generate a debugger compatible module name from a filename.
596 *
597 * @returns Pointer to module name (doesn't need to be pszName).
598 * @param pszFilename The source filename.
599 * @param pszName Buffer to put the module name in.
600 * @param cbName Buffer size.
601 */
602static const char *dbgDiggerWintNtFilenameToModuleName(const char *pszFilename, char *pszName, size_t cbName)
603{
604 /* Skip to the filename part of the filename. :-) */
605 pszFilename = RTPathFilenameEx(pszFilename, RTPATH_STR_F_STYLE_DOS);
606
607 /* We try use 'nt' for the kernel. */
608 if ( RTStrICmpAscii(pszFilename, "ntoskrnl.exe") == 0
609 || RTStrICmpAscii(pszFilename, "ntkrnlmp.exe") == 0)
610 return "nt";
611
612
613 /* Drop the extension if .dll or .sys. */
614 size_t cchFilename = strlen(pszFilename);
615 if ( cchFilename > 4
616 && pszFilename[cchFilename - 4] == '.')
617 {
618 if ( RTStrICmpAscii(&pszFilename[cchFilename - 4], ".sys") == 0
619 || RTStrICmpAscii(&pszFilename[cchFilename - 4], ".dll") == 0)
620 cchFilename -= 4;
621 }
622
623 /* Copy and do replacements. */
624 if (cchFilename >= cbName)
625 cchFilename = cbName - 1;
626 size_t off;
627 for (off = 0; off < cchFilename; off++)
628 {
629 char ch = pszFilename[off];
630 if (!RT_C_IS_ALNUM(ch))
631 ch = '_';
632 pszName[off] = ch;
633 }
634 pszName[off] = '\0';
635 return pszName;
636}
637
638
639/**
640 * @interface_method_impl{DBGFOSIWINNT,pfnQueryVersion}
641 */
642static DECLCALLBACK(int) dbgDiggerWinNtIWinNt_QueryVersion(struct DBGFOSIWINNT *pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
643 uint32_t *puVersMajor, uint32_t *puVersMinor,
644 uint32_t *puBuildNumber, bool *pf32Bit)
645{
646 PDBGDIGGERWINNT pData = RT_FROM_MEMBER(pThis, DBGDIGGERWINNT, IWinNt);
647 RT_NOREF(pUVM, pVMM);
648
649 if (puVersMajor)
650 *puVersMajor = pData->NtMajorVersion;
651 if (puVersMinor)
652 *puVersMinor = pData->NtMinorVersion;
653 if (puBuildNumber)
654 *puBuildNumber = pData->NtBuildNumber;
655 if (pf32Bit)
656 *pf32Bit = pData->f32Bit;
657 return VINF_SUCCESS;
658}
659
660
661/**
662 * @interface_method_impl{DBGFOSIWINNT,pfnQueryKernelPtrs}
663 */
664static DECLCALLBACK(int) dbgDiggerWinNtIWinNt_QueryKernelPtrs(struct DBGFOSIWINNT *pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
665 PRTGCUINTPTR pGCPtrKernBase, PRTGCUINTPTR pGCPtrPsLoadedModuleList)
666{
667 PDBGDIGGERWINNT pData = RT_FROM_MEMBER(pThis, DBGDIGGERWINNT, IWinNt);
668 RT_NOREF(pUVM, pVMM);
669
670 *pGCPtrKernBase = pData->KernelAddr.FlatPtr;
671 *pGCPtrPsLoadedModuleList = pData->PsLoadedModuleListAddr.FlatPtr;
672 return VINF_SUCCESS;
673}
674
675
676/**
677 * @interface_method_impl{DBGFOSIWINNT,pfnQueryKpcrForVCpu}
678 */
679static DECLCALLBACK(int) dbgDiggerWinNtIWinNt_QueryKpcrForVCpu(struct DBGFOSIWINNT *pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
680 VMCPUID idCpu, PRTGCUINTPTR pKpcr, PRTGCUINTPTR pKpcrb)
681{
682 PDBGDIGGERWINNT pData = RT_FROM_MEMBER(pThis, DBGDIGGERWINNT, IWinNt);
683
684 if (!pData->paKpcrAddr)
685 return VERR_NOT_SUPPORTED;
686
687 AssertReturn(idCpu < pVMM->pfnDBGFR3CpuGetCount(pUVM), VERR_INVALID_CPU_ID);
688
689 if (pKpcr)
690 *pKpcr = pData->paKpcrAddr[idCpu].FlatPtr;
691 if (pKpcrb)
692 *pKpcrb = pData->paKpcrbAddr[idCpu].FlatPtr;
693 return VINF_SUCCESS;
694}
695
696
697/**
698 * @interface_method_impl{DBGFOSIWINNT,pfnQueryCurThrdForVCpu}
699 */
700static DECLCALLBACK(int) dbgDiggerWinNtIWinNt_QueryCurThrdForVCpu(struct DBGFOSIWINNT *pThis, PUVM pUVM, PCVMMR3VTABLE pVMM,
701 VMCPUID idCpu, PRTGCUINTPTR pCurThrd)
702{
703 PDBGDIGGERWINNT pData = RT_FROM_MEMBER(pThis, DBGDIGGERWINNT, IWinNt);
704
705 if (!pData->paKpcrAddr)
706 return VERR_NOT_SUPPORTED;
707
708 AssertReturn(idCpu < pVMM->pfnDBGFR3CpuGetCount(pUVM), VERR_INVALID_CPU_ID);
709
710 DBGFADDRESS AddrCurThrdPtr = pData->paKpcrbAddr[idCpu];
711 pVMM->pfnDBGFR3AddrAdd(&AddrCurThrdPtr, 0x08); /** @todo Make this prettier. */
712 return pVMM->pfnDBGFR3MemRead(pUVM, idCpu, &AddrCurThrdPtr, pCurThrd, sizeof(*pCurThrd));
713}
714
715
716/**
717 * @copydoc DBGFOSREG::pfnStackUnwindAssist
718 */
719static DECLCALLBACK(int) dbgDiggerWinNtStackUnwindAssist(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, VMCPUID idCpu,
720 PDBGFSTACKFRAME pFrame, PRTDBGUNWINDSTATE pState, PCCPUMCTX pInitialCtx,
721 RTDBGAS hAs, uint64_t *puScratch)
722{
723 Assert(pInitialCtx);
724
725 /*
726 * We want to locate trap frames here. The trap frame structure contains
727 * the 64-bit IRET frame, so given unwind information it's easy to identify
728 * using the return type and frame address.
729 */
730 if (pFrame->fFlags & DBGFSTACKFRAME_FLAGS_64BIT)
731 {
732 /*
733 * Is this a trap frame? If so, try read the trap frame.
734 */
735 if ( pFrame->enmReturnType == RTDBGRETURNTYPE_IRET64
736 && !(pFrame->AddrFrame.FlatPtr & 0x7)
737 && WINNT64_VALID_ADDRESS(pFrame->AddrFrame.FlatPtr) )
738 {
739 KTRAP_FRAME_AMD64 TrapFrame;
740 RT_ZERO(TrapFrame);
741 uint64_t const uTrapFrameAddr = pFrame->AddrFrame.FlatPtr
742 - RT_UOFFSETOF(KTRAP_FRAME_AMD64, ErrCdOrXcptFrameOrS);
743 int rc = pState->pfnReadStack(pState, uTrapFrameAddr, sizeof(TrapFrame), &TrapFrame);
744 if (RT_SUCCESS(rc))
745 {
746 /* Valid? Not too much else we can check here (EFlags isn't
747 reliable in manually construct frames). */
748 if (TrapFrame.ExceptionActive <= 2)
749 {
750 pFrame->fFlags |= DBGFSTACKFRAME_FLAGS_TRAP_FRAME;
751
752 /*
753 * Add sure 'register' information from the frame to the frame.
754 *
755 * To avoid code duplication, we do this in two steps in a loop.
756 * The first iteration only figures out how many registers we're
757 * going to save and allocates room for them. The second iteration
758 * does the actual adding.
759 */
760 uint32_t cRegs = pFrame->cSureRegs;
761 PDBGFREGVALEX paSureRegs = NULL;
762#define ADD_REG_NAMED(a_Type, a_ValMemb, a_Value, a_pszName) do { \
763 if (paSureRegs) \
764 { \
765 paSureRegs[iReg].pszName = a_pszName;\
766 paSureRegs[iReg].enmReg = DBGFREG_END; \
767 paSureRegs[iReg].enmType = a_Type; \
768 paSureRegs[iReg].Value.a_ValMemb = (a_Value); \
769 } \
770 iReg++; \
771 } while (0)
772#define MAYBE_ADD_GREG(a_Value, a_enmReg, a_idxReg) do { \
773 if (!(pState->u.x86.Loaded.s.fRegs & RT_BIT(a_idxReg))) \
774 { \
775 if (paSureRegs) \
776 { \
777 pState->u.x86.Loaded.s.fRegs |= RT_BIT(a_idxReg); \
778 pState->u.x86.auRegs[a_idxReg] = (a_Value); \
779 paSureRegs[iReg].Value.u64 = (a_Value); \
780 paSureRegs[iReg].enmReg = a_enmReg; \
781 paSureRegs[iReg].enmType = DBGFREGVALTYPE_U64; \
782 paSureRegs[iReg].pszName = NULL; \
783 } \
784 iReg++; \
785 } \
786 } while (0)
787 for (unsigned iLoop = 0; iLoop < 2; iLoop++)
788 {
789 uint32_t iReg = pFrame->cSureRegs;
790 ADD_REG_NAMED(DBGFREGVALTYPE_U64, u64, uTrapFrameAddr, "TrapFrame");
791 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, TrapFrame.ExceptionActive, "ExceptionActive");
792 if (TrapFrame.ExceptionActive == 0)
793 {
794 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, TrapFrame.PreviousIrql, "PrevIrql");
795 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, (uint8_t)TrapFrame.ErrCdOrXcptFrameOrS, "IntNo");
796 }
797 else if ( TrapFrame.ExceptionActive == 1
798 && TrapFrame.FaultIndicator == ((TrapFrame.ErrCdOrXcptFrameOrS >> 1) & 0x9))
799 ADD_REG_NAMED(DBGFREGVALTYPE_U64, u64, TrapFrame.FaultAddrOrCtxRecOrTS, "cr2-probably");
800 if (TrapFrame.SegCs & X86_SEL_RPL)
801 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, 1, "UserMode");
802 else
803 ADD_REG_NAMED(DBGFREGVALTYPE_U8, u8, 1, "KernelMode");
804 if (TrapFrame.ExceptionActive <= 1)
805 {
806 MAYBE_ADD_GREG(TrapFrame.Rax, DBGFREG_RAX, X86_GREG_xAX);
807 MAYBE_ADD_GREG(TrapFrame.Rcx, DBGFREG_RCX, X86_GREG_xCX);
808 MAYBE_ADD_GREG(TrapFrame.Rdx, DBGFREG_RDX, X86_GREG_xDX);
809 MAYBE_ADD_GREG(TrapFrame.R8, DBGFREG_R8, X86_GREG_x8);
810 MAYBE_ADD_GREG(TrapFrame.R9, DBGFREG_R9, X86_GREG_x9);
811 MAYBE_ADD_GREG(TrapFrame.R10, DBGFREG_R10, X86_GREG_x10);
812 MAYBE_ADD_GREG(TrapFrame.R11, DBGFREG_R11, X86_GREG_x11);
813 }
814 else if (TrapFrame.ExceptionActive == 2)
815 {
816 MAYBE_ADD_GREG(TrapFrame.Rbx, DBGFREG_RBX, X86_GREG_xBX);
817 MAYBE_ADD_GREG(TrapFrame.Rsi, DBGFREG_RSI, X86_GREG_xSI);
818 MAYBE_ADD_GREG(TrapFrame.Rdi, DBGFREG_RDI, X86_GREG_xDI);
819 }
820 // MAYBE_ADD_GREG(TrapFrame.Rbp, DBGFREG_RBP, X86_GREG_xBP); - KiInterrupt[Sub]Dispatch* may leave this invalid.
821
822 /* Done? */
823 if (iLoop > 0)
824 {
825 Assert(cRegs == iReg);
826 break;
827 }
828
829 /* Resize the array, zeroing the extension. */
830 if (pFrame->cSureRegs)
831 paSureRegs = (PDBGFREGVALEX)pVMM->pfnMMR3HeapRealloc(pFrame->paSureRegs, iReg * sizeof(paSureRegs[0]));
832 else
833 paSureRegs = (PDBGFREGVALEX)pVMM->pfnMMR3HeapAllocU(pUVM, MM_TAG_DBGF_STACK, iReg * sizeof(paSureRegs[0]));
834 AssertReturn(paSureRegs, VERR_NO_MEMORY);
835
836 pFrame->paSureRegs = paSureRegs;
837 RT_BZERO(&paSureRegs[pFrame->cSureRegs], (iReg - pFrame->cSureRegs) * sizeof(paSureRegs[0]));
838 cRegs = iReg;
839 }
840#undef ADD_REG_NAMED
841#undef MAYBE_ADD_GREG
842
843 /* Commit the register update. */
844 pFrame->cSureRegs = cRegs;
845 }
846 }
847 }
848 }
849
850 RT_NOREF(pUVM, pVMM, pvData, idCpu, hAs, pInitialCtx, puScratch);
851 return VINF_SUCCESS;
852}
853
854
855/**
856 * @copydoc DBGFOSREG::pfnQueryInterface
857 */
858static DECLCALLBACK(void *) dbgDiggerWinNtQueryInterface(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData, DBGFOSINTERFACE enmIf)
859{
860 RT_NOREF(pUVM, pVMM);
861 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
862
863 switch (enmIf)
864 {
865 case DBGFOSINTERFACE_WINNT:
866 return &pThis->IWinNt;
867 default:
868 return NULL;
869 }
870}
871
872
873/**
874 * @copydoc DBGFOSREG::pfnQueryVersion
875 */
876static DECLCALLBACK(int) dbgDiggerWinNtQueryVersion(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData,
877 char *pszVersion, size_t cchVersion)
878{
879 RT_NOREF(pUVM, pVMM);
880 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
881 Assert(pThis->fValid);
882
883 const char *pszNtProductType;
884 switch (pThis->NtProductType)
885 {
886 case kNtProductType_WinNt: pszNtProductType = "-WinNT"; break;
887 case kNtProductType_LanManNt: pszNtProductType = "-LanManNT"; break;
888 case kNtProductType_Server: pszNtProductType = "-Server"; break;
889 default: pszNtProductType = ""; break;
890 }
891
892 RTStrPrintf(pszVersion, cchVersion, "%u.%u-%s%s (BuildNumber %u)", pThis->NtMajorVersion, pThis->NtMinorVersion,
893 pThis->f32Bit ? "x86" : "AMD64", pszNtProductType, pThis->NtBuildNumber);
894 return VINF_SUCCESS;
895}
896
897
898/**
899 * @copydoc DBGFOSREG::pfnTerm
900 */
901static DECLCALLBACK(void) dbgDiggerWinNtTerm(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
902{
903 RT_NOREF1(pUVM);
904 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
905 Assert(pThis->fValid);
906
907 /*
908 * As long as we're using our private LDR reader implementation,
909 * we must unlink and ditch the modules we created.
910 */
911 RTDBGAS hDbgAs = pVMM->pfnDBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
912 if (hDbgAs != NIL_RTDBGAS)
913 {
914 uint32_t iMod = RTDbgAsModuleCount(hDbgAs);
915 while (iMod-- > 0)
916 {
917 RTDBGMOD hMod = RTDbgAsModuleByIndex(hDbgAs, iMod);
918 if (hMod != NIL_RTDBGMOD)
919 {
920 if (RTDbgModGetTag(hMod) == DIG_WINNT_MOD_TAG)
921 {
922 int rc = RTDbgAsModuleUnlink(hDbgAs, hMod);
923 AssertRC(rc);
924 }
925 RTDbgModRelease(hMod);
926 }
927 }
928 RTDbgAsRelease(hDbgAs);
929 }
930
931 if (pThis->paKpcrAddr)
932 RTMemFree(pThis->paKpcrAddr);
933 /* pThis->paKpcrbAddr comes from the same allocation as pThis->paKpcrAddr. */
934
935 pThis->paKpcrAddr = NULL;
936 pThis->paKpcrbAddr = NULL;
937
938 pThis->fValid = false;
939}
940
941
942/**
943 * @copydoc DBGFOSREG::pfnRefresh
944 */
945static DECLCALLBACK(int) dbgDiggerWinNtRefresh(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
946{
947 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
948 NOREF(pThis);
949 Assert(pThis->fValid);
950
951 /*
952 * For now we'll flush and reload everything.
953 */
954 dbgDiggerWinNtTerm(pUVM, pVMM, pvData);
955
956 return dbgDiggerWinNtInit(pUVM, pVMM, pvData);
957}
958
959
960/**
961 * @copydoc DBGFOSREG::pfnInit
962 */
963static DECLCALLBACK(int) dbgDiggerWinNtInit(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
964{
965 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
966 Assert(!pThis->fValid);
967
968 union
969 {
970 uint8_t au8[0x2000];
971 RTUTF16 wsz[0x2000/2];
972 NTKUSERSHAREDDATA UserSharedData;
973 } u;
974 DBGFADDRESS Addr;
975 int rc;
976
977 /*
978 * Figure the NT version.
979 */
980 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, pThis->f32Bit ? NTKUSERSHAREDDATA_WINNT32 : NTKUSERSHAREDDATA_WINNT64);
981 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &u, PAGE_SIZE);
982 if (RT_SUCCESS(rc))
983 {
984 pThis->NtProductType = u.UserSharedData.ProductTypeIsValid && u.UserSharedData.NtProductType <= kNtProductType_Server
985 ? (NTPRODUCTTYPE)u.UserSharedData.NtProductType
986 : kNtProductType_Invalid;
987 pThis->NtMajorVersion = u.UserSharedData.NtMajorVersion;
988 pThis->NtMinorVersion = u.UserSharedData.NtMinorVersion;
989 pThis->NtBuildNumber = u.UserSharedData.NtBuildNumber;
990 }
991 else if (pThis->fNt31)
992 {
993 pThis->NtProductType = kNtProductType_WinNt;
994 pThis->NtMajorVersion = 3;
995 pThis->NtMinorVersion = 1;
996 pThis->NtBuildNumber = 0;
997 }
998 else
999 {
1000 Log(("DigWinNt: Error reading KUSER_SHARED_DATA: %Rrc\n", rc));
1001 return rc;
1002 }
1003
1004 /*
1005 * Dig out the module chain.
1006 */
1007 DBGFADDRESS AddrPrev = pThis->PsLoadedModuleListAddr;
1008 Addr = pThis->KernelMteAddr;
1009 do
1010 {
1011 /* Read the validate the MTE. */
1012 NTMTE Mte;
1013 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &Mte, pThis->f32Bit ? sizeof(Mte.vX_32) : sizeof(Mte.vX_64));
1014 if (RT_FAILURE(rc))
1015 break;
1016 if (WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Blink) != AddrPrev.FlatPtr)
1017 {
1018 Log(("DigWinNt: Bad Mte At %RGv - backpointer\n", Addr.FlatPtr));
1019 break;
1020 }
1021 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Flink)) )
1022 {
1023 Log(("DigWinNt: Bad Mte at %RGv - forward pointer\n", Addr.FlatPtr));
1024 break;
1025 }
1026 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer)))
1027 {
1028 Log(("DigWinNt: Bad Mte at %RGv - BaseDllName=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer)));
1029 break;
1030 }
1031 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, FullDllName.Buffer)))
1032 {
1033 Log(("DigWinNt: Bad Mte at %RGv - FullDllName=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, FullDllName.Buffer)));
1034 break;
1035 }
1036 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, DllBase)))
1037 {
1038 Log(("DigWinNt: Bad Mte at %RGv - DllBase=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, DllBase) ));
1039 break;
1040 }
1041
1042 uint32_t const cbImageMte = !pThis->fNt31 ? WINNT_UNION(pThis, &Mte, SizeOfImage) : 0;
1043 if ( !pThis->fNt31
1044 && ( cbImageMte > _256M
1045 || WINNT_UNION(pThis, &Mte, EntryPoint) - WINNT_UNION(pThis, &Mte, DllBase) > cbImageMte) )
1046 {
1047 Log(("DigWinNt: Bad Mte at %RGv - EntryPoint=%llx SizeOfImage=%x DllBase=%llx\n",
1048 Addr.FlatPtr, WINNT_UNION(pThis, &Mte, EntryPoint), cbImageMte, WINNT_UNION(pThis, &Mte, DllBase)));
1049 break;
1050 }
1051
1052 /* Read the full name. */
1053 DBGFADDRESS AddrName;
1054 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrName, WINNT_UNION(pThis, &Mte, FullDllName.Buffer));
1055 uint16_t cbName = WINNT_UNION(pThis, &Mte, FullDllName.Length);
1056 if (cbName < sizeof(u))
1057 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrName, &u, cbName);
1058 else
1059 rc = VERR_OUT_OF_RANGE;
1060 if (RT_FAILURE(rc))
1061 {
1062 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &AddrName, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer));
1063 cbName = WINNT_UNION(pThis, &Mte, BaseDllName.Length);
1064 if (cbName < sizeof(u))
1065 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrName, &u, cbName);
1066 else
1067 rc = VERR_OUT_OF_RANGE;
1068 }
1069 if (RT_SUCCESS(rc))
1070 {
1071 u.wsz[cbName / 2] = '\0';
1072
1073 char *pszFilename;
1074 rc = RTUtf16ToUtf8(u.wsz, &pszFilename);
1075 if (RT_SUCCESS(rc))
1076 {
1077 char szModName[128];
1078 const char *pszModName = dbgDiggerWintNtFilenameToModuleName(pszFilename, szModName, sizeof(szModName));
1079
1080 /* Read the start of the PE image and pass it along to a worker. */
1081 DBGFADDRESS ImageAddr;
1082 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &ImageAddr, WINNT_UNION(pThis, &Mte, DllBase));
1083 dbgDiggerWinNtProcessImage(pThis, pUVM, pVMM, pszModName, pszFilename, &ImageAddr, cbImageMte);
1084 RTStrFree(pszFilename);
1085 }
1086 }
1087
1088 /* next */
1089 AddrPrev = Addr;
1090 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Flink));
1091 } while ( Addr.FlatPtr != pThis->KernelMteAddr.FlatPtr
1092 && Addr.FlatPtr != pThis->PsLoadedModuleListAddr.FlatPtr);
1093
1094 /* Try resolving the KPCR and KPCRB addresses for each vCPU. */
1095 dbgDiggerWinNtResolveKpcr(pThis, pUVM, pVMM);
1096
1097 pThis->fValid = true;
1098 return VINF_SUCCESS;
1099}
1100
1101
1102/**
1103 * @copydoc DBGFOSREG::pfnProbe
1104 */
1105static DECLCALLBACK(bool) dbgDiggerWinNtProbe(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1106{
1107 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
1108 DBGFADDRESS Addr;
1109 union
1110 {
1111 uint8_t au8[8192];
1112 uint16_t au16[8192/2];
1113 uint32_t au32[8192/4];
1114 IMAGE_DOS_HEADER MzHdr;
1115 RTUTF16 wsz[8192/2];
1116 X86DESCGATE a32Gates[X86_XCPT_PF + 1];
1117 X86DESC64GATE a64Gates[X86_XCPT_PF + 1];
1118 } u;
1119
1120 union
1121 {
1122 NTMTE32 v32;
1123 NTMTE64 v64;
1124 } uMte, uMte2, uMte3;
1125
1126 /*
1127 * NT only runs in protected or long mode.
1128 */
1129 CPUMMODE const enmMode = pVMM->pfnDBGFR3CpuGetMode(pUVM, 0 /*idCpu*/);
1130 if (enmMode != CPUMMODE_PROTECTED && enmMode != CPUMMODE_LONG)
1131 return false;
1132 bool const f64Bit = enmMode == CPUMMODE_LONG;
1133 uint64_t const uStart = f64Bit ? UINT64_C(0xffff080000000000) : UINT32_C(0x80001000);
1134 uint64_t const uEnd = f64Bit ? UINT64_C(0xffffffffffff0000) : UINT32_C(0xffff0000);
1135
1136 /*
1137 * To approximately locate the kernel we examine the IDTR handlers.
1138 *
1139 * The exception/trap/fault handlers are all in NT kernel image, we pick
1140 * KiPageFault here.
1141 */
1142 uint64_t uIdtrBase = 0;
1143 uint16_t uIdtrLimit = 0;
1144 int rc = pVMM->pfnDBGFR3RegCpuQueryXdtr(pUVM, 0, DBGFREG_IDTR, &uIdtrBase, &uIdtrLimit);
1145 AssertRCReturn(rc, false);
1146
1147 const uint16_t cbMinIdtr = (X86_XCPT_PF + 1) * (f64Bit ? sizeof(X86DESC64GATE) : sizeof(X86DESCGATE));
1148 if (uIdtrLimit < cbMinIdtr)
1149 return false;
1150
1151 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, uIdtrBase), &u, cbMinIdtr);
1152 if (RT_FAILURE(rc))
1153 return false;
1154
1155 uint64_t uKrnlStart = uStart;
1156 uint64_t uKrnlEnd = uEnd;
1157 if (f64Bit)
1158 {
1159 uint64_t uHandler = u.a64Gates[X86_XCPT_PF].u16OffsetLow
1160 | ((uint32_t)u.a64Gates[X86_XCPT_PF].u16OffsetHigh << 16)
1161 | ((uint64_t)u.a64Gates[X86_XCPT_PF].u32OffsetTop << 32);
1162 if (uHandler < uStart || uHandler > uEnd)
1163 return false;
1164 uKrnlStart = (uHandler & ~(uint64_t)_4M) - _512M;
1165 uKrnlEnd = (uHandler + (uint64_t)_4M) & ~(uint64_t)_4M;
1166 }
1167 else
1168 {
1169 uint32_t uHandler = RT_MAKE_U32(u.a32Gates[X86_XCPT_PF].u16OffsetLow, u.a32Gates[X86_XCPT_PF].u16OffsetHigh);
1170 if (uHandler < uStart || uHandler > uEnd)
1171 return false;
1172 uKrnlStart = (uHandler & ~(uint64_t)_4M) - _64M;
1173 uKrnlEnd = (uHandler + (uint64_t)_4M) & ~(uint64_t)_4M;
1174 }
1175
1176 /*
1177 * Look for the PAGELK section name that seems to be a part of all kernels.
1178 * Then try find the module table entry for it. Since it's the first entry
1179 * in the PsLoadedModuleList we can easily validate the list head and report
1180 * success.
1181 *
1182 * Note! We ASSUME the section name is 8 byte aligned.
1183 */
1184 DBGFADDRESS KernelAddr;
1185 for (pVMM->pfnDBGFR3AddrFromFlat(pUVM, &KernelAddr, uKrnlStart);
1186 KernelAddr.FlatPtr < uKrnlEnd;
1187 KernelAddr.FlatPtr += PAGE_SIZE)
1188 {
1189 bool fNt31 = false;
1190 DBGFADDRESS const RetryAddress = KernelAddr;
1191 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, uEnd - KernelAddr.FlatPtr,
1192 8, "PAGELK\0", sizeof("PAGELK\0"), &KernelAddr);
1193 if ( rc == VERR_DBGF_MEM_NOT_FOUND
1194 && enmMode != CPUMMODE_LONG)
1195 {
1196 /* NT3.1 didn't have a PAGELK section, so look for _TEXT instead. The
1197 following VirtualSize is zero, so check for that too. */
1198 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &RetryAddress, uEnd - RetryAddress.FlatPtr,
1199 8, "_TEXT\0\0\0\0\0\0", sizeof("_TEXT\0\0\0\0\0\0"), &KernelAddr);
1200 fNt31 = true;
1201 }
1202 if (RT_FAILURE(rc))
1203 break;
1204 pVMM->pfnDBGFR3AddrSub(&KernelAddr, KernelAddr.FlatPtr & PAGE_OFFSET_MASK);
1205
1206 /* MZ + PE header. */
1207 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/, &KernelAddr, &u, sizeof(u));
1208 if ( RT_SUCCESS(rc)
1209 && u.MzHdr.e_magic == IMAGE_DOS_SIGNATURE
1210 && !(u.MzHdr.e_lfanew & 0x7)
1211 && u.MzHdr.e_lfanew >= 0x080
1212 && u.MzHdr.e_lfanew <= 0x400) /* W8 is at 0x288*/
1213 {
1214 if (enmMode != CPUMMODE_LONG)
1215 {
1216 IMAGE_NT_HEADERS32 const *pHdrs = (IMAGE_NT_HEADERS32 const *)&u.au8[u.MzHdr.e_lfanew];
1217 if ( pHdrs->Signature == IMAGE_NT_SIGNATURE
1218 && pHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_I386
1219 && pHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pHdrs->OptionalHeader)
1220 && pHdrs->FileHeader.NumberOfSections >= 10 /* the kernel has lots */
1221 && (pHdrs->FileHeader.Characteristics & (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL)) == IMAGE_FILE_EXECUTABLE_IMAGE
1222 && pHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
1223 && pHdrs->OptionalHeader.NumberOfRvaAndSizes == IMAGE_NUMBEROF_DIRECTORY_ENTRIES
1224 )
1225 {
1226 /* Find the MTE. */
1227 RT_ZERO(uMte);
1228 uMte.v32.DllBase = KernelAddr.FlatPtr;
1229 uMte.v32.EntryPoint = KernelAddr.FlatPtr + pHdrs->OptionalHeader.AddressOfEntryPoint;
1230 uMte.v32.SizeOfImage = !fNt31 ? pHdrs->OptionalHeader.SizeOfImage : 0; /* NT 3.1 didn't set the size. */
1231 DBGFADDRESS HitAddr;
1232 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, uEnd - KernelAddr.FlatPtr,
1233 4 /*align*/, &uMte.v32.DllBase, 3 * sizeof(uint32_t), &HitAddr);
1234 while (RT_SUCCESS(rc))
1235 {
1236 /* check the name. */
1237 DBGFADDRESS MteAddr = HitAddr;
1238 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1239 pVMM->pfnDBGFR3AddrSub(&MteAddr, RT_OFFSETOF(NTMTE32, DllBase)),
1240 &uMte2.v32, sizeof(uMte2.v32));
1241 if ( RT_SUCCESS(rc)
1242 && uMte2.v32.DllBase == uMte.v32.DllBase
1243 && uMte2.v32.EntryPoint == uMte.v32.EntryPoint
1244 && uMte2.v32.SizeOfImage == uMte.v32.SizeOfImage
1245 && WINNT32_VALID_ADDRESS(uMte2.v32.InLoadOrderLinks.Flink)
1246 && WINNT32_VALID_ADDRESS(uMte2.v32.BaseDllName.Buffer)
1247 && WINNT32_VALID_ADDRESS(uMte2.v32.FullDllName.Buffer)
1248 && uMte2.v32.BaseDllName.Length <= 128
1249 && uMte2.v32.FullDllName.Length <= 260
1250 )
1251 {
1252 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1253 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, uMte2.v32.BaseDllName.Buffer),
1254 u.wsz, uMte2.v32.BaseDllName.Length);
1255 u.wsz[uMte2.v32.BaseDllName.Length / 2] = '\0';
1256 if ( RT_SUCCESS(rc)
1257 && ( !RTUtf16ICmp(u.wsz, g_wszKernelNames[0])
1258 /* || !RTUtf16ICmp(u.wsz, g_wszKernelNames[1]) */
1259 )
1260 )
1261 {
1262 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1263 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr,
1264 uMte2.v32.InLoadOrderLinks.Blink),
1265 &uMte3.v32, RT_SIZEOFMEMB(NTMTE32, InLoadOrderLinks));
1266 if ( RT_SUCCESS(rc)
1267 && uMte3.v32.InLoadOrderLinks.Flink == MteAddr.FlatPtr
1268 && WINNT32_VALID_ADDRESS(uMte3.v32.InLoadOrderLinks.Blink) )
1269 {
1270 Log(("DigWinNt: MteAddr=%RGv KernelAddr=%RGv SizeOfImage=%x &PsLoadedModuleList=%RGv (32-bit)\n",
1271 MteAddr.FlatPtr, KernelAddr.FlatPtr, uMte2.v32.SizeOfImage, Addr.FlatPtr));
1272 pThis->KernelAddr = KernelAddr;
1273 pThis->KernelMteAddr = MteAddr;
1274 pThis->PsLoadedModuleListAddr = Addr;
1275 pThis->f32Bit = true;
1276 pThis->fNt31 = fNt31;
1277 return true;
1278 }
1279 }
1280 else if (RT_SUCCESS(rc))
1281 {
1282 Log2(("DigWinNt: Wrong module: MteAddr=%RGv ImageAddr=%RGv SizeOfImage=%#x '%ls'\n",
1283 MteAddr.FlatPtr, KernelAddr.FlatPtr, uMte2.v32.SizeOfImage, u.wsz));
1284 break; /* Not NT kernel */
1285 }
1286 }
1287
1288 /* next */
1289 pVMM->pfnDBGFR3AddrAdd(&HitAddr, 4);
1290 if (HitAddr.FlatPtr < uEnd)
1291 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, uEnd - HitAddr.FlatPtr,
1292 4 /*align*/, &uMte.v32.DllBase, 3 * sizeof(uint32_t), &HitAddr);
1293 else
1294 rc = VERR_DBGF_MEM_NOT_FOUND;
1295 }
1296 }
1297 }
1298 else
1299 {
1300 IMAGE_NT_HEADERS64 const *pHdrs = (IMAGE_NT_HEADERS64 const *)&u.au8[u.MzHdr.e_lfanew];
1301 if ( pHdrs->Signature == IMAGE_NT_SIGNATURE
1302 && pHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64
1303 && pHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pHdrs->OptionalHeader)
1304 && pHdrs->FileHeader.NumberOfSections >= 10 /* the kernel has lots */
1305 && (pHdrs->FileHeader.Characteristics & (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL))
1306 == IMAGE_FILE_EXECUTABLE_IMAGE
1307 && pHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC
1308 && pHdrs->OptionalHeader.NumberOfRvaAndSizes == IMAGE_NUMBEROF_DIRECTORY_ENTRIES
1309 )
1310 {
1311 /* Find the MTE. */
1312 RT_ZERO(uMte.v64);
1313 uMte.v64.DllBase = KernelAddr.FlatPtr;
1314 uMte.v64.EntryPoint = KernelAddr.FlatPtr + pHdrs->OptionalHeader.AddressOfEntryPoint;
1315 uMte.v64.SizeOfImage = pHdrs->OptionalHeader.SizeOfImage;
1316 DBGFADDRESS ScanAddr;
1317 DBGFADDRESS HitAddr;
1318 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, pVMM->pfnDBGFR3AddrFromFlat(pUVM, &ScanAddr, uStart),
1319 uEnd - uStart, 8 /*align*/, &uMte.v64.DllBase, 5 * sizeof(uint32_t), &HitAddr);
1320 while (RT_SUCCESS(rc))
1321 {
1322 /* Read the start of the MTE and check some basic members. */
1323 DBGFADDRESS MteAddr = HitAddr;
1324 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1325 pVMM->pfnDBGFR3AddrSub(&MteAddr, RT_OFFSETOF(NTMTE64, DllBase)),
1326 &uMte2.v64, sizeof(uMte2.v64));
1327 if ( RT_SUCCESS(rc)
1328 && uMte2.v64.DllBase == uMte.v64.DllBase
1329 && uMte2.v64.EntryPoint == uMte.v64.EntryPoint
1330 && uMte2.v64.SizeOfImage == uMte.v64.SizeOfImage
1331 && WINNT64_VALID_ADDRESS(uMte2.v64.InLoadOrderLinks.Flink)
1332 && WINNT64_VALID_ADDRESS(uMte2.v64.BaseDllName.Buffer)
1333 && WINNT64_VALID_ADDRESS(uMte2.v64.FullDllName.Buffer)
1334 && uMte2.v64.BaseDllName.Length <= 128
1335 && uMte2.v64.FullDllName.Length <= 260
1336 )
1337 {
1338 /* Try read the base name and compare with known NT kernel names. */
1339 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1340 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr, uMte2.v64.BaseDllName.Buffer),
1341 u.wsz, uMte2.v64.BaseDllName.Length);
1342 u.wsz[uMte2.v64.BaseDllName.Length / 2] = '\0';
1343 if ( RT_SUCCESS(rc)
1344 && ( !RTUtf16ICmp(u.wsz, g_wszKernelNames[0])
1345 /* || !RTUtf16ICmp(u.wsz, g_wszKernelNames[1]) */
1346 )
1347 )
1348 {
1349 /* Read the link entry of the previous entry in the list and check that its
1350 forward pointer points at the MTE we've found. */
1351 rc = pVMM->pfnDBGFR3MemRead(pUVM, 0 /*idCpu*/,
1352 pVMM->pfnDBGFR3AddrFromFlat(pUVM, &Addr,
1353 uMte2.v64.InLoadOrderLinks.Blink),
1354 &uMte3.v64, RT_SIZEOFMEMB(NTMTE64, InLoadOrderLinks));
1355 if ( RT_SUCCESS(rc)
1356 && uMte3.v64.InLoadOrderLinks.Flink == MteAddr.FlatPtr
1357 && WINNT64_VALID_ADDRESS(uMte3.v64.InLoadOrderLinks.Blink) )
1358 {
1359 Log(("DigWinNt: MteAddr=%RGv KernelAddr=%RGv SizeOfImage=%x &PsLoadedModuleList=%RGv (32-bit)\n",
1360 MteAddr.FlatPtr, KernelAddr.FlatPtr, uMte2.v64.SizeOfImage, Addr.FlatPtr));
1361 pThis->KernelAddr = KernelAddr;
1362 pThis->KernelMteAddr = MteAddr;
1363 pThis->PsLoadedModuleListAddr = Addr;
1364 pThis->f32Bit = false;
1365 pThis->fNt31 = false;
1366 return true;
1367 }
1368 }
1369 else if (RT_SUCCESS(rc))
1370 {
1371 Log2(("DigWinNt: Wrong module: MteAddr=%RGv ImageAddr=%RGv SizeOfImage=%#x '%ls'\n",
1372 MteAddr.FlatPtr, KernelAddr.FlatPtr, uMte2.v64.SizeOfImage, u.wsz));
1373 break; /* Not NT kernel */
1374 }
1375 }
1376
1377 /* next */
1378 pVMM->pfnDBGFR3AddrAdd(&HitAddr, 8);
1379 if (HitAddr.FlatPtr < uEnd)
1380 rc = pVMM->pfnDBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, uEnd - HitAddr.FlatPtr,
1381 8 /*align*/, &uMte.v64.DllBase, 3 * sizeof(uint32_t), &HitAddr);
1382 else
1383 rc = VERR_DBGF_MEM_NOT_FOUND;
1384 }
1385 }
1386 }
1387 }
1388 }
1389 return false;
1390}
1391
1392
1393/**
1394 * @copydoc DBGFOSREG::pfnDestruct
1395 */
1396static DECLCALLBACK(void) dbgDiggerWinNtDestruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1397{
1398 RT_NOREF(pUVM, pVMM, pvData);
1399}
1400
1401
1402/**
1403 * @copydoc DBGFOSREG::pfnConstruct
1404 */
1405static DECLCALLBACK(int) dbgDiggerWinNtConstruct(PUVM pUVM, PCVMMR3VTABLE pVMM, void *pvData)
1406{
1407 RT_NOREF(pUVM, pVMM);
1408 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
1409 pThis->fValid = false;
1410 pThis->f32Bit = false;
1411 pThis->enmVer = DBGDIGGERWINNTVER_UNKNOWN;
1412
1413 pThis->IWinNt.u32Magic = DBGFOSIWINNT_MAGIC;
1414 pThis->IWinNt.pfnQueryVersion = dbgDiggerWinNtIWinNt_QueryVersion;
1415 pThis->IWinNt.pfnQueryKernelPtrs = dbgDiggerWinNtIWinNt_QueryKernelPtrs;
1416 pThis->IWinNt.pfnQueryKpcrForVCpu = dbgDiggerWinNtIWinNt_QueryKpcrForVCpu;
1417 pThis->IWinNt.pfnQueryCurThrdForVCpu = dbgDiggerWinNtIWinNt_QueryCurThrdForVCpu;
1418 pThis->IWinNt.u32EndMagic = DBGFOSIWINNT_MAGIC;
1419
1420 return VINF_SUCCESS;
1421}
1422
1423
1424const DBGFOSREG g_DBGDiggerWinNt =
1425{
1426 /* .u32Magic = */ DBGFOSREG_MAGIC,
1427 /* .fFlags = */ 0,
1428 /* .cbData = */ sizeof(DBGDIGGERWINNT),
1429 /* .szName = */ "WinNT",
1430 /* .pfnConstruct = */ dbgDiggerWinNtConstruct,
1431 /* .pfnDestruct = */ dbgDiggerWinNtDestruct,
1432 /* .pfnProbe = */ dbgDiggerWinNtProbe,
1433 /* .pfnInit = */ dbgDiggerWinNtInit,
1434 /* .pfnRefresh = */ dbgDiggerWinNtRefresh,
1435 /* .pfnTerm = */ dbgDiggerWinNtTerm,
1436 /* .pfnQueryVersion = */ dbgDiggerWinNtQueryVersion,
1437 /* .pfnQueryInterface = */ dbgDiggerWinNtQueryInterface,
1438 /* .pfnStackUnwindAssist = */ dbgDiggerWinNtStackUnwindAssist,
1439 /* .u32EndMagic = */ DBGFOSREG_MAGIC
1440};
1441
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use