VirtualBox

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

Last change on this file since 104611 was 104611, checked in by vboxsync, 6 months ago

Debugger/DBGPlugInWinNt.cpp: Log error when linking module into address space, superfluous variable initialization, bugref:3409

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