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 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
);
121 #endif // !defined(MOZILLA_INTERNAL_API)
124 PVOID NTAPI
RtlAllocateHeap(PVOID aHeapHandle
, ULONG aFlags
, SIZE_T aSize
);
126 PVOID NTAPI
RtlReAllocateHeap(PVOID aHeapHandle
, ULONG aFlags
, LPVOID aMem
,
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
);
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
{
153 AllocatedUnicodeString() : mUnicodeString() {}
155 #if defined(MOZILLA_INTERNAL_API)
156 AllocatedUnicodeString(const AllocatedUnicodeString
& aOther
) = delete;
158 AllocatedUnicodeString
& operator=(const AllocatedUnicodeString
& aOther
) =
161 explicit AllocatedUnicodeString(PCUNICODE_STRING aSrc
) {
170 explicit AllocatedUnicodeString(const char* aSrc
) {
179 AllocatedUnicodeString(const AllocatedUnicodeString
& aOther
) {
180 Duplicate(&aOther
.mUnicodeString
);
183 AllocatedUnicodeString
& operator=(const AllocatedUnicodeString
& aOther
) {
185 Duplicate(&aOther
.mUnicodeString
);
189 AllocatedUnicodeString
& operator=(PCUNICODE_STRING aSrc
) {
194 #endif // defined(MOZILLA_INTERNAL_API)
196 AllocatedUnicodeString(AllocatedUnicodeString
&& aOther
)
197 : mUnicodeString(aOther
.mUnicodeString
) {
198 aOther
.mUnicodeString
= {};
201 AllocatedUnicodeString
& operator=(AllocatedUnicodeString
&& aOther
) {
203 mUnicodeString
= aOther
.mUnicodeString
;
204 aOther
.mUnicodeString
= {};
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)
233 #if !defined(MOZILLA_INTERNAL_API)
234 void Duplicate(PCUNICODE_STRING 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)
249 void Duplicate(const char* aSrc
) {
253 RtlInitAnsiString(&ansiStr
, aSrc
);
255 ::RtlAnsiStringToUnicodeString(&mUnicodeString
, &ansiStr
, TRUE
);
256 MOZ_ASSERT(NT_SUCCESS(ntStatus
));
257 if (!NT_SUCCESS(ntStatus
)) {
261 #endif // !defined(MOZILLA_INTERNAL_API)
264 if (!mUnicodeString
.Buffer
) {
268 ::RtlFreeUnicodeString(&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
);
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
);
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
)) {}
330 static MemorySectionNameOnHeap
GetBackingFilePath(HANDLE aProcess
,
331 void* aSectionAddr
) {
332 SIZE_T bufferLen
= MAX_PATH
* 2;
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
)) {
344 if (ntStatus
!= STATUS_INFO_LENGTH_MISMATCH
||
345 bufferLen
>= requiredBytes
) {
349 bufferLen
= requiredBytes
;
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
) {
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
)];
390 if (!aPredicate(*cur
)) {
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
) {
409 if (!FindCharInUnicodeString(aLeafName
, L
'.', start
)) {
414 if (!FindCharInUnicodeString(aLeafName
, L
'.', end
, start
)) {
418 if (end
- start
!= 12) {
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
) {
439 if (!FindCharInUnicodeString(aLeafName
, L
'.', dotIndex
)) {
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];
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
);
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') {
505 inline int StricmpASCII(const char* aLeft
, const char* aRight
) {
506 char curLeft
, curRight
;
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
;
520 curLeft
= *(aLeft
++);
521 curRight
= *(aRight
++);
522 } while (curLeft
&& curLeft
== curRight
);
524 return curLeft
- curRight
;
527 inline size_t StrlenASCII(const char* aStr
) {
537 struct CodeViewRecord70
{
541 // A UTF-8 string, according to
542 // https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/locator.cpp#L785
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
553 struct VS_VERSIONINFO_HEADER
{
557 WCHAR szKey
[16]; // ArrayLength(L"VS_VERSION_INFO")
558 // Additional data goes here, aligned on a 4-byte boundary
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
) &
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
),
580 mImageLimit(nullptr),
581 mIsImportDirectoryTampered(false) {
582 if (!mMzHeader
|| mMzHeader
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
586 mPeHeader
= RVAToPtrUnchecked
<PIMAGE_NT_HEADERS
>(mMzHeader
->e_lfanew
);
587 if (!mPeHeader
|| mPeHeader
->Signature
!= IMAGE_NT_SIGNATURE
) {
591 if (mPeHeader
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
) {
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
599 if (imageSize
< sizeof(IMAGE_DOS_HEADER
) + sizeof(IMAGE_NT_HEADERS
)) {
603 mImageLimit
= RVAToPtrUnchecked
<void*>(imageSize
- 1UL);
605 PIMAGE_DATA_DIRECTORY importDirEntry
=
606 GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_IMPORT
);
607 if (!importDirEntry
) {
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
630 template <typename T
, typename R
>
631 T
RVAToPtr(void* aBase
, R aRva
) const {
636 char* absAddress
= reinterpret_cast<char*>(aBase
) + aRva
;
637 if (absAddress
< reinterpret_cast<char*>(mMzHeader
) ||
638 absAddress
> reinterpret_cast<char*>(mImageLimit
)) {
642 return reinterpret_cast<T
>(absAddress
);
645 Maybe
<Range
<const uint8_t>> GetBounds() const {
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 {
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
) {
696 PIMAGE_DATA_DIRECTORY dirEntry
=
697 &optionalHeader
.DataDirectory
[aDirectoryIndex
];
699 *aOutRva
= reinterpret_cast<char*>(dirEntry
) -
700 reinterpret_cast<char*>(mMzHeader
);
701 MOZ_ASSERT(*aOutRva
);
707 bool GetVersionInfo(uint64_t& aOutVersion
) const {
709 // Version resources require an id of 1
710 auto root
= FindResourceLeaf
<VS_VERSIONINFO_HEADER
*>(16, 1);
715 VS_FIXEDFILEINFO
* fixedInfo
= GetFixedFileInfo(root
);
720 aOutVersion
= ((static_cast<uint64_t>(fixedInfo
->dwFileVersionMS
) << 32) |
721 static_cast<uint64_t>(fixedInfo
->dwFileVersionLS
));
725 bool GetTimeStamp(DWORD
& aResult
) const {
730 aResult
= mPeHeader
->FileHeader
.TimeDateStamp
;
734 bool GetImageSize(DWORD
& aResult
) const {
739 aResult
= mPeHeader
->OptionalHeader
.SizeOfImage
;
743 bool GetCheckSum(DWORD
& aResult
) const {
748 aResult
= mPeHeader
->OptionalHeader
.CheckSum
;
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
);
763 if (StricmpASCII(aModuleNameASCII
, curName
)) {
767 // curImpDesc now points to the IAT for the module we're interested in
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
);
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
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
);
803 this->template RVAToPtr
<PIMAGE_THUNK_DATA
>(impDesc
->FirstThunk
);
804 if (!firstIatThunk
) {
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
)) {
813 reinterpret_cast<const uint8_t*>(curIatThunk
->u1
.Function
);
814 if (iatEntry
< aBoundaries
->begin().get() ||
815 iatEntry
>= aBoundaries
->end().get()) {
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();
838 PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry
=
839 FindResourceEntry(topLevel
, aType
);
840 if (!typeEntry
|| !typeEntry
->DataIsDirectory
) {
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
) {
851 auto langDir
= RVAToPtr
<PIMAGE_RESOURCE_DIRECTORY
>(
852 topLevel
, idEntry
->OffsetToDirectory
);
853 PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry
;
855 langEntry
= FindResourceEntry(langDir
, aLangId
);
857 langEntry
= FindFirstResourceEntry(langDir
);
860 if (!langEntry
|| langEntry
->DataIsDirectory
) {
865 RVAToPtr
<PIMAGE_RESOURCE_DATA_ENTRY
>(topLevel
, langEntry
->OffsetToData
);
866 return dataEntry
? RVAToPtr
<T
>(dataEntry
->OffsetToData
) : nullptr;
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 "
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
)) {
887 if (!(sectionHeader
.Characteristics
& aCharacteristicsMask
)) {
888 // We found the section but it does not have the expected
893 DWORD rva
= sectionHeader
.VirtualAddress
;
898 DWORD size
= sectionHeader
.Misc
.VirtualSize
;
903 auto base
= RVAToPtr
<const uint8_t*>(rva
);
904 return Some(Span(base
, size
));
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
|
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
) {
940 const CodeViewRecord70
* debugInfo
=
941 RVAToPtr
<CodeViewRecord70
*>(debugDirectory
->AddressOfRawData
);
942 return (debugInfo
&& debugInfo
->signature
== 'SDSR') ? debugInfo
: nullptr;
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
);
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 {
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)
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 {
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.
987 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY
>(aCurLevel
+ 1) +
988 aCurLevel
->NumberOfNamedEntries
;
989 if (!(IsWithinImage(dirEnt
) &&
990 IsWithinImage(&dirEnt
[aCurLevel
->NumberOfIdEntries
- 1].Id
))) {
994 for (WORD i
= 0; i
< aCurLevel
->NumberOfIdEntries
; ++i
) {
995 if (dirEnt
[i
].Id
== aId
) {
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.
1009 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY
>(aCurLevel
+ 1);
1011 aCurLevel
->NumberOfNamedEntries
+ aCurLevel
->NumberOfIdEntries
;
1019 VS_FIXEDFILEINFO
* GetFixedFileInfo(VS_VERSIONINFO_HEADER
* aVerInfo
) const {
1020 WORD length
= aVerInfo
->wLength
;
1021 if (length
< sizeof(VS_VERSIONINFO_HEADER
)) {
1025 const wchar_t kVersionInfoKey
[] = L
"VS_VERSION_INFO";
1026 if (::RtlCompareMemory(aVerInfo
->szKey
, kVersionInfoKey
,
1027 ArrayLength(kVersionInfoKey
)) !=
1028 ArrayLength(kVersionInfoKey
)) {
1032 if (aVerInfo
->wValueLength
!= sizeof(VS_FIXEDFILEINFO
)) {
1033 // Fixed file info does not exist
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
) {
1048 auto result
= reinterpret_cast<VS_FIXEDFILEINFO
*>(base
+ offset
);
1049 if (result
->dwSignature
!= 0xFEEF04BD) {
1057 PIMAGE_DOS_HEADER mMzHeader
;
1058 PIMAGE_NT_HEADERS mPeHeader
;
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
;
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
),
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
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
) {
1158 auto rvaToFunction
= mExportAddressTable
[aOrdinal
- mOrdinalBase
];
1159 if (!rvaToFunction
) {
1162 return reinterpret_cast<FARPROC
>(mImageBase
+ *rvaToFunction
);
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 {
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
1197 const DWORD
* FindExportAddressTableEntry(
1198 const char* aFunctionNameASCII
) const {
1199 if (!*this || !aFunctionNameASCII
) {
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
);
1225 if (!mExportNameTable
.BinarySearchIf(comp
, &match
)) {
1229 const WORD
* index
= mExportOrdinalTable
[match
];
1234 const DWORD
* rvaToFunction
= mExportAddressTable
[*index
];
1235 if (!rvaToFunction
) {
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
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
) {
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
) &
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
;
1308 ULONG_PTR UniqueProcessId
;
1309 ULONG_PTR InheritedFromUniqueProcessId
;
1313 PROCESS_BASIC_INFORMATION pbi
= {};
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
);
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
));
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
) {
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
);
1387 return ppeb
.propagateErr();
1393 #if defined(MOZILLA_INTERNAL_API)
1394 if (!::ReadProcessMemory(aProcess
, ppeb
.unwrap(), &peb
, sizeof(peb
),
1396 bytesRead
!= sizeof(peb
)) {
1397 return LAUNCHER_ERROR_FROM_LAST();
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
);
1407 // peb.ImageBaseAddress
1408 void* baseAddress
= peb
.Reserved3
[1];
1411 #if defined(MOZILLA_INTERNAL_API)
1412 if (!::ReadProcessMemory(aProcess
, baseAddress
, mzMagic
, sizeof(mzMagic
),
1414 bytesRead
!= sizeof(mzMagic
)) {
1415 return LAUNCHER_ERROR_FROM_LAST();
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
);
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
;
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
);
1454 return ::GetModuleHandleW(nullptr);
1458 LauncherVoidResult
EnsureRemoteImagebase() {
1459 if (!mRemoteImagebase
) {
1460 LauncherResult
<HMODULE
> remoteImageBaseResult
=
1461 GetProcessExeModule(mRemoteProcess
);
1462 if (remoteImageBaseResult
.isErr()) {
1463 return remoteImageBaseResult
.propagateErr();
1467 reinterpret_cast<uint8_t*>(remoteImageBaseResult
.unwrap());
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
);
1487 explicit CrossExecTransferManager(HANDLE aRemoteProcess
)
1488 : mRemoteProcess(aRemoteProcess
),
1490 PEHeaders::HModuleToBaseAddr
<uint8_t*>(GetLocalExecModule())),
1491 mLocalExec(mLocalImagebase
),
1492 mRemoteImagebase(nullptr) {}
1494 CrossExecTransferManager(HANDLE aRemoteProcess
, HMODULE aLocalImagebase
)
1495 : mRemoteProcess(aRemoteProcess
),
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
,
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();
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());
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
, ¤tTableEntry
->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
{
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
; }
1583 class MOZ_RAII AutoExclusiveLock final
{
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;
1600 class MOZ_RAII AutoSharedLock final
{
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;
1615 #endif // !defined(MOZILLA_INTERNAL_API)
1617 class RtlAllocPolicy
{
1619 template <typename T
>
1620 T
* maybe_pod_malloc(size_t aNumElems
) {
1621 if (aNumElems
& mozilla::tl::MulOverflowMask
<sizeof(T
)>::value
) {
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
) {
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
) {
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
{
1682 #if defined(MOZILLA_INTERNAL_API)
1683 ::UnmapViewOfFile(mView
);
1685 NTSTATUS status
= ::NtUnmapViewOfSection(nt::kCurrentProcess
, mView
);
1686 if (!NT_SUCCESS(status
)) {
1687 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status
));
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);
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
));
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
) {
1718 mView
= aOther
.mView
;
1719 aOther
.mView
= nullptr;
1723 AutoMappedView(const AutoMappedView
&) = delete;
1724 AutoMappedView
& operator=(const AutoMappedView
&) = delete;
1726 explicit operator bool() const { return !!mView
; }
1727 template <typename T
>
1729 return reinterpret_cast<T
*>(mView
);
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
) {
1746 # if defined(__MINGW32__)
1750 # endif // __MINGW32__
1756 } // namespace mozilla
1758 #endif // mozilla_NativeNt_h