Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / mozglue / misc / NativeNt.h
blobe28ad0f29141ddcb3fa27648a6cc1795c171b8ff
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 NTSTATUS NTAPI RtlSleepConditionVariableSRW(
97 PCONDITION_VARIABLE aConditionVariable, PSRWLOCK aSRWLock,
98 PLARGE_INTEGER aTimeOut, ULONG aFlags);
99 VOID NTAPI RtlWakeAllConditionVariable(PCONDITION_VARIABLE aConditionVariable);
101 ULONG NTAPI RtlNtStatusToDosError(NTSTATUS aStatus);
102 VOID NTAPI RtlSetLastWin32Error(DWORD aError);
103 DWORD NTAPI RtlGetLastWin32Error();
105 VOID NTAPI RtlRunOnceInitialize(PRTL_RUN_ONCE aRunOnce);
107 NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress,
108 PVOID aBuffer, SIZE_T aNumBytesToRead,
109 PSIZE_T aNumBytesRead);
111 NTSTATUS NTAPI LdrLoadDll(PWCHAR aDllPath, PULONG aFlags,
112 PUNICODE_STRING aDllName, PHANDLE aOutHandle);
114 typedef ULONG(NTAPI* PRTL_RUN_ONCE_INIT_FN)(PRTL_RUN_ONCE, PVOID, PVOID*);
115 NTSTATUS NTAPI RtlRunOnceExecuteOnce(PRTL_RUN_ONCE aRunOnce,
116 PRTL_RUN_ONCE_INIT_FN aInitFn,
117 PVOID aContext, PVOID* aParameter);
119 } // extern "C"
121 #endif // !defined(MOZILLA_INTERNAL_API)
123 extern "C" {
124 PVOID NTAPI RtlAllocateHeap(PVOID aHeapHandle, ULONG aFlags, SIZE_T aSize);
126 PVOID NTAPI RtlReAllocateHeap(PVOID aHeapHandle, ULONG aFlags, LPVOID aMem,
127 SIZE_T aNewSize);
129 BOOLEAN NTAPI RtlFreeHeap(PVOID aHeapHandle, ULONG aFlags, PVOID aHeapBase);
131 BOOLEAN NTAPI RtlQueryPerformanceCounter(LARGE_INTEGER* aPerfCount);
133 #define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE 1
134 #define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING 2
135 NTSTATUS NTAPI RtlDuplicateUnicodeString(ULONG aFlags, PCUNICODE_STRING aSrc,
136 PUNICODE_STRING aDest);
138 VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING aUnicodeString);
139 } // extern "C"
141 namespace mozilla {
142 namespace nt {
145 * This class encapsulates a UNICODE_STRING that owns its own buffer. The
146 * buffer is always NULL terminated, thus allowing us to cast to a wide C-string
147 * without requiring any mutation.
149 * We only allow creation of this owned buffer from outside XUL.
151 class AllocatedUnicodeString final {
152 public:
153 AllocatedUnicodeString() : mUnicodeString() {}
155 #if defined(MOZILLA_INTERNAL_API)
156 AllocatedUnicodeString(const AllocatedUnicodeString& aOther) = delete;
158 AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) =
159 delete;
160 #else
161 explicit AllocatedUnicodeString(PCUNICODE_STRING aSrc) {
162 if (!aSrc) {
163 mUnicodeString = {};
164 return;
167 Duplicate(aSrc);
170 explicit AllocatedUnicodeString(const char* aSrc) {
171 if (!aSrc) {
172 mUnicodeString = {};
173 return;
176 Duplicate(aSrc);
179 AllocatedUnicodeString(const AllocatedUnicodeString& aOther) {
180 Duplicate(&aOther.mUnicodeString);
183 AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) {
184 Clear();
185 Duplicate(&aOther.mUnicodeString);
186 return *this;
189 AllocatedUnicodeString& operator=(PCUNICODE_STRING aSrc) {
190 Clear();
191 Duplicate(aSrc);
192 return *this;
194 #endif // defined(MOZILLA_INTERNAL_API)
196 AllocatedUnicodeString(AllocatedUnicodeString&& aOther)
197 : mUnicodeString(aOther.mUnicodeString) {
198 aOther.mUnicodeString = {};
201 AllocatedUnicodeString& operator=(AllocatedUnicodeString&& aOther) {
202 Clear();
203 mUnicodeString = aOther.mUnicodeString;
204 aOther.mUnicodeString = {};
205 return *this;
208 ~AllocatedUnicodeString() { Clear(); }
210 bool IsEmpty() const {
211 return !mUnicodeString.Buffer || !mUnicodeString.Length;
214 operator PCUNICODE_STRING() const { return &mUnicodeString; }
216 operator const WCHAR*() const { return mUnicodeString.Buffer; }
218 USHORT CharLen() const { return mUnicodeString.Length / sizeof(WCHAR); }
220 #if defined(MOZILLA_INTERNAL_API)
221 nsDependentString AsString() const {
222 if (!mUnicodeString.Buffer) {
223 return nsDependentString();
226 // We can use nsDependentString here as we guaranteed null termination
227 // when we allocated the string.
228 return nsDependentString(mUnicodeString.Buffer, CharLen());
230 #endif // defined(MOZILLA_INTERNAL_API)
232 private:
233 #if !defined(MOZILLA_INTERNAL_API)
234 void Duplicate(PCUNICODE_STRING aSrc) {
235 MOZ_ASSERT(aSrc);
237 // We duplicate with null termination so that this string may be used
238 // as a wide C-string without any further manipulation.
239 NTSTATUS ntStatus = ::RtlDuplicateUnicodeString(
240 RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, aSrc, &mUnicodeString);
241 MOZ_ASSERT(NT_SUCCESS(ntStatus));
242 if (!NT_SUCCESS(ntStatus)) {
243 // Make sure that mUnicodeString does not contain bogus data
244 // (since not all callers zero it out before invoking)
245 mUnicodeString = {};
249 void Duplicate(const char* aSrc) {
250 MOZ_ASSERT(aSrc);
252 ANSI_STRING ansiStr;
253 RtlInitAnsiString(&ansiStr, aSrc);
254 NTSTATUS ntStatus =
255 ::RtlAnsiStringToUnicodeString(&mUnicodeString, &ansiStr, TRUE);
256 MOZ_ASSERT(NT_SUCCESS(ntStatus));
257 if (!NT_SUCCESS(ntStatus)) {
258 mUnicodeString = {};
261 #endif // !defined(MOZILLA_INTERNAL_API)
263 void Clear() {
264 if (!mUnicodeString.Buffer) {
265 return;
268 ::RtlFreeUnicodeString(&mUnicodeString);
269 mUnicodeString = {};
272 UNICODE_STRING mUnicodeString;
275 #if !defined(MOZILLA_INTERNAL_API)
277 struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME {
278 MemorySectionNameBuf() {
279 mSectionFileName.Length = 0;
280 mSectionFileName.MaximumLength = sizeof(mBuf);
281 mSectionFileName.Buffer = mBuf;
284 MemorySectionNameBuf(const MemorySectionNameBuf& aOther) { *this = aOther; }
286 MemorySectionNameBuf(MemorySectionNameBuf&& aOther) {
287 *this = std::move(aOther);
290 // We cannot use default copy here because mSectionFileName.Buffer needs to
291 // be updated to point to |this->mBuf|, not |aOther.mBuf|.
292 MemorySectionNameBuf& operator=(const MemorySectionNameBuf& aOther) {
293 mSectionFileName.Length = aOther.mSectionFileName.Length;
294 mSectionFileName.MaximumLength = sizeof(mBuf);
295 MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
296 mSectionFileName.Buffer = mBuf;
297 memcpy(mBuf, aOther.mBuf, aOther.mSectionFileName.Length);
298 return *this;
301 MemorySectionNameBuf& operator=(MemorySectionNameBuf&& aOther) {
302 mSectionFileName.Length = aOther.mSectionFileName.Length;
303 aOther.mSectionFileName.Length = 0;
304 mSectionFileName.MaximumLength = sizeof(mBuf);
305 MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
306 aOther.mSectionFileName.MaximumLength = sizeof(aOther.mBuf);
307 mSectionFileName.Buffer = mBuf;
308 memmove(mBuf, aOther.mBuf, mSectionFileName.Length);
309 return *this;
312 // Native NT paths, so we can't assume MAX_PATH. Use a larger buffer.
313 WCHAR mBuf[2 * MAX_PATH];
315 bool IsEmpty() const {
316 return !mSectionFileName.Buffer || !mSectionFileName.Length;
319 operator PCUNICODE_STRING() const { return &mSectionFileName; }
322 class MemorySectionNameOnHeap {
323 UniquePtr<uint8_t[]> mBuffer;
325 MemorySectionNameOnHeap() = default;
326 explicit MemorySectionNameOnHeap(size_t aBufferLen)
327 : mBuffer(MakeUnique<uint8_t[]>(aBufferLen)) {}
329 public:
330 static MemorySectionNameOnHeap GetBackingFilePath(HANDLE aProcess,
331 void* aSectionAddr) {
332 SIZE_T bufferLen = MAX_PATH * 2;
333 do {
334 MemorySectionNameOnHeap sectionName(bufferLen);
336 SIZE_T requiredBytes;
337 NTSTATUS ntStatus = ::NtQueryVirtualMemory(
338 aProcess, aSectionAddr, MemorySectionName, sectionName.mBuffer.get(),
339 bufferLen, &requiredBytes);
340 if (NT_SUCCESS(ntStatus)) {
341 return sectionName;
344 if (ntStatus != STATUS_INFO_LENGTH_MISMATCH ||
345 bufferLen >= requiredBytes) {
346 break;
349 bufferLen = requiredBytes;
350 } while (1);
352 return MemorySectionNameOnHeap();
355 // Allow move & Disallow copy
356 MemorySectionNameOnHeap(MemorySectionNameOnHeap&&) = default;
357 MemorySectionNameOnHeap& operator=(MemorySectionNameOnHeap&&) = default;
358 MemorySectionNameOnHeap(const MemorySectionNameOnHeap&) = delete;
359 MemorySectionNameOnHeap& operator=(const MemorySectionNameOnHeap&) = delete;
361 PCUNICODE_STRING AsUnicodeString() const {
362 return reinterpret_cast<PCUNICODE_STRING>(mBuffer.get());
366 inline bool FindCharInUnicodeString(const UNICODE_STRING& aStr, WCHAR aChar,
367 uint16_t& aPos, uint16_t aStartIndex = 0) {
368 const uint16_t aMaxIndex = aStr.Length / sizeof(WCHAR);
370 for (uint16_t curIndex = aStartIndex; curIndex < aMaxIndex; ++curIndex) {
371 if (aStr.Buffer[curIndex] == aChar) {
372 aPos = curIndex;
373 return true;
377 return false;
380 inline bool IsHexDigit(WCHAR aChar) {
381 return (aChar >= L'0' && aChar <= L'9') || (aChar >= L'A' && aChar <= L'F') ||
382 (aChar >= L'a' && aChar <= L'f');
385 inline bool MatchUnicodeString(const UNICODE_STRING& aStr,
386 bool (*aPredicate)(WCHAR)) {
387 WCHAR* cur = aStr.Buffer;
388 WCHAR* end = &aStr.Buffer[aStr.Length / sizeof(WCHAR)];
389 while (cur < end) {
390 if (!aPredicate(*cur)) {
391 return false;
394 ++cur;
397 return true;
400 inline bool Contains12DigitHexString(const UNICODE_STRING& aLeafName) {
401 // Quick check: If the string is too short, don't bother
402 // (We need at least 12 hex digits, one char for '.', and 3 for extension)
403 const USHORT kMinLen = (12 + 1 + 3) * sizeof(wchar_t);
404 if (aLeafName.Length < kMinLen) {
405 return false;
408 uint16_t start, end;
409 if (!FindCharInUnicodeString(aLeafName, L'.', start)) {
410 return false;
413 ++start;
414 if (!FindCharInUnicodeString(aLeafName, L'.', end, start)) {
415 return false;
418 if (end - start != 12) {
419 return false;
422 UNICODE_STRING test;
423 test.Buffer = &aLeafName.Buffer[start];
424 test.Length = (end - start) * sizeof(WCHAR);
425 test.MaximumLength = test.Length;
427 return MatchUnicodeString(test, &IsHexDigit);
430 inline bool IsFileNameAtLeast16HexDigits(const UNICODE_STRING& aLeafName) {
431 // Quick check: If the string is too short, don't bother
432 // (We need 16 hex digits, one char for '.', and 3 for extension)
433 const USHORT kMinLen = (16 + 1 + 3) * sizeof(wchar_t);
434 if (aLeafName.Length < kMinLen) {
435 return false;
438 uint16_t dotIndex;
439 if (!FindCharInUnicodeString(aLeafName, L'.', dotIndex)) {
440 return false;
443 if (dotIndex < 16) {
444 return false;
447 UNICODE_STRING test;
448 test.Buffer = aLeafName.Buffer;
449 test.Length = dotIndex * sizeof(WCHAR);
450 test.MaximumLength = aLeafName.MaximumLength;
452 return MatchUnicodeString(test, &IsHexDigit);
455 inline void GetLeafName(PUNICODE_STRING aDestString,
456 PCUNICODE_STRING aSrcString) {
457 WCHAR* buf = aSrcString->Buffer;
458 WCHAR* end = &aSrcString->Buffer[(aSrcString->Length / sizeof(WCHAR)) - 1];
459 WCHAR* cur = end;
460 while (cur >= buf) {
461 if (*cur == L'\\') {
462 break;
465 --cur;
468 // At this point, either cur points to the final backslash, or it points to
469 // buf - 1. Either way, we're interested in cur + 1 as the desired buffer.
470 aDestString->Buffer = cur + 1;
471 aDestString->Length = (end - aDestString->Buffer + 1) * sizeof(WCHAR);
472 aDestString->MaximumLength = aDestString->Length;
475 #endif // !defined(MOZILLA_INTERNAL_API)
477 #if defined(MOZILLA_INTERNAL_API)
479 inline const nsDependentSubstring GetLeafName(const nsAString& aString) {
480 auto it = aString.EndReading();
481 size_t pos = aString.Length();
482 while (it > aString.BeginReading()) {
483 if (*(it - 1) == u'\\') {
484 return Substring(aString, pos);
487 MOZ_ASSERT(pos > 0);
488 --pos;
489 --it;
492 return Substring(aString, 0); // No backslash in the string
495 #endif // defined(MOZILLA_INTERNAL_API)
497 inline char EnsureLowerCaseASCII(char aChar) {
498 if (aChar >= 'A' && aChar <= 'Z') {
499 aChar -= 'A' - 'a';
502 return aChar;
505 inline int StricmpASCII(const char* aLeft, const char* aRight) {
506 char curLeft, curRight;
508 do {
509 curLeft = EnsureLowerCaseASCII(*(aLeft++));
510 curRight = EnsureLowerCaseASCII(*(aRight++));
511 } while (curLeft && curLeft == curRight);
513 return curLeft - curRight;
516 inline int StrcmpASCII(const char* aLeft, const char* aRight) {
517 char curLeft, curRight;
519 do {
520 curLeft = *(aLeft++);
521 curRight = *(aRight++);
522 } while (curLeft && curLeft == curRight);
524 return curLeft - curRight;
527 inline size_t StrlenASCII(const char* aStr) {
528 size_t len = 0;
530 while (*(aStr++)) {
531 ++len;
534 return len;
537 struct CodeViewRecord70 {
538 uint32_t signature;
539 GUID pdbSignature;
540 uint32_t pdbAge;
541 // A UTF-8 string, according to
542 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/locator.cpp#L785
543 char pdbFileName[1];
546 class MOZ_RAII PEHeaders final {
548 * This structure is documented on MSDN as VS_VERSIONINFO, but is not present
549 * in SDK headers because it cannot be specified as a C struct. The following
550 * structure contains the fixed-length fields at the beginning of
551 * VS_VERSIONINFO.
553 struct VS_VERSIONINFO_HEADER {
554 WORD wLength;
555 WORD wValueLength;
556 WORD wType;
557 WCHAR szKey[16]; // std::size(L"VS_VERSION_INFO")
558 // Additional data goes here, aligned on a 4-byte boundary
561 public:
562 // The lowest two bits of an HMODULE are used as flags. Stripping those bits
563 // from the HMODULE yields the base address of the binary's memory mapping.
564 // (See LoadLibraryEx docs on MSDN)
565 template <typename T>
566 static T HModuleToBaseAddr(HMODULE aModule) {
567 return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(aModule) &
568 ~uintptr_t(3));
571 explicit PEHeaders(void* aBaseAddress)
572 : PEHeaders(reinterpret_cast<PIMAGE_DOS_HEADER>(aBaseAddress)) {}
574 explicit PEHeaders(HMODULE aModule)
575 : PEHeaders(HModuleToBaseAddr<PIMAGE_DOS_HEADER>(aModule)) {}
577 explicit PEHeaders(PIMAGE_DOS_HEADER aMzHeader)
578 : mMzHeader(aMzHeader),
579 mPeHeader(nullptr),
580 mImageLimit(nullptr),
581 mIsImportDirectoryTampered(false) {
582 if (!mMzHeader || mMzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
583 return;
586 mPeHeader = RVAToPtrUnchecked<PIMAGE_NT_HEADERS>(mMzHeader->e_lfanew);
587 if (!mPeHeader || mPeHeader->Signature != IMAGE_NT_SIGNATURE) {
588 return;
591 if (mPeHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
592 return;
595 DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage;
596 // This is a coarse-grained check to ensure that the image size is
597 // reasonable. It we aren't big enough to contain headers, we have a
598 // problem!
599 if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) {
600 return;
603 mImageLimit = RVAToPtrUnchecked<void*>(imageSize - 1UL);
605 PIMAGE_DATA_DIRECTORY importDirEntry =
606 GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_IMPORT);
607 if (!importDirEntry) {
608 return;
611 mIsImportDirectoryTampered = (importDirEntry->VirtualAddress >= imageSize);
614 explicit operator bool() const { return !!mImageLimit; }
617 * This overload computes absolute virtual addresses relative to the base
618 * address of the binary.
620 template <typename T, typename R>
621 T RVAToPtr(R aRva) const {
622 return RVAToPtr<T>(mMzHeader, aRva);
626 * This overload computes a result by adding aRva to aBase, but also ensures
627 * that the resulting pointer falls within the bounds of this binary's memory
628 * mapping.
630 template <typename T, typename R>
631 T RVAToPtr(void* aBase, R aRva) const {
632 if (!mImageLimit) {
633 return nullptr;
636 char* absAddress = reinterpret_cast<char*>(aBase) + aRva;
637 if (absAddress < reinterpret_cast<char*>(mMzHeader) ||
638 absAddress > reinterpret_cast<char*>(mImageLimit)) {
639 return nullptr;
642 return reinterpret_cast<T>(absAddress);
645 Maybe<Range<const uint8_t>> GetBounds() const {
646 if (!mImageLimit) {
647 return Nothing();
650 auto base = reinterpret_cast<const uint8_t*>(mMzHeader);
651 DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage;
652 return Some(Range(base, imageSize));
655 DWORD GetFileCharacteristics() const {
656 return mPeHeader ? mPeHeader->FileHeader.Characteristics : 0;
659 bool IsWithinImage(const void* aAddress) const {
660 uintptr_t addr = reinterpret_cast<uintptr_t>(aAddress);
661 uintptr_t imageBase = reinterpret_cast<uintptr_t>(mMzHeader);
662 uintptr_t imageLimit = reinterpret_cast<uintptr_t>(mImageLimit);
663 return addr >= imageBase && addr <= imageLimit;
666 PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() const {
667 // If the import directory is already tampered, we skip bounds check
668 // because it could be located outside the mapped image.
669 return mIsImportDirectoryTampered
670 ? GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR,
671 BoundsCheckPolicy::Skip>(
672 IMAGE_DIRECTORY_ENTRY_IMPORT)
673 : GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR>(
674 IMAGE_DIRECTORY_ENTRY_IMPORT);
677 PIMAGE_RESOURCE_DIRECTORY GetResourceTable() const {
678 return GetImageDirectoryEntry<PIMAGE_RESOURCE_DIRECTORY>(
679 IMAGE_DIRECTORY_ENTRY_RESOURCE);
682 PIMAGE_DATA_DIRECTORY GetImageDirectoryEntryPtr(
683 const uint32_t aDirectoryIndex, uint32_t* aOutRva = nullptr) const {
684 if (aOutRva) {
685 *aOutRva = 0;
688 IMAGE_OPTIONAL_HEADER& optionalHeader = mPeHeader->OptionalHeader;
690 const uint32_t maxIndex = std::min(optionalHeader.NumberOfRvaAndSizes,
691 DWORD(IMAGE_NUMBEROF_DIRECTORY_ENTRIES));
692 if (aDirectoryIndex >= maxIndex) {
693 return nullptr;
696 PIMAGE_DATA_DIRECTORY dirEntry =
697 &optionalHeader.DataDirectory[aDirectoryIndex];
698 if (aOutRva) {
699 *aOutRva = reinterpret_cast<char*>(dirEntry) -
700 reinterpret_cast<char*>(mMzHeader);
701 MOZ_ASSERT(*aOutRva);
704 return dirEntry;
707 bool GetVersionInfo(uint64_t& aOutVersion) const {
708 // RT_VERSION == 16
709 // Version resources require an id of 1
710 auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1);
711 if (!root) {
712 return false;
715 VS_FIXEDFILEINFO* fixedInfo = GetFixedFileInfo(root);
716 if (!fixedInfo) {
717 return false;
720 aOutVersion = ((static_cast<uint64_t>(fixedInfo->dwFileVersionMS) << 32) |
721 static_cast<uint64_t>(fixedInfo->dwFileVersionLS));
722 return true;
725 bool GetTimeStamp(DWORD& aResult) const {
726 if (!(*this)) {
727 return false;
730 aResult = mPeHeader->FileHeader.TimeDateStamp;
731 return true;
734 bool GetImageSize(DWORD& aResult) const {
735 if (!(*this)) {
736 return false;
739 aResult = mPeHeader->OptionalHeader.SizeOfImage;
740 return true;
743 bool GetCheckSum(DWORD& aResult) const {
744 if (!(*this)) {
745 return false;
748 aResult = mPeHeader->OptionalHeader.CheckSum;
749 return true;
752 PIMAGE_IMPORT_DESCRIPTOR
753 GetImportDescriptor(const char* aModuleNameASCII) const {
754 for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
755 IsValid(curImpDesc); ++curImpDesc) {
756 auto curName = mIsImportDirectoryTampered
757 ? RVAToPtrUnchecked<const char*>(curImpDesc->Name)
758 : RVAToPtr<const char*>(curImpDesc->Name);
759 if (!curName) {
760 return nullptr;
763 if (StricmpASCII(aModuleNameASCII, curName)) {
764 continue;
767 // curImpDesc now points to the IAT for the module we're interested in
768 return curImpDesc;
771 return nullptr;
774 template <typename CallbackT>
775 void EnumImportChunks(const CallbackT& aCallback) const {
776 for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
777 IsValid(curImpDesc); ++curImpDesc) {
778 auto curName = mIsImportDirectoryTampered
779 ? RVAToPtrUnchecked<const char*>(curImpDesc->Name)
780 : RVAToPtr<const char*>(curImpDesc->Name);
781 if (!curName) {
782 continue;
785 aCallback(curName);
790 * If |aBoundaries| is given, this method checks whether each IAT entry is
791 * within the given range, and if any entry is out of the range, we return
792 * Nothing().
794 Maybe<Span<IMAGE_THUNK_DATA>> GetIATThunksForModule(
795 const char* aModuleNameASCII,
796 const Range<const uint8_t>* aBoundaries = nullptr) const {
797 PIMAGE_IMPORT_DESCRIPTOR impDesc = GetImportDescriptor(aModuleNameASCII);
798 if (!impDesc) {
799 return Nothing();
802 auto firstIatThunk =
803 this->template RVAToPtr<PIMAGE_THUNK_DATA>(impDesc->FirstThunk);
804 if (!firstIatThunk) {
805 return Nothing();
808 // Find the length by iterating through the table until we find a null entry
809 PIMAGE_THUNK_DATA curIatThunk = firstIatThunk;
810 while (IsValid(curIatThunk)) {
811 if (aBoundaries) {
812 auto iatEntry =
813 reinterpret_cast<const uint8_t*>(curIatThunk->u1.Function);
814 if (iatEntry < aBoundaries->begin().get() ||
815 iatEntry >= aBoundaries->end().get()) {
816 return Nothing();
820 ++curIatThunk;
823 return Some(Span(firstIatThunk, curIatThunk));
827 * Resources are stored in a three-level tree. To locate a particular entry,
828 * you must supply a resource type, the resource id, and then the language id.
829 * If aLangId == 0, we just resolve the first entry regardless of language.
831 template <typename T>
832 T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) const {
833 PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable();
834 if (!topLevel) {
835 return nullptr;
838 PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry =
839 FindResourceEntry(topLevel, aType);
840 if (!typeEntry || !typeEntry->DataIsDirectory) {
841 return nullptr;
844 auto idDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
845 topLevel, typeEntry->OffsetToDirectory);
846 PIMAGE_RESOURCE_DIRECTORY_ENTRY idEntry = FindResourceEntry(idDir, aResId);
847 if (!idEntry || !idEntry->DataIsDirectory) {
848 return nullptr;
851 auto langDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
852 topLevel, idEntry->OffsetToDirectory);
853 PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry;
854 if (aLangId) {
855 langEntry = FindResourceEntry(langDir, aLangId);
856 } else {
857 langEntry = FindFirstResourceEntry(langDir);
860 if (!langEntry || langEntry->DataIsDirectory) {
861 return nullptr;
864 auto dataEntry =
865 RVAToPtr<PIMAGE_RESOURCE_DATA_ENTRY>(topLevel, langEntry->OffsetToData);
866 return dataEntry ? RVAToPtr<T>(dataEntry->OffsetToData) : nullptr;
869 template <size_t N>
870 Maybe<Span<const uint8_t>> FindSection(const char (&aSecName)[N],
871 DWORD aCharacteristicsMask) const {
872 static_assert((N - 1) <= IMAGE_SIZEOF_SHORT_NAME,
873 "Section names must be at most 8 characters excluding null "
874 "terminator");
876 if (!(*this)) {
877 return Nothing();
880 Span<IMAGE_SECTION_HEADER> sectionTable = GetSectionTable();
881 for (auto&& sectionHeader : sectionTable) {
882 if (strncmp(reinterpret_cast<const char*>(sectionHeader.Name), aSecName,
883 IMAGE_SIZEOF_SHORT_NAME)) {
884 continue;
887 if (!(sectionHeader.Characteristics & aCharacteristicsMask)) {
888 // We found the section but it does not have the expected
889 // characteristics
890 return Nothing();
893 DWORD rva = sectionHeader.VirtualAddress;
894 if (!rva) {
895 return Nothing();
898 DWORD size = sectionHeader.Misc.VirtualSize;
899 if (!size) {
900 return Nothing();
903 auto base = RVAToPtr<const uint8_t*>(rva);
904 return Some(Span(base, size));
907 return Nothing();
910 // There may be other code sections in the binary besides .text
911 Maybe<Span<const uint8_t>> GetTextSectionInfo() const {
912 return FindSection(".text", IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE |
913 IMAGE_SCN_MEM_READ);
916 // There may be other data sections in the binary besides .data
917 Maybe<Span<const uint8_t>> GetDataSectionInfo() const {
918 return FindSection(".data", IMAGE_SCN_CNT_INITIALIZED_DATA |
919 IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE);
922 static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc) {
923 return aImpDesc && aImpDesc->OriginalFirstThunk != 0;
926 static bool IsValid(PIMAGE_THUNK_DATA aImgThunk) {
927 return aImgThunk && aImgThunk->u1.Ordinal != 0;
930 bool IsImportDirectoryTampered() const { return mIsImportDirectoryTampered; }
932 FARPROC GetEntryPoint() const {
933 // Use the unchecked version because the entrypoint may be tampered.
934 return RVAToPtrUnchecked<FARPROC>(
935 mPeHeader->OptionalHeader.AddressOfEntryPoint);
938 const CodeViewRecord70* GetPdbInfo() const {
939 PIMAGE_DEBUG_DIRECTORY debugDirectory =
940 GetImageDirectoryEntry<PIMAGE_DEBUG_DIRECTORY>(
941 IMAGE_DIRECTORY_ENTRY_DEBUG);
942 if (!debugDirectory) {
943 return nullptr;
946 const CodeViewRecord70* debugInfo =
947 RVAToPtr<CodeViewRecord70*>(debugDirectory->AddressOfRawData);
948 return (debugInfo && debugInfo->signature == 'SDSR') ? debugInfo : nullptr;
951 private:
952 enum class BoundsCheckPolicy { Default, Skip };
954 template <typename T, BoundsCheckPolicy Policy = BoundsCheckPolicy::Default>
955 T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) const {
956 PIMAGE_DATA_DIRECTORY dirEntry = GetImageDirectoryEntryPtr(aDirectoryIndex);
957 if (!dirEntry) {
958 return nullptr;
961 return Policy == BoundsCheckPolicy::Skip
962 ? RVAToPtrUnchecked<T>(dirEntry->VirtualAddress)
963 : RVAToPtr<T>(dirEntry->VirtualAddress);
966 // This private variant does not have bounds checks, because we need to be
967 // able to resolve the bounds themselves.
968 template <typename T, typename R>
969 T RVAToPtrUnchecked(R aRva) const {
970 return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
973 Span<IMAGE_SECTION_HEADER> GetSectionTable() const {
974 MOZ_ASSERT(*this);
975 auto base = RVAToPtr<PIMAGE_SECTION_HEADER>(
976 &mPeHeader->OptionalHeader, mPeHeader->FileHeader.SizeOfOptionalHeader);
977 // The Windows loader has an internal limit of 96 sections (per PE spec)
978 auto numSections =
979 std::min(mPeHeader->FileHeader.NumberOfSections, WORD(96));
980 return Span{base, numSections};
983 PIMAGE_RESOURCE_DIRECTORY_ENTRY
984 FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) const {
985 if (!aCurLevel) {
986 return nullptr;
989 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
990 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
991 // searches by ID, we need to skip past any named entries before iterating.
992 auto dirEnt =
993 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) +
994 aCurLevel->NumberOfNamedEntries;
995 if (!(IsWithinImage(dirEnt) &&
996 IsWithinImage(&dirEnt[aCurLevel->NumberOfIdEntries - 1].Id))) {
997 return nullptr;
1000 for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) {
1001 if (dirEnt[i].Id == aId) {
1002 return &dirEnt[i];
1006 return nullptr;
1009 PIMAGE_RESOURCE_DIRECTORY_ENTRY
1010 FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) const {
1011 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
1012 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first
1013 // entry, regardless of whether it is indexed by name or by id.
1014 auto dirEnt =
1015 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1);
1016 WORD numEntries =
1017 aCurLevel->NumberOfNamedEntries + aCurLevel->NumberOfIdEntries;
1018 if (!numEntries) {
1019 return nullptr;
1022 return dirEnt;
1025 VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) const {
1026 WORD length = aVerInfo->wLength;
1027 if (length < sizeof(VS_VERSIONINFO_HEADER)) {
1028 return nullptr;
1031 const wchar_t kVersionInfoKey[] = L"VS_VERSION_INFO";
1032 if (::RtlCompareMemory(aVerInfo->szKey, kVersionInfoKey,
1033 std::size(kVersionInfoKey)) !=
1034 std::size(kVersionInfoKey)) {
1035 return nullptr;
1038 if (aVerInfo->wValueLength != sizeof(VS_FIXEDFILEINFO)) {
1039 // Fixed file info does not exist
1040 return nullptr;
1043 WORD offset = sizeof(VS_VERSIONINFO_HEADER);
1045 uintptr_t base = reinterpret_cast<uintptr_t>(aVerInfo);
1046 // Align up to 4-byte boundary
1047 #pragma warning(suppress : 4146)
1048 offset += (-(base + offset) & 3);
1050 if (offset >= length) {
1051 return nullptr;
1054 auto result = reinterpret_cast<VS_FIXEDFILEINFO*>(base + offset);
1055 if (result->dwSignature != 0xFEEF04BD) {
1056 return nullptr;
1059 return result;
1062 private:
1063 PIMAGE_DOS_HEADER mMzHeader;
1064 PIMAGE_NT_HEADERS mPeHeader;
1065 void* mImageLimit;
1066 bool mIsImportDirectoryTampered;
1069 // This class represents an export section of a local/remote process.
1070 template <typename MMPolicy>
1071 class MOZ_RAII PEExportSection {
1072 const MMPolicy& mMMPolicy;
1073 uintptr_t mImageBase;
1074 DWORD mOrdinalBase;
1075 DWORD mRvaDirStart;
1076 DWORD mRvaDirEnd;
1077 mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportAddressTable;
1078 mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportNameTable;
1079 mozilla::interceptor::TargetObjectArray<MMPolicy, WORD> mExportOrdinalTable;
1081 explicit PEExportSection(const MMPolicy& aMMPolicy)
1082 : mMMPolicy(aMMPolicy),
1083 mImageBase(0),
1084 mOrdinalBase(0),
1085 mRvaDirStart(0),
1086 mRvaDirEnd(0),
1087 mExportAddressTable(mMMPolicy),
1088 mExportNameTable(mMMPolicy),
1089 mExportOrdinalTable(mMMPolicy) {}
1091 PEExportSection(const MMPolicy& aMMPolicy, uintptr_t aImageBase,
1092 DWORD aRvaDirStart, DWORD aRvaDirEnd,
1093 const IMAGE_EXPORT_DIRECTORY& exportDir)
1094 : mMMPolicy(aMMPolicy),
1095 mImageBase(aImageBase),
1096 mOrdinalBase(exportDir.Base),
1097 mRvaDirStart(aRvaDirStart),
1098 mRvaDirEnd(aRvaDirEnd),
1099 mExportAddressTable(mMMPolicy,
1100 mImageBase + exportDir.AddressOfFunctions,
1101 exportDir.NumberOfFunctions),
1102 mExportNameTable(mMMPolicy, mImageBase + exportDir.AddressOfNames,
1103 exportDir.NumberOfNames),
1104 mExportOrdinalTable(mMMPolicy,
1105 mImageBase + exportDir.AddressOfNameOrdinals,
1106 exportDir.NumberOfNames) {}
1108 static const PEExportSection Get(uintptr_t aImageBase,
1109 const MMPolicy& aMMPolicy) {
1110 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_DOS_HEADER> mzHeader(
1111 aMMPolicy, aImageBase);
1112 if (!mzHeader || mzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
1113 return PEExportSection(aMMPolicy);
1116 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_NT_HEADERS> peHeader(
1117 aMMPolicy, aImageBase + mzHeader->e_lfanew);
1118 if (!peHeader || peHeader->Signature != IMAGE_NT_SIGNATURE) {
1119 return PEExportSection(aMMPolicy);
1122 if (peHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
1123 return PEExportSection(aMMPolicy);
1126 const IMAGE_OPTIONAL_HEADER& optionalHeader = peHeader->OptionalHeader;
1128 DWORD imageSize = optionalHeader.SizeOfImage;
1129 // This is a coarse-grained check to ensure that the image size is
1130 // reasonable. It we aren't big enough to contain headers, we have a
1131 // problem!
1132 if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) {
1133 return PEExportSection(aMMPolicy);
1136 if (optionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) {
1137 return PEExportSection(aMMPolicy);
1140 const IMAGE_DATA_DIRECTORY& exportDirectoryEntry =
1141 optionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
1142 if (!exportDirectoryEntry.VirtualAddress || !exportDirectoryEntry.Size) {
1143 return PEExportSection(aMMPolicy);
1146 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_EXPORT_DIRECTORY>
1147 exportDirectory(aMMPolicy,
1148 aImageBase + exportDirectoryEntry.VirtualAddress);
1149 if (!exportDirectory || !exportDirectory->NumberOfFunctions) {
1150 return PEExportSection(aMMPolicy);
1153 return PEExportSection(
1154 aMMPolicy, aImageBase, exportDirectoryEntry.VirtualAddress,
1155 exportDirectoryEntry.VirtualAddress + exportDirectoryEntry.Size,
1156 *exportDirectory.GetLocalBase());
1159 FARPROC GetProcAddressByOrdinal(WORD aOrdinal) const {
1160 if (aOrdinal < mOrdinalBase) {
1161 return nullptr;
1164 auto rvaToFunction = mExportAddressTable[aOrdinal - mOrdinalBase];
1165 if (!rvaToFunction) {
1166 return nullptr;
1168 return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
1171 public:
1172 static const PEExportSection Get(HMODULE aModule, const MMPolicy& aMMPolicy) {
1173 return Get(PEHeaders::HModuleToBaseAddr<uintptr_t>(aModule), aMMPolicy);
1176 explicit operator bool() const {
1177 // Because PEExportSection doesn't use MMPolicy::Reserve(), a boolified
1178 // mMMPolicy is expected to be false. We don't check mMMPolicy here.
1179 return mImageBase && mRvaDirStart && mRvaDirEnd && mExportAddressTable &&
1180 mExportNameTable && mExportOrdinalTable;
1183 template <typename T>
1184 T RVAToPtr(uint32_t aRva) const {
1185 return reinterpret_cast<T>(mImageBase + aRva);
1188 PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const {
1189 if (!*this) {
1190 return nullptr;
1193 return RVAToPtr<PIMAGE_EXPORT_DIRECTORY>(mRvaDirStart);
1197 * This functions searches the export table for a given string as
1198 * GetProcAddress does, but this returns a matched entry of the Export
1199 * Address Table i.e. a pointer to an RVA of a matched function instead
1200 * of a function address. If the entry is forwarded, this function
1201 * returns nullptr.
1203 const DWORD* FindExportAddressTableEntry(
1204 const char* aFunctionNameASCII) const {
1205 if (!*this || !aFunctionNameASCII) {
1206 return nullptr;
1209 struct NameTableComparator {
1210 NameTableComparator(const PEExportSection<MMPolicy>& aExportSection,
1211 const char* aTarget)
1212 : mExportSection(aExportSection),
1213 mTargetName(aTarget),
1214 mTargetNamelength(StrlenASCII(aTarget)) {}
1216 int operator()(DWORD aRVAToString) const {
1217 mozilla::interceptor::TargetObjectArray<MMPolicy, char> itemString(
1218 mExportSection.mMMPolicy, mExportSection.mImageBase + aRVAToString,
1219 mTargetNamelength + 1);
1220 return StrcmpASCII(mTargetName, itemString[0]);
1223 const PEExportSection<MMPolicy>& mExportSection;
1224 const char* mTargetName;
1225 size_t mTargetNamelength;
1228 const NameTableComparator comp(*this, aFunctionNameASCII);
1230 size_t match;
1231 if (!mExportNameTable.BinarySearchIf(comp, &match)) {
1232 return nullptr;
1235 const WORD* index = mExportOrdinalTable[match];
1236 if (!index) {
1237 return nullptr;
1240 const DWORD* rvaToFunction = mExportAddressTable[*index];
1241 if (!rvaToFunction) {
1242 return nullptr;
1245 if (*rvaToFunction >= mRvaDirStart && *rvaToFunction < mRvaDirEnd) {
1246 // If an entry points to an address within the export section, the
1247 // field is a forwarder RVA. We return nullptr because the entry is
1248 // not a function address but a null-terminated string used for export
1249 // forwarding.
1250 return nullptr;
1253 return rvaToFunction;
1257 * This functions behaves the same as the native ::GetProcAddress except
1258 * the following cases:
1259 * - Returns nullptr if a target entry is forwarded to another dll.
1261 FARPROC GetProcAddress(const char* aFunctionNameASCII) const {
1262 uintptr_t maybeOdrinal = reinterpret_cast<uintptr_t>(aFunctionNameASCII);
1263 // When the high-order word of |aFunctionNameASCII| is zero, it's not
1264 // a string but an ordinal value.
1265 if (maybeOdrinal < 0x10000) {
1266 return GetProcAddressByOrdinal(static_cast<WORD>(maybeOdrinal));
1269 auto rvaToFunction = FindExportAddressTableEntry(aFunctionNameASCII);
1270 if (!rvaToFunction) {
1271 return nullptr;
1273 return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
1277 inline HANDLE RtlGetProcessHeap() {
1278 PTEB teb = ::NtCurrentTeb();
1279 PPEB peb = teb->ProcessEnvironmentBlock;
1280 return peb->Reserved4[1];
1283 inline PVOID RtlGetThreadLocalStoragePointer() {
1284 return ::NtCurrentTeb()->Reserved1[11];
1287 inline void RtlSetThreadLocalStoragePointerForTestingOnly(PVOID aNewValue) {
1288 ::NtCurrentTeb()->Reserved1[11] = aNewValue;
1291 inline DWORD RtlGetCurrentThreadId() {
1292 PTEB teb = ::NtCurrentTeb();
1293 CLIENT_ID* cid = reinterpret_cast<CLIENT_ID*>(&teb->Reserved1[8]);
1294 return static_cast<DWORD>(reinterpret_cast<uintptr_t>(cid->UniqueThread) &
1295 0xFFFFFFFFUL);
1298 inline PVOID RtlGetThreadStackBase() {
1299 return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackBase;
1302 inline PVOID RtlGetThreadStackLimit() {
1303 return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackLimit;
1306 const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
1308 inline LauncherResult<DWORD> GetParentProcessId() {
1309 struct PROCESS_BASIC_INFORMATION {
1310 NTSTATUS ExitStatus;
1311 PPEB PebBaseAddress;
1312 ULONG_PTR AffinityMask;
1313 LONG BasePriority;
1314 ULONG_PTR UniqueProcessId;
1315 ULONG_PTR InheritedFromUniqueProcessId;
1318 ULONG returnLength;
1319 PROCESS_BASIC_INFORMATION pbi = {};
1320 NTSTATUS status =
1321 ::NtQueryInformationProcess(kCurrentProcess, ProcessBasicInformation,
1322 &pbi, sizeof(pbi), &returnLength);
1323 if (!NT_SUCCESS(status)) {
1324 return LAUNCHER_ERROR_FROM_NTSTATUS(status);
1327 return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF);
1330 inline SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
1331 PMEMORY_BASIC_INFORMATION aMemInfo,
1332 SIZE_T aMemInfoLen) {
1333 #if defined(MOZILLA_INTERNAL_API)
1334 return ::VirtualQueryEx(aProcess, aAddress, aMemInfo, aMemInfoLen);
1335 #else
1336 SIZE_T returnedLength;
1337 NTSTATUS status = ::NtQueryVirtualMemory(
1338 aProcess, const_cast<PVOID>(aAddress), MemoryBasicInformation, aMemInfo,
1339 aMemInfoLen, &returnedLength);
1340 if (!NT_SUCCESS(status)) {
1341 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1342 returnedLength = 0;
1344 return returnedLength;
1345 #endif // defined(MOZILLA_INTERNAL_API)
1348 inline SIZE_T WINAPI VirtualQuery(LPCVOID aAddress,
1349 PMEMORY_BASIC_INFORMATION aMemInfo,
1350 SIZE_T aMemInfoLen) {
1351 return nt::VirtualQueryEx(kCurrentProcess, aAddress, aMemInfo, aMemInfoLen);
1354 struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY {
1355 DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}
1357 MOZ_IMPLICIT DataDirectoryEntry(const _IMAGE_DATA_DIRECTORY& aOther)
1358 : _IMAGE_DATA_DIRECTORY(aOther) {}
1360 DataDirectoryEntry(const DataDirectoryEntry& aOther) = default;
1362 bool operator==(const DataDirectoryEntry& aOther) const {
1363 return VirtualAddress == aOther.VirtualAddress && Size == aOther.Size;
1366 bool operator!=(const DataDirectoryEntry& aOther) const {
1367 return !(*this == aOther);
1371 inline LauncherResult<void*> GetProcessPebPtr(HANDLE aProcess) {
1372 ULONG returnLength;
1373 PROCESS_BASIC_INFORMATION pbi;
1374 NTSTATUS status = ::NtQueryInformationProcess(
1375 aProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &returnLength);
1376 if (!NT_SUCCESS(status)) {
1377 return LAUNCHER_ERROR_FROM_NTSTATUS(status);
1380 return pbi.PebBaseAddress;
1384 * This function relies on a specific offset into the mostly-undocumented PEB
1385 * structure. The risk is reduced thanks to the fact that the Chromium sandbox
1386 * relies on the location of this field. It is unlikely to change at this point.
1387 * To further reduce the risk, we also check for the magic 'MZ' signature that
1388 * should indicate the beginning of a PE image.
1390 inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) {
1391 LauncherResult<void*> ppeb = GetProcessPebPtr(aProcess);
1392 if (ppeb.isErr()) {
1393 return ppeb.propagateErr();
1396 PEB peb;
1397 SIZE_T bytesRead;
1399 #if defined(MOZILLA_INTERNAL_API)
1400 if (!::ReadProcessMemory(aProcess, ppeb.unwrap(), &peb, sizeof(peb),
1401 &bytesRead) ||
1402 bytesRead != sizeof(peb)) {
1403 return LAUNCHER_ERROR_FROM_LAST();
1405 #else
1406 NTSTATUS ntStatus = ::NtReadVirtualMemory(aProcess, ppeb.unwrap(), &peb,
1407 sizeof(peb), &bytesRead);
1408 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(peb)) {
1409 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
1411 #endif
1413 // peb.ImageBaseAddress
1414 void* baseAddress = peb.Reserved3[1];
1416 char mzMagic[2];
1417 #if defined(MOZILLA_INTERNAL_API)
1418 if (!::ReadProcessMemory(aProcess, baseAddress, mzMagic, sizeof(mzMagic),
1419 &bytesRead) ||
1420 bytesRead != sizeof(mzMagic)) {
1421 return LAUNCHER_ERROR_FROM_LAST();
1423 #else
1424 ntStatus = ::NtReadVirtualMemory(aProcess, baseAddress, mzMagic,
1425 sizeof(mzMagic), &bytesRead);
1426 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(mzMagic)) {
1427 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
1429 #endif
1431 MOZ_ASSERT(mzMagic[0] == 'M' && mzMagic[1] == 'Z');
1432 if (mzMagic[0] != 'M' || mzMagic[1] != 'Z') {
1433 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
1436 return static_cast<HMODULE>(baseAddress);
1439 #if defined(_MSC_VER)
1440 extern "C" IMAGE_DOS_HEADER __ImageBase;
1441 #endif
1443 // This class manages data transfer from the local process's executable
1444 // to another process's executable via WriteProcessMemory.
1445 // Bug 1662560 told us the same executable may be mapped onto a different
1446 // address in a different process. This means when we transfer data within
1447 // the mapped executable such as a global variable or IAT from the current
1448 // process to another process, we need to shift its address by the difference
1449 // between two executable's mapped imagebase.
1450 class CrossExecTransferManager final {
1451 HANDLE mRemoteProcess;
1452 uint8_t* mLocalImagebase;
1453 PEHeaders mLocalExec;
1454 uint8_t* mRemoteImagebase;
1456 static HMODULE GetLocalExecModule() {
1457 #if defined(_MSC_VER)
1458 return reinterpret_cast<HMODULE>(&__ImageBase);
1459 #else
1460 return ::GetModuleHandleW(nullptr);
1461 #endif
1464 LauncherVoidResult EnsureRemoteImagebase() {
1465 if (!mRemoteImagebase) {
1466 LauncherResult<HMODULE> remoteImageBaseResult =
1467 GetProcessExeModule(mRemoteProcess);
1468 if (remoteImageBaseResult.isErr()) {
1469 return remoteImageBaseResult.propagateErr();
1472 mRemoteImagebase =
1473 reinterpret_cast<uint8_t*>(remoteImageBaseResult.unwrap());
1475 return Ok();
1478 template <typename T>
1479 T* LocalExecToRemoteExec(T* aLocalAddress) const {
1480 MOZ_ASSERT(mRemoteImagebase);
1481 MOZ_ASSERT(mLocalExec.IsWithinImage(aLocalAddress));
1483 if (!mRemoteImagebase || !mLocalExec.IsWithinImage(aLocalAddress)) {
1484 return aLocalAddress;
1487 uintptr_t offset = reinterpret_cast<uintptr_t>(aLocalAddress) -
1488 reinterpret_cast<uintptr_t>(mLocalImagebase);
1489 return reinterpret_cast<T*>(mRemoteImagebase + offset);
1492 public:
1493 explicit CrossExecTransferManager(HANDLE aRemoteProcess)
1494 : mRemoteProcess(aRemoteProcess),
1495 mLocalImagebase(
1496 PEHeaders::HModuleToBaseAddr<uint8_t*>(GetLocalExecModule())),
1497 mLocalExec(mLocalImagebase),
1498 mRemoteImagebase(nullptr) {}
1500 CrossExecTransferManager(HANDLE aRemoteProcess, HMODULE aLocalImagebase)
1501 : mRemoteProcess(aRemoteProcess),
1502 mLocalImagebase(
1503 PEHeaders::HModuleToBaseAddr<uint8_t*>(aLocalImagebase)),
1504 mLocalExec(mLocalImagebase),
1505 mRemoteImagebase(nullptr) {}
1507 explicit operator bool() const { return !!mLocalExec; }
1508 HANDLE RemoteProcess() const { return mRemoteProcess; }
1509 const PEHeaders& LocalPEHeaders() const { return mLocalExec; }
1511 AutoVirtualProtect Protect(void* aLocalAddress, size_t aLength,
1512 DWORD aProtFlags) {
1513 // If EnsureRemoteImagebase() fails, a subsequent operaion will fail.
1514 Unused << EnsureRemoteImagebase();
1515 return AutoVirtualProtect(LocalExecToRemoteExec(aLocalAddress), aLength,
1516 aProtFlags, mRemoteProcess);
1519 LauncherVoidResult Transfer(LPVOID aDestinationAddress,
1520 LPCVOID aBufferToWrite, SIZE_T aBufferSize) {
1521 LauncherVoidResult result = EnsureRemoteImagebase();
1522 if (result.isErr()) {
1523 return result.propagateErr();
1526 if (!::WriteProcessMemory(mRemoteProcess,
1527 LocalExecToRemoteExec(aDestinationAddress),
1528 aBufferToWrite, aBufferSize, nullptr)) {
1529 return LAUNCHER_ERROR_FROM_LAST();
1532 return Ok();
1536 #if !defined(MOZILLA_INTERNAL_API)
1538 inline LauncherResult<HMODULE> GetModuleHandleFromLeafName(
1539 const UNICODE_STRING& aTarget) {
1540 auto maybePeb = nt::GetProcessPebPtr(kCurrentProcess);
1541 if (maybePeb.isErr()) {
1542 return maybePeb.propagateErr();
1545 const PPEB peb = reinterpret_cast<PPEB>(maybePeb.unwrap());
1546 if (!peb->Ldr) {
1547 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
1550 auto firstItem = &peb->Ldr->InMemoryOrderModuleList;
1551 for (auto p = firstItem->Flink; p != firstItem; p = p->Flink) {
1552 const auto currentTableEntry =
1553 CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
1555 UNICODE_STRING leafName;
1556 nt::GetLeafName(&leafName, &currentTableEntry->FullDllName);
1558 if (::RtlCompareUnicodeString(&leafName, &aTarget, TRUE) == 0) {
1559 return reinterpret_cast<HMODULE>(currentTableEntry->DllBase);
1563 return LAUNCHER_ERROR_FROM_WIN32(ERROR_MOD_NOT_FOUND);
1566 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS SRWLock final {
1567 public:
1568 constexpr SRWLock() : mLock(SRWLOCK_INIT) {}
1570 void LockShared() { ::RtlAcquireSRWLockShared(&mLock); }
1572 void LockExclusive() { ::RtlAcquireSRWLockExclusive(&mLock); }
1574 void UnlockShared() { ::RtlReleaseSRWLockShared(&mLock); }
1576 void UnlockExclusive() { ::RtlReleaseSRWLockExclusive(&mLock); }
1578 SRWLock(const SRWLock&) = delete;
1579 SRWLock(SRWLock&&) = delete;
1580 SRWLock& operator=(const SRWLock&) = delete;
1581 SRWLock& operator=(SRWLock&&) = delete;
1583 SRWLOCK* operator&() { return &mLock; }
1585 private:
1586 SRWLOCK mLock;
1589 class MOZ_RAII AutoExclusiveLock final {
1590 public:
1591 explicit AutoExclusiveLock(SRWLock& aLock) : mLock(aLock) {
1592 aLock.LockExclusive();
1595 ~AutoExclusiveLock() { mLock.UnlockExclusive(); }
1597 AutoExclusiveLock(const AutoExclusiveLock&) = delete;
1598 AutoExclusiveLock(AutoExclusiveLock&&) = delete;
1599 AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete;
1600 AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete;
1602 private:
1603 SRWLock& mLock;
1606 class MOZ_RAII AutoSharedLock final {
1607 public:
1608 explicit AutoSharedLock(SRWLock& aLock) : mLock(aLock) { aLock.LockShared(); }
1610 ~AutoSharedLock() { mLock.UnlockShared(); }
1612 AutoSharedLock(const AutoSharedLock&) = delete;
1613 AutoSharedLock(AutoSharedLock&&) = delete;
1614 AutoSharedLock& operator=(const AutoSharedLock&) = delete;
1615 AutoSharedLock& operator=(AutoSharedLock&&) = delete;
1617 private:
1618 SRWLock& mLock;
1621 #endif // !defined(MOZILLA_INTERNAL_API)
1623 class RtlAllocPolicy {
1624 public:
1625 template <typename T>
1626 T* maybe_pod_malloc(size_t aNumElems) {
1627 if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1628 return nullptr;
1631 return static_cast<T*>(
1632 ::RtlAllocateHeap(RtlGetProcessHeap(), 0, aNumElems * sizeof(T)));
1635 template <typename T>
1636 T* maybe_pod_calloc(size_t aNumElems) {
1637 if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1638 return nullptr;
1641 return static_cast<T*>(::RtlAllocateHeap(
1642 RtlGetProcessHeap(), HEAP_ZERO_MEMORY, aNumElems * sizeof(T)));
1645 template <typename T>
1646 T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
1647 if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1648 return nullptr;
1651 return static_cast<T*>(::RtlReAllocateHeap(RtlGetProcessHeap(), 0, aPtr,
1652 aNewSize * sizeof(T)));
1655 template <typename T>
1656 T* pod_malloc(size_t aNumElems) {
1657 return maybe_pod_malloc<T>(aNumElems);
1660 template <typename T>
1661 T* pod_calloc(size_t aNumElems) {
1662 return maybe_pod_calloc<T>(aNumElems);
1665 template <typename T>
1666 T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
1667 return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
1670 template <typename T>
1671 void free_(T* aPtr, size_t aNumElems = 0) {
1672 ::RtlFreeHeap(RtlGetProcessHeap(), 0, aPtr);
1675 void reportAllocOverflow() const {}
1677 [[nodiscard]] bool checkSimulatedOOM() const { return true; }
1680 class AutoMappedView final {
1681 void* mView;
1683 void Unmap() {
1684 if (!mView) {
1685 return;
1688 #if defined(MOZILLA_INTERNAL_API)
1689 ::UnmapViewOfFile(mView);
1690 #else
1691 NTSTATUS status = ::NtUnmapViewOfSection(nt::kCurrentProcess, mView);
1692 if (!NT_SUCCESS(status)) {
1693 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1695 #endif
1696 mView = nullptr;
1699 public:
1700 explicit AutoMappedView(void* aView) : mView(aView) {}
1702 AutoMappedView(HANDLE aSection, ULONG aProtectionFlags) : mView(nullptr) {
1703 #if defined(MOZILLA_INTERNAL_API)
1704 mView = ::MapViewOfFile(aSection, aProtectionFlags, 0, 0, 0);
1705 #else
1706 SIZE_T viewSize = 0;
1707 NTSTATUS status = ::NtMapViewOfSection(aSection, nt::kCurrentProcess,
1708 &mView, 0, 0, nullptr, &viewSize,
1709 ViewUnmap, 0, aProtectionFlags);
1710 if (!NT_SUCCESS(status)) {
1711 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1713 #endif
1715 ~AutoMappedView() { Unmap(); }
1717 // Allow move & Disallow copy
1718 AutoMappedView(AutoMappedView&& aOther) : mView(aOther.mView) {
1719 aOther.mView = nullptr;
1721 AutoMappedView& operator=(AutoMappedView&& aOther) {
1722 if (this != &aOther) {
1723 Unmap();
1724 mView = aOther.mView;
1725 aOther.mView = nullptr;
1727 return *this;
1729 AutoMappedView(const AutoMappedView&) = delete;
1730 AutoMappedView& operator=(const AutoMappedView&) = delete;
1732 explicit operator bool() const { return !!mView; }
1733 template <typename T>
1734 T* as() {
1735 return reinterpret_cast<T*>(mView);
1738 void* release() {
1739 void* p = mView;
1740 mView = nullptr;
1741 return p;
1745 #if defined(_M_X64)
1746 // CheckStack ensures that stack memory pages are committed up to a given size
1747 // in bytes from the current stack pointer. It updates the thread stack limit,
1748 // which points to the lowest committed stack address.
1749 MOZ_NEVER_INLINE MOZ_NAKED inline void CheckStack(uint32_t size) {
1750 asm volatile(
1751 "mov %ecx, %eax;"
1752 # if defined(__MINGW32__)
1753 "jmp ___chkstk_ms;"
1754 # else
1755 "jmp __chkstk;"
1756 # endif // __MINGW32__
1759 #endif // _M_X64
1761 } // namespace nt
1762 } // namespace mozilla
1764 #endif // mozilla_NativeNt_h