TestNativeNt.cpp (20661B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #include "nscore.h" 8 #include "mozilla/NativeNt.h" 9 #include "mozilla/ThreadLocal.h" 10 #include "mozilla/UniquePtr.h" 11 #include "mozilla/WindowsEnumProcessModules.h" 12 13 #include <limits> 14 #include <stdio.h> 15 #include <windows.h> 16 #include <strsafe.h> 17 18 const wchar_t kNormal[] = L"Foo.dll"; 19 const wchar_t kHex12[] = L"Foo.ABCDEF012345.dll"; 20 const wchar_t kHex15[] = L"ABCDEF012345678.dll"; 21 const wchar_t kHex16[] = L"ABCDEF0123456789.dll"; 22 const wchar_t kHex17[] = L"ABCDEF0123456789a.dll"; 23 const wchar_t kHex24[] = L"ABCDEF0123456789cdabef98.dll"; 24 const wchar_t kHex8[] = L"01234567.dll"; 25 const wchar_t kNonHex12[] = L"Foo.ABCDEFG12345.dll"; 26 const wchar_t kHex13[] = L"Foo.ABCDEF0123456.dll"; 27 const wchar_t kHex11[] = L"Foo.ABCDEF01234.dll"; 28 const wchar_t kPrefixedHex16[] = L"Pabcdef0123456789.dll"; 29 const uint32_t kTlsDataValue = 1234; 30 static MOZ_THREAD_LOCAL(uint32_t) sTlsData; 31 32 // Need non-inline functions to bypass compiler optimization that the thread 33 // local storage pointer is cached in a register before accessing a thread-local 34 // variable. See bug 1803322 for a motivating example. 35 MOZ_NEVER_INLINE uint32_t getTlsData() { return sTlsData.get(); } 36 MOZ_NEVER_INLINE void setTlsData(uint32_t x) { sTlsData.set(x); } 37 38 const char kFailFmt[] = 39 "TEST-FAILED | NativeNt | %s(%s) should have returned %s but did not\n"; 40 41 #define RUN_TEST(fn, varName, expected) \ 42 if (fn(varName) == !expected) { \ 43 printf(kFailFmt, #fn, #varName, #expected); \ 44 return 1; \ 45 } 46 47 #define EXPECT_FAIL(fn, varName) RUN_TEST(fn, varName, false) 48 49 #define EXPECT_SUCCESS(fn, varName) RUN_TEST(fn, varName, true) 50 51 using namespace mozilla; 52 using namespace mozilla::nt; 53 54 bool TestVirtualQuery(HANDLE aProcess, LPCVOID aAddress) { 55 MEMORY_BASIC_INFORMATION info1 = {}, info2 = {}; 56 SIZE_T result1 = ::VirtualQueryEx(aProcess, aAddress, &info1, sizeof(info1)), 57 result2 = mozilla::nt::VirtualQueryEx(aProcess, aAddress, &info2, 58 sizeof(info2)); 59 if (result1 != result2) { 60 printf("TEST-FAILED | NativeNt | The returned values mismatch\n"); 61 return false; 62 } 63 64 if (!result1) { 65 // Both APIs failed. 66 return true; 67 } 68 69 if (memcmp(&info1, &info2, result1) != 0) { 70 printf("TEST-FAILED | NativeNt | The returned structures mismatch\n"); 71 return false; 72 } 73 74 return true; 75 } 76 77 // This class copies the self executable file to the %temp%\<outer>\<inner> 78 // folder. The length of its path is longer than MAX_PATH. 79 class LongNameModule { 80 wchar_t mOuterDirBuffer[MAX_PATH]; 81 wchar_t mInnerDirBuffer[MAX_PATH * 2]; 82 wchar_t mTargetFileBuffer[MAX_PATH * 2]; 83 84 const wchar_t* mOuterDir; 85 const wchar_t* mInnerDir; 86 const wchar_t* mTargetFile; 87 88 public: 89 explicit LongNameModule(const wchar_t* aNewLeafNameAfterCopy) 90 : mOuterDir(nullptr), mInnerDir(nullptr), mTargetFile(nullptr) { 91 const wchar_t kFolderName160Chars[] = 92 L"0123456789ABCDEF0123456789ABCDEF" 93 L"0123456789ABCDEF0123456789ABCDEF" 94 L"0123456789ABCDEF0123456789ABCDEF" 95 L"0123456789ABCDEF0123456789ABCDEF" 96 L"0123456789ABCDEF0123456789ABCDEF"; 97 UniquePtr<wchar_t[]> thisExe = GetFullBinaryPath(); 98 if (!thisExe) { 99 return; 100 } 101 102 // If the buffer is too small, GetTempPathW returns the required 103 // length including a null character, while on a successful case 104 // it returns the number of copied characters which does not include 105 // a null character. This means len == MAX_PATH should never happen 106 // and len > MAX_PATH means GetTempPathW failed. 107 wchar_t tempDir[MAX_PATH]; 108 DWORD len = ::GetTempPathW(MAX_PATH, tempDir); 109 if (!len || len >= MAX_PATH) { 110 return; 111 } 112 113 if (FAILED(::StringCbPrintfW(mOuterDirBuffer, sizeof(mOuterDirBuffer), 114 L"\\\\?\\%s%s", tempDir, 115 kFolderName160Chars)) || 116 !::CreateDirectoryW(mOuterDirBuffer, nullptr)) { 117 return; 118 } 119 mOuterDir = mOuterDirBuffer; 120 121 if (FAILED(::StringCbPrintfW(mInnerDirBuffer, sizeof(mInnerDirBuffer), 122 L"\\\\?\\%s%s\\%s", tempDir, 123 kFolderName160Chars, kFolderName160Chars)) || 124 !::CreateDirectoryW(mInnerDirBuffer, nullptr)) { 125 return; 126 } 127 mInnerDir = mInnerDirBuffer; 128 129 if (FAILED(::StringCbPrintfW(mTargetFileBuffer, sizeof(mTargetFileBuffer), 130 L"\\\\?\\%s%s\\%s\\%s", tempDir, 131 kFolderName160Chars, kFolderName160Chars, 132 aNewLeafNameAfterCopy)) || 133 !::CopyFileW(thisExe.get(), mTargetFileBuffer, 134 /*bFailIfExists*/ TRUE)) { 135 return; 136 } 137 mTargetFile = mTargetFileBuffer; 138 } 139 140 ~LongNameModule() { 141 if (mTargetFile) { 142 ::DeleteFileW(mTargetFile); 143 } 144 if (mInnerDir) { 145 ::RemoveDirectoryW(mInnerDir); 146 } 147 if (mOuterDir) { 148 ::RemoveDirectoryW(mOuterDir); 149 } 150 } 151 152 operator const wchar_t*() const { return mTargetFile; } 153 }; 154 155 // Make sure module info retrieved from nt::PEHeaders is the same as one 156 // retrieved from GetModuleInformation API. 157 bool CompareModuleInfo(HMODULE aModuleForApi, HMODULE aModuleForPEHeader) { 158 MODULEINFO moduleInfo; 159 if (!::GetModuleInformation(::GetCurrentProcess(), aModuleForApi, &moduleInfo, 160 sizeof(moduleInfo))) { 161 printf("TEST-FAILED | NativeNt | GetModuleInformation failed - %08lx\n", 162 ::GetLastError()); 163 return false; 164 } 165 166 PEHeaders headers(aModuleForPEHeader); 167 if (!headers) { 168 printf("TEST-FAILED | NativeNt | Failed to instantiate PEHeaders\n"); 169 return false; 170 } 171 172 Maybe<Range<const uint8_t>> bounds = headers.GetBounds(); 173 if (!bounds) { 174 printf("TEST-FAILED | NativeNt | PEHeaders::GetBounds failed\n"); 175 return false; 176 } 177 178 if (bounds->length() != moduleInfo.SizeOfImage) { 179 printf("TEST-FAILED | NativeNt | SizeOfImage does not match\n"); 180 return false; 181 } 182 183 // GetModuleInformation sets EntryPoint to 0 for executables 184 // except the running self. 185 static const HMODULE sSelf = ::GetModuleHandleW(nullptr); 186 if (aModuleForApi != sSelf && 187 !(headers.GetFileCharacteristics() & IMAGE_FILE_DLL)) { 188 if (moduleInfo.EntryPoint) { 189 printf( 190 "TEST-FAIL | NativeNt | " 191 "GetModuleInformation returned a non-zero entrypoint " 192 "for an executable\n"); 193 return false; 194 } 195 196 // Cannot verify PEHeaders::GetEntryPoint. 197 return true; 198 } 199 200 // For a module whose entrypoint is 0 (e.g. ntdll.dll or win32u.dll), 201 // MODULEINFO::EntryPoint is set to 0, while PEHeaders::GetEntryPoint 202 // returns the imagebase (RVA=0). 203 intptr_t rvaEntryPoint = 204 moduleInfo.EntryPoint 205 ? reinterpret_cast<uintptr_t>(moduleInfo.EntryPoint) - 206 reinterpret_cast<uintptr_t>(moduleInfo.lpBaseOfDll) 207 : 0; 208 if (rvaEntryPoint < 0) { 209 printf("TEST-FAILED | NativeNt | MODULEINFO is invalid\n"); 210 return false; 211 } 212 213 if (headers.RVAToPtr<FARPROC>(rvaEntryPoint) != headers.GetEntryPoint()) { 214 printf("TEST-FAILED | NativeNt | Entrypoint does not match\n"); 215 return false; 216 } 217 218 return true; 219 } 220 221 bool TestModuleInfo() { 222 UNICODE_STRING newLeafName; 223 ::RtlInitUnicodeString(&newLeafName, 224 L"\u672D\u5E4C\u5473\u564C.\u30E9\u30FC\u30E1\u30F3"); 225 226 LongNameModule longNameModule(newLeafName.Buffer); 227 if (!longNameModule) { 228 printf( 229 "TEST-FAILED | NativeNt | " 230 "Failed to copy the executable to a long directory path\n"); 231 return 1; 232 } 233 234 { 235 nsModuleHandle module(::LoadLibraryW(longNameModule)); 236 237 bool detectedTarget = false; 238 bool passedAllModules = true; 239 auto moduleCallback = [&](const wchar_t* aModulePath, HMODULE aModule) { 240 UNICODE_STRING modulePath, moduleName; 241 ::RtlInitUnicodeString(&modulePath, aModulePath); 242 GetLeafName(&moduleName, &modulePath); 243 if (::RtlEqualUnicodeString(&moduleName, &newLeafName, 244 /*aCaseInsensitive*/ TRUE)) { 245 detectedTarget = true; 246 } 247 248 if (!CompareModuleInfo(aModule, aModule)) { 249 passedAllModules = false; 250 } 251 }; 252 253 if (!mozilla::EnumerateProcessModules(moduleCallback)) { 254 printf("TEST-FAILED | NativeNt | EnumerateProcessModules failed\n"); 255 return false; 256 } 257 258 if (!detectedTarget) { 259 printf( 260 "TEST-FAILED | NativeNt | " 261 "EnumerateProcessModules missed the target file\n"); 262 return false; 263 } 264 265 if (!passedAllModules) { 266 return false; 267 } 268 } 269 270 return true; 271 } 272 273 // Make sure PEHeaders works for a module loaded with LOAD_LIBRARY_AS_DATAFILE 274 // as well as a module loaded normally. 275 bool TestModuleLoadedAsData() { 276 const wchar_t kNewLeafName[] = L"\u03BC\u0061\u9EBA.txt"; 277 278 LongNameModule longNameModule(kNewLeafName); 279 if (!longNameModule) { 280 printf( 281 "TEST-FAILED | NativeNt | " 282 "Failed to copy the executable to a long directory path\n"); 283 return 1; 284 } 285 286 const wchar_t* kManualLoadModules[] = { 287 L"mshtml.dll", 288 L"shell32.dll", 289 longNameModule, 290 }; 291 292 for (const auto moduleName : kManualLoadModules) { 293 // Must load a module as data first, 294 nsModuleHandle moduleAsData(::LoadLibraryExW( 295 moduleName, nullptr, 296 LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE)); 297 298 // then load a module normally to map it on a different address. 299 nsModuleHandle module(::LoadLibraryW(moduleName)); 300 301 if (!CompareModuleInfo(module.get(), moduleAsData.get())) { 302 return false; 303 } 304 305 PEHeaders peAsData(moduleAsData.get()); 306 PEHeaders pe(module.get()); 307 if (!peAsData || !pe) { 308 printf("TEST-FAIL | NativeNt | Failed to load the module\n"); 309 return false; 310 } 311 312 if (peAsData.RVAToPtr<HMODULE>(0) == pe.RVAToPtr<HMODULE>(0)) { 313 printf( 314 "TEST-FAIL | NativeNt | " 315 "The module should have been mapped onto two different places\n"); 316 return false; 317 } 318 319 const auto* pdb1 = peAsData.GetPdbInfo(); 320 const auto* pdb2 = pe.GetPdbInfo(); 321 if (pdb1 && pdb2) { 322 if (pdb1->pdbSignature != pdb2->pdbSignature || 323 pdb1->pdbAge != pdb2->pdbAge || 324 strcmp(pdb1->pdbFileName, pdb2->pdbFileName)) { 325 printf( 326 "TEST-FAIL | NativeNt | " 327 "PDB info from the same module did not match.\n"); 328 return false; 329 } 330 } else if (pdb1 || pdb2) { 331 printf( 332 "TEST-FAIL | NativeNt | Failed to get PDB info from the module.\n"); 333 return false; 334 } 335 336 uint64_t version1, version2; 337 bool result1 = peAsData.GetVersionInfo(version1); 338 bool result2 = pe.GetVersionInfo(version2); 339 if (result1 && result2) { 340 if (version1 != version2) { 341 printf("TEST-FAIL | NativeNt | Version mismatch\n"); 342 return false; 343 } 344 } else if (result1 || result2) { 345 printf( 346 "TEST-FAIL | NativeNt | Failed to get PDB info from the module.\n"); 347 return false; 348 } 349 } 350 351 return true; 352 } 353 354 LauncherResult<HMODULE> GetModuleHandleFromLeafName(const wchar_t* aName) { 355 UNICODE_STRING name; 356 ::RtlInitUnicodeString(&name, aName); 357 return nt::GetModuleHandleFromLeafName(name); 358 } 359 360 // Need a non-inline function to bypass compiler optimization that the thread 361 // local storage pointer is cached in a register before accessing a thread-local 362 // variable. 363 MOZ_NEVER_INLINE PVOID SwapThreadLocalStoragePointer(PVOID aNewValue) { 364 auto oldValue = RtlGetThreadLocalStoragePointer(); 365 RtlSetThreadLocalStoragePointerForTestingOnly(aNewValue); 366 return oldValue; 367 } 368 369 #if defined(_M_X64) 370 bool TestCheckStack() { 371 auto stackBase = reinterpret_cast<uint8_t*>(RtlGetThreadStackBase()); 372 auto stackLimit = reinterpret_cast<uint8_t*>(RtlGetThreadStackLimit()); 373 uint8_t* stackPointer = nullptr; 374 asm volatile("mov %%rsp, %0;" : "=r"(stackPointer)); 375 if (!(stackLimit < stackBase && stackLimit <= stackPointer && 376 stackPointer < stackBase)) { 377 printf("TEST-FAIL | NativeNt | Stack addresses are not coherent.\n"); 378 return false; 379 } 380 uintptr_t committedBytes = stackPointer - stackLimit; 381 const uint32_t maxExtraCommittedBytes = 0x10000; 382 if ((committedBytes + maxExtraCommittedBytes) > 383 std::numeric_limits<uint32_t>::max()) { 384 printf( 385 "TEST-FAIL | NativeNt | The stack limit is too high to perform the " 386 "test.\n"); 387 return false; 388 } 389 for (uint32_t extraSize = 0; extraSize < maxExtraCommittedBytes; 390 ++extraSize) { 391 CheckStack(static_cast<uint32_t>(committedBytes) + extraSize); 392 auto expectedNewLimit = stackLimit - ((extraSize + 0xFFF) & ~0xFFF); 393 if (expectedNewLimit != RtlGetThreadStackLimit()) { 394 printf( 395 "TEST-FAIL | NativeNt | CheckStack did not grow the stack " 396 "correctly (expected: %p, got: %p).\n", 397 expectedNewLimit, RtlGetThreadStackLimit()); 398 return false; 399 } 400 } 401 return true; 402 } 403 #endif // _M_X64 404 405 int wmain(int argc, wchar_t* argv[]) { 406 UNICODE_STRING normal; 407 ::RtlInitUnicodeString(&normal, kNormal); 408 409 UNICODE_STRING hex12; 410 ::RtlInitUnicodeString(&hex12, kHex12); 411 412 UNICODE_STRING hex16; 413 ::RtlInitUnicodeString(&hex16, kHex16); 414 415 UNICODE_STRING hex24; 416 ::RtlInitUnicodeString(&hex24, kHex24); 417 418 UNICODE_STRING hex8; 419 ::RtlInitUnicodeString(&hex8, kHex8); 420 421 UNICODE_STRING nonHex12; 422 ::RtlInitUnicodeString(&nonHex12, kNonHex12); 423 424 UNICODE_STRING hex13; 425 ::RtlInitUnicodeString(&hex13, kHex13); 426 427 UNICODE_STRING hex11; 428 ::RtlInitUnicodeString(&hex11, kHex11); 429 430 UNICODE_STRING hex15; 431 ::RtlInitUnicodeString(&hex15, kHex15); 432 433 UNICODE_STRING hex17; 434 ::RtlInitUnicodeString(&hex17, kHex17); 435 436 UNICODE_STRING prefixedHex16; 437 ::RtlInitUnicodeString(&prefixedHex16, kPrefixedHex16); 438 439 EXPECT_FAIL(Contains12DigitHexString, normal); 440 EXPECT_SUCCESS(Contains12DigitHexString, hex12); 441 EXPECT_FAIL(Contains12DigitHexString, hex13); 442 EXPECT_FAIL(Contains12DigitHexString, hex11); 443 EXPECT_FAIL(Contains12DigitHexString, hex16); 444 EXPECT_FAIL(Contains12DigitHexString, nonHex12); 445 446 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, normal); 447 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex12); 448 EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex24); 449 EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex16); 450 EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex17); 451 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex8); 452 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex15); 453 EXPECT_FAIL(IsFileNameAtLeast16HexDigits, prefixedHex16); 454 455 if (RtlGetProcessHeap() != ::GetProcessHeap()) { 456 printf("TEST-FAILED | NativeNt | RtlGetProcessHeap() is broken\n"); 457 return 1; 458 } 459 460 #ifdef HAVE_SEH_EXCEPTIONS 461 PVOID origTlsHead = nullptr; 462 bool isExceptionThrown = false; 463 // Touch sTlsData.get() several times to prevent the call to sTlsData.set() 464 // from being optimized out in PGO build. 465 printf("sTlsData#1 = %08x\n", getTlsData()); 466 MOZ_SEH_TRY { 467 // Need to call SwapThreadLocalStoragePointer inside __try to make sure 468 // accessing sTlsData is caught by SEH. This is due to clang's design. 469 // https://bugs.llvm.org/show_bug.cgi?id=44174. 470 origTlsHead = SwapThreadLocalStoragePointer(nullptr); 471 setTlsData(~kTlsDataValue); 472 } 473 MOZ_SEH_EXCEPT(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION 474 ? EXCEPTION_EXECUTE_HANDLER 475 : EXCEPTION_CONTINUE_SEARCH) { 476 isExceptionThrown = true; 477 } 478 SwapThreadLocalStoragePointer(origTlsHead); 479 printf("sTlsData#2 = %08x\n", getTlsData()); 480 setTlsData(kTlsDataValue); 481 printf("sTlsData#3 = %08x\n", getTlsData()); 482 if (!isExceptionThrown || getTlsData() != kTlsDataValue) { 483 printf( 484 "TEST-FAILED | NativeNt | RtlGetThreadLocalStoragePointer() is " 485 "broken\n"); 486 return 1; 487 } 488 #endif 489 490 if (RtlGetCurrentThreadId() != ::GetCurrentThreadId()) { 491 printf("TEST-FAILED | NativeNt | RtlGetCurrentThreadId() is broken\n"); 492 return 1; 493 } 494 495 const wchar_t kKernel32[] = L"kernel32.dll"; 496 DWORD verInfoSize = ::GetFileVersionInfoSizeW(kKernel32, nullptr); 497 if (!verInfoSize) { 498 printf( 499 "TEST-FAILED | NativeNt | Call to GetFileVersionInfoSizeW failed with " 500 "code %lu\n", 501 ::GetLastError()); 502 return 1; 503 } 504 505 auto verInfoBuf = MakeUnique<char[]>(verInfoSize); 506 507 if (!::GetFileVersionInfoW(kKernel32, 0, verInfoSize, verInfoBuf.get())) { 508 printf( 509 "TEST-FAILED | NativeNt | Call to GetFileVersionInfoW failed with code " 510 "%lu\n", 511 ::GetLastError()); 512 return 1; 513 } 514 515 UINT len; 516 VS_FIXEDFILEINFO* fixedFileInfo = nullptr; 517 if (!::VerQueryValueW(verInfoBuf.get(), L"\\", (LPVOID*)&fixedFileInfo, 518 &len)) { 519 printf( 520 "TEST-FAILED | NativeNt | Call to VerQueryValueW failed with code " 521 "%lu\n", 522 ::GetLastError()); 523 return 1; 524 } 525 526 const uint64_t expectedVersion = 527 (static_cast<uint64_t>(fixedFileInfo->dwFileVersionMS) << 32) | 528 static_cast<uint64_t>(fixedFileInfo->dwFileVersionLS); 529 530 PEHeaders k32headers(::GetModuleHandleW(kKernel32)); 531 if (!k32headers) { 532 printf( 533 "TEST-FAILED | NativeNt | Failed parsing kernel32.dll's PE headers\n"); 534 return 1; 535 } 536 537 uint64_t version; 538 if (!k32headers.GetVersionInfo(version)) { 539 printf( 540 "TEST-FAILED | NativeNt | Unable to obtain version information from " 541 "kernel32.dll\n"); 542 return 1; 543 } 544 545 if (version != expectedVersion) { 546 printf( 547 "TEST-FAILED | NativeNt | kernel32.dll's detected version " 548 "(0x%016llX) does not match expected version (0x%016llX)\n", 549 version, expectedVersion); 550 return 1; 551 } 552 553 Maybe<Span<IMAGE_THUNK_DATA>> iatThunks = 554 k32headers.GetIATThunksForModule("kernel32.dll"); 555 if (iatThunks) { 556 printf( 557 "TEST-FAILED | NativeNt | Detected the IAT thunk for kernel32 " 558 "in kernel32.dll\n"); 559 return 1; 560 } 561 562 const mozilla::nt::CodeViewRecord70* debugInfo = k32headers.GetPdbInfo(); 563 if (!debugInfo) { 564 printf( 565 "TEST-FAILED | NativeNt | Unable to obtain debug information from " 566 "kernel32.dll\n"); 567 return 1; 568 } 569 570 #ifndef WIN32 // failure on windows10x32 571 if (stricmp(debugInfo->pdbFileName, "kernel32.pdb")) { 572 printf( 573 "TEST-FAILED | NativeNt | Unexpected PDB filename " 574 "in kernel32.dll: %s\n", 575 debugInfo->pdbFileName); 576 return 1; 577 } 578 #endif 579 580 PEHeaders ntdllheaders(::GetModuleHandleW(L"ntdll.dll")); 581 582 auto ntdllBoundaries = ntdllheaders.GetBounds(); 583 if (!ntdllBoundaries) { 584 printf( 585 "TEST-FAILED | NativeNt | " 586 "Unable to obtain the boundaries of ntdll.dll\n"); 587 return 1; 588 } 589 590 iatThunks = 591 k32headers.GetIATThunksForModule("ntdll.dll", ntdllBoundaries.ptr()); 592 if (!iatThunks) { 593 printf( 594 "TEST-FAILED | NativeNt | Unable to find the IAT thunk for " 595 "ntdll.dll in kernel32.dll\n"); 596 return 1; 597 } 598 599 // To test the Ex version of API, we purposely get a real handle 600 // instead of a pseudo handle. 601 nsAutoHandle process( 602 ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId())); 603 if (!process) { 604 printf("TEST-FAILED | NativeNt | OpenProcess() failed - %08lx\n", 605 ::GetLastError()); 606 return 1; 607 } 608 609 // Test Null page, Heap, Mapped image, and Invalid handle 610 if (!TestVirtualQuery(process, nullptr) || !TestVirtualQuery(process, argv) || 611 !TestVirtualQuery(process, kNormal) || 612 !TestVirtualQuery(nullptr, kNormal)) { 613 return 1; 614 } 615 616 auto moduleResult = GetModuleHandleFromLeafName(kKernel32); 617 if (moduleResult.isErr() || 618 moduleResult.inspect() != k32headers.template RVAToPtr<HMODULE>(0)) { 619 printf( 620 "TEST-FAILED | NativeNt | " 621 "GetModuleHandleFromLeafName returns a wrong value.\n"); 622 return 1; 623 } 624 625 moduleResult = GetModuleHandleFromLeafName(L"invalid"); 626 if (moduleResult.isOk()) { 627 printf( 628 "TEST-FAILED | NativeNt | " 629 "GetModuleHandleFromLeafName unexpectedly returns a value.\n"); 630 return 1; 631 } 632 633 if (!TestModuleInfo()) { 634 return 1; 635 } 636 637 if (!TestModuleLoadedAsData()) { 638 return 1; 639 } 640 641 #if defined(_M_X64) 642 if (!TestCheckStack()) { 643 return 1; 644 } 645 #endif // _M_X64 646 647 printf("TEST-PASS | NativeNt | All tests ran successfully\n"); 648 return 0; 649 }