2018-06-06 00:19:30 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
|
|
|
#ifndef mozilla_NativeNt_h
|
|
|
|
#define mozilla_NativeNt_h
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <windows.h>
|
|
|
|
#include <winnt.h>
|
|
|
|
#include <winternl.h>
|
|
|
|
|
2019-04-04 00:56:24 +03:00
|
|
|
#include <algorithm>
|
Bug 1609996 - Reorder some includes affected by the previous patches. r=froydnj
This was done by:
This was done by applying:
```
diff --git a/python/mozbuild/mozbuild/code-analysis/mach_commands.py b/python/mozbuild/mozbuild/code-analysis/mach_commands.py
index 789affde7bbf..fe33c4c7d4d1 100644
--- a/python/mozbuild/mozbuild/code-analysis/mach_commands.py
+++ b/python/mozbuild/mozbuild/code-analysis/mach_commands.py
@@ -2007,7 +2007,7 @@ class StaticAnalysis(MachCommandBase):
from subprocess import Popen, PIPE, check_output, CalledProcessError
diff_process = Popen(self._get_clang_format_diff_command(commit), stdout=PIPE)
- args = [sys.executable, clang_format_diff, "-p1", "-binary=%s" % clang_format]
+ args = [sys.executable, clang_format_diff, "-p1", "-binary=%s" % clang_format, '-sort-includes']
if not output_file:
args.append("-i")
```
Then running `./mach clang-format -c <commit-hash>`
Then undoing that patch.
Then running check_spidermonkey_style.py --fixup
Then running `./mach clang-format`
I had to fix four things:
* I needed to move <utility> back down in GuardObjects.h because I was hitting
obscure problems with our system include wrappers like this:
0:03.94 /usr/include/stdlib.h:550:14: error: exception specification in declaration does not match previous declaration
0:03.94 extern void *realloc (void *__ptr, size_t __size)
0:03.94 ^
0:03.94 /home/emilio/src/moz/gecko-2/obj-debug/dist/include/malloc_decls.h:53:1: note: previous declaration is here
0:03.94 MALLOC_DECL(realloc, void*, void*, size_t)
0:03.94 ^
0:03.94 /home/emilio/src/moz/gecko-2/obj-debug/dist/include/mozilla/mozalloc.h:22:32: note: expanded from macro 'MALLOC_DECL'
0:03.94 MOZ_MEMORY_API return_type name##_impl(__VA_ARGS__);
0:03.94 ^
0:03.94 <scratch space>:178:1: note: expanded from here
0:03.94 realloc_impl
0:03.94 ^
0:03.94 /home/emilio/src/moz/gecko-2/obj-debug/dist/include/mozmemory_wrap.h:142:41: note: expanded from macro 'realloc_impl'
0:03.94 #define realloc_impl mozmem_malloc_impl(realloc)
Which I really didn't feel like digging into.
* I had to restore the order of TrustOverrideUtils.h and related files in nss
because the .inc files depend on TrustOverrideUtils.h being included earlier.
* I had to add a missing include to RollingNumber.h
* Also had to partially restore include order in JsepSessionImpl.cpp to avoid
some -WError issues due to some static inline functions being defined in a
header but not used in the rest of the compilation unit.
Differential Revision: https://phabricator.services.mozilla.com/D60327
--HG--
extra : moz-landing-system : lando
2020-01-20 19:19:48 +03:00
|
|
|
#include <utility>
|
2019-04-04 00:56:24 +03:00
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
#include "mozilla/ArrayUtils.h"
|
|
|
|
#include "mozilla/Attributes.h"
|
2019-09-23 23:16:51 +03:00
|
|
|
#include "mozilla/DebugOnly.h"
|
2019-04-26 18:55:11 +03:00
|
|
|
#include "mozilla/Maybe.h"
|
2020-03-03 03:19:41 +03:00
|
|
|
#include "mozilla/Range.h"
|
2019-04-26 18:55:11 +03:00
|
|
|
#include "mozilla/Span.h"
|
2019-09-23 23:16:51 +03:00
|
|
|
#include "mozilla/WinHeaderOnlyUtils.h"
|
Bug 1610790: Part 2 - Implement GetProcAddress for a remote process. r=handyman
This patch adds a function to get an exported function in a remote process.
We need this implementation to address Bug 1604008, Bug 1608645, and Bug 1610790.
When `WindowsDllInterceptor` detours a function in a remote process, we used the
native `GetProcAddress` locally, and then detours the returned address in the
target process. The problem is if the caller's export table was modified, the
address returned from `GetProcAddress` might be invalid in the target process,
which is Bug 1604008.
I implemented `GetProcAddress` depending on both local and remote process image,
but it caused two regressions Bug 1608645 and Bug 1610790 because multiple
applications modify firefox's export table in multiple ways, such as replacing
an entry of EAT, replacing an RVA to Export section, or etc.
With this patch, we can use `PEExportSection<MMPolicy>::GetProcAddress` to get
an exported function in a remote process without relying on any local data so
that it's not impacted by modification of the local export table.
Differential Revision: https://phabricator.services.mozilla.com//D62315
Depends on D62314
2020-02-11 23:21:10 +03:00
|
|
|
#include "mozilla/interceptor/MMPolicies.h"
|
|
|
|
#include "mozilla/interceptor/TargetFunction.h"
|
2019-09-23 23:16:51 +03:00
|
|
|
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
# include "nsString.h"
|
|
|
|
#endif // defined(MOZILLA_INTERNAL_API)
|
2018-11-21 02:49:36 +03:00
|
|
|
|
2019-01-16 02:10:00 +03:00
|
|
|
// 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)
|
2018-06-06 00:19:30 +03:00
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
# if !defined(STATUS_ACCESS_DENIED)
|
|
|
|
# define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
|
|
|
|
# endif // !defined(STATUS_ACCESS_DENIED)
|
|
|
|
|
|
|
|
# if !defined(STATUS_DLL_NOT_FOUND)
|
|
|
|
# define STATUS_DLL_NOT_FOUND ((NTSTATUS)0xC0000135L)
|
|
|
|
# endif // !defined(STATUS_DLL_NOT_FOUND)
|
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
# if !defined(STATUS_UNSUCCESSFUL)
|
|
|
|
# define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
|
|
|
|
# endif // !defined(STATUS_UNSUCCESSFUL)
|
|
|
|
|
2020-11-10 23:52:00 +03:00
|
|
|
# if !defined(STATUS_INFO_LENGTH_MISMATCH)
|
|
|
|
# define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
|
|
|
|
# endif
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
enum SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 };
|
|
|
|
|
|
|
|
NTSTATUS NTAPI NtMapViewOfSection(
|
|
|
|
HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress, ULONG_PTR aZeroBits,
|
|
|
|
SIZE_T aCommitSize, PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
|
|
|
|
SECTION_INHERIT aInheritDisposition, ULONG aAllocationType,
|
|
|
|
ULONG aProtectionFlags);
|
|
|
|
|
|
|
|
NTSTATUS NTAPI NtUnmapViewOfSection(HANDLE aProcess, PVOID aBaseAddress);
|
|
|
|
|
|
|
|
enum MEMORY_INFORMATION_CLASS {
|
|
|
|
MemoryBasicInformation = 0,
|
|
|
|
MemorySectionName = 2
|
|
|
|
};
|
|
|
|
|
|
|
|
// NB: When allocating, space for the buffer must also be included
|
|
|
|
typedef struct _MEMORY_SECTION_NAME {
|
|
|
|
UNICODE_STRING mSectionFileName;
|
|
|
|
} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
|
|
|
|
|
|
|
|
NTSTATUS NTAPI NtQueryVirtualMemory(HANDLE aProcess, PVOID aBaseAddress,
|
|
|
|
MEMORY_INFORMATION_CLASS aMemInfoClass,
|
|
|
|
PVOID aMemInfo, SIZE_T aMemInfoLen,
|
|
|
|
PSIZE_T aReturnLen);
|
|
|
|
|
|
|
|
LONG NTAPI RtlCompareUnicodeString(PCUNICODE_STRING aStr1,
|
|
|
|
PCUNICODE_STRING aStr2,
|
|
|
|
BOOLEAN aCaseInsensitive);
|
|
|
|
|
|
|
|
BOOLEAN NTAPI RtlEqualUnicodeString(PCUNICODE_STRING aStr1,
|
|
|
|
PCUNICODE_STRING aStr2,
|
|
|
|
BOOLEAN aCaseInsensitive);
|
|
|
|
|
|
|
|
NTSTATUS NTAPI RtlGetVersion(PRTL_OSVERSIONINFOW aOutVersionInformation);
|
|
|
|
|
|
|
|
VOID NTAPI RtlAcquireSRWLockExclusive(PSRWLOCK aLock);
|
2019-09-23 23:16:51 +03:00
|
|
|
VOID NTAPI RtlAcquireSRWLockShared(PSRWLOCK aLock);
|
2018-06-06 00:19:30 +03:00
|
|
|
|
|
|
|
VOID NTAPI RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
|
2019-09-23 23:16:51 +03:00
|
|
|
VOID NTAPI RtlReleaseSRWLockShared(PSRWLOCK aLock);
|
2018-06-06 00:19:30 +03:00
|
|
|
|
2023-09-13 17:32:51 +03:00
|
|
|
NTSTATUS NTAPI RtlSleepConditionVariableSRW(
|
|
|
|
PCONDITION_VARIABLE aConditionVariable, PSRWLOCK aSRWLock,
|
|
|
|
PLARGE_INTEGER aTimeOut, ULONG aFlags);
|
|
|
|
VOID NTAPI RtlWakeAllConditionVariable(PCONDITION_VARIABLE aConditionVariable);
|
|
|
|
|
2020-04-08 17:27:01 +03:00
|
|
|
ULONG NTAPI RtlNtStatusToDosError(NTSTATUS aStatus);
|
|
|
|
VOID NTAPI RtlSetLastWin32Error(DWORD aError);
|
2021-01-13 18:13:18 +03:00
|
|
|
DWORD NTAPI RtlGetLastWin32Error();
|
|
|
|
|
|
|
|
VOID NTAPI RtlRunOnceInitialize(PRTL_RUN_ONCE aRunOnce);
|
2020-04-08 17:27:01 +03:00
|
|
|
|
2019-04-12 22:58:01 +03:00
|
|
|
NTSTATUS NTAPI NtReadVirtualMemory(HANDLE aProcessHandle, PVOID aBaseAddress,
|
|
|
|
PVOID aBuffer, SIZE_T aNumBytesToRead,
|
|
|
|
PSIZE_T aNumBytesRead);
|
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
NTSTATUS NTAPI LdrLoadDll(PWCHAR aDllPath, PULONG aFlags,
|
|
|
|
PUNICODE_STRING aDllName, PHANDLE aOutHandle);
|
|
|
|
|
|
|
|
typedef ULONG(NTAPI* PRTL_RUN_ONCE_INIT_FN)(PRTL_RUN_ONCE, PVOID, PVOID*);
|
|
|
|
NTSTATUS NTAPI RtlRunOnceExecuteOnce(PRTL_RUN_ONCE aRunOnce,
|
|
|
|
PRTL_RUN_ONCE_INIT_FN aInitFn,
|
|
|
|
PVOID aContext, PVOID* aParameter);
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
} // extern "C"
|
|
|
|
|
2019-01-16 02:10:00 +03:00
|
|
|
#endif // !defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
extern "C" {
|
|
|
|
PVOID NTAPI RtlAllocateHeap(PVOID aHeapHandle, ULONG aFlags, SIZE_T aSize);
|
|
|
|
|
|
|
|
PVOID NTAPI RtlReAllocateHeap(PVOID aHeapHandle, ULONG aFlags, LPVOID aMem,
|
|
|
|
SIZE_T aNewSize);
|
|
|
|
|
|
|
|
BOOLEAN NTAPI RtlFreeHeap(PVOID aHeapHandle, ULONG aFlags, PVOID aHeapBase);
|
|
|
|
|
|
|
|
BOOLEAN NTAPI RtlQueryPerformanceCounter(LARGE_INTEGER* aPerfCount);
|
|
|
|
|
|
|
|
#define RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE 1
|
|
|
|
#define RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING 2
|
|
|
|
NTSTATUS NTAPI RtlDuplicateUnicodeString(ULONG aFlags, PCUNICODE_STRING aSrc,
|
|
|
|
PUNICODE_STRING aDest);
|
|
|
|
|
|
|
|
VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING aUnicodeString);
|
|
|
|
} // extern "C"
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
namespace mozilla {
|
|
|
|
namespace nt {
|
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
/**
|
|
|
|
* 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() {}
|
|
|
|
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
AllocatedUnicodeString(const AllocatedUnicodeString& aOther) = delete;
|
|
|
|
|
|
|
|
AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) =
|
|
|
|
delete;
|
|
|
|
#else
|
|
|
|
explicit AllocatedUnicodeString(PCUNICODE_STRING aSrc) {
|
|
|
|
if (!aSrc) {
|
|
|
|
mUnicodeString = {};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Duplicate(aSrc);
|
|
|
|
}
|
|
|
|
|
2021-01-13 18:13:18 +03:00
|
|
|
explicit AllocatedUnicodeString(const char* aSrc) {
|
|
|
|
if (!aSrc) {
|
|
|
|
mUnicodeString = {};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Duplicate(aSrc);
|
|
|
|
}
|
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
AllocatedUnicodeString(const AllocatedUnicodeString& aOther) {
|
|
|
|
Duplicate(&aOther.mUnicodeString);
|
|
|
|
}
|
|
|
|
|
|
|
|
AllocatedUnicodeString& operator=(const AllocatedUnicodeString& aOther) {
|
|
|
|
Clear();
|
|
|
|
Duplicate(&aOther.mUnicodeString);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
AllocatedUnicodeString& operator=(PCUNICODE_STRING aSrc) {
|
|
|
|
Clear();
|
|
|
|
Duplicate(aSrc);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
#endif // defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
|
|
|
AllocatedUnicodeString(AllocatedUnicodeString&& aOther)
|
|
|
|
: mUnicodeString(aOther.mUnicodeString) {
|
|
|
|
aOther.mUnicodeString = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
AllocatedUnicodeString& operator=(AllocatedUnicodeString&& aOther) {
|
|
|
|
Clear();
|
|
|
|
mUnicodeString = aOther.mUnicodeString;
|
|
|
|
aOther.mUnicodeString = {};
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
~AllocatedUnicodeString() { Clear(); }
|
|
|
|
|
|
|
|
bool IsEmpty() const {
|
|
|
|
return !mUnicodeString.Buffer || !mUnicodeString.Length;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator PCUNICODE_STRING() const { return &mUnicodeString; }
|
|
|
|
|
|
|
|
operator const WCHAR*() const { return mUnicodeString.Buffer; }
|
|
|
|
|
|
|
|
USHORT CharLen() const { return mUnicodeString.Length / sizeof(WCHAR); }
|
|
|
|
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
nsDependentString AsString() const {
|
|
|
|
if (!mUnicodeString.Buffer) {
|
|
|
|
return nsDependentString();
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
|
|
private:
|
|
|
|
#if !defined(MOZILLA_INTERNAL_API)
|
|
|
|
void Duplicate(PCUNICODE_STRING aSrc) {
|
|
|
|
MOZ_ASSERT(aSrc);
|
|
|
|
|
|
|
|
// 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 = {};
|
|
|
|
}
|
|
|
|
}
|
2021-01-13 18:13:18 +03:00
|
|
|
|
|
|
|
void Duplicate(const char* aSrc) {
|
|
|
|
MOZ_ASSERT(aSrc);
|
|
|
|
|
|
|
|
ANSI_STRING ansiStr;
|
|
|
|
RtlInitAnsiString(&ansiStr, aSrc);
|
|
|
|
NTSTATUS ntStatus =
|
|
|
|
::RtlAnsiStringToUnicodeString(&mUnicodeString, &ansiStr, TRUE);
|
|
|
|
MOZ_ASSERT(NT_SUCCESS(ntStatus));
|
|
|
|
if (!NT_SUCCESS(ntStatus)) {
|
|
|
|
mUnicodeString = {};
|
|
|
|
}
|
|
|
|
}
|
2019-09-23 23:16:51 +03:00
|
|
|
#endif // !defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
|
|
|
void Clear() {
|
|
|
|
if (!mUnicodeString.Buffer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
::RtlFreeUnicodeString(&mUnicodeString);
|
|
|
|
mUnicodeString = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
UNICODE_STRING mUnicodeString;
|
|
|
|
};
|
|
|
|
|
2019-01-16 02:10:00 +03:00
|
|
|
#if !defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME {
|
|
|
|
MemorySectionNameBuf() {
|
|
|
|
mSectionFileName.Length = 0;
|
|
|
|
mSectionFileName.MaximumLength = sizeof(mBuf);
|
|
|
|
mSectionFileName.Buffer = mBuf;
|
|
|
|
}
|
|
|
|
|
2019-12-20 01:11:24 +03:00
|
|
|
MemorySectionNameBuf(const MemorySectionNameBuf& aOther) { *this = aOther; }
|
|
|
|
|
2019-12-23 23:27:38 +03:00
|
|
|
MemorySectionNameBuf(MemorySectionNameBuf&& aOther) {
|
|
|
|
*this = std::move(aOther);
|
|
|
|
}
|
|
|
|
|
2019-12-20 01:11:24 +03:00
|
|
|
// 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);
|
2019-12-23 23:27:38 +03:00
|
|
|
MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
|
2019-12-20 01:11:24 +03:00
|
|
|
mSectionFileName.Buffer = mBuf;
|
|
|
|
memcpy(mBuf, aOther.mBuf, aOther.mSectionFileName.Length);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2019-12-23 23:27:38 +03:00
|
|
|
MemorySectionNameBuf& operator=(MemorySectionNameBuf&& aOther) {
|
|
|
|
mSectionFileName.Length = aOther.mSectionFileName.Length;
|
|
|
|
aOther.mSectionFileName.Length = 0;
|
|
|
|
mSectionFileName.MaximumLength = sizeof(mBuf);
|
|
|
|
MOZ_ASSERT(mSectionFileName.Length <= mSectionFileName.MaximumLength);
|
|
|
|
aOther.mSectionFileName.MaximumLength = sizeof(aOther.mBuf);
|
|
|
|
mSectionFileName.Buffer = mBuf;
|
|
|
|
memmove(mBuf, aOther.mBuf, mSectionFileName.Length);
|
|
|
|
return *this;
|
|
|
|
}
|
2019-12-20 01:11:24 +03:00
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
// Native NT paths, so we can't assume MAX_PATH. Use a larger buffer.
|
|
|
|
WCHAR mBuf[2 * MAX_PATH];
|
2019-11-16 01:53:49 +03:00
|
|
|
|
|
|
|
bool IsEmpty() const {
|
|
|
|
return !mSectionFileName.Buffer || !mSectionFileName.Length;
|
|
|
|
}
|
|
|
|
|
|
|
|
operator PCUNICODE_STRING() const { return &mSectionFileName; }
|
2018-06-06 00:19:30 +03:00
|
|
|
};
|
|
|
|
|
2020-11-10 23:52:00 +03:00
|
|
|
class MemorySectionNameOnHeap {
|
|
|
|
UniquePtr<uint8_t[]> mBuffer;
|
|
|
|
|
|
|
|
MemorySectionNameOnHeap() = default;
|
|
|
|
explicit MemorySectionNameOnHeap(size_t aBufferLen)
|
|
|
|
: mBuffer(MakeUnique<uint8_t[]>(aBufferLen)) {}
|
|
|
|
|
|
|
|
public:
|
|
|
|
static MemorySectionNameOnHeap GetBackingFilePath(HANDLE aProcess,
|
|
|
|
void* aSectionAddr) {
|
|
|
|
SIZE_T bufferLen = MAX_PATH * 2;
|
|
|
|
do {
|
|
|
|
MemorySectionNameOnHeap sectionName(bufferLen);
|
|
|
|
|
|
|
|
SIZE_T requiredBytes;
|
|
|
|
NTSTATUS ntStatus = ::NtQueryVirtualMemory(
|
|
|
|
aProcess, aSectionAddr, MemorySectionName, sectionName.mBuffer.get(),
|
|
|
|
bufferLen, &requiredBytes);
|
|
|
|
if (NT_SUCCESS(ntStatus)) {
|
|
|
|
return sectionName;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ntStatus != STATUS_INFO_LENGTH_MISMATCH ||
|
|
|
|
bufferLen >= requiredBytes) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
bufferLen = requiredBytes;
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
return MemorySectionNameOnHeap();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allow move & Disallow copy
|
|
|
|
MemorySectionNameOnHeap(MemorySectionNameOnHeap&&) = default;
|
|
|
|
MemorySectionNameOnHeap& operator=(MemorySectionNameOnHeap&&) = default;
|
|
|
|
MemorySectionNameOnHeap(const MemorySectionNameOnHeap&) = delete;
|
|
|
|
MemorySectionNameOnHeap& operator=(const MemorySectionNameOnHeap&) = delete;
|
|
|
|
|
|
|
|
PCUNICODE_STRING AsUnicodeString() const {
|
|
|
|
return reinterpret_cast<PCUNICODE_STRING>(mBuffer.get());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
inline bool FindCharInUnicodeString(const UNICODE_STRING& aStr, WCHAR aChar,
|
|
|
|
uint16_t& aPos, uint16_t aStartIndex = 0) {
|
|
|
|
const uint16_t aMaxIndex = aStr.Length / sizeof(WCHAR);
|
|
|
|
|
|
|
|
for (uint16_t curIndex = aStartIndex; curIndex < aMaxIndex; ++curIndex) {
|
|
|
|
if (aStr.Buffer[curIndex] == aChar) {
|
|
|
|
aPos = curIndex;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool IsHexDigit(WCHAR aChar) {
|
2018-08-08 23:47:29 +03:00
|
|
|
return (aChar >= L'0' && aChar <= L'9') || (aChar >= L'A' && aChar <= L'F') ||
|
|
|
|
(aChar >= L'a' && aChar <= L'f');
|
2018-06-06 00:19:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
inline bool 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)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
++cur;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool Contains12DigitHexString(const UNICODE_STRING& aLeafName) {
|
2018-07-07 03:11:48 +03:00
|
|
|
// 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) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
uint16_t start, end;
|
|
|
|
if (!FindCharInUnicodeString(aLeafName, L'.', start)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
++start;
|
|
|
|
if (!FindCharInUnicodeString(aLeafName, L'.', end, start)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end - start != 12) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
UNICODE_STRING test;
|
|
|
|
test.Buffer = &aLeafName.Buffer[start];
|
|
|
|
test.Length = (end - start) * sizeof(WCHAR);
|
|
|
|
test.MaximumLength = test.Length;
|
|
|
|
|
|
|
|
return MatchUnicodeString(test, &IsHexDigit);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool IsFileNameAtLeast16HexDigits(const UNICODE_STRING& aLeafName) {
|
2018-07-07 03:11:48 +03:00
|
|
|
// 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) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
uint16_t dotIndex;
|
|
|
|
if (!FindCharInUnicodeString(aLeafName, L'.', dotIndex)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dotIndex < 16) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
UNICODE_STRING test;
|
|
|
|
test.Buffer = aLeafName.Buffer;
|
|
|
|
test.Length = dotIndex * sizeof(WCHAR);
|
|
|
|
test.MaximumLength = aLeafName.MaximumLength;
|
|
|
|
|
|
|
|
return MatchUnicodeString(test, &IsHexDigit);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void 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;
|
|
|
|
}
|
|
|
|
|
2019-01-16 02:10:00 +03:00
|
|
|
#endif // !defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
2020-05-04 20:20:51 +03:00
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
2021-05-29 01:35:57 +03:00
|
|
|
inline const 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
|
2020-05-04 20:20:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif // defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
inline char EnsureLowerCaseASCII(char aChar) {
|
|
|
|
if (aChar >= 'A' && aChar <= 'Z') {
|
|
|
|
aChar -= 'A' - 'a';
|
|
|
|
}
|
|
|
|
|
|
|
|
return aChar;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int StricmpASCII(const char* aLeft, const char* aRight) {
|
|
|
|
char curLeft, curRight;
|
|
|
|
|
|
|
|
do {
|
|
|
|
curLeft = EnsureLowerCaseASCII(*(aLeft++));
|
|
|
|
curRight = EnsureLowerCaseASCII(*(aRight++));
|
|
|
|
} while (curLeft && curLeft == curRight);
|
|
|
|
|
|
|
|
return curLeft - curRight;
|
|
|
|
}
|
|
|
|
|
Bug 1610790: Part 2 - Implement GetProcAddress for a remote process. r=handyman
This patch adds a function to get an exported function in a remote process.
We need this implementation to address Bug 1604008, Bug 1608645, and Bug 1610790.
When `WindowsDllInterceptor` detours a function in a remote process, we used the
native `GetProcAddress` locally, and then detours the returned address in the
target process. The problem is if the caller's export table was modified, the
address returned from `GetProcAddress` might be invalid in the target process,
which is Bug 1604008.
I implemented `GetProcAddress` depending on both local and remote process image,
but it caused two regressions Bug 1608645 and Bug 1610790 because multiple
applications modify firefox's export table in multiple ways, such as replacing
an entry of EAT, replacing an RVA to Export section, or etc.
With this patch, we can use `PEExportSection<MMPolicy>::GetProcAddress` to get
an exported function in a remote process without relying on any local data so
that it's not impacted by modification of the local export table.
Differential Revision: https://phabricator.services.mozilla.com//D62315
Depends on D62314
2020-02-11 23:21:10 +03:00
|
|
|
inline int StrcmpASCII(const char* aLeft, const char* aRight) {
|
|
|
|
char curLeft, curRight;
|
|
|
|
|
|
|
|
do {
|
|
|
|
curLeft = *(aLeft++);
|
|
|
|
curRight = *(aRight++);
|
|
|
|
} while (curLeft && curLeft == curRight);
|
|
|
|
|
|
|
|
return curLeft - curRight;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline size_t StrlenASCII(const char* aStr) {
|
|
|
|
size_t len = 0;
|
|
|
|
|
|
|
|
while (*(aStr++)) {
|
|
|
|
++len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2021-05-19 21:01:48 +03:00
|
|
|
struct CodeViewRecord70 {
|
|
|
|
uint32_t signature;
|
|
|
|
GUID pdbSignature;
|
|
|
|
uint32_t pdbAge;
|
|
|
|
// A UTF-8 string, according to
|
|
|
|
// https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/locator.cpp#L785
|
|
|
|
char pdbFileName[1];
|
|
|
|
};
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
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]; // ArrayLength(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)
|
Bug 1610790: Part 2 - Implement GetProcAddress for a remote process. r=handyman
This patch adds a function to get an exported function in a remote process.
We need this implementation to address Bug 1604008, Bug 1608645, and Bug 1610790.
When `WindowsDllInterceptor` detours a function in a remote process, we used the
native `GetProcAddress` locally, and then detours the returned address in the
target process. The problem is if the caller's export table was modified, the
address returned from `GetProcAddress` might be invalid in the target process,
which is Bug 1604008.
I implemented `GetProcAddress` depending on both local and remote process image,
but it caused two regressions Bug 1608645 and Bug 1610790 because multiple
applications modify firefox's export table in multiple ways, such as replacing
an entry of EAT, replacing an RVA to Export section, or etc.
With this patch, we can use `PEExportSection<MMPolicy>::GetProcAddress` to get
an exported function in a remote process without relying on any local data so
that it's not impacted by modification of the local export table.
Differential Revision: https://phabricator.services.mozilla.com//D62315
Depends on D62314
2020-02-11 23:21:10 +03:00
|
|
|
template <typename T>
|
|
|
|
static T HModuleToBaseAddr(HMODULE aModule) {
|
|
|
|
return reinterpret_cast<T>(reinterpret_cast<uintptr_t>(aModule) &
|
|
|
|
~uintptr_t(3));
|
2019-04-12 22:58:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
explicit PEHeaders(void* aBaseAddress)
|
|
|
|
: PEHeaders(reinterpret_cast<PIMAGE_DOS_HEADER>(aBaseAddress)) {}
|
|
|
|
|
Bug 1610790: Part 2 - Implement GetProcAddress for a remote process. r=handyman
This patch adds a function to get an exported function in a remote process.
We need this implementation to address Bug 1604008, Bug 1608645, and Bug 1610790.
When `WindowsDllInterceptor` detours a function in a remote process, we used the
native `GetProcAddress` locally, and then detours the returned address in the
target process. The problem is if the caller's export table was modified, the
address returned from `GetProcAddress` might be invalid in the target process,
which is Bug 1604008.
I implemented `GetProcAddress` depending on both local and remote process image,
but it caused two regressions Bug 1608645 and Bug 1610790 because multiple
applications modify firefox's export table in multiple ways, such as replacing
an entry of EAT, replacing an RVA to Export section, or etc.
With this patch, we can use `PEExportSection<MMPolicy>::GetProcAddress` to get
an exported function in a remote process without relying on any local data so
that it's not impacted by modification of the local export table.
Differential Revision: https://phabricator.services.mozilla.com//D62315
Depends on D62314
2020-02-11 23:21:10 +03:00
|
|
|
explicit PEHeaders(HMODULE aModule)
|
|
|
|
: PEHeaders(HModuleToBaseAddr<PIMAGE_DOS_HEADER>(aModule)) {}
|
2019-04-12 22:58:01 +03:00
|
|
|
|
|
|
|
explicit PEHeaders(PIMAGE_DOS_HEADER aMzHeader)
|
2019-11-07 00:54:55 +03:00
|
|
|
: mMzHeader(aMzHeader),
|
|
|
|
mPeHeader(nullptr),
|
|
|
|
mImageLimit(nullptr),
|
|
|
|
mIsImportDirectoryTampered(false) {
|
2019-04-12 22:58:01 +03:00
|
|
|
if (!mMzHeader || mMzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mPeHeader = RVAToPtrUnchecked<PIMAGE_NT_HEADERS>(mMzHeader->e_lfanew);
|
|
|
|
if (!mPeHeader || mPeHeader->Signature != IMAGE_NT_SIGNATURE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
mImageLimit = RVAToPtrUnchecked<void*>(imageSize - 1UL);
|
2020-05-04 20:20:51 +03:00
|
|
|
|
|
|
|
PIMAGE_DATA_DIRECTORY importDirEntry =
|
|
|
|
GetImageDirectoryEntryPtr(IMAGE_DIRECTORY_ENTRY_IMPORT);
|
|
|
|
if (!importDirEntry) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mIsImportDirectoryTampered = (importDirEntry->VirtualAddress >= imageSize);
|
2019-04-12 22:58:01 +03:00
|
|
|
}
|
2018-06-06 00:19:30 +03:00
|
|
|
|
|
|
|
explicit operator bool() const { return !!mImageLimit; }
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This overload computes absolute virtual addresses relative to the base
|
|
|
|
* address of the binary.
|
|
|
|
*/
|
|
|
|
template <typename T, typename R>
|
2019-04-26 18:55:11 +03:00
|
|
|
T RVAToPtr(R aRva) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
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>
|
2019-04-26 18:55:11 +03:00
|
|
|
T RVAToPtr(void* aBase, R aRva) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
if (!mImageLimit) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* absAddress = reinterpret_cast<char*>(aBase) + aRva;
|
|
|
|
if (absAddress < reinterpret_cast<char*>(mMzHeader) ||
|
|
|
|
absAddress > reinterpret_cast<char*>(mImageLimit)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return reinterpret_cast<T>(absAddress);
|
|
|
|
}
|
|
|
|
|
2020-03-03 03:19:41 +03:00
|
|
|
Maybe<Range<const uint8_t>> GetBounds() const {
|
2019-05-29 13:13:28 +03:00
|
|
|
if (!mImageLimit) {
|
|
|
|
return Nothing();
|
|
|
|
}
|
|
|
|
|
|
|
|
auto base = reinterpret_cast<const uint8_t*>(mMzHeader);
|
|
|
|
DWORD imageSize = mPeHeader->OptionalHeader.SizeOfImage;
|
2020-03-03 03:19:41 +03:00
|
|
|
return Some(Range(base, imageSize));
|
2019-05-29 13:13:28 +03:00
|
|
|
}
|
|
|
|
|
2021-05-19 21:01:49 +03:00
|
|
|
DWORD GetFileCharacteristics() const {
|
|
|
|
return mPeHeader ? mPeHeader->FileHeader.Characteristics : 0;
|
|
|
|
}
|
|
|
|
|
2020-10-27 17:09:00 +03:00
|
|
|
bool IsWithinImage(const void* aAddress) const {
|
|
|
|
uintptr_t addr = reinterpret_cast<uintptr_t>(aAddress);
|
|
|
|
uintptr_t imageBase = reinterpret_cast<uintptr_t>(mMzHeader);
|
|
|
|
uintptr_t imageLimit = reinterpret_cast<uintptr_t>(mImageLimit);
|
|
|
|
return addr >= imageBase && addr <= imageLimit;
|
|
|
|
}
|
|
|
|
|
|
|
|
PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory() const {
|
2019-11-07 00:54:55 +03:00
|
|
|
// 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);
|
2018-06-06 00:19:30 +03:00
|
|
|
}
|
|
|
|
|
2020-04-08 17:27:01 +03:00
|
|
|
PIMAGE_RESOURCE_DIRECTORY GetResourceTable() const {
|
2018-06-06 00:19:30 +03:00
|
|
|
return GetImageDirectoryEntry<PIMAGE_RESOURCE_DIRECTORY>(
|
|
|
|
IMAGE_DIRECTORY_ENTRY_RESOURCE);
|
|
|
|
}
|
|
|
|
|
2019-04-12 22:58:01 +03:00
|
|
|
PIMAGE_DATA_DIRECTORY GetImageDirectoryEntryPtr(
|
|
|
|
const uint32_t aDirectoryIndex, uint32_t* aOutRva = nullptr) const {
|
|
|
|
if (aOutRva) {
|
|
|
|
*aOutRva = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
IMAGE_OPTIONAL_HEADER& optionalHeader = mPeHeader->OptionalHeader;
|
|
|
|
|
|
|
|
const uint32_t maxIndex = std::min(optionalHeader.NumberOfRvaAndSizes,
|
|
|
|
DWORD(IMAGE_NUMBEROF_DIRECTORY_ENTRIES));
|
|
|
|
if (aDirectoryIndex >= maxIndex) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PIMAGE_DATA_DIRECTORY dirEntry =
|
|
|
|
&optionalHeader.DataDirectory[aDirectoryIndex];
|
|
|
|
if (aOutRva) {
|
|
|
|
*aOutRva = reinterpret_cast<char*>(dirEntry) -
|
|
|
|
reinterpret_cast<char*>(mMzHeader);
|
|
|
|
MOZ_ASSERT(*aOutRva);
|
|
|
|
}
|
|
|
|
|
|
|
|
return dirEntry;
|
|
|
|
}
|
|
|
|
|
2020-04-08 17:27:01 +03:00
|
|
|
bool GetVersionInfo(uint64_t& aOutVersion) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
// RT_VERSION == 16
|
|
|
|
// Version resources require an id of 1
|
|
|
|
auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1);
|
|
|
|
if (!root) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
VS_FIXEDFILEINFO* fixedInfo = GetFixedFileInfo(root);
|
|
|
|
if (!fixedInfo) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aOutVersion = ((static_cast<uint64_t>(fixedInfo->dwFileVersionMS) << 32) |
|
|
|
|
static_cast<uint64_t>(fixedInfo->dwFileVersionLS));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-04-08 17:27:01 +03:00
|
|
|
bool GetTimeStamp(DWORD& aResult) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
if (!(*this)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aResult = mPeHeader->FileHeader.TimeDateStamp;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-02-14 17:50:57 +03:00
|
|
|
bool GetImageSize(DWORD& aResult) const {
|
|
|
|
if (!(*this)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aResult = mPeHeader->OptionalHeader.SizeOfImage;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-07-10 16:59:16 +03:00
|
|
|
bool GetCheckSum(DWORD& aResult) const {
|
|
|
|
if (!(*this)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aResult = mPeHeader->OptionalHeader.CheckSum;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
PIMAGE_IMPORT_DESCRIPTOR
|
2020-10-27 17:09:00 +03:00
|
|
|
GetImportDescriptor(const char* aModuleNameASCII) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
|
|
|
|
IsValid(curImpDesc); ++curImpDesc) {
|
2019-11-07 00:54:55 +03:00
|
|
|
auto curName = mIsImportDirectoryTampered
|
|
|
|
? RVAToPtrUnchecked<const char*>(curImpDesc->Name)
|
|
|
|
: RVAToPtr<const char*>(curImpDesc->Name);
|
2018-06-06 00:19:30 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-11-10 23:51:00 +03:00
|
|
|
template <typename CallbackT>
|
|
|
|
void EnumImportChunks(const CallbackT& aCallback) const {
|
2020-05-04 20:20:51 +03:00
|
|
|
for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
|
|
|
|
IsValid(curImpDesc); ++curImpDesc) {
|
|
|
|
auto curName = mIsImportDirectoryTampered
|
|
|
|
? RVAToPtrUnchecked<const char*>(curImpDesc->Name)
|
|
|
|
: RVAToPtr<const char*>(curImpDesc->Name);
|
|
|
|
if (!curName) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-11-10 23:51:00 +03:00
|
|
|
aCallback(curName);
|
2020-05-04 20:20:51 +03:00
|
|
|
}
|
2020-11-10 23:51:00 +03:00
|
|
|
}
|
2020-05-04 20:20:51 +03:00
|
|
|
|
2020-03-03 03:19:41 +03:00
|
|
|
/**
|
|
|
|
* 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().
|
|
|
|
*/
|
2019-08-07 01:58:20 +03:00
|
|
|
Maybe<Span<IMAGE_THUNK_DATA>> GetIATThunksForModule(
|
2020-03-03 03:19:41 +03:00
|
|
|
const char* aModuleNameASCII,
|
2020-10-27 17:09:00 +03:00
|
|
|
const Range<const uint8_t>* aBoundaries = nullptr) const {
|
2019-12-24 02:11:07 +03:00
|
|
|
PIMAGE_IMPORT_DESCRIPTOR impDesc = GetImportDescriptor(aModuleNameASCII);
|
2019-04-12 22:58:01 +03:00
|
|
|
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)) {
|
2020-03-03 03:19:41 +03:00
|
|
|
if (aBoundaries) {
|
|
|
|
auto iatEntry =
|
|
|
|
reinterpret_cast<const uint8_t*>(curIatThunk->u1.Function);
|
|
|
|
if (iatEntry < aBoundaries->begin().get() ||
|
|
|
|
iatEntry >= aBoundaries->end().get()) {
|
|
|
|
return Nothing();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-12 22:58:01 +03:00
|
|
|
++curIatThunk;
|
|
|
|
}
|
|
|
|
|
2020-08-07 10:49:47 +03:00
|
|
|
return Some(Span(firstIatThunk, curIatThunk));
|
2019-04-12 22:58:01 +03:00
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
/**
|
|
|
|
* 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>
|
2020-04-08 17:27:01 +03:00
|
|
|
T FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable();
|
|
|
|
if (!topLevel) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry =
|
|
|
|
FindResourceEntry(topLevel, aType);
|
|
|
|
if (!typeEntry || !typeEntry->DataIsDirectory) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto idDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
|
|
|
|
topLevel, typeEntry->OffsetToDirectory);
|
|
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY idEntry = FindResourceEntry(idDir, aResId);
|
|
|
|
if (!idEntry || !idEntry->DataIsDirectory) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto langDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(
|
|
|
|
topLevel, idEntry->OffsetToDirectory);
|
|
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry;
|
|
|
|
if (aLangId) {
|
|
|
|
langEntry = FindResourceEntry(langDir, aLangId);
|
|
|
|
} else {
|
|
|
|
langEntry = FindFirstResourceEntry(langDir);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!langEntry || langEntry->DataIsDirectory) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto dataEntry =
|
|
|
|
RVAToPtr<PIMAGE_RESOURCE_DATA_ENTRY>(topLevel, langEntry->OffsetToData);
|
2021-07-28 22:11:43 +03:00
|
|
|
return dataEntry ? RVAToPtr<T>(dataEntry->OffsetToData) : nullptr;
|
2018-06-06 00:19:30 +03:00
|
|
|
}
|
|
|
|
|
2019-04-26 18:55:11 +03:00
|
|
|
template <size_t N>
|
|
|
|
Maybe<Span<const uint8_t>> FindSection(const char (&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<const char*>(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);
|
2020-08-07 10:49:47 +03:00
|
|
|
return Some(Span(base, size));
|
2019-04-26 18:55:11 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc) {
|
|
|
|
return aImpDesc && aImpDesc->OriginalFirstThunk != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsValid(PIMAGE_THUNK_DATA aImgThunk) {
|
|
|
|
return aImgThunk && aImgThunk->u1.Ordinal != 0;
|
|
|
|
}
|
|
|
|
|
2020-05-04 20:20:51 +03:00
|
|
|
bool IsImportDirectoryTampered() const { return mIsImportDirectoryTampered; }
|
2019-11-07 00:54:55 +03:00
|
|
|
|
2020-04-08 17:27:02 +03:00
|
|
|
FARPROC GetEntryPoint() const {
|
|
|
|
// Use the unchecked version because the entrypoint may be tampered.
|
|
|
|
return RVAToPtrUnchecked<FARPROC>(
|
|
|
|
mPeHeader->OptionalHeader.AddressOfEntryPoint);
|
|
|
|
}
|
|
|
|
|
2021-05-19 21:01:48 +03:00
|
|
|
const CodeViewRecord70* GetPdbInfo() const {
|
|
|
|
PIMAGE_DEBUG_DIRECTORY debugDirectory =
|
|
|
|
GetImageDirectoryEntry<PIMAGE_DEBUG_DIRECTORY>(
|
|
|
|
IMAGE_DIRECTORY_ENTRY_DEBUG);
|
|
|
|
if (!debugDirectory) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const CodeViewRecord70* debugInfo =
|
|
|
|
RVAToPtr<CodeViewRecord70*>(debugDirectory->AddressOfRawData);
|
|
|
|
return (debugInfo && debugInfo->signature == 'SDSR') ? debugInfo : nullptr;
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
private:
|
2019-11-07 00:54:55 +03:00
|
|
|
enum class BoundsCheckPolicy { Default, Skip };
|
|
|
|
|
|
|
|
template <typename T, BoundsCheckPolicy Policy = BoundsCheckPolicy::Default>
|
2020-04-08 17:27:01 +03:00
|
|
|
T GetImageDirectoryEntry(const uint32_t aDirectoryIndex) const {
|
2019-04-12 22:58:01 +03:00
|
|
|
PIMAGE_DATA_DIRECTORY dirEntry = GetImageDirectoryEntryPtr(aDirectoryIndex);
|
|
|
|
if (!dirEntry) {
|
2018-06-06 00:19:30 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2019-11-07 00:54:55 +03:00
|
|
|
return Policy == BoundsCheckPolicy::Skip
|
|
|
|
? RVAToPtrUnchecked<T>(dirEntry->VirtualAddress)
|
|
|
|
: RVAToPtr<T>(dirEntry->VirtualAddress);
|
2018-06-06 00:19:30 +03:00
|
|
|
}
|
|
|
|
|
2019-04-12 22:58:01 +03:00
|
|
|
// This private variant does not have bounds checks, because we need to be
|
2018-06-06 00:19:30 +03:00
|
|
|
// able to resolve the bounds themselves.
|
|
|
|
template <typename T, typename R>
|
2019-04-26 18:55:11 +03:00
|
|
|
T RVAToPtrUnchecked(R aRva) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
|
|
|
|
}
|
|
|
|
|
2019-04-26 18:55:11 +03:00
|
|
|
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));
|
2020-08-07 10:42:50 +03:00
|
|
|
return Span{base, numSections};
|
2019-04-26 18:55:11 +03:00
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY
|
2020-04-08 17:27:01 +03:00
|
|
|
FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId) const {
|
2021-07-28 22:11:43 +03:00
|
|
|
if (!aCurLevel) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
// 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;
|
2023-07-06 20:03:34 +03:00
|
|
|
if (!(IsWithinImage(dirEnt) &&
|
|
|
|
IsWithinImage(&dirEnt[aCurLevel->NumberOfIdEntries - 1].Id))) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) {
|
|
|
|
if (dirEnt[i].Id == aId) {
|
|
|
|
return &dirEnt[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY
|
2020-04-08 17:27:01 +03:00
|
|
|
FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2020-04-08 17:27:01 +03:00
|
|
|
VS_FIXEDFILEINFO* GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo) const {
|
2018-06-06 00:19:30 +03:00
|
|
|
WORD length = aVerInfo->wLength;
|
2018-07-20 00:59:10 +03:00
|
|
|
if (length < sizeof(VS_VERSIONINFO_HEADER)) {
|
2018-06-06 00:19:30 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const wchar_t kVersionInfoKey[] = L"VS_VERSION_INFO";
|
|
|
|
if (::RtlCompareMemory(aVerInfo->szKey, kVersionInfoKey,
|
|
|
|
ArrayLength(kVersionInfoKey)) !=
|
|
|
|
ArrayLength(kVersionInfoKey)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-07-20 00:59:10 +03:00
|
|
|
if (aVerInfo->wValueLength != sizeof(VS_FIXEDFILEINFO)) {
|
|
|
|
// Fixed file info does not exist
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
WORD offset = sizeof(VS_VERSIONINFO_HEADER);
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
uintptr_t base = reinterpret_cast<uintptr_t>(aVerInfo);
|
|
|
|
// Align up to 4-byte boundary
|
|
|
|
#pragma warning(suppress : 4146)
|
|
|
|
offset += (-(base + offset) & 3);
|
|
|
|
|
2018-07-20 00:59:10 +03:00
|
|
|
if (offset >= length) {
|
2018-06-06 00:19:30 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto result = reinterpret_cast<VS_FIXEDFILEINFO*>(base + offset);
|
|
|
|
if (result->dwSignature != 0xFEEF04BD) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
PIMAGE_DOS_HEADER mMzHeader;
|
|
|
|
PIMAGE_NT_HEADERS mPeHeader;
|
|
|
|
void* mImageLimit;
|
2019-11-07 00:54:55 +03:00
|
|
|
bool mIsImportDirectoryTampered;
|
2018-06-06 00:19:30 +03:00
|
|
|
};
|
|
|
|
|
Bug 1610790: Part 2 - Implement GetProcAddress for a remote process. r=handyman
This patch adds a function to get an exported function in a remote process.
We need this implementation to address Bug 1604008, Bug 1608645, and Bug 1610790.
When `WindowsDllInterceptor` detours a function in a remote process, we used the
native `GetProcAddress` locally, and then detours the returned address in the
target process. The problem is if the caller's export table was modified, the
address returned from `GetProcAddress` might be invalid in the target process,
which is Bug 1604008.
I implemented `GetProcAddress` depending on both local and remote process image,
but it caused two regressions Bug 1608645 and Bug 1610790 because multiple
applications modify firefox's export table in multiple ways, such as replacing
an entry of EAT, replacing an RVA to Export section, or etc.
With this patch, we can use `PEExportSection<MMPolicy>::GetProcAddress` to get
an exported function in a remote process without relying on any local data so
that it's not impacted by modification of the local export table.
Differential Revision: https://phabricator.services.mozilla.com//D62315
Depends on D62314
2020-02-11 23:21:10 +03:00
|
|
|
// This class represents an export section of a local/remote process.
|
|
|
|
template <typename MMPolicy>
|
|
|
|
class MOZ_RAII PEExportSection {
|
|
|
|
const MMPolicy& mMMPolicy;
|
|
|
|
uintptr_t mImageBase;
|
|
|
|
DWORD mOrdinalBase;
|
|
|
|
DWORD mRvaDirStart;
|
|
|
|
DWORD mRvaDirEnd;
|
|
|
|
mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportAddressTable;
|
|
|
|
mozilla::interceptor::TargetObjectArray<MMPolicy, DWORD> mExportNameTable;
|
|
|
|
mozilla::interceptor::TargetObjectArray<MMPolicy, WORD> mExportOrdinalTable;
|
|
|
|
|
|
|
|
explicit PEExportSection(const MMPolicy& aMMPolicy)
|
|
|
|
: mMMPolicy(aMMPolicy),
|
|
|
|
mImageBase(0),
|
|
|
|
mOrdinalBase(0),
|
|
|
|
mRvaDirStart(0),
|
|
|
|
mRvaDirEnd(0),
|
|
|
|
mExportAddressTable(mMMPolicy),
|
|
|
|
mExportNameTable(mMMPolicy),
|
|
|
|
mExportOrdinalTable(mMMPolicy) {}
|
|
|
|
|
|
|
|
PEExportSection(const MMPolicy& aMMPolicy, uintptr_t aImageBase,
|
|
|
|
DWORD aRvaDirStart, DWORD aRvaDirEnd,
|
|
|
|
const IMAGE_EXPORT_DIRECTORY& exportDir)
|
|
|
|
: mMMPolicy(aMMPolicy),
|
|
|
|
mImageBase(aImageBase),
|
|
|
|
mOrdinalBase(exportDir.Base),
|
|
|
|
mRvaDirStart(aRvaDirStart),
|
|
|
|
mRvaDirEnd(aRvaDirEnd),
|
|
|
|
mExportAddressTable(mMMPolicy,
|
|
|
|
mImageBase + exportDir.AddressOfFunctions,
|
|
|
|
exportDir.NumberOfFunctions),
|
|
|
|
mExportNameTable(mMMPolicy, mImageBase + exportDir.AddressOfNames,
|
|
|
|
exportDir.NumberOfNames),
|
|
|
|
mExportOrdinalTable(mMMPolicy,
|
|
|
|
mImageBase + exportDir.AddressOfNameOrdinals,
|
|
|
|
exportDir.NumberOfNames) {}
|
|
|
|
|
|
|
|
static const PEExportSection Get(uintptr_t aImageBase,
|
|
|
|
const MMPolicy& aMMPolicy) {
|
|
|
|
mozilla::interceptor::TargetObject<MMPolicy, IMAGE_DOS_HEADER> mzHeader(
|
|
|
|
aMMPolicy, aImageBase);
|
|
|
|
if (!mzHeader || mzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
|
|
return PEExportSection(aMMPolicy);
|
|
|
|
}
|
|
|
|
|
|
|
|
mozilla::interceptor::TargetObject<MMPolicy, IMAGE_NT_HEADERS> peHeader(
|
|
|
|
aMMPolicy, aImageBase + mzHeader->e_lfanew);
|
|
|
|
if (!peHeader || peHeader->Signature != IMAGE_NT_SIGNATURE) {
|
|
|
|
return PEExportSection(aMMPolicy);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (peHeader->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
|
|
|
|
return PEExportSection(aMMPolicy);
|
|
|
|
}
|
|
|
|
|
|
|
|
const IMAGE_OPTIONAL_HEADER& optionalHeader = peHeader->OptionalHeader;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
const IMAGE_DATA_DIRECTORY& exportDirectoryEntry =
|
|
|
|
optionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
|
|
|
if (!exportDirectoryEntry.VirtualAddress || !exportDirectoryEntry.Size) {
|
|
|
|
return PEExportSection(aMMPolicy);
|
|
|
|
}
|
|
|
|
|
|
|
|
mozilla::interceptor::TargetObject<MMPolicy, IMAGE_EXPORT_DIRECTORY>
|
|
|
|
exportDirectory(aMMPolicy,
|
|
|
|
aImageBase + exportDirectoryEntry.VirtualAddress);
|
|
|
|
if (!exportDirectory || !exportDirectory->NumberOfFunctions) {
|
|
|
|
return PEExportSection(aMMPolicy);
|
|
|
|
}
|
|
|
|
|
|
|
|
return PEExportSection(
|
|
|
|
aMMPolicy, aImageBase, exportDirectoryEntry.VirtualAddress,
|
|
|
|
exportDirectoryEntry.VirtualAddress + exportDirectoryEntry.Size,
|
|
|
|
*exportDirectory.GetLocalBase());
|
|
|
|
}
|
|
|
|
|
|
|
|
FARPROC GetProcAddressByOrdinal(WORD aOrdinal) const {
|
|
|
|
if (aOrdinal < mOrdinalBase) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto rvaToFunction = mExportAddressTable[aOrdinal - mOrdinalBase];
|
|
|
|
if (!rvaToFunction) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
static const PEExportSection Get(HMODULE aModule, const MMPolicy& aMMPolicy) {
|
|
|
|
return Get(PEHeaders::HModuleToBaseAddr<uintptr_t>(aModule), aMMPolicy);
|
|
|
|
}
|
|
|
|
|
|
|
|
explicit operator bool() const {
|
|
|
|
// Because PEExportSection doesn't use MMPolicy::Reserve(), a boolified
|
|
|
|
// mMMPolicy is expected to be false. We don't check mMMPolicy here.
|
|
|
|
return mImageBase && mRvaDirStart && mRvaDirEnd && mExportAddressTable &&
|
|
|
|
mExportNameTable && mExportOrdinalTable;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T RVAToPtr(uint32_t aRva) const {
|
|
|
|
return reinterpret_cast<T>(mImageBase + aRva);
|
|
|
|
}
|
|
|
|
|
|
|
|
PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const {
|
|
|
|
if (!*this) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return RVAToPtr<PIMAGE_EXPORT_DIRECTORY>(mRvaDirStart);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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(
|
|
|
|
const char* aFunctionNameASCII) const {
|
|
|
|
if (!*this || !aFunctionNameASCII) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct NameTableComparator {
|
|
|
|
NameTableComparator(const PEExportSection<MMPolicy>& aExportSection,
|
|
|
|
const char* aTarget)
|
|
|
|
: mExportSection(aExportSection),
|
|
|
|
mTargetName(aTarget),
|
|
|
|
mTargetNamelength(StrlenASCII(aTarget)) {}
|
|
|
|
|
|
|
|
int operator()(DWORD aRVAToString) const {
|
|
|
|
mozilla::interceptor::TargetObjectArray<MMPolicy, char> itemString(
|
|
|
|
mExportSection.mMMPolicy, mExportSection.mImageBase + aRVAToString,
|
|
|
|
mTargetNamelength + 1);
|
|
|
|
return StrcmpASCII(mTargetName, itemString[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
const PEExportSection<MMPolicy>& mExportSection;
|
|
|
|
const char* mTargetName;
|
|
|
|
size_t mTargetNamelength;
|
|
|
|
};
|
|
|
|
|
|
|
|
const NameTableComparator comp(*this, aFunctionNameASCII);
|
|
|
|
|
|
|
|
size_t match;
|
|
|
|
if (!mExportNameTable.BinarySearchIf(comp, &match)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const WORD* index = mExportOrdinalTable[match];
|
|
|
|
if (!index) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const DWORD* rvaToFunction = mExportAddressTable[*index];
|
|
|
|
if (!rvaToFunction) {
|
|
|
|
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(const char* 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;
|
|
|
|
}
|
|
|
|
return reinterpret_cast<FARPROC>(mImageBase + *rvaToFunction);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
inline HANDLE RtlGetProcessHeap() {
|
|
|
|
PTEB teb = ::NtCurrentTeb();
|
|
|
|
PPEB peb = teb->ProcessEnvironmentBlock;
|
|
|
|
return peb->Reserved4[1];
|
|
|
|
}
|
|
|
|
|
2020-02-07 21:08:11 +03:00
|
|
|
inline PVOID RtlGetThreadLocalStoragePointer() {
|
|
|
|
return ::NtCurrentTeb()->Reserved1[11];
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void RtlSetThreadLocalStoragePointerForTestingOnly(PVOID aNewValue) {
|
|
|
|
::NtCurrentTeb()->Reserved1[11] = aNewValue;
|
|
|
|
}
|
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
inline DWORD RtlGetCurrentThreadId() {
|
|
|
|
PTEB teb = ::NtCurrentTeb();
|
|
|
|
CLIENT_ID* cid = reinterpret_cast<CLIENT_ID*>(&teb->Reserved1[8]);
|
|
|
|
return static_cast<DWORD>(reinterpret_cast<uintptr_t>(cid->UniqueThread) &
|
|
|
|
0xFFFFFFFFUL);
|
|
|
|
}
|
|
|
|
|
2023-09-12 15:27:46 +03:00
|
|
|
inline PVOID RtlGetThreadStackBase() {
|
|
|
|
return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackBase;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline PVOID RtlGetThreadStackLimit() {
|
|
|
|
return reinterpret_cast<_NT_TIB*>(::NtCurrentTeb())->StackLimit;
|
|
|
|
}
|
|
|
|
|
2020-04-08 17:27:01 +03:00
|
|
|
const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
|
|
|
|
|
2018-11-21 02:49:36 +03:00
|
|
|
inline LauncherResult<DWORD> GetParentProcessId() {
|
2018-07-17 01:41:55 +03:00
|
|
|
struct PROCESS_BASIC_INFORMATION {
|
|
|
|
NTSTATUS ExitStatus;
|
|
|
|
PPEB PebBaseAddress;
|
|
|
|
ULONG_PTR AffinityMask;
|
2018-08-08 21:28:04 +03:00
|
|
|
LONG BasePriority;
|
2018-07-17 01:41:55 +03:00
|
|
|
ULONG_PTR UniqueProcessId;
|
|
|
|
ULONG_PTR InheritedFromUniqueProcessId;
|
|
|
|
};
|
|
|
|
|
|
|
|
ULONG returnLength;
|
|
|
|
PROCESS_BASIC_INFORMATION pbi = {};
|
2018-11-21 02:49:36 +03:00
|
|
|
NTSTATUS status =
|
|
|
|
::NtQueryInformationProcess(kCurrentProcess, ProcessBasicInformation,
|
2018-07-17 01:41:55 +03:00
|
|
|
&pbi, sizeof(pbi), &returnLength);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
2018-11-21 02:49:36 +03:00
|
|
|
return LAUNCHER_ERROR_FROM_NTSTATUS(status);
|
2018-07-17 01:41:55 +03:00
|
|
|
}
|
|
|
|
|
2018-11-21 02:49:36 +03:00
|
|
|
return static_cast<DWORD>(pbi.InheritedFromUniqueProcessId & 0xFFFFFFFF);
|
2018-07-17 01:41:55 +03:00
|
|
|
}
|
|
|
|
|
2020-04-08 17:27:01 +03:00
|
|
|
inline SIZE_T WINAPI VirtualQueryEx(HANDLE aProcess, LPCVOID aAddress,
|
|
|
|
PMEMORY_BASIC_INFORMATION aMemInfo,
|
|
|
|
SIZE_T aMemInfoLen) {
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
return ::VirtualQueryEx(aProcess, aAddress, aMemInfo, aMemInfoLen);
|
|
|
|
#else
|
|
|
|
SIZE_T returnedLength;
|
|
|
|
NTSTATUS status = ::NtQueryVirtualMemory(
|
|
|
|
aProcess, const_cast<PVOID>(aAddress), MemoryBasicInformation, aMemInfo,
|
|
|
|
aMemInfoLen, &returnedLength);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
|
|
|
|
returnedLength = 0;
|
|
|
|
}
|
|
|
|
return returnedLength;
|
|
|
|
#endif // defined(MOZILLA_INTERNAL_API)
|
|
|
|
}
|
|
|
|
|
|
|
|
inline SIZE_T WINAPI VirtualQuery(LPCVOID aAddress,
|
|
|
|
PMEMORY_BASIC_INFORMATION aMemInfo,
|
|
|
|
SIZE_T aMemInfoLen) {
|
|
|
|
return nt::VirtualQueryEx(kCurrentProcess, aAddress, aMemInfo, aMemInfoLen);
|
|
|
|
}
|
|
|
|
|
2019-04-12 22:58:01 +03:00
|
|
|
struct DataDirectoryEntry : public _IMAGE_DATA_DIRECTORY {
|
|
|
|
DataDirectoryEntry() : _IMAGE_DATA_DIRECTORY() {}
|
|
|
|
|
|
|
|
MOZ_IMPLICIT DataDirectoryEntry(const _IMAGE_DATA_DIRECTORY& aOther)
|
|
|
|
: _IMAGE_DATA_DIRECTORY(aOther) {}
|
|
|
|
|
|
|
|
DataDirectoryEntry(const DataDirectoryEntry& aOther) = default;
|
2019-11-07 00:54:55 +03:00
|
|
|
|
|
|
|
bool operator==(const DataDirectoryEntry& aOther) const {
|
|
|
|
return VirtualAddress == aOther.VirtualAddress && Size == aOther.Size;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool operator!=(const DataDirectoryEntry& aOther) const {
|
|
|
|
return !(*this == aOther);
|
|
|
|
}
|
2019-04-12 22:58:01 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
inline LauncherResult<void*> GetProcessPebPtr(HANDLE aProcess) {
|
|
|
|
ULONG returnLength;
|
|
|
|
PROCESS_BASIC_INFORMATION pbi;
|
|
|
|
NTSTATUS status = ::NtQueryInformationProcess(
|
|
|
|
aProcess, ProcessBasicInformation, &pbi, sizeof(pbi), &returnLength);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
return LAUNCHER_ERROR_FROM_NTSTATUS(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pbi.PebBaseAddress;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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()) {
|
2020-06-25 19:07:28 +03:00
|
|
|
return ppeb.propagateErr();
|
2019-04-12 22:58:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
PEB peb;
|
|
|
|
SIZE_T bytesRead;
|
|
|
|
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
if (!::ReadProcessMemory(aProcess, ppeb.unwrap(), &peb, sizeof(peb),
|
|
|
|
&bytesRead) ||
|
|
|
|
bytesRead != sizeof(peb)) {
|
|
|
|
return LAUNCHER_ERROR_FROM_LAST();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
NTSTATUS ntStatus = ::NtReadVirtualMemory(aProcess, ppeb.unwrap(), &peb,
|
|
|
|
sizeof(peb), &bytesRead);
|
|
|
|
if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(peb)) {
|
|
|
|
return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// peb.ImageBaseAddress
|
|
|
|
void* baseAddress = peb.Reserved3[1];
|
|
|
|
|
|
|
|
char mzMagic[2];
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
if (!::ReadProcessMemory(aProcess, baseAddress, mzMagic, sizeof(mzMagic),
|
|
|
|
&bytesRead) ||
|
|
|
|
bytesRead != sizeof(mzMagic)) {
|
|
|
|
return LAUNCHER_ERROR_FROM_LAST();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
ntStatus = ::NtReadVirtualMemory(aProcess, baseAddress, mzMagic,
|
|
|
|
sizeof(mzMagic), &bytesRead);
|
|
|
|
if (!NT_SUCCESS(ntStatus) || bytesRead != sizeof(mzMagic)) {
|
|
|
|
return LAUNCHER_ERROR_FROM_NTSTATUS(ntStatus);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
MOZ_ASSERT(mzMagic[0] == 'M' && mzMagic[1] == 'Z');
|
|
|
|
if (mzMagic[0] != 'M' || mzMagic[1] != 'Z') {
|
|
|
|
return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
|
|
|
|
}
|
|
|
|
|
|
|
|
return static_cast<HMODULE>(baseAddress);
|
|
|
|
}
|
|
|
|
|
2020-10-27 17:09:00 +03:00
|
|
|
#if defined(_MSC_VER)
|
|
|
|
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
static HMODULE GetLocalExecModule() {
|
|
|
|
#if defined(_MSC_VER)
|
|
|
|
return reinterpret_cast<HMODULE>(&__ImageBase);
|
|
|
|
#else
|
|
|
|
return ::GetModuleHandleW(nullptr);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
LauncherVoidResult EnsureRemoteImagebase() {
|
|
|
|
if (!mRemoteImagebase) {
|
|
|
|
LauncherResult<HMODULE> remoteImageBaseResult =
|
|
|
|
GetProcessExeModule(mRemoteProcess);
|
|
|
|
if (remoteImageBaseResult.isErr()) {
|
|
|
|
return remoteImageBaseResult.propagateErr();
|
|
|
|
}
|
|
|
|
|
|
|
|
mRemoteImagebase =
|
|
|
|
reinterpret_cast<uint8_t*>(remoteImageBaseResult.unwrap());
|
|
|
|
}
|
|
|
|
return Ok();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* LocalExecToRemoteExec(T* aLocalAddress) const {
|
|
|
|
MOZ_ASSERT(mRemoteImagebase);
|
|
|
|
MOZ_ASSERT(mLocalExec.IsWithinImage(aLocalAddress));
|
|
|
|
|
|
|
|
if (!mRemoteImagebase || !mLocalExec.IsWithinImage(aLocalAddress)) {
|
|
|
|
return aLocalAddress;
|
|
|
|
}
|
|
|
|
|
|
|
|
uintptr_t offset = reinterpret_cast<uintptr_t>(aLocalAddress) -
|
|
|
|
reinterpret_cast<uintptr_t>(mLocalImagebase);
|
|
|
|
return reinterpret_cast<T*>(mRemoteImagebase + offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit CrossExecTransferManager(HANDLE aRemoteProcess)
|
|
|
|
: mRemoteProcess(aRemoteProcess),
|
|
|
|
mLocalImagebase(
|
|
|
|
PEHeaders::HModuleToBaseAddr<uint8_t*>(GetLocalExecModule())),
|
|
|
|
mLocalExec(mLocalImagebase),
|
|
|
|
mRemoteImagebase(nullptr) {}
|
|
|
|
|
|
|
|
CrossExecTransferManager(HANDLE aRemoteProcess, HMODULE aLocalImagebase)
|
|
|
|
: mRemoteProcess(aRemoteProcess),
|
|
|
|
mLocalImagebase(
|
|
|
|
PEHeaders::HModuleToBaseAddr<uint8_t*>(aLocalImagebase)),
|
|
|
|
mLocalExec(mLocalImagebase),
|
|
|
|
mRemoteImagebase(nullptr) {}
|
|
|
|
|
|
|
|
explicit operator bool() const { return !!mLocalExec; }
|
|
|
|
HANDLE RemoteProcess() const { return mRemoteProcess; }
|
|
|
|
const PEHeaders& LocalPEHeaders() const { return mLocalExec; }
|
|
|
|
|
|
|
|
AutoVirtualProtect Protect(void* aLocalAddress, size_t aLength,
|
|
|
|
DWORD aProtFlags) {
|
|
|
|
// If EnsureRemoteImagebase() fails, a subsequent operaion will fail.
|
|
|
|
Unused << EnsureRemoteImagebase();
|
|
|
|
return AutoVirtualProtect(LocalExecToRemoteExec(aLocalAddress), aLength,
|
|
|
|
aProtFlags, mRemoteProcess);
|
|
|
|
}
|
|
|
|
|
|
|
|
LauncherVoidResult Transfer(LPVOID aDestinationAddress,
|
|
|
|
LPCVOID aBufferToWrite, SIZE_T aBufferSize) {
|
|
|
|
LauncherVoidResult result = EnsureRemoteImagebase();
|
|
|
|
if (result.isErr()) {
|
|
|
|
return result.propagateErr();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!::WriteProcessMemory(mRemoteProcess,
|
|
|
|
LocalExecToRemoteExec(aDestinationAddress),
|
|
|
|
aBufferToWrite, aBufferSize, nullptr)) {
|
|
|
|
return LAUNCHER_ERROR_FROM_LAST();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Ok();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
#if !defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
2020-04-08 17:27:02 +03:00
|
|
|
inline LauncherResult<HMODULE> GetModuleHandleFromLeafName(
|
|
|
|
const UNICODE_STRING& aTarget) {
|
|
|
|
auto maybePeb = nt::GetProcessPebPtr(kCurrentProcess);
|
|
|
|
if (maybePeb.isErr()) {
|
2020-06-25 19:07:28 +03:00
|
|
|
return maybePeb.propagateErr();
|
2020-04-08 17:27:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
const PPEB peb = reinterpret_cast<PPEB>(maybePeb.unwrap());
|
|
|
|
if (!peb->Ldr) {
|
|
|
|
return LAUNCHER_ERROR_FROM_WIN32(ERROR_BAD_EXE_FORMAT);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto firstItem = &peb->Ldr->InMemoryOrderModuleList;
|
|
|
|
for (auto p = firstItem->Flink; p != firstItem; p = p->Flink) {
|
|
|
|
const auto currentTableEntry =
|
|
|
|
CONTAINING_RECORD(p, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
|
|
|
|
|
|
|
UNICODE_STRING leafName;
|
|
|
|
nt::GetLeafName(&leafName, ¤tTableEntry->FullDllName);
|
|
|
|
|
|
|
|
if (::RtlCompareUnicodeString(&leafName, &aTarget, TRUE) == 0) {
|
|
|
|
return reinterpret_cast<HMODULE>(currentTableEntry->DllBase);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return LAUNCHER_ERROR_FROM_WIN32(ERROR_MOD_NOT_FOUND);
|
|
|
|
}
|
|
|
|
|
2019-09-23 23:16:51 +03:00
|
|
|
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS SRWLock final {
|
|
|
|
public:
|
|
|
|
constexpr SRWLock() : mLock(SRWLOCK_INIT) {}
|
|
|
|
|
|
|
|
void LockShared() { ::RtlAcquireSRWLockShared(&mLock); }
|
|
|
|
|
|
|
|
void LockExclusive() { ::RtlAcquireSRWLockExclusive(&mLock); }
|
|
|
|
|
|
|
|
void UnlockShared() { ::RtlReleaseSRWLockShared(&mLock); }
|
|
|
|
|
|
|
|
void UnlockExclusive() { ::RtlReleaseSRWLockExclusive(&mLock); }
|
|
|
|
|
|
|
|
SRWLock(const SRWLock&) = delete;
|
|
|
|
SRWLock(SRWLock&&) = delete;
|
|
|
|
SRWLock& operator=(const SRWLock&) = delete;
|
|
|
|
SRWLock& operator=(SRWLock&&) = delete;
|
|
|
|
|
|
|
|
SRWLOCK* operator&() { return &mLock; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
SRWLOCK mLock;
|
|
|
|
};
|
|
|
|
|
|
|
|
class MOZ_RAII AutoExclusiveLock final {
|
|
|
|
public:
|
|
|
|
explicit AutoExclusiveLock(SRWLock& aLock) : mLock(aLock) {
|
|
|
|
aLock.LockExclusive();
|
|
|
|
}
|
|
|
|
|
|
|
|
~AutoExclusiveLock() { mLock.UnlockExclusive(); }
|
|
|
|
|
|
|
|
AutoExclusiveLock(const AutoExclusiveLock&) = delete;
|
|
|
|
AutoExclusiveLock(AutoExclusiveLock&&) = delete;
|
|
|
|
AutoExclusiveLock& operator=(const AutoExclusiveLock&) = delete;
|
|
|
|
AutoExclusiveLock& operator=(AutoExclusiveLock&&) = delete;
|
|
|
|
|
|
|
|
private:
|
|
|
|
SRWLock& mLock;
|
|
|
|
};
|
|
|
|
|
|
|
|
class MOZ_RAII AutoSharedLock final {
|
|
|
|
public:
|
|
|
|
explicit AutoSharedLock(SRWLock& aLock) : mLock(aLock) { aLock.LockShared(); }
|
|
|
|
|
|
|
|
~AutoSharedLock() { mLock.UnlockShared(); }
|
|
|
|
|
|
|
|
AutoSharedLock(const AutoSharedLock&) = delete;
|
|
|
|
AutoSharedLock(AutoSharedLock&&) = delete;
|
|
|
|
AutoSharedLock& operator=(const AutoSharedLock&) = delete;
|
|
|
|
AutoSharedLock& operator=(AutoSharedLock&&) = delete;
|
|
|
|
|
|
|
|
private:
|
|
|
|
SRWLock& mLock;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif // !defined(MOZILLA_INTERNAL_API)
|
|
|
|
|
|
|
|
class RtlAllocPolicy {
|
|
|
|
public:
|
|
|
|
template <typename T>
|
|
|
|
T* maybe_pod_malloc(size_t aNumElems) {
|
|
|
|
if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return static_cast<T*>(
|
|
|
|
::RtlAllocateHeap(RtlGetProcessHeap(), 0, aNumElems * sizeof(T)));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* maybe_pod_calloc(size_t aNumElems) {
|
|
|
|
if (aNumElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return static_cast<T*>(::RtlAllocateHeap(
|
|
|
|
RtlGetProcessHeap(), HEAP_ZERO_MEMORY, aNumElems * sizeof(T)));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
|
|
|
|
if (aNewSize & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return static_cast<T*>(::RtlReAllocateHeap(RtlGetProcessHeap(), 0, aPtr,
|
|
|
|
aNewSize * sizeof(T)));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* pod_malloc(size_t aNumElems) {
|
|
|
|
return maybe_pod_malloc<T>(aNumElems);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* pod_calloc(size_t aNumElems) {
|
|
|
|
return maybe_pod_calloc<T>(aNumElems);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize) {
|
|
|
|
return maybe_pod_realloc<T>(aPtr, aOldSize, aNewSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void free_(T* aPtr, size_t aNumElems = 0) {
|
|
|
|
::RtlFreeHeap(RtlGetProcessHeap(), 0, aPtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void reportAllocOverflow() const {}
|
|
|
|
|
2020-04-13 18:15:32 +03:00
|
|
|
[[nodiscard]] bool checkSimulatedOOM() const { return true; }
|
2019-09-23 23:16:51 +03:00
|
|
|
};
|
|
|
|
|
2020-11-10 23:51:00 +03:00
|
|
|
class AutoMappedView final {
|
|
|
|
void* mView;
|
|
|
|
|
|
|
|
void Unmap() {
|
|
|
|
if (!mView) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
::UnmapViewOfFile(mView);
|
|
|
|
#else
|
|
|
|
NTSTATUS status = ::NtUnmapViewOfSection(nt::kCurrentProcess, mView);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
mView = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
explicit AutoMappedView(void* aView) : mView(aView) {}
|
|
|
|
|
|
|
|
AutoMappedView(HANDLE aSection, ULONG aProtectionFlags) : mView(nullptr) {
|
|
|
|
#if defined(MOZILLA_INTERNAL_API)
|
|
|
|
mView = ::MapViewOfFile(aSection, aProtectionFlags, 0, 0, 0);
|
|
|
|
#else
|
|
|
|
SIZE_T viewSize = 0;
|
|
|
|
NTSTATUS status = ::NtMapViewOfSection(aSection, nt::kCurrentProcess,
|
|
|
|
&mView, 0, 0, nullptr, &viewSize,
|
|
|
|
ViewUnmap, 0, aProtectionFlags);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
::RtlSetLastWin32Error(::RtlNtStatusToDosError(status));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
~AutoMappedView() { Unmap(); }
|
|
|
|
|
|
|
|
// Allow move & Disallow copy
|
|
|
|
AutoMappedView(AutoMappedView&& aOther) : mView(aOther.mView) {
|
|
|
|
aOther.mView = nullptr;
|
|
|
|
}
|
|
|
|
AutoMappedView& operator=(AutoMappedView&& aOther) {
|
|
|
|
if (this != &aOther) {
|
|
|
|
Unmap();
|
|
|
|
mView = aOther.mView;
|
|
|
|
aOther.mView = nullptr;
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
AutoMappedView(const AutoMappedView&) = delete;
|
|
|
|
AutoMappedView& operator=(const AutoMappedView&) = delete;
|
|
|
|
|
|
|
|
explicit operator bool() const { return !!mView; }
|
|
|
|
template <typename T>
|
|
|
|
T* as() {
|
|
|
|
return reinterpret_cast<T*>(mView);
|
|
|
|
}
|
|
|
|
|
|
|
|
void* release() {
|
|
|
|
void* p = mView;
|
|
|
|
mView = nullptr;
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-09-12 15:27:46 +03:00
|
|
|
#if defined(_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 __attribute__((naked)) inline void CheckStack(uint32_t size) {
|
|
|
|
asm volatile(
|
|
|
|
"mov %ecx, %eax;"
|
|
|
|
# if defined(__MINGW32__)
|
|
|
|
"jmp ___chkstk_ms;"
|
|
|
|
# else
|
|
|
|
"jmp __chkstk;"
|
|
|
|
# endif // __MINGW32__
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#endif // _M_X64
|
|
|
|
|
2018-06-06 00:19:30 +03:00
|
|
|
} // namespace nt
|
|
|
|
} // namespace mozilla
|
|
|
|
|
|
|
|
#endif // mozilla_NativeNt_h
|