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