[SM91] Update to Spidermonkey 91.1.3 APIs
[0ad.git] / libraries / source / spidermonkey / include-win32-debug / mozilla / NativeNt.h
blob1cce667317118a905268dc2e5bafdd337950c864
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/. */
7 #ifndef mozilla_NativeNt_h
8 #define mozilla_NativeNt_h
10 #include <stdint.h>
11 #include <windows.h>
12 #include <winnt.h>
13 #include <winternl.h>
15 #include <algorithm>
16 #include <utility>
18 #include "mozilla/ArrayUtils.h"
19 #include "mozilla/Attributes.h"
20 #include "mozilla/DebugOnly.h"
21 #include "mozilla/Maybe.h"
22 #include "mozilla/Range.h"
23 #include "mozilla/Span.h"
24 #include "mozilla/WinHeaderOnlyUtils.h"
25 #include "mozilla/interceptor/MMPolicies.h"
26 #include "mozilla/interceptor/TargetFunction.h"
28 #if defined(MOZILLA_INTERNAL_API)
29 # include "nsString.h"
30 #endif // defined(MOZILLA_INTERNAL_API)
32 // The declarations within this #if block are intended to be used for initial
33 // process initialization ONLY. You probably don't want to be using these in
34 // normal Gecko code!
35 #if !defined(MOZILLA_INTERNAL_API)
37 extern "C" {
39 # if !defined(STATUS_ACCESS_DENIED)
40 # define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
41 # endif // !defined(STATUS_ACCESS_DENIED)
43 # if !defined(STATUS_DLL_NOT_FOUND)
44 # define STATUS_DLL_NOT_FOUND ((NTSTATUS)0xC0000135L)
45 # endif // !defined(STATUS_DLL_NOT_FOUND)
47 # if !defined(STATUS_UNSUCCESSFUL)
48 # define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
49 # endif // !defined(STATUS_UNSUCCESSFUL)
51 # if !defined(STATUS_INFO_LENGTH_MISMATCH)
52 # define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
53 # endif
55 enum SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 };
57 NTSTATUS NTAPI NtMapViewOfSection(
58 HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits,
59 SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
60 SECTION_INHERIT aInheritDisposition, ULONG aAllocationType,
61 ULONG aProtectionFlags);
63 NTSTATUS NTAPI NtUnmapViewOfSection(HANDLE aProcess, PVOID aBaseAddress);
65 enum MEMORY_INFORMATION_CLASS {
66 MemoryBasicInformation = 0,
67 MemorySectionName = 2
70 // NB: When allocating, space for the buffer must also be included
71 typedef struct _MEMORY_SECTION_NAME {
72 UNICODE_STRING mSectionFileName;
73 } MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
75 NTSTATUS NTAPI NtQueryVirtualMemory(HANDLE aProcess, PVOID aBaseAddress,
76 MEMORY_INFORMATION_CLASS aMemInfoClass,
77 PVOID aMemInfo, SIZE_T aMemInfoLen,
78 PSIZE_T aReturnLen);
80 LONG NTAPI RtlCompareUnicodeString(PCUNICODE_STRING aStr1,
81 PCUNICODE_STRING aStr2,
82 BOOLEAN aCaseInsensitive);
84 BOOLEAN NTAPI RtlEqualUnicodeString(PCUNICODE_STRING aStr1,
85 PCUNICODE_STRING aStr2,
86 BOOLEAN aCaseInsensitive);
88 NTSTATUS NTAPI RtlGetVersion(PRTL_OSVERSIONINFOW aOutVersionInformation);
90 VOID NTAPI RtlAcquireSRWLockExclusive(PSRWLOCK aLock);
91 VOID NTAPI RtlAcquireSRWLockShared(PSRWLOCK aLock);
93 VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
94 VOID NTAPI RtlReleaseSRWLockShared(PSRWLOCK aLock);
96 ULONG NTAPI RtlNtStatusToDosError(NTSTATUS aStatus);
97 VOID NTAPI RtlSetLastWin32Error(DWORD aError);
98 DWORD NTAPI RtlGetLastWin32Error();
100 VOID NTAPI RtlRunOnceInitialize(PRTL_RUN_ONCE aRunOnce);
102 NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress,
103 PVOID aBuffer, SIZE_T aNumBytesToRead,
104 PSIZE_T aNumBytesRead);
106 NTSTATUS NTAPI LdrLoadDll(PWCHAR aDllPath, PULONG aFlags,
107 PUNICODE_STRING aDllName, PHANDLE aOutHandle);
109 typedef ULONG(NTAPI* PRTL_RUN_ONCE_INIT_FN)(PRTL_RUN_ONCE, PVOID, PVOID*);
110 NTSTATUS NTAPI RtlRunOnceExecuteOnce(PRTL_RUN_ONCE aRunOnce,
111 PRTL_RUN_ONCE_INIT_FN aInitFn,
112 PVOID aContext, PVOID* aParameter);
114 } // extern "C"
116 #endif // !defined(MOZILLA_INTERNAL_API)
118 extern "C" {
119 PVOID NTAPI RtlAllocateHeap(PVOID aHeapHandle, ULONG aFlags, SIZE_T aSize);
121 PVOID NTAPI RtlReAllocateHeap(PVOID aHeapHandle, ULONG aFlags, LPVOID aMem,
122 SIZE_T aNewSize);
124 BOOLEAN NTAPI RtlFreeHeap(PVOID aHeapHandle, ULONG aFlags, PVOID aHeapBase);
126 BOOLEAN NTAPI RtlQueryPerformanceCounter(LARGE_INTEGER* aPerfCount);
128 #define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE 1
129 #define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING 2
130 NTSTATUS NTAPI RtlDuplicateUnicodeString(ULONG aFlags, PCUNICODE_STRING aSrc,
131 PUNICODE_STRING aDest);
133 VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING aUnicodeString);
134 } // extern "C"
136 namespace mozilla {
137 namespace nt {
140 * This class encapsulates a UNICODE_STRING that owns its own buffer. The
141 * buffer is always NULL terminated, thus allowing us to cast to a wide C-string
142 * without requiring any mutation.
144 * We only allow creation of this owned buffer from outside XUL.
146 class AllocatedUnicodeString final {
147 public:
148 AllocatedUnicodeString() : mUnicodeString() {}
150 #if defined(MOZILLA_INTERNAL_API)
151 AllocatedUnicodeString(const AllocatedUnicodeString& aOther) = delete;
153 AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) =
154 delete;
155 #else
156 explicit AllocatedUnicodeString(PCUNICODE_STRING aSrc) {
157 if (!aSrc) {
158 mUnicodeString = {};
159 return;
162 Duplicate(aSrc);
165 explicit AllocatedUnicodeString(const char* aSrc) {
166 if (!aSrc) {
167 mUnicodeString = {};
168 return;
171 Duplicate(aSrc);
174 AllocatedUnicodeString(const AllocatedUnicodeString& aOther) {
175 Duplicate(&aOther.mUnicodeString);
178 AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) {
179 Clear();
180 Duplicate(&aOther.mUnicodeString);
181 return *this;
184 AllocatedUnicodeString& operator=(PCUNICODE_STRING aSrc) {
185 Clear();
186 Duplicate(aSrc);
187 return *this;
189 #endif // defined(MOZILLA_INTERNAL_API)
191 AllocatedUnicodeString(AllocatedUnicodeString&& aOther)
192 : mUnicodeString(aOther.mUnicodeString) {
193 aOther.mUnicodeString = {};
196 AllocatedUnicodeString& operator=(AllocatedUnicodeString&& aOther) {
197 Clear();
198 mUnicodeString = aOther.mUnicodeString;
199 aOther.mUnicodeString = {};
200 return *this;
203 ~AllocatedUnicodeString() { Clear(); }
205 bool IsEmpty() const {
206 return !mUnicodeString.Buffer || !mUnicodeString.Length;
209 operator PCUNICODE_STRING() const { return &mUnicodeString; }
211 operator const WCHAR*() const { return mUnicodeString.Buffer; }
213 USHORT CharLen() const { return mUnicodeString.Length / sizeof(WCHAR); }
215 #if defined(MOZILLA_INTERNAL_API)
216 nsDependentString AsString() const {
217 if (!mUnicodeString.Buffer) {
218 return nsDependentString();
221 // We can use nsDependentString here as we guaranteed null termination
222 // when we allocated the string.
223 return nsDependentString(mUnicodeString.Buffer, CharLen());
225 #endif // defined(MOZILLA_INTERNAL_API)
227 private:
228 #if !defined(MOZILLA_INTERNAL_API)
229 void Duplicate(PCUNICODE_STRING aSrc) {
230 MOZ_ASSERT(aSrc);
232 // We duplicate with null termination so that this string may be used
233 // as a wide C-string without any further manipulation.
234 NTSTATUS ntStatus = ::RtlDuplicateUnicodeString(
235 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, aSrc, &mUnicodeString);
236 MOZ_ASSERT(NT_SUCCESS(ntStatus));
237 if (!NT_SUCCESS(ntStatus)) {
238 // Make sure that mUnicodeString does not contain bogus data
239 // (since not all callers zero it out before invoking)
240 mUnicodeString = {};
244 void Duplicate(const char* aSrc) {
245 MOZ_ASSERT(aSrc);
247 ANSI_STRING ansiStr;
248 RtlInitAnsiString(&ansiStr, aSrc);
249 NTSTATUS ntStatus =
250 ::RtlAnsiStringToUnicodeString(&mUnicodeString, &ansiStr, TRUE);
251 MOZ_ASSERT(NT_SUCCESS(ntStatus));
252 if (!NT_SUCCESS(ntStatus)) {
253 mUnicodeString = {};
256 #endif // !defined(MOZILLA_INTERNAL_API)
258 void Clear() {
259 if (!mUnicodeString.Buffer) {
260 return;
263 ::RtlFreeUnicodeString(&mUnicodeString);
264 mUnicodeString = {};
267 UNICODE_STRING mUnicodeString;
270 #if !defined(MOZILLA_INTERNAL_API)
272 struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME {
273 MemorySectionNameBuf() {
274 mSectionFileName.Length = 0;
275 mSectionFileName.MaximumLength = sizeof(mBuf);
276 mSectionFileName.Buffer = mBuf;
279 MemorySectionNameBuf(const MemorySectionNameBuf& aOther) { *this = aOther; }
281 MemorySectionNameBuf(MemorySectionNameBuf&& aOther) {
282 *this = std::move(aOther);
285 // We cannot use default copy here because mSectionFileName.Buffer needs to
286 // be updated to point to |this->mBuf|, not |aOther.mBuf|.
287 MemorySectionNameBuf& operator=(const MemorySectionNameBuf& aOther) {
288 mSectionFileName.Length = aOther.mSectionFileName.Length;
289 mSectionFileName.MaximumLength = sizeof(mBuf);
290 MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
291 mSectionFileName.Buffer = mBuf;
292 memcpy(mBuf, aOther.mBuf, aOther.mSectionFileName.Length);
293 return *this;
296 MemorySectionNameBuf& operator=(MemorySectionNameBuf&& aOther) {
297 mSectionFileName.Length = aOther.mSectionFileName.Length;
298 aOther.mSectionFileName.Length = 0;
299 mSectionFileName.MaximumLength = sizeof(mBuf);
300 MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
301 aOther.mSectionFileName.MaximumLength = sizeof(aOther.mBuf);
302 mSectionFileName.Buffer = mBuf;
303 memmove(mBuf, aOther.mBuf, mSectionFileName.Length);
304 return *this;
307 // Native NT paths, so we can't assume MAX_PATH. Use a larger buffer.
308 WCHAR mBuf[2 * MAX_PATH];
310 bool IsEmpty() const {
311 return !mSectionFileName.Buffer || !mSectionFileName.Length;
314 operator PCUNICODE_STRING() const { return &mSectionFileName; }
317 class MemorySectionNameOnHeap {
318 UniquePtr<uint8_t[]> mBuffer;
320 MemorySectionNameOnHeap() = default;
321 explicit MemorySectionNameOnHeap(size_t aBufferLen)
322 : mBuffer(MakeUnique<uint8_t[]>(aBufferLen)) {}
324 public:
325 static MemorySectionNameOnHeap GetBackingFilePath(HANDLE aProcess,
326 void* aSectionAddr) {
327 SIZE_T bufferLen = MAX_PATH * 2;
328 do {
329 MemorySectionNameOnHeap sectionName(bufferLen);
331 SIZE_T requiredBytes;
332 NTSTATUS ntStatus = ::NtQueryVirtualMemory(
333 aProcess, aSectionAddr, MemorySectionName, sectionName.mBuffer.get(),
334 bufferLen, &requiredBytes);
335 if (NT_SUCCESS(ntStatus)) {
336 return sectionName;
339 if (ntStatus != STATUS_INFO_LENGTH_MISMATCH ||
340 bufferLen >= requiredBytes) {
341 break;
344 bufferLen = requiredBytes;
345 } while (1);
347 return MemorySectionNameOnHeap();
350 // Allow move & Disallow copy
351 MemorySectionNameOnHeap(MemorySectionNameOnHeap&&) = default;
352 MemorySectionNameOnHeap& operator=(MemorySectionNameOnHeap&&) = default;
353 MemorySectionNameOnHeap(const MemorySectionNameOnHeap&) = delete;
354 MemorySectionNameOnHeap& operator=(const MemorySectionNameOnHeap&) = delete;
356 PCUNICODE_STRING AsUnicodeString() const {
357 return reinterpret_cast<PCUNICODE_STRING>(mBuffer.get());
361 inline bool FindCharInUnicodeString(const UNICODE_STRING& aStr, WCHAR aChar,
362 uint16_t& aPos, uint16_t aStartIndex = 0) {
363 const uint16_t aMaxIndex = aStr.Length / sizeof(WCHAR);
365 for (uint16_t curIndex = aStartIndex; curIndex < aMaxIndex; ++curIndex) {
366 if (aStr.Buffer[curIndex] == aChar) {
367 aPos = curIndex;
368 return true;
372 return false;
375 inline bool IsHexDigit(WCHAR aChar) {
376 return (aChar >= L'0' && aChar <= L'9') || (aChar >= L'A' && aChar <= L'F') ||
377 (aChar >= L'a' && aChar <= L'f');
380 inline bool MatchUnicodeString(const UNICODE_STRING& aStr,
381 bool (*aPredicate)(WCHAR)) {
382 WCHAR* cur = aStr.Buffer;
383 WCHAR* end = &aStr.Buffer[aStr.Length / sizeof(WCHAR)];
384 while (cur < end) {
385 if (!aPredicate(*cur)) {
386 return false;
389 ++cur;
392 return true;
395 inline bool Contains12DigitHexString(const UNICODE_STRING& aLeafName) {
396 // Quick check: If the string is too short, don't bother
397 // (We need at least 12 hex digits, one char for '.', and 3 for extension)
398 const USHORT kMinLen = (12 + 1 + 3) * sizeof(wchar_t);
399 if (aLeafName.Length < kMinLen) {
400 return false;
403 uint16_t start, end;
404 if (!FindCharInUnicodeString(aLeafName, L'.', start)) {
405 return false;
408 ++start;
409 if (!FindCharInUnicodeString(aLeafName, L'.', end, start)) {
410 return false;
413 if (end - start != 12) {
414 return false;
417 UNICODE_STRING test;
418 test.Buffer = &aLeafName.Buffer[start];
419 test.Length = (end - start) * sizeof(WCHAR);
420 test.MaximumLength = test.Length;
422 return MatchUnicodeString(test, &IsHexDigit);
425 inline bool IsFileNameAtLeast16HexDigits(const UNICODE_STRING& aLeafName) {
426 // Quick check: If the string is too short, don't bother
427 // (We need 16 hex digits, one char for '.', and 3 for extension)
428 const USHORT kMinLen = (16 + 1 + 3) * sizeof(wchar_t);
429 if (aLeafName.Length < kMinLen) {
430 return false;
433 uint16_t dotIndex;
434 if (!FindCharInUnicodeString(aLeafName, L'.', dotIndex)) {
435 return false;
438 if (dotIndex < 16) {
439 return false;
442 UNICODE_STRING test;
443 test.Buffer = aLeafName.Buffer;
444 test.Length = dotIndex * sizeof(WCHAR);
445 test.MaximumLength = aLeafName.MaximumLength;
447 return MatchUnicodeString(test, &IsHexDigit);
450 inline void GetLeafName(PUNICODE_STRING aDestString,
451 PCUNICODE_STRING aSrcString) {
452 WCHAR* buf = aSrcString->Buffer;
453 WCHAR* end = &aSrcString->Buffer[(aSrcString->Length / sizeof(WCHAR)) - 1];
454 WCHAR* cur = end;
455 while (cur >= buf) {
456 if (*cur == L'\\') {
457 break;
460 --cur;
463 // At this point, either cur points to the final backslash, or it points to
464 // buf - 1. Either way, we're interested in cur + 1 as the desired buffer.
465 aDestString->Buffer = cur + 1;
466 aDestString->Length = (end - aDestString->Buffer + 1) * sizeof(WCHAR);
467 aDestString->MaximumLength = aDestString->Length;
470 #endif // !defined(MOZILLA_INTERNAL_API)
472 #if defined(MOZILLA_INTERNAL_API)
474 inline const nsDependentSubstring GetLeafName(const nsAString& aString) {
475 auto it = aString.EndReading();
476 size_t pos = aString.Length();
477 while (it > aString.BeginReading()) {
478 if (*(it - 1) == u'\\') {
479 return Substring(aString, pos);
482 MOZ_ASSERT(pos > 0);
483 --pos;
484 --it;
487 return Substring(aString, 0); // No backslash in the string
490 #endif // defined(MOZILLA_INTERNAL_API)
492 inline char EnsureLowerCaseASCII(char aChar) {
493 if (aChar >= 'A' && aChar <= 'Z') {
494 aChar -= 'A' - 'a';
497 return aChar;
500 inline int StricmpASCII(const char* aLeft, const char* aRight) {
501 char curLeft, curRight;
503 do {
504 curLeft = EnsureLowerCaseASCII(*(aLeft++));
505 curRight = EnsureLowerCaseASCII(*(aRight++));
506 } while (curLeft && curLeft == curRight);
508 return curLeft - curRight;
511 inline int StrcmpASCII(const char* aLeft, const char* aRight) {
512 char curLeft, curRight;
514 do {
515 curLeft = *(aLeft++);
516 curRight = *(aRight++);
517 } while (curLeft && curLeft == curRight);
519 return curLeft - curRight;
522 inline size_t StrlenASCII(const char* aStr) {
523 size_t len = 0;
525 while (*(aStr++)) {
526 ++len;
529 return len;
532 struct CodeViewRecord70 {
533 uint32_t signature;
534 GUID pdbSignature;
535 uint32_t pdbAge;
536 // A UTF-8 string, according to
537 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/locator.cpp#L785
538 char pdbFileName[1];
541 class MOZ_RAII PEHeaders final {
543 * This structure is documented on MSDN as VS_VERSIONINFO, but is not present
544 * in SDK headers because it cannot be specified as a C struct. The following
545 * structure contains the fixed-length fields at the beginning of
546 * VS_VERSIONINFO.
548 struct VS_VERSIONINFO_HEADER {
549 WORD wLength;
550 WORD wValueLength;
551 WORD wType;
552 WCHAR szKey[16]; // ArrayLength(L"VS_VERSION_INFO")
553 // Additional data goes here, aligned on a 4-byte boundary
556 public:
557 // The lowest two bits of an HMODULE are used as flags. Stripping those bits
558 // from the HMODULE yields the base address of the binary's memory mapping.
559 // (See LoadLibraryEx docs on MSDN)
560 template <typename T>
561 static T HModuleToBaseAddr(HMODULE aModule) {
562 return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(aModule) &
563 ~uintptr_t(3));
566 explicit PEHeaders(void* aBaseAddress)
567 : PEHeaders(reinterpret_cast<PIMAGE_DOS_HEADER>(aBaseAddress)) {}
569 explicit PEHeaders(HMODULE aModule)
570 : PEHeaders(HModuleToBaseAddr<PIMAGE_DOS_HEADER>(aModule)) {}
572 explicit PEHeaders(PIMAGE_DOS_HEADER aMzHeader)
573 : mMzHeader(aMzHeader),
574 mPeHeader(nullptr),
575 mImageLimit(nullptr),
576 mIsImportDirectoryTampered(false) {
577 if (!mMzHeader || mMzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
578 return;
581 mPeHeader = RVAToPtrUnchecked<PIMAGE_NT_HEADERS>(mMzHeader->e_lfanew);
582 if (!mPeHeader || mPeHeader->Signature != IMAGE_NT_SIGNATURE) {
583 return;
586 if (mPeHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
587 return;
590 DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage;
591 // This is a coarse-grained check to ensure that the image size is
592 // reasonable. It we aren't big enough to contain headers, we have a
593 // problem!
594 if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) {
595 return;
598 mImageLimit = RVAToPtrUnchecked<void*>(imageSize - 1UL);
600 PIMAGE_DATA_DIRECTORY importDirEntry =
601 GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_IMPORT);
602 if (!importDirEntry) {
603 return;
606 mIsImportDirectoryTampered = (importDirEntry->VirtualAddress >= imageSize);
609 explicit operator bool() const { return !!mImageLimit; }
612 * This overload computes absolute virtual addresses relative to the base
613 * address of the binary.
615 template <typename T, typename R>
616 T RVAToPtr(R aRva) const {
617 return RVAToPtr<T>(mMzHeader, aRva);
621 * This overload computes a result by adding aRva to aBase, but also ensures
622 * that the resulting pointer falls within the bounds of this binary's memory
623 * mapping.
625 template <typename T, typename R>
626 T RVAToPtr(void* aBase, R aRva) const {
627 if (!mImageLimit) {
628 return nullptr;
631 char* absAddress = reinterpret_cast<char*>(aBase) + aRva;
632 if (absAddress < reinterpret_cast<char*>(mMzHeader) ||
633 absAddress > reinterpret_cast<char*>(mImageLimit)) {
634 return nullptr;
637 return reinterpret_cast<T>(absAddress);
640 Maybe<Range<const uint8_t>> GetBounds() const {
641 if (!mImageLimit) {
642 return Nothing();
645 auto base = reinterpret_cast<const uint8_t*>(mMzHeader);
646 DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage;
647 return Some(Range(base, imageSize));
650 DWORD GetFileCharacteristics() const {
651 return mPeHeader ? mPeHeader->FileHeader.Characteristics : 0;
654 bool IsWithinImage(const void* aAddress) const {
655 uintptr_t addr = reinterpret_cast<uintptr_t>(aAddress);
656 uintptr_t imageBase = reinterpret_cast<uintptr_t>(mMzHeader);
657 uintptr_t imageLimit = reinterpret_cast<uintptr_t>(mImageLimit);
658 return addr >= imageBase && addr <= imageLimit;
661 PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() const {
662 // If the import directory is already tampered, we skip bounds check
663 // because it could be located outside the mapped image.
664 return mIsImportDirectoryTampered
665 ? GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR,
666 BoundsCheckPolicy::Skip>(
667 IMAGE_DIRECTORY_ENTRY_IMPORT)
668 : GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR>(
669 IMAGE_DIRECTORY_ENTRY_IMPORT);
672 PIMAGE_RESOURCE_DIRECTORY GetResourceTable() const {
673 return GetImageDirectoryEntry<PIMAGE_RESOURCE_DIRECTORY>(
674 IMAGE_DIRECTORY_ENTRY_RESOURCE);
677 PIMAGE_DATA_DIRECTORY GetImageDirectoryEntryPtr(
678 const uint32_t aDirectoryIndex, uint32_t* aOutRva = nullptr) const {
679 if (aOutRva) {
680 *aOutRva = 0;
683 IMAGE_OPTIONAL_HEADER& optionalHeader = mPeHeader->OptionalHeader;
685 const uint32_t maxIndex = std::min(optionalHeader.NumberOfRvaAndSizes,
686 DWORD(IMAGE_NUMBEROF_DIRECTORY_ENTRIES));
687 if (aDirectoryIndex >= maxIndex) {
688 return nullptr;
691 PIMAGE_DATA_DIRECTORY dirEntry =
692 &optionalHeader.DataDirectory[aDirectoryIndex];
693 if (aOutRva) {
694 *aOutRva = reinterpret_cast<char*>(dirEntry) -
695 reinterpret_cast<char*>(mMzHeader);
696 MOZ_ASSERT(*aOutRva);
699 return dirEntry;
702 bool GetVersionInfo(uint64_t& aOutVersion) const {
703 // RT_VERSION == 16
704 // Version resources require an id of 1
705 auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1);
706 if (!root) {
707 return false;
710 VS_FIXEDFILEINFO* fixedInfo = GetFixedFileInfo(root);
711 if (!fixedInfo) {
712 return false;
715 aOutVersion = ((static_cast<uint64_t>(fixedInfo->dwFileVersionMS) << 32) |
716 static_cast<uint64_t>(fixedInfo->dwFileVersionLS));
717 return true;
720 bool GetTimeStamp(DWORD& aResult) const {
721 if (!(*this)) {
722 return false;
725 aResult = mPeHeader->FileHeader.TimeDateStamp;
726 return true;
729 PIMAGE_IMPORT_DESCRIPTOR
730 GetImportDescriptor(const char* aModuleNameASCII) const {
731 for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
732 IsValid(curImpDesc); ++curImpDesc) {
733 auto curName = mIsImportDirectoryTampered
734 ? RVAToPtrUnchecked<const char*>(curImpDesc->Name)
735 : RVAToPtr<const char*>(curImpDesc->Name);
736 if (!curName) {
737 return nullptr;
740 if (StricmpASCII(aModuleNameASCII, curName)) {
741 continue;
744 // curImpDesc now points to the IAT for the module we're interested in
745 return curImpDesc;
748 return nullptr;
751 template <typename CallbackT>
752 void EnumImportChunks(const CallbackT& aCallback) 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 continue;
762 aCallback(curName);
767 * If |aBoundaries| is given, this method checks whether each IAT entry is
768 * within the given range, and if any entry is out of the range, we return
769 * Nothing().
771 Maybe<Span<IMAGE_THUNK_DATA>> GetIATThunksForModule(
772 const char* aModuleNameASCII,
773 const Range<const uint8_t>* aBoundaries = nullptr) const {
774 PIMAGE_IMPORT_DESCRIPTOR impDesc = GetImportDescriptor(aModuleNameASCII);
775 if (!impDesc) {
776 return Nothing();
779 auto firstIatThunk =
780 this->template RVAToPtr<PIMAGE_THUNK_DATA>(impDesc->FirstThunk);
781 if (!firstIatThunk) {
782 return Nothing();
785 // Find the length by iterating through the table until we find a null entry
786 PIMAGE_THUNK_DATA curIatThunk = firstIatThunk;
787 while (IsValid(curIatThunk)) {
788 if (aBoundaries) {
789 auto iatEntry =
790 reinterpret_cast<const uint8_t*>(curIatThunk->u1.Function);
791 if (iatEntry < aBoundaries->begin().get() ||
792 iatEntry >= aBoundaries->end().get()) {
793 return Nothing();
797 ++curIatThunk;
800 return Some(Span(firstIatThunk, curIatThunk));
804 * Resources are stored in a three-level tree. To locate a particular entry,
805 * you must supply a resource type, the resource id, and then the language id.
806 * If aLangId == 0, we just resolve the first entry regardless of language.
808 template <typename T>
809 T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) const {
810 PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable();
811 if (!topLevel) {
812 return nullptr;
815 PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry =
816 FindResourceEntry(topLevel, aType);
817 if (!typeEntry || !typeEntry->DataIsDirectory) {
818 return nullptr;
821 auto idDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
822 topLevel, typeEntry->OffsetToDirectory);
823 PIMAGE_RESOURCE_DIRECTORY_ENTRY idEntry = FindResourceEntry(idDir, aResId);
824 if (!idEntry || !idEntry->DataIsDirectory) {
825 return nullptr;
828 auto langDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
829 topLevel, idEntry->OffsetToDirectory);
830 PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry;
831 if (aLangId) {
832 langEntry = FindResourceEntry(langDir, aLangId);
833 } else {
834 langEntry = FindFirstResourceEntry(langDir);
837 if (!langEntry || langEntry->DataIsDirectory) {
838 return nullptr;
841 auto dataEntry =
842 RVAToPtr<PIMAGE_RESOURCE_DATA_ENTRY>(topLevel, langEntry->OffsetToData);
843 return dataEntry ? RVAToPtr<T>(dataEntry->OffsetToData) : nullptr;
846 template <size_t N>
847 Maybe<Span<const uint8_t>> FindSection(const char (&aSecName)[N],
848 DWORD aCharacteristicsMask) const {
849 static_assert((N - 1) <= IMAGE_SIZEOF_SHORT_NAME,
850 "Section names must be at most 8 characters excluding null "
851 "terminator");
853 if (!(*this)) {
854 return Nothing();
857 Span<IMAGE_SECTION_HEADER> sectionTable = GetSectionTable();
858 for (auto&& sectionHeader : sectionTable) {
859 if (strncmp(reinterpret_cast<const char*>(sectionHeader.Name), aSecName,
860 IMAGE_SIZEOF_SHORT_NAME)) {
861 continue;
864 if (!(sectionHeader.Characteristics & aCharacteristicsMask)) {
865 // We found the section but it does not have the expected
866 // characteristics
867 return Nothing();
870 DWORD rva = sectionHeader.VirtualAddress;
871 if (!rva) {
872 return Nothing();
875 DWORD size = sectionHeader.Misc.VirtualSize;
876 if (!size) {
877 return Nothing();
880 auto base = RVAToPtr<const uint8_t*>(rva);
881 return Some(Span(base, size));
884 return Nothing();
887 // There may be other code sections in the binary besides .text
888 Maybe<Span<const uint8_t>> GetTextSectionInfo() const {
889 return FindSection(".text", IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE |
890 IMAGE_SCN_MEM_READ);
893 static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc) {
894 return aImpDesc && aImpDesc->OriginalFirstThunk != 0;
897 static bool IsValid(PIMAGE_THUNK_DATA aImgThunk) {
898 return aImgThunk && aImgThunk->u1.Ordinal != 0;
901 bool IsImportDirectoryTampered() const { return mIsImportDirectoryTampered; }
903 FARPROC GetEntryPoint() const {
904 // Use the unchecked version because the entrypoint may be tampered.
905 return RVAToPtrUnchecked<FARPROC>(
906 mPeHeader->OptionalHeader.AddressOfEntryPoint);
909 const CodeViewRecord70* GetPdbInfo() const {
910 PIMAGE_DEBUG_DIRECTORY debugDirectory =
911 GetImageDirectoryEntry<PIMAGE_DEBUG_DIRECTORY>(
912 IMAGE_DIRECTORY_ENTRY_DEBUG);
913 if (!debugDirectory) {
914 return nullptr;
917 const CodeViewRecord70* debugInfo =
918 RVAToPtr<CodeViewRecord70*>(debugDirectory->AddressOfRawData);
919 return (debugInfo && debugInfo->signature == 'SDSR') ? debugInfo : nullptr;
922 private:
923 enum class BoundsCheckPolicy { Default, Skip };
925 template <typename T, BoundsCheckPolicy Policy = BoundsCheckPolicy::Default>
926 T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) const {
927 PIMAGE_DATA_DIRECTORY dirEntry = GetImageDirectoryEntryPtr(aDirectoryIndex);
928 if (!dirEntry) {
929 return nullptr;
932 return Policy == BoundsCheckPolicy::Skip
933 ? RVAToPtrUnchecked<T>(dirEntry->VirtualAddress)
934 : RVAToPtr<T>(dirEntry->VirtualAddress);
937 // This private variant does not have bounds checks, because we need to be
938 // able to resolve the bounds themselves.
939 template <typename T, typename R>
940 T RVAToPtrUnchecked(R aRva) const {
941 return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
944 Span<IMAGE_SECTION_HEADER> GetSectionTable() const {
945 MOZ_ASSERT(*this);
946 auto base = RVAToPtr<PIMAGE_SECTION_HEADER>(
947 &mPeHeader->OptionalHeader, mPeHeader->FileHeader.SizeOfOptionalHeader);
948 // The Windows loader has an internal limit of 96 sections (per PE spec)
949 auto numSections =
950 std::min(mPeHeader->FileHeader.NumberOfSections, WORD(96));
951 return Span{base, numSections};
954 PIMAGE_RESOURCE_DIRECTORY_ENTRY
955 FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) const {
956 if (!aCurLevel) {
957 return nullptr;
960 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
961 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
962 // searches by ID, we need to skip past any named entries before iterating.
963 auto dirEnt =
964 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) +
965 aCurLevel->NumberOfNamedEntries;
966 for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) {
967 if (dirEnt[i].Id == aId) {
968 return &dirEnt[i];
972 return nullptr;
975 PIMAGE_RESOURCE_DIRECTORY_ENTRY
976 FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) const {
977 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
978 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first
979 // entry, regardless of whether it is indexed by name or by id.
980 auto dirEnt =
981 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1);
982 WORD numEntries =
983 aCurLevel->NumberOfNamedEntries + aCurLevel->NumberOfIdEntries;
984 if (!numEntries) {
985 return nullptr;
988 return dirEnt;
991 VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) const {
992 WORD length = aVerInfo->wLength;
993 if (length < sizeof(VS_VERSIONINFO_HEADER)) {
994 return nullptr;
997 const wchar_t kVersionInfoKey[] = L"VS_VERSION_INFO";
998 if (::RtlCompareMemory(aVerInfo->szKey, kVersionInfoKey,
999 ArrayLength(kVersionInfoKey)) !=
1000 ArrayLength(kVersionInfoKey)) {
1001 return nullptr;
1004 if (aVerInfo->wValueLength != sizeof(VS_FIXEDFILEINFO)) {
1005 // Fixed file info does not exist
1006 return nullptr;
1009 WORD offset = sizeof(VS_VERSIONINFO_HEADER);
1011 uintptr_t base = reinterpret_cast<uintptr_t>(aVerInfo);
1012 // Align up to 4-byte boundary
1013 #pragma warning(suppress : 4146)
1014 offset += (-(base + offset) & 3);
1016 if (offset >= length) {
1017 return nullptr;
1020 auto result = reinterpret_cast<VS_FIXEDFILEINFO*>(base + offset);
1021 if (result->dwSignature != 0xFEEF04BD) {
1022 return nullptr;
1025 return result;
1028 private:
1029 PIMAGE_DOS_HEADER mMzHeader;
1030 PIMAGE_NT_HEADERS mPeHeader;
1031 void* mImageLimit;
1032 bool mIsImportDirectoryTampered;
1035 // This class represents an export section of a local/remote process.
1036 template <typename MMPolicy>
1037 class MOZ_RAII PEExportSection {
1038 const MMPolicy& mMMPolicy;
1039 uintptr_t mImageBase;
1040 DWORD mOrdinalBase;
1041 DWORD mRvaDirStart;
1042 DWORD mRvaDirEnd;
1043 mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportAddressTable;
1044 mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportNameTable;
1045 mozilla::interceptor::TargetObjectArray<MMPolicy, WORD> mExportOrdinalTable;
1047 explicit PEExportSection(const MMPolicy& aMMPolicy)
1048 : mMMPolicy(aMMPolicy),
1049 mImageBase(0),
1050 mOrdinalBase(0),
1051 mRvaDirStart(0),
1052 mRvaDirEnd(0),
1053 mExportAddressTable(mMMPolicy),
1054 mExportNameTable(mMMPolicy),
1055 mExportOrdinalTable(mMMPolicy) {}
1057 PEExportSection(const MMPolicy& aMMPolicy, uintptr_t aImageBase,
1058 DWORD aRvaDirStart, DWORD aRvaDirEnd,
1059 const IMAGE_EXPORT_DIRECTORY& exportDir)
1060 : mMMPolicy(aMMPolicy),
1061 mImageBase(aImageBase),
1062 mOrdinalBase(exportDir.Base),
1063 mRvaDirStart(aRvaDirStart),
1064 mRvaDirEnd(aRvaDirEnd),
1065 mExportAddressTable(mMMPolicy,
1066 mImageBase + exportDir.AddressOfFunctions,
1067 exportDir.NumberOfFunctions),
1068 mExportNameTable(mMMPolicy, mImageBase + exportDir.AddressOfNames,
1069 exportDir.NumberOfNames),
1070 mExportOrdinalTable(mMMPolicy,
1071 mImageBase + exportDir.AddressOfNameOrdinals,
1072 exportDir.NumberOfNames) {}
1074 static const PEExportSection Get(uintptr_t aImageBase,
1075 const MMPolicy& aMMPolicy) {
1076 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_DOS_HEADER> mzHeader(
1077 aMMPolicy, aImageBase);
1078 if (!mzHeader || mzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
1079 return PEExportSection(aMMPolicy);
1082 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_NT_HEADERS> peHeader(
1083 aMMPolicy, aImageBase + mzHeader->e_lfanew);
1084 if (!peHeader || peHeader->Signature != IMAGE_NT_SIGNATURE) {
1085 return PEExportSection(aMMPolicy);
1088 if (peHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
1089 return PEExportSection(aMMPolicy);
1092 const IMAGE_OPTIONAL_HEADER& optionalHeader = peHeader->OptionalHeader;
1094 DWORD imageSize = optionalHeader.SizeOfImage;
1095 // This is a coarse-grained check to ensure that the image size is
1096 // reasonable. It we aren't big enough to contain headers, we have a
1097 // problem!
1098 if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) {
1099 return PEExportSection(aMMPolicy);
1102 if (optionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) {
1103 return PEExportSection(aMMPolicy);
1106 const IMAGE_DATA_DIRECTORY& exportDirectoryEntry =
1107 optionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
1108 if (!exportDirectoryEntry.VirtualAddress || !exportDirectoryEntry.Size) {
1109 return PEExportSection(aMMPolicy);
1112 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_EXPORT_DIRECTORY>
1113 exportDirectory(aMMPolicy,
1114 aImageBase + exportDirectoryEntry.VirtualAddress);
1115 if (!exportDirectory || !exportDirectory->NumberOfFunctions) {
1116 return PEExportSection(aMMPolicy);
1119 return PEExportSection(
1120 aMMPolicy, aImageBase, exportDirectoryEntry.VirtualAddress,
1121 exportDirectoryEntry.VirtualAddress + exportDirectoryEntry.Size,
1122 *exportDirectory.GetLocalBase());
1125 FARPROC GetProcAddressByOrdinal(WORD aOrdinal) const {
1126 if (aOrdinal < mOrdinalBase) {
1127 return nullptr;
1130 auto rvaToFunction = mExportAddressTable[aOrdinal - mOrdinalBase];
1131 if (!rvaToFunction) {
1132 return nullptr;
1134 return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
1137 public:
1138 static const PEExportSection Get(HMODULE aModule, const MMPolicy& aMMPolicy) {
1139 return Get(PEHeaders::HModuleToBaseAddr<uintptr_t>(aModule), aMMPolicy);
1142 explicit operator bool() const {
1143 // Because PEExportSection doesn't use MMPolicy::Reserve(), a boolified
1144 // mMMPolicy is expected to be false. We don't check mMMPolicy here.
1145 return mImageBase && mRvaDirStart && mRvaDirEnd && mExportAddressTable &&
1146 mExportNameTable && mExportOrdinalTable;
1149 template <typename T>
1150 T RVAToPtr(uint32_t aRva) const {
1151 return reinterpret_cast<T>(mImageBase + aRva);
1154 PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const {
1155 if (!*this) {
1156 return nullptr;
1159 return RVAToPtr<PIMAGE_EXPORT_DIRECTORY>(mRvaDirStart);
1163 * This functions searches the export table for a given string as
1164 * GetProcAddress does, but this returns a matched entry of the Export
1165 * Address Table i.e. a pointer to an RVA of a matched function instead
1166 * of a function address. If the entry is forwarded, this function
1167 * returns nullptr.
1169 const DWORD* FindExportAddressTableEntry(
1170 const char* aFunctionNameASCII) const {
1171 if (!*this || !aFunctionNameASCII) {
1172 return nullptr;
1175 struct NameTableComparator {
1176 NameTableComparator(const PEExportSection<MMPolicy>& aExportSection,
1177 const char* aTarget)
1178 : mExportSection(aExportSection),
1179 mTargetName(aTarget),
1180 mTargetNamelength(StrlenASCII(aTarget)) {}
1182 int operator()(DWORD aRVAToString) const {
1183 mozilla::interceptor::TargetObjectArray<MMPolicy, char> itemString(
1184 mExportSection.mMMPolicy, mExportSection.mImageBase + aRVAToString,
1185 mTargetNamelength + 1);
1186 return StrcmpASCII(mTargetName, itemString[0]);
1189 const PEExportSection<MMPolicy>& mExportSection;
1190 const char* mTargetName;
1191 size_t mTargetNamelength;
1194 const NameTableComparator comp(*this, aFunctionNameASCII);
1196 size_t match;
1197 if (!mExportNameTable.BinarySearchIf(comp, &match)) {
1198 return nullptr;
1201 const WORD* index = mExportOrdinalTable[match];
1202 if (!index) {
1203 return nullptr;
1206 const DWORD* rvaToFunction = mExportAddressTable[*index];
1207 if (!rvaToFunction) {
1208 return nullptr;
1211 if (*rvaToFunction >= mRvaDirStart && *rvaToFunction < mRvaDirEnd) {
1212 // If an entry points to an address within the export section, the
1213 // field is a forwarder RVA. We return nullptr because the entry is
1214 // not a function address but a null-terminated string used for export
1215 // forwarding.
1216 return nullptr;
1219 return rvaToFunction;
1223 * This functions behaves the same as the native ::GetProcAddress except
1224 * the following cases:
1225 * - Returns nullptr if a target entry is forwarded to another dll.
1227 FARPROC GetProcAddress(const char* aFunctionNameASCII) const {
1228 uintptr_t maybeOdrinal = reinterpret_cast<uintptr_t>(aFunctionNameASCII);
1229 // When the high-order word of |aFunctionNameASCII| is zero, it's not
1230 // a string but an ordinal value.
1231 if (maybeOdrinal < 0x10000) {
1232 return GetProcAddressByOrdinal(static_cast<WORD>(maybeOdrinal));
1235 auto rvaToFunction = FindExportAddressTableEntry(aFunctionNameASCII);
1236 if (!rvaToFunction) {
1237 return nullptr;
1239 return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
1243 inline HANDLE RtlGetProcessHeap() {
1244 PTEB teb = ::NtCurrentTeb();
1245 PPEB peb = teb->ProcessEnvironmentBlock;
1246 return peb->Reserved4[1];
1249 inline PVOID RtlGetThreadLocalStoragePointer() {
1250 return ::NtCurrentTeb()->Reserved1[11];
1253 inline void RtlSetThreadLocalStoragePointerForTestingOnly(PVOID aNewValue) {
1254 ::NtCurrentTeb()->Reserved1[11] = aNewValue;
1257 inline DWORD RtlGetCurrentThreadId() {
1258 PTEB teb = ::NtCurrentTeb();
1259 CLIENT_ID* cid = reinterpret_cast<CLIENT_ID*>(&teb->Reserved1[8]);
1260 return static_cast<DWORD>(reinterpret_cast<uintptr_t>(cid->UniqueThread) &
1261 0xFFFFFFFFUL);
1264 const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
1266 inline LauncherResult<DWORD> GetParentProcessId() {
1267 struct PROCESS_BASIC_INFORMATION {
1268 NTSTATUS ExitStatus;
1269 PPEB PebBaseAddress;
1270 ULONG_PTR AffinityMask;
1271 LONG BasePriority;
1272 ULONG_PTR UniqueProcessId;
1273 ULONG_PTR InheritedFromUniqueProcessId;
1276 ULONG returnLength;
1277 PROCESS_BASIC_INFORMATION pbi = {};
1278 NTSTATUS status =
1279 ::NtQueryInformationProcess(kCurrentProcess, ProcessBasicInformation,
1280 &pbi, sizeof(pbi), &returnLength);
1281 if (!NT_SUCCESS(status)) {
1282 return LAUNCHER_ERROR_FROM_NTSTATUS(status);
1285 return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF);
1288 inline SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
1289 PMEMORY_BASIC_INFORMATION aMemInfo,
1290 SIZE_T aMemInfoLen) {
1291 #if defined(MOZILLA_INTERNAL_API)
1292 return ::VirtualQueryEx(aProcess, aAddress, aMemInfo, aMemInfoLen);
1293 #else
1294 SIZE_T returnedLength;
1295 NTSTATUS status = ::NtQueryVirtualMemory(
1296 aProcess, const_cast<PVOID>(aAddress), MemoryBasicInformation, aMemInfo,
1297 aMemInfoLen, &returnedLength);
1298 if (!NT_SUCCESS(status)) {
1299 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1300 returnedLength = 0;
1302 return returnedLength;
1303 #endif // defined(MOZILLA_INTERNAL_API)
1306 inline SIZE_T WINAPI VirtualQuery(LPCVOID aAddress,
1307 PMEMORY_BASIC_INFORMATION aMemInfo,
1308 SIZE_T aMemInfoLen) {
1309 return nt::VirtualQueryEx(kCurrentProcess, aAddress, aMemInfo, aMemInfoLen);
1312 struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY {
1313 DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}
1315 MOZ_IMPLICIT DataDirectoryEntry(const _IMAGE_DATA_DIRECTORY& aOther)
1316 : _IMAGE_DATA_DIRECTORY(aOther) {}
1318 DataDirectoryEntry(const DataDirectoryEntry& aOther) = default;
1320 bool operator==(const DataDirectoryEntry& aOther) const {
1321 return VirtualAddress == aOther.VirtualAddress && Size == aOther.Size;
1324 bool operator!=(const DataDirectoryEntry& aOther) const {
1325 return !(*this == aOther);
1329 inline LauncherResult<void*> GetProcessPebPtr(HANDLE aProcess) {
1330 ULONG returnLength;
1331 PROCESS_BASIC_INFORMATION pbi;
1332 NTSTATUS status = ::NtQueryInformationProcess(
1333 aProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &returnLength);
1334 if (!NT_SUCCESS(status)) {
1335 return LAUNCHER_ERROR_FROM_NTSTATUS(status);
1338 return pbi.PebBaseAddress;
1342 * This function relies on a specific offset into the mostly-undocumented PEB
1343 * structure. The risk is reduced thanks to the fact that the Chromium sandbox
1344 * relies on the location of this field. It is unlikely to change at this point.
1345 * To further reduce the risk, we also check for the magic 'MZ' signature that
1346 * should indicate the beginning of a PE image.
1348 inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) {
1349 LauncherResult<void*> ppeb = GetProcessPebPtr(aProcess);
1350 if (ppeb.isErr()) {
1351 return ppeb.propagateErr();
1354 PEB peb;
1355 SIZE_T bytesRead;
1357 #if defined(MOZILLA_INTERNAL_API)
1358 if (!::ReadProcessMemory(aProcess, ppeb.unwrap(), &peb, sizeof(peb),
1359 &bytesRead) ||
1360 bytesRead != sizeof(peb)) {
1361 return LAUNCHER_ERROR_FROM_LAST();
1363 #else
1364 NTSTATUS ntStatus = ::NtReadVirtualMemory(aProcess, ppeb.unwrap(), &peb,
1365 sizeof(peb), &bytesRead);
1366 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(peb)) {
1367 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
1369 #endif
1371 // peb.ImageBaseAddress
1372 void* baseAddress = peb.Reserved3[1];
1374 char mzMagic[2];
1375 #if defined(MOZILLA_INTERNAL_API)
1376 if (!::ReadProcessMemory(aProcess, baseAddress, mzMagic, sizeof(mzMagic),
1377 &bytesRead) ||
1378 bytesRead != sizeof(mzMagic)) {
1379 return LAUNCHER_ERROR_FROM_LAST();
1381 #else
1382 ntStatus = ::NtReadVirtualMemory(aProcess, baseAddress, mzMagic,
1383 sizeof(mzMagic), &bytesRead);
1384 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(mzMagic)) {
1385 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
1387 #endif
1389 MOZ_ASSERT(mzMagic[0] == 'M' && mzMagic[1] == 'Z');
1390 if (mzMagic[0] != 'M' || mzMagic[1] != 'Z') {
1391 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
1394 return static_cast<HMODULE>(baseAddress);
1397 #if defined(_MSC_VER)
1398 extern "C" IMAGE_DOS_HEADER __ImageBase;
1399 #endif
1401 // This class manages data transfer from the local process's executable
1402 // to another process's executable via WriteProcessMemory.
1403 // Bug 1662560 told us the same executable may be mapped onto a different
1404 // address in a different process. This means when we transfer data within
1405 // the mapped executable such as a global variable or IAT from the current
1406 // process to another process, we need to shift its address by the difference
1407 // between two executable's mapped imagebase.
1408 class CrossExecTransferManager final {
1409 HANDLE mRemoteProcess;
1410 uint8_t* mLocalImagebase;
1411 PEHeaders mLocalExec;
1412 uint8_t* mRemoteImagebase;
1414 static HMODULE GetLocalExecModule() {
1415 #if defined(_MSC_VER)
1416 return reinterpret_cast<HMODULE>(&__ImageBase);
1417 #else
1418 return ::GetModuleHandleW(nullptr);
1419 #endif
1422 LauncherVoidResult EnsureRemoteImagebase() {
1423 if (!mRemoteImagebase) {
1424 LauncherResult<HMODULE> remoteImageBaseResult =
1425 GetProcessExeModule(mRemoteProcess);
1426 if (remoteImageBaseResult.isErr()) {
1427 return remoteImageBaseResult.propagateErr();
1430 mRemoteImagebase =
1431 reinterpret_cast<uint8_t*>(remoteImageBaseResult.unwrap());
1433 return Ok();
1436 template <typename T>
1437 T* LocalExecToRemoteExec(T* aLocalAddress) const {
1438 MOZ_ASSERT(mRemoteImagebase);
1439 MOZ_ASSERT(mLocalExec.IsWithinImage(aLocalAddress));
1441 if (!mRemoteImagebase || !mLocalExec.IsWithinImage(aLocalAddress)) {
1442 return aLocalAddress;
1445 uintptr_t offset = reinterpret_cast<uintptr_t>(aLocalAddress) -
1446 reinterpret_cast<uintptr_t>(mLocalImagebase);
1447 return reinterpret_cast<T*>(mRemoteImagebase + offset);
1450 public:
1451 explicit CrossExecTransferManager(HANDLE aRemoteProcess)
1452 : mRemoteProcess(aRemoteProcess),
1453 mLocalImagebase(
1454 PEHeaders::HModuleToBaseAddr<uint8_t*>(GetLocalExecModule())),
1455 mLocalExec(mLocalImagebase),
1456 mRemoteImagebase(nullptr) {}
1458 CrossExecTransferManager(HANDLE aRemoteProcess, HMODULE aLocalImagebase)
1459 : mRemoteProcess(aRemoteProcess),
1460 mLocalImagebase(
1461 PEHeaders::HModuleToBaseAddr<uint8_t*>(aLocalImagebase)),
1462 mLocalExec(mLocalImagebase),
1463 mRemoteImagebase(nullptr) {}
1465 explicit operator bool() const { return !!mLocalExec; }
1466 HANDLE RemoteProcess() const { return mRemoteProcess; }
1467 const PEHeaders& LocalPEHeaders() const { return mLocalExec; }
1469 AutoVirtualProtect Protect(void* aLocalAddress, size_t aLength,
1470 DWORD aProtFlags) {
1471 // If EnsureRemoteImagebase() fails, a subsequent operaion will fail.
1472 Unused << EnsureRemoteImagebase();
1473 return AutoVirtualProtect(LocalExecToRemoteExec(aLocalAddress), aLength,
1474 aProtFlags, mRemoteProcess);
1477 LauncherVoidResult Transfer(LPVOID aDestinationAddress,
1478 LPCVOID aBufferToWrite, SIZE_T aBufferSize) {
1479 LauncherVoidResult result = EnsureRemoteImagebase();
1480 if (result.isErr()) {
1481 return result.propagateErr();
1484 if (!::WriteProcessMemory(mRemoteProcess,
1485 LocalExecToRemoteExec(aDestinationAddress),
1486 aBufferToWrite, aBufferSize, nullptr)) {
1487 return LAUNCHER_ERROR_FROM_LAST();
1490 return Ok();
1494 #if !defined(MOZILLA_INTERNAL_API)
1496 inline LauncherResult<HMODULE> GetModuleHandleFromLeafName(
1497 const UNICODE_STRING& aTarget) {
1498 auto maybePeb = nt::GetProcessPebPtr(kCurrentProcess);
1499 if (maybePeb.isErr()) {
1500 return maybePeb.propagateErr();
1503 const PPEB peb = reinterpret_cast<PPEB>(maybePeb.unwrap());
1504 if (!peb->Ldr) {
1505 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
1508 auto firstItem = &peb->Ldr->InMemoryOrderModuleList;
1509 for (auto p = firstItem->Flink; p != firstItem; p = p->Flink) {
1510 const auto currentTableEntry =
1511 CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
1513 UNICODE_STRING leafName;
1514 nt::GetLeafName(&leafName, &currentTableEntry->FullDllName);
1516 if (::RtlCompareUnicodeString(&leafName, &aTarget, TRUE) == 0) {
1517 return reinterpret_cast<HMODULE>(currentTableEntry->DllBase);
1521 return LAUNCHER_ERROR_FROM_WIN32(ERROR_MOD_NOT_FOUND);
1524 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS SRWLock final {
1525 public:
1526 constexpr SRWLock() : mLock(SRWLOCK_INIT) {}
1528 void LockShared() { ::RtlAcquireSRWLockShared(&mLock); }
1530 void LockExclusive() { ::RtlAcquireSRWLockExclusive(&mLock); }
1532 void UnlockShared() { ::RtlReleaseSRWLockShared(&mLock); }
1534 void UnlockExclusive() { ::RtlReleaseSRWLockExclusive(&mLock); }
1536 SRWLock(const SRWLock&) = delete;
1537 SRWLock(SRWLock&&) = delete;
1538 SRWLock& operator=(const SRWLock&) = delete;
1539 SRWLock& operator=(SRWLock&&) = delete;
1541 SRWLOCK* operator&() { return &mLock; }
1543 private:
1544 SRWLOCK mLock;
1547 class MOZ_RAII AutoExclusiveLock final {
1548 public:
1549 explicit AutoExclusiveLock(SRWLock& aLock) : mLock(aLock) {
1550 aLock.LockExclusive();
1553 ~AutoExclusiveLock() { mLock.UnlockExclusive(); }
1555 AutoExclusiveLock(const AutoExclusiveLock&) = delete;
1556 AutoExclusiveLock(AutoExclusiveLock&&) = delete;
1557 AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete;
1558 AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete;
1560 private:
1561 SRWLock& mLock;
1564 class MOZ_RAII AutoSharedLock final {
1565 public:
1566 explicit AutoSharedLock(SRWLock& aLock) : mLock(aLock) { aLock.LockShared(); }
1568 ~AutoSharedLock() { mLock.UnlockShared(); }
1570 AutoSharedLock(const AutoSharedLock&) = delete;
1571 AutoSharedLock(AutoSharedLock&&) = delete;
1572 AutoSharedLock& operator=(const AutoSharedLock&) = delete;
1573 AutoSharedLock& operator=(AutoSharedLock&&) = delete;
1575 private:
1576 SRWLock& mLock;
1579 #endif // !defined(MOZILLA_INTERNAL_API)
1581 class RtlAllocPolicy {
1582 public:
1583 template <typename T>
1584 T* maybe_pod_malloc(size_t aNumElems) {
1585 if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1586 return nullptr;
1589 return static_cast<T*>(
1590 ::RtlAllocateHeap(RtlGetProcessHeap(), 0, aNumElems * sizeof(T)));
1593 template <typename T>
1594 T* maybe_pod_calloc(size_t aNumElems) {
1595 if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1596 return nullptr;
1599 return static_cast<T*>(::RtlAllocateHeap(
1600 RtlGetProcessHeap(), HEAP_ZERO_MEMORY, aNumElems * sizeof(T)));
1603 template <typename T>
1604 T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
1605 if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1606 return nullptr;
1609 return static_cast<T*>(::RtlReAllocateHeap(RtlGetProcessHeap(), 0, aPtr,
1610 aNewSize * sizeof(T)));
1613 template <typename T>
1614 T* pod_malloc(size_t aNumElems) {
1615 return maybe_pod_malloc<T>(aNumElems);
1618 template <typename T>
1619 T* pod_calloc(size_t aNumElems) {
1620 return maybe_pod_calloc<T>(aNumElems);
1623 template <typename T>
1624 T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
1625 return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
1628 template <typename T>
1629 void free_(T* aPtr, size_t aNumElems = 0) {
1630 ::RtlFreeHeap(RtlGetProcessHeap(), 0, aPtr);
1633 void reportAllocOverflow() const {}
1635 [[nodiscard]] bool checkSimulatedOOM() const { return true; }
1638 class AutoMappedView final {
1639 void* mView;
1641 void Unmap() {
1642 if (!mView) {
1643 return;
1646 #if defined(MOZILLA_INTERNAL_API)
1647 ::UnmapViewOfFile(mView);
1648 #else
1649 NTSTATUS status = ::NtUnmapViewOfSection(nt::kCurrentProcess, mView);
1650 if (!NT_SUCCESS(status)) {
1651 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1653 #endif
1654 mView = nullptr;
1657 public:
1658 explicit AutoMappedView(void* aView) : mView(aView) {}
1660 AutoMappedView(HANDLE aSection, ULONG aProtectionFlags) : mView(nullptr) {
1661 #if defined(MOZILLA_INTERNAL_API)
1662 mView = ::MapViewOfFile(aSection, aProtectionFlags, 0, 0, 0);
1663 #else
1664 SIZE_T viewSize = 0;
1665 NTSTATUS status = ::NtMapViewOfSection(aSection, nt::kCurrentProcess,
1666 &mView, 0, 0, nullptr, &viewSize,
1667 ViewUnmap, 0, aProtectionFlags);
1668 if (!NT_SUCCESS(status)) {
1669 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1671 #endif
1673 ~AutoMappedView() { Unmap(); }
1675 // Allow move & Disallow copy
1676 AutoMappedView(AutoMappedView&& aOther) : mView(aOther.mView) {
1677 aOther.mView = nullptr;
1679 AutoMappedView& operator=(AutoMappedView&& aOther) {
1680 if (this != &aOther) {
1681 Unmap();
1682 mView = aOther.mView;
1683 aOther.mView = nullptr;
1685 return *this;
1687 AutoMappedView(const AutoMappedView&) = delete;
1688 AutoMappedView& operator=(const AutoMappedView&) = delete;
1690 explicit operator bool() const { return !!mView; }
1691 template <typename T>
1692 T* as() {
1693 return reinterpret_cast<T*>(mView);
1696 void* release() {
1697 void* p = mView;
1698 mView = nullptr;
1699 return p;
1703 } // namespace nt
1704 } // namespace mozilla
1706 #endif // mozilla_NativeNt_h