VirtualBox

source: vbox/trunk/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp

Last change on this file was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 32.3 KB
Line 
1/* $Id: nt3fakes-r0drv-nt.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - NT 3.x fakes for NT 4.0 KPIs.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define _IMAGE_NT_HEADERS RT_CONCAT(_IMAGE_NT_HEADERS,ARCH_BITS)
42#include "the-nt-kernel.h"
43#include <iprt/mem.h>
44
45#include <iprt/assert.h>
46#include <iprt/asm.h>
47#include <iprt/ctype.h>
48#include <iprt/dbg.h>
49#include <iprt/err.h>
50#include <iprt/log.h>
51#include <iprt/string.h>
52#include <iprt/utf16.h>
53#include <iprt/x86.h>
54#include <iprt/formats/mz.h>
55#include <iprt/formats/pecoff.h>
56#include "internal-r0drv-nt.h"
57
58typedef uint32_t DWORD;
59#include <VerRsrc.h>
60
61
62/*********************************************************************************************************************************
63* Internal Functions *
64*********************************************************************************************************************************/
65DECLASM(void) rtNt3InitSymbolsAssembly(void); /* in nt3fakesA-r0drv-nt.asm */
66
67
68/*********************************************************************************************************************************
69* Global Variables *
70*********************************************************************************************************************************/
71static uint32_t g_uNt3MajorVer = 3;
72static uint32_t g_uNt3MinorVer = 51;
73static uint32_t g_uNt3BuildNo = 1057;
74static bool g_fNt3Checked = false;
75static bool g_fNt3Smp = false; /**< Not reliable. */
76static bool volatile g_fNt3VersionInitialized = false;
77
78static uint8_t *g_pbNt3OsKrnl = (uint8_t *)UINT32_C(0x80100000);
79static uint32_t g_cbNt3OsKrnl = 0x300000;
80static uint8_t *g_pbNt3Hal = (uint8_t *)UINT32_C(0x80400000);
81static uint32_t g_cbNt3Hal = _512K;
82static bool volatile g_fNt3ModuleInfoInitialized = false;
83
84
85RT_C_DECLS_BEGIN
86/** @name KPIs we provide fallback implementations for.
87 *
88 * The assembly init routine will point the __imp_xxx variable to the NT
89 * implementation if available, using the fallback if not.
90 * @{ */
91decltype(PsGetVersion) *g_pfnrtPsGetVersion;
92decltype(ZwQuerySystemInformation) *g_pfnrtZwQuerySystemInformation;
93decltype(KeSetTimerEx) *g_pfnrtKeSetTimerEx;
94decltype(IoAttachDeviceToDeviceStack) *g_pfnrtIoAttachDeviceToDeviceStack;
95decltype(PsGetCurrentProcessId) *g_pfnrtPsGetCurrentProcessId;
96decltype(ZwYieldExecution) *g_pfnrtZwYieldExecution;
97decltype(ExAcquireFastMutex) *g_pfnrtExAcquireFastMutex;
98decltype(ExReleaseFastMutex) *g_pfnrtExReleaseFastMutex;
99/** @} */
100
101/** @name Fastcall optimizations not present in NT 3.1.
102 *
103 * We try resolve both the stdcall and fastcall variants and patch it up in
104 * assembly. The last four routines are in the hal.
105 *
106 * @{ */
107decltype(IofCompleteRequest) *g_pfnrtIofCompleteRequest;
108decltype(ObfDereferenceObject) *g_pfnrtObfDereferenceObject;
109decltype(IofCallDriver) *g_pfnrtIofCallDriver;
110decltype(KfAcquireSpinLock) *g_pfnrtKfAcquireSpinLock;
111decltype(KfReleaseSpinLock) *g_pfnrtKfReleaseSpinLock;
112decltype(KefAcquireSpinLockAtDpcLevel) *g_pfnrtKefAcquireSpinLockAtDpcLevel;
113decltype(KefReleaseSpinLockFromDpcLevel) *g_pfnrtKefReleaseSpinLockFromDpcLevel;
114decltype(KfLowerIrql) *g_pfnrtKfLowerIrql;
115decltype(KfRaiseIrql) *g_pfnrtKfRaiseIrql;
116
117VOID (__stdcall *g_pfnrtIoCompleteRequest)(PIRP, CCHAR);
118LONG_PTR (__stdcall *g_pfnrtObDereferenceObject)(PVOID);
119NTSTATUS (__stdcall *g_pfnrtIoCallDriver)(PDEVICE_OBJECT, PIRP);
120KIRQL (__stdcall *g_pfnrtKeAcquireSpinLock)(PKSPIN_LOCK);
121VOID (__stdcall *g_pfnrtKeReleaseSpinLock)(PKSPIN_LOCK, KIRQL);
122KIRQL (__stdcall *g_pfnrtKeAcquireSpinLockAtDpcLevel)(PKSPIN_LOCK);
123VOID (__stdcall *g_pfnrtKeReleaseSpinLockFromDpcLevel)(PKSPIN_LOCK);
124VOID (__stdcall *g_pfnrtKeLowerIrql)(KIRQL);
125KIRQL (__stdcall *g_pfnrtKeRaiseIrql)(KIRQL);
126/** @} */
127
128/** @name DATA exports and associated stuff
129 * @{ */
130/** Import address table entry for KeTickCount (defined in asm). */
131extern KSYSTEM_TIME *_imp__KeTickCount;
132/** @} */
133
134RT_C_DECLS_END
135
136
137/*********************************************************************************************************************************
138* Internal Functions *
139*********************************************************************************************************************************/
140static void rtR0Nt3InitModuleInfo(void);
141
142
143/**
144 * Converts a string to a number, stopping at the first non-digit.
145 *
146 * @returns The value
147 * @param ppwcValue Pointer to the string pointer variable. Updated.
148 * @param pcwcValue Pointer to the string length variable. Updated.
149 */
150static uint32_t rtR0Nt3StringToNum(PCRTUTF16 *ppwcValue, size_t *pcwcValue)
151{
152 uint32_t uValue = 0;
153 PCRTUTF16 pwcValue = *ppwcValue;
154 size_t cwcValue = *pcwcValue;
155
156 while (cwcValue > 0)
157 {
158 RTUTF16 uc = *pwcValue;
159 unsigned uDigit = (unsigned)uc - (unsigned)'0';
160 if (uDigit < (unsigned)10)
161 {
162 uValue *= 10;
163 uValue += uDigit;
164 }
165 else
166 break;
167 pwcValue++;
168 cwcValue--;
169 }
170
171 *ppwcValue = pwcValue;
172 *pcwcValue = cwcValue;
173 return uValue;
174}
175
176
177/**
178 * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
179 * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentVersion'
180 */
181static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentVersion(PWSTR pwszValueName, ULONG uValueType,
182 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
183{
184 RT_NOREF(pwszValueName, pvEntryCtx);
185 if ( uValueType == REG_SZ
186 || uValueType == REG_EXPAND_SZ)
187 {
188 PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
189 size_t cwcValue = cbValue / sizeof(*pwcValue);
190 uint32_t uMajor = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
191 uint32_t uMinor = 0;
192 if (cwcValue > 1)
193 {
194 pwcValue++;
195 cwcValue--;
196 uMinor = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
197 }
198
199 if (uMajor >= 3)
200 {
201 g_uNt3MajorVer = uMajor;
202 g_uNt3MinorVer = uMinor;
203 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion found: uMajor=%u uMinor=%u\n", uMajor, uMinor);
204 *(uint32_t *)pvUser |= RT_BIT_32(0);
205 return STATUS_SUCCESS;
206 }
207
208 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
209 }
210 else
211 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
212 return STATUS_SUCCESS;
213}
214
215
216/**
217 * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
218 * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentBuildNumber'
219 */
220static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentBuildNumber(PWSTR pwszValueName, ULONG uValueType,
221 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
222{
223 RT_NOREF(pwszValueName, pvEntryCtx);
224 if ( uValueType == REG_SZ
225 || uValueType == REG_EXPAND_SZ)
226 {
227 PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
228 size_t cwcValue = cbValue / sizeof(*pwcValue);
229 uint32_t uBuildNo = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
230
231 if (uBuildNo >= 100 && uBuildNo < _1M)
232 {
233 g_uNt3BuildNo = uBuildNo;
234 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber found: uBuildNo=%u\n", uBuildNo);
235 *(uint32_t *)pvUser |= RT_BIT_32(1);
236 return STATUS_SUCCESS;
237 }
238
239 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
240 }
241 else
242 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
243 return STATUS_SUCCESS;
244}
245
246
247/**
248 * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
249 * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentType'
250 */
251static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentType(PWSTR pwszValueName, ULONG uValueType,
252 PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
253{
254 RT_NOREF(pwszValueName, pvEntryCtx);
255 if ( uValueType == REG_SZ
256 || uValueType == REG_EXPAND_SZ)
257 {
258 PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
259 size_t cwcValue = cbValue / sizeof(*pwcValue);
260
261 int fSmp = -1;
262 if (cwcValue >= 12 && RTUtf16NICmpAscii(pwcValue, "Uniprocessor", 12) == 0)
263 {
264 cwcValue -= 12;
265 pwcValue += 12;
266 fSmp = 0;
267 }
268 else if (cwcValue >= 14 && RTUtf16NICmpAscii(pwcValue, "Multiprocessor", 14) == 0)
269 {
270 cwcValue -= 14;
271 pwcValue += 14;
272 fSmp = 1;
273 }
274 if (fSmp != -1)
275 {
276 while (cwcValue > 0 && RT_C_IS_SPACE(*pwcValue))
277 cwcValue--, pwcValue++;
278
279 int fChecked = -1;
280 if (cwcValue >= 4 && RTUtf16NICmpAscii(pwcValue, "Free", 4) == 0)
281 fChecked = 0;
282 else if (cwcValue >= 7 && RTUtf16NICmpAscii(pwcValue, "Checked", 7) == 0)
283 fChecked = 1;
284 if (fChecked != -1)
285 {
286 g_fNt3Smp = fSmp != 0;
287 g_fNt3Checked = fChecked != 0;
288 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType found: fSmp=%d fChecked=%d\n", fSmp, fChecked);
289 *(uint32_t *)pvUser |= RT_BIT_32(2);
290 return STATUS_SUCCESS;
291 }
292 }
293
294 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
295 }
296 else
297 RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
298 return STATUS_SUCCESS;
299}
300
301
302/**
303 * Figure out the NT 3 version from the registry.
304 *
305 * @note this will be called before the rtR0Nt3InitSymbols is called.
306 */
307static void rtR0Nt3InitVersion(void)
308{
309 /*
310 * No PsGetVersion, so try the registry. Unfortunately not necessarily
311 * initialized when we're loaded.
312 */
313 RTL_QUERY_REGISTRY_TABLE aQuery[4];
314 RT_ZERO(aQuery);
315 aQuery[0].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentVersion;
316 aQuery[0].Flags = 0;
317 aQuery[0].Name = L"CurrentVersion";
318 aQuery[0].EntryContext = NULL;
319 aQuery[0].DefaultType = REG_NONE;
320
321 aQuery[1].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentBuildNumber;
322 aQuery[1].Flags = 0;
323 aQuery[1].Name = L"CurrentBuildNumber";
324 aQuery[1].EntryContext = NULL;
325 aQuery[1].DefaultType = REG_NONE;
326
327 aQuery[2].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentType;
328 aQuery[2].Flags = 0;
329 aQuery[2].Name = L"CurrentType";
330 aQuery[2].EntryContext = NULL;
331 aQuery[2].DefaultType = REG_NONE;
332
333 uint32_t fFound = 0;
334 //NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT, NULL, &aQuery[0], &fFound, NULL /*Environment*/);
335 NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
336 L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
337 &aQuery[0], &fFound, NULL /*Environment*/);
338 if (!NT_SUCCESS(rcNt))
339 RTLogBackdoorPrintf("rtR0Nt3InitVersion: RtlQueryRegistryValues failed: %#x\n", rcNt);
340 else
341 RTLogBackdoorPrintf("rtR0Nt3InitVersion: Didn't get all values: fFound=%#x\n", fFound);
342
343 /*
344 * We really need the version number. Build, type and SMP is off less importance.
345 * Derive it from the NT kernel PE header.
346 */
347 if (!(fFound & RT_BIT_32(0)))
348 {
349 if (!g_fNt3ModuleInfoInitialized)
350 rtR0Nt3InitModuleInfo();
351
352 PIMAGE_DOS_HEADER pMzHdr = (PIMAGE_DOS_HEADER)g_pbNt3OsKrnl;
353 PIMAGE_NT_HEADERS32 pNtHdrs = (PIMAGE_NT_HEADERS32)&g_pbNt3OsKrnl[pMzHdr->e_lfanew];
354 if (pNtHdrs->OptionalHeader.MajorOperatingSystemVersion == 1)
355 {
356 /* NT 3.1 and NT 3.50 both set OS version to 1.0 in the optional header. */
357 g_uNt3MajorVer = 3;
358 if ( pNtHdrs->OptionalHeader.MajorLinkerVersion == 2
359 && pNtHdrs->OptionalHeader.MinorLinkerVersion < 50)
360 g_uNt3MinorVer = 10;
361 else
362 g_uNt3MinorVer = 50;
363 }
364 else
365 {
366 g_uNt3MajorVer = pNtHdrs->OptionalHeader.MajorOperatingSystemVersion;
367 g_uNt3MinorVer = pNtHdrs->OptionalHeader.MinorOperatingSystemVersion;
368 }
369 RTLogBackdoorPrintf("rtR0Nt3InitVersion: guessed %u.%u from PE header\n", g_uNt3MajorVer, g_uNt3MinorVer);
370
371 /* Check out the resource section, looking for VS_FIXEDFILEINFO. */
372 __try /* (pointless) */
373 {
374 PIMAGE_SECTION_HEADER paShdrs = (PIMAGE_SECTION_HEADER)(pNtHdrs + 1);
375 uint32_t const cShdrs = pNtHdrs->FileHeader.NumberOfSections;
376 uint32_t iShdr = 0;
377 while (iShdr < cShdrs && memcmp(paShdrs[iShdr].Name, ".rsrc", 6) != 0)
378 iShdr++;
379 if (iShdr < cShdrs)
380 {
381 if ( paShdrs[iShdr].VirtualAddress > 0
382 && paShdrs[iShdr].VirtualAddress < pNtHdrs->OptionalHeader.SizeOfImage)
383 {
384 uint32_t const cbRsrc = RT_MIN(paShdrs[iShdr].Misc.VirtualSize
385 ? paShdrs[iShdr].Misc.VirtualSize : paShdrs[iShdr].SizeOfRawData,
386 pNtHdrs->OptionalHeader.SizeOfImage - paShdrs[iShdr].VirtualAddress);
387 uint8_t const *pbRsrc = &g_pbNt3OsKrnl[paShdrs[iShdr].VirtualAddress];
388 uint32_t const *puDwords = (uint32_t const *)pbRsrc;
389 uint32_t cDWords = (cbRsrc - sizeof(VS_FIXEDFILEINFO) + sizeof(uint32_t)) / sizeof(uint32_t);
390 while (cDWords-- > 0)
391 {
392 if ( puDwords[0] == VS_FFI_SIGNATURE
393 && puDwords[1] == VS_FFI_STRUCVERSION)
394 {
395 VS_FIXEDFILEINFO const *pVerInfo = (VS_FIXEDFILEINFO const *)puDwords;
396 g_uNt3MajorVer = pVerInfo->dwProductVersionMS >> 16;
397 g_uNt3MinorVer = pVerInfo->dwProductVersionMS >> 16;
398 g_uNt3BuildNo = pVerInfo->dwProductVersionLS >> 16;
399 RTLogBackdoorPrintf("rtR0Nt3InitVersion: Found version info %u.%u build %u\n",
400 g_uNt3MajorVer, g_uNt3MinorVer, g_uNt3BuildNo);
401 break;
402 }
403 puDwords++;
404 }
405 }
406 }
407 }
408 __except(EXCEPTION_EXECUTE_HANDLER)
409 {
410 RTLogBackdoorPrintf("rtR0Nt3InitVersion: Exception scanning .rsrc section for version info!\n");
411 }
412 }
413
414 /*
415 * If we've got PsGetVersion, use it to override the above finding!
416 * (We may end up here for reasons other than the PsGetVersion fallback.)
417 */
418 if (g_pfnrtPsGetVersion)
419 {
420 WCHAR wszCsd[64];
421 UNICODE_STRING UniStr;
422 UniStr.Buffer = wszCsd;
423 UniStr.MaximumLength = sizeof(wszCsd) - sizeof(WCHAR);
424 UniStr.Length = 0;
425 RT_ZERO(wszCsd);
426 ULONG uMajor = 3;
427 ULONG uMinor = 51;
428 ULONG uBuildNo = 1057;
429 BOOLEAN fChecked = g_pfnrtPsGetVersion(&uMajor, &uMinor, &uBuildNo, &UniStr);
430
431 g_uNt3MajorVer = uMajor;
432 g_uNt3MinorVer = uMinor;
433 g_uNt3BuildNo = uBuildNo;
434 g_fNt3Checked = fChecked != FALSE;
435 }
436
437 g_fNt3VersionInitialized = true;
438}
439
440
441extern "C" DECLEXPORT(BOOLEAN) __stdcall
442Nt3Fb_PsGetVersion(ULONG *puMajor, ULONG *puMinor, ULONG *puBuildNo, UNICODE_STRING *pCsdStr)
443{
444 if (!g_fNt3VersionInitialized)
445 rtR0Nt3InitVersion();
446 if (puMajor)
447 *puMajor = g_uNt3MajorVer;
448 if (puMinor)
449 *puMinor = g_uNt3MinorVer;
450 if (puBuildNo)
451 *puBuildNo = g_uNt3BuildNo;
452 if (pCsdStr)
453 {
454 pCsdStr->Buffer[0] = '\0';
455 pCsdStr->Length = 0;
456 }
457 return g_fNt3Checked;
458}
459
460
461/**
462 * Worker for rtR0Nt3InitModuleInfo.
463 */
464static bool rtR0Nt3InitModuleInfoOne(const char *pszImage, uint8_t const *pbCode, uint8_t **ppbModule, uint32_t *pcbModule)
465{
466 uintptr_t const uImageAlign = _4K; /* XP may put the kernel at */
467
468 /* Align pbCode. */
469 pbCode = (uint8_t const *)((uintptr_t)pbCode & ~(uintptr_t)(uImageAlign - 1));
470
471 /* Scan backwards till we find a PE signature. */
472 for (uint32_t cbChecked = 0; cbChecked < _64M; cbChecked += uImageAlign, pbCode -= uImageAlign)
473 {
474 if (!MmIsAddressValid((void *)pbCode))
475 continue;
476
477 uint32_t uZero = 0;
478 uint32_t offNewHdr = 0;
479 __try /* pointless */
480 {
481 uZero = *(uint32_t const *)pbCode;
482 offNewHdr = *(uint32_t const *)&pbCode[RT_UOFFSETOF(IMAGE_DOS_HEADER, e_lfanew)];
483 }
484 __except(EXCEPTION_EXECUTE_HANDLER)
485 {
486 RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Exception at %p scanning for DOS header...\n", pbCode);
487 continue;
488 }
489 if ( (uint16_t)uZero == IMAGE_DOS_SIGNATURE
490 && offNewHdr < _2K
491 && offNewHdr >= sizeof(IMAGE_DOS_HEADER))
492 {
493 RT_CONCAT(IMAGE_NT_HEADERS,ARCH_BITS) NtHdrs;
494 __try /* pointless */
495 {
496 NtHdrs = *(decltype(NtHdrs) const *)&pbCode[offNewHdr];
497 }
498 __except(EXCEPTION_EXECUTE_HANDLER)
499 {
500 RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Exception at %p reading NT headers...\n", pbCode);
501 continue;
502 }
503 if ( NtHdrs.Signature == IMAGE_NT_SIGNATURE
504 && NtHdrs.FileHeader.SizeOfOptionalHeader == sizeof(NtHdrs.OptionalHeader)
505 && NtHdrs.FileHeader.NumberOfSections > 2
506 && NtHdrs.FileHeader.NumberOfSections < _4K
507 && NtHdrs.OptionalHeader.Magic == RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
508 {
509 *ppbModule = (uint8_t *)pbCode;
510 *pcbModule = NtHdrs.OptionalHeader.SizeOfImage;
511 RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Found %s at %#p LB %#x\n",
512 pszImage, pbCode, NtHdrs.OptionalHeader.SizeOfImage);
513 return true;
514 }
515 }
516 }
517 RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Warning! Unable to locate %s...\n");
518 return false;
519}
520
521
522/**
523 * Initializes the module information (NTOSKRNL + HAL) using exported symbols.
524 * This only works as long as noone is intercepting the symbols.
525 */
526static void rtR0Nt3InitModuleInfo(void)
527{
528 rtR0Nt3InitModuleInfoOne("ntoskrnl.exe", (uint8_t const *)(uintptr_t)IoGetCurrentProcess, &g_pbNt3OsKrnl, &g_cbNt3OsKrnl);
529 rtR0Nt3InitModuleInfoOne("hal.dll", (uint8_t const *)(uintptr_t)HalGetBusData, &g_pbNt3Hal, &g_cbNt3Hal);
530 g_fNt3ModuleInfoInitialized = true;
531}
532
533
534extern "C" DECLEXPORT(NTSTATUS) __stdcall
535Nt3Fb_ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS enmClass, PVOID pvBuf, ULONG cbBuf, PULONG pcbActual)
536{
537 switch (enmClass)
538 {
539 case SystemModuleInformation:
540 {
541 PRTL_PROCESS_MODULES pInfo = (PRTL_PROCESS_MODULES)pvBuf;
542 ULONG cbNeeded = RT_UOFFSETOF(RTL_PROCESS_MODULES, Modules[2]);
543 if (pcbActual)
544 *pcbActual = cbNeeded;
545 if (cbBuf < cbNeeded)
546 return STATUS_INFO_LENGTH_MISMATCH;
547
548 if (!g_fNt3ModuleInfoInitialized)
549 rtR0Nt3InitModuleInfo();
550
551 pInfo->NumberOfModules = 2;
552
553 /* ntoskrnl.exe */
554 pInfo->Modules[0].Section = NULL;
555 pInfo->Modules[0].MappedBase = g_pbNt3OsKrnl;
556 pInfo->Modules[0].ImageBase = g_pbNt3OsKrnl;
557 pInfo->Modules[0].ImageSize = g_cbNt3OsKrnl;
558 pInfo->Modules[0].Flags = 0;
559 pInfo->Modules[0].LoadOrderIndex = 0;
560 pInfo->Modules[0].InitOrderIndex = 0;
561 pInfo->Modules[0].LoadCount = 1024;
562 pInfo->Modules[0].OffsetToFileName = sizeof("\\SystemRoot\\System32\\") - 1;
563 memcpy(pInfo->Modules[0].FullPathName, RT_STR_TUPLE("\\SystemRoot\\System32\\ntoskrnl.exe"));
564
565 /* hal.dll */
566 pInfo->Modules[1].Section = NULL;
567 pInfo->Modules[1].MappedBase = g_pbNt3Hal;
568 pInfo->Modules[1].ImageBase = g_pbNt3Hal;
569 pInfo->Modules[1].ImageSize = g_cbNt3Hal;
570 pInfo->Modules[1].Flags = 0;
571 pInfo->Modules[1].LoadOrderIndex = 1;
572 pInfo->Modules[1].InitOrderIndex = 0;
573 pInfo->Modules[1].LoadCount = 1024;
574 pInfo->Modules[1].OffsetToFileName = sizeof("\\SystemRoot\\System32\\") - 1;
575 memcpy(pInfo->Modules[1].FullPathName, RT_STR_TUPLE("\\SystemRoot\\System32\\hal.dll"));
576
577 return STATUS_SUCCESS;
578 }
579
580 default:
581 return STATUS_INVALID_INFO_CLASS;
582 }
583}
584
585/**
586 * Calculates the length indicated by an ModR/M sequence.
587 *
588 * @returns Length, including RM byte.
589 * @param bRm The RM byte.
590 */
591static uint32_t rtR0Nt3CalcModRmLength(uint8_t bRm)
592{
593 uint32_t cbRm = 1;
594
595 if ( (bRm & X86_MODRM_MOD_MASK) == (3 << X86_MODRM_MOD_SHIFT)
596 || (bRm & (X86_MODRM_MOD_MASK | X86_MODRM_RM_MASK)) == 5)
597 cbRm += 4; /* disp32 */
598 else if ((bRm & X86_MODRM_MOD_MASK) == (1 << X86_MODRM_MOD_SHIFT))
599 cbRm += 1; /* disp8 */
600 else if ((bRm & X86_MODRM_MOD_MASK) == (2 << X86_MODRM_MOD_SHIFT))
601 cbRm += 2; /* disp16 */
602
603 if ((bRm & X86_MODRM_RM_MASK) == 4 && (bRm & X86_MODRM_MOD_MASK) != (3 << X86_MODRM_MOD_SHIFT))
604 cbRm += 1; /* SIB */
605
606 return cbRm;
607}
608
609
610/**
611 * Init symbols.
612 *
613 * This is called after both ZwQuerySystemInformation and PsGetVersion are used
614 * for the first time.
615 *
616 * @returns IPRT status code
617 * @param hKrnlInfo Kernel symbol digger handle.
618 */
619DECLHIDDEN(int) rtR0Nt3InitSymbols(RTDBGKRNLINFO hKrnlInfo)
620{
621 /*
622 * Resolve symbols. (We set C variables (g_pfnrtXxx) here, not the __imp__Xxx ones.)
623 */
624#define GET_SYSTEM_ROUTINE(a_fnName) do { \
625 RT_CONCAT(g_pfnrt, a_fnName) = (decltype(RT_CONCAT(g_pfnrt, a_fnName)))RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, #a_fnName); \
626 } while (0)
627
628 GET_SYSTEM_ROUTINE(PsGetVersion);
629 GET_SYSTEM_ROUTINE(ZwQuerySystemInformation);
630 GET_SYSTEM_ROUTINE(KeSetTimerEx);
631 GET_SYSTEM_ROUTINE(IoAttachDeviceToDeviceStack);
632 GET_SYSTEM_ROUTINE(PsGetCurrentProcessId);
633 GET_SYSTEM_ROUTINE(ZwYieldExecution);
634 GET_SYSTEM_ROUTINE(ExAcquireFastMutex);
635 GET_SYSTEM_ROUTINE(ExReleaseFastMutex);
636
637#define GET_FAST_CALL_SYSTEM_ROUTINE(a_fnFastcall, a_fnStdcall) do { \
638 GET_SYSTEM_ROUTINE(a_fnFastcall); \
639 GET_SYSTEM_ROUTINE(a_fnStdcall); \
640 AssertLogRelReturn(RT_CONCAT(g_pfnrt,a_fnFastcall) || RT_CONCAT(g_pfnrt,a_fnStdcall), VERR_INTERNAL_ERROR_3); \
641 } while (0)
642 GET_FAST_CALL_SYSTEM_ROUTINE(IofCompleteRequest, IoCompleteRequest);
643 GET_FAST_CALL_SYSTEM_ROUTINE(ObfDereferenceObject, ObDereferenceObject);
644 GET_FAST_CALL_SYSTEM_ROUTINE(IofCallDriver, IoCallDriver);
645 GET_FAST_CALL_SYSTEM_ROUTINE(KfAcquireSpinLock, KeAcquireSpinLock);
646 GET_FAST_CALL_SYSTEM_ROUTINE(KfReleaseSpinLock, KeReleaseSpinLock);
647 GET_FAST_CALL_SYSTEM_ROUTINE(KfLowerIrql, KeLowerIrql);
648 GET_FAST_CALL_SYSTEM_ROUTINE(KfRaiseIrql, KeRaiseIrql);
649 GET_FAST_CALL_SYSTEM_ROUTINE(KefAcquireSpinLockAtDpcLevel, KeAcquireSpinLockAtDpcLevel);
650 GET_FAST_CALL_SYSTEM_ROUTINE(KefReleaseSpinLockFromDpcLevel, KeReleaseSpinLockFromDpcLevel);
651
652 /*
653 * We need to call assembly to update the __imp__Xxx entries, since C
654 * doesn't allow '@' in symbols.
655 */
656 rtNt3InitSymbolsAssembly();
657
658 /*
659 * Tick count data. We disassemble KeQueryTickCount until we find the
660 * first absolute address referenced in it.
661 * %80105b70 8b 44 24 04 mov eax, dword [esp+004h]
662 * %80105b74 c7 40 04 00 00 00 00 mov dword [eax+004h], 000000000h
663 * %80105b7b 8b 0d 88 70 19 80 mov ecx, dword [080197088h]
664 * %80105b81 89 08 mov dword [eax], ecx
665 * %80105b83 c2 04 00 retn 00004h
666 */
667 _imp__KeTickCount = (decltype(_imp__KeTickCount))RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeTickCount");
668 if (!_imp__KeTickCount)
669 {
670 if (!g_fNt3VersionInitialized)
671 rtR0Nt3InitVersion();
672 Assert(g_uNt3MajorVer == 3 && g_uNt3MinorVer < 50);
673
674 uint8_t const *pbCode = (uint8_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeQueryTickCount");
675 AssertLogRelReturn(pbCode, VERR_INTERNAL_ERROR_2);
676
677 for (uint32_t off = 0; off < 128 && _imp__KeTickCount == NULL;)
678 {
679 uint8_t const b1 = pbCode[off++];
680 switch (b1)
681 {
682 case 0x8b: /* mov reg, r/m ; We're looking for absolute address in r/m. */
683 if ((pbCode[off] & (X86_MODRM_MOD_MASK | X86_MODRM_RM_MASK)) == 5 /*disp32*/)
684 _imp__KeTickCount = *(KSYSTEM_TIME **)&pbCode[off + 1];
685 RT_FALL_THRU();
686 case 0x89: /* mov r/m, reg */
687 off += rtR0Nt3CalcModRmLength(pbCode[off]);
688 break;
689
690 case 0xc7:
691 if ((pbCode[off] & X86_MODRM_REG_MASK) == 0) /* mov r/m, imm32 */
692 off += rtR0Nt3CalcModRmLength(pbCode[off]) + 4;
693 else
694 {
695 RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered unknown opcode at %#x! %.*Rhxs\n",
696 off - 1, RT_MAX(off + 16, RT_MIN(PAGE_SIZE - ((uintptr_t)pbCode & PAGE_OFFSET_MASK), 128)), pbCode);
697 return VERR_INTERNAL_ERROR_3;
698 }
699 break;
700
701 case 0xc2: /* ret iw */
702 RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered RET! %.*Rhxs\n",
703 off + 2, pbCode);
704 return VERR_INTERNAL_ERROR_3;
705
706 default:
707 RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered unknown opcode at %#x! %.*Rhxs\n",
708 off - 1, RT_MAX(off + 16, RT_MIN(PAGE_SIZE - ((uintptr_t)pbCode & PAGE_OFFSET_MASK), 128)), pbCode);
709 return VERR_INTERNAL_ERROR_3;
710
711 /* Just in case: */
712
713 case 0xa1: /* mov eax, [m32] */
714 _imp__KeTickCount = *(KSYSTEM_TIME **)&pbCode[off];
715 off += 4;
716 break;
717
718 case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: /* push reg */
719 break;
720 }
721 }
722 if (!_imp__KeTickCount)
723 {
724 RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount after 128 bytes! %.*Rhxs\n", 128, pbCode);
725 return VERR_INTERNAL_ERROR_3;
726 }
727 }
728
729 return VINF_SUCCESS;
730}
731
732
733extern "C" DECLEXPORT(VOID)
734Nt3Fb_KeInitializeTimerEx(PKTIMER pTimer, TIMER_TYPE enmType)
735{
736 KeInitializeTimer(pTimer);
737 NOREF(enmType);
738 /** @todo Default is NotificationTimer, for SyncrhonizationTimer we need to
739 * do more work. timer-r0drv-nt.cpp is using the latter. :/ */
740}
741
742
743extern "C" DECLEXPORT(BOOLEAN) __stdcall
744Nt3Fb_KeSetTimerEx(PKTIMER pTimer, LARGE_INTEGER DueTime, LONG cMsPeriod, PKDPC pDpc)
745{
746 AssertReturn(cMsPeriod == 0, FALSE);
747 return KeSetTimer(pTimer, DueTime, pDpc);
748}
749
750
751extern "C" DECLEXPORT(PDEVICE_OBJECT)
752Nt3Fb_IoAttachDeviceToDeviceStack(PDEVICE_OBJECT pSourceDevice, PDEVICE_OBJECT pTargetDevice)
753{
754 NOREF(pSourceDevice); NOREF(pTargetDevice);
755 return NULL;
756}
757
758
759extern "C" DECLEXPORT(HANDLE)
760Nt3Fb_PsGetCurrentProcessId(void)
761{
762 if (!g_fNt3VersionInitialized)
763 rtR0Nt3InitVersion();
764
765 uint8_t const *pbProcess = (uint8_t const *)IoGetCurrentProcess();
766 if ( g_uNt3MajorVer > 3
767 || g_uNt3MinorVer >= 50)
768 return *(HANDLE const *)&pbProcess[0x94];
769 return *(HANDLE const *)&pbProcess[0xb0];
770}
771
772
773extern "C" DECLEXPORT(NTSTATUS)
774Nt3Fb_ZwYieldExecution(VOID)
775{
776 LARGE_INTEGER Interval;
777 Interval.QuadPart = 0;
778 KeDelayExecutionThread(KernelMode, FALSE, &Interval);
779 return STATUS_SUCCESS;
780}
781
782
783/**
784 * This is a simple implementation of the fast mutex api introduced in 3.50.
785 */
786extern "C" DECLEXPORT(VOID) FASTCALL
787Nt3Fb_ExAcquireFastMutex(PFAST_MUTEX pFastMtx)
788{
789 PETHREAD pSelf = PsGetCurrentThread();
790 KIRQL OldIrql;
791 KeRaiseIrql(APC_LEVEL, &OldIrql);
792
793 /* The Count member is initialized to 1. So if we decrement it to zero, we're
794 the first locker and owns the mutex. Otherwise we must wait for our turn. */
795 int32_t cLockers = ASMAtomicDecS32((int32_t volatile *)&pFastMtx->Count);
796 if (cLockers != 0)
797 {
798 ASMAtomicIncU32((uint32_t volatile *)&pFastMtx->Contention);
799 KeWaitForSingleObject(&pFastMtx->Event, Executive, KernelMode, FALSE /*fAlertable*/, NULL /*pTimeout*/);
800 }
801
802 pFastMtx->Owner = (PKTHREAD)pSelf;
803 pFastMtx->OldIrql = OldIrql;
804}
805
806
807/**
808 * This is a simple implementation of the fast mutex api introduced in 3.50.
809 */
810extern "C" DECLEXPORT(VOID) FASTCALL
811Nt3Fb_ExReleaseFastMutex(PFAST_MUTEX pFastMtx)
812{
813 AssertMsg(pFastMtx->Owner == (PKTHREAD)PsGetCurrentThread(), ("Owner=%p, expected %p\n", pFastMtx->Owner, PsGetCurrentThread()));
814
815 KIRQL OldIrql = pFastMtx->OldIrql;
816 pFastMtx->Owner = NULL;
817 int32_t cLockers = ASMAtomicIncS32((int32_t volatile *)&pFastMtx->Count);
818 if (cLockers <= 0)
819 KeSetEvent(&pFastMtx->Event, EVENT_INCREMENT, FALSE /*fWait*/);
820 if (OldIrql != APC_LEVEL)
821 KeLowerIrql(OldIrql);
822}
823
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