зеркало из https://github.com/mozilla/gecko-dev.git
601 строка
18 KiB
C++
601 строка
18 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "nscore.h"
|
|
#include "mozilla/NativeNt.h"
|
|
#include "mozilla/ThreadLocal.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "mozilla/WindowsEnumProcessModules.h"
|
|
|
|
#include <stdio.h>
|
|
#include <windows.h>
|
|
#include <strsafe.h>
|
|
|
|
const wchar_t kNormal[] = L"Foo.dll";
|
|
const wchar_t kHex12[] = L"Foo.ABCDEF012345.dll";
|
|
const wchar_t kHex15[] = L"ABCDEF012345678.dll";
|
|
const wchar_t kHex16[] = L"ABCDEF0123456789.dll";
|
|
const wchar_t kHex17[] = L"ABCDEF0123456789a.dll";
|
|
const wchar_t kHex24[] = L"ABCDEF0123456789cdabef98.dll";
|
|
const wchar_t kHex8[] = L"01234567.dll";
|
|
const wchar_t kNonHex12[] = L"Foo.ABCDEFG12345.dll";
|
|
const wchar_t kHex13[] = L"Foo.ABCDEF0123456.dll";
|
|
const wchar_t kHex11[] = L"Foo.ABCDEF01234.dll";
|
|
const wchar_t kPrefixedHex16[] = L"Pabcdef0123456789.dll";
|
|
const uint32_t kTlsDataValue = 1234;
|
|
static MOZ_THREAD_LOCAL(uint32_t) sTlsData;
|
|
|
|
const char kFailFmt[] =
|
|
"TEST-FAILED | NativeNt | %s(%s) should have returned %s but did not\n";
|
|
|
|
#define RUN_TEST(fn, varName, expected) \
|
|
if (fn(varName) == !expected) { \
|
|
printf(kFailFmt, #fn, #varName, #expected); \
|
|
return 1; \
|
|
}
|
|
|
|
#define EXPECT_FAIL(fn, varName) RUN_TEST(fn, varName, false)
|
|
|
|
#define EXPECT_SUCCESS(fn, varName) RUN_TEST(fn, varName, true)
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::nt;
|
|
|
|
bool TestVirtualQuery(HANDLE aProcess, LPCVOID aAddress) {
|
|
MEMORY_BASIC_INFORMATION info1 = {}, info2 = {};
|
|
SIZE_T result1 = ::VirtualQueryEx(aProcess, aAddress, &info1, sizeof(info1)),
|
|
result2 = mozilla::nt::VirtualQueryEx(aProcess, aAddress, &info2,
|
|
sizeof(info2));
|
|
if (result1 != result2) {
|
|
printf("TEST-FAILED | NativeNt | The returned values mismatch\n");
|
|
return false;
|
|
}
|
|
|
|
if (!result1) {
|
|
// Both APIs failed.
|
|
return true;
|
|
}
|
|
|
|
if (memcmp(&info1, &info2, result1) != 0) {
|
|
printf("TEST-FAILED | NativeNt | The returned structures mismatch\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// This class copies the self executable file to the %temp%\<outer>\<inner>
|
|
// folder. The length of its path is longer than MAX_PATH.
|
|
class LongNameModule {
|
|
wchar_t mOuterDirBuffer[MAX_PATH];
|
|
wchar_t mInnerDirBuffer[MAX_PATH * 2];
|
|
wchar_t mTargetFileBuffer[MAX_PATH * 2];
|
|
|
|
const wchar_t* mOuterDir;
|
|
const wchar_t* mInnerDir;
|
|
const wchar_t* mTargetFile;
|
|
|
|
public:
|
|
explicit LongNameModule(const wchar_t* aNewLeafNameAfterCopy)
|
|
: mOuterDir(nullptr), mInnerDir(nullptr), mTargetFile(nullptr) {
|
|
const wchar_t kFolderName160Chars[] =
|
|
L"0123456789ABCDEF0123456789ABCDEF"
|
|
L"0123456789ABCDEF0123456789ABCDEF"
|
|
L"0123456789ABCDEF0123456789ABCDEF"
|
|
L"0123456789ABCDEF0123456789ABCDEF"
|
|
L"0123456789ABCDEF0123456789ABCDEF";
|
|
UniquePtr<wchar_t[]> thisExe = GetFullBinaryPath();
|
|
if (!thisExe) {
|
|
return;
|
|
}
|
|
|
|
// If the buffer is too small, GetTempPathW returns the required
|
|
// length including a null character, while on a successful case
|
|
// it returns the number of copied characters which does not include
|
|
// a null character. This means len == MAX_PATH should never happen
|
|
// and len > MAX_PATH means GetTempPathW failed.
|
|
wchar_t tempDir[MAX_PATH];
|
|
DWORD len = ::GetTempPathW(MAX_PATH, tempDir);
|
|
if (!len || len >= MAX_PATH) {
|
|
return;
|
|
}
|
|
|
|
if (FAILED(::StringCbPrintfW(mOuterDirBuffer, sizeof(mOuterDirBuffer),
|
|
L"\\\\?\\%s%s", tempDir,
|
|
kFolderName160Chars)) ||
|
|
!::CreateDirectoryW(mOuterDirBuffer, nullptr)) {
|
|
return;
|
|
}
|
|
mOuterDir = mOuterDirBuffer;
|
|
|
|
if (FAILED(::StringCbPrintfW(mInnerDirBuffer, sizeof(mInnerDirBuffer),
|
|
L"\\\\?\\%s%s\\%s", tempDir,
|
|
kFolderName160Chars, kFolderName160Chars)) ||
|
|
!::CreateDirectoryW(mInnerDirBuffer, nullptr)) {
|
|
return;
|
|
}
|
|
mInnerDir = mInnerDirBuffer;
|
|
|
|
if (FAILED(::StringCbPrintfW(mTargetFileBuffer, sizeof(mTargetFileBuffer),
|
|
L"\\\\?\\%s%s\\%s\\%s", tempDir,
|
|
kFolderName160Chars, kFolderName160Chars,
|
|
aNewLeafNameAfterCopy)) ||
|
|
!::CopyFileW(thisExe.get(), mTargetFileBuffer,
|
|
/*bFailIfExists*/ TRUE)) {
|
|
return;
|
|
}
|
|
mTargetFile = mTargetFileBuffer;
|
|
}
|
|
|
|
~LongNameModule() {
|
|
if (mTargetFile) {
|
|
::DeleteFileW(mTargetFile);
|
|
}
|
|
if (mInnerDir) {
|
|
::RemoveDirectoryW(mInnerDir);
|
|
}
|
|
if (mOuterDir) {
|
|
::RemoveDirectoryW(mOuterDir);
|
|
}
|
|
}
|
|
|
|
operator const wchar_t*() const { return mTargetFile; }
|
|
};
|
|
|
|
// Make sure module info retrieved from nt::PEHeaders is the same as one
|
|
// retrieved from GetModuleInformation API.
|
|
bool CompareModuleInfo(HMODULE aModuleForApi, HMODULE aModuleForPEHeader) {
|
|
MODULEINFO moduleInfo;
|
|
if (!::GetModuleInformation(::GetCurrentProcess(), aModuleForApi, &moduleInfo,
|
|
sizeof(moduleInfo))) {
|
|
printf("TEST-FAILED | NativeNt | GetModuleInformation failed - %08lx\n",
|
|
::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
PEHeaders headers(aModuleForPEHeader);
|
|
if (!headers) {
|
|
printf("TEST-FAILED | NativeNt | Failed to instantiate PEHeaders\n");
|
|
return false;
|
|
}
|
|
|
|
Maybe<Range<const uint8_t>> bounds = headers.GetBounds();
|
|
if (!bounds) {
|
|
printf("TEST-FAILED | NativeNt | PEHeaders::GetBounds failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (bounds->length() != moduleInfo.SizeOfImage) {
|
|
printf("TEST-FAILED | NativeNt | SizeOfImage does not match\n");
|
|
return false;
|
|
}
|
|
|
|
// GetModuleInformation sets EntryPoint to 0 for executables
|
|
// except the running self.
|
|
static const HMODULE sSelf = ::GetModuleHandleW(nullptr);
|
|
if (aModuleForApi != sSelf &&
|
|
!(headers.GetFileCharacteristics() & IMAGE_FILE_DLL)) {
|
|
if (moduleInfo.EntryPoint) {
|
|
printf(
|
|
"TEST-FAIL | NativeNt | "
|
|
"GetModuleInformation returned a non-zero entrypoint "
|
|
"for an executable\n");
|
|
return false;
|
|
}
|
|
|
|
// Cannot verify PEHeaders::GetEntryPoint.
|
|
return true;
|
|
}
|
|
|
|
// For a module whose entrypoint is 0 (e.g. ntdll.dll or win32u.dll),
|
|
// MODULEINFO::EntryPoint is set to 0, while PEHeaders::GetEntryPoint
|
|
// returns the imagebase (RVA=0).
|
|
intptr_t rvaEntryPoint =
|
|
moduleInfo.EntryPoint
|
|
? reinterpret_cast<uintptr_t>(moduleInfo.EntryPoint) -
|
|
reinterpret_cast<uintptr_t>(moduleInfo.lpBaseOfDll)
|
|
: 0;
|
|
if (rvaEntryPoint < 0) {
|
|
printf("TEST-FAILED | NativeNt | MODULEINFO is invalid\n");
|
|
return false;
|
|
}
|
|
|
|
if (headers.RVAToPtr<FARPROC>(rvaEntryPoint) != headers.GetEntryPoint()) {
|
|
printf("TEST-FAILED | NativeNt | Entrypoint does not match\n");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TestModuleInfo() {
|
|
UNICODE_STRING newLeafName;
|
|
::RtlInitUnicodeString(&newLeafName,
|
|
L"\u672D\u5E4C\u5473\u564C.\u30E9\u30FC\u30E1\u30F3");
|
|
|
|
LongNameModule longNameModule(newLeafName.Buffer);
|
|
if (!longNameModule) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | "
|
|
"Failed to copy the executable to a long directory path\n");
|
|
return 1;
|
|
}
|
|
|
|
{
|
|
nsModuleHandle module(::LoadLibraryW(longNameModule));
|
|
|
|
bool detectedTarget = false;
|
|
bool passedAllModules = true;
|
|
auto moduleCallback = [&](const wchar_t* aModulePath, HMODULE aModule) {
|
|
UNICODE_STRING modulePath, moduleName;
|
|
::RtlInitUnicodeString(&modulePath, aModulePath);
|
|
GetLeafName(&moduleName, &modulePath);
|
|
if (::RtlEqualUnicodeString(&moduleName, &newLeafName,
|
|
/*aCaseInsensitive*/ TRUE)) {
|
|
detectedTarget = true;
|
|
}
|
|
|
|
if (!CompareModuleInfo(aModule, aModule)) {
|
|
passedAllModules = false;
|
|
}
|
|
};
|
|
|
|
if (!mozilla::EnumerateProcessModules(moduleCallback)) {
|
|
printf("TEST-FAILED | NativeNt | EnumerateProcessModules failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (!detectedTarget) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | "
|
|
"EnumerateProcessModules missed the target file\n");
|
|
return false;
|
|
}
|
|
|
|
if (!passedAllModules) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Make sure PEHeaders works for a module loaded with LOAD_LIBRARY_AS_DATAFILE
|
|
// as well as a module loaded normally.
|
|
bool TestModuleLoadedAsData() {
|
|
const wchar_t kNewLeafName[] = L"\u03BC\u0061\u9EBA.txt";
|
|
|
|
LongNameModule longNameModule(kNewLeafName);
|
|
if (!longNameModule) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | "
|
|
"Failed to copy the executable to a long directory path\n");
|
|
return 1;
|
|
}
|
|
|
|
const wchar_t* kManualLoadModules[] = {
|
|
L"mshtml.dll",
|
|
L"shell32.dll",
|
|
longNameModule,
|
|
};
|
|
|
|
for (const auto moduleName : kManualLoadModules) {
|
|
// Must load a module as data first,
|
|
nsModuleHandle moduleAsData(::LoadLibraryExW(
|
|
moduleName, nullptr,
|
|
LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE));
|
|
|
|
// then load a module normally to map it on a different address.
|
|
nsModuleHandle module(::LoadLibraryW(moduleName));
|
|
|
|
if (!CompareModuleInfo(module.get(), moduleAsData.get())) {
|
|
return false;
|
|
}
|
|
|
|
PEHeaders peAsData(moduleAsData.get());
|
|
PEHeaders pe(module.get());
|
|
if (!peAsData || !pe) {
|
|
printf("TEST-FAIL | NativeNt | Failed to load the module\n");
|
|
return false;
|
|
}
|
|
|
|
if (peAsData.RVAToPtr<HMODULE>(0) == pe.RVAToPtr<HMODULE>(0)) {
|
|
printf(
|
|
"TEST-FAIL | NativeNt | "
|
|
"The module should have been mapped onto two different places\n");
|
|
return false;
|
|
}
|
|
|
|
const auto* pdb1 = peAsData.GetPdbInfo();
|
|
const auto* pdb2 = pe.GetPdbInfo();
|
|
if (pdb1 && pdb2) {
|
|
if (pdb1->pdbSignature != pdb2->pdbSignature ||
|
|
pdb1->pdbAge != pdb2->pdbAge ||
|
|
strcmp(pdb1->pdbFileName, pdb2->pdbFileName)) {
|
|
printf(
|
|
"TEST-FAIL | NativeNt | "
|
|
"PDB info from the same module did not match.\n");
|
|
return false;
|
|
}
|
|
} else if (pdb1 || pdb2) {
|
|
printf(
|
|
"TEST-FAIL | NativeNt | Failed to get PDB info from the module.\n");
|
|
return false;
|
|
}
|
|
|
|
uint64_t version1, version2;
|
|
bool result1 = peAsData.GetVersionInfo(version1);
|
|
bool result2 = pe.GetVersionInfo(version2);
|
|
if (result1 && result2) {
|
|
if (version1 != version2) {
|
|
printf("TEST-FAIL | NativeNt | Version mismatch\n");
|
|
return false;
|
|
}
|
|
} else if (result1 || result2) {
|
|
printf(
|
|
"TEST-FAIL | NativeNt | Failed to get PDB info from the module.\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
LauncherResult<HMODULE> GetModuleHandleFromLeafName(const wchar_t* aName) {
|
|
UNICODE_STRING name;
|
|
::RtlInitUnicodeString(&name, aName);
|
|
return nt::GetModuleHandleFromLeafName(name);
|
|
}
|
|
|
|
// Need a non-inline function to bypass compiler optimization that the thread
|
|
// local storage pointer is cached in a register before accessing a thread-local
|
|
// variable.
|
|
MOZ_NEVER_INLINE PVOID SwapThreadLocalStoragePointer(PVOID aNewValue) {
|
|
auto oldValue = RtlGetThreadLocalStoragePointer();
|
|
RtlSetThreadLocalStoragePointerForTestingOnly(aNewValue);
|
|
return oldValue;
|
|
}
|
|
|
|
int wmain(int argc, wchar_t* argv[]) {
|
|
UNICODE_STRING normal;
|
|
::RtlInitUnicodeString(&normal, kNormal);
|
|
|
|
UNICODE_STRING hex12;
|
|
::RtlInitUnicodeString(&hex12, kHex12);
|
|
|
|
UNICODE_STRING hex16;
|
|
::RtlInitUnicodeString(&hex16, kHex16);
|
|
|
|
UNICODE_STRING hex24;
|
|
::RtlInitUnicodeString(&hex24, kHex24);
|
|
|
|
UNICODE_STRING hex8;
|
|
::RtlInitUnicodeString(&hex8, kHex8);
|
|
|
|
UNICODE_STRING nonHex12;
|
|
::RtlInitUnicodeString(&nonHex12, kNonHex12);
|
|
|
|
UNICODE_STRING hex13;
|
|
::RtlInitUnicodeString(&hex13, kHex13);
|
|
|
|
UNICODE_STRING hex11;
|
|
::RtlInitUnicodeString(&hex11, kHex11);
|
|
|
|
UNICODE_STRING hex15;
|
|
::RtlInitUnicodeString(&hex15, kHex15);
|
|
|
|
UNICODE_STRING hex17;
|
|
::RtlInitUnicodeString(&hex17, kHex17);
|
|
|
|
UNICODE_STRING prefixedHex16;
|
|
::RtlInitUnicodeString(&prefixedHex16, kPrefixedHex16);
|
|
|
|
EXPECT_FAIL(Contains12DigitHexString, normal);
|
|
EXPECT_SUCCESS(Contains12DigitHexString, hex12);
|
|
EXPECT_FAIL(Contains12DigitHexString, hex13);
|
|
EXPECT_FAIL(Contains12DigitHexString, hex11);
|
|
EXPECT_FAIL(Contains12DigitHexString, hex16);
|
|
EXPECT_FAIL(Contains12DigitHexString, nonHex12);
|
|
|
|
EXPECT_FAIL(IsFileNameAtLeast16HexDigits, normal);
|
|
EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex12);
|
|
EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex24);
|
|
EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex16);
|
|
EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex17);
|
|
EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex8);
|
|
EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex15);
|
|
EXPECT_FAIL(IsFileNameAtLeast16HexDigits, prefixedHex16);
|
|
|
|
if (RtlGetProcessHeap() != ::GetProcessHeap()) {
|
|
printf("TEST-FAILED | NativeNt | RtlGetProcessHeap() is broken\n");
|
|
return 1;
|
|
}
|
|
|
|
#ifdef HAVE_SEH_EXCEPTIONS
|
|
PVOID origTlsHead = nullptr;
|
|
bool isExceptionThrown = false;
|
|
// Touch sTlsData.get() several times to prevent the call to sTlsData.set()
|
|
// from being optimized out in PGO build.
|
|
printf("sTlsData#1 = %08x\n", sTlsData.get());
|
|
MOZ_SEH_TRY {
|
|
// Need to call SwapThreadLocalStoragePointer inside __try to make sure
|
|
// accessing sTlsData is caught by SEH. This is due to clang's design.
|
|
// https://bugs.llvm.org/show_bug.cgi?id=44174.
|
|
origTlsHead = SwapThreadLocalStoragePointer(nullptr);
|
|
sTlsData.set(~kTlsDataValue);
|
|
}
|
|
MOZ_SEH_EXCEPT(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION
|
|
? EXCEPTION_EXECUTE_HANDLER
|
|
: EXCEPTION_CONTINUE_SEARCH) {
|
|
isExceptionThrown = true;
|
|
}
|
|
SwapThreadLocalStoragePointer(origTlsHead);
|
|
printf("sTlsData#2 = %08x\n", sTlsData.get());
|
|
sTlsData.set(kTlsDataValue);
|
|
printf("sTlsData#3 = %08x\n", sTlsData.get());
|
|
if (!isExceptionThrown || sTlsData.get() != kTlsDataValue) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | RtlGetThreadLocalStoragePointer() is "
|
|
"broken\n");
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
if (RtlGetCurrentThreadId() != ::GetCurrentThreadId()) {
|
|
printf("TEST-FAILED | NativeNt | RtlGetCurrentThreadId() is broken\n");
|
|
return 1;
|
|
}
|
|
|
|
const wchar_t kKernel32[] = L"kernel32.dll";
|
|
DWORD verInfoSize = ::GetFileVersionInfoSizeW(kKernel32, nullptr);
|
|
if (!verInfoSize) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Call to GetFileVersionInfoSizeW failed with "
|
|
"code %lu\n",
|
|
::GetLastError());
|
|
return 1;
|
|
}
|
|
|
|
auto verInfoBuf = MakeUnique<char[]>(verInfoSize);
|
|
|
|
if (!::GetFileVersionInfoW(kKernel32, 0, verInfoSize, verInfoBuf.get())) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Call to GetFileVersionInfoW failed with code "
|
|
"%lu\n",
|
|
::GetLastError());
|
|
return 1;
|
|
}
|
|
|
|
UINT len;
|
|
VS_FIXEDFILEINFO* fixedFileInfo = nullptr;
|
|
if (!::VerQueryValueW(verInfoBuf.get(), L"\\", (LPVOID*)&fixedFileInfo,
|
|
&len)) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Call to VerQueryValueW failed with code "
|
|
"%lu\n",
|
|
::GetLastError());
|
|
return 1;
|
|
}
|
|
|
|
const uint64_t expectedVersion =
|
|
(static_cast<uint64_t>(fixedFileInfo->dwFileVersionMS) << 32) |
|
|
static_cast<uint64_t>(fixedFileInfo->dwFileVersionLS);
|
|
|
|
PEHeaders k32headers(::GetModuleHandleW(kKernel32));
|
|
if (!k32headers) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Failed parsing kernel32.dll's PE headers\n");
|
|
return 1;
|
|
}
|
|
|
|
uint64_t version;
|
|
if (!k32headers.GetVersionInfo(version)) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Unable to obtain version information from "
|
|
"kernel32.dll\n");
|
|
return 1;
|
|
}
|
|
|
|
if (version != expectedVersion) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | kernel32.dll's detected version "
|
|
"(0x%016llX) does not match expected version (0x%016llX)\n",
|
|
version, expectedVersion);
|
|
return 1;
|
|
}
|
|
|
|
Maybe<Span<IMAGE_THUNK_DATA>> iatThunks =
|
|
k32headers.GetIATThunksForModule("kernel32.dll");
|
|
if (iatThunks) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Detected the IAT thunk for kernel32 "
|
|
"in kernel32.dll\n");
|
|
return 1;
|
|
}
|
|
|
|
const mozilla::nt::CodeViewRecord70* debugInfo = k32headers.GetPdbInfo();
|
|
if (!debugInfo) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Unable to obtain debug information from "
|
|
"kernel32.dll\n");
|
|
return 1;
|
|
}
|
|
|
|
#ifndef WIN32 // failure on windows10x32
|
|
if (stricmp(debugInfo->pdbFileName, "kernel32.pdb")) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Unexpected PDB filename "
|
|
"in kernel32.dll: %s\n",
|
|
debugInfo->pdbFileName);
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
PEHeaders ntdllheaders(::GetModuleHandleW(L"ntdll.dll"));
|
|
|
|
auto ntdllBoundaries = ntdllheaders.GetBounds();
|
|
if (!ntdllBoundaries) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | "
|
|
"Unable to obtain the boundaries of ntdll.dll\n");
|
|
return 1;
|
|
}
|
|
|
|
iatThunks =
|
|
k32headers.GetIATThunksForModule("ntdll.dll", ntdllBoundaries.ptr());
|
|
if (!iatThunks) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | Unable to find the IAT thunk for "
|
|
"ntdll.dll in kernel32.dll\n");
|
|
return 1;
|
|
}
|
|
|
|
// To test the Ex version of API, we purposely get a real handle
|
|
// instead of a pseudo handle.
|
|
nsAutoHandle process(
|
|
::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()));
|
|
if (!process) {
|
|
printf("TEST-FAILED | NativeNt | OpenProcess() failed - %08lx\n",
|
|
::GetLastError());
|
|
return 1;
|
|
}
|
|
|
|
// Test Null page, Heap, Mapped image, and Invalid handle
|
|
if (!TestVirtualQuery(process, nullptr) || !TestVirtualQuery(process, argv) ||
|
|
!TestVirtualQuery(process, kNormal) ||
|
|
!TestVirtualQuery(nullptr, kNormal)) {
|
|
return 1;
|
|
}
|
|
|
|
auto moduleResult = GetModuleHandleFromLeafName(kKernel32);
|
|
if (moduleResult.isErr() ||
|
|
moduleResult.inspect() != k32headers.template RVAToPtr<HMODULE>(0)) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | "
|
|
"GetModuleHandleFromLeafName returns a wrong value.\n");
|
|
return 1;
|
|
}
|
|
|
|
moduleResult = GetModuleHandleFromLeafName(L"invalid");
|
|
if (moduleResult.isOk()) {
|
|
printf(
|
|
"TEST-FAILED | NativeNt | "
|
|
"GetModuleHandleFromLeafName unexpectedly returns a value.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!TestModuleInfo()) {
|
|
return 1;
|
|
}
|
|
|
|
if (!TestModuleLoadedAsData()) {
|
|
return 1;
|
|
}
|
|
|
|
printf("TEST-PASS | NativeNt | All tests ran successfully\n");
|
|
return 0;
|
|
}
|