VirtualBox

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

Last change on this file since 96870 was 96870, checked in by vboxsync, 2 years ago

Debugger/DBGPlugInWinNt: Some unfinished code to hook into the guests DbgPrint/vDbgPrint API to get at kernel log messages (disabled by default), bugref:1098 [build fix]

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