no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / mozglue / misc / NativeNt.h
blob932dcd0a7b06f7b2a6f27d276f19654fb30ef4fb
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]; // ArrayLength(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 static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc) {
917 return aImpDesc && aImpDesc->OriginalFirstThunk != 0;
920 static bool IsValid(PIMAGE_THUNK_DATA aImgThunk) {
921 return aImgThunk && aImgThunk->u1.Ordinal != 0;
924 bool IsImportDirectoryTampered() const { return mIsImportDirectoryTampered; }
926 FARPROC GetEntryPoint() const {
927 // Use the unchecked version because the entrypoint may be tampered.
928 return RVAToPtrUnchecked<FARPROC>(
929 mPeHeader->OptionalHeader.AddressOfEntryPoint);
932 const CodeViewRecord70* GetPdbInfo() const {
933 PIMAGE_DEBUG_DIRECTORY debugDirectory =
934 GetImageDirectoryEntry<PIMAGE_DEBUG_DIRECTORY>(
935 IMAGE_DIRECTORY_ENTRY_DEBUG);
936 if (!debugDirectory) {
937 return nullptr;
940 const CodeViewRecord70* debugInfo =
941 RVAToPtr<CodeViewRecord70*>(debugDirectory->AddressOfRawData);
942 return (debugInfo && debugInfo->signature == 'SDSR') ? debugInfo : nullptr;
945 private:
946 enum class BoundsCheckPolicy { Default, Skip };
948 template <typename T, BoundsCheckPolicy Policy = BoundsCheckPolicy::Default>
949 T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) const {
950 PIMAGE_DATA_DIRECTORY dirEntry = GetImageDirectoryEntryPtr(aDirectoryIndex);
951 if (!dirEntry) {
952 return nullptr;
955 return Policy == BoundsCheckPolicy::Skip
956 ? RVAToPtrUnchecked<T>(dirEntry->VirtualAddress)
957 : RVAToPtr<T>(dirEntry->VirtualAddress);
960 // This private variant does not have bounds checks, because we need to be
961 // able to resolve the bounds themselves.
962 template <typename T, typename R>
963 T RVAToPtrUnchecked(R aRva) const {
964 return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
967 Span<IMAGE_SECTION_HEADER> GetSectionTable() const {
968 MOZ_ASSERT(*this);
969 auto base = RVAToPtr<PIMAGE_SECTION_HEADER>(
970 &mPeHeader->OptionalHeader, mPeHeader->FileHeader.SizeOfOptionalHeader);
971 // The Windows loader has an internal limit of 96 sections (per PE spec)
972 auto numSections =
973 std::min(mPeHeader->FileHeader.NumberOfSections, WORD(96));
974 return Span{base, numSections};
977 PIMAGE_RESOURCE_DIRECTORY_ENTRY
978 FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) const {
979 if (!aCurLevel) {
980 return nullptr;
983 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
984 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
985 // searches by ID, we need to skip past any named entries before iterating.
986 auto dirEnt =
987 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) +
988 aCurLevel->NumberOfNamedEntries;
989 if (!(IsWithinImage(dirEnt) &&
990 IsWithinImage(&dirEnt[aCurLevel->NumberOfIdEntries - 1].Id))) {
991 return nullptr;
994 for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) {
995 if (dirEnt[i].Id == aId) {
996 return &dirEnt[i];
1000 return nullptr;
1003 PIMAGE_RESOURCE_DIRECTORY_ENTRY
1004 FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) const {
1005 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
1006 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first
1007 // entry, regardless of whether it is indexed by name or by id.
1008 auto dirEnt =
1009 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1);
1010 WORD numEntries =
1011 aCurLevel->NumberOfNamedEntries + aCurLevel->NumberOfIdEntries;
1012 if (!numEntries) {
1013 return nullptr;
1016 return dirEnt;
1019 VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) const {
1020 WORD length = aVerInfo->wLength;
1021 if (length < sizeof(VS_VERSIONINFO_HEADER)) {
1022 return nullptr;
1025 const wchar_t kVersionInfoKey[] = L"VS_VERSION_INFO";
1026 if (::RtlCompareMemory(aVerInfo->szKey, kVersionInfoKey,
1027 ArrayLength(kVersionInfoKey)) !=
1028 ArrayLength(kVersionInfoKey)) {
1029 return nullptr;
1032 if (aVerInfo->wValueLength != sizeof(VS_FIXEDFILEINFO)) {
1033 // Fixed file info does not exist
1034 return nullptr;
1037 WORD offset = sizeof(VS_VERSIONINFO_HEADER);
1039 uintptr_t base = reinterpret_cast<uintptr_t>(aVerInfo);
1040 // Align up to 4-byte boundary
1041 #pragma warning(suppress : 4146)
1042 offset += (-(base + offset) & 3);
1044 if (offset >= length) {
1045 return nullptr;
1048 auto result = reinterpret_cast<VS_FIXEDFILEINFO*>(base + offset);
1049 if (result->dwSignature != 0xFEEF04BD) {
1050 return nullptr;
1053 return result;
1056 private:
1057 PIMAGE_DOS_HEADER mMzHeader;
1058 PIMAGE_NT_HEADERS mPeHeader;
1059 void* mImageLimit;
1060 bool mIsImportDirectoryTampered;
1063 // This class represents an export section of a local/remote process.
1064 template <typename MMPolicy>
1065 class MOZ_RAII PEExportSection {
1066 const MMPolicy& mMMPolicy;
1067 uintptr_t mImageBase;
1068 DWORD mOrdinalBase;
1069 DWORD mRvaDirStart;
1070 DWORD mRvaDirEnd;
1071 mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportAddressTable;
1072 mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportNameTable;
1073 mozilla::interceptor::TargetObjectArray<MMPolicy, WORD> mExportOrdinalTable;
1075 explicit PEExportSection(const MMPolicy& aMMPolicy)
1076 : mMMPolicy(aMMPolicy),
1077 mImageBase(0),
1078 mOrdinalBase(0),
1079 mRvaDirStart(0),
1080 mRvaDirEnd(0),
1081 mExportAddressTable(mMMPolicy),
1082 mExportNameTable(mMMPolicy),
1083 mExportOrdinalTable(mMMPolicy) {}
1085 PEExportSection(const MMPolicy& aMMPolicy, uintptr_t aImageBase,
1086 DWORD aRvaDirStart, DWORD aRvaDirEnd,
1087 const IMAGE_EXPORT_DIRECTORY& exportDir)
1088 : mMMPolicy(aMMPolicy),
1089 mImageBase(aImageBase),
1090 mOrdinalBase(exportDir.Base),
1091 mRvaDirStart(aRvaDirStart),
1092 mRvaDirEnd(aRvaDirEnd),
1093 mExportAddressTable(mMMPolicy,
1094 mImageBase + exportDir.AddressOfFunctions,
1095 exportDir.NumberOfFunctions),
1096 mExportNameTable(mMMPolicy, mImageBase + exportDir.AddressOfNames,
1097 exportDir.NumberOfNames),
1098 mExportOrdinalTable(mMMPolicy,
1099 mImageBase + exportDir.AddressOfNameOrdinals,
1100 exportDir.NumberOfNames) {}
1102 static const PEExportSection Get(uintptr_t aImageBase,
1103 const MMPolicy& aMMPolicy) {
1104 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_DOS_HEADER> mzHeader(
1105 aMMPolicy, aImageBase);
1106 if (!mzHeader || mzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
1107 return PEExportSection(aMMPolicy);
1110 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_NT_HEADERS> peHeader(
1111 aMMPolicy, aImageBase + mzHeader->e_lfanew);
1112 if (!peHeader || peHeader->Signature != IMAGE_NT_SIGNATURE) {
1113 return PEExportSection(aMMPolicy);
1116 if (peHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
1117 return PEExportSection(aMMPolicy);
1120 const IMAGE_OPTIONAL_HEADER& optionalHeader = peHeader->OptionalHeader;
1122 DWORD imageSize = optionalHeader.SizeOfImage;
1123 // This is a coarse-grained check to ensure that the image size is
1124 // reasonable. It we aren't big enough to contain headers, we have a
1125 // problem!
1126 if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) {
1127 return PEExportSection(aMMPolicy);
1130 if (optionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) {
1131 return PEExportSection(aMMPolicy);
1134 const IMAGE_DATA_DIRECTORY& exportDirectoryEntry =
1135 optionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
1136 if (!exportDirectoryEntry.VirtualAddress || !exportDirectoryEntry.Size) {
1137 return PEExportSection(aMMPolicy);
1140 mozilla::interceptor::TargetObject<MMPolicy, IMAGE_EXPORT_DIRECTORY>
1141 exportDirectory(aMMPolicy,
1142 aImageBase + exportDirectoryEntry.VirtualAddress);
1143 if (!exportDirectory || !exportDirectory->NumberOfFunctions) {
1144 return PEExportSection(aMMPolicy);
1147 return PEExportSection(
1148 aMMPolicy, aImageBase, exportDirectoryEntry.VirtualAddress,
1149 exportDirectoryEntry.VirtualAddress + exportDirectoryEntry.Size,
1150 *exportDirectory.GetLocalBase());
1153 FARPROC GetProcAddressByOrdinal(WORD aOrdinal) const {
1154 if (aOrdinal < mOrdinalBase) {
1155 return nullptr;
1158 auto rvaToFunction = mExportAddressTable[aOrdinal - mOrdinalBase];
1159 if (!rvaToFunction) {
1160 return nullptr;
1162 return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
1165 public:
1166 static const PEExportSection Get(HMODULE aModule, const MMPolicy& aMMPolicy) {
1167 return Get(PEHeaders::HModuleToBaseAddr<uintptr_t>(aModule), aMMPolicy);
1170 explicit operator bool() const {
1171 // Because PEExportSection doesn't use MMPolicy::Reserve(), a boolified
1172 // mMMPolicy is expected to be false. We don't check mMMPolicy here.
1173 return mImageBase && mRvaDirStart && mRvaDirEnd && mExportAddressTable &&
1174 mExportNameTable && mExportOrdinalTable;
1177 template <typename T>
1178 T RVAToPtr(uint32_t aRva) const {
1179 return reinterpret_cast<T>(mImageBase + aRva);
1182 PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const {
1183 if (!*this) {
1184 return nullptr;
1187 return RVAToPtr<PIMAGE_EXPORT_DIRECTORY>(mRvaDirStart);
1191 * This functions searches the export table for a given string as
1192 * GetProcAddress does, but this returns a matched entry of the Export
1193 * Address Table i.e. a pointer to an RVA of a matched function instead
1194 * of a function address. If the entry is forwarded, this function
1195 * returns nullptr.
1197 const DWORD* FindExportAddressTableEntry(
1198 const char* aFunctionNameASCII) const {
1199 if (!*this || !aFunctionNameASCII) {
1200 return nullptr;
1203 struct NameTableComparator {
1204 NameTableComparator(const PEExportSection<MMPolicy>& aExportSection,
1205 const char* aTarget)
1206 : mExportSection(aExportSection),
1207 mTargetName(aTarget),
1208 mTargetNamelength(StrlenASCII(aTarget)) {}
1210 int operator()(DWORD aRVAToString) const {
1211 mozilla::interceptor::TargetObjectArray<MMPolicy, char> itemString(
1212 mExportSection.mMMPolicy, mExportSection.mImageBase + aRVAToString,
1213 mTargetNamelength + 1);
1214 return StrcmpASCII(mTargetName, itemString[0]);
1217 const PEExportSection<MMPolicy>& mExportSection;
1218 const char* mTargetName;
1219 size_t mTargetNamelength;
1222 const NameTableComparator comp(*this, aFunctionNameASCII);
1224 size_t match;
1225 if (!mExportNameTable.BinarySearchIf(comp, &match)) {
1226 return nullptr;
1229 const WORD* index = mExportOrdinalTable[match];
1230 if (!index) {
1231 return nullptr;
1234 const DWORD* rvaToFunction = mExportAddressTable[*index];
1235 if (!rvaToFunction) {
1236 return nullptr;
1239 if (*rvaToFunction >= mRvaDirStart && *rvaToFunction < mRvaDirEnd) {
1240 // If an entry points to an address within the export section, the
1241 // field is a forwarder RVA. We return nullptr because the entry is
1242 // not a function address but a null-terminated string used for export
1243 // forwarding.
1244 return nullptr;
1247 return rvaToFunction;
1251 * This functions behaves the same as the native ::GetProcAddress except
1252 * the following cases:
1253 * - Returns nullptr if a target entry is forwarded to another dll.
1255 FARPROC GetProcAddress(const char* aFunctionNameASCII) const {
1256 uintptr_t maybeOdrinal = reinterpret_cast<uintptr_t>(aFunctionNameASCII);
1257 // When the high-order word of |aFunctionNameASCII| is zero, it's not
1258 // a string but an ordinal value.
1259 if (maybeOdrinal < 0x10000) {
1260 return GetProcAddressByOrdinal(static_cast<WORD>(maybeOdrinal));
1263 auto rvaToFunction = FindExportAddressTableEntry(aFunctionNameASCII);
1264 if (!rvaToFunction) {
1265 return nullptr;
1267 return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
1271 inline HANDLE RtlGetProcessHeap() {
1272 PTEB teb = ::NtCurrentTeb();
1273 PPEB peb = teb->ProcessEnvironmentBlock;
1274 return peb->Reserved4[1];
1277 inline PVOID RtlGetThreadLocalStoragePointer() {
1278 return ::NtCurrentTeb()->Reserved1[11];
1281 inline void RtlSetThreadLocalStoragePointerForTestingOnly(PVOID aNewValue) {
1282 ::NtCurrentTeb()->Reserved1[11] = aNewValue;
1285 inline DWORD RtlGetCurrentThreadId() {
1286 PTEB teb = ::NtCurrentTeb();
1287 CLIENT_ID* cid = reinterpret_cast<CLIENT_ID*>(&teb->Reserved1[8]);
1288 return static_cast<DWORD>(reinterpret_cast<uintptr_t>(cid->UniqueThread) &
1289 0xFFFFFFFFUL);
1292 inline PVOID RtlGetThreadStackBase() {
1293 return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackBase;
1296 inline PVOID RtlGetThreadStackLimit() {
1297 return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackLimit;
1300 const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
1302 inline LauncherResult<DWORD> GetParentProcessId() {
1303 struct PROCESS_BASIC_INFORMATION {
1304 NTSTATUS ExitStatus;
1305 PPEB PebBaseAddress;
1306 ULONG_PTR AffinityMask;
1307 LONG BasePriority;
1308 ULONG_PTR UniqueProcessId;
1309 ULONG_PTR InheritedFromUniqueProcessId;
1312 ULONG returnLength;
1313 PROCESS_BASIC_INFORMATION pbi = {};
1314 NTSTATUS status =
1315 ::NtQueryInformationProcess(kCurrentProcess, ProcessBasicInformation,
1316 &pbi, sizeof(pbi), &returnLength);
1317 if (!NT_SUCCESS(status)) {
1318 return LAUNCHER_ERROR_FROM_NTSTATUS(status);
1321 return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF);
1324 inline SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
1325 PMEMORY_BASIC_INFORMATION aMemInfo,
1326 SIZE_T aMemInfoLen) {
1327 #if defined(MOZILLA_INTERNAL_API)
1328 return ::VirtualQueryEx(aProcess, aAddress, aMemInfo, aMemInfoLen);
1329 #else
1330 SIZE_T returnedLength;
1331 NTSTATUS status = ::NtQueryVirtualMemory(
1332 aProcess, const_cast<PVOID>(aAddress), MemoryBasicInformation, aMemInfo,
1333 aMemInfoLen, &returnedLength);
1334 if (!NT_SUCCESS(status)) {
1335 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1336 returnedLength = 0;
1338 return returnedLength;
1339 #endif // defined(MOZILLA_INTERNAL_API)
1342 inline SIZE_T WINAPI VirtualQuery(LPCVOID aAddress,
1343 PMEMORY_BASIC_INFORMATION aMemInfo,
1344 SIZE_T aMemInfoLen) {
1345 return nt::VirtualQueryEx(kCurrentProcess, aAddress, aMemInfo, aMemInfoLen);
1348 struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY {
1349 DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}
1351 MOZ_IMPLICIT DataDirectoryEntry(const _IMAGE_DATA_DIRECTORY& aOther)
1352 : _IMAGE_DATA_DIRECTORY(aOther) {}
1354 DataDirectoryEntry(const DataDirectoryEntry& aOther) = default;
1356 bool operator==(const DataDirectoryEntry& aOther) const {
1357 return VirtualAddress == aOther.VirtualAddress && Size == aOther.Size;
1360 bool operator!=(const DataDirectoryEntry& aOther) const {
1361 return !(*this == aOther);
1365 inline LauncherResult<void*> GetProcessPebPtr(HANDLE aProcess) {
1366 ULONG returnLength;
1367 PROCESS_BASIC_INFORMATION pbi;
1368 NTSTATUS status = ::NtQueryInformationProcess(
1369 aProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &returnLength);
1370 if (!NT_SUCCESS(status)) {
1371 return LAUNCHER_ERROR_FROM_NTSTATUS(status);
1374 return pbi.PebBaseAddress;
1378 * This function relies on a specific offset into the mostly-undocumented PEB
1379 * structure. The risk is reduced thanks to the fact that the Chromium sandbox
1380 * relies on the location of this field. It is unlikely to change at this point.
1381 * To further reduce the risk, we also check for the magic 'MZ' signature that
1382 * should indicate the beginning of a PE image.
1384 inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) {
1385 LauncherResult<void*> ppeb = GetProcessPebPtr(aProcess);
1386 if (ppeb.isErr()) {
1387 return ppeb.propagateErr();
1390 PEB peb;
1391 SIZE_T bytesRead;
1393 #if defined(MOZILLA_INTERNAL_API)
1394 if (!::ReadProcessMemory(aProcess, ppeb.unwrap(), &peb, sizeof(peb),
1395 &bytesRead) ||
1396 bytesRead != sizeof(peb)) {
1397 return LAUNCHER_ERROR_FROM_LAST();
1399 #else
1400 NTSTATUS ntStatus = ::NtReadVirtualMemory(aProcess, ppeb.unwrap(), &peb,
1401 sizeof(peb), &bytesRead);
1402 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(peb)) {
1403 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
1405 #endif
1407 // peb.ImageBaseAddress
1408 void* baseAddress = peb.Reserved3[1];
1410 char mzMagic[2];
1411 #if defined(MOZILLA_INTERNAL_API)
1412 if (!::ReadProcessMemory(aProcess, baseAddress, mzMagic, sizeof(mzMagic),
1413 &bytesRead) ||
1414 bytesRead != sizeof(mzMagic)) {
1415 return LAUNCHER_ERROR_FROM_LAST();
1417 #else
1418 ntStatus = ::NtReadVirtualMemory(aProcess, baseAddress, mzMagic,
1419 sizeof(mzMagic), &bytesRead);
1420 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(mzMagic)) {
1421 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
1423 #endif
1425 MOZ_ASSERT(mzMagic[0] == 'M' && mzMagic[1] == 'Z');
1426 if (mzMagic[0] != 'M' || mzMagic[1] != 'Z') {
1427 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
1430 return static_cast<HMODULE>(baseAddress);
1433 #if defined(_MSC_VER)
1434 extern "C" IMAGE_DOS_HEADER __ImageBase;
1435 #endif
1437 // This class manages data transfer from the local process's executable
1438 // to another process's executable via WriteProcessMemory.
1439 // Bug 1662560 told us the same executable may be mapped onto a different
1440 // address in a different process. This means when we transfer data within
1441 // the mapped executable such as a global variable or IAT from the current
1442 // process to another process, we need to shift its address by the difference
1443 // between two executable's mapped imagebase.
1444 class CrossExecTransferManager final {
1445 HANDLE mRemoteProcess;
1446 uint8_t* mLocalImagebase;
1447 PEHeaders mLocalExec;
1448 uint8_t* mRemoteImagebase;
1450 static HMODULE GetLocalExecModule() {
1451 #if defined(_MSC_VER)
1452 return reinterpret_cast<HMODULE>(&__ImageBase);
1453 #else
1454 return ::GetModuleHandleW(nullptr);
1455 #endif
1458 LauncherVoidResult EnsureRemoteImagebase() {
1459 if (!mRemoteImagebase) {
1460 LauncherResult<HMODULE> remoteImageBaseResult =
1461 GetProcessExeModule(mRemoteProcess);
1462 if (remoteImageBaseResult.isErr()) {
1463 return remoteImageBaseResult.propagateErr();
1466 mRemoteImagebase =
1467 reinterpret_cast<uint8_t*>(remoteImageBaseResult.unwrap());
1469 return Ok();
1472 template <typename T>
1473 T* LocalExecToRemoteExec(T* aLocalAddress) const {
1474 MOZ_ASSERT(mRemoteImagebase);
1475 MOZ_ASSERT(mLocalExec.IsWithinImage(aLocalAddress));
1477 if (!mRemoteImagebase || !mLocalExec.IsWithinImage(aLocalAddress)) {
1478 return aLocalAddress;
1481 uintptr_t offset = reinterpret_cast<uintptr_t>(aLocalAddress) -
1482 reinterpret_cast<uintptr_t>(mLocalImagebase);
1483 return reinterpret_cast<T*>(mRemoteImagebase + offset);
1486 public:
1487 explicit CrossExecTransferManager(HANDLE aRemoteProcess)
1488 : mRemoteProcess(aRemoteProcess),
1489 mLocalImagebase(
1490 PEHeaders::HModuleToBaseAddr<uint8_t*>(GetLocalExecModule())),
1491 mLocalExec(mLocalImagebase),
1492 mRemoteImagebase(nullptr) {}
1494 CrossExecTransferManager(HANDLE aRemoteProcess, HMODULE aLocalImagebase)
1495 : mRemoteProcess(aRemoteProcess),
1496 mLocalImagebase(
1497 PEHeaders::HModuleToBaseAddr<uint8_t*>(aLocalImagebase)),
1498 mLocalExec(mLocalImagebase),
1499 mRemoteImagebase(nullptr) {}
1501 explicit operator bool() const { return !!mLocalExec; }
1502 HANDLE RemoteProcess() const { return mRemoteProcess; }
1503 const PEHeaders& LocalPEHeaders() const { return mLocalExec; }
1505 AutoVirtualProtect Protect(void* aLocalAddress, size_t aLength,
1506 DWORD aProtFlags) {
1507 // If EnsureRemoteImagebase() fails, a subsequent operaion will fail.
1508 Unused << EnsureRemoteImagebase();
1509 return AutoVirtualProtect(LocalExecToRemoteExec(aLocalAddress), aLength,
1510 aProtFlags, mRemoteProcess);
1513 LauncherVoidResult Transfer(LPVOID aDestinationAddress,
1514 LPCVOID aBufferToWrite, SIZE_T aBufferSize) {
1515 LauncherVoidResult result = EnsureRemoteImagebase();
1516 if (result.isErr()) {
1517 return result.propagateErr();
1520 if (!::WriteProcessMemory(mRemoteProcess,
1521 LocalExecToRemoteExec(aDestinationAddress),
1522 aBufferToWrite, aBufferSize, nullptr)) {
1523 return LAUNCHER_ERROR_FROM_LAST();
1526 return Ok();
1530 #if !defined(MOZILLA_INTERNAL_API)
1532 inline LauncherResult<HMODULE> GetModuleHandleFromLeafName(
1533 const UNICODE_STRING& aTarget) {
1534 auto maybePeb = nt::GetProcessPebPtr(kCurrentProcess);
1535 if (maybePeb.isErr()) {
1536 return maybePeb.propagateErr();
1539 const PPEB peb = reinterpret_cast<PPEB>(maybePeb.unwrap());
1540 if (!peb->Ldr) {
1541 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
1544 auto firstItem = &peb->Ldr->InMemoryOrderModuleList;
1545 for (auto p = firstItem->Flink; p != firstItem; p = p->Flink) {
1546 const auto currentTableEntry =
1547 CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
1549 UNICODE_STRING leafName;
1550 nt::GetLeafName(&leafName, &currentTableEntry->FullDllName);
1552 if (::RtlCompareUnicodeString(&leafName, &aTarget, TRUE) == 0) {
1553 return reinterpret_cast<HMODULE>(currentTableEntry->DllBase);
1557 return LAUNCHER_ERROR_FROM_WIN32(ERROR_MOD_NOT_FOUND);
1560 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS SRWLock final {
1561 public:
1562 constexpr SRWLock() : mLock(SRWLOCK_INIT) {}
1564 void LockShared() { ::RtlAcquireSRWLockShared(&mLock); }
1566 void LockExclusive() { ::RtlAcquireSRWLockExclusive(&mLock); }
1568 void UnlockShared() { ::RtlReleaseSRWLockShared(&mLock); }
1570 void UnlockExclusive() { ::RtlReleaseSRWLockExclusive(&mLock); }
1572 SRWLock(const SRWLock&) = delete;
1573 SRWLock(SRWLock&&) = delete;
1574 SRWLock& operator=(const SRWLock&) = delete;
1575 SRWLock& operator=(SRWLock&&) = delete;
1577 SRWLOCK* operator&() { return &mLock; }
1579 private:
1580 SRWLOCK mLock;
1583 class MOZ_RAII AutoExclusiveLock final {
1584 public:
1585 explicit AutoExclusiveLock(SRWLock& aLock) : mLock(aLock) {
1586 aLock.LockExclusive();
1589 ~AutoExclusiveLock() { mLock.UnlockExclusive(); }
1591 AutoExclusiveLock(const AutoExclusiveLock&) = delete;
1592 AutoExclusiveLock(AutoExclusiveLock&&) = delete;
1593 AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete;
1594 AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete;
1596 private:
1597 SRWLock& mLock;
1600 class MOZ_RAII AutoSharedLock final {
1601 public:
1602 explicit AutoSharedLock(SRWLock& aLock) : mLock(aLock) { aLock.LockShared(); }
1604 ~AutoSharedLock() { mLock.UnlockShared(); }
1606 AutoSharedLock(const AutoSharedLock&) = delete;
1607 AutoSharedLock(AutoSharedLock&&) = delete;
1608 AutoSharedLock& operator=(const AutoSharedLock&) = delete;
1609 AutoSharedLock& operator=(AutoSharedLock&&) = delete;
1611 private:
1612 SRWLock& mLock;
1615 #endif // !defined(MOZILLA_INTERNAL_API)
1617 class RtlAllocPolicy {
1618 public:
1619 template <typename T>
1620 T* maybe_pod_malloc(size_t aNumElems) {
1621 if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1622 return nullptr;
1625 return static_cast<T*>(
1626 ::RtlAllocateHeap(RtlGetProcessHeap(), 0, aNumElems * sizeof(T)));
1629 template <typename T>
1630 T* maybe_pod_calloc(size_t aNumElems) {
1631 if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1632 return nullptr;
1635 return static_cast<T*>(::RtlAllocateHeap(
1636 RtlGetProcessHeap(), HEAP_ZERO_MEMORY, aNumElems * sizeof(T)));
1639 template <typename T>
1640 T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
1641 if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
1642 return nullptr;
1645 return static_cast<T*>(::RtlReAllocateHeap(RtlGetProcessHeap(), 0, aPtr,
1646 aNewSize * sizeof(T)));
1649 template <typename T>
1650 T* pod_malloc(size_t aNumElems) {
1651 return maybe_pod_malloc<T>(aNumElems);
1654 template <typename T>
1655 T* pod_calloc(size_t aNumElems) {
1656 return maybe_pod_calloc<T>(aNumElems);
1659 template <typename T>
1660 T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
1661 return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
1664 template <typename T>
1665 void free_(T* aPtr, size_t aNumElems = 0) {
1666 ::RtlFreeHeap(RtlGetProcessHeap(), 0, aPtr);
1669 void reportAllocOverflow() const {}
1671 [[nodiscard]] bool checkSimulatedOOM() const { return true; }
1674 class AutoMappedView final {
1675 void* mView;
1677 void Unmap() {
1678 if (!mView) {
1679 return;
1682 #if defined(MOZILLA_INTERNAL_API)
1683 ::UnmapViewOfFile(mView);
1684 #else
1685 NTSTATUS status = ::NtUnmapViewOfSection(nt::kCurrentProcess, mView);
1686 if (!NT_SUCCESS(status)) {
1687 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1689 #endif
1690 mView = nullptr;
1693 public:
1694 explicit AutoMappedView(void* aView) : mView(aView) {}
1696 AutoMappedView(HANDLE aSection, ULONG aProtectionFlags) : mView(nullptr) {
1697 #if defined(MOZILLA_INTERNAL_API)
1698 mView = ::MapViewOfFile(aSection, aProtectionFlags, 0, 0, 0);
1699 #else
1700 SIZE_T viewSize = 0;
1701 NTSTATUS status = ::NtMapViewOfSection(aSection, nt::kCurrentProcess,
1702 &mView, 0, 0, nullptr, &viewSize,
1703 ViewUnmap, 0, aProtectionFlags);
1704 if (!NT_SUCCESS(status)) {
1705 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
1707 #endif
1709 ~AutoMappedView() { Unmap(); }
1711 // Allow move & Disallow copy
1712 AutoMappedView(AutoMappedView&& aOther) : mView(aOther.mView) {
1713 aOther.mView = nullptr;
1715 AutoMappedView& operator=(AutoMappedView&& aOther) {
1716 if (this != &aOther) {
1717 Unmap();
1718 mView = aOther.mView;
1719 aOther.mView = nullptr;
1721 return *this;
1723 AutoMappedView(const AutoMappedView&) = delete;
1724 AutoMappedView& operator=(const AutoMappedView&) = delete;
1726 explicit operator bool() const { return !!mView; }
1727 template <typename T>
1728 T* as() {
1729 return reinterpret_cast<T*>(mView);
1732 void* release() {
1733 void* p = mView;
1734 mView = nullptr;
1735 return p;
1739 #if defined(_M_X64)
1740 // CheckStack ensures that stack memory pages are committed up to a given size
1741 // in bytes from the current stack pointer. It updates the thread stack limit,
1742 // which points to the lowest committed stack address.
1743 MOZ_NEVER_INLINE MOZ_NAKED inline void CheckStack(uint32_t size) {
1744 asm volatile(
1745 "mov %ecx, %eax;"
1746 # if defined(__MINGW32__)
1747 "jmp ___chkstk_ms;"
1748 # else
1749 "jmp __chkstk;"
1750 # endif // __MINGW32__
1753 #endif // _M_X64
1755 } // namespace nt
1756 } // namespace mozilla
1758 #endif // mozilla_NativeNt_h