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]; // std::size(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 // There may be other data sections in the binary besides .data
917 Maybe
<Span
<const uint8_t>> GetDataSectionInfo() const {
918 return FindSection(".data", IMAGE_SCN_CNT_INITIALIZED_DATA
|
919 IMAGE_SCN_MEM_READ
| IMAGE_SCN_MEM_WRITE
);
922 static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc
) {
923 return aImpDesc
&& aImpDesc
->OriginalFirstThunk
!= 0;
926 static bool IsValid(PIMAGE_THUNK_DATA aImgThunk
) {
927 return aImgThunk
&& aImgThunk
->u1
.Ordinal
!= 0;
930 bool IsImportDirectoryTampered() const { return mIsImportDirectoryTampered
; }
932 FARPROC
GetEntryPoint() const {
933 // Use the unchecked version because the entrypoint may be tampered.
934 return RVAToPtrUnchecked
<FARPROC
>(
935 mPeHeader
->OptionalHeader
.AddressOfEntryPoint
);
938 const CodeViewRecord70
* GetPdbInfo() const {
939 PIMAGE_DEBUG_DIRECTORY debugDirectory
=
940 GetImageDirectoryEntry
<PIMAGE_DEBUG_DIRECTORY
>(
941 IMAGE_DIRECTORY_ENTRY_DEBUG
);
942 if (!debugDirectory
) {
946 const CodeViewRecord70
* debugInfo
=
947 RVAToPtr
<CodeViewRecord70
*>(debugDirectory
->AddressOfRawData
);
948 return (debugInfo
&& debugInfo
->signature
== 'SDSR') ? debugInfo
: nullptr;
952 enum class BoundsCheckPolicy
{ Default
, Skip
};
954 template <typename T
, BoundsCheckPolicy Policy
= BoundsCheckPolicy::Default
>
955 T
GetImageDirectoryEntry(const uint32_t aDirectoryIndex
) const {
956 PIMAGE_DATA_DIRECTORY dirEntry
= GetImageDirectoryEntryPtr(aDirectoryIndex
);
961 return Policy
== BoundsCheckPolicy::Skip
962 ? RVAToPtrUnchecked
<T
>(dirEntry
->VirtualAddress
)
963 : RVAToPtr
<T
>(dirEntry
->VirtualAddress
);
966 // This private variant does not have bounds checks, because we need to be
967 // able to resolve the bounds themselves.
968 template <typename T
, typename R
>
969 T
RVAToPtrUnchecked(R aRva
) const {
970 return reinterpret_cast<T
>(reinterpret_cast<char*>(mMzHeader
) + aRva
);
973 Span
<IMAGE_SECTION_HEADER
> GetSectionTable() const {
975 auto base
= RVAToPtr
<PIMAGE_SECTION_HEADER
>(
976 &mPeHeader
->OptionalHeader
, mPeHeader
->FileHeader
.SizeOfOptionalHeader
);
977 // The Windows loader has an internal limit of 96 sections (per PE spec)
979 std::min(mPeHeader
->FileHeader
.NumberOfSections
, WORD(96));
980 return Span
{base
, numSections
};
983 PIMAGE_RESOURCE_DIRECTORY_ENTRY
984 FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel
, WORD aId
) const {
989 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
990 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
991 // searches by ID, we need to skip past any named entries before iterating.
993 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY
>(aCurLevel
+ 1) +
994 aCurLevel
->NumberOfNamedEntries
;
995 if (!(IsWithinImage(dirEnt
) &&
996 IsWithinImage(&dirEnt
[aCurLevel
->NumberOfIdEntries
- 1].Id
))) {
1000 for (WORD i
= 0; i
< aCurLevel
->NumberOfIdEntries
; ++i
) {
1001 if (dirEnt
[i
].Id
== aId
) {
1009 PIMAGE_RESOURCE_DIRECTORY_ENTRY
1010 FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel
) const {
1011 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
1012 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first
1013 // entry, regardless of whether it is indexed by name or by id.
1015 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY
>(aCurLevel
+ 1);
1017 aCurLevel
->NumberOfNamedEntries
+ aCurLevel
->NumberOfIdEntries
;
1025 VS_FIXEDFILEINFO
* GetFixedFileInfo(VS_VERSIONINFO_HEADER
* aVerInfo
) const {
1026 WORD length
= aVerInfo
->wLength
;
1027 if (length
< sizeof(VS_VERSIONINFO_HEADER
)) {
1031 const wchar_t kVersionInfoKey
[] = L
"VS_VERSION_INFO";
1032 if (::RtlCompareMemory(aVerInfo
->szKey
, kVersionInfoKey
,
1033 std::size(kVersionInfoKey
)) !=
1034 std::size(kVersionInfoKey
)) {
1038 if (aVerInfo
->wValueLength
!= sizeof(VS_FIXEDFILEINFO
)) {
1039 // Fixed file info does not exist
1043 WORD offset
= sizeof(VS_VERSIONINFO_HEADER
);
1045 uintptr_t base
= reinterpret_cast<uintptr_t>(aVerInfo
);
1046 // Align up to 4-byte boundary
1047 #pragma warning(suppress : 4146)
1048 offset
+= (-(base
+ offset
) & 3);
1050 if (offset
>= length
) {
1054 auto result
= reinterpret_cast<VS_FIXEDFILEINFO
*>(base
+ offset
);
1055 if (result
->dwSignature
!= 0xFEEF04BD) {
1063 PIMAGE_DOS_HEADER mMzHeader
;
1064 PIMAGE_NT_HEADERS mPeHeader
;
1066 bool mIsImportDirectoryTampered
;
1069 // This class represents an export section of a local/remote process.
1070 template <typename MMPolicy
>
1071 class MOZ_RAII PEExportSection
{
1072 const MMPolicy
& mMMPolicy
;
1073 uintptr_t mImageBase
;
1077 mozilla::interceptor::TargetObjectArray
<MMPolicy
, DWORD
> mExportAddressTable
;
1078 mozilla::interceptor::TargetObjectArray
<MMPolicy
, DWORD
> mExportNameTable
;
1079 mozilla::interceptor::TargetObjectArray
<MMPolicy
, WORD
> mExportOrdinalTable
;
1081 explicit PEExportSection(const MMPolicy
& aMMPolicy
)
1082 : mMMPolicy(aMMPolicy
),
1087 mExportAddressTable(mMMPolicy
),
1088 mExportNameTable(mMMPolicy
),
1089 mExportOrdinalTable(mMMPolicy
) {}
1091 PEExportSection(const MMPolicy
& aMMPolicy
, uintptr_t aImageBase
,
1092 DWORD aRvaDirStart
, DWORD aRvaDirEnd
,
1093 const IMAGE_EXPORT_DIRECTORY
& exportDir
)
1094 : mMMPolicy(aMMPolicy
),
1095 mImageBase(aImageBase
),
1096 mOrdinalBase(exportDir
.Base
),
1097 mRvaDirStart(aRvaDirStart
),
1098 mRvaDirEnd(aRvaDirEnd
),
1099 mExportAddressTable(mMMPolicy
,
1100 mImageBase
+ exportDir
.AddressOfFunctions
,
1101 exportDir
.NumberOfFunctions
),
1102 mExportNameTable(mMMPolicy
, mImageBase
+ exportDir
.AddressOfNames
,
1103 exportDir
.NumberOfNames
),
1104 mExportOrdinalTable(mMMPolicy
,
1105 mImageBase
+ exportDir
.AddressOfNameOrdinals
,
1106 exportDir
.NumberOfNames
) {}
1108 static const PEExportSection
Get(uintptr_t aImageBase
,
1109 const MMPolicy
& aMMPolicy
) {
1110 mozilla::interceptor::TargetObject
<MMPolicy
, IMAGE_DOS_HEADER
> mzHeader(
1111 aMMPolicy
, aImageBase
);
1112 if (!mzHeader
|| mzHeader
->e_magic
!= IMAGE_DOS_SIGNATURE
) {
1113 return PEExportSection(aMMPolicy
);
1116 mozilla::interceptor::TargetObject
<MMPolicy
, IMAGE_NT_HEADERS
> peHeader(
1117 aMMPolicy
, aImageBase
+ mzHeader
->e_lfanew
);
1118 if (!peHeader
|| peHeader
->Signature
!= IMAGE_NT_SIGNATURE
) {
1119 return PEExportSection(aMMPolicy
);
1122 if (peHeader
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
) {
1123 return PEExportSection(aMMPolicy
);
1126 const IMAGE_OPTIONAL_HEADER
& optionalHeader
= peHeader
->OptionalHeader
;
1128 DWORD imageSize
= optionalHeader
.SizeOfImage
;
1129 // This is a coarse-grained check to ensure that the image size is
1130 // reasonable. It we aren't big enough to contain headers, we have a
1132 if (imageSize
< sizeof(IMAGE_DOS_HEADER
) + sizeof(IMAGE_NT_HEADERS
)) {
1133 return PEExportSection(aMMPolicy
);
1136 if (optionalHeader
.NumberOfRvaAndSizes
<= IMAGE_DIRECTORY_ENTRY_EXPORT
) {
1137 return PEExportSection(aMMPolicy
);
1140 const IMAGE_DATA_DIRECTORY
& exportDirectoryEntry
=
1141 optionalHeader
.DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
];
1142 if (!exportDirectoryEntry
.VirtualAddress
|| !exportDirectoryEntry
.Size
) {
1143 return PEExportSection(aMMPolicy
);
1146 mozilla::interceptor::TargetObject
<MMPolicy
, IMAGE_EXPORT_DIRECTORY
>
1147 exportDirectory(aMMPolicy
,
1148 aImageBase
+ exportDirectoryEntry
.VirtualAddress
);
1149 if (!exportDirectory
|| !exportDirectory
->NumberOfFunctions
) {
1150 return PEExportSection(aMMPolicy
);
1153 return PEExportSection(
1154 aMMPolicy
, aImageBase
, exportDirectoryEntry
.VirtualAddress
,
1155 exportDirectoryEntry
.VirtualAddress
+ exportDirectoryEntry
.Size
,
1156 *exportDirectory
.GetLocalBase());
1159 FARPROC
GetProcAddressByOrdinal(WORD aOrdinal
) const {
1160 if (aOrdinal
< mOrdinalBase
) {
1164 auto rvaToFunction
= mExportAddressTable
[aOrdinal
- mOrdinalBase
];
1165 if (!rvaToFunction
) {
1168 return reinterpret_cast<FARPROC
>(mImageBase
+ *rvaToFunction
);
1172 static const PEExportSection
Get(HMODULE aModule
, const MMPolicy
& aMMPolicy
) {
1173 return Get(PEHeaders::HModuleToBaseAddr
<uintptr_t>(aModule
), aMMPolicy
);
1176 explicit operator bool() const {
1177 // Because PEExportSection doesn't use MMPolicy::Reserve(), a boolified
1178 // mMMPolicy is expected to be false. We don't check mMMPolicy here.
1179 return mImageBase
&& mRvaDirStart
&& mRvaDirEnd
&& mExportAddressTable
&&
1180 mExportNameTable
&& mExportOrdinalTable
;
1183 template <typename T
>
1184 T
RVAToPtr(uint32_t aRva
) const {
1185 return reinterpret_cast<T
>(mImageBase
+ aRva
);
1188 PIMAGE_EXPORT_DIRECTORY
GetExportDirectory() const {
1193 return RVAToPtr
<PIMAGE_EXPORT_DIRECTORY
>(mRvaDirStart
);
1197 * This functions searches the export table for a given string as
1198 * GetProcAddress does, but this returns a matched entry of the Export
1199 * Address Table i.e. a pointer to an RVA of a matched function instead
1200 * of a function address. If the entry is forwarded, this function
1203 const DWORD
* FindExportAddressTableEntry(
1204 const char* aFunctionNameASCII
) const {
1205 if (!*this || !aFunctionNameASCII
) {
1209 struct NameTableComparator
{
1210 NameTableComparator(const PEExportSection
<MMPolicy
>& aExportSection
,
1211 const char* aTarget
)
1212 : mExportSection(aExportSection
),
1213 mTargetName(aTarget
),
1214 mTargetNamelength(StrlenASCII(aTarget
)) {}
1216 int operator()(DWORD aRVAToString
) const {
1217 mozilla::interceptor::TargetObjectArray
<MMPolicy
, char> itemString(
1218 mExportSection
.mMMPolicy
, mExportSection
.mImageBase
+ aRVAToString
,
1219 mTargetNamelength
+ 1);
1220 return StrcmpASCII(mTargetName
, itemString
[0]);
1223 const PEExportSection
<MMPolicy
>& mExportSection
;
1224 const char* mTargetName
;
1225 size_t mTargetNamelength
;
1228 const NameTableComparator
comp(*this, aFunctionNameASCII
);
1231 if (!mExportNameTable
.BinarySearchIf(comp
, &match
)) {
1235 const WORD
* index
= mExportOrdinalTable
[match
];
1240 const DWORD
* rvaToFunction
= mExportAddressTable
[*index
];
1241 if (!rvaToFunction
) {
1245 if (*rvaToFunction
>= mRvaDirStart
&& *rvaToFunction
< mRvaDirEnd
) {
1246 // If an entry points to an address within the export section, the
1247 // field is a forwarder RVA. We return nullptr because the entry is
1248 // not a function address but a null-terminated string used for export
1253 return rvaToFunction
;
1257 * This functions behaves the same as the native ::GetProcAddress except
1258 * the following cases:
1259 * - Returns nullptr if a target entry is forwarded to another dll.
1261 FARPROC
GetProcAddress(const char* aFunctionNameASCII
) const {
1262 uintptr_t maybeOdrinal
= reinterpret_cast<uintptr_t>(aFunctionNameASCII
);
1263 // When the high-order word of |aFunctionNameASCII| is zero, it's not
1264 // a string but an ordinal value.
1265 if (maybeOdrinal
< 0x10000) {
1266 return GetProcAddressByOrdinal(static_cast<WORD
>(maybeOdrinal
));
1269 auto rvaToFunction
= FindExportAddressTableEntry(aFunctionNameASCII
);
1270 if (!rvaToFunction
) {
1273 return reinterpret_cast<FARPROC
>(mImageBase
+ *rvaToFunction
);
1277 inline HANDLE
RtlGetProcessHeap() {
1278 PTEB teb
= ::NtCurrentTeb();
1279 PPEB peb
= teb
->ProcessEnvironmentBlock
;
1280 return peb
->Reserved4
[1];
1283 inline PVOID
RtlGetThreadLocalStoragePointer() {
1284 return ::NtCurrentTeb()->Reserved1
[11];
1287 inline void RtlSetThreadLocalStoragePointerForTestingOnly(PVOID aNewValue
) {
1288 ::NtCurrentTeb()->Reserved1
[11] = aNewValue
;
1291 inline DWORD
RtlGetCurrentThreadId() {
1292 PTEB teb
= ::NtCurrentTeb();
1293 CLIENT_ID
* cid
= reinterpret_cast<CLIENT_ID
*>(&teb
->Reserved1
[8]);
1294 return static_cast<DWORD
>(reinterpret_cast<uintptr_t>(cid
->UniqueThread
) &
1298 inline PVOID
RtlGetThreadStackBase() {
1299 return reinterpret_cast<_NT_TIB
*>(::NtCurrentTeb())->StackBase
;
1302 inline PVOID
RtlGetThreadStackLimit() {
1303 return reinterpret_cast<_NT_TIB
*>(::NtCurrentTeb())->StackLimit
;
1306 const HANDLE kCurrentProcess
= reinterpret_cast<HANDLE
>(-1);
1308 inline LauncherResult
<DWORD
> GetParentProcessId() {
1309 struct PROCESS_BASIC_INFORMATION
{
1310 NTSTATUS ExitStatus
;
1311 PPEB PebBaseAddress
;
1312 ULONG_PTR AffinityMask
;
1314 ULONG_PTR UniqueProcessId
;
1315 ULONG_PTR InheritedFromUniqueProcessId
;
1319 PROCESS_BASIC_INFORMATION pbi
= {};
1321 ::NtQueryInformationProcess(kCurrentProcess
, ProcessBasicInformation
,
1322 &pbi
, sizeof(pbi
), &returnLength
);
1323 if (!NT_SUCCESS(status
)) {
1324 return LAUNCHER_ERROR_FROM_NTSTATUS(status
);
1327 return static_cast<DWORD
>(pbi
.InheritedFromUniqueProcessId
& 0xFFFFFFFF);
1330 inline SIZE_T WINAPI
VirtualQueryEx(HANDLE aProcess
, LPCVOID aAddress
,
1331 PMEMORY_BASIC_INFORMATION aMemInfo
,
1332 SIZE_T aMemInfoLen
) {
1333 #if defined(MOZILLA_INTERNAL_API)
1334 return ::VirtualQueryEx(aProcess
, aAddress
, aMemInfo
, aMemInfoLen
);
1336 SIZE_T returnedLength
;
1337 NTSTATUS status
= ::NtQueryVirtualMemory(
1338 aProcess
, const_cast<PVOID
>(aAddress
), MemoryBasicInformation
, aMemInfo
,
1339 aMemInfoLen
, &returnedLength
);
1340 if (!NT_SUCCESS(status
)) {
1341 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status
));
1344 return returnedLength
;
1345 #endif // defined(MOZILLA_INTERNAL_API)
1348 inline SIZE_T WINAPI
VirtualQuery(LPCVOID aAddress
,
1349 PMEMORY_BASIC_INFORMATION aMemInfo
,
1350 SIZE_T aMemInfoLen
) {
1351 return nt::VirtualQueryEx(kCurrentProcess
, aAddress
, aMemInfo
, aMemInfoLen
);
1354 struct DataDirectoryEntry
: public _IMAGE_DATA_DIRECTORY
{
1355 DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}
1357 MOZ_IMPLICIT
DataDirectoryEntry(const _IMAGE_DATA_DIRECTORY
& aOther
)
1358 : _IMAGE_DATA_DIRECTORY(aOther
) {}
1360 DataDirectoryEntry(const DataDirectoryEntry
& aOther
) = default;
1362 bool operator==(const DataDirectoryEntry
& aOther
) const {
1363 return VirtualAddress
== aOther
.VirtualAddress
&& Size
== aOther
.Size
;
1366 bool operator!=(const DataDirectoryEntry
& aOther
) const {
1367 return !(*this == aOther
);
1371 inline LauncherResult
<void*> GetProcessPebPtr(HANDLE aProcess
) {
1373 PROCESS_BASIC_INFORMATION pbi
;
1374 NTSTATUS status
= ::NtQueryInformationProcess(
1375 aProcess
, ProcessBasicInformation
, &pbi
, sizeof(pbi
), &returnLength
);
1376 if (!NT_SUCCESS(status
)) {
1377 return LAUNCHER_ERROR_FROM_NTSTATUS(status
);
1380 return pbi
.PebBaseAddress
;
1384 * This function relies on a specific offset into the mostly-undocumented PEB
1385 * structure. The risk is reduced thanks to the fact that the Chromium sandbox
1386 * relies on the location of this field. It is unlikely to change at this point.
1387 * To further reduce the risk, we also check for the magic 'MZ' signature that
1388 * should indicate the beginning of a PE image.
1390 inline LauncherResult
<HMODULE
> GetProcessExeModule(HANDLE aProcess
) {
1391 LauncherResult
<void*> ppeb
= GetProcessPebPtr(aProcess
);
1393 return ppeb
.propagateErr();
1399 #if defined(MOZILLA_INTERNAL_API)
1400 if (!::ReadProcessMemory(aProcess
, ppeb
.unwrap(), &peb
, sizeof(peb
),
1402 bytesRead
!= sizeof(peb
)) {
1403 return LAUNCHER_ERROR_FROM_LAST();
1406 NTSTATUS ntStatus
= ::NtReadVirtualMemory(aProcess
, ppeb
.unwrap(), &peb
,
1407 sizeof(peb
), &bytesRead
);
1408 if (!NT_SUCCESS(ntStatus
) || bytesRead
!= sizeof(peb
)) {
1409 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus
);
1413 // peb.ImageBaseAddress
1414 void* baseAddress
= peb
.Reserved3
[1];
1417 #if defined(MOZILLA_INTERNAL_API)
1418 if (!::ReadProcessMemory(aProcess
, baseAddress
, mzMagic
, sizeof(mzMagic
),
1420 bytesRead
!= sizeof(mzMagic
)) {
1421 return LAUNCHER_ERROR_FROM_LAST();
1424 ntStatus
= ::NtReadVirtualMemory(aProcess
, baseAddress
, mzMagic
,
1425 sizeof(mzMagic
), &bytesRead
);
1426 if (!NT_SUCCESS(ntStatus
) || bytesRead
!= sizeof(mzMagic
)) {
1427 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus
);
1431 MOZ_ASSERT(mzMagic
[0] == 'M' && mzMagic
[1] == 'Z');
1432 if (mzMagic
[0] != 'M' || mzMagic
[1] != 'Z') {
1433 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT
);
1436 return static_cast<HMODULE
>(baseAddress
);
1439 #if defined(_MSC_VER)
1440 extern "C" IMAGE_DOS_HEADER __ImageBase
;
1443 // This class manages data transfer from the local process's executable
1444 // to another process's executable via WriteProcessMemory.
1445 // Bug 1662560 told us the same executable may be mapped onto a different
1446 // address in a different process. This means when we transfer data within
1447 // the mapped executable such as a global variable or IAT from the current
1448 // process to another process, we need to shift its address by the difference
1449 // between two executable's mapped imagebase.
1450 class CrossExecTransferManager final
{
1451 HANDLE mRemoteProcess
;
1452 uint8_t* mLocalImagebase
;
1453 PEHeaders mLocalExec
;
1454 uint8_t* mRemoteImagebase
;
1456 static HMODULE
GetLocalExecModule() {
1457 #if defined(_MSC_VER)
1458 return reinterpret_cast<HMODULE
>(&__ImageBase
);
1460 return ::GetModuleHandleW(nullptr);
1464 LauncherVoidResult
EnsureRemoteImagebase() {
1465 if (!mRemoteImagebase
) {
1466 LauncherResult
<HMODULE
> remoteImageBaseResult
=
1467 GetProcessExeModule(mRemoteProcess
);
1468 if (remoteImageBaseResult
.isErr()) {
1469 return remoteImageBaseResult
.propagateErr();
1473 reinterpret_cast<uint8_t*>(remoteImageBaseResult
.unwrap());
1478 template <typename T
>
1479 T
* LocalExecToRemoteExec(T
* aLocalAddress
) const {
1480 MOZ_ASSERT(mRemoteImagebase
);
1481 MOZ_ASSERT(mLocalExec
.IsWithinImage(aLocalAddress
));
1483 if (!mRemoteImagebase
|| !mLocalExec
.IsWithinImage(aLocalAddress
)) {
1484 return aLocalAddress
;
1487 uintptr_t offset
= reinterpret_cast<uintptr_t>(aLocalAddress
) -
1488 reinterpret_cast<uintptr_t>(mLocalImagebase
);
1489 return reinterpret_cast<T
*>(mRemoteImagebase
+ offset
);
1493 explicit CrossExecTransferManager(HANDLE aRemoteProcess
)
1494 : mRemoteProcess(aRemoteProcess
),
1496 PEHeaders::HModuleToBaseAddr
<uint8_t*>(GetLocalExecModule())),
1497 mLocalExec(mLocalImagebase
),
1498 mRemoteImagebase(nullptr) {}
1500 CrossExecTransferManager(HANDLE aRemoteProcess
, HMODULE aLocalImagebase
)
1501 : mRemoteProcess(aRemoteProcess
),
1503 PEHeaders::HModuleToBaseAddr
<uint8_t*>(aLocalImagebase
)),
1504 mLocalExec(mLocalImagebase
),
1505 mRemoteImagebase(nullptr) {}
1507 explicit operator bool() const { return !!mLocalExec
; }
1508 HANDLE
RemoteProcess() const { return mRemoteProcess
; }
1509 const PEHeaders
& LocalPEHeaders() const { return mLocalExec
; }
1511 AutoVirtualProtect
Protect(void* aLocalAddress
, size_t aLength
,
1513 // If EnsureRemoteImagebase() fails, a subsequent operaion will fail.
1514 Unused
<< EnsureRemoteImagebase();
1515 return AutoVirtualProtect(LocalExecToRemoteExec(aLocalAddress
), aLength
,
1516 aProtFlags
, mRemoteProcess
);
1519 LauncherVoidResult
Transfer(LPVOID aDestinationAddress
,
1520 LPCVOID aBufferToWrite
, SIZE_T aBufferSize
) {
1521 LauncherVoidResult result
= EnsureRemoteImagebase();
1522 if (result
.isErr()) {
1523 return result
.propagateErr();
1526 if (!::WriteProcessMemory(mRemoteProcess
,
1527 LocalExecToRemoteExec(aDestinationAddress
),
1528 aBufferToWrite
, aBufferSize
, nullptr)) {
1529 return LAUNCHER_ERROR_FROM_LAST();
1536 #if !defined(MOZILLA_INTERNAL_API)
1538 inline LauncherResult
<HMODULE
> GetModuleHandleFromLeafName(
1539 const UNICODE_STRING
& aTarget
) {
1540 auto maybePeb
= nt::GetProcessPebPtr(kCurrentProcess
);
1541 if (maybePeb
.isErr()) {
1542 return maybePeb
.propagateErr();
1545 const PPEB peb
= reinterpret_cast<PPEB
>(maybePeb
.unwrap());
1547 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT
);
1550 auto firstItem
= &peb
->Ldr
->InMemoryOrderModuleList
;
1551 for (auto p
= firstItem
->Flink
; p
!= firstItem
; p
= p
->Flink
) {
1552 const auto currentTableEntry
=
1553 CONTAINING_RECORD(p
, LDR_DATA_TABLE_ENTRY
, InMemoryOrderLinks
);
1555 UNICODE_STRING leafName
;
1556 nt::GetLeafName(&leafName
, ¤tTableEntry
->FullDllName
);
1558 if (::RtlCompareUnicodeString(&leafName
, &aTarget
, TRUE
) == 0) {
1559 return reinterpret_cast<HMODULE
>(currentTableEntry
->DllBase
);
1563 return LAUNCHER_ERROR_FROM_WIN32(ERROR_MOD_NOT_FOUND
);
1566 class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS SRWLock final
{
1568 constexpr SRWLock() : mLock(SRWLOCK_INIT
) {}
1570 void LockShared() { ::RtlAcquireSRWLockShared(&mLock
); }
1572 void LockExclusive() { ::RtlAcquireSRWLockExclusive(&mLock
); }
1574 void UnlockShared() { ::RtlReleaseSRWLockShared(&mLock
); }
1576 void UnlockExclusive() { ::RtlReleaseSRWLockExclusive(&mLock
); }
1578 SRWLock(const SRWLock
&) = delete;
1579 SRWLock(SRWLock
&&) = delete;
1580 SRWLock
& operator=(const SRWLock
&) = delete;
1581 SRWLock
& operator=(SRWLock
&&) = delete;
1583 SRWLOCK
* operator&() { return &mLock
; }
1589 class MOZ_RAII AutoExclusiveLock final
{
1591 explicit AutoExclusiveLock(SRWLock
& aLock
) : mLock(aLock
) {
1592 aLock
.LockExclusive();
1595 ~AutoExclusiveLock() { mLock
.UnlockExclusive(); }
1597 AutoExclusiveLock(const AutoExclusiveLock
&) = delete;
1598 AutoExclusiveLock(AutoExclusiveLock
&&) = delete;
1599 AutoExclusiveLock
& operator=(const AutoExclusiveLock
&) = delete;
1600 AutoExclusiveLock
& operator=(AutoExclusiveLock
&&) = delete;
1606 class MOZ_RAII AutoSharedLock final
{
1608 explicit AutoSharedLock(SRWLock
& aLock
) : mLock(aLock
) { aLock
.LockShared(); }
1610 ~AutoSharedLock() { mLock
.UnlockShared(); }
1612 AutoSharedLock(const AutoSharedLock
&) = delete;
1613 AutoSharedLock(AutoSharedLock
&&) = delete;
1614 AutoSharedLock
& operator=(const AutoSharedLock
&) = delete;
1615 AutoSharedLock
& operator=(AutoSharedLock
&&) = delete;
1621 #endif // !defined(MOZILLA_INTERNAL_API)
1623 class RtlAllocPolicy
{
1625 template <typename T
>
1626 T
* maybe_pod_malloc(size_t aNumElems
) {
1627 if (aNumElems
& mozilla::tl::MulOverflowMask
<sizeof(T
)>::value
) {
1631 return static_cast<T
*>(
1632 ::RtlAllocateHeap(RtlGetProcessHeap(), 0, aNumElems
* sizeof(T
)));
1635 template <typename T
>
1636 T
* maybe_pod_calloc(size_t aNumElems
) {
1637 if (aNumElems
& mozilla::tl::MulOverflowMask
<sizeof(T
)>::value
) {
1641 return static_cast<T
*>(::RtlAllocateHeap(
1642 RtlGetProcessHeap(), HEAP_ZERO_MEMORY
, aNumElems
* sizeof(T
)));
1645 template <typename T
>
1646 T
* maybe_pod_realloc(T
* aPtr
, size_t aOldSize
, size_t aNewSize
) {
1647 if (aNewSize
& mozilla::tl::MulOverflowMask
<sizeof(T
)>::value
) {
1651 return static_cast<T
*>(::RtlReAllocateHeap(RtlGetProcessHeap(), 0, aPtr
,
1652 aNewSize
* sizeof(T
)));
1655 template <typename T
>
1656 T
* pod_malloc(size_t aNumElems
) {
1657 return maybe_pod_malloc
<T
>(aNumElems
);
1660 template <typename T
>
1661 T
* pod_calloc(size_t aNumElems
) {
1662 return maybe_pod_calloc
<T
>(aNumElems
);
1665 template <typename T
>
1666 T
* pod_realloc(T
* aPtr
, size_t aOldSize
, size_t aNewSize
) {
1667 return maybe_pod_realloc
<T
>(aPtr
, aOldSize
, aNewSize
);
1670 template <typename T
>
1671 void free_(T
* aPtr
, size_t aNumElems
= 0) {
1672 ::RtlFreeHeap(RtlGetProcessHeap(), 0, aPtr
);
1675 void reportAllocOverflow() const {}
1677 [[nodiscard
]] bool checkSimulatedOOM() const { return true; }
1680 class AutoMappedView final
{
1688 #if defined(MOZILLA_INTERNAL_API)
1689 ::UnmapViewOfFile(mView
);
1691 NTSTATUS status
= ::NtUnmapViewOfSection(nt::kCurrentProcess
, mView
);
1692 if (!NT_SUCCESS(status
)) {
1693 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status
));
1700 explicit AutoMappedView(void* aView
) : mView(aView
) {}
1702 AutoMappedView(HANDLE aSection
, ULONG aProtectionFlags
) : mView(nullptr) {
1703 #if defined(MOZILLA_INTERNAL_API)
1704 mView
= ::MapViewOfFile(aSection
, aProtectionFlags
, 0, 0, 0);
1706 SIZE_T viewSize
= 0;
1707 NTSTATUS status
= ::NtMapViewOfSection(aSection
, nt::kCurrentProcess
,
1708 &mView
, 0, 0, nullptr, &viewSize
,
1709 ViewUnmap
, 0, aProtectionFlags
);
1710 if (!NT_SUCCESS(status
)) {
1711 ::RtlSetLastWin32Error(::RtlNtStatusToDosError(status
));
1715 ~AutoMappedView() { Unmap(); }
1717 // Allow move & Disallow copy
1718 AutoMappedView(AutoMappedView
&& aOther
) : mView(aOther
.mView
) {
1719 aOther
.mView
= nullptr;
1721 AutoMappedView
& operator=(AutoMappedView
&& aOther
) {
1722 if (this != &aOther
) {
1724 mView
= aOther
.mView
;
1725 aOther
.mView
= nullptr;
1729 AutoMappedView(const AutoMappedView
&) = delete;
1730 AutoMappedView
& operator=(const AutoMappedView
&) = delete;
1732 explicit operator bool() const { return !!mView
; }
1733 template <typename T
>
1735 return reinterpret_cast<T
*>(mView
);
1746 // CheckStack ensures that stack memory pages are committed up to a given size
1747 // in bytes from the current stack pointer. It updates the thread stack limit,
1748 // which points to the lowest committed stack address.
1749 MOZ_NEVER_INLINE MOZ_NAKED
inline void CheckStack(uint32_t size
) {
1752 # if defined(__MINGW32__)
1756 # endif // __MINGW32__
1762 } // namespace mozilla
1764 #endif // mozilla_NativeNt_h