VirtualBox

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

Last change on this file since 46083 was 46083, checked in by vboxsync, 12 years ago

Made it possible to find symbols for windows nt using a image-in-guest-memory loader fallback.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.5 KB
Line 
1/* $Id: DBGPlugInWinNt.cpp 46083 2013-05-14 23:39:28Z vboxsync $ */
2/** @file
3 * DBGPlugInWindows - Debugger and Guest OS Digger Plugin For Windows NT.
4 */
5
6/*
7 * Copyright (C) 2009-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGF ///@todo add new log group.
23#include "DBGPlugIns.h"
24#include <VBox/vmm/dbgf.h>
25#include <VBox/err.h>
26#include <VBox/param.h>
27#include <iprt/ldr.h>
28#include <iprt/mem.h>
29#include <iprt/stream.h>
30#include <iprt/string.h>
31
32#include "../Runtime/include/internal/ldrMZ.h" /* ugly */
33#include "../Runtime/include/internal/ldrPE.h" /* ugly */
34
35
36/*******************************************************************************
37* Structures and Typedefs *
38*******************************************************************************/
39
40/** @name Internal WinNT structures
41 * @{ */
42/**
43 * PsLoadedModuleList entry for 32-bit NT aka LDR_DATA_TABLE_ENTRY.
44 * Tested with XP.
45 */
46typedef struct NTMTE32
47{
48 struct
49 {
50 uint32_t Flink;
51 uint32_t Blink;
52 } InLoadOrderLinks,
53 InMemoryOrderModuleList,
54 InInitializationOrderModuleList;
55 uint32_t DllBase;
56 uint32_t EntryPoint;
57 uint32_t SizeOfImage;
58 struct
59 {
60 uint16_t Length;
61 uint16_t MaximumLength;
62 uint32_t Buffer;
63 } FullDllName,
64 BaseDllName;
65 uint32_t Flags;
66 uint16_t LoadCount;
67 uint16_t TlsIndex;
68 /* ... there is more ... */
69} NTMTE32;
70typedef NTMTE32 *PNTMTE32;
71
72/**
73 * PsLoadedModuleList entry for 32-bit NT aka LDR_DATA_TABLE_ENTRY.
74 * Tested with XP.
75 *
76 * @todo This is incomplete and just to get rid of warnings.
77 */
78typedef struct NTMTE64
79{
80 struct
81 {
82 uint64_t Flink;
83 uint64_t Blink;
84 } InLoadOrderLinks,
85 InMemoryOrderModuleList,
86 InInitializationOrderModuleList;
87 uint64_t DllBase;
88 uint64_t EntryPoint;
89 uint32_t SizeOfImage;
90 uint32_t Alignment;
91 struct
92 {
93 uint16_t Length;
94 uint16_t MaximumLength;
95 uint32_t Alignment;
96 uint64_t Buffer;
97 } FullDllName,
98 BaseDllName;
99 uint32_t Flags;
100 uint16_t LoadCount;
101 uint16_t TlsIndex;
102 /* ... there is more ... */
103} NTMTE64;
104typedef NTMTE64 *PNTMTE64;
105
106/** MTE union. */
107typedef union NTMTE
108{
109 NTMTE32 vX_32;
110 NTMTE64 vX_64;
111} NTMTE;
112typedef NTMTE *PNTMTE;
113
114
115/**
116 * The essential bits of the KUSER_SHARED_DATA structure.
117 */
118typedef struct NTKUSERSHAREDDATA
119{
120 uint32_t TickCountLowDeprecated;
121 uint32_t TickCountMultiplier;
122 struct
123 {
124 uint32_t LowPart;
125 int32_t High1Time;
126 int32_t High2Time;
127
128 } InterruptTime,
129 SystemTime,
130 TimeZoneBias;
131 uint16_t ImageNumberLow;
132 uint16_t ImageNumberHigh;
133 RTUTF16 NtSystemRoot[260];
134 uint32_t MaxStackTraceDepth;
135 uint32_t CryptoExponent;
136 uint32_t TimeZoneId;
137 uint32_t LargePageMinimum;
138 uint32_t Reserved2[7];
139 uint32_t NtProductType;
140 uint8_t ProductTypeIsValid;
141 uint8_t abPadding[3];
142 uint32_t NtMajorVersion;
143 uint32_t NtMinorVersion;
144 /* uint8_t ProcessorFeatures[64];
145 ...
146 */
147} NTKUSERSHAREDDATA;
148typedef NTKUSERSHAREDDATA *PNTKUSERSHAREDDATA;
149
150/** KI_USER_SHARED_DATA for i386 */
151#define NTKUSERSHAREDDATA_WINNT32 UINT32_C(0xffdf0000)
152/** KI_USER_SHARED_DATA for AMD64 */
153#define NTKUSERSHAREDDATA_WINNT64 UINT64_C(0xfffff78000000000)
154
155/** NTKUSERSHAREDDATA::NtProductType */
156typedef enum NTPRODUCTTYPE
157{
158 kNtProductType_Invalid = 0,
159 kNtProductType_WinNt = 1,
160 kNtProductType_LanManNt,
161 kNtProductType_Server
162} NTPRODUCTTYPE;
163
164/**
165 * PDB v2.0 in image debug info.
166 * The URL is constructed from the timestamp and the %02x age?
167 */
168typedef struct CV_INFO_PDB20
169{
170 uint32_t Signature; /**< CV_SIGNATURE_PDB70. */
171 int32_t Offset; /**< Always 0. Used to be the offset to the real debug info. */
172 uint32_t TimeDateStamp;
173 uint32_t Age;
174 uint8_t PdbFilename[4];
175} CV_INFO_PDB20;
176/** The CV_INFO_PDB20 signature. */
177#define CV_SIGNATURE_PDB20 RT_MAKE_U32_FROM_U8('N','B','1','0')
178
179/**
180 * PDB v7.0 in image debug info.
181 * The URL is constructed from the signature and the %02x age.
182 */
183#pragma pack(4)
184typedef struct CV_INFO_PDB70
185{
186 uint32_t CvSignature; /**< CV_SIGNATURE_PDB70. */
187 RTUUID Signature;
188 uint32_t Age;
189 uint8_t PdbFilename[4];
190} CV_INFO_PDB70;
191#pragma pack()
192AssertCompileMemberOffset(CV_INFO_PDB70, Signature, 4);
193AssertCompileMemberOffset(CV_INFO_PDB70, Age, 4 + 16);
194/** The CV_INFO_PDB70 signature. */
195#define CV_SIGNATURE_PDB70 RT_MAKE_U32_FROM_U8('R','S','D','S')
196
197/** @} */
198
199
200typedef enum DBGDIGGERWINNTVER
201{
202 DBGDIGGERWINNTVER_UNKNOWN,
203 DBGDIGGERWINNTVER_3_1,
204 DBGDIGGERWINNTVER_3_5,
205 DBGDIGGERWINNTVER_4_0,
206 DBGDIGGERWINNTVER_5_0,
207 DBGDIGGERWINNTVER_5_1,
208 DBGDIGGERWINNTVER_6_0
209} DBGDIGGERWINNTVER;
210
211/**
212 * WinNT guest OS digger instance data.
213 */
214typedef struct DBGDIGGERWINNT
215{
216 /** Whether the information is valid or not.
217 * (For fending off illegal interface method calls.) */
218 bool fValid;
219 /** 32-bit (true) or 64-bit (false) */
220 bool f32Bit;
221
222 /** The NT version. */
223 DBGDIGGERWINNTVER enmVer;
224 /** NTKUSERSHAREDDATA::NtProductType */
225 NTPRODUCTTYPE NtProductType;
226 /** NTKUSERSHAREDDATA::NtMajorVersion */
227 uint32_t NtMajorVersion;
228 /** NTKUSERSHAREDDATA::NtMinorVersion */
229 uint32_t NtMinorVersion;
230
231 /** The address of the ntoskrnl.exe image. */
232 DBGFADDRESS KernelAddr;
233 /** The address of the ntoskrnl.exe module table entry. */
234 DBGFADDRESS KernelMteAddr;
235 /** The address of PsLoadedModuleList. */
236 DBGFADDRESS PsLoadedModuleListAddr;
237} DBGDIGGERWINNT;
238/** Pointer to the linux guest OS digger instance data. */
239typedef DBGDIGGERWINNT *PDBGDIGGERWINNT;
240
241
242/**
243 * The WinNT digger's loader reader instance data.
244 */
245typedef struct DBGDIGGERWINNTRDR
246{
247 /** The VM handle (referenced). */
248 PUVM pUVM;
249 /** The image base. */
250 DBGFADDRESS ImageAddr;
251 /** The image size. */
252 uint32_t cbImage;
253 /** Number of entries in the aMappings table. */
254 uint32_t cMappings;
255 /** Mapping hint. */
256 uint32_t iHint;
257 /** Mapping file offset to memory offsets, ordered by file offset. */
258 struct
259 {
260 /** The file offset. */
261 uint32_t offFile;
262 /** The size of this mapping. */
263 uint32_t cbMem;
264 /** The offset to the memory from the start of the image. */
265 uint32_t offMem;
266 } aMappings[1];
267} DBGDIGGERWINNTRDR;
268/** Pointer a WinNT loader reader instance data. */
269typedef DBGDIGGERWINNTRDR *PDBGDIGGERWINNTRDR;
270
271
272/*******************************************************************************
273* Defined Constants And Macros *
274*******************************************************************************/
275/** Validates a 32-bit Windows NT kernel address */
276#define WINNT32_VALID_ADDRESS(Addr) ((Addr) > UINT32_C(0x80000000) && (Addr) < UINT32_C(0xfffff000))
277/** Validates a 64-bit Windows NT kernel address */
278#define WINNT64_VALID_ADDRESS(Addr) ((Addr) > UINT64_C(0xffffffff80000000) && (Addr) < UINT64_C(0xfffffffffffff000))
279/** Validates a kernel address. */
280#define WINNT_VALID_ADDRESS(pThis, Addr) ((pThis)->f32Bit ? WINNT32_VALID_ADDRESS(Addr) : WINNT64_VALID_ADDRESS(Addr))
281/** Versioned and bitness wrapper. */
282#define WINNT_UNION(pThis, pUnion, Member) ((pThis)->f32Bit ? (pUnion)->vX_32. Member : (pUnion)->vX_64. Member )
283
284/** The length (in chars) of the kernel file name (no path). */
285#define WINNT_KERNEL_BASE_NAME_LEN 12
286
287/** WindowsNT on little endian ASCII systems. */
288#define DIG_WINNT_MOD_TAG UINT64_C(0x54696e646f774e54)
289
290
291/*******************************************************************************
292* Internal Functions *
293*******************************************************************************/
294static DECLCALLBACK(int) dbgDiggerWinNtInit(PUVM pUVM, void *pvData);
295
296
297/*******************************************************************************
298* Global Variables *
299*******************************************************************************/
300/** Kernel names. */
301static const RTUTF16 g_wszKernelNames[][WINNT_KERNEL_BASE_NAME_LEN + 1] =
302{
303 { 'n', 't', 'o', 's', 'k', 'r', 'n', 'l', '.', 'e', 'x', 'e' }
304};
305
306
307
308/** @callback_method_impl{PFNRTLDRRDRMEMREAD} */
309static DECLCALLBACK(int) dbgDiggerWinNtRdr_Read(void *pvBuf, size_t cb, size_t off, void *pvUser)
310{
311 PDBGDIGGERWINNTRDR pThis = (PDBGDIGGERWINNTRDR)pvUser;
312
313 uint32_t i = pThis->iHint;
314 if (pThis->aMappings[i].offFile > off)
315 {
316 i = pThis->cMappings;
317 while (i-- > 0)
318 if (off >= pThis->aMappings[i].offFile)
319 break;
320 pThis->iHint = i;
321 }
322
323 while (cb > 0)
324 {
325 uint32_t offNextMap = i + 1 < pThis->cMappings ? pThis->aMappings[i + 1].offFile : pThis->cbImage;
326 uint32_t offMap = (uint32_t)off - pThis->aMappings[i].offFile;
327
328 /* Read file bits backed by memory. */
329 if (off < pThis->aMappings[i].cbMem)
330 {
331 uint32_t cbToRead = pThis->aMappings[i].cbMem - offMap;
332 if (cbToRead > cb)
333 cbToRead = (uint32_t)cb;
334
335 DBGFADDRESS Addr = pThis->ImageAddr;
336 DBGFR3AddrAdd(&Addr, pThis->aMappings[i].offMem + offMap);
337
338 int rc = DBGFR3MemRead(pThis->pUVM, 0 /*idCpu*/, &Addr, pvBuf, cbToRead);
339 if (RT_FAILURE(rc))
340 return rc;
341 if (cbToRead == cb)
342 break;
343
344 off += cbToRead;
345 cb -= cbToRead;
346 pvBuf = (char *)pvBuf + cbToRead;
347 }
348
349 /* Mind the gap. */
350 if (offNextMap > off)
351 {
352 uint32_t cbZero = offNextMap - (uint32_t)off;
353 if (cbZero > cb)
354 {
355 RT_BZERO(pvBuf, cb);
356 break;
357 }
358
359 RT_BZERO(pvBuf, cbZero);
360 off += cbZero;
361 cb -= cbZero;
362 pvBuf = (char *)pvBuf + cbZero;
363 }
364
365 pThis->iHint = ++i;
366 }
367
368 return VINF_SUCCESS;
369}
370
371
372/** @callback_method_impl{PFNRTLDRRDRMEMDTOR} */
373static DECLCALLBACK(void) dbgDiggerWinNtRdr_Dtor(void *pvUser)
374{
375 PDBGDIGGERWINNTRDR pThis = (PDBGDIGGERWINNTRDR)pvUser;
376
377 VMR3ReleaseUVM(pThis->pUVM);
378 pThis->pUVM = NULL;
379 RTMemFree(pvUser);
380}
381
382
383/**
384 * Process a PE image found in guest memory.
385 *
386 * @param pThis The instance data.
387 * @param pUVM The user mode VM handle.
388 * @param pszName The image name.
389 * @param pImageAddr The image address.
390 * @param cbImage The size of the image.
391 * @param pbBuf Scratch buffer containing the first
392 * RT_MIN(cbBuf, cbImage) bytes of the image.
393 * @param cbBuf The scratch buffer size.
394 */
395static void dbgDiggerWinNtProcessImage(PDBGDIGGERWINNT pThis, PUVM pUVM, const char *pszName,
396 PCDBGFADDRESS pImageAddr, uint32_t cbImage,
397 uint8_t *pbBuf, size_t cbBuf)
398{
399 LogFlow(("DigWinNt: %RGp %#x %s\n", pImageAddr->FlatPtr, cbImage, pszName));
400
401 /*
402 * Do some basic validation first.
403 * This is the usual exteremely verbose and messy code...
404 */
405 Assert(cbBuf >= sizeof(IMAGE_NT_HEADERS64));
406 if ( cbImage < sizeof(IMAGE_NT_HEADERS64)
407 || cbImage >= _1M * 256)
408 {
409 Log(("DigWinNt: %s: Bad image size: %#x\n", pszName, cbImage));
410 return;
411 }
412
413 /* Dig out the NT/PE headers. */
414 IMAGE_DOS_HEADER const *pMzHdr = (IMAGE_DOS_HEADER const *)pbBuf;
415 typedef union NTHDRSU
416 {
417 IMAGE_NT_HEADERS32 vX_32;
418 IMAGE_NT_HEADERS64 vX_64;
419 } NTHDRS;
420 NTHDRS const *pHdrs;
421 uint32_t offHdrs;
422 if (pMzHdr->e_magic != IMAGE_DOS_SIGNATURE)
423 {
424 offHdrs = 0;
425 pHdrs = (NTHDRS const *)pbBuf;
426 }
427 else if ( pMzHdr->e_lfanew >= cbImage
428 || pMzHdr->e_lfanew < sizeof(*pMzHdr)
429 || pMzHdr->e_lfanew + sizeof(IMAGE_NT_HEADERS64) > cbImage)
430 {
431 Log(("DigWinNt: %s: PE header to far into image: %#x cbImage=%#x\n", pMzHdr->e_lfanew, cbImage));
432 return;
433 }
434 else if ( pMzHdr->e_lfanew < cbBuf
435 && pMzHdr->e_lfanew + sizeof(IMAGE_NT_HEADERS64) <= cbBuf)
436 {
437 offHdrs = pMzHdr->e_lfanew;
438 pHdrs = (NTHDRS const *)(pbBuf + offHdrs);
439 }
440 else
441 {
442 Log(("DigWinNt: %s: PE header to far into image (lazy bird): %#x\n", pMzHdr->e_lfanew));
443 return;
444 }
445 if (pHdrs->vX_32.Signature != IMAGE_NT_SIGNATURE)
446 {
447 Log(("DigWinNt: %s: Bad PE signature: %#x\n", pszName, pHdrs->vX_32.Signature));
448 return;
449 }
450
451 /* The file header is the same on both archs */
452 if (pHdrs->vX_32.FileHeader.Machine != (pThis->f32Bit ? IMAGE_FILE_MACHINE_I386 : IMAGE_FILE_MACHINE_AMD64))
453 {
454 Log(("DigWinNt: %s: Invalid FH.Machine: %#x\n", pszName, pHdrs->vX_32.FileHeader.Machine));
455 return;
456 }
457 if (pHdrs->vX_32.FileHeader.SizeOfOptionalHeader != (pThis->f32Bit ? sizeof(IMAGE_OPTIONAL_HEADER32) : sizeof(IMAGE_OPTIONAL_HEADER64)))
458 {
459 Log(("DigWinNt: %s: Invalid FH.SizeOfOptionalHeader: %#x\n", pszName, pHdrs->vX_32.FileHeader.SizeOfOptionalHeader));
460 return;
461 }
462 const uint32_t TimeDateStamp = pHdrs->vX_32.FileHeader.TimeDateStamp;
463
464 /* The optional header is not... */
465 if (WINNT_UNION(pThis, pHdrs, OptionalHeader.Magic) != (pThis->f32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC))
466 {
467 Log(("DigWinNt: %s: Invalid OH.Magic: %#x\n", pszName, WINNT_UNION(pThis, pHdrs, OptionalHeader.Magic)));
468 return;
469 }
470 uint32_t cbImageFromHdr = WINNT_UNION(pThis, pHdrs, OptionalHeader.SizeOfImage);
471 if (RT_ALIGN(cbImageFromHdr, _4K) != RT_ALIGN(cbImage, _4K))
472 {
473 Log(("DigWinNt: %s: Invalid OH.SizeOfImage: %#x, expected %#x\n", pszName, cbImageFromHdr, cbImage));
474 return;
475 }
476 if (WINNT_UNION(pThis, pHdrs, OptionalHeader.NumberOfRvaAndSizes) != IMAGE_NUMBEROF_DIRECTORY_ENTRIES)
477 {
478 Log(("DigWinNt: %s: Invalid OH.NumberOfRvaAndSizes: %#x\n", pszName, WINNT_UNION(pThis, pHdrs, OptionalHeader.NumberOfRvaAndSizes)));
479 return;
480 }
481
482 uint32_t uRvaDebugDir = 0;
483 uint32_t cbDebugDir = 0;
484 IMAGE_DATA_DIRECTORY const *pDir = &WINNT_UNION(pThis, pHdrs, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG]);
485 if ( pDir->VirtualAddress > offHdrs
486 && pDir->VirtualAddress < cbImage
487 && pDir->Size >= sizeof(IMAGE_DEBUG_DIRECTORY)
488 && pDir->Size < cbImage
489 && pDir->VirtualAddress + pDir->Size <= cbImage
490 )
491 {
492 uRvaDebugDir = pDir->VirtualAddress;
493 cbDebugDir = pDir->Size;
494 }
495
496 /*
497 * Create the module.
498 */
499 RTDBGMOD hMod;
500 int rc = RTDbgModCreateFromPeImage(&hMod, pszName, NULL, NIL_RTLDRMOD,
501 cbImageFromHdr, TimeDateStamp, DBGFR3AsGetConfig(pUVM));
502 if (RT_FAILURE(rc))
503 {
504 /*
505 * Probably didn't find the image any where, try fake an image
506 * from guest memory.
507 */
508 uint32_t cMappings = WINNT_UNION(pThis, pHdrs, FileHeader.NumberOfSections) + 3;
509 PDBGDIGGERWINNTRDR pRdr = (PDBGDIGGERWINNTRDR)RTMemAlloc(RT_OFFSETOF(DBGDIGGERWINNTRDR, aMappings[cMappings]));
510 if (!pRdr)
511 return;
512
513 VMR3RetainUVM(pUVM);
514 pRdr->pUVM = pUVM;
515 pRdr->ImageAddr = *pImageAddr;
516 pRdr->cbImage = cbImageFromHdr;
517 pRdr->cMappings = 1;
518 pRdr->iHint = 0;
519 pRdr->aMappings[0].offFile = 0;
520 pRdr->aMappings[0].offMem = 0;
521 pRdr->aMappings[0].cbMem = cbImageFromHdr;
522
523 RTLDRMOD hLdrMod;
524 rc = RTLdrOpenInMemory(pszName, RTLDR_O_FOR_DEBUG, RTLDRARCH_WHATEVER, pRdr->cbImage,
525 dbgDiggerWinNtRdr_Read, dbgDiggerWinNtRdr_Dtor, pRdr,
526 &hLdrMod);
527 if (RT_SUCCESS(rc))
528 {
529 rc = RTDbgModCreateFromPeImage(&hMod, pszName, NULL, hLdrMod,
530 cbImageFromHdr, TimeDateStamp, DBGFR3AsGetConfig(pUVM));
531 if (RT_FAILURE(rc))
532 RTLdrClose(hLdrMod);
533 }
534 if (RT_FAILURE(rc))
535 {
536 /*
537 * Final fallback is a container module.
538 */
539 rc = RTDbgModCreate(&hMod, pszName, cbImage, 0);
540 if (RT_FAILURE(rc))
541 return;
542
543 rc = RTDbgModSymbolAdd(hMod, "Headers", 0 /*iSeg*/, 0, cbImage, 0 /*fFlags*/, NULL); AssertRC(rc);
544 }
545 }
546 rc = RTDbgModSetTag(hMod, DIG_WINNT_MOD_TAG); AssertRC(rc);
547
548#if 0
549 /*
550 * Dig out debug info if possible. What we're after is the CODEVIEW part.
551 */
552 /** @todo do we really need this? */
553 if (uRvaDebugDir != 0)
554 {
555 DBGFADDRESS Addr = *pImageAddr;
556 DBGFR3AddrAdd(&Addr, uRvaDebugDir);
557 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, pbBuf, RT_MIN(cbDebugDir, cbBuf));
558 if (RT_SUCCESS(rc))
559 {
560 IMAGE_DEBUG_DIRECTORY const *pa = (IMAGE_DEBUG_DIRECTORY const *)pbBuf;
561 size_t c = RT_MIN(RT_MIN(cbDebugDir, cbBuf) / sizeof(*pa), 10);
562 for (uint32_t i = 0; i < c; i++)
563 if ( pa[i].AddressOfRawData > offHdrs
564 && pa[i].AddressOfRawData < cbImage
565 && pa[i].SizeOfData < cbImage
566 && pa[i].AddressOfRawData + pa[i].SizeOfData <= cbImage
567 && pa[i].TimeDateStamp == TimeDateStamp /* too paranoid? */
568 && pa[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW
569 )
570 {
571 }
572 }
573 }
574#endif
575
576 /*
577 * Link the module.
578 */
579 RTDBGAS hAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
580 if (hAs != NIL_RTDBGAS)
581 rc = RTDbgAsModuleLink(hAs, hMod, pImageAddr->FlatPtr, RTDBGASLINK_FLAGS_REPLACE /*fFlags*/);
582 else
583 rc = VERR_INTERNAL_ERROR;
584 RTDbgModRelease(hMod);
585 RTDbgAsRelease(hAs);
586}
587
588
589/**
590 * @copydoc DBGFOSREG::pfnQueryInterface
591 */
592static DECLCALLBACK(void *) dbgDiggerWinNtQueryInterface(PUVM pUVM, void *pvData, DBGFOSINTERFACE enmIf)
593{
594 return NULL;
595}
596
597
598/**
599 * @copydoc DBGFOSREG::pfnQueryVersion
600 */
601static DECLCALLBACK(int) dbgDiggerWinNtQueryVersion(PUVM pUVM, void *pvData, char *pszVersion, size_t cchVersion)
602{
603 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
604 Assert(pThis->fValid);
605 const char *pszNtProductType;
606 switch (pThis->NtProductType)
607 {
608 case kNtProductType_WinNt: pszNtProductType = "-WinNT"; break;
609 case kNtProductType_LanManNt: pszNtProductType = "-LanManNT"; break;
610 case kNtProductType_Server: pszNtProductType = "-Server"; break;
611 default: pszNtProductType = ""; break;
612 }
613 RTStrPrintf(pszVersion, cchVersion, "%u.%u%s", pThis->NtMajorVersion, pThis->NtMinorVersion, pszNtProductType);
614 return VINF_SUCCESS;
615}
616
617
618/**
619 * @copydoc DBGFOSREG::pfnTerm
620 */
621static DECLCALLBACK(void) dbgDiggerWinNtTerm(PUVM pUVM, void *pvData)
622{
623 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
624 Assert(pThis->fValid);
625
626 pThis->fValid = false;
627}
628
629
630/**
631 * @copydoc DBGFOSREG::pfnRefresh
632 */
633static DECLCALLBACK(int) dbgDiggerWinNtRefresh(PUVM pUVM, void *pvData)
634{
635 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
636 NOREF(pThis);
637 Assert(pThis->fValid);
638
639 /*
640 * For now we'll flush and reload everything.
641 */
642 RTDBGAS hDbgAs = DBGFR3AsResolveAndRetain(pUVM, DBGF_AS_KERNEL);
643 if (hDbgAs != NIL_RTDBGAS)
644 {
645 uint32_t iMod = RTDbgAsModuleCount(hDbgAs);
646 while (iMod-- > 0)
647 {
648 RTDBGMOD hMod = RTDbgAsModuleByIndex(hDbgAs, iMod);
649 if (hMod != NIL_RTDBGMOD)
650 {
651 if (RTDbgModGetTag(hMod) == DIG_WINNT_MOD_TAG)
652 {
653 int rc = RTDbgAsModuleUnlink(hDbgAs, hMod);
654 AssertRC(rc);
655 }
656 RTDbgModRelease(hMod);
657 }
658 }
659 RTDbgAsRelease(hDbgAs);
660 }
661
662 dbgDiggerWinNtTerm(pUVM, pvData);
663 return dbgDiggerWinNtInit(pUVM, pvData);
664}
665
666
667/**
668 * @copydoc DBGFOSREG::pfnInit
669 */
670static DECLCALLBACK(int) dbgDiggerWinNtInit(PUVM pUVM, void *pvData)
671{
672 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
673 Assert(!pThis->fValid);
674
675 union
676 {
677 uint8_t au8[0x2000];
678 RTUTF16 wsz[0x2000/2];
679 NTKUSERSHAREDDATA UserSharedData;
680 } u;
681 DBGFADDRESS Addr;
682 int rc;
683
684 /*
685 * Figure the NT version.
686 */
687 DBGFR3AddrFromFlat(pUVM, &Addr, pThis->f32Bit ? NTKUSERSHAREDDATA_WINNT32 : NTKUSERSHAREDDATA_WINNT64);
688 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &u, PAGE_SIZE);
689 if (RT_FAILURE(rc))
690 return rc;
691 pThis->NtProductType = u.UserSharedData.ProductTypeIsValid && u.UserSharedData.NtProductType <= kNtProductType_Server
692 ? (NTPRODUCTTYPE)u.UserSharedData.NtProductType
693 : kNtProductType_Invalid;
694 pThis->NtMajorVersion = u.UserSharedData.NtMajorVersion;
695 pThis->NtMinorVersion = u.UserSharedData.NtMinorVersion;
696
697 /*
698 * Dig out the module chain.
699 */
700 DBGFADDRESS AddrPrev = pThis->PsLoadedModuleListAddr;
701 Addr = pThis->KernelMteAddr;
702 do
703 {
704 /* Read the validate the MTE. */
705 NTMTE Mte;
706 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &Addr, &Mte, pThis->f32Bit ? sizeof(Mte.vX_32) : sizeof(Mte.vX_64));
707 if (RT_FAILURE(rc))
708 break;
709 if (WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Blink) != AddrPrev.FlatPtr)
710 {
711 Log(("DigWinNt: Bad Mte At %RGv - backpointer\n", Addr.FlatPtr));
712 break;
713 }
714 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Flink)) )
715 {
716 Log(("DigWinNt: Bad Mte at %RGv - forward pointer\n", Addr.FlatPtr));
717 break;
718 }
719 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer)))
720 {
721 Log(("DigWinNt: Bad Mte at %RGv - BaseDllName=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer)));
722 break;
723 }
724 if (!WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, FullDllName.Buffer)))
725 {
726 Log(("DigWinNt: Bad Mte at %RGv - FullDllName=%llx\n", Addr.FlatPtr, WINNT_UNION(pThis, &Mte, FullDllName.Buffer)));
727 break;
728 }
729 if ( !WINNT_VALID_ADDRESS(pThis, WINNT_UNION(pThis, &Mte, DllBase))
730 || WINNT_UNION(pThis, &Mte, SizeOfImage) > _1M*256
731 || WINNT_UNION(pThis, &Mte, EntryPoint) - WINNT_UNION(pThis, &Mte, DllBase) > WINNT_UNION(pThis, &Mte, SizeOfImage) )
732 {
733 Log(("DigWinNt: Bad Mte at %RGv - EntryPoint=%llx SizeOfImage=%x DllBase=%llx\n",
734 Addr.FlatPtr, WINNT_UNION(pThis, &Mte, EntryPoint), WINNT_UNION(pThis, &Mte, SizeOfImage), WINNT_UNION(pThis, &Mte, DllBase)));
735 break;
736 }
737
738 /* Read the full name. */
739 DBGFADDRESS AddrName;
740 DBGFR3AddrFromFlat(pUVM, &AddrName, WINNT_UNION(pThis, &Mte, FullDllName.Buffer));
741 uint16_t cbName = WINNT_UNION(pThis, &Mte, FullDllName.Length);
742 if (cbName < sizeof(u))
743 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrName, &u, cbName);
744 else
745 rc = VERR_OUT_OF_RANGE;
746 if (RT_FAILURE(rc))
747 {
748 DBGFR3AddrFromFlat(pUVM, &AddrName, WINNT_UNION(pThis, &Mte, BaseDllName.Buffer));
749 cbName = WINNT_UNION(pThis, &Mte, BaseDllName.Length);
750 if (cbName < sizeof(u))
751 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &AddrName, &u, cbName);
752 else
753 rc = VERR_OUT_OF_RANGE;
754 }
755 if (RT_SUCCESS(rc))
756 {
757 u.wsz[cbName/2] = '\0';
758 char *pszName;
759 rc = RTUtf16ToUtf8(u.wsz, &pszName);
760 if (RT_SUCCESS(rc))
761 {
762 /* Read the start of the PE image and pass it along to a worker. */
763 DBGFADDRESS ImageAddr;
764 DBGFR3AddrFromFlat(pUVM, &ImageAddr, WINNT_UNION(pThis, &Mte, DllBase));
765 uint32_t cbImageBuf = RT_MIN(sizeof(u), WINNT_UNION(pThis, &Mte, SizeOfImage));
766 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &ImageAddr, &u, cbImageBuf);
767 if (RT_SUCCESS(rc))
768 dbgDiggerWinNtProcessImage(pThis,
769 pUVM,
770 pszName,
771 &ImageAddr,
772 WINNT_UNION(pThis, &Mte, SizeOfImage),
773 &u.au8[0],
774 sizeof(u));
775 RTStrFree(pszName);
776 }
777 }
778
779 /* next */
780 AddrPrev = Addr;
781 DBGFR3AddrFromFlat(pUVM, &Addr, WINNT_UNION(pThis, &Mte, InLoadOrderLinks.Flink));
782 } while ( Addr.FlatPtr != pThis->KernelMteAddr.FlatPtr
783 && Addr.FlatPtr != pThis->PsLoadedModuleListAddr.FlatPtr);
784
785 pThis->fValid = true;
786 return VINF_SUCCESS;
787}
788
789
790/**
791 * @copydoc DBGFOSREG::pfnProbe
792 */
793static DECLCALLBACK(bool) dbgDiggerWinNtProbe(PUVM pUVM, void *pvData)
794{
795 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
796 DBGFADDRESS Addr;
797 union
798 {
799 uint8_t au8[8192];
800 uint16_t au16[8192/2];
801 uint32_t au32[8192/4];
802 IMAGE_DOS_HEADER MzHdr;
803 RTUTF16 wsz[8192/2];
804 } u;
805
806 /*
807 * Look for the PAGELK section name that seems to be a part of all kernels.
808 * Then try find the module table entry for it. Since it's the first entry
809 * in the PsLoadedModuleList we can easily validate the list head and report
810 * success.
811 */
812 CPUMMODE enmMode = DBGFR3CpuGetMode(pUVM, 0 /*idCpu*/);
813 if (enmMode == CPUMMODE_LONG)
814 {
815 /** @todo when 32-bit is working, add support for 64-bit windows nt. */
816 }
817 else
818 {
819 DBGFADDRESS KernelAddr;
820 for (DBGFR3AddrFromFlat(pUVM, &KernelAddr, UINT32_C(0x80001000));
821 KernelAddr.FlatPtr < UINT32_C(0xffff0000);
822 KernelAddr.FlatPtr += PAGE_SIZE)
823 {
824 int rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, UINT32_C(0xffff0000) - KernelAddr.FlatPtr,
825 1, "PAGELK\0", sizeof("PAGELK\0"), &KernelAddr);
826 if (RT_FAILURE(rc))
827 break;
828 DBGFR3AddrSub(&KernelAddr, KernelAddr.FlatPtr & PAGE_OFFSET_MASK);
829
830 /* MZ + PE header. */
831 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, &KernelAddr, &u, sizeof(u));
832 if ( RT_SUCCESS(rc)
833 && u.MzHdr.e_magic == IMAGE_DOS_SIGNATURE
834 && !(u.MzHdr.e_lfanew & 0x7)
835 && u.MzHdr.e_lfanew >= 0x080
836 && u.MzHdr.e_lfanew <= 0x200)
837 {
838 IMAGE_NT_HEADERS32 const *pHdrs = (IMAGE_NT_HEADERS32 const *)&u.au8[u.MzHdr.e_lfanew];
839 if ( pHdrs->Signature == IMAGE_NT_SIGNATURE
840 && pHdrs->FileHeader.Machine == IMAGE_FILE_MACHINE_I386
841 && pHdrs->FileHeader.SizeOfOptionalHeader == sizeof(pHdrs->OptionalHeader)
842 && pHdrs->FileHeader.NumberOfSections >= 10 /* the kernel has lots */
843 && (pHdrs->FileHeader.Characteristics & (IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL)) == IMAGE_FILE_EXECUTABLE_IMAGE
844 && pHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC
845 && pHdrs->OptionalHeader.NumberOfRvaAndSizes == IMAGE_NUMBEROF_DIRECTORY_ENTRIES
846 /** @todo need more ntoskrnl signs? */
847 )
848 {
849 /* Find the MTE. */
850 NTMTE32 Mte;
851 RT_ZERO(Mte);
852 Mte.DllBase = KernelAddr.FlatPtr;
853 Mte.EntryPoint = KernelAddr.FlatPtr + pHdrs->OptionalHeader.AddressOfEntryPoint;
854 Mte.SizeOfImage = pHdrs->OptionalHeader.SizeOfImage;
855 DBGFADDRESS HitAddr;
856 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &KernelAddr, UINT32_MAX - KernelAddr.FlatPtr,
857 4 /*align*/, &Mte.DllBase, 3 * sizeof(uint32_t), &HitAddr);
858 while (RT_SUCCESS(rc))
859 {
860 /* check the name. */
861 NTMTE32 Mte2;
862 DBGFADDRESS MteAddr = HitAddr;
863 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrSub(&MteAddr, RT_OFFSETOF(NTMTE32, DllBase)),
864 &Mte2, sizeof(Mte2));
865 if ( RT_SUCCESS(rc)
866 && Mte2.DllBase == Mte.DllBase
867 && Mte2.EntryPoint == Mte.EntryPoint
868 && Mte2.SizeOfImage == Mte.SizeOfImage
869 && WINNT32_VALID_ADDRESS(Mte2.InLoadOrderLinks.Flink)
870 && Mte2.InLoadOrderLinks.Blink > KernelAddr.FlatPtr /* list head inside ntoskrnl */
871 && Mte2.InLoadOrderLinks.Blink < KernelAddr.FlatPtr + Mte.SizeOfImage
872 && WINNT32_VALID_ADDRESS(Mte2.BaseDllName.Buffer)
873 && WINNT32_VALID_ADDRESS(Mte2.FullDllName.Buffer)
874 && Mte2.BaseDllName.Length <= Mte2.BaseDllName.MaximumLength
875 && Mte2.BaseDllName.Length == WINNT_KERNEL_BASE_NAME_LEN * 2
876 && Mte2.FullDllName.Length <= Mte2.FullDllName.MaximumLength
877 && Mte2.FullDllName.Length <= 256
878 )
879 {
880 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, Mte2.BaseDllName.Buffer),
881 u.wsz, Mte2.BaseDllName.Length);
882 u.wsz[Mte2.BaseDllName.Length / 2] = '\0';
883 if ( RT_SUCCESS(rc)
884 && ( !RTUtf16ICmp(u.wsz, g_wszKernelNames[0])
885 /* || !RTUtf16ICmp(u.wsz, g_wszKernelNames[1]) */
886 )
887 )
888 {
889 NTMTE32 Mte3;
890 rc = DBGFR3MemRead(pUVM, 0 /*idCpu*/, DBGFR3AddrFromFlat(pUVM, &Addr, Mte2.InLoadOrderLinks.Blink),
891 &Mte3, RT_SIZEOFMEMB(NTMTE32, InLoadOrderLinks));
892 if ( RT_SUCCESS(rc)
893 && Mte3.InLoadOrderLinks.Flink == MteAddr.FlatPtr
894 && WINNT32_VALID_ADDRESS(Mte3.InLoadOrderLinks.Blink) )
895 {
896 Log(("DigWinNt: MteAddr=%RGv KernelAddr=%RGv SizeOfImage=%x &PsLoadedModuleList=%RGv (32-bit)\n",
897 MteAddr.FlatPtr, KernelAddr.FlatPtr, Mte2.SizeOfImage, Addr.FlatPtr));
898 pThis->KernelAddr = KernelAddr;
899 pThis->KernelMteAddr = MteAddr;
900 pThis->PsLoadedModuleListAddr = Addr;
901 pThis->f32Bit = true;
902 return true;
903 }
904 }
905 }
906
907 /* next */
908 DBGFR3AddrAdd(&HitAddr, 4);
909 if (HitAddr.FlatPtr <= UINT32_C(0xfffff000))
910 rc = DBGFR3MemScan(pUVM, 0 /*idCpu*/, &HitAddr, UINT32_MAX - HitAddr.FlatPtr,
911 4 /*align*/, &Mte.DllBase, 3 * sizeof(uint32_t), &HitAddr);
912 else
913 rc = VERR_DBGF_MEM_NOT_FOUND;
914 }
915 }
916 }
917 }
918 }
919 return false;
920}
921
922
923/**
924 * @copydoc DBGFOSREG::pfnDestruct
925 */
926static DECLCALLBACK(void) dbgDiggerWinNtDestruct(PUVM pUVM, void *pvData)
927{
928
929}
930
931
932/**
933 * @copydoc DBGFOSREG::pfnConstruct
934 */
935static DECLCALLBACK(int) dbgDiggerWinNtConstruct(PUVM pUVM, void *pvData)
936{
937 PDBGDIGGERWINNT pThis = (PDBGDIGGERWINNT)pvData;
938 pThis->fValid = false;
939 pThis->f32Bit = false;
940 pThis->enmVer = DBGDIGGERWINNTVER_UNKNOWN;
941 return VINF_SUCCESS;
942}
943
944
945const DBGFOSREG g_DBGDiggerWinNt =
946{
947 /* .u32Magic = */ DBGFOSREG_MAGIC,
948 /* .fFlags = */ 0,
949 /* .cbData = */ sizeof(DBGDIGGERWINNT),
950 /* .szName = */ "WinNT",
951 /* .pfnConstruct = */ dbgDiggerWinNtConstruct,
952 /* .pfnDestruct = */ dbgDiggerWinNtDestruct,
953 /* .pfnProbe = */ dbgDiggerWinNtProbe,
954 /* .pfnInit = */ dbgDiggerWinNtInit,
955 /* .pfnRefresh = */ dbgDiggerWinNtRefresh,
956 /* .pfnTerm = */ dbgDiggerWinNtTerm,
957 /* .pfnQueryVersion = */ dbgDiggerWinNtQueryVersion,
958 /* .pfnQueryInterface = */ dbgDiggerWinNtQueryInterface,
959 /* .u32EndMagic = */ DBGFOSREG_MAGIC
960};
961
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