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
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
24 #if !defined(MOZILLA_INTERNAL_API)
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,
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
,
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
,
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
);
88 #endif // !defined(MOZILLA_INTERNAL_API)
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
) {
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
)];
129 if (!aPredicate(*cur
)) {
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
) {
148 if (!FindCharInUnicodeString(aLeafName
, L
'.', start
)) {
153 if (!FindCharInUnicodeString(aLeafName
, L
'.', end
, start
)) {
157 if (end
- start
!= 12) {
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
) {
178 if (!FindCharInUnicodeString(aLeafName
, L
'.', dotIndex
)) {
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];
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') {
224 inline int StricmpASCII(const char* aLeft
, const char* aRight
) {
225 char curLeft
, curRight
;
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
242 struct VS_VERSIONINFO_HEADER
{
246 WCHAR szKey
[16]; // ArrayLength(L"VS_VERSION_INFO")
247 // Additional data goes here, aligned on a 4-byte boundary
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
) {
270 mPeHeader
= RVAToPtrUnchecked
<PIMAGE_NT_HEADERS
>(mMzHeader
->e_lfanew
);
271 if (!mPeHeader
|| mPeHeader
->Signature
!= IMAGE_NT_SIGNATURE
) {
275 if (mPeHeader
->OptionalHeader
.Magic
!= IMAGE_NT_OPTIONAL_HDR_MAGIC
) {
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
283 if (imageSize
< sizeof(IMAGE_DOS_HEADER
) + sizeof(IMAGE_NT_HEADERS
)) {
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
>
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
306 template <typename T
, typename R
>
307 T
RVAToPtr(void* aBase
, R aRva
) {
312 char* absAddress
= reinterpret_cast<char*>(aBase
) + aRva
;
313 if (absAddress
< reinterpret_cast<char*>(mMzHeader
) ||
314 absAddress
> reinterpret_cast<char*>(mImageLimit
)) {
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 {
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
) {
345 PIMAGE_DATA_DIRECTORY dirEntry
=
346 &optionalHeader
.DataDirectory
[aDirectoryIndex
];
348 *aOutRva
= reinterpret_cast<char*>(dirEntry
) -
349 reinterpret_cast<char*>(mMzHeader
);
350 MOZ_ASSERT(*aOutRva
);
356 bool GetVersionInfo(uint64_t& aOutVersion
) {
358 // Version resources require an id of 1
359 auto root
= FindResourceLeaf
<VS_VERSIONINFO_HEADER
*>(16, 1);
364 VS_FIXEDFILEINFO
* fixedInfo
= GetFixedFileInfo(root
);
369 aOutVersion
= ((static_cast<uint64_t>(fixedInfo
->dwFileVersionMS
) << 32) |
370 static_cast<uint64_t>(fixedInfo
->dwFileVersionLS
));
374 bool GetTimeStamp(DWORD
& aResult
) {
379 aResult
= mPeHeader
->FileHeader
.TimeDateStamp
;
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
);
392 if (StricmpASCII(aModuleNameASCII
, curName
)) {
396 // curImpDesc now points to the IAT for the module we're interested in
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
);
422 this->template RVAToPtr
<PIMAGE_THUNK_DATA
>(impDesc
->FirstThunk
);
423 if (!firstIatThunk
) {
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
)) {
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();
449 PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry
=
450 FindResourceEntry(topLevel
, aType
);
451 if (!typeEntry
|| !typeEntry
->DataIsDirectory
) {
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
) {
462 auto langDir
= RVAToPtr
<PIMAGE_RESOURCE_DIRECTORY
>(
463 topLevel
, idEntry
->OffsetToDirectory
);
464 PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry
;
466 langEntry
= FindResourceEntry(langDir
, aLangId
);
468 langEntry
= FindFirstResourceEntry(langDir
);
471 if (!langEntry
|| langEntry
->DataIsDirectory
) {
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;
489 template <typename T
>
490 T
GetImageDirectoryEntry(const uint32_t aDirectoryIndex
) {
491 PIMAGE_DATA_DIRECTORY dirEntry
= GetImageDirectoryEntryPtr(aDirectoryIndex
);
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.
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
) {
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.
529 reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY
>(aCurLevel
+ 1);
531 aCurLevel
->NumberOfNamedEntries
+ aCurLevel
->NumberOfIdEntries
;
539 VS_FIXEDFILEINFO
* GetFixedFileInfo(VS_VERSIONINFO_HEADER
* aVerInfo
) {
540 WORD length
= aVerInfo
->wLength
;
541 if (length
< sizeof(VS_VERSIONINFO_HEADER
)) {
545 const wchar_t kVersionInfoKey
[] = L
"VS_VERSION_INFO";
546 if (::RtlCompareMemory(aVerInfo
->szKey
, kVersionInfoKey
,
547 ArrayLength(kVersionInfoKey
)) !=
548 ArrayLength(kVersionInfoKey
)) {
552 if (aVerInfo
->wValueLength
!= sizeof(VS_FIXEDFILEINFO
)) {
553 // Fixed file info does not exist
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
) {
568 auto result
= reinterpret_cast<VS_FIXEDFILEINFO
*>(base
+ offset
);
569 if (result
->dwSignature
!= 0xFEEF04BD) {
577 PIMAGE_DOS_HEADER mMzHeader
;
578 PIMAGE_NT_HEADERS mPeHeader
;
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
{
592 ULONG_PTR AffinityMask
;
594 ULONG_PTR UniqueProcessId
;
595 ULONG_PTR InheritedFromUniqueProcessId
;
598 const HANDLE kCurrentProcess
= reinterpret_cast<HANDLE
>(-1);
600 PROCESS_BASIC_INFORMATION pbi
= {};
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
) {
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
);
642 return LAUNCHER_ERROR_FROM_RESULT(ppeb
);
648 #if defined(MOZILLA_INTERNAL_API)
649 if (!::ReadProcessMemory(aProcess
, ppeb
.unwrap(), &peb
, sizeof(peb
),
651 bytesRead
!= sizeof(peb
)) {
652 return LAUNCHER_ERROR_FROM_LAST();
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
);
662 // peb.ImageBaseAddress
663 void* baseAddress
= peb
.Reserved3
[1];
666 #if defined(MOZILLA_INTERNAL_API)
667 if (!::ReadProcessMemory(aProcess
, baseAddress
, mzMagic
, sizeof(mzMagic
),
669 bytesRead
!= sizeof(mzMagic
)) {
670 return LAUNCHER_ERROR_FROM_LAST();
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
);
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
);
689 } // namespace mozilla
691 #endif // mozilla_NativeNt_h