forked from mirrors/gecko-dev
Bug 1443411: Add gtests for blocking threads with LoadLibrary start address;r=aklotz
MozReview-Commit-ID: 2wIUNnNoKa8 --HG-- extra : rebase_source : f1990af6cd130d9bca38ef21d64d66584d20b94e
This commit is contained in:
parent
a162eddf84
commit
63153c7e3b
9 changed files with 436 additions and 1 deletions
|
|
@ -374,6 +374,45 @@ static wchar_t* lastslash(wchar_t* s, int len)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef ENABLE_TESTS
|
||||||
|
DllLoadHookType gDllLoadHook = nullptr;
|
||||||
|
|
||||||
|
void
|
||||||
|
DllBlocklist_SetDllLoadHook(DllLoadHookType aHook)
|
||||||
|
{
|
||||||
|
gDllLoadHook = aHook;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CallDllLoadHook(bool aDllLoaded, NTSTATUS aStatus, HANDLE aDllBase, PUNICODE_STRING aDllName)
|
||||||
|
{
|
||||||
|
if (gDllLoadHook) {
|
||||||
|
gDllLoadHook(aDllLoaded, aStatus, aDllBase, aDllName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateThreadHookType gCreateThreadHook = nullptr;
|
||||||
|
|
||||||
|
void
|
||||||
|
DllBlocklist_SetCreateThreadHook(CreateThreadHookType aHook)
|
||||||
|
{
|
||||||
|
gCreateThreadHook = aHook;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CallCreateThreadHook(bool aWasAllowed, void* aStartAddress)
|
||||||
|
{
|
||||||
|
if (gCreateThreadHook) {
|
||||||
|
gCreateThreadHook(aWasAllowed, aStartAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // ENABLE_TESTS
|
||||||
|
#define CallDllLoadHook(...)
|
||||||
|
#define CallCreateThreadHook(...)
|
||||||
|
#endif // ENABLE_TESTS
|
||||||
|
|
||||||
static NTSTATUS NTAPI
|
static NTSTATUS NTAPI
|
||||||
patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle)
|
patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle)
|
||||||
{
|
{
|
||||||
|
|
@ -453,6 +492,7 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
|
||||||
char * end = nullptr;
|
char * end = nullptr;
|
||||||
_strtoui64(dot+1, &end, 16);
|
_strtoui64(dot+1, &end, 16);
|
||||||
if (end == dot+13) {
|
if (end == dot+13) {
|
||||||
|
CallDllLoadHook(false, STATUS_DLL_NOT_FOUND, 0, moduleFileName);
|
||||||
return STATUS_DLL_NOT_FOUND;
|
return STATUS_DLL_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -463,6 +503,7 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
|
||||||
current++;
|
current++;
|
||||||
}
|
}
|
||||||
if (current == dot) {
|
if (current == dot) {
|
||||||
|
CallDllLoadHook(false, STATUS_DLL_NOT_FOUND, 0, moduleFileName);
|
||||||
return STATUS_DLL_NOT_FOUND;
|
return STATUS_DLL_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -510,6 +551,7 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
|
||||||
if (!full_fname) {
|
if (!full_fname) {
|
||||||
// uh, we couldn't find the DLL at all, so...
|
// uh, we couldn't find the DLL at all, so...
|
||||||
printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
|
printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
|
||||||
|
CallDllLoadHook(false, STATUS_DLL_NOT_FOUND, 0, moduleFileName);
|
||||||
return STATUS_DLL_NOT_FOUND;
|
return STATUS_DLL_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -548,6 +590,7 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam
|
||||||
if (!load_ok) {
|
if (!load_ok) {
|
||||||
printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
|
printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
|
||||||
DllBlockSet::Add(info->name, fVersion);
|
DllBlockSet::Add(info->name, fVersion);
|
||||||
|
CallDllLoadHook(false, STATUS_DLL_NOT_FOUND, 0, moduleFileName);
|
||||||
return STATUS_DLL_NOT_FOUND;
|
return STATUS_DLL_NOT_FOUND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -570,7 +613,9 @@ continue_loading:
|
||||||
AutoSuppressStackWalking suppress;
|
AutoSuppressStackWalking suppress;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return stub_LdrLoadDll(filePath, flags, moduleFileName, handle);
|
NTSTATUS ret = stub_LdrLoadDll(filePath, flags, moduleFileName, handle);
|
||||||
|
CallDllLoadHook(true, ret, handle ? *handle : 0, moduleFileName);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(NIGHTLY_BUILD)
|
#if defined(NIGHTLY_BUILD)
|
||||||
|
|
@ -616,7 +661,10 @@ patched_BaseThreadInitThunk(BOOL aIsInitialThread, void* aStartAddress,
|
||||||
void* aThreadParam)
|
void* aThreadParam)
|
||||||
{
|
{
|
||||||
if (ShouldBlockThread(aStartAddress)) {
|
if (ShouldBlockThread(aStartAddress)) {
|
||||||
|
CallCreateThreadHook(false, aStartAddress);
|
||||||
aStartAddress = (void*)NopThreadProc;
|
aStartAddress = (void*)NopThreadProc;
|
||||||
|
} else {
|
||||||
|
CallCreateThreadHook(true, aStartAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
stub_BaseThreadInitThunk(aIsInitialThread, aStartAddress, aThreadParam);
|
stub_BaseThreadInitThunk(aIsInitialThread, aStartAddress, aThreadParam);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@
|
||||||
#if (defined(_MSC_VER) || defined(__MINGW32__)) && (defined(_M_IX86) || defined(_M_X64))
|
#if (defined(_MSC_VER) || defined(__MINGW32__)) && (defined(_M_IX86) || defined(_M_X64))
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#ifdef ENABLE_TESTS
|
||||||
|
#include <winternl.h>
|
||||||
|
#endif // ENABLE_TESTS
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/Types.h"
|
#include "mozilla/Types.h"
|
||||||
|
|
||||||
|
|
@ -25,6 +28,14 @@ MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags = eDllBlocklistInitFla
|
||||||
MFBT_API void DllBlocklist_WriteNotes(HANDLE file);
|
MFBT_API void DllBlocklist_WriteNotes(HANDLE file);
|
||||||
MFBT_API bool DllBlocklist_CheckStatus();
|
MFBT_API bool DllBlocklist_CheckStatus();
|
||||||
|
|
||||||
|
#ifdef ENABLE_TESTS
|
||||||
|
typedef void (*DllLoadHookType)(bool aDllLoaded, NTSTATUS aNtStatus,
|
||||||
|
HANDLE aDllBase, PUNICODE_STRING aDllName);
|
||||||
|
MFBT_API void DllBlocklist_SetDllLoadHook(DllLoadHookType aHook);
|
||||||
|
typedef void (*CreateThreadHookType)(bool aWasAllowed, void *aStartAddress);
|
||||||
|
MFBT_API void DllBlocklist_SetCreateThreadHook(CreateThreadHookType aHook);
|
||||||
|
#endif // ENABLE_TESTS
|
||||||
|
|
||||||
// Forward declaration
|
// Forward declaration
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace glue {
|
namespace glue {
|
||||||
|
|
|
||||||
50
mozglue/tests/gtest/Injector/Injector.cpp
Normal file
50
mozglue/tests/gtest/Injector/Injector.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc < 4) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Not enough command line arguments.\n"
|
||||||
|
"Command line syntax:\n"
|
||||||
|
"Injector.exe [pid] [startAddr] [threadParam]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD pid = strtoul(argv[1], 0, 0);
|
||||||
|
#ifdef HAVE_64BIT_BUILD
|
||||||
|
void* startAddr = (void*)strtoull(argv[2], 0, 0);
|
||||||
|
void* threadParam = (void*)strtoull(argv[3], 0, 0);
|
||||||
|
#else
|
||||||
|
void* startAddr = (void*)strtoul(argv[2], 0, 0);
|
||||||
|
void* threadParam = (void*)strtoul(argv[3], 0, 0);
|
||||||
|
#endif
|
||||||
|
HANDLE targetProc = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
|
||||||
|
PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
|
||||||
|
FALSE,
|
||||||
|
pid);
|
||||||
|
if (targetProc == nullptr) {
|
||||||
|
fprintf(stderr, "Error %lu opening target process, PID %lu \n", GetLastError(), pid);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE hThread = CreateRemoteThread(targetProc, nullptr, 0,
|
||||||
|
(LPTHREAD_START_ROUTINE)startAddr,
|
||||||
|
threadParam, 0, nullptr);
|
||||||
|
if (hThread == nullptr) {
|
||||||
|
fprintf(stderr, "Error %lu in CreateRemoteThread\n", GetLastError());
|
||||||
|
CloseHandle(targetProc);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(hThread);
|
||||||
|
CloseHandle(targetProc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
9
mozglue/tests/gtest/Injector/moz.build
Normal file
9
mozglue/tests/gtest/Injector/moz.build
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# 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 http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
DIST_INSTALL = False
|
||||||
|
|
||||||
|
SimplePrograms(['Injector'])
|
||||||
|
|
||||||
|
TEST_HARNESS_FILES.gtest += ['!Injector.exe']
|
||||||
11
mozglue/tests/gtest/InjectorDLL/InjectorDLL.cpp
Normal file
11
mozglue/tests/gtest/InjectorDLL/InjectorDLL.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD aReason, LPVOID)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
14
mozglue/tests/gtest/InjectorDLL/moz.build
Normal file
14
mozglue/tests/gtest/InjectorDLL/moz.build
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||||
|
# 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 http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
DIST_INSTALL = False
|
||||||
|
|
||||||
|
SharedLibrary('InjectorDLL')
|
||||||
|
|
||||||
|
UNIFIED_SOURCES = [
|
||||||
|
'InjectorDLL.cpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
TEST_HARNESS_FILES.gtest += ['!InjectorDLL.dll']
|
||||||
277
mozglue/tests/gtest/TestDLLEject.cpp
Normal file
277
mozglue/tests/gtest/TestDLLEject.cpp
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
/* 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 http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "nsReadableUtils.h"
|
||||||
|
#include "nsString.h"
|
||||||
|
#include "nsUnicharUtils.h"
|
||||||
|
#include "mozilla/ArrayUtils.h"
|
||||||
|
#include "mozilla/ScopeExit.h"
|
||||||
|
#include "mozilla/UniquePtr.h"
|
||||||
|
#include "mozilla/WindowsDllBlocklist.h"
|
||||||
|
|
||||||
|
static HANDLE sThreadWasBlocked = 0;
|
||||||
|
static HANDLE sThreadWasAllowed = 0;
|
||||||
|
static HANDLE sDllWasLoaded = 0;
|
||||||
|
static uintptr_t sStartAddress = 0;
|
||||||
|
|
||||||
|
static const int sTimeoutMS = 10000;
|
||||||
|
|
||||||
|
#define DLL_LEAF_NAME (u"InjectorDLL.dll")
|
||||||
|
|
||||||
|
static nsString
|
||||||
|
makeString(PUNICODE_STRING aOther)
|
||||||
|
{
|
||||||
|
size_t numChars = aOther->Length / sizeof(WCHAR);
|
||||||
|
return nsString((const char16_t *)aOther->Buffer, numChars);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
DllLoadHook(bool aDllLoaded, NTSTATUS aStatus, HANDLE aDllBase,
|
||||||
|
PUNICODE_STRING aDllName)
|
||||||
|
{
|
||||||
|
nsString str = makeString(aDllName);
|
||||||
|
|
||||||
|
nsString dllName = nsString(DLL_LEAF_NAME);
|
||||||
|
if (StringEndsWith(str, dllName, nsCaseInsensitiveStringComparator())) {
|
||||||
|
if (aDllLoaded) {
|
||||||
|
SetEvent(sDllWasLoaded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
CreateThreadHook(bool aWasAllowed, void* aStartAddress)
|
||||||
|
{
|
||||||
|
if (sStartAddress == (uintptr_t)aStartAddress) {
|
||||||
|
if (!aWasAllowed) {
|
||||||
|
SetEvent(sThreadWasBlocked);
|
||||||
|
} else {
|
||||||
|
SetEvent(sThreadWasAllowed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function tests that we correctly block DLLs injected into this process
|
||||||
|
* via an injection technique which calls CreateRemoteThread with LoadLibrary*()
|
||||||
|
* as the thread start address, and the path to the DLL as the thread param.
|
||||||
|
*
|
||||||
|
* We prevent this technique by blocking threads with a start address in any
|
||||||
|
* LoadLibrary*() APIs.
|
||||||
|
*
|
||||||
|
* This function launches Injector.exe which simulates a 3rd-party application
|
||||||
|
* executing this technique.
|
||||||
|
*
|
||||||
|
* @param aGetArgsProc A callable procedure that specifies the thread start
|
||||||
|
* address and thread param passed as arguments to
|
||||||
|
* Injector.exe, which are in turn passed as arguments to
|
||||||
|
* CreateRemoteThread. This procedure is defined as such:
|
||||||
|
*
|
||||||
|
* void (*aGetArgsProc)(const nsString& aDllPath,
|
||||||
|
* const nsCString& aDllPathC,
|
||||||
|
* uintptr_t& startAddress,
|
||||||
|
* uintptr_t& threadParam);
|
||||||
|
*
|
||||||
|
* aDllPath is a WCHAR-friendly path to InjectorDLL.dll.
|
||||||
|
* Its memory will persist during the injection attempt.
|
||||||
|
*
|
||||||
|
* aDllPathC is the equivalent char-friendly path.
|
||||||
|
*
|
||||||
|
* startAddress and threadParam are passed into
|
||||||
|
* CreateRemoteThread as arguments.
|
||||||
|
*/
|
||||||
|
template<typename TgetArgsProc>
|
||||||
|
static void
|
||||||
|
DoTest_CreateRemoteThread_LoadLibrary(TgetArgsProc aGetArgsProc)
|
||||||
|
{
|
||||||
|
sThreadWasBlocked = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
if (!sThreadWasBlocked) {
|
||||||
|
EXPECT_TRUE(!"Unable to create sThreadWasBlocked event");
|
||||||
|
ASSERT_EQ(GetLastError(), ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
sThreadWasAllowed = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
if (!sThreadWasAllowed) {
|
||||||
|
EXPECT_TRUE(!"Unable to create sThreadWasAllowed event");
|
||||||
|
ASSERT_EQ(GetLastError(), ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
sDllWasLoaded = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||||
|
if (!sDllWasLoaded) {
|
||||||
|
EXPECT_TRUE(!"Unable to create sDllWasLoaded event");
|
||||||
|
ASSERT_EQ(GetLastError(), ERROR_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto closeEvents = mozilla::MakeScopeExit([&](){
|
||||||
|
CloseHandle(sThreadWasAllowed);
|
||||||
|
CloseHandle(sThreadWasBlocked);
|
||||||
|
CloseHandle(sDllWasLoaded);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hook into our DLL and thread blocking routines during this test.
|
||||||
|
DllBlocklist_SetDllLoadHook(DllLoadHook);
|
||||||
|
DllBlocklist_SetCreateThreadHook(CreateThreadHook);
|
||||||
|
auto undoHooks = mozilla::MakeScopeExit([&](){
|
||||||
|
DllBlocklist_SetDllLoadHook(nullptr);
|
||||||
|
DllBlocklist_SetCreateThreadHook(nullptr);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Launch Injector.exe.
|
||||||
|
STARTUPINFOW si = { 0 };
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
::GetStartupInfoW(&si);
|
||||||
|
PROCESS_INFORMATION pi = { 0 };
|
||||||
|
|
||||||
|
nsString path(u"Injector.exe");
|
||||||
|
nsString dllPath(DLL_LEAF_NAME);
|
||||||
|
nsCString dllPathC = NS_ConvertUTF16toUTF8(dllPath);
|
||||||
|
|
||||||
|
uintptr_t threadParam;
|
||||||
|
aGetArgsProc(dllPath, dllPathC, sStartAddress, threadParam);
|
||||||
|
|
||||||
|
path.AppendPrintf(" %lu 0x%p 0x%p", GetCurrentProcessId(), sStartAddress,
|
||||||
|
threadParam);
|
||||||
|
if (::CreateProcessW(NULL, path.get(), 0, 0, FALSE, 0, NULL, NULL,
|
||||||
|
&si, &pi) == FALSE) {
|
||||||
|
EXPECT_TRUE(!"Error in CreateProcessW() launching Injector.exe");
|
||||||
|
ASSERT_EQ(GetLastError(), ERROR_SUCCESS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure Injector.exe doesn't stay running after this test finishes.
|
||||||
|
auto cleanup = mozilla::MakeScopeExit([&](){
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
EXPECT_TRUE("Shutting down.");
|
||||||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wait for information to come in and complete the test.
|
||||||
|
HANDLE handles[] = {
|
||||||
|
sThreadWasBlocked,
|
||||||
|
sThreadWasAllowed,
|
||||||
|
sDllWasLoaded,
|
||||||
|
pi.hProcess
|
||||||
|
};
|
||||||
|
int handleCount = mozilla::ArrayLength(handles);
|
||||||
|
bool keepGoing = true; // Set to false to signal that the test is over.
|
||||||
|
|
||||||
|
while(keepGoing) {
|
||||||
|
switch(WaitForMultipleObjectsEx(handleCount, handles,
|
||||||
|
FALSE, sTimeoutMS, FALSE)) {
|
||||||
|
case WAIT_OBJECT_0: { // sThreadWasBlocked
|
||||||
|
EXPECT_TRUE("Thread was blocked successfully.");
|
||||||
|
// No need to continue testing; blocking was successful.
|
||||||
|
keepGoing = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WAIT_OBJECT_0 + 1: { // sThreadWasAllowed
|
||||||
|
EXPECT_TRUE(!"Thread was allowed but should have been blocked.");
|
||||||
|
// No need to continue testing; blocking failed.
|
||||||
|
keepGoing = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WAIT_OBJECT_0 + 2: { // sDllWasLoaded
|
||||||
|
EXPECT_TRUE(!"DLL was loaded.");
|
||||||
|
// No need to continue testing; blocking failed and the DLL was
|
||||||
|
// consequently loaded. In theory we should never see this fire, because
|
||||||
|
// the thread being allowed should already trigger a test failure.
|
||||||
|
keepGoing = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WAIT_OBJECT_0 + 3: { // pi.hProcess
|
||||||
|
// Check to see if we got an error code from Injector.exe, in which case
|
||||||
|
// fail the test and exit.
|
||||||
|
DWORD exitCode;
|
||||||
|
if (!GetExitCodeProcess(pi.hProcess, &exitCode)) {
|
||||||
|
EXPECT_TRUE(!"Injector.exe exited but we were unable to get the exit code.");
|
||||||
|
keepGoing = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(exitCode, 0);
|
||||||
|
if (exitCode != 0) {
|
||||||
|
EXPECT_TRUE(!"Injector.exe returned non-zero exit code");
|
||||||
|
keepGoing = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Process exited successfully. This can be ignored; we expect to get an
|
||||||
|
// event whether the DLL was loaded or blocked.
|
||||||
|
EXPECT_TRUE("Process exited as expected.");
|
||||||
|
handleCount--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WAIT_TIMEOUT:
|
||||||
|
default: {
|
||||||
|
EXPECT_TRUE(!"An error or timeout occurred while waiting for activity "
|
||||||
|
"from Injector.exe");
|
||||||
|
keepGoing = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-check that injectordll is not loaded.
|
||||||
|
auto hExisting = GetModuleHandleW(dllPath.get());
|
||||||
|
EXPECT_TRUE(!hExisting);
|
||||||
|
|
||||||
|
// If the DLL was erroneously loaded, attempt to unload it before exiting.
|
||||||
|
if (hExisting) {
|
||||||
|
FreeLibrary(hExisting);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestInjectEject, CreateRemoteThread_LoadLibraryA)
|
||||||
|
{
|
||||||
|
DoTest_CreateRemoteThread_LoadLibrary([](const nsString& dllPath,
|
||||||
|
const nsCString& dllPathC,
|
||||||
|
uintptr_t& aStartAddress,
|
||||||
|
uintptr_t& aThreadParam){
|
||||||
|
HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
|
||||||
|
aStartAddress = (uintptr_t)GetProcAddress(hKernel32, "LoadLibraryA");
|
||||||
|
aThreadParam = (uintptr_t)dllPathC.get();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestInjectEject, CreateRemoteThread_LoadLibraryW)
|
||||||
|
{
|
||||||
|
DoTest_CreateRemoteThread_LoadLibrary([](const nsString& dllPath,
|
||||||
|
const nsCString& dllPathC,
|
||||||
|
uintptr_t& aStartAddress,
|
||||||
|
uintptr_t& aThreadParam){
|
||||||
|
HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
|
||||||
|
aStartAddress = (uintptr_t)GetProcAddress(hKernel32, "LoadLibraryW");
|
||||||
|
aThreadParam = (uintptr_t)dllPath.get();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestInjectEject, CreateRemoteThread_LoadLibraryExW)
|
||||||
|
{
|
||||||
|
DoTest_CreateRemoteThread_LoadLibrary([](const nsString& dllPath,
|
||||||
|
const nsCString& dllPathC,
|
||||||
|
uintptr_t& aStartAddress,
|
||||||
|
uintptr_t& aThreadParam){
|
||||||
|
HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
|
||||||
|
// LoadLibraryEx requires three arguments so this injection method may not
|
||||||
|
// be viable. It's certainly not an allowable thread start in any case.
|
||||||
|
aStartAddress = (uintptr_t)GetProcAddress(hKernel32, "LoadLibraryExW");
|
||||||
|
aThreadParam = (uintptr_t)dllPath.get();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TestInjectEject, CreateRemoteThread_LoadLibraryExA)
|
||||||
|
{
|
||||||
|
DoTest_CreateRemoteThread_LoadLibrary([](const nsString& dllPath,
|
||||||
|
const nsCString& dllPathC,
|
||||||
|
uintptr_t& aStartAddress,
|
||||||
|
uintptr_t& aThreadParam){
|
||||||
|
HMODULE hKernel32 = GetModuleHandleW(L"Kernel32");
|
||||||
|
aStartAddress = (uintptr_t)GetProcAddress(hKernel32, "LoadLibraryExA");
|
||||||
|
aThreadParam = (uintptr_t)dllPathC.get();
|
||||||
|
});
|
||||||
|
}
|
||||||
14
mozglue/tests/gtest/moz.build
Normal file
14
mozglue/tests/gtest/moz.build
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# 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 http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
UNIFIED_SOURCES += [
|
||||||
|
'TestDLLEject.cpp'
|
||||||
|
]
|
||||||
|
|
||||||
|
FINAL_LIBRARY = 'xul-gtest'
|
||||||
|
|
||||||
|
TEST_DIRS += [
|
||||||
|
'Injector',
|
||||||
|
'InjectorDLL',
|
||||||
|
]
|
||||||
|
|
@ -17,4 +17,5 @@ CppUnitTests([
|
||||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||||
TEST_DIRS += [
|
TEST_DIRS += [
|
||||||
'interceptor',
|
'interceptor',
|
||||||
|
'gtest',
|
||||||
]
|
]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue