Bug 1538144 [wpt PR 15985] - Add tests for pixel-snapping under 3d transform, a=testonly
[gecko.git] / mozglue / misc / NativeNt.h
blobba136aefca9823c7993e38dc8cc0abe89b1cd932
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_NativeNt_h
8 #define mozilla_NativeNt_h
10 #include <stdint.h>
11 #include <windows.h>
12 #include <winnt.h>
13 #include <winternl.h>
15 #include <algorithm>
17 #include "mozilla/ArrayUtils.h"
18 #include "mozilla/Attributes.h"
19 #include "mozilla/LauncherResult.h"
21 // The declarations within this #if block are intended to be used for initial
22 // process initialization ONLY. You probably don't want to be using these in
23 // normal Gecko code!
24 #if !defined(MOZILLA_INTERNAL_API)
26 extern "C" {
28 # if !defined(STATUS_ACCESS_DENIED)
29 # define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
30 # endif // !defined(STATUS_ACCESS_DENIED)
32 # if !defined(STATUS_DLL_NOT_FOUND)
33 # define STATUS_DLL_NOT_FOUND ((NTSTATUS)0xC0000135L)
34 # endif // !defined(STATUS_DLL_NOT_FOUND)
36 enum SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 };
38 NTSTATUS NTAPI NtMapViewOfSection(
39 HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits,
40 SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
41 SECTION_INHERIT aInheritDisposition, ULONG aAllocationType,
42 ULONG aProtectionFlags);
44 NTSTATUS NTAPI NtUnmapViewOfSection(HANDLE aProcess, PVOID aBaseAddress);
46 enum MEMORY_INFORMATION_CLASS {
47 MemoryBasicInformation = 0,
48 MemorySectionName = 2
51 // NB: When allocating, space for the buffer must also be included
52 typedef struct _MEMORY_SECTION_NAME {
53 UNICODE_STRING mSectionFileName;
54 } MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
56 NTSTATUS NTAPI NtQueryVirtualMemory(HANDLE aProcess, PVOID aBaseAddress,
57 MEMORY_INFORMATION_CLASS aMemInfoClass,
58 PVOID aMemInfo, SIZE_T aMemInfoLen,
59 PSIZE_T aReturnLen);
61 LONG NTAPI RtlCompareUnicodeString(PCUNICODE_STRING aStr1,
62 PCUNICODE_STRING aStr2,
63 BOOLEAN aCaseInsensitive);
65 BOOLEAN NTAPI RtlEqualUnicodeString(PCUNICODE_STRING aStr1,
66 PCUNICODE_STRING aStr2,
67 BOOLEAN aCaseInsensitive);
69 NTSTATUS NTAPI RtlGetVersion(PRTL_OSVERSIONINFOW aOutVersionInformation);
71 PVOID NTAPI RtlAllocateHeap(PVOID aHeapHandle, ULONG aFlags, SIZE_T aSize);
73 PVOID NTAPI RtlReAllocateHeap(PVOID aHeapHandle, ULONG aFlags, LPVOID aMem,
74 SIZE_T aNewSize);
76 BOOLEAN NTAPI RtlFreeHeap(PVOID aHeapHandle, ULONG aFlags, PVOID aHeapBase);
78 VOID NTAPI RtlAcquireSRWLockExclusive(PSRWLOCK aLock);
80 VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
82 NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress,
83 PVOID aBuffer, SIZE_T aNumBytesToRead,
84 PSIZE_T aNumBytesRead);
86 } // extern "C"
88 #endif // !defined(MOZILLA_INTERNAL_API)
90 namespace mozilla {
91 namespace nt {
93 #if !defined(MOZILLA_INTERNAL_API)
95 struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME {
96 MemorySectionNameBuf() {
97 mSectionFileName.Length = 0;
98 mSectionFileName.MaximumLength = sizeof(mBuf);
99 mSectionFileName.Buffer = mBuf;
102 WCHAR mBuf[MAX_PATH];
105 inline bool FindCharInUnicodeString(const UNICODE_STRING& aStr, WCHAR aChar,
106 uint16_t& aPos, uint16_t aStartIndex = 0) {
107 const uint16_t aMaxIndex = aStr.Length / sizeof(WCHAR);
109 for (uint16_t curIndex = aStartIndex; curIndex < aMaxIndex; ++curIndex) {
110 if (aStr.Buffer[curIndex] == aChar) {
111 aPos = curIndex;
112 return true;
116 return false;
119 inline bool IsHexDigit(WCHAR aChar) {
120 return (aChar >= L'0' && aChar <= L'9') || (aChar >= L'A' && aChar <= L'F') ||
121 (aChar >= L'a' && aChar <= L'f');
124 inline bool MatchUnicodeString(const UNICODE_STRING& aStr,
125 bool (*aPredicate)(WCHAR)) {
126 WCHAR* cur = aStr.Buffer;
127 WCHAR* end = &aStr.Buffer[aStr.Length / sizeof(WCHAR)];
128 while (cur < end) {
129 if (!aPredicate(*cur)) {
130 return false;
133 ++cur;
136 return true;
139 inline bool Contains12DigitHexString(const UNICODE_STRING& aLeafName) {
140 // Quick check: If the string is too short, don't bother
141 // (We need at least 12 hex digits, one char for '.', and 3 for extension)
142 const USHORT kMinLen = (12 + 1 + 3) * sizeof(wchar_t);
143 if (aLeafName.Length < kMinLen) {
144 return false;
147 uint16_t start, end;
148 if (!FindCharInUnicodeString(aLeafName, L'.', start)) {
149 return false;
152 ++start;
153 if (!FindCharInUnicodeString(aLeafName, L'.', end, start)) {
154 return false;
157 if (end - start != 12) {
158 return false;
161 UNICODE_STRING test;
162 test.Buffer = &aLeafName.Buffer[start];
163 test.Length = (end - start) * sizeof(WCHAR);
164 test.MaximumLength = test.Length;
166 return MatchUnicodeString(test, &IsHexDigit);
169 inline bool IsFileNameAtLeast16HexDigits(const UNICODE_STRING& aLeafName) {
170 // Quick check: If the string is too short, don't bother
171 // (We need 16 hex digits, one char for '.', and 3 for extension)
172 const USHORT kMinLen = (16 + 1 + 3) * sizeof(wchar_t);
173 if (aLeafName.Length < kMinLen) {
174 return false;
177 uint16_t dotIndex;
178 if (!FindCharInUnicodeString(aLeafName, L'.', dotIndex)) {
179 return false;
182 if (dotIndex < 16) {
183 return false;
186 UNICODE_STRING test;
187 test.Buffer = aLeafName.Buffer;
188 test.Length = dotIndex * sizeof(WCHAR);
189 test.MaximumLength = aLeafName.MaximumLength;
191 return MatchUnicodeString(test, &IsHexDigit);
194 inline void GetLeafName(PUNICODE_STRING aDestString,
195 PCUNICODE_STRING aSrcString) {
196 WCHAR* buf = aSrcString->Buffer;
197 WCHAR* end = &aSrcString->Buffer[(aSrcString->Length / sizeof(WCHAR)) - 1];
198 WCHAR* cur = end;
199 while (cur >= buf) {
200 if (*cur == L'\\') {
201 break;
204 --cur;
207 // At this point, either cur points to the final backslash, or it points to
208 // buf - 1. Either way, we're interested in cur + 1 as the desired buffer.
209 aDestString->Buffer = cur + 1;
210 aDestString->Length = (end - aDestString->Buffer + 1) * sizeof(WCHAR);
211 aDestString->MaximumLength = aDestString->Length;
214 #endif // !defined(MOZILLA_INTERNAL_API)
216 inline char EnsureLowerCaseASCII(char aChar) {
217 if (aChar >= 'A' && aChar <= 'Z') {
218 aChar -= 'A' - 'a';
221 return aChar;
224 inline int StricmpASCII(const char* aLeft, const char* aRight) {
225 char curLeft, curRight;
227 do {
228 curLeft = EnsureLowerCaseASCII(*(aLeft++));
229 curRight = EnsureLowerCaseASCII(*(aRight++));
230 } while (curLeft && curLeft == curRight);
232 return curLeft - curRight;
235 class MOZ_RAII PEHeaders final {
237 * This structure is documented on MSDN as VS_VERSIONINFO, but is not present
238 * in SDK headers because it cannot be specified as a C struct. The following
239 * structure contains the fixed-length fields at the beginning of
240 * VS_VERSIONINFO.
242 struct VS_VERSIONINFO_HEADER {
243 WORD wLength;
244 WORD wValueLength;
245 WORD wType;
246 WCHAR szKey[16]; // ArrayLength(L"VS_VERSION_INFO")
247 // Additional data goes here, aligned on a 4-byte boundary
250 public:
251 // The lowest two bits of an HMODULE are used as flags. Stripping those bits
252 // from the HMODULE yields the base address of the binary's memory mapping.
253 // (See LoadLibraryEx docs on MSDN)
254 static PIMAGE_DOS_HEADER HModuleToBaseAddr(HMODULE aModule) {
255 return reinterpret_cast<PIMAGE_DOS_HEADER>(
256 reinterpret_cast<uintptr_t>(aModule) & ~uintptr_t(3));
259 explicit PEHeaders(void* aBaseAddress)
260 : PEHeaders(reinterpret_cast<PIMAGE_DOS_HEADER>(aBaseAddress)) {}
262 explicit PEHeaders(HMODULE aModule) : PEHeaders(HModuleToBaseAddr(aModule)) {}
264 explicit PEHeaders(PIMAGE_DOS_HEADER aMzHeader)
265 : mMzHeader(aMzHeader), mPeHeader(nullptr), mImageLimit(nullptr) {
266 if (!mMzHeader || mMzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
267 return;
270 mPeHeader = RVAToPtrUnchecked<PIMAGE_NT_HEADERS>(mMzHeader->e_lfanew);
271 if (!mPeHeader || mPeHeader->Signature != IMAGE_NT_SIGNATURE) {
272 return;
275 if (mPeHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
276 return;
279 DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage;
280 // This is a coarse-grained check to ensure that the image size is
281 // reasonable. It we aren't big enough to contain headers, we have a
282 // problem!
283 if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) {
284 return;
287 mImageLimit = RVAToPtrUnchecked<void*>(imageSize - 1UL);
290 explicit operator bool() const { return !!mImageLimit; }
293 * This overload computes absolute virtual addresses relative to the base
294 * address of the binary.
296 template <typename T, typename R>
297 T RVAToPtr(R aRva) {
298 return RVAToPtr<T>(mMzHeader, aRva);
302 * This overload computes a result by adding aRva to aBase, but also ensures
303 * that the resulting pointer falls within the bounds of this binary's memory
304 * mapping.
306 template <typename T, typename R>
307 T RVAToPtr(void* aBase, R aRva) {
308 if (!mImageLimit) {
309 return nullptr;
312 char* absAddress = reinterpret_cast<char*>(aBase) + aRva;
313 if (absAddress < reinterpret_cast<char*>(mMzHeader) ||
314 absAddress > reinterpret_cast<char*>(mImageLimit)) {
315 return nullptr;
318 return reinterpret_cast<T>(absAddress);
321 PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() {
322 return GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR>(
323 IMAGE_DIRECTORY_ENTRY_IMPORT);
326 PIMAGE_RESOURCE_DIRECTORY GetResourceTable() {
327 return GetImageDirectoryEntry<PIMAGE_RESOURCE_DIRECTORY>(
328 IMAGE_DIRECTORY_ENTRY_RESOURCE);
331 PIMAGE_DATA_DIRECTORY GetImageDirectoryEntryPtr(
332 const uint32_t aDirectoryIndex, uint32_t* aOutRva = nullptr) const {
333 if (aOutRva) {
334 *aOutRva = 0;
337 IMAGE_OPTIONAL_HEADER& optionalHeader = mPeHeader->OptionalHeader;
339 const uint32_t maxIndex = std::min(optionalHeader.NumberOfRvaAndSizes,
340 DWORD(IMAGE_NUMBEROF_DIRECTORY_ENTRIES));
341 if (aDirectoryIndex >= maxIndex) {
342 return nullptr;
345 PIMAGE_DATA_DIRECTORY dirEntry =
346 &optionalHeader.DataDirectory[aDirectoryIndex];
347 if (aOutRva) {
348 *aOutRva = reinterpret_cast<char*>(dirEntry) -
349 reinterpret_cast<char*>(mMzHeader);
350 MOZ_ASSERT(*aOutRva);
353 return dirEntry;
356 bool GetVersionInfo(uint64_t& aOutVersion) {
357 // RT_VERSION == 16
358 // Version resources require an id of 1
359 auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1);
360 if (!root) {
361 return false;
364 VS_FIXEDFILEINFO* fixedInfo = GetFixedFileInfo(root);
365 if (!fixedInfo) {
366 return false;
369 aOutVersion = ((static_cast<uint64_t>(fixedInfo->dwFileVersionMS) << 32) |
370 static_cast<uint64_t>(fixedInfo->dwFileVersionLS));
371 return true;
374 bool GetTimeStamp(DWORD& aResult) {
375 if (!(*this)) {
376 return false;
379 aResult = mPeHeader->FileHeader.TimeDateStamp;
380 return true;
383 PIMAGE_IMPORT_DESCRIPTOR
384 GetIATForModule(const char* aModuleNameASCII) {
385 for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
386 IsValid(curImpDesc); ++curImpDesc) {
387 auto curName = RVAToPtr<const char*>(curImpDesc->Name);
388 if (!curName) {
389 return nullptr;
392 if (StricmpASCII(aModuleNameASCII, curName)) {
393 continue;
396 // curImpDesc now points to the IAT for the module we're interested in
397 return curImpDesc;
400 return nullptr;
403 struct IATThunks {
404 IATThunks(PIMAGE_THUNK_DATA aFirstThunk, ptrdiff_t aNumThunks)
405 : mFirstThunk(aFirstThunk), mNumThunks(aNumThunks) {}
407 size_t Length() const {
408 return size_t(mNumThunks) * sizeof(IMAGE_THUNK_DATA);
411 PIMAGE_THUNK_DATA mFirstThunk;
412 ptrdiff_t mNumThunks;
415 Maybe<IATThunks> GetIATThunksForModule(const char* aModuleNameASCII) {
416 PIMAGE_IMPORT_DESCRIPTOR impDesc = GetIATForModule(aModuleNameASCII);
417 if (!impDesc) {
418 return Nothing();
421 auto firstIatThunk =
422 this->template RVAToPtr<PIMAGE_THUNK_DATA>(impDesc->FirstThunk);
423 if (!firstIatThunk) {
424 return Nothing();
427 // Find the length by iterating through the table until we find a null entry
428 PIMAGE_THUNK_DATA curIatThunk = firstIatThunk;
429 while (IsValid(curIatThunk)) {
430 ++curIatThunk;
433 ptrdiff_t thunkCount = curIatThunk - firstIatThunk;
434 return Some(IATThunks(firstIatThunk, thunkCount));
438 * Resources are stored in a three-level tree. To locate a particular entry,
439 * you must supply a resource type, the resource id, and then the language id.
440 * If aLangId == 0, we just resolve the first entry regardless of language.
442 template <typename T>
443 T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) {
444 PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable();
445 if (!topLevel) {
446 return nullptr;
449 PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry =
450 FindResourceEntry(topLevel, aType);
451 if (!typeEntry || !typeEntry->DataIsDirectory) {
452 return nullptr;
455 auto idDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
456 topLevel, typeEntry->OffsetToDirectory);
457 PIMAGE_RESOURCE_DIRECTORY_ENTRY idEntry = FindResourceEntry(idDir, aResId);
458 if (!idEntry || !idEntry->DataIsDirectory) {
459 return nullptr;
462 auto langDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
463 topLevel, idEntry->OffsetToDirectory);
464 PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry;
465 if (aLangId) {
466 langEntry = FindResourceEntry(langDir, aLangId);
467 } else {
468 langEntry = FindFirstResourceEntry(langDir);
471 if (!langEntry || langEntry->DataIsDirectory) {
472 return nullptr;
475 auto dataEntry =
476 RVAToPtr<PIMAGE_RESOURCE_DATA_ENTRY>(topLevel, langEntry->OffsetToData);
477 return RVAToPtr<T>(dataEntry->OffsetToData);
480 static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc) {
481 return aImpDesc && aImpDesc->OriginalFirstThunk != 0;
484 static bool IsValid(PIMAGE_THUNK_DATA aImgThunk) {
485 return aImgThunk && aImgThunk->u1.Ordinal != 0;
488 private:
489 template <typename T>
490 T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) {
491 PIMAGE_DATA_DIRECTORY dirEntry = GetImageDirectoryEntryPtr(aDirectoryIndex);
492 if (!dirEntry) {
493 return nullptr;
496 return RVAToPtr<T>(dirEntry->VirtualAddress);
499 // This private variant does not have bounds checks, because we need to be
500 // able to resolve the bounds themselves.
501 template <typename T, typename R>
502 T RVAToPtrUnchecked(R aRva) {
503 return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
506 PIMAGE_RESOURCE_DIRECTORY_ENTRY
507 FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) {
508 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
509 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
510 // searches by ID, we need to skip past any named entries before iterating.
511 auto dirEnt =
512 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) +
513 aCurLevel->NumberOfNamedEntries;
514 for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) {
515 if (dirEnt[i].Id == aId) {
516 return &dirEnt[i];
520 return nullptr;
523 PIMAGE_RESOURCE_DIRECTORY_ENTRY
524 FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) {
525 // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
526 // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first
527 // entry, regardless of whether it is indexed by name or by id.
528 auto dirEnt =
529 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1);
530 WORD numEntries =
531 aCurLevel->NumberOfNamedEntries + aCurLevel->NumberOfIdEntries;
532 if (!numEntries) {
533 return nullptr;
536 return dirEnt;
539 VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) {
540 WORD length = aVerInfo->wLength;
541 if (length < sizeof(VS_VERSIONINFO_HEADER)) {
542 return nullptr;
545 const wchar_t kVersionInfoKey[] = L"VS_VERSION_INFO";
546 if (::RtlCompareMemory(aVerInfo->szKey, kVersionInfoKey,
547 ArrayLength(kVersionInfoKey)) !=
548 ArrayLength(kVersionInfoKey)) {
549 return nullptr;
552 if (aVerInfo->wValueLength != sizeof(VS_FIXEDFILEINFO)) {
553 // Fixed file info does not exist
554 return nullptr;
557 WORD offset = sizeof(VS_VERSIONINFO_HEADER);
559 uintptr_t base = reinterpret_cast<uintptr_t>(aVerInfo);
560 // Align up to 4-byte boundary
561 #pragma warning(suppress : 4146)
562 offset += (-(base + offset) & 3);
564 if (offset >= length) {
565 return nullptr;
568 auto result = reinterpret_cast<VS_FIXEDFILEINFO*>(base + offset);
569 if (result->dwSignature != 0xFEEF04BD) {
570 return nullptr;
573 return result;
576 private:
577 PIMAGE_DOS_HEADER mMzHeader;
578 PIMAGE_NT_HEADERS mPeHeader;
579 void* mImageLimit;
582 inline HANDLE RtlGetProcessHeap() {
583 PTEB teb = ::NtCurrentTeb();
584 PPEB peb = teb->ProcessEnvironmentBlock;
585 return peb->Reserved4[1];
588 inline LauncherResult<DWORD> GetParentProcessId() {
589 struct PROCESS_BASIC_INFORMATION {
590 NTSTATUS ExitStatus;
591 PPEB PebBaseAddress;
592 ULONG_PTR AffinityMask;
593 LONG BasePriority;
594 ULONG_PTR UniqueProcessId;
595 ULONG_PTR InheritedFromUniqueProcessId;
598 const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
599 ULONG returnLength;
600 PROCESS_BASIC_INFORMATION pbi = {};
601 NTSTATUS status =
602 ::NtQueryInformationProcess(kCurrentProcess, ProcessBasicInformation,
603 &pbi, sizeof(pbi), &returnLength);
604 if (!NT_SUCCESS(status)) {
605 return LAUNCHER_ERROR_FROM_NTSTATUS(status);
608 return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF);
611 struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY {
612 DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}
614 MOZ_IMPLICIT DataDirectoryEntry(const _IMAGE_DATA_DIRECTORY& aOther)
615 : _IMAGE_DATA_DIRECTORY(aOther) {}
617 DataDirectoryEntry(const DataDirectoryEntry& aOther) = default;
620 inline LauncherResult<void*> GetProcessPebPtr(HANDLE aProcess) {
621 ULONG returnLength;
622 PROCESS_BASIC_INFORMATION pbi;
623 NTSTATUS status = ::NtQueryInformationProcess(
624 aProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &returnLength);
625 if (!NT_SUCCESS(status)) {
626 return LAUNCHER_ERROR_FROM_NTSTATUS(status);
629 return pbi.PebBaseAddress;
633 * This function relies on a specific offset into the mostly-undocumented PEB
634 * structure. The risk is reduced thanks to the fact that the Chromium sandbox
635 * relies on the location of this field. It is unlikely to change at this point.
636 * To further reduce the risk, we also check for the magic 'MZ' signature that
637 * should indicate the beginning of a PE image.
639 inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) {
640 LauncherResult<void*> ppeb = GetProcessPebPtr(aProcess);
641 if (ppeb.isErr()) {
642 return LAUNCHER_ERROR_FROM_RESULT(ppeb);
645 PEB peb;
646 SIZE_T bytesRead;
648 #if defined(MOZILLA_INTERNAL_API)
649 if (!::ReadProcessMemory(aProcess, ppeb.unwrap(), &peb, sizeof(peb),
650 &bytesRead) ||
651 bytesRead != sizeof(peb)) {
652 return LAUNCHER_ERROR_FROM_LAST();
654 #else
655 NTSTATUS ntStatus = ::NtReadVirtualMemory(aProcess, ppeb.unwrap(), &peb,
656 sizeof(peb), &bytesRead);
657 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(peb)) {
658 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
660 #endif
662 // peb.ImageBaseAddress
663 void* baseAddress = peb.Reserved3[1];
665 char mzMagic[2];
666 #if defined(MOZILLA_INTERNAL_API)
667 if (!::ReadProcessMemory(aProcess, baseAddress, mzMagic, sizeof(mzMagic),
668 &bytesRead) ||
669 bytesRead != sizeof(mzMagic)) {
670 return LAUNCHER_ERROR_FROM_LAST();
672 #else
673 ntStatus = ::NtReadVirtualMemory(aProcess, baseAddress, mzMagic,
674 sizeof(mzMagic), &bytesRead);
675 if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(mzMagic)) {
676 return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
678 #endif
680 MOZ_ASSERT(mzMagic[0] == 'M' && mzMagic[1] == 'Z');
681 if (mzMagic[0] != 'M' || mzMagic[1] != 'Z') {
682 return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
685 return static_cast<HMODULE>(baseAddress);
688 } // namespace nt
689 } // namespace mozilla
691 #endif // mozilla_NativeNt_h