NativeNt.h (54874B)
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 #ifndef mozilla_NativeNt_h 8 #define mozilla_NativeNt_h 9 10 #include <stdint.h> 11 #include <windows.h> 12 #include <winnt.h> 13 #include <winternl.h> 14 15 #include <algorithm> 16 #include <utility> 17 18 #include "mozilla/Attributes.h" 19 #include "mozilla/CheckedArithmetic.h" 20 #include "mozilla/Maybe.h" 21 #include "mozilla/Range.h" 22 #include "mozilla/Span.h" 23 #include "mozilla/WinHeaderOnlyUtils.h" 24 #include "mozilla/interceptor/MMPolicies.h" 25 #include "mozilla/interceptor/TargetFunction.h" 26 27 #if defined(MOZILLA_INTERNAL_API) 28 # include "nsString.h" 29 #endif // defined(MOZILLA_INTERNAL_API) 30 31 // The declarations within this #if block are intended to be used for initial 32 // process initialization ONLY. You probably don't want to be using these in 33 // normal Gecko code! 34 #if !defined(MOZILLA_INTERNAL_API) 35 36 extern "C" { 37 38 # if !defined(STATUS_ACCESS_DENIED) 39 # define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L) 40 # endif // !defined(STATUS_ACCESS_DENIED) 41 42 # if !defined(STATUS_DLL_NOT_FOUND) 43 # define STATUS_DLL_NOT_FOUND ((NTSTATUS)0xC0000135L) 44 # endif // !defined(STATUS_DLL_NOT_FOUND) 45 46 # if !defined(STATUS_UNSUCCESSFUL) 47 # define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) 48 # endif // !defined(STATUS_UNSUCCESSFUL) 49 50 # if !defined(STATUS_INFO_LENGTH_MISMATCH) 51 # define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) 52 # endif 53 54 enum SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 }; 55 56 NTSTATUS NTAPI NtMapViewOfSection( 57 HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits, 58 SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize, 59 SECTION_INHERIT aInheritDisposition, ULONG aAllocationType, 60 ULONG aProtectionFlags); 61 62 NTSTATUS NTAPI NtUnmapViewOfSection(HANDLE aProcess, PVOID aBaseAddress); 63 64 enum MEMORY_INFORMATION_CLASS { 65 MemoryBasicInformation = 0, 66 MemorySectionName = 2 67 }; 68 69 // NB: When allocating, space for the buffer must also be included 70 typedef struct _MEMORY_SECTION_NAME { 71 UNICODE_STRING mSectionFileName; 72 } MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME; 73 74 NTSTATUS NTAPI NtQueryVirtualMemory(HANDLE aProcess, PVOID aBaseAddress, 75 MEMORY_INFORMATION_CLASS aMemInfoClass, 76 PVOID aMemInfo, SIZE_T aMemInfoLen, 77 PSIZE_T aReturnLen); 78 79 LONG NTAPI RtlCompareUnicodeString(PCUNICODE_STRING aStr1, 80 PCUNICODE_STRING aStr2, 81 BOOLEAN aCaseInsensitive); 82 83 BOOLEAN NTAPI RtlEqualUnicodeString(PCUNICODE_STRING aStr1, 84 PCUNICODE_STRING aStr2, 85 BOOLEAN aCaseInsensitive); 86 87 NTSTATUS NTAPI RtlGetVersion(PRTL_OSVERSIONINFOW aOutVersionInformation); 88 89 VOID NTAPI RtlAcquireSRWLockExclusive(PSRWLOCK aLock); 90 VOID NTAPI RtlAcquireSRWLockShared(PSRWLOCK aLock); 91 92 VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock); 93 VOID NTAPI RtlReleaseSRWLockShared(PSRWLOCK aLock); 94 95 NTSTATUS NTAPI RtlSleepConditionVariableSRW( 96 PCONDITION_VARIABLE aConditionVariable, PSRWLOCK aSRWLock, 97 PLARGE_INTEGER aTimeOut, ULONG aFlags); 98 VOID NTAPI RtlWakeAllConditionVariable(PCONDITION_VARIABLE aConditionVariable); 99 100 ULONG NTAPI RtlNtStatusToDosError(NTSTATUS aStatus); 101 VOID NTAPI RtlSetLastWin32Error(DWORD aError); 102 DWORD NTAPI RtlGetLastWin32Error(); 103 104 VOID NTAPI RtlRunOnceInitialize(PRTL_RUN_ONCE aRunOnce); 105 106 NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress, 107 PVOID aBuffer, SIZE_T aNumBytesToRead, 108 PSIZE_T aNumBytesRead); 109 110 NTSTATUS NTAPI LdrLoadDll(PWCHAR aDllPath, PULONG aFlags, 111 PUNICODE_STRING aDllName, PHANDLE aOutHandle); 112 113 typedef ULONG(NTAPI* PRTL_RUN_ONCE_INIT_FN)(PRTL_RUN_ONCE, PVOID, PVOID*); 114 NTSTATUS NTAPI RtlRunOnceExecuteOnce(PRTL_RUN_ONCE aRunOnce, 115 PRTL_RUN_ONCE_INIT_FN aInitFn, 116 PVOID aContext, PVOID* aParameter); 117 118 } // extern "C" 119 120 #endif // !defined(MOZILLA_INTERNAL_API) 121 122 extern "C" { 123 PVOID NTAPI RtlAllocateHeap(PVOID aHeapHandle, ULONG aFlags, SIZE_T aSize); 124 125 PVOID NTAPI RtlReAllocateHeap(PVOID aHeapHandle, ULONG aFlags, LPVOID aMem, 126 SIZE_T aNewSize); 127 128 BOOLEAN NTAPI RtlFreeHeap(PVOID aHeapHandle, ULONG aFlags, PVOID aHeapBase); 129 130 BOOLEAN NTAPI RtlQueryPerformanceCounter(LARGE_INTEGER* aPerfCount); 131 132 #define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE 1 133 #define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING 2 134 NTSTATUS NTAPI RtlDuplicateUnicodeString(ULONG aFlags, PCUNICODE_STRING aSrc, 135 PUNICODE_STRING aDest); 136 137 VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING aUnicodeString); 138 } // extern "C" 139 140 namespace mozilla { 141 namespace nt { 142 143 /** 144 * This class encapsulates a UNICODE_STRING that owns its own buffer. The 145 * buffer is always NULL terminated, thus allowing us to cast to a wide C-string 146 * without requiring any mutation. 147 * 148 * We only allow creation of this owned buffer from outside XUL. 149 */ 150 class AllocatedUnicodeString final { 151 public: 152 AllocatedUnicodeString() : mUnicodeString() {} 153 154 #if defined(MOZILLA_INTERNAL_API) 155 AllocatedUnicodeString(const AllocatedUnicodeString& aOther) = delete; 156 157 AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) = 158 delete; 159 #else 160 explicit AllocatedUnicodeString(PCUNICODE_STRING aSrc) { 161 if (!aSrc) { 162 mUnicodeString = {}; 163 return; 164 } 165 166 Duplicate(aSrc); 167 } 168 169 explicit AllocatedUnicodeString(const char* aSrc) { 170 if (!aSrc) { 171 mUnicodeString = {}; 172 return; 173 } 174 175 Duplicate(aSrc); 176 } 177 178 AllocatedUnicodeString(const AllocatedUnicodeString& aOther) { 179 Duplicate(&aOther.mUnicodeString); 180 } 181 182 AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) { 183 Clear(); 184 Duplicate(&aOther.mUnicodeString); 185 return *this; 186 } 187 188 AllocatedUnicodeString& operator=(PCUNICODE_STRING aSrc) { 189 Clear(); 190 Duplicate(aSrc); 191 return *this; 192 } 193 #endif // defined(MOZILLA_INTERNAL_API) 194 195 AllocatedUnicodeString(AllocatedUnicodeString&& aOther) 196 : mUnicodeString(aOther.mUnicodeString) { 197 aOther.mUnicodeString = {}; 198 } 199 200 AllocatedUnicodeString& operator=(AllocatedUnicodeString&& aOther) { 201 Clear(); 202 mUnicodeString = aOther.mUnicodeString; 203 aOther.mUnicodeString = {}; 204 return *this; 205 } 206 207 ~AllocatedUnicodeString() { Clear(); } 208 209 bool IsEmpty() const { 210 return !mUnicodeString.Buffer || !mUnicodeString.Length; 211 } 212 213 operator PCUNICODE_STRING() const { return &mUnicodeString; } 214 215 operator const WCHAR*() const { return mUnicodeString.Buffer; } 216 217 USHORT CharLen() const { return mUnicodeString.Length / sizeof(WCHAR); } 218 219 #if defined(MOZILLA_INTERNAL_API) 220 nsDependentString AsString() const { 221 if (!mUnicodeString.Buffer) { 222 return nsDependentString(); 223 } 224 225 // We can use nsDependentString here as we guaranteed null termination 226 // when we allocated the string. 227 return nsDependentString(mUnicodeString.Buffer, CharLen()); 228 } 229 #endif // defined(MOZILLA_INTERNAL_API) 230 231 private: 232 #if !defined(MOZILLA_INTERNAL_API) 233 void Duplicate(PCUNICODE_STRING aSrc) { 234 MOZ_ASSERT(aSrc); 235 236 // We duplicate with null termination so that this string may be used 237 // as a wide C-string without any further manipulation. 238 NTSTATUS ntStatus = ::RtlDuplicateUnicodeString( 239 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, aSrc, &mUnicodeString); 240 MOZ_ASSERT(NT_SUCCESS(ntStatus)); 241 if (!NT_SUCCESS(ntStatus)) { 242 // Make sure that mUnicodeString does not contain bogus data 243 // (since not all callers zero it out before invoking) 244 mUnicodeString = {}; 245 } 246 } 247 248 void Duplicate(const char* aSrc) { 249 MOZ_ASSERT(aSrc); 250 251 ANSI_STRING ansiStr; 252 RtlInitAnsiString(&ansiStr, aSrc); 253 NTSTATUS ntStatus = 254 ::RtlAnsiStringToUnicodeString(&mUnicodeString, &ansiStr, TRUE); 255 MOZ_ASSERT(NT_SUCCESS(ntStatus)); 256 if (!NT_SUCCESS(ntStatus)) { 257 mUnicodeString = {}; 258 } 259 } 260 #endif // !defined(MOZILLA_INTERNAL_API) 261 262 void Clear() { 263 if (!mUnicodeString.Buffer) { 264 return; 265 } 266 267 ::RtlFreeUnicodeString(&mUnicodeString); 268 mUnicodeString = {}; 269 } 270 271 UNICODE_STRING mUnicodeString; 272 }; 273 274 #if !defined(MOZILLA_INTERNAL_API) 275 276 struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME { 277 MemorySectionNameBuf() { 278 mSectionFileName.Length = 0; 279 mSectionFileName.MaximumLength = sizeof(mBuf); 280 mSectionFileName.Buffer = mBuf; 281 } 282 283 MemorySectionNameBuf(const MemorySectionNameBuf& aOther) { *this = aOther; } 284 285 MemorySectionNameBuf(MemorySectionNameBuf&& aOther) { 286 *this = std::move(aOther); 287 } 288 289 // We cannot use default copy here because mSectionFileName.Buffer needs to 290 // be updated to point to |this->mBuf|, not |aOther.mBuf|. 291 MemorySectionNameBuf& operator=(const MemorySectionNameBuf& aOther) { 292 mSectionFileName.Length = aOther.mSectionFileName.Length; 293 mSectionFileName.MaximumLength = sizeof(mBuf); 294 MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength); 295 mSectionFileName.Buffer = mBuf; 296 memcpy(mBuf, aOther.mBuf, aOther.mSectionFileName.Length); 297 return *this; 298 } 299 300 MemorySectionNameBuf& operator=(MemorySectionNameBuf&& aOther) { 301 mSectionFileName.Length = aOther.mSectionFileName.Length; 302 aOther.mSectionFileName.Length = 0; 303 mSectionFileName.MaximumLength = sizeof(mBuf); 304 MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength); 305 aOther.mSectionFileName.MaximumLength = sizeof(aOther.mBuf); 306 mSectionFileName.Buffer = mBuf; 307 memmove(mBuf, aOther.mBuf, mSectionFileName.Length); 308 return *this; 309 } 310 311 // Native NT paths, so we can't assume MAX_PATH. Use a larger buffer. 312 WCHAR mBuf[2 * MAX_PATH]; 313 314 bool IsEmpty() const { 315 return !mSectionFileName.Buffer || !mSectionFileName.Length; 316 } 317 318 operator PCUNICODE_STRING() const { return &mSectionFileName; } 319 }; 320 321 class MemorySectionNameOnHeap { 322 UniquePtr<uint8_t[]> mBuffer; 323 324 MemorySectionNameOnHeap() = default; 325 explicit MemorySectionNameOnHeap(size_t aBufferLen) 326 : mBuffer(MakeUnique<uint8_t[]>(aBufferLen)) {} 327 328 public: 329 static MemorySectionNameOnHeap GetBackingFilePath(HANDLE aProcess, 330 void* aSectionAddr) { 331 SIZE_T bufferLen = MAX_PATH * 2; 332 do { 333 MemorySectionNameOnHeap sectionName(bufferLen); 334 335 SIZE_T requiredBytes; 336 NTSTATUS ntStatus = ::NtQueryVirtualMemory( 337 aProcess, aSectionAddr, MemorySectionName, sectionName.mBuffer.get(), 338 bufferLen, &requiredBytes); 339 if (NT_SUCCESS(ntStatus)) { 340 return sectionName; 341 } 342 343 if (ntStatus != STATUS_INFO_LENGTH_MISMATCH || 344 bufferLen >= requiredBytes) { 345 break; 346 } 347 348 bufferLen = requiredBytes; 349 } while (1); 350 351 return MemorySectionNameOnHeap(); 352 } 353 354 // Allow move & Disallow copy 355 MemorySectionNameOnHeap(MemorySectionNameOnHeap&&) = default; 356 MemorySectionNameOnHeap& operator=(MemorySectionNameOnHeap&&) = default; 357 MemorySectionNameOnHeap(const MemorySectionNameOnHeap&) = delete; 358 MemorySectionNameOnHeap& operator=(const MemorySectionNameOnHeap&) = delete; 359 360 PCUNICODE_STRING AsUnicodeString() const { 361 return reinterpret_cast<PCUNICODE_STRING>(mBuffer.get()); 362 } 363 }; 364 365 inline bool FindCharInUnicodeString(const UNICODE_STRING& aStr, WCHAR aChar, 366 uint16_t& aPos, uint16_t aStartIndex = 0) { 367 const uint16_t aMaxIndex = aStr.Length / sizeof(WCHAR); 368 369 for (uint16_t curIndex = aStartIndex; curIndex < aMaxIndex; ++curIndex) { 370 if (aStr.Buffer[curIndex] == aChar) { 371 aPos = curIndex; 372 return true; 373 } 374 } 375 376 return false; 377 } 378 379 inline bool IsHexDigit(WCHAR aChar) { 380 return (aChar >= L'0' && aChar <= L'9') || (aChar >= L'A' && aChar <= L'F') || 381 (aChar >= L'a' && aChar <= L'f'); 382 } 383 384 inline bool MatchUnicodeString(const UNICODE_STRING& aStr, 385 bool (*aPredicate)(WCHAR)) { 386 WCHAR* cur = aStr.Buffer; 387 WCHAR* end = &aStr.Buffer[aStr.Length / sizeof(WCHAR)]; 388 while (cur < end) { 389 if (!aPredicate(*cur)) { 390 return false; 391 } 392 393 ++cur; 394 } 395 396 return true; 397 } 398 399 inline bool Contains12DigitHexString(const UNICODE_STRING& aLeafName) { 400 // Quick check: If the string is too short, don't bother 401 // (We need at least 12 hex digits, one char for '.', and 3 for extension) 402 const USHORT kMinLen = (12 + 1 + 3) * sizeof(wchar_t); 403 if (aLeafName.Length < kMinLen) { 404 return false; 405 } 406 407 uint16_t start, end; 408 if (!FindCharInUnicodeString(aLeafName, L'.', start)) { 409 return false; 410 } 411 412 ++start; 413 if (!FindCharInUnicodeString(aLeafName, L'.', end, start)) { 414 return false; 415 } 416 417 if (end - start != 12) { 418 return false; 419 } 420 421 UNICODE_STRING test; 422 test.Buffer = &aLeafName.Buffer[start]; 423 test.Length = (end - start) * sizeof(WCHAR); 424 test.MaximumLength = test.Length; 425 426 return MatchUnicodeString(test, &IsHexDigit); 427 } 428 429 inline bool IsFileNameAtLeast16HexDigits(const UNICODE_STRING& aLeafName) { 430 // Quick check: If the string is too short, don't bother 431 // (We need 16 hex digits, one char for '.', and 3 for extension) 432 const USHORT kMinLen = (16 + 1 + 3) * sizeof(wchar_t); 433 if (aLeafName.Length < kMinLen) { 434 return false; 435 } 436 437 uint16_t dotIndex; 438 if (!FindCharInUnicodeString(aLeafName, L'.', dotIndex)) { 439 return false; 440 } 441 442 if (dotIndex < 16) { 443 return false; 444 } 445 446 UNICODE_STRING test; 447 test.Buffer = aLeafName.Buffer; 448 test.Length = dotIndex * sizeof(WCHAR); 449 test.MaximumLength = aLeafName.MaximumLength; 450 451 return MatchUnicodeString(test, &IsHexDigit); 452 } 453 454 inline void GetLeafName(PUNICODE_STRING aDestString, 455 PCUNICODE_STRING aSrcString) { 456 WCHAR* buf = aSrcString->Buffer; 457 WCHAR* end = &aSrcString->Buffer[(aSrcString->Length / sizeof(WCHAR)) - 1]; 458 WCHAR* cur = end; 459 while (cur >= buf) { 460 if (*cur == L'\\') { 461 break; 462 } 463 464 --cur; 465 } 466 467 // At this point, either cur points to the final backslash, or it points to 468 // buf - 1. Either way, we're interested in cur + 1 as the desired buffer. 469 aDestString->Buffer = cur + 1; 470 aDestString->Length = (end - aDestString->Buffer + 1) * sizeof(WCHAR); 471 aDestString->MaximumLength = aDestString->Length; 472 } 473 474 #endif // !defined(MOZILLA_INTERNAL_API) 475 476 #if defined(MOZILLA_INTERNAL_API) 477 478 inline const nsDependentSubstring GetLeafName(const nsAString& aString) { 479 auto it = aString.EndReading(); 480 size_t pos = aString.Length(); 481 while (it > aString.BeginReading()) { 482 if (*(it - 1) == u'\\') { 483 return Substring(aString, pos); 484 } 485 486 MOZ_ASSERT(pos > 0); 487 --pos; 488 --it; 489 } 490 491 return Substring(aString, 0); // No backslash in the string 492 } 493 494 #endif // defined(MOZILLA_INTERNAL_API) 495 496 inline char EnsureLowerCaseASCII(char aChar) { 497 if (aChar >= 'A' && aChar <= 'Z') { 498 aChar -= 'A' - 'a'; 499 } 500 501 return aChar; 502 } 503 504 inline int StricmpASCII(const char* aLeft, const char* aRight) { 505 char curLeft, curRight; 506 507 do { 508 curLeft = EnsureLowerCaseASCII(*(aLeft++)); 509 curRight = EnsureLowerCaseASCII(*(aRight++)); 510 } while (curLeft && curLeft == curRight); 511 512 return curLeft - curRight; 513 } 514 515 inline int StrcmpASCII(const char* aLeft, const char* aRight) { 516 char curLeft, curRight; 517 518 do { 519 curLeft = *(aLeft++); 520 curRight = *(aRight++); 521 } while (curLeft && curLeft == curRight); 522 523 return curLeft - curRight; 524 } 525 526 inline size_t StrlenASCII(const char* aStr) { 527 size_t len = 0; 528 529 while (*(aStr++)) { 530 ++len; 531 } 532 533 return len; 534 } 535 536 struct CodeViewRecord70 { 537 uint32_t signature; 538 GUID pdbSignature; 539 uint32_t pdbAge; 540 // A UTF-8 string, according to 541 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/locator.cpp#L785 542 char pdbFileName[1]; 543 }; 544 545 class MOZ_RAII PEHeaders final { 546 /** 547 * This structure is documented on MSDN as VS_VERSIONINFO, but is not present 548 * in SDK headers because it cannot be specified as a C struct. The following 549 * structure contains the fixed-length fields at the beginning of 550 * VS_VERSIONINFO. 551 */ 552 struct VS_VERSIONINFO_HEADER { 553 WORD wLength; 554 WORD wValueLength; 555 WORD wType; 556 WCHAR szKey[16]; // std::size(L"VS_VERSION_INFO") 557 // Additional data goes here, aligned on a 4-byte boundary 558 }; 559 560 public: 561 // The lowest two bits of an HMODULE are used as flags. Stripping those bits 562 // from the HMODULE yields the base address of the binary's memory mapping. 563 // (See LoadLibraryEx docs on MSDN) 564 template <typename T> 565 static T HModuleToBaseAddr(HMODULE aModule) { 566 return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(aModule) & 567 ~uintptr_t(3)); 568 } 569 570 explicit PEHeaders(void* aBaseAddress) 571 : PEHeaders(reinterpret_cast<PIMAGE_DOS_HEADER>(aBaseAddress)) {} 572 573 explicit PEHeaders(HMODULE aModule) 574 : PEHeaders(HModuleToBaseAddr<PIMAGE_DOS_HEADER>(aModule)) {} 575 576 explicit PEHeaders(PIMAGE_DOS_HEADER aMzHeader) 577 : mMzHeader(aMzHeader), 578 mPeHeader(nullptr), 579 mImageLimit(nullptr), 580 mIsImportDirectoryTampered(false) { 581 if (!mMzHeader || mMzHeader->e_magic != IMAGE_DOS_SIGNATURE) { 582 return; 583 } 584 585 mPeHeader = RVAToPtrUnchecked<PIMAGE_NT_HEADERS>(mMzHeader->e_lfanew); 586 if (!mPeHeader || mPeHeader->Signature != IMAGE_NT_SIGNATURE) { 587 return; 588 } 589 590 if (mPeHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) { 591 return; 592 } 593 594 DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage; 595 // This is a coarse-grained check to ensure that the image size is 596 // reasonable. It we aren't big enough to contain headers, we have a 597 // problem! 598 if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) { 599 return; 600 } 601 602 mImageLimit = RVAToPtrUnchecked<void*>(imageSize - 1UL); 603 604 PIMAGE_DATA_DIRECTORY importDirEntry = 605 GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_IMPORT); 606 if (!importDirEntry) { 607 return; 608 } 609 610 mIsImportDirectoryTampered = (importDirEntry->VirtualAddress >= imageSize); 611 } 612 613 explicit operator bool() const { return !!mImageLimit; } 614 615 /** 616 * This overload computes absolute virtual addresses relative to the base 617 * address of the binary. 618 */ 619 template <typename T, typename R> 620 T RVAToPtr(R aRva) const { 621 return RVAToPtr<T>(mMzHeader, aRva); 622 } 623 624 /** 625 * This overload computes a result by adding aRva to aBase, but also ensures 626 * that the resulting pointer falls within the bounds of this binary's memory 627 * mapping. 628 */ 629 template <typename T, typename R> 630 T RVAToPtr(void* aBase, R aRva) const { 631 if (!mImageLimit) { 632 return nullptr; 633 } 634 635 char* absAddress = reinterpret_cast<char*>(aBase) + aRva; 636 if (absAddress < reinterpret_cast<char*>(mMzHeader) || 637 absAddress > reinterpret_cast<char*>(mImageLimit)) { 638 return nullptr; 639 } 640 641 return reinterpret_cast<T>(absAddress); 642 } 643 644 Maybe<Range<const uint8_t>> GetBounds() const { 645 if (!mImageLimit) { 646 return Nothing(); 647 } 648 649 auto base = reinterpret_cast<const uint8_t*>(mMzHeader); 650 DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage; 651 return Some(Range(base, imageSize)); 652 } 653 654 DWORD GetFileCharacteristics() const { 655 return mPeHeader ? mPeHeader->FileHeader.Characteristics : 0; 656 } 657 658 bool IsWithinImage(const void* aAddress) const { 659 uintptr_t addr = reinterpret_cast<uintptr_t>(aAddress); 660 uintptr_t imageBase = reinterpret_cast<uintptr_t>(mMzHeader); 661 uintptr_t imageLimit = reinterpret_cast<uintptr_t>(mImageLimit); 662 return addr >= imageBase && addr <= imageLimit; 663 } 664 665 PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() const { 666 // If the import directory is already tampered, we skip bounds check 667 // because it could be located outside the mapped image. 668 return mIsImportDirectoryTampered 669 ? GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR, 670 BoundsCheckPolicy::Skip>( 671 IMAGE_DIRECTORY_ENTRY_IMPORT) 672 : GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR>( 673 IMAGE_DIRECTORY_ENTRY_IMPORT); 674 } 675 676 PIMAGE_RESOURCE_DIRECTORY GetResourceTable() const { 677 return GetImageDirectoryEntry<PIMAGE_RESOURCE_DIRECTORY>( 678 IMAGE_DIRECTORY_ENTRY_RESOURCE); 679 } 680 681 PIMAGE_DATA_DIRECTORY GetImageDirectoryEntryPtr( 682 const uint32_t aDirectoryIndex, uint32_t* aOutRva = nullptr) const { 683 if (aOutRva) { 684 *aOutRva = 0; 685 } 686 687 IMAGE_OPTIONAL_HEADER& optionalHeader = mPeHeader->OptionalHeader; 688 689 const uint32_t maxIndex = std::min(optionalHeader.NumberOfRvaAndSizes, 690 DWORD(IMAGE_NUMBEROF_DIRECTORY_ENTRIES)); 691 if (aDirectoryIndex >= maxIndex) { 692 return nullptr; 693 } 694 695 PIMAGE_DATA_DIRECTORY dirEntry = 696 &optionalHeader.DataDirectory[aDirectoryIndex]; 697 if (aOutRva) { 698 *aOutRva = reinterpret_cast<char*>(dirEntry) - 699 reinterpret_cast<char*>(mMzHeader); 700 MOZ_ASSERT(*aOutRva); 701 } 702 703 return dirEntry; 704 } 705 706 bool GetVersionInfo(uint64_t& aOutVersion) const { 707 // RT_VERSION == 16 708 // Version resources require an id of 1 709 auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1); 710 if (!root) { 711 return false; 712 } 713 714 VS_FIXEDFILEINFO* fixedInfo = GetFixedFileInfo(root); 715 if (!fixedInfo) { 716 return false; 717 } 718 719 aOutVersion = ((static_cast<uint64_t>(fixedInfo->dwFileVersionMS) << 32) | 720 static_cast<uint64_t>(fixedInfo->dwFileVersionLS)); 721 return true; 722 } 723 724 bool GetTimeStamp(DWORD& aResult) const { 725 if (!(*this)) { 726 return false; 727 } 728 729 aResult = mPeHeader->FileHeader.TimeDateStamp; 730 return true; 731 } 732 733 bool GetImageSize(DWORD& aResult) const { 734 if (!(*this)) { 735 return false; 736 } 737 738 aResult = mPeHeader->OptionalHeader.SizeOfImage; 739 return true; 740 } 741 742 bool GetCheckSum(DWORD& aResult) const { 743 if (!(*this)) { 744 return false; 745 } 746 747 aResult = mPeHeader->OptionalHeader.CheckSum; 748 return true; 749 } 750 751 PIMAGE_IMPORT_DESCRIPTOR 752 GetImportDescriptor(const char* aModuleNameASCII) const { 753 for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory(); 754 IsValid(curImpDesc); ++curImpDesc) { 755 auto curName = mIsImportDirectoryTampered 756 ? RVAToPtrUnchecked<const char*>(curImpDesc->Name) 757 : RVAToPtr<const char*>(curImpDesc->Name); 758 if (!curName) { 759 return nullptr; 760 } 761 762 if (StricmpASCII(aModuleNameASCII, curName)) { 763 continue; 764 } 765 766 // curImpDesc now points to the IAT for the module we're interested in 767 return curImpDesc; 768 } 769 770 return nullptr; 771 } 772 773 template <typename CallbackT> 774 void EnumImportChunks(const CallbackT& aCallback) const { 775 for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory(); 776 IsValid(curImpDesc); ++curImpDesc) { 777 auto curName = mIsImportDirectoryTampered 778 ? RVAToPtrUnchecked<const char*>(curImpDesc->Name) 779 : RVAToPtr<const char*>(curImpDesc->Name); 780 if (!curName) { 781 continue; 782 } 783 784 aCallback(curName); 785 } 786 } 787 788 /** 789 * If |aBoundaries| is given, this method checks whether each IAT entry is 790 * within the given range, and if any entry is out of the range, we return 791 * Nothing(). 792 */ 793 Maybe<Span<IMAGE_THUNK_DATA>> GetIATThunksForModule( 794 const char* aModuleNameASCII, 795 const Range<const uint8_t>* aBoundaries = nullptr) const { 796 PIMAGE_IMPORT_DESCRIPTOR impDesc = GetImportDescriptor(aModuleNameASCII); 797 if (!impDesc) { 798 return Nothing(); 799 } 800 801 auto firstIatThunk = 802 this->template RVAToPtr<PIMAGE_THUNK_DATA>(impDesc->FirstThunk); 803 if (!firstIatThunk) { 804 return Nothing(); 805 } 806 807 // Find the length by iterating through the table until we find a null entry 808 PIMAGE_THUNK_DATA curIatThunk = firstIatThunk; 809 while (IsValid(curIatThunk)) { 810 if (aBoundaries) { 811 auto iatEntry = 812 reinterpret_cast<const uint8_t*>(curIatThunk->u1.Function); 813 if (iatEntry < aBoundaries->begin().get() || 814 iatEntry >= aBoundaries->end().get()) { 815 return Nothing(); 816 } 817 } 818 819 ++curIatThunk; 820 } 821 822 return Some(Span(firstIatThunk, curIatThunk)); 823 } 824 825 /** 826 * Resources are stored in a three-level tree. To locate a particular entry, 827 * you must supply a resource type, the resource id, and then the language id. 828 * If aLangId == 0, we just resolve the first entry regardless of language. 829 */ 830 template <typename T> 831 T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) const { 832 PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable(); 833 if (!topLevel) { 834 return nullptr; 835 } 836 837 PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry = 838 FindResourceEntry(topLevel, aType); 839 if (!typeEntry || !typeEntry->DataIsDirectory) { 840 return nullptr; 841 } 842 843 auto idDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>( 844 topLevel, typeEntry->OffsetToDirectory); 845 PIMAGE_RESOURCE_DIRECTORY_ENTRY idEntry = FindResourceEntry(idDir, aResId); 846 if (!idEntry || !idEntry->DataIsDirectory) { 847 return nullptr; 848 } 849 850 auto langDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>( 851 topLevel, idEntry->OffsetToDirectory); 852 PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry; 853 if (aLangId) { 854 langEntry = FindResourceEntry(langDir, aLangId); 855 } else { 856 langEntry = FindFirstResourceEntry(langDir); 857 } 858 859 if (!langEntry || langEntry->DataIsDirectory) { 860 return nullptr; 861 } 862 863 auto dataEntry = 864 RVAToPtr<PIMAGE_RESOURCE_DATA_ENTRY>(topLevel, langEntry->OffsetToData); 865 return dataEntry ? RVAToPtr<T>(dataEntry->OffsetToData) : nullptr; 866 } 867 868 template <size_t N> 869 Maybe<Span<const uint8_t>> FindSection(const char (&aSecName)[N], 870 DWORD aCharacteristicsMask) const { 871 static_assert((N - 1) <= IMAGE_SIZEOF_SHORT_NAME, 872 "Section names must be at most 8 characters excluding null " 873 "terminator"); 874 875 if (!(*this)) { 876 return Nothing(); 877 } 878 879 Span<IMAGE_SECTION_HEADER> sectionTable = GetSectionTable(); 880 for (auto&& sectionHeader : sectionTable) { 881 if (strncmp(reinterpret_cast<const char*>(sectionHeader.Name), aSecName, 882 IMAGE_SIZEOF_SHORT_NAME)) { 883 continue; 884 } 885 886 if (!(sectionHeader.Characteristics & aCharacteristicsMask)) { 887 // We found the section but it does not have the expected 888 // characteristics 889 return Nothing(); 890 } 891 892 DWORD rva = sectionHeader.VirtualAddress; 893 if (!rva) { 894 return Nothing(); 895 } 896 897 DWORD size = sectionHeader.Misc.VirtualSize; 898 if (!size) { 899 return Nothing(); 900 } 901 902 auto base = RVAToPtr<const uint8_t*>(rva); 903 return Some(Span(base, size)); 904 } 905 906 return Nothing(); 907 } 908 909 // There may be other code sections in the binary besides .text 910 Maybe<Span<const uint8_t>> GetTextSectionInfo() const { 911 return FindSection(".text", IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | 912 IMAGE_SCN_MEM_READ); 913 } 914 915 // There may be other data sections in the binary besides .data 916 Maybe<Span<const uint8_t>> GetDataSectionInfo() const { 917 return FindSection(".data", IMAGE_SCN_CNT_INITIALIZED_DATA | 918 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE); 919 } 920 921 static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc) { 922 return aImpDesc && aImpDesc->OriginalFirstThunk != 0; 923 } 924 925 static bool IsValid(PIMAGE_THUNK_DATA aImgThunk) { 926 return aImgThunk && aImgThunk->u1.Ordinal != 0; 927 } 928 929 bool IsImportDirectoryTampered() const { return mIsImportDirectoryTampered; } 930 931 FARPROC GetEntryPoint() const { 932 // Use the unchecked version because the entrypoint may be tampered. 933 return RVAToPtrUnchecked<FARPROC>( 934 mPeHeader->OptionalHeader.AddressOfEntryPoint); 935 } 936 937 const CodeViewRecord70* GetPdbInfo() const { 938 PIMAGE_DEBUG_DIRECTORY debugDirectory = 939 GetImageDirectoryEntry<PIMAGE_DEBUG_DIRECTORY>( 940 IMAGE_DIRECTORY_ENTRY_DEBUG); 941 if (!debugDirectory) { 942 return nullptr; 943 } 944 945 const CodeViewRecord70* debugInfo = 946 RVAToPtr<CodeViewRecord70*>(debugDirectory->AddressOfRawData); 947 return (debugInfo && debugInfo->signature == 'SDSR') ? debugInfo : nullptr; 948 } 949 950 private: 951 enum class BoundsCheckPolicy { Default, Skip }; 952 953 template <typename T, BoundsCheckPolicy Policy = BoundsCheckPolicy::Default> 954 T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) const { 955 PIMAGE_DATA_DIRECTORY dirEntry = GetImageDirectoryEntryPtr(aDirectoryIndex); 956 if (!dirEntry) { 957 return nullptr; 958 } 959 960 return Policy == BoundsCheckPolicy::Skip 961 ? RVAToPtrUnchecked<T>(dirEntry->VirtualAddress) 962 : RVAToPtr<T>(dirEntry->VirtualAddress); 963 } 964 965 // This private variant does not have bounds checks, because we need to be 966 // able to resolve the bounds themselves. 967 template <typename T, typename R> 968 T RVAToPtrUnchecked(R aRva) const { 969 return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva); 970 } 971 972 Span<IMAGE_SECTION_HEADER> GetSectionTable() const { 973 MOZ_ASSERT(*this); 974 auto base = RVAToPtr<PIMAGE_SECTION_HEADER>( 975 &mPeHeader->OptionalHeader, mPeHeader->FileHeader.SizeOfOptionalHeader); 976 // The Windows loader has an internal limit of 96 sections (per PE spec) 977 auto numSections = 978 std::min(mPeHeader->FileHeader.NumberOfSections, WORD(96)); 979 return Span{base, numSections}; 980 } 981 982 PIMAGE_RESOURCE_DIRECTORY_ENTRY 983 FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) const { 984 if (!aCurLevel) { 985 return nullptr; 986 } 987 988 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array 989 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function 990 // searches by ID, we need to skip past any named entries before iterating. 991 auto dirEnt = 992 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) + 993 aCurLevel->NumberOfNamedEntries; 994 if (!(IsWithinImage(dirEnt) && 995 IsWithinImage(&dirEnt[aCurLevel->NumberOfIdEntries - 1].Id))) { 996 return nullptr; 997 } 998 999 for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) { 1000 if (dirEnt[i].Id == aId) { 1001 return &dirEnt[i]; 1002 } 1003 } 1004 1005 return nullptr; 1006 } 1007 1008 PIMAGE_RESOURCE_DIRECTORY_ENTRY 1009 FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) const { 1010 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array 1011 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first 1012 // entry, regardless of whether it is indexed by name or by id. 1013 auto dirEnt = 1014 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1); 1015 WORD numEntries = 1016 aCurLevel->NumberOfNamedEntries + aCurLevel->NumberOfIdEntries; 1017 if (!numEntries) { 1018 return nullptr; 1019 } 1020 1021 return dirEnt; 1022 } 1023 1024 VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) const { 1025 WORD length = aVerInfo->wLength; 1026 if (length < sizeof(VS_VERSIONINFO_HEADER)) { 1027 return nullptr; 1028 } 1029 1030 const wchar_t kVersionInfoKey[] = L"VS_VERSION_INFO"; 1031 if (::RtlCompareMemory(aVerInfo->szKey, kVersionInfoKey, 1032 std::size(kVersionInfoKey)) != 1033 std::size(kVersionInfoKey)) { 1034 return nullptr; 1035 } 1036 1037 if (aVerInfo->wValueLength != sizeof(VS_FIXEDFILEINFO)) { 1038 // Fixed file info does not exist 1039 return nullptr; 1040 } 1041 1042 WORD offset = sizeof(VS_VERSIONINFO_HEADER); 1043 1044 uintptr_t base = reinterpret_cast<uintptr_t>(aVerInfo); 1045 // Align up to 4-byte boundary 1046 #pragma warning(suppress : 4146) 1047 offset += (-(base + offset) & 3); 1048 1049 if (offset >= length) { 1050 return nullptr; 1051 } 1052 1053 auto result = reinterpret_cast<VS_FIXEDFILEINFO*>(base + offset); 1054 if (result->dwSignature != 0xFEEF04BD) { 1055 return nullptr; 1056 } 1057 1058 return result; 1059 } 1060 1061 private: 1062 PIMAGE_DOS_HEADER mMzHeader; 1063 PIMAGE_NT_HEADERS mPeHeader; 1064 void* mImageLimit; 1065 bool mIsImportDirectoryTampered; 1066 }; 1067 1068 // This class represents an export section of a local/remote process. 1069 template <typename MMPolicy> 1070 class MOZ_RAII PEExportSection { 1071 const MMPolicy& mMMPolicy; 1072 uintptr_t mImageBase; 1073 DWORD mOrdinalBase; 1074 DWORD mRvaDirStart; 1075 DWORD mRvaDirEnd; 1076 mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportAddressTable; 1077 mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportNameTable; 1078 mozilla::interceptor::TargetObjectArray<MMPolicy, WORD> mExportOrdinalTable; 1079 1080 explicit PEExportSection(const MMPolicy& aMMPolicy) 1081 : mMMPolicy(aMMPolicy), 1082 mImageBase(0), 1083 mOrdinalBase(0), 1084 mRvaDirStart(0), 1085 mRvaDirEnd(0), 1086 mExportAddressTable(mMMPolicy), 1087 mExportNameTable(mMMPolicy), 1088 mExportOrdinalTable(mMMPolicy) {} 1089 1090 PEExportSection(const MMPolicy& aMMPolicy, uintptr_t aImageBase, 1091 DWORD aRvaDirStart, DWORD aRvaDirEnd, 1092 const IMAGE_EXPORT_DIRECTORY& exportDir) 1093 : mMMPolicy(aMMPolicy), 1094 mImageBase(aImageBase), 1095 mOrdinalBase(exportDir.Base), 1096 mRvaDirStart(aRvaDirStart), 1097 mRvaDirEnd(aRvaDirEnd), 1098 mExportAddressTable(mMMPolicy, 1099 mImageBase + exportDir.AddressOfFunctions, 1100 exportDir.NumberOfFunctions), 1101 mExportNameTable(mMMPolicy, mImageBase + exportDir.AddressOfNames, 1102 exportDir.NumberOfNames), 1103 mExportOrdinalTable(mMMPolicy, 1104 mImageBase + exportDir.AddressOfNameOrdinals, 1105 exportDir.NumberOfNames) {} 1106 1107 static const PEExportSection Get(uintptr_t aImageBase, 1108 const MMPolicy& aMMPolicy) { 1109 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_DOS_HEADER> mzHeader( 1110 aMMPolicy, aImageBase); 1111 if (!mzHeader || mzHeader->e_magic != IMAGE_DOS_SIGNATURE) { 1112 return PEExportSection(aMMPolicy); 1113 } 1114 1115 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_NT_HEADERS> peHeader( 1116 aMMPolicy, aImageBase + mzHeader->e_lfanew); 1117 if (!peHeader || peHeader->Signature != IMAGE_NT_SIGNATURE) { 1118 return PEExportSection(aMMPolicy); 1119 } 1120 1121 if (peHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) { 1122 return PEExportSection(aMMPolicy); 1123 } 1124 1125 const IMAGE_OPTIONAL_HEADER& optionalHeader = peHeader->OptionalHeader; 1126 1127 DWORD imageSize = optionalHeader.SizeOfImage; 1128 // This is a coarse-grained check to ensure that the image size is 1129 // reasonable. It we aren't big enough to contain headers, we have a 1130 // problem! 1131 if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) { 1132 return PEExportSection(aMMPolicy); 1133 } 1134 1135 if (optionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) { 1136 return PEExportSection(aMMPolicy); 1137 } 1138 1139 const IMAGE_DATA_DIRECTORY& exportDirectoryEntry = 1140 optionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 1141 if (!exportDirectoryEntry.VirtualAddress || !exportDirectoryEntry.Size) { 1142 return PEExportSection(aMMPolicy); 1143 } 1144 1145 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_EXPORT_DIRECTORY> 1146 exportDirectory(aMMPolicy, 1147 aImageBase + exportDirectoryEntry.VirtualAddress); 1148 if (!exportDirectory || !exportDirectory->NumberOfFunctions) { 1149 return PEExportSection(aMMPolicy); 1150 } 1151 1152 return PEExportSection( 1153 aMMPolicy, aImageBase, exportDirectoryEntry.VirtualAddress, 1154 exportDirectoryEntry.VirtualAddress + exportDirectoryEntry.Size, 1155 *exportDirectory.GetLocalBase()); 1156 } 1157 1158 FARPROC GetProcAddressByOrdinal(WORD aOrdinal) const { 1159 if (aOrdinal < mOrdinalBase) { 1160 return nullptr; 1161 } 1162 1163 auto rvaToFunction = mExportAddressTable[aOrdinal - mOrdinalBase]; 1164 if (!rvaToFunction) { 1165 return nullptr; 1166 } 1167 return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction); 1168 } 1169 1170 public: 1171 static const PEExportSection Get(HMODULE aModule, const MMPolicy& aMMPolicy) { 1172 return Get(PEHeaders::HModuleToBaseAddr<uintptr_t>(aModule), aMMPolicy); 1173 } 1174 1175 explicit operator bool() const { 1176 // Because PEExportSection doesn't use MMPolicy::Reserve(), a boolified 1177 // mMMPolicy is expected to be false. We don't check mMMPolicy here. 1178 return mImageBase && mRvaDirStart && mRvaDirEnd && mExportAddressTable && 1179 mExportNameTable && mExportOrdinalTable; 1180 } 1181 1182 template <typename T> 1183 T RVAToPtr(uint32_t aRva) const { 1184 return reinterpret_cast<T>(mImageBase + aRva); 1185 } 1186 1187 PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const { 1188 if (!*this) { 1189 return nullptr; 1190 } 1191 1192 return RVAToPtr<PIMAGE_EXPORT_DIRECTORY>(mRvaDirStart); 1193 } 1194 1195 /** 1196 * This functions searches the export table for a given string as 1197 * GetProcAddress does, but this returns a matched entry of the Export 1198 * Address Table i.e. a pointer to an RVA of a matched function instead 1199 * of a function address. If the entry is forwarded, this function 1200 * returns nullptr. 1201 */ 1202 const DWORD* FindExportAddressTableEntry( 1203 const char* aFunctionNameASCII) const { 1204 if (!*this || !aFunctionNameASCII) { 1205 return nullptr; 1206 } 1207 1208 struct NameTableComparator { 1209 NameTableComparator(const PEExportSection<MMPolicy>& aExportSection, 1210 const char* aTarget) 1211 : mExportSection(aExportSection), 1212 mTargetName(aTarget), 1213 mTargetNamelength(StrlenASCII(aTarget)) {} 1214 1215 int operator()(DWORD aRVAToString) const { 1216 mozilla::interceptor::TargetObjectArray<MMPolicy, char> itemString( 1217 mExportSection.mMMPolicy, mExportSection.mImageBase + aRVAToString, 1218 mTargetNamelength + 1); 1219 return StrcmpASCII(mTargetName, itemString[0]); 1220 } 1221 1222 const PEExportSection<MMPolicy>& mExportSection; 1223 const char* mTargetName; 1224 size_t mTargetNamelength; 1225 }; 1226 1227 const NameTableComparator comp(*this, aFunctionNameASCII); 1228 1229 size_t match; 1230 if (!mExportNameTable.BinarySearchIf(comp, &match)) { 1231 return nullptr; 1232 } 1233 1234 const WORD* index = mExportOrdinalTable[match]; 1235 if (!index) { 1236 return nullptr; 1237 } 1238 1239 const DWORD* rvaToFunction = mExportAddressTable[*index]; 1240 if (!rvaToFunction) { 1241 return nullptr; 1242 } 1243 1244 if (*rvaToFunction >= mRvaDirStart && *rvaToFunction < mRvaDirEnd) { 1245 // If an entry points to an address within the export section, the 1246 // field is a forwarder RVA. We return nullptr because the entry is 1247 // not a function address but a null-terminated string used for export 1248 // forwarding. 1249 return nullptr; 1250 } 1251 1252 return rvaToFunction; 1253 } 1254 1255 /** 1256 * This functions behaves the same as the native ::GetProcAddress except 1257 * the following cases: 1258 * - Returns nullptr if a target entry is forwarded to another dll. 1259 */ 1260 FARPROC GetProcAddress(const char* aFunctionNameASCII) const { 1261 uintptr_t maybeOdrinal = reinterpret_cast<uintptr_t>(aFunctionNameASCII); 1262 // When the high-order word of |aFunctionNameASCII| is zero, it's not 1263 // a string but an ordinal value. 1264 if (maybeOdrinal < 0x10000) { 1265 return GetProcAddressByOrdinal(static_cast<WORD>(maybeOdrinal)); 1266 } 1267 1268 auto rvaToFunction = FindExportAddressTableEntry(aFunctionNameASCII); 1269 if (!rvaToFunction) { 1270 return nullptr; 1271 } 1272 return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction); 1273 } 1274 }; 1275 1276 inline HANDLE RtlGetProcessHeap() { 1277 PTEB teb = ::NtCurrentTeb(); 1278 PPEB peb = teb->ProcessEnvironmentBlock; 1279 return peb->Reserved4[1]; 1280 } 1281 1282 inline PVOID RtlGetThreadLocalStoragePointer() { 1283 return ::NtCurrentTeb()->Reserved1[11]; 1284 } 1285 1286 inline void RtlSetThreadLocalStoragePointerForTestingOnly(PVOID aNewValue) { 1287 ::NtCurrentTeb()->Reserved1[11] = aNewValue; 1288 } 1289 1290 inline DWORD RtlGetCurrentThreadId() { 1291 PTEB teb = ::NtCurrentTeb(); 1292 CLIENT_ID* cid = reinterpret_cast<CLIENT_ID*>(&teb->Reserved1[8]); 1293 return static_cast<DWORD>(reinterpret_cast<uintptr_t>(cid->UniqueThread) & 1294 0xFFFFFFFFUL); 1295 } 1296 1297 inline PVOID RtlGetThreadStackBase() { 1298 return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackBase; 1299 } 1300 1301 inline PVOID RtlGetThreadStackLimit() { 1302 return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackLimit; 1303 } 1304 1305 const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1); 1306 1307 inline LauncherResult<DWORD> GetParentProcessId() { 1308 struct PROCESS_BASIC_INFORMATION { 1309 NTSTATUS ExitStatus; 1310 PPEB PebBaseAddress; 1311 ULONG_PTR AffinityMask; 1312 LONG BasePriority; 1313 ULONG_PTR UniqueProcessId; 1314 ULONG_PTR InheritedFromUniqueProcessId; 1315 }; 1316 1317 ULONG returnLength; 1318 PROCESS_BASIC_INFORMATION pbi = {}; 1319 NTSTATUS status = 1320 ::NtQueryInformationProcess(kCurrentProcess, ProcessBasicInformation, 1321 &pbi, sizeof(pbi), &returnLength); 1322 if (!NT_SUCCESS(status)) { 1323 return LAUNCHER_ERROR_FROM_NTSTATUS(status); 1324 } 1325 1326 return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF); 1327 } 1328 1329 inline SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress, 1330 PMEMORY_BASIC_INFORMATION aMemInfo, 1331 SIZE_T aMemInfoLen) { 1332 #if defined(MOZILLA_INTERNAL_API) 1333 return ::VirtualQueryEx(aProcess, aAddress, aMemInfo, aMemInfoLen); 1334 #else 1335 SIZE_T returnedLength; 1336 NTSTATUS status = ::NtQueryVirtualMemory( 1337 aProcess, const_cast<PVOID>(aAddress), MemoryBasicInformation, aMemInfo, 1338 aMemInfoLen, &returnedLength); 1339 if (!NT_SUCCESS(status)) { 1340 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status)); 1341 returnedLength = 0; 1342 } 1343 return returnedLength; 1344 #endif // defined(MOZILLA_INTERNAL_API) 1345 } 1346 1347 inline SIZE_T WINAPI VirtualQuery(LPCVOID aAddress, 1348 PMEMORY_BASIC_INFORMATION aMemInfo, 1349 SIZE_T aMemInfoLen) { 1350 return nt::VirtualQueryEx(kCurrentProcess, aAddress, aMemInfo, aMemInfoLen); 1351 } 1352 1353 struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY { 1354 DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {} 1355 1356 MOZ_IMPLICIT DataDirectoryEntry(const _IMAGE_DATA_DIRECTORY& aOther) 1357 : _IMAGE_DATA_DIRECTORY(aOther) {} 1358 1359 DataDirectoryEntry(const DataDirectoryEntry& aOther) = default; 1360 1361 bool operator==(const DataDirectoryEntry& aOther) const { 1362 return VirtualAddress == aOther.VirtualAddress && Size == aOther.Size; 1363 } 1364 1365 bool operator!=(const DataDirectoryEntry& aOther) const { 1366 return !(*this == aOther); 1367 } 1368 }; 1369 1370 inline LauncherResult<void*> GetProcessPebPtr(HANDLE aProcess) { 1371 ULONG returnLength; 1372 PROCESS_BASIC_INFORMATION pbi; 1373 NTSTATUS status = ::NtQueryInformationProcess( 1374 aProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &returnLength); 1375 if (!NT_SUCCESS(status)) { 1376 return LAUNCHER_ERROR_FROM_NTSTATUS(status); 1377 } 1378 1379 return pbi.PebBaseAddress; 1380 } 1381 1382 /** 1383 * This function relies on a specific offset into the mostly-undocumented PEB 1384 * structure. The risk is reduced thanks to the fact that the Chromium sandbox 1385 * relies on the location of this field. It is unlikely to change at this point. 1386 * To further reduce the risk, we also check for the magic 'MZ' signature that 1387 * should indicate the beginning of a PE image. 1388 */ 1389 inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) { 1390 LauncherResult<void*> ppeb = GetProcessPebPtr(aProcess); 1391 if (ppeb.isErr()) { 1392 return ppeb.propagateErr(); 1393 } 1394 1395 PEB peb; 1396 SIZE_T bytesRead; 1397 1398 #if defined(MOZILLA_INTERNAL_API) 1399 if (!::ReadProcessMemory(aProcess, ppeb.unwrap(), &peb, sizeof(peb), 1400 &bytesRead) || 1401 bytesRead != sizeof(peb)) { 1402 return LAUNCHER_ERROR_FROM_LAST(); 1403 } 1404 #else 1405 NTSTATUS ntStatus = ::NtReadVirtualMemory(aProcess, ppeb.unwrap(), &peb, 1406 sizeof(peb), &bytesRead); 1407 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(peb)) { 1408 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus); 1409 } 1410 #endif 1411 1412 // peb.ImageBaseAddress 1413 void* baseAddress = peb.Reserved3[1]; 1414 1415 char mzMagic[2]; 1416 #if defined(MOZILLA_INTERNAL_API) 1417 if (!::ReadProcessMemory(aProcess, baseAddress, mzMagic, sizeof(mzMagic), 1418 &bytesRead) || 1419 bytesRead != sizeof(mzMagic)) { 1420 return LAUNCHER_ERROR_FROM_LAST(); 1421 } 1422 #else 1423 ntStatus = ::NtReadVirtualMemory(aProcess, baseAddress, mzMagic, 1424 sizeof(mzMagic), &bytesRead); 1425 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(mzMagic)) { 1426 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus); 1427 } 1428 #endif 1429 1430 MOZ_ASSERT(mzMagic[0] == 'M' && mzMagic[1] == 'Z'); 1431 if (mzMagic[0] != 'M' || mzMagic[1] != 'Z') { 1432 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); 1433 } 1434 1435 return static_cast<HMODULE>(baseAddress); 1436 } 1437 1438 #if defined(_MSC_VER) 1439 extern "C" IMAGE_DOS_HEADER __ImageBase; 1440 #endif 1441 1442 // This class manages data transfer from the local process's executable 1443 // to another process's executable via WriteProcessMemory. 1444 // Bug 1662560 told us the same executable may be mapped onto a different 1445 // address in a different process. This means when we transfer data within 1446 // the mapped executable such as a global variable or IAT from the current 1447 // process to another process, we need to shift its address by the difference 1448 // between two executable's mapped imagebase. 1449 class CrossExecTransferManager final { 1450 HANDLE mRemoteProcess; 1451 uint8_t* mLocalImagebase; 1452 PEHeaders mLocalExec; 1453 uint8_t* mRemoteImagebase; 1454 1455 static HMODULE GetLocalExecModule() { 1456 #if defined(_MSC_VER) 1457 return reinterpret_cast<HMODULE>(&__ImageBase); 1458 #else 1459 return ::GetModuleHandleW(nullptr); 1460 #endif 1461 } 1462 1463 LauncherVoidResult EnsureRemoteImagebase() { 1464 if (!mRemoteImagebase) { 1465 LauncherResult<HMODULE> remoteImageBaseResult = 1466 GetProcessExeModule(mRemoteProcess); 1467 if (remoteImageBaseResult.isErr()) { 1468 return remoteImageBaseResult.propagateErr(); 1469 } 1470 1471 mRemoteImagebase = 1472 reinterpret_cast<uint8_t*>(remoteImageBaseResult.unwrap()); 1473 } 1474 return Ok(); 1475 } 1476 1477 template <typename T> 1478 T* LocalExecToRemoteExec(T* aLocalAddress) const { 1479 MOZ_ASSERT(mRemoteImagebase); 1480 MOZ_ASSERT(mLocalExec.IsWithinImage(aLocalAddress)); 1481 1482 if (!mRemoteImagebase || !mLocalExec.IsWithinImage(aLocalAddress)) { 1483 return aLocalAddress; 1484 } 1485 1486 uintptr_t offset = reinterpret_cast<uintptr_t>(aLocalAddress) - 1487 reinterpret_cast<uintptr_t>(mLocalImagebase); 1488 return reinterpret_cast<T*>(mRemoteImagebase + offset); 1489 } 1490 1491 public: 1492 explicit CrossExecTransferManager(HANDLE aRemoteProcess) 1493 : mRemoteProcess(aRemoteProcess), 1494 mLocalImagebase( 1495 PEHeaders::HModuleToBaseAddr<uint8_t*>(GetLocalExecModule())), 1496 mLocalExec(mLocalImagebase), 1497 mRemoteImagebase(nullptr) {} 1498 1499 CrossExecTransferManager(HANDLE aRemoteProcess, HMODULE aLocalImagebase) 1500 : mRemoteProcess(aRemoteProcess), 1501 mLocalImagebase( 1502 PEHeaders::HModuleToBaseAddr<uint8_t*>(aLocalImagebase)), 1503 mLocalExec(mLocalImagebase), 1504 mRemoteImagebase(nullptr) {} 1505 1506 explicit operator bool() const { return !!mLocalExec; } 1507 HANDLE RemoteProcess() const { return mRemoteProcess; } 1508 const PEHeaders& LocalPEHeaders() const { return mLocalExec; } 1509 1510 AutoVirtualProtect Protect(void* aLocalAddress, size_t aLength, 1511 DWORD aProtFlags) { 1512 // If EnsureRemoteImagebase() fails, a subsequent operaion will fail. 1513 (void)EnsureRemoteImagebase(); 1514 return AutoVirtualProtect(LocalExecToRemoteExec(aLocalAddress), aLength, 1515 aProtFlags, mRemoteProcess); 1516 } 1517 1518 LauncherVoidResult Transfer(LPVOID aDestinationAddress, 1519 LPCVOID aBufferToWrite, SIZE_T aBufferSize) { 1520 LauncherVoidResult result = EnsureRemoteImagebase(); 1521 if (result.isErr()) { 1522 return result.propagateErr(); 1523 } 1524 1525 if (!::WriteProcessMemory(mRemoteProcess, 1526 LocalExecToRemoteExec(aDestinationAddress), 1527 aBufferToWrite, aBufferSize, nullptr)) { 1528 return LAUNCHER_ERROR_FROM_LAST(); 1529 } 1530 1531 return Ok(); 1532 } 1533 }; 1534 1535 #if !defined(MOZILLA_INTERNAL_API) 1536 1537 inline LauncherResult<HMODULE> GetModuleHandleFromLeafName( 1538 const UNICODE_STRING& aTarget) { 1539 auto maybePeb = nt::GetProcessPebPtr(kCurrentProcess); 1540 if (maybePeb.isErr()) { 1541 return maybePeb.propagateErr(); 1542 } 1543 1544 const PPEB peb = reinterpret_cast<PPEB>(maybePeb.unwrap()); 1545 if (!peb->Ldr) { 1546 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT); 1547 } 1548 1549 auto firstItem = &peb->Ldr->InMemoryOrderModuleList; 1550 for (auto p = firstItem->Flink; p != firstItem; p = p->Flink) { 1551 const auto currentTableEntry = 1552 CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 1553 1554 UNICODE_STRING leafName; 1555 nt::GetLeafName(&leafName, ¤tTableEntry->FullDllName); 1556 1557 if (::RtlCompareUnicodeString(&leafName, &aTarget, TRUE) == 0) { 1558 return reinterpret_cast<HMODULE>(currentTableEntry->DllBase); 1559 } 1560 } 1561 1562 return LAUNCHER_ERROR_FROM_WIN32(ERROR_MOD_NOT_FOUND); 1563 } 1564 1565 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS SRWLock final { 1566 public: 1567 constexpr SRWLock() : mLock(SRWLOCK_INIT) {} 1568 1569 void LockShared() { ::RtlAcquireSRWLockShared(&mLock); } 1570 1571 void LockExclusive() { ::RtlAcquireSRWLockExclusive(&mLock); } 1572 1573 void UnlockShared() { ::RtlReleaseSRWLockShared(&mLock); } 1574 1575 void UnlockExclusive() { ::RtlReleaseSRWLockExclusive(&mLock); } 1576 1577 SRWLock(const SRWLock&) = delete; 1578 SRWLock(SRWLock&&) = delete; 1579 SRWLock& operator=(const SRWLock&) = delete; 1580 SRWLock& operator=(SRWLock&&) = delete; 1581 1582 SRWLOCK* operator&() { return &mLock; } 1583 1584 private: 1585 SRWLOCK mLock; 1586 }; 1587 1588 class MOZ_RAII AutoExclusiveLock final { 1589 public: 1590 explicit AutoExclusiveLock(SRWLock& aLock) : mLock(aLock) { 1591 aLock.LockExclusive(); 1592 } 1593 1594 ~AutoExclusiveLock() { mLock.UnlockExclusive(); } 1595 1596 AutoExclusiveLock(const AutoExclusiveLock&) = delete; 1597 AutoExclusiveLock(AutoExclusiveLock&&) = delete; 1598 AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete; 1599 AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete; 1600 1601 private: 1602 SRWLock& mLock; 1603 }; 1604 1605 class MOZ_RAII AutoSharedLock final { 1606 public: 1607 explicit AutoSharedLock(SRWLock& aLock) : mLock(aLock) { aLock.LockShared(); } 1608 1609 ~AutoSharedLock() { mLock.UnlockShared(); } 1610 1611 AutoSharedLock(const AutoSharedLock&) = delete; 1612 AutoSharedLock(AutoSharedLock&&) = delete; 1613 AutoSharedLock& operator=(const AutoSharedLock&) = delete; 1614 AutoSharedLock& operator=(AutoSharedLock&&) = delete; 1615 1616 private: 1617 SRWLock& mLock; 1618 }; 1619 1620 #endif // !defined(MOZILLA_INTERNAL_API) 1621 1622 class RtlAllocPolicy { 1623 public: 1624 template <typename T> 1625 T* maybe_pod_malloc(size_t aNumElems) { 1626 size_t size; 1627 if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) { 1628 return nullptr; 1629 } 1630 1631 return static_cast<T*>(::RtlAllocateHeap(RtlGetProcessHeap(), 0, size)); 1632 } 1633 1634 template <typename T> 1635 T* maybe_pod_calloc(size_t aNumElems) { 1636 size_t size; 1637 if (MOZ_UNLIKELY(!mozilla::SafeMul(aNumElems, sizeof(T), &size))) { 1638 return nullptr; 1639 } 1640 1641 return static_cast<T*>( 1642 ::RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, size)); 1643 } 1644 1645 template <typename T> 1646 T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) { 1647 size_t size; 1648 if (MOZ_UNLIKELY(!mozilla::SafeMul(aNewSize, sizeof(T), &size))) { 1649 return nullptr; 1650 } 1651 1652 return static_cast<T*>( 1653 ::RtlReAllocateHeap(RtlGetProcessHeap(), 0, aPtr, size)); 1654 } 1655 1656 template <typename T> 1657 T* pod_malloc(size_t aNumElems) { 1658 return maybe_pod_malloc<T>(aNumElems); 1659 } 1660 1661 template <typename T> 1662 T* pod_calloc(size_t aNumElems) { 1663 return maybe_pod_calloc<T>(aNumElems); 1664 } 1665 1666 template <typename T> 1667 T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) { 1668 return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize); 1669 } 1670 1671 template <typename T> 1672 void free_(T* aPtr, size_t aNumElems = 0) { 1673 ::RtlFreeHeap(RtlGetProcessHeap(), 0, aPtr); 1674 } 1675 1676 void reportAllocOverflow() const {} 1677 1678 [[nodiscard]] bool checkSimulatedOOM() const { return true; } 1679 }; 1680 1681 class AutoMappedView final { 1682 void* mView; 1683 1684 void Unmap() { 1685 if (!mView) { 1686 return; 1687 } 1688 1689 #if defined(MOZILLA_INTERNAL_API) 1690 ::UnmapViewOfFile(mView); 1691 #else 1692 NTSTATUS status = ::NtUnmapViewOfSection(nt::kCurrentProcess, mView); 1693 if (!NT_SUCCESS(status)) { 1694 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status)); 1695 } 1696 #endif 1697 mView = nullptr; 1698 } 1699 1700 public: 1701 explicit AutoMappedView(void* aView) : mView(aView) {} 1702 1703 AutoMappedView(HANDLE aSection, ULONG aProtectionFlags) : mView(nullptr) { 1704 #if defined(MOZILLA_INTERNAL_API) 1705 mView = ::MapViewOfFile(aSection, aProtectionFlags, 0, 0, 0); 1706 #else 1707 SIZE_T viewSize = 0; 1708 NTSTATUS status = ::NtMapViewOfSection(aSection, nt::kCurrentProcess, 1709 &mView, 0, 0, nullptr, &viewSize, 1710 ViewUnmap, 0, aProtectionFlags); 1711 if (!NT_SUCCESS(status)) { 1712 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status)); 1713 } 1714 #endif 1715 } 1716 ~AutoMappedView() { Unmap(); } 1717 1718 // Allow move & Disallow copy 1719 AutoMappedView(AutoMappedView&& aOther) : mView(aOther.mView) { 1720 aOther.mView = nullptr; 1721 } 1722 AutoMappedView& operator=(AutoMappedView&& aOther) { 1723 if (this != &aOther) { 1724 Unmap(); 1725 mView = aOther.mView; 1726 aOther.mView = nullptr; 1727 } 1728 return *this; 1729 } 1730 AutoMappedView(const AutoMappedView&) = delete; 1731 AutoMappedView& operator=(const AutoMappedView&) = delete; 1732 1733 explicit operator bool() const { return !!mView; } 1734 template <typename T> 1735 T* as() { 1736 return reinterpret_cast<T*>(mView); 1737 } 1738 1739 void* release() { 1740 void* p = mView; 1741 mView = nullptr; 1742 return p; 1743 } 1744 }; 1745 1746 #if defined(_M_X64) 1747 // CheckStack ensures that stack memory pages are committed up to a given size 1748 // in bytes from the current stack pointer. It updates the thread stack limit, 1749 // which points to the lowest committed stack address. 1750 MOZ_NEVER_INLINE MOZ_NAKED inline void CheckStack(uint32_t size) { 1751 asm volatile( 1752 "mov %ecx, %eax;" 1753 # if defined(__MINGW32__) 1754 "jmp ___chkstk_ms;" 1755 # else 1756 "jmp __chkstk;" 1757 # endif // __MINGW32__ 1758 ); 1759 } 1760 #endif // _M_X64 1761 1762 } // namespace nt 1763 } // namespace mozilla 1764 1765 #endif // mozilla_NativeNt_h