/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#ifdefined(MOZILLA_INTERNAL_API) # include "nsString.h" #endif// defined(MOZILLA_INTERNAL_API)
// The declarations within this #if block are intended to be used for initial // process initialization ONLY. You probably don't want to be using these in // normal Gecko code! #if !defined(MOZILLA_INTERNAL_API)
// NB: When allocating, space for the buffer must also be included typedefstruct _MEMORY_SECTION_NAME {
UNICODE_STRING mSectionFileName;
} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
/** * This class encapsulates a UNICODE_STRING that owns its own buffer. The * buffer is always NULL terminated, thus allowing us to cast to a wide C-string * without requiring any mutation. * * We only allow creation of this owned buffer from outside XUL.
*/ class AllocatedUnicodeString final { public:
AllocatedUnicodeString() : mUnicodeString() {}
// We can use nsDependentString here as we guaranteed null termination // when we allocated the string. return nsDependentString(mUnicodeString.Buffer, CharLen());
} #endif// defined(MOZILLA_INTERNAL_API)
// We duplicate with null termination so that this string may be used // as a wide C-string without any further manipulation.
NTSTATUS ntStatus = ::RtlDuplicateUnicodeString(
RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE, aSrc, &mUnicodeString);
MOZ_ASSERT(NT_SUCCESS(ntStatus)); if (!NT_SUCCESS(ntStatus)) { // Make sure that mUnicodeString does not contain bogus data // (since not all callers zero it out before invoking)
mUnicodeString = {};
}
}
// We cannot use default copy here because mSectionFileName.Buffer needs to // be updated to point to |this->mBuf|, not |aOther.mBuf|.
MemorySectionNameBuf& operator=(const MemorySectionNameBuf& aOther) {
mSectionFileName.Length = aOther.mSectionFileName.Length;
mSectionFileName.MaximumLength = sizeof(mBuf);
MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
mSectionFileName.Buffer = mBuf;
memcpy(mBuf, aOther.mBuf, aOther.mSectionFileName.Length); return *this;
}
inlinebool MatchUnicodeString(const UNICODE_STRING& aStr, bool (*aPredicate)(WCHAR)) {
WCHAR* cur = aStr.Buffer;
WCHAR* end = &aStr.Buffer[aStr.Length / sizeof(WCHAR)]; while (cur < end) { if (!aPredicate(*cur)) { returnfalse;
}
++cur;
}
returntrue;
}
inlinebool Contains12DigitHexString(const UNICODE_STRING& aLeafName) { // Quick check: If the string is too short, don't bother // (We need at least 12 hex digits, one char for '.', and 3 for extension) const USHORT kMinLen = (12 + 1 + 3) * sizeof(wchar_t); if (aLeafName.Length < kMinLen) { returnfalse;
}
uint16_t start, end; if (!FindCharInUnicodeString(aLeafName, L'.', start)) { returnfalse;
}
++start; if (!FindCharInUnicodeString(aLeafName, L'.', end, start)) { returnfalse;
}
inlinebool IsFileNameAtLeast16HexDigits(const UNICODE_STRING& aLeafName) { // Quick check: If the string is too short, don't bother // (We need 16 hex digits, one char for '.', and 3 for extension) const USHORT kMinLen = (16 + 1 + 3) * sizeof(wchar_t); if (aLeafName.Length < kMinLen) { returnfalse;
}
uint16_t dotIndex; if (!FindCharInUnicodeString(aLeafName, L'.', dotIndex)) { returnfalse;
}
inlinevoid GetLeafName(PUNICODE_STRING aDestString,
PCUNICODE_STRING aSrcString) {
WCHAR* buf = aSrcString->Buffer;
WCHAR* end = &aSrcString->Buffer[(aSrcString->Length / sizeof(WCHAR)) - 1];
WCHAR* cur = end; while (cur >= buf) { if (*cur == L'\\') { break;
}
--cur;
}
// At this point, either cur points to the final backslash, or it points to // buf - 1. Either way, we're interested in cur + 1 as the desired buffer.
aDestString->Buffer = cur + 1;
aDestString->Length = (end - aDestString->Buffer + 1) * sizeof(WCHAR);
aDestString->MaximumLength = aDestString->Length;
}
#endif// !defined(MOZILLA_INTERNAL_API)
#ifdefined(MOZILLA_INTERNAL_API)
inlineconst nsDependentSubstring GetLeafName(const nsAString& aString) { auto it = aString.EndReading();
size_t pos = aString.Length(); while (it > aString.BeginReading()) { if (*(it - 1) == u'\\') { return Substring(aString, pos);
}
MOZ_ASSERT(pos > 0);
--pos;
--it;
}
return Substring(aString, 0); // No backslash in the string
}
class MOZ_RAII PEHeaders final { /** * This structure is documented on MSDN as VS_VERSIONINFO, but is not present * in SDK headers because it cannot be specified as a C struct. The following * structure contains the fixed-length fields at the beginning of * VS_VERSIONINFO.
*/ struct VS_VERSIONINFO_HEADER {
WORD wLength;
WORD wValueLength;
WORD wType;
WCHAR szKey[16]; // std::size(L"VS_VERSION_INFO") // Additional data goes here, aligned on a 4-byte boundary
};
public: // The lowest two bits of an HMODULE are used as flags. Stripping those bits // from the HMODULE yields the base address of the binary's memory mapping. // (See LoadLibraryEx docs on MSDN) template <typename T> static T HModuleToBaseAddr(HMODULE aModule) { returnreinterpret_cast<T>(reinterpret_cast<uintptr_t>(aModule) &
~uintptr_t(3));
}
if (mPeHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) { return;
}
DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage; // This is a coarse-grained check to ensure that the image size is // reasonable. It we aren't big enough to contain headers, we have a // problem! if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) { return;
}
/** * This overload computes absolute virtual addresses relative to the base * address of the binary.
*/ template <typename T, typename R>
T RVAToPtr(R aRva) const { return RVAToPtr<T>(mMzHeader, aRva);
}
/** * This overload computes a result by adding aRva to aBase, but also ensures * that the resulting pointer falls within the bounds of this binary's memory * mapping.
*/ template <typename T, typename R>
T RVAToPtr(void* aBase, R aRva) const { if (!mImageLimit) { return nullptr;
}
PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() const { // If the import directory is already tampered, we skip bounds check // because it could be located outside the mapped image. return mIsImportDirectoryTampered
? GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR,
BoundsCheckPolicy::Skip>(
IMAGE_DIRECTORY_ENTRY_IMPORT)
: GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR>(
IMAGE_DIRECTORY_ENTRY_IMPORT);
}
bool GetVersionInfo(uint64_t& aOutVersion) const { // RT_VERSION == 16 // Version resources require an id of 1 auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1); if (!root) { returnfalse;
}
VS_FIXEDFILEINFO* fixedInfo = GetFixedFileInfo(root); if (!fixedInfo) { returnfalse;
}
PIMAGE_IMPORT_DESCRIPTOR
GetImportDescriptor(constchar* aModuleNameASCII) const { for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
IsValid(curImpDesc); ++curImpDesc) { auto curName = mIsImportDirectoryTampered
? RVAToPtrUnchecked<constchar*>(curImpDesc->Name)
: RVAToPtr<constchar*>(curImpDesc->Name); if (!curName) { return nullptr;
}
if (StricmpASCII(aModuleNameASCII, curName)) { continue;
}
// curImpDesc now points to the IAT for the module we're interested in return curImpDesc;
}
return nullptr;
}
template <typename CallbackT> void EnumImportChunks(const CallbackT& aCallback) const { for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
IsValid(curImpDesc); ++curImpDesc) { auto curName = mIsImportDirectoryTampered
? RVAToPtrUnchecked<constchar*>(curImpDesc->Name)
: RVAToPtr<constchar*>(curImpDesc->Name); if (!curName) { continue;
}
aCallback(curName);
}
}
/** * If |aBoundaries| is given, this method checks whether each IAT entry is * within the given range, and if any entry is out of the range, we return * Nothing().
*/
Maybe<Span<IMAGE_THUNK_DATA>> GetIATThunksForModule( constchar* aModuleNameASCII, const Range<const uint8_t>* aBoundaries = nullptr) const {
PIMAGE_IMPORT_DESCRIPTOR impDesc = GetImportDescriptor(aModuleNameASCII); if (!impDesc) { return Nothing();
}
auto firstIatThunk =
this->template RVAToPtr<PIMAGE_THUNK_DATA>(impDesc->FirstThunk); if (!firstIatThunk) { return Nothing();
}
// Find the length by iterating through the table until we find a null entry
PIMAGE_THUNK_DATA curIatThunk = firstIatThunk; while (IsValid(curIatThunk)) { if (aBoundaries) { auto iatEntry = reinterpret_cast<const uint8_t*>(curIatThunk->u1.Function); if (iatEntry < aBoundaries->begin().get() ||
iatEntry >= aBoundaries->end().get()) { return Nothing();
}
}
++curIatThunk;
}
return Some(Span(firstIatThunk, curIatThunk));
}
/** * Resources are stored in a three-level tree. To locate a particular entry, * you must supply a resource type, the resource id, and then the language id. * If aLangId == 0, we just resolve the first entry regardless of language.
*/ template <typename T>
T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) const {
PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable(); if (!topLevel) { return nullptr;
}
template <size_t N>
Maybe<Span<const uint8_t>> FindSection(constchar (&aSecName)[N],
DWORD aCharacteristicsMask) const {
static_assert((N - 1) <= IMAGE_SIZEOF_SHORT_NAME, "Section names must be at most 8 characters excluding null " "terminator");
if (!(*this)) { return Nothing();
}
Span<IMAGE_SECTION_HEADER> sectionTable = GetSectionTable(); for (auto&& sectionHeader : sectionTable) { if (strncmp(reinterpret_cast<constchar*>(sectionHeader.Name), aSecName,
IMAGE_SIZEOF_SHORT_NAME)) { continue;
}
if (!(sectionHeader.Characteristics & aCharacteristicsMask)) { // We found the section but it does not have the expected // characteristics return Nothing();
}
DWORD rva = sectionHeader.VirtualAddress; if (!rva) { return Nothing();
}
DWORD size = sectionHeader.Misc.VirtualSize; if (!size) { return Nothing();
}
auto base = RVAToPtr<const uint8_t*>(rva); return Some(Span(base, size));
}
return Nothing();
}
// There may be other code sections in the binary besides .text
Maybe<Span<const uint8_t>> GetTextSectionInfo() const { return FindSection(".text", IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE |
IMAGE_SCN_MEM_READ);
}
// There may be other data sections in the binary besides .data
Maybe<Span<const uint8_t>> GetDataSectionInfo() const { return FindSection(".data", IMAGE_SCN_CNT_INITIALIZED_DATA |
IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE);
}
FARPROC GetEntryPoint() const { // Use the unchecked version because the entrypoint may be tampered. return RVAToPtrUnchecked<FARPROC>(
mPeHeader->OptionalHeader.AddressOfEntryPoint);
}
// This private variant does not have bounds checks, because we need to be // able to resolve the bounds themselves. template <typename T, typename R>
T RVAToPtrUnchecked(R aRva) const { returnreinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
}
Span<IMAGE_SECTION_HEADER> GetSectionTable() const {
MOZ_ASSERT(*this); auto base = RVAToPtr<PIMAGE_SECTION_HEADER>(
&mPeHeader->OptionalHeader, mPeHeader->FileHeader.SizeOfOptionalHeader); // The Windows loader has an internal limit of 96 sections (per PE spec) auto numSections =
std::min(mPeHeader->FileHeader.NumberOfSections, WORD(96)); return Span{base, numSections};
}
PIMAGE_RESOURCE_DIRECTORY_ENTRY
FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) const { if (!aCurLevel) { return nullptr;
}
// Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function // searches by ID, we need to skip past any named entries before iterating. auto dirEnt = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) +
aCurLevel->NumberOfNamedEntries; if (!(IsWithinImage(dirEnt) &&
IsWithinImage(&dirEnt[aCurLevel->NumberOfIdEntries - 1].Id))) { return nullptr;
}
for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) { if (dirEnt[i].Id == aId) { return &dirEnt[i];
}
}
return nullptr;
}
PIMAGE_RESOURCE_DIRECTORY_ENTRY
FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) const { // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first // entry, regardless of whether it is indexed by name or by id. auto dirEnt = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1);
WORD numEntries =
aCurLevel->NumberOfNamedEntries + aCurLevel->NumberOfIdEntries; if (!numEntries) { return nullptr;
}
return dirEnt;
}
VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) const {
WORD length = aVerInfo->wLength; if (length < sizeof(VS_VERSIONINFO_HEADER)) { return nullptr;
}
DWORD imageSize = optionalHeader.SizeOfImage; // This is a coarse-grained check to ensure that the image size is // reasonable. It we aren't big enough to contain headers, we have a // problem! if (imageSize < sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS)) { return PEExportSection(aMMPolicy);
}
if (optionalHeader.NumberOfRvaAndSizes <= IMAGE_DIRECTORY_ENTRY_EXPORT) { return PEExportSection(aMMPolicy);
}
/** * This functions searches the export table for a given string as * GetProcAddress does, but this returns a matched entry of the Export * Address Table i.e. a pointer to an RVA of a matched function instead * of a function address. If the entry is forwarded, this function * returns nullptr.
*/ const DWORD* FindExportAddressTableEntry( constchar* aFunctionNameASCII) const { if (!*this || !aFunctionNameASCII) { return nullptr;
}
if (*rvaToFunction >= mRvaDirStart && *rvaToFunction < mRvaDirEnd) { // If an entry points to an address within the export section, the // field is a forwarder RVA. We return nullptr because the entry is // not a function address but a null-terminated string used for export // forwarding. return nullptr;
}
return rvaToFunction;
}
/** * This functions behaves the same as the native ::GetProcAddress except * the following cases: * - Returns nullptr if a target entry is forwarded to another dll.
*/
FARPROC GetProcAddress(constchar* aFunctionNameASCII) const {
uintptr_t maybeOdrinal = reinterpret_cast<uintptr_t>(aFunctionNameASCII); // When the high-order word of |aFunctionNameASCII| is zero, it's not // a string but an ordinal value. if (maybeOdrinal < 0x10000) { return GetProcAddressByOrdinal(static_cast<WORD>(maybeOdrinal));
}
auto rvaToFunction = FindExportAddressTableEntry(aFunctionNameASCII); if (!rvaToFunction) { return nullptr;
} returnreinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
}
};
/** * This function relies on a specific offset into the mostly-undocumented PEB * structure. The risk is reduced thanks to the fact that the Chromium sandbox * relies on the location of this field. It is unlikely to change at this point. * To further reduce the risk, we also check for the magic 'MZ' signature that * should indicate the beginning of a PE image.
*/ inline LauncherResult<HMODULE> GetProcessExeModule(HANDLE aProcess) {
LauncherResult<void*> ppeb = GetProcessPebPtr(aProcess); if (ppeb.isErr()) { return ppeb.propagateErr();
}
// This class manages data transfer from the local process's executable // to another process's executable via WriteProcessMemory. // Bug 1662560 told us the same executable may be mapped onto a different // address in a different process. This means when we transfer data within // the mapped executable such as a global variable or IAT from the current // process to another process, we need to shift its address by the difference // between two executable's mapped imagebase. class CrossExecTransferManager final {
HANDLE mRemoteProcess;
uint8_t* mLocalImagebase;
PEHeaders mLocalExec;
uint8_t* mRemoteImagebase;
auto firstItem = &peb->Ldr->InMemoryOrderModuleList; for (auto p = firstItem->Flink; p != firstItem; p = p->Flink) { constauto currentTableEntry =
CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
#ifdefined(_M_X64) // CheckStack ensures that stack memory pages are committed up to a given size // in bytes from the current stack pointer. It updates the thread stack limit, // which points to the lowest committed stack address.
MOZ_NEVER_INLINE MOZ_NAKED inlinevoid CheckStack(uint32_t size) { asmvolatile( "mov %ecx, %eax;" # ifdefined(__MINGW32__) "jmp ___chkstk_ms;" # else "jmp __chkstk;" # endif // __MINGW32__
);
} #endif// _M_X64
} // namespace nt
} // namespace mozilla
#endif// mozilla_NativeNt_h
¤ Dauer der Verarbeitung: 0.26 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.