summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp')
-rw-r--r--src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp823
1 files changed, 823 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp b/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp
new file mode 100644
index 00000000..8a21e12a
--- /dev/null
+++ b/src/VBox/Runtime/r0drv/nt/nt3fakes-r0drv-nt.cpp
@@ -0,0 +1,823 @@
+/* $Id: nt3fakes-r0drv-nt.cpp $ */
+/** @file
+ * IPRT - NT 3.x fakes for NT 4.0 KPIs.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define _IMAGE_NT_HEADERS RT_CONCAT(_IMAGE_NT_HEADERS,ARCH_BITS)
+#include "the-nt-kernel.h"
+#include <iprt/mem.h>
+
+#include <iprt/assert.h>
+#include <iprt/asm.h>
+#include <iprt/ctype.h>
+#include <iprt/dbg.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/x86.h>
+#include <iprt/formats/mz.h>
+#include <iprt/formats/pecoff.h>
+#include "internal-r0drv-nt.h"
+
+typedef uint32_t DWORD;
+#include <VerRsrc.h>
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+DECLASM(void) rtNt3InitSymbolsAssembly(void); /* in nt3fakesA-r0drv-nt.asm */
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static uint32_t g_uNt3MajorVer = 3;
+static uint32_t g_uNt3MinorVer = 51;
+static uint32_t g_uNt3BuildNo = 1057;
+static bool g_fNt3Checked = false;
+static bool g_fNt3Smp = false; /**< Not reliable. */
+static bool volatile g_fNt3VersionInitialized = false;
+
+static uint8_t *g_pbNt3OsKrnl = (uint8_t *)UINT32_C(0x80100000);
+static uint32_t g_cbNt3OsKrnl = 0x300000;
+static uint8_t *g_pbNt3Hal = (uint8_t *)UINT32_C(0x80400000);
+static uint32_t g_cbNt3Hal = _512K;
+static bool volatile g_fNt3ModuleInfoInitialized = false;
+
+
+RT_C_DECLS_BEGIN
+/** @name KPIs we provide fallback implementations for.
+ *
+ * The assembly init routine will point the __imp_xxx variable to the NT
+ * implementation if available, using the fallback if not.
+ * @{ */
+decltype(PsGetVersion) *g_pfnrtPsGetVersion;
+decltype(ZwQuerySystemInformation) *g_pfnrtZwQuerySystemInformation;
+decltype(KeSetTimerEx) *g_pfnrtKeSetTimerEx;
+decltype(IoAttachDeviceToDeviceStack) *g_pfnrtIoAttachDeviceToDeviceStack;
+decltype(PsGetCurrentProcessId) *g_pfnrtPsGetCurrentProcessId;
+decltype(ZwYieldExecution) *g_pfnrtZwYieldExecution;
+decltype(ExAcquireFastMutex) *g_pfnrtExAcquireFastMutex;
+decltype(ExReleaseFastMutex) *g_pfnrtExReleaseFastMutex;
+/** @} */
+
+/** @name Fastcall optimizations not present in NT 3.1.
+ *
+ * We try resolve both the stdcall and fastcall variants and patch it up in
+ * assembly. The last four routines are in the hal.
+ *
+ * @{ */
+decltype(IofCompleteRequest) *g_pfnrtIofCompleteRequest;
+decltype(ObfDereferenceObject) *g_pfnrtObfDereferenceObject;
+decltype(IofCallDriver) *g_pfnrtIofCallDriver;
+decltype(KfAcquireSpinLock) *g_pfnrtKfAcquireSpinLock;
+decltype(KfReleaseSpinLock) *g_pfnrtKfReleaseSpinLock;
+decltype(KefAcquireSpinLockAtDpcLevel) *g_pfnrtKefAcquireSpinLockAtDpcLevel;
+decltype(KefReleaseSpinLockFromDpcLevel) *g_pfnrtKefReleaseSpinLockFromDpcLevel;
+decltype(KfLowerIrql) *g_pfnrtKfLowerIrql;
+decltype(KfRaiseIrql) *g_pfnrtKfRaiseIrql;
+
+VOID (__stdcall *g_pfnrtIoCompleteRequest)(PIRP, CCHAR);
+LONG_PTR (__stdcall *g_pfnrtObDereferenceObject)(PVOID);
+NTSTATUS (__stdcall *g_pfnrtIoCallDriver)(PDEVICE_OBJECT, PIRP);
+KIRQL (__stdcall *g_pfnrtKeAcquireSpinLock)(PKSPIN_LOCK);
+VOID (__stdcall *g_pfnrtKeReleaseSpinLock)(PKSPIN_LOCK, KIRQL);
+KIRQL (__stdcall *g_pfnrtKeAcquireSpinLockAtDpcLevel)(PKSPIN_LOCK);
+VOID (__stdcall *g_pfnrtKeReleaseSpinLockFromDpcLevel)(PKSPIN_LOCK);
+VOID (__stdcall *g_pfnrtKeLowerIrql)(KIRQL);
+KIRQL (__stdcall *g_pfnrtKeRaiseIrql)(KIRQL);
+/** @} */
+
+/** @name DATA exports and associated stuff
+ * @{ */
+/** Import address table entry for KeTickCount (defined in asm). */
+extern KSYSTEM_TIME *_imp__KeTickCount;
+/** @} */
+
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtR0Nt3InitModuleInfo(void);
+
+
+/**
+ * Converts a string to a number, stopping at the first non-digit.
+ *
+ * @returns The value
+ * @param ppwcValue Pointer to the string pointer variable. Updated.
+ * @param pcwcValue Pointer to the string length variable. Updated.
+ */
+static uint32_t rtR0Nt3StringToNum(PCRTUTF16 *ppwcValue, size_t *pcwcValue)
+{
+ uint32_t uValue = 0;
+ PCRTUTF16 pwcValue = *ppwcValue;
+ size_t cwcValue = *pcwcValue;
+
+ while (cwcValue > 0)
+ {
+ RTUTF16 uc = *pwcValue;
+ unsigned uDigit = (unsigned)uc - (unsigned)'0';
+ if (uDigit < (unsigned)10)
+ {
+ uValue *= 10;
+ uValue += uDigit;
+ }
+ else
+ break;
+ pwcValue++;
+ cwcValue--;
+ }
+
+ *ppwcValue = pwcValue;
+ *pcwcValue = cwcValue;
+ return uValue;
+}
+
+
+/**
+ * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
+ * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentVersion'
+ */
+static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentVersion(PWSTR pwszValueName, ULONG uValueType,
+ PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
+{
+ RT_NOREF(pwszValueName, pvEntryCtx);
+ if ( uValueType == REG_SZ
+ || uValueType == REG_EXPAND_SZ)
+ {
+ PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
+ size_t cwcValue = cbValue / sizeof(*pwcValue);
+ uint32_t uMajor = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
+ uint32_t uMinor = 0;
+ if (cwcValue > 1)
+ {
+ pwcValue++;
+ cwcValue--;
+ uMinor = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
+ }
+
+ if (uMajor >= 3)
+ {
+ g_uNt3MajorVer = uMajor;
+ g_uNt3MinorVer = uMinor;
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion found: uMajor=%u uMinor=%u\n", uMajor, uMinor);
+ *(uint32_t *)pvUser |= RT_BIT_32(0);
+ return STATUS_SUCCESS;
+ }
+
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
+ }
+ else
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentVersion: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
+ * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentBuildNumber'
+ */
+static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentBuildNumber(PWSTR pwszValueName, ULONG uValueType,
+ PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
+{
+ RT_NOREF(pwszValueName, pvEntryCtx);
+ if ( uValueType == REG_SZ
+ || uValueType == REG_EXPAND_SZ)
+ {
+ PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
+ size_t cwcValue = cbValue / sizeof(*pwcValue);
+ uint32_t uBuildNo = rtR0Nt3StringToNum(&pwcValue, &cwcValue);
+
+ if (uBuildNo >= 100 && uBuildNo < _1M)
+ {
+ g_uNt3BuildNo = uBuildNo;
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber found: uBuildNo=%u\n", uBuildNo);
+ *(uint32_t *)pvUser |= RT_BIT_32(1);
+ return STATUS_SUCCESS;
+ }
+
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
+ }
+ else
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentBuildNumber: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Implements RTL_QUERY_REGISTRY_ROUTINE for processing
+ * 'HKLM/Software/Microsoft/Window NT/CurrentVersion/CurrentType'
+ */
+static NTSTATUS NTAPI rtR0Nt3VerEnumCallback_CurrentType(PWSTR pwszValueName, ULONG uValueType,
+ PVOID pvValue, ULONG cbValue, PVOID pvUser, PVOID pvEntryCtx)
+{
+ RT_NOREF(pwszValueName, pvEntryCtx);
+ if ( uValueType == REG_SZ
+ || uValueType == REG_EXPAND_SZ)
+ {
+ PCRTUTF16 pwcValue = (PCRTUTF16)pvValue;
+ size_t cwcValue = cbValue / sizeof(*pwcValue);
+
+ int fSmp = -1;
+ if (cwcValue >= 12 && RTUtf16NICmpAscii(pwcValue, "Uniprocessor", 12) == 0)
+ {
+ cwcValue -= 12;
+ pwcValue += 12;
+ fSmp = 0;
+ }
+ else if (cwcValue >= 14 && RTUtf16NICmpAscii(pwcValue, "Multiprocessor", 14) == 0)
+ {
+ cwcValue -= 14;
+ pwcValue += 14;
+ fSmp = 1;
+ }
+ if (fSmp != -1)
+ {
+ while (cwcValue > 0 && RT_C_IS_SPACE(*pwcValue))
+ cwcValue--, pwcValue++;
+
+ int fChecked = -1;
+ if (cwcValue >= 4 && RTUtf16NICmpAscii(pwcValue, "Free", 4) == 0)
+ fChecked = 0;
+ else if (cwcValue >= 7 && RTUtf16NICmpAscii(pwcValue, "Checked", 7) == 0)
+ fChecked = 1;
+ if (fChecked != -1)
+ {
+ g_fNt3Smp = fSmp != 0;
+ g_fNt3Checked = fChecked != 0;
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType found: fSmp=%d fChecked=%d\n", fSmp, fChecked);
+ *(uint32_t *)pvUser |= RT_BIT_32(2);
+ return STATUS_SUCCESS;
+ }
+ }
+
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType: '%.*ls'\n", cbValue / sizeof(RTUTF16), pvValue);
+ }
+ else
+ RTLogBackdoorPrintf("rtR0Nt3VerEnumCallback_CurrentType: uValueType=%u %.*Rhxs\n", uValueType, cbValue, pvValue);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * Figure out the NT 3 version from the registry.
+ *
+ * @note this will be called before the rtR0Nt3InitSymbols is called.
+ */
+static void rtR0Nt3InitVersion(void)
+{
+ /*
+ * No PsGetVersion, so try the registry. Unfortunately not necessarily
+ * initialized when we're loaded.
+ */
+ RTL_QUERY_REGISTRY_TABLE aQuery[4];
+ RT_ZERO(aQuery);
+ aQuery[0].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentVersion;
+ aQuery[0].Flags = 0;
+ aQuery[0].Name = L"CurrentVersion";
+ aQuery[0].EntryContext = NULL;
+ aQuery[0].DefaultType = REG_NONE;
+
+ aQuery[1].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentBuildNumber;
+ aQuery[1].Flags = 0;
+ aQuery[1].Name = L"CurrentBuildNumber";
+ aQuery[1].EntryContext = NULL;
+ aQuery[1].DefaultType = REG_NONE;
+
+ aQuery[2].QueryRoutine = rtR0Nt3VerEnumCallback_CurrentType;
+ aQuery[2].Flags = 0;
+ aQuery[2].Name = L"CurrentType";
+ aQuery[2].EntryContext = NULL;
+ aQuery[2].DefaultType = REG_NONE;
+
+ uint32_t fFound = 0;
+ //NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT, NULL, &aQuery[0], &fFound, NULL /*Environment*/);
+ NTSTATUS rcNt = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
+ L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion",
+ &aQuery[0], &fFound, NULL /*Environment*/);
+ if (!NT_SUCCESS(rcNt))
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: RtlQueryRegistryValues failed: %#x\n", rcNt);
+ else
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: Didn't get all values: fFound=%#x\n", fFound);
+
+ /*
+ * We really need the version number. Build, type and SMP is off less importance.
+ * Derive it from the NT kernel PE header.
+ */
+ if (!(fFound & RT_BIT_32(0)))
+ {
+ if (!g_fNt3ModuleInfoInitialized)
+ rtR0Nt3InitModuleInfo();
+
+ PIMAGE_DOS_HEADER pMzHdr = (PIMAGE_DOS_HEADER)g_pbNt3OsKrnl;
+ PIMAGE_NT_HEADERS32 pNtHdrs = (PIMAGE_NT_HEADERS32)&g_pbNt3OsKrnl[pMzHdr->e_lfanew];
+ if (pNtHdrs->OptionalHeader.MajorOperatingSystemVersion == 1)
+ {
+ /* NT 3.1 and NT 3.50 both set OS version to 1.0 in the optional header. */
+ g_uNt3MajorVer = 3;
+ if ( pNtHdrs->OptionalHeader.MajorLinkerVersion == 2
+ && pNtHdrs->OptionalHeader.MinorLinkerVersion < 50)
+ g_uNt3MinorVer = 10;
+ else
+ g_uNt3MinorVer = 50;
+ }
+ else
+ {
+ g_uNt3MajorVer = pNtHdrs->OptionalHeader.MajorOperatingSystemVersion;
+ g_uNt3MinorVer = pNtHdrs->OptionalHeader.MinorOperatingSystemVersion;
+ }
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: guessed %u.%u from PE header\n", g_uNt3MajorVer, g_uNt3MinorVer);
+
+ /* Check out the resource section, looking for VS_FIXEDFILEINFO. */
+ __try /* (pointless) */
+ {
+ PIMAGE_SECTION_HEADER paShdrs = (PIMAGE_SECTION_HEADER)(pNtHdrs + 1);
+ uint32_t const cShdrs = pNtHdrs->FileHeader.NumberOfSections;
+ uint32_t iShdr = 0;
+ while (iShdr < cShdrs && memcmp(paShdrs[iShdr].Name, ".rsrc", 6) != 0)
+ iShdr++;
+ if (iShdr < cShdrs)
+ {
+ if ( paShdrs[iShdr].VirtualAddress > 0
+ && paShdrs[iShdr].VirtualAddress < pNtHdrs->OptionalHeader.SizeOfImage)
+ {
+ uint32_t const cbRsrc = RT_MIN(paShdrs[iShdr].Misc.VirtualSize
+ ? paShdrs[iShdr].Misc.VirtualSize : paShdrs[iShdr].SizeOfRawData,
+ pNtHdrs->OptionalHeader.SizeOfImage - paShdrs[iShdr].VirtualAddress);
+ uint8_t const *pbRsrc = &g_pbNt3OsKrnl[paShdrs[iShdr].VirtualAddress];
+ uint32_t const *puDwords = (uint32_t const *)pbRsrc;
+ uint32_t cDWords = (cbRsrc - sizeof(VS_FIXEDFILEINFO) + sizeof(uint32_t)) / sizeof(uint32_t);
+ while (cDWords-- > 0)
+ {
+ if ( puDwords[0] == VS_FFI_SIGNATURE
+ && puDwords[1] == VS_FFI_STRUCVERSION)
+ {
+ VS_FIXEDFILEINFO const *pVerInfo = (VS_FIXEDFILEINFO const *)puDwords;
+ g_uNt3MajorVer = pVerInfo->dwProductVersionMS >> 16;
+ g_uNt3MinorVer = pVerInfo->dwProductVersionMS >> 16;
+ g_uNt3BuildNo = pVerInfo->dwProductVersionLS >> 16;
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: Found version info %u.%u build %u\n",
+ g_uNt3MajorVer, g_uNt3MinorVer, g_uNt3BuildNo);
+ break;
+ }
+ puDwords++;
+ }
+ }
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitVersion: Exception scanning .rsrc section for version info!\n");
+ }
+ }
+
+ /*
+ * If we've got PsGetVersion, use it to override the above finding!
+ * (We may end up here for reasons other than the PsGetVersion fallback.)
+ */
+ if (g_pfnrtPsGetVersion)
+ {
+ WCHAR wszCsd[64];
+ UNICODE_STRING UniStr;
+ UniStr.Buffer = wszCsd;
+ UniStr.MaximumLength = sizeof(wszCsd) - sizeof(WCHAR);
+ UniStr.Length = 0;
+ RT_ZERO(wszCsd);
+ ULONG uMajor = 3;
+ ULONG uMinor = 51;
+ ULONG uBuildNo = 1057;
+ BOOLEAN fChecked = g_pfnrtPsGetVersion(&uMajor, &uMinor, &uBuildNo, &UniStr);
+
+ g_uNt3MajorVer = uMajor;
+ g_uNt3MinorVer = uMinor;
+ g_uNt3BuildNo = uBuildNo;
+ g_fNt3Checked = fChecked != FALSE;
+ }
+
+ g_fNt3VersionInitialized = true;
+}
+
+
+extern "C" DECLEXPORT(BOOLEAN) __stdcall
+Nt3Fb_PsGetVersion(ULONG *puMajor, ULONG *puMinor, ULONG *puBuildNo, UNICODE_STRING *pCsdStr)
+{
+ if (!g_fNt3VersionInitialized)
+ rtR0Nt3InitVersion();
+ if (puMajor)
+ *puMajor = g_uNt3MajorVer;
+ if (puMinor)
+ *puMinor = g_uNt3MinorVer;
+ if (puBuildNo)
+ *puBuildNo = g_uNt3BuildNo;
+ if (pCsdStr)
+ {
+ pCsdStr->Buffer[0] = '\0';
+ pCsdStr->Length = 0;
+ }
+ return g_fNt3Checked;
+}
+
+
+/**
+ * Worker for rtR0Nt3InitModuleInfo.
+ */
+static bool rtR0Nt3InitModuleInfoOne(const char *pszImage, uint8_t const *pbCode, uint8_t **ppbModule, uint32_t *pcbModule)
+{
+ uintptr_t const uImageAlign = _4K; /* XP may put the kernel at */
+
+ /* Align pbCode. */
+ pbCode = (uint8_t const *)((uintptr_t)pbCode & ~(uintptr_t)(uImageAlign - 1));
+
+ /* Scan backwards till we find a PE signature. */
+ for (uint32_t cbChecked = 0; cbChecked < _64M; cbChecked += uImageAlign, pbCode -= uImageAlign)
+ {
+ if (!MmIsAddressValid((void *)pbCode))
+ continue;
+
+ uint32_t uZero = 0;
+ uint32_t offNewHdr = 0;
+ __try /* pointless */
+ {
+ uZero = *(uint32_t const *)pbCode;
+ offNewHdr = *(uint32_t const *)&pbCode[RT_UOFFSETOF(IMAGE_DOS_HEADER, e_lfanew)];
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Exception at %p scanning for DOS header...\n", pbCode);
+ continue;
+ }
+ if ( (uint16_t)uZero == IMAGE_DOS_SIGNATURE
+ && offNewHdr < _2K
+ && offNewHdr >= sizeof(IMAGE_DOS_HEADER))
+ {
+ RT_CONCAT(IMAGE_NT_HEADERS,ARCH_BITS) NtHdrs;
+ __try /* pointless */
+ {
+ NtHdrs = *(decltype(NtHdrs) const *)&pbCode[offNewHdr];
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Exception at %p reading NT headers...\n", pbCode);
+ continue;
+ }
+ if ( NtHdrs.Signature == IMAGE_NT_SIGNATURE
+ && NtHdrs.FileHeader.SizeOfOptionalHeader == sizeof(NtHdrs.OptionalHeader)
+ && NtHdrs.FileHeader.NumberOfSections > 2
+ && NtHdrs.FileHeader.NumberOfSections < _4K
+ && NtHdrs.OptionalHeader.Magic == RT_CONCAT3(IMAGE_NT_OPTIONAL_HDR,ARCH_BITS,_MAGIC))
+ {
+ *ppbModule = (uint8_t *)pbCode;
+ *pcbModule = NtHdrs.OptionalHeader.SizeOfImage;
+ RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Found %s at %#p LB %#x\n",
+ pszImage, pbCode, NtHdrs.OptionalHeader.SizeOfImage);
+ return true;
+ }
+ }
+ }
+ RTLogBackdoorPrintf("rtR0Nt3InitModuleInfo: Warning! Unable to locate %s...\n");
+ return false;
+}
+
+
+/**
+ * Initializes the module information (NTOSKRNL + HAL) using exported symbols.
+ * This only works as long as noone is intercepting the symbols.
+ */
+static void rtR0Nt3InitModuleInfo(void)
+{
+ rtR0Nt3InitModuleInfoOne("ntoskrnl.exe", (uint8_t const *)(uintptr_t)IoGetCurrentProcess, &g_pbNt3OsKrnl, &g_cbNt3OsKrnl);
+ rtR0Nt3InitModuleInfoOne("hal.dll", (uint8_t const *)(uintptr_t)HalGetBusData, &g_pbNt3Hal, &g_cbNt3Hal);
+ g_fNt3ModuleInfoInitialized = true;
+}
+
+
+extern "C" DECLEXPORT(NTSTATUS) __stdcall
+Nt3Fb_ZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS enmClass, PVOID pvBuf, ULONG cbBuf, PULONG pcbActual)
+{
+ switch (enmClass)
+ {
+ case SystemModuleInformation:
+ {
+ PRTL_PROCESS_MODULES pInfo = (PRTL_PROCESS_MODULES)pvBuf;
+ ULONG cbNeeded = RT_UOFFSETOF(RTL_PROCESS_MODULES, Modules[2]);
+ if (pcbActual)
+ *pcbActual = cbNeeded;
+ if (cbBuf < cbNeeded)
+ return STATUS_INFO_LENGTH_MISMATCH;
+
+ if (!g_fNt3ModuleInfoInitialized)
+ rtR0Nt3InitModuleInfo();
+
+ pInfo->NumberOfModules = 2;
+
+ /* ntoskrnl.exe */
+ pInfo->Modules[0].Section = NULL;
+ pInfo->Modules[0].MappedBase = g_pbNt3OsKrnl;
+ pInfo->Modules[0].ImageBase = g_pbNt3OsKrnl;
+ pInfo->Modules[0].ImageSize = g_cbNt3OsKrnl;
+ pInfo->Modules[0].Flags = 0;
+ pInfo->Modules[0].LoadOrderIndex = 0;
+ pInfo->Modules[0].InitOrderIndex = 0;
+ pInfo->Modules[0].LoadCount = 1024;
+ pInfo->Modules[0].OffsetToFileName = sizeof("\\SystemRoot\\System32\\") - 1;
+ memcpy(pInfo->Modules[0].FullPathName, RT_STR_TUPLE("\\SystemRoot\\System32\\ntoskrnl.exe"));
+
+ /* hal.dll */
+ pInfo->Modules[1].Section = NULL;
+ pInfo->Modules[1].MappedBase = g_pbNt3Hal;
+ pInfo->Modules[1].ImageBase = g_pbNt3Hal;
+ pInfo->Modules[1].ImageSize = g_cbNt3Hal;
+ pInfo->Modules[1].Flags = 0;
+ pInfo->Modules[1].LoadOrderIndex = 1;
+ pInfo->Modules[1].InitOrderIndex = 0;
+ pInfo->Modules[1].LoadCount = 1024;
+ pInfo->Modules[1].OffsetToFileName = sizeof("\\SystemRoot\\System32\\") - 1;
+ memcpy(pInfo->Modules[1].FullPathName, RT_STR_TUPLE("\\SystemRoot\\System32\\hal.dll"));
+
+ return STATUS_SUCCESS;
+ }
+
+ default:
+ return STATUS_INVALID_INFO_CLASS;
+ }
+}
+
+/**
+ * Calculates the length indicated by an ModR/M sequence.
+ *
+ * @returns Length, including RM byte.
+ * @param bRm The RM byte.
+ */
+static uint32_t rtR0Nt3CalcModRmLength(uint8_t bRm)
+{
+ uint32_t cbRm = 1;
+
+ if ( (bRm & X86_MODRM_MOD_MASK) == (3 << X86_MODRM_MOD_SHIFT)
+ || (bRm & (X86_MODRM_MOD_MASK | X86_MODRM_RM_MASK)) == 5)
+ cbRm += 4; /* disp32 */
+ else if ((bRm & X86_MODRM_MOD_MASK) == (1 << X86_MODRM_MOD_SHIFT))
+ cbRm += 1; /* disp8 */
+ else if ((bRm & X86_MODRM_MOD_MASK) == (2 << X86_MODRM_MOD_SHIFT))
+ cbRm += 2; /* disp16 */
+
+ if ((bRm & X86_MODRM_RM_MASK) == 4 && (bRm & X86_MODRM_MOD_MASK) != (3 << X86_MODRM_MOD_SHIFT))
+ cbRm += 1; /* SIB */
+
+ return cbRm;
+}
+
+
+/**
+ * Init symbols.
+ *
+ * This is called after both ZwQuerySystemInformation and PsGetVersion are used
+ * for the first time.
+ *
+ * @returns IPRT status code
+ * @param hKrnlInfo Kernel symbol digger handle.
+ */
+DECLHIDDEN(int) rtR0Nt3InitSymbols(RTDBGKRNLINFO hKrnlInfo)
+{
+ /*
+ * Resolve symbols. (We set C variables (g_pfnrtXxx) here, not the __imp__Xxx ones.)
+ */
+#define GET_SYSTEM_ROUTINE(a_fnName) do { \
+ RT_CONCAT(g_pfnrt, a_fnName) = (decltype(RT_CONCAT(g_pfnrt, a_fnName)))RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, #a_fnName); \
+ } while (0)
+
+ GET_SYSTEM_ROUTINE(PsGetVersion);
+ GET_SYSTEM_ROUTINE(ZwQuerySystemInformation);
+ GET_SYSTEM_ROUTINE(KeSetTimerEx);
+ GET_SYSTEM_ROUTINE(IoAttachDeviceToDeviceStack);
+ GET_SYSTEM_ROUTINE(PsGetCurrentProcessId);
+ GET_SYSTEM_ROUTINE(ZwYieldExecution);
+ GET_SYSTEM_ROUTINE(ExAcquireFastMutex);
+ GET_SYSTEM_ROUTINE(ExReleaseFastMutex);
+
+#define GET_FAST_CALL_SYSTEM_ROUTINE(a_fnFastcall, a_fnStdcall) do { \
+ GET_SYSTEM_ROUTINE(a_fnFastcall); \
+ GET_SYSTEM_ROUTINE(a_fnStdcall); \
+ AssertLogRelReturn(RT_CONCAT(g_pfnrt,a_fnFastcall) || RT_CONCAT(g_pfnrt,a_fnStdcall), VERR_INTERNAL_ERROR_3); \
+ } while (0)
+ GET_FAST_CALL_SYSTEM_ROUTINE(IofCompleteRequest, IoCompleteRequest);
+ GET_FAST_CALL_SYSTEM_ROUTINE(ObfDereferenceObject, ObDereferenceObject);
+ GET_FAST_CALL_SYSTEM_ROUTINE(IofCallDriver, IoCallDriver);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KfAcquireSpinLock, KeAcquireSpinLock);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KfReleaseSpinLock, KeReleaseSpinLock);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KfLowerIrql, KeLowerIrql);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KfRaiseIrql, KeRaiseIrql);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KefAcquireSpinLockAtDpcLevel, KeAcquireSpinLockAtDpcLevel);
+ GET_FAST_CALL_SYSTEM_ROUTINE(KefReleaseSpinLockFromDpcLevel, KeReleaseSpinLockFromDpcLevel);
+
+ /*
+ * We need to call assembly to update the __imp__Xxx entries, since C
+ * doesn't allow '@' in symbols.
+ */
+ rtNt3InitSymbolsAssembly();
+
+ /*
+ * Tick count data. We disassemble KeQueryTickCount until we find the
+ * first absolute address referenced in it.
+ * %80105b70 8b 44 24 04 mov eax, dword [esp+004h]
+ * %80105b74 c7 40 04 00 00 00 00 mov dword [eax+004h], 000000000h
+ * %80105b7b 8b 0d 88 70 19 80 mov ecx, dword [080197088h]
+ * %80105b81 89 08 mov dword [eax], ecx
+ * %80105b83 c2 04 00 retn 00004h
+ */
+ _imp__KeTickCount = (decltype(_imp__KeTickCount))RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeTickCount");
+ if (!_imp__KeTickCount)
+ {
+ if (!g_fNt3VersionInitialized)
+ rtR0Nt3InitVersion();
+ Assert(g_uNt3MajorVer == 3 && g_uNt3MinorVer < 50);
+
+ uint8_t const *pbCode = (uint8_t const *)RTR0DbgKrnlInfoGetSymbol(hKrnlInfo, NULL, "KeQueryTickCount");
+ AssertLogRelReturn(pbCode, VERR_INTERNAL_ERROR_2);
+
+ for (uint32_t off = 0; off < 128 && _imp__KeTickCount == NULL;)
+ {
+ uint8_t const b1 = pbCode[off++];
+ switch (b1)
+ {
+ case 0x8b: /* mov reg, r/m ; We're looking for absolute address in r/m. */
+ if ((pbCode[off] & (X86_MODRM_MOD_MASK | X86_MODRM_RM_MASK)) == 5 /*disp32*/)
+ _imp__KeTickCount = *(KSYSTEM_TIME **)&pbCode[off + 1];
+ RT_FALL_THRU();
+ case 0x89: /* mov r/m, reg */
+ off += rtR0Nt3CalcModRmLength(pbCode[off]);
+ break;
+
+ case 0xc7:
+ if ((pbCode[off] & X86_MODRM_REG_MASK) == 0) /* mov r/m, imm32 */
+ off += rtR0Nt3CalcModRmLength(pbCode[off]) + 4;
+ else
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered unknown opcode at %#x! %.*Rhxs\n",
+ off - 1, RT_MAX(off + 16, RT_MIN(PAGE_SIZE - ((uintptr_t)pbCode & PAGE_OFFSET_MASK), 128)), pbCode);
+ return VERR_INTERNAL_ERROR_3;
+ }
+ break;
+
+ case 0xc2: /* ret iw */
+ RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered RET! %.*Rhxs\n",
+ off + 2, pbCode);
+ return VERR_INTERNAL_ERROR_3;
+
+ default:
+ RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount! Encountered unknown opcode at %#x! %.*Rhxs\n",
+ off - 1, RT_MAX(off + 16, RT_MIN(PAGE_SIZE - ((uintptr_t)pbCode & PAGE_OFFSET_MASK), 128)), pbCode);
+ return VERR_INTERNAL_ERROR_3;
+
+ /* Just in case: */
+
+ case 0xa1: /* mov eax, [m32] */
+ _imp__KeTickCount = *(KSYSTEM_TIME **)&pbCode[off];
+ off += 4;
+ break;
+
+ case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: /* push reg */
+ break;
+ }
+ }
+ if (!_imp__KeTickCount)
+ {
+ RTLogBackdoorPrintf("rtR0Nt3InitSymbols: Failed to find KeTickCount after 128 bytes! %.*Rhxs\n", 128, pbCode);
+ return VERR_INTERNAL_ERROR_3;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+extern "C" DECLEXPORT(VOID)
+Nt3Fb_KeInitializeTimerEx(PKTIMER pTimer, TIMER_TYPE enmType)
+{
+ KeInitializeTimer(pTimer);
+ NOREF(enmType);
+ /** @todo Default is NotificationTimer, for SyncrhonizationTimer we need to
+ * do more work. timer-r0drv-nt.cpp is using the latter. :/ */
+}
+
+
+extern "C" DECLEXPORT(BOOLEAN) __stdcall
+Nt3Fb_KeSetTimerEx(PKTIMER pTimer, LARGE_INTEGER DueTime, LONG cMsPeriod, PKDPC pDpc)
+{
+ AssertReturn(cMsPeriod == 0, FALSE);
+ return KeSetTimer(pTimer, DueTime, pDpc);
+}
+
+
+extern "C" DECLEXPORT(PDEVICE_OBJECT)
+Nt3Fb_IoAttachDeviceToDeviceStack(PDEVICE_OBJECT pSourceDevice, PDEVICE_OBJECT pTargetDevice)
+{
+ NOREF(pSourceDevice); NOREF(pTargetDevice);
+ return NULL;
+}
+
+
+extern "C" DECLEXPORT(HANDLE)
+Nt3Fb_PsGetCurrentProcessId(void)
+{
+ if (!g_fNt3VersionInitialized)
+ rtR0Nt3InitVersion();
+
+ uint8_t const *pbProcess = (uint8_t const *)IoGetCurrentProcess();
+ if ( g_uNt3MajorVer > 3
+ || g_uNt3MinorVer >= 50)
+ return *(HANDLE const *)&pbProcess[0x94];
+ return *(HANDLE const *)&pbProcess[0xb0];
+}
+
+
+extern "C" DECLEXPORT(NTSTATUS)
+Nt3Fb_ZwYieldExecution(VOID)
+{
+ LARGE_INTEGER Interval;
+ Interval.QuadPart = 0;
+ KeDelayExecutionThread(KernelMode, FALSE, &Interval);
+ return STATUS_SUCCESS;
+}
+
+
+/**
+ * This is a simple implementation of the fast mutex api introduced in 3.50.
+ */
+extern "C" DECLEXPORT(VOID) FASTCALL
+Nt3Fb_ExAcquireFastMutex(PFAST_MUTEX pFastMtx)
+{
+ PETHREAD pSelf = PsGetCurrentThread();
+ KIRQL OldIrql;
+ KeRaiseIrql(APC_LEVEL, &OldIrql);
+
+ /* The Count member is initialized to 1. So if we decrement it to zero, we're
+ the first locker and owns the mutex. Otherwise we must wait for our turn. */
+ int32_t cLockers = ASMAtomicDecS32((int32_t volatile *)&pFastMtx->Count);
+ if (cLockers != 0)
+ {
+ ASMAtomicIncU32((uint32_t volatile *)&pFastMtx->Contention);
+ KeWaitForSingleObject(&pFastMtx->Event, Executive, KernelMode, FALSE /*fAlertable*/, NULL /*pTimeout*/);
+ }
+
+ pFastMtx->Owner = (PKTHREAD)pSelf;
+ pFastMtx->OldIrql = OldIrql;
+}
+
+
+/**
+ * This is a simple implementation of the fast mutex api introduced in 3.50.
+ */
+extern "C" DECLEXPORT(VOID) FASTCALL
+Nt3Fb_ExReleaseFastMutex(PFAST_MUTEX pFastMtx)
+{
+ AssertMsg(pFastMtx->Owner == (PKTHREAD)PsGetCurrentThread(), ("Owner=%p, expected %p\n", pFastMtx->Owner, PsGetCurrentThread()));
+
+ KIRQL OldIrql = pFastMtx->OldIrql;
+ pFastMtx->Owner = NULL;
+ int32_t cLockers = ASMAtomicIncS32((int32_t volatile *)&pFastMtx->Count);
+ if (cLockers <= 0)
+ KeSetEvent(&pFastMtx->Event, EVENT_INCREMENT, FALSE /*fWait*/);
+ if (OldIrql != APC_LEVEL)
+ KeLowerIrql(OldIrql);
+}
+