1211
This commit is contained in:
72
test/Detours/tests/Makefile
Normal file
72
test/Detours/tests/Makefile
Normal file
@@ -0,0 +1,72 @@
|
||||
##############################################################################
|
||||
##
|
||||
## Detours Unit Tests.
|
||||
##
|
||||
## Microsoft Research Detours Package
|
||||
##
|
||||
## Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
##
|
||||
|
||||
ROOT = ..
|
||||
!include ..\samples\common.mak
|
||||
|
||||
DEPS = $(LIBD)\detours.lib
|
||||
LIBS=$(LIBS) kernel32.lib rpcrt4.lib
|
||||
CFLAGS=$(CFLAGS) /EHsc /DCATCH_CONFIG_NO_WINDOWS_SEH
|
||||
|
||||
##############################################################################
|
||||
|
||||
all: dirs \
|
||||
$(BIND)\unittests.exe \
|
||||
\
|
||||
|
||||
##############################################################################
|
||||
|
||||
dirs:
|
||||
@if not exist $(BIND) mkdir $(BIND) && echo. Created $(BIND)
|
||||
@if not exist $(OBJD) mkdir $(OBJD) && echo. Created $(OBJD)
|
||||
|
||||
$(OBJD)\main.obj : main.cpp
|
||||
$(OBJD)\test_module_api.obj : test_module_api.cpp
|
||||
$(OBJD)\test_image_api.obj : test_image_api.cpp
|
||||
$(OBJD)\corruptor.obj : corruptor.cpp
|
||||
$(OBJD)\process_helpers.obj : process_helpers.cpp
|
||||
$(OBJD)\payload.obj : payload.cpp
|
||||
|
||||
$(BIND)\unittests.exe : $(OBJD)\main.obj \
|
||||
$(OBJD)\test_module_api.obj \
|
||||
$(OBJD)\test_image_api.obj \
|
||||
$(OBJD)\corruptor.obj \
|
||||
$(OBJD)\process_helpers.obj \
|
||||
$(OBJD)\payload.obj $(DEPS)
|
||||
cl $(CFLAGS) /Fe$@ /Fd$(@R).pdb \
|
||||
$(OBJD)\main.obj \
|
||||
$(OBJD)\test_module_api.obj \
|
||||
$(OBJD)\test_image_api.obj \
|
||||
$(OBJD)\corruptor.obj \
|
||||
$(OBJD)\process_helpers.obj \
|
||||
$(OBJD)\payload.obj \
|
||||
/link $(LINKFLAGS) $(LIBS) /subsystem:console
|
||||
|
||||
##############################################################################
|
||||
|
||||
clean:
|
||||
-del *~ 2>nul
|
||||
-del $(BIND)\unittests*.* 2>nul
|
||||
-rmdir /q /s $(OBJD) 2>nul
|
||||
|
||||
realclean: clean
|
||||
-rmdir /q /s $(OBJDS) 2>nul
|
||||
|
||||
option:
|
||||
##############################################################################
|
||||
|
||||
test: all
|
||||
@cls
|
||||
$(BIND)\unittests.exe --reporter console --success --durations yes
|
||||
|
||||
debug: all
|
||||
windbg -o $(BIND)\unittests.exe
|
||||
|
||||
|
||||
################################################################# End of File.
|
||||
17799
test/Detours/tests/catch.hpp
Normal file
17799
test/Detours/tests/catch.hpp
Normal file
File diff suppressed because it is too large
Load Diff
67
test/Detours/tests/corruptor.cpp
Normal file
67
test/Detours/tests/corruptor.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Unit Test Image Corruptor (corruptor.cpp of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#include "windows.h"
|
||||
#include "corruptor.h"
|
||||
|
||||
ImageCorruptor::ImageCorruptor(PIMAGE_DOS_HEADER Header)
|
||||
{
|
||||
m_TargetDosHeader = Header;
|
||||
m_OriginalDosHeader = *Header;
|
||||
m_OriginalDosProtection = 0;
|
||||
m_TargetNtHeaders = (PIMAGE_NT_HEADERS)((PBYTE)Header + Header->e_lfanew);
|
||||
m_OriginalNtHeaders = *m_TargetNtHeaders;
|
||||
m_OriginalNtProtection = 0;
|
||||
|
||||
VirtualProtect(
|
||||
m_TargetDosHeader,
|
||||
sizeof(*m_TargetDosHeader),
|
||||
PAGE_READWRITE,
|
||||
&m_OriginalDosProtection);
|
||||
|
||||
VirtualProtect(
|
||||
m_TargetNtHeaders,
|
||||
sizeof(*m_TargetNtHeaders),
|
||||
PAGE_READWRITE,
|
||||
&m_OriginalNtProtection);
|
||||
}
|
||||
|
||||
ImageCorruptor::~ImageCorruptor()
|
||||
{
|
||||
// Restore original header contents.
|
||||
//
|
||||
*m_TargetDosHeader = m_OriginalDosHeader;
|
||||
*m_TargetNtHeaders = m_OriginalNtHeaders;
|
||||
|
||||
// Restore original protection of DOS header.
|
||||
//
|
||||
DWORD OldProtection {};
|
||||
VirtualProtect(
|
||||
m_TargetDosHeader,
|
||||
sizeof(*m_TargetDosHeader),
|
||||
m_OriginalDosProtection,
|
||||
&OldProtection);
|
||||
|
||||
// Restore original protection of NT headers.
|
||||
//
|
||||
VirtualProtect(
|
||||
m_TargetNtHeaders,
|
||||
sizeof(*m_TargetNtHeaders),
|
||||
m_OriginalNtProtection,
|
||||
&OldProtection);
|
||||
}
|
||||
|
||||
void ImageCorruptor::ModifyDosMagic(WORD Value)
|
||||
{
|
||||
m_TargetDosHeader->e_magic = Value;
|
||||
}
|
||||
|
||||
void ImageCorruptor::ModifyNtSignature(ULONG Value)
|
||||
{
|
||||
m_TargetNtHeaders->Signature = Value;
|
||||
}
|
||||
47
test/Detours/tests/corruptor.h
Normal file
47
test/Detours/tests/corruptor.h
Normal file
@@ -0,0 +1,47 @@
|
||||
//////////////////////////////////////////////////////
|
||||
//
|
||||
// Unit Test Image Corruptor (corruptor.h of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
class ImageCorruptor final
|
||||
{
|
||||
public:
|
||||
|
||||
ImageCorruptor(PIMAGE_DOS_HEADER Header);
|
||||
|
||||
~ImageCorruptor();
|
||||
|
||||
void ModifyDosMagic(WORD Value);
|
||||
|
||||
void ModifyNtSignature(ULONG Value);
|
||||
|
||||
private:
|
||||
// Pointer to the target image header to corrupt.
|
||||
//
|
||||
PIMAGE_DOS_HEADER m_TargetDosHeader;
|
||||
|
||||
// Cached copy of the DOS header, to restore state with.
|
||||
//
|
||||
IMAGE_DOS_HEADER m_OriginalDosHeader;
|
||||
|
||||
// The original protection of the DOS header.
|
||||
//
|
||||
DWORD m_OriginalDosProtection;
|
||||
|
||||
// Pointer to the target NT image header to corrupt.
|
||||
//
|
||||
PIMAGE_NT_HEADERS m_TargetNtHeaders;
|
||||
|
||||
// Cached copy of the NT headers, to restore state with.
|
||||
//
|
||||
IMAGE_NT_HEADERS m_OriginalNtHeaders;
|
||||
|
||||
// The original protection of the NT headers.
|
||||
//
|
||||
DWORD m_OriginalNtProtection;
|
||||
};
|
||||
10
test/Detours/tests/main.cpp
Normal file
10
test/Detours/tests/main.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Unit Test Main (main.cpp of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
||||
25
test/Detours/tests/payload.cpp
Normal file
25
test/Detours/tests/payload.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Test Payload for Detours Module API tests (payload.cpp of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#include "payload.h"
|
||||
|
||||
// Define a detours payload for testing.
|
||||
//
|
||||
#pragma data_seg(".detour")
|
||||
|
||||
static CPrivateStuff private_stuff = {
|
||||
DETOUR_SECTION_HEADER_DECLARE(sizeof(CPrivateStuff)),
|
||||
{
|
||||
(sizeof(CPrivateStuff) - sizeof(DETOUR_SECTION_HEADER)),
|
||||
0,
|
||||
TEST_PAYLOAD_GUID
|
||||
},
|
||||
"Testing Payload 123"
|
||||
};
|
||||
|
||||
#pragma data_seg()
|
||||
25
test/Detours/tests/payload.h
Normal file
25
test/Detours/tests/payload.h
Normal file
@@ -0,0 +1,25 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Test Payload for Detours Module API tests (payload.h of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#include "windows.h"
|
||||
#include "detours.h"
|
||||
|
||||
// {85ECA590-6E6A-40FC-BA75-451D96A2A746}
|
||||
static constexpr GUID TEST_PAYLOAD_GUID =
|
||||
{ 0x85eca590, 0x6e6a, 0x40fc, { 0xba, 0x75, 0x45, 0x1d, 0x96, 0xa2, 0xa7, 0x46 } };
|
||||
|
||||
static constexpr std::size_t TEST_PAYLOAD_SIZE = 32;
|
||||
|
||||
struct CPrivateStuff
|
||||
{
|
||||
DETOUR_SECTION_HEADER header;
|
||||
DETOUR_SECTION_RECORD record;
|
||||
CHAR szMessage[TEST_PAYLOAD_SIZE];
|
||||
};
|
||||
44
test/Detours/tests/process_helpers.cpp
Normal file
44
test/Detours/tests/process_helpers.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Process Test Helpers (process_helpers.cpp of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#include "windows.h"
|
||||
#include "process_helpers.h"
|
||||
|
||||
HRESULT GetProcessFileName(HANDLE process, std::wstring& filename)
|
||||
{
|
||||
filename.resize(MAX_PATH);
|
||||
|
||||
DWORD size = static_cast<DWORD>(filename.size()) + 1;
|
||||
if (QueryFullProcessImageNameW(process, 0, &filename[0], &size))
|
||||
{
|
||||
filename.resize(size);
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT CreateSuspendedCopy(TerminateOnScopeExit& wrapper)
|
||||
{
|
||||
std::wstring location;
|
||||
const auto hr = GetProcessFileName(GetCurrentProcess(), location);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
STARTUPINFOW si = { sizeof(si) };
|
||||
if (!CreateProcessW(location.c_str(), nullptr, nullptr, nullptr, false, CREATE_SUSPENDED, nullptr, nullptr, &si, &wrapper.information))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
37
test/Detours/tests/process_helpers.h
Normal file
37
test/Detours/tests/process_helpers.h
Normal file
@@ -0,0 +1,37 @@
|
||||
//////////////////////////////////////////////////////
|
||||
//
|
||||
// Process Test Helpers (process_helpers.h of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
struct TerminateOnScopeExit
|
||||
{
|
||||
PROCESS_INFORMATION information;
|
||||
|
||||
TerminateOnScopeExit(const TerminateOnScopeExit&) = delete;
|
||||
TerminateOnScopeExit& operator=(const TerminateOnScopeExit&) = delete;
|
||||
|
||||
~TerminateOnScopeExit()
|
||||
{
|
||||
if (information.hThread)
|
||||
{
|
||||
TerminateThread(information.hThread, 0);
|
||||
CloseHandle(information.hThread);
|
||||
}
|
||||
|
||||
if (information.hProcess)
|
||||
{
|
||||
TerminateProcess(information.hProcess, 0);
|
||||
CloseHandle(information.hProcess);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
HRESULT GetProcessFileName(HANDLE process, std::wstring& filename);
|
||||
HRESULT CreateSuspendedCopy(TerminateOnScopeExit& wrapper);
|
||||
21
test/Detours/tests/test_image_api.cpp
Normal file
21
test/Detours/tests/test_image_api.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Unit Tests for Detours Image API (test_image_api.cpp of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#include "catch.hpp"
|
||||
#include "windows.h"
|
||||
#include "detours.h"
|
||||
|
||||
TEST_CASE("DetourBinaryOpen", "[image]")
|
||||
{
|
||||
SECTION("Passing INVALID_HANDLE, results in error")
|
||||
{
|
||||
auto binary = DetourBinaryOpen(INVALID_HANDLE_VALUE);
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_HANDLE );
|
||||
REQUIRE( binary == nullptr );
|
||||
}
|
||||
}
|
||||
731
test/Detours/tests/test_module_api.cpp
Normal file
731
test/Detours/tests/test_module_api.cpp
Normal file
@@ -0,0 +1,731 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Unit Tests for Detours Module API (test_module_api.cpp of unittests.exe)
|
||||
//
|
||||
// Microsoft Research Detours Package
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
#include "catch.hpp"
|
||||
#include "windows.h"
|
||||
|
||||
#define DETOURS_INTERNAL
|
||||
|
||||
#include "detours.h"
|
||||
#include "corruptor.h"
|
||||
#include "payload.h"
|
||||
#include "process_helpers.h"
|
||||
|
||||
// Expose the image base of the current module for test assertions.
|
||||
//
|
||||
extern "C" IMAGE_DOS_HEADER __ImageBase;
|
||||
|
||||
// Expose default module entry point for test assertions.
|
||||
//
|
||||
extern "C" int mainCRTStartup();
|
||||
|
||||
// Dummy function pointer used for tests.
|
||||
//
|
||||
void NoopFunction() { }
|
||||
|
||||
TEST_CASE("DetourLoadImageHlp", "[module]")
|
||||
{
|
||||
SECTION("Passing own function, results in own HMODULE")
|
||||
{
|
||||
auto info = DetourLoadImageHlp();
|
||||
|
||||
REQUIRE( info != nullptr );
|
||||
REQUIRE( info->hDbgHelp != NULL);
|
||||
REQUIRE( info->pfImagehlpApiVersionEx != nullptr );
|
||||
REQUIRE( info->pfSymInitialize != nullptr );
|
||||
REQUIRE( info->pfSymSetOptions != nullptr );
|
||||
REQUIRE( info->pfSymGetOptions != nullptr );
|
||||
REQUIRE( info->pfSymLoadModule64 != nullptr );
|
||||
REQUIRE( info->pfSymGetModuleInfo64 != nullptr );
|
||||
REQUIRE( info->pfSymFromName != nullptr );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourFindFunction", "[module]")
|
||||
{
|
||||
SECTION("Passing nullptr for all parameters, results in nullptr")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto func = DetourFindFunction(nullptr, nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_PARAMETER );
|
||||
REQUIRE( func == nullptr );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr for function, results in nullptr")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto func = DetourFindFunction("ntdll.dll", nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_PARAMETER );
|
||||
REQUIRE( func == nullptr );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr for module, results in nullptr")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto func = DetourFindFunction(nullptr, "FunctionThatDoesntExist");
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_PARAMETER );
|
||||
REQUIRE( func == nullptr );
|
||||
}
|
||||
|
||||
SECTION("Finding ntdll export is successful")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto func = DetourFindFunction("ntdll.dll", "NtDeviceIoControlFile");
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( func != nullptr );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourGetContainingModule", "[module]")
|
||||
{
|
||||
SECTION("Passing nullptr, results in nullptr")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto mod = DetourGetContainingModule(nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_BAD_EXE_FORMAT );
|
||||
REQUIRE( mod == nullptr );
|
||||
}
|
||||
|
||||
SECTION("Passing GetCommandLineW, results in kernel32 HMODULE")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
auto mod = DetourGetContainingModule(GetCommandLineW);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( mod == LoadLibraryW(L"kernel32.dll") );
|
||||
}
|
||||
|
||||
SECTION("Passing own function, results in own HMODULE")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
auto mod = DetourGetContainingModule(NoopFunction);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( mod == reinterpret_cast<HMODULE>(&__ImageBase) );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourGetEntyPoint", "[module]")
|
||||
{
|
||||
SECTION("Passing nullptr, results in CRT entrypoint")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
auto entry = DetourGetEntryPoint(nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( entry == mainCRTStartup );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr, equals executing image")
|
||||
{
|
||||
REQUIRE( DetourGetEntryPoint(nullptr) ==
|
||||
DetourGetEntryPoint(reinterpret_cast<HMODULE>(&__ImageBase)) );
|
||||
}
|
||||
|
||||
SECTION("Passing ImageBase, results in CRT main")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
auto entry = DetourGetEntryPoint(reinterpret_cast<HMODULE>(&__ImageBase));
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( entry == mainCRTStartup );
|
||||
}
|
||||
|
||||
SECTION("Corrupt image DOS header magic, results in bad exe format error")
|
||||
{
|
||||
ImageCorruptor corruptor(&__ImageBase);
|
||||
corruptor.ModifyDosMagic(0xDEAD);
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto entry = DetourGetEntryPoint(reinterpret_cast<HMODULE>(&__ImageBase));
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_BAD_EXE_FORMAT );
|
||||
REQUIRE( entry == nullptr );
|
||||
}
|
||||
|
||||
SECTION("Corrupt image NT header signature, results in invalid signature error")
|
||||
{
|
||||
ImageCorruptor corruptor(&__ImageBase);
|
||||
corruptor.ModifyNtSignature(0xDEADBEEF);
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto entry = DetourGetEntryPoint(reinterpret_cast<HMODULE>(&__ImageBase));
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_EXE_SIGNATURE );
|
||||
REQUIRE( entry == nullptr );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourGetModuleSize", "[module]")
|
||||
{
|
||||
SECTION("Passing nullptr, results in current module size")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
auto size = DetourGetModuleSize(nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( size > 0 );
|
||||
}
|
||||
|
||||
SECTION("Passing stack, results in error")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
int value;
|
||||
auto size = DetourGetModuleSize(reinterpret_cast<HMODULE>(&value));
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_BAD_EXE_FORMAT);
|
||||
REQUIRE( size == 0 );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr, equals executing image")
|
||||
{
|
||||
REQUIRE( DetourGetModuleSize(nullptr) ==
|
||||
DetourGetModuleSize(reinterpret_cast<HMODULE>(&__ImageBase)) );
|
||||
}
|
||||
|
||||
SECTION("Corrupt image DOS header magic, results in bad exe format error")
|
||||
{
|
||||
ImageCorruptor corruptor(&__ImageBase);
|
||||
corruptor.ModifyDosMagic(0xDEAD);
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto size = DetourGetModuleSize(reinterpret_cast<HMODULE>(&__ImageBase));
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_BAD_EXE_FORMAT );
|
||||
REQUIRE( size == 0 );
|
||||
}
|
||||
|
||||
SECTION("Corrupt image NT header signature, results in invalid signature error")
|
||||
{
|
||||
ImageCorruptor corruptor(&__ImageBase);
|
||||
corruptor.ModifyNtSignature(0xDEADBEEF);
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto size = DetourGetModuleSize(reinterpret_cast<HMODULE>(&__ImageBase));
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_EXE_SIGNATURE );
|
||||
REQUIRE( size == 0 );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourEnumerateModules", "[module]")
|
||||
{
|
||||
SECTION("Passing nullptr, results in current module being returned")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
auto mod = DetourEnumerateModules(nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( mod == reinterpret_cast<HMODULE>(&__ImageBase) );
|
||||
}
|
||||
|
||||
SECTION("Passing stack, results in module")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
int value;
|
||||
auto mod = DetourEnumerateModules(reinterpret_cast<HMODULE>(&value));
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( mod != NULL );
|
||||
}
|
||||
}
|
||||
|
||||
// Export test function, only used for test assertions.
|
||||
//
|
||||
__declspec(dllexport) void TestFunctionExport() { }
|
||||
|
||||
// Context object passed to DetourEnumerateExport(..)
|
||||
//
|
||||
struct EnumerateExportsTestContext
|
||||
{
|
||||
// Number of exports
|
||||
//
|
||||
int ExportCount { 0 };
|
||||
|
||||
// If the 'TestFunctionExport' export exists in the module.
|
||||
//
|
||||
bool ExportFound { false };
|
||||
};
|
||||
|
||||
// Callback for each modue enumerated with DetourEnumerateExport(..)
|
||||
//
|
||||
BOOL CALLBACK ExportCallback(
|
||||
_In_opt_ PVOID pContext,
|
||||
_In_ ULONG nOrdinal,
|
||||
_In_opt_ LPCSTR pszSymbol,
|
||||
_In_opt_ PVOID pbTarget)
|
||||
{
|
||||
(void)pContext;
|
||||
(void)pbTarget;
|
||||
(void)nOrdinal;
|
||||
|
||||
EnumerateExportsTestContext* context =
|
||||
reinterpret_cast<EnumerateExportsTestContext*>(pContext);
|
||||
|
||||
context->ExportCount++;
|
||||
|
||||
context->ExportFound |= Catch::contains(pszSymbol, "TestFunctionExport");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("DetourEnumerateExports", "[module]")
|
||||
{
|
||||
SECTION("Passing nullptr all, results in failure.")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto success = DetourEnumerateExports(nullptr, nullptr, nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_PARAMETER );
|
||||
REQUIRE_FALSE( success );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr for just the module, resolves export in current modulee.")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
EnumerateExportsTestContext context {};
|
||||
auto success = DetourEnumerateExports(nullptr, &context, ExportCallback);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( success );
|
||||
REQUIRE( context.ExportCount == 1 );
|
||||
REQUIRE( context.ExportFound );
|
||||
}
|
||||
|
||||
SECTION("Passing current module, resolves export correctly.")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
EnumerateExportsTestContext context {};
|
||||
auto mod = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
auto success = DetourEnumerateExports(mod, &context, ExportCallback);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( success );
|
||||
|
||||
REQUIRE( context.ExportCount == 1 );
|
||||
REQUIRE( context.ExportFound );
|
||||
}
|
||||
|
||||
SECTION("Passing stack, results in error")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
int value;
|
||||
auto mod = reinterpret_cast<HMODULE>(&value);
|
||||
|
||||
EnumerateExportsTestContext context {};
|
||||
auto success = DetourEnumerateExports(mod, &context, ExportCallback);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_BAD_EXE_FORMAT);
|
||||
REQUIRE_FALSE( success );
|
||||
}
|
||||
|
||||
SECTION("Corrupt image DOS header magic, results in bad exe format error")
|
||||
{
|
||||
ImageCorruptor corruptor(&__ImageBase);
|
||||
corruptor.ModifyDosMagic(0xDEAD);
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
EnumerateExportsTestContext context {};
|
||||
auto mod = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
auto success = DetourEnumerateExports(mod, &context, ExportCallback);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_BAD_EXE_FORMAT );
|
||||
REQUIRE_FALSE( success );
|
||||
}
|
||||
|
||||
SECTION("Corrupt image NT header signature, results in invalid signature error")
|
||||
{
|
||||
ImageCorruptor corruptor(&__ImageBase);
|
||||
corruptor.ModifyNtSignature(0xDEADBEEF);
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
EnumerateExportsTestContext context {};
|
||||
auto mod = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
auto success = DetourEnumerateExports(mod, &context, ExportCallback);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_EXE_SIGNATURE );
|
||||
REQUIRE_FALSE( success );
|
||||
}
|
||||
}
|
||||
|
||||
// Context object passed to DetourEnumerateimportsExport(..)
|
||||
//
|
||||
struct EnumerateImportsTestContext
|
||||
{
|
||||
// Number of imports
|
||||
//
|
||||
int ImportCount { 0 };
|
||||
|
||||
// If the 'TestFunctionExport' export exists in the module.
|
||||
//
|
||||
bool ImportModuleFound { false };
|
||||
|
||||
// Number of imports
|
||||
//
|
||||
int ImportFuncCount { 0 };
|
||||
|
||||
// If the 'TestFunctionExport' export exists in the module.
|
||||
//
|
||||
bool ImportFuncFound { false };
|
||||
};
|
||||
|
||||
// Callback for each module enumerated with DetourEnumerateImports(..)
|
||||
//
|
||||
BOOL WINAPI ImportFileCallback(PVOID pContext, HMODULE, PCSTR pszFile)
|
||||
{
|
||||
EnumerateImportsTestContext* context =
|
||||
reinterpret_cast<EnumerateImportsTestContext*>(pContext);
|
||||
|
||||
context->ImportCount++;
|
||||
context->ImportModuleFound |= Catch::contains(pszFile, "ntdll");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Callback for each function enumerated with DetourEnumerateImports(..)
|
||||
//
|
||||
BOOL WINAPI ImportFuncCallback(_In_opt_ PVOID pContext,
|
||||
_In_ DWORD nOrdinal,
|
||||
_In_opt_ LPCSTR pszFunc,
|
||||
_In_opt_ PVOID pvFunc)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(nOrdinal);
|
||||
UNREFERENCED_PARAMETER(pszFunc);
|
||||
UNREFERENCED_PARAMETER(pvFunc);
|
||||
|
||||
EnumerateImportsTestContext* context =
|
||||
reinterpret_cast<EnumerateImportsTestContext*>(pContext);
|
||||
|
||||
context->ImportFuncCount++;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
TEST_CASE("DetourEnumerateImports", "[module]")
|
||||
{
|
||||
SECTION("Passing nullptr all, results in invalid parameter.")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto success = DetourEnumerateImports(nullptr, nullptr, nullptr, nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_PARAMETER );
|
||||
REQUIRE_FALSE( success );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr for module callback, results in invalid parameter.")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
EnumerateImportsTestContext context {};
|
||||
auto success = DetourEnumerateImports(nullptr, &context, ImportFileCallback, nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_PARAMETER );
|
||||
REQUIRE_FALSE( success );
|
||||
REQUIRE( context.ImportCount == 0 );
|
||||
REQUIRE_FALSE( context.ImportModuleFound );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr for function callback, resolves in invalid parameter.")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
EnumerateImportsTestContext context {};
|
||||
auto success = DetourEnumerateImports(nullptr, &context, nullptr, ImportFuncCallback);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_PARAMETER );
|
||||
REQUIRE_FALSE( success );
|
||||
|
||||
REQUIRE( context.ImportFuncCount == 0 );
|
||||
REQUIRE_FALSE( context.ImportFuncFound );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourGetSizeOfPayloads", "[module]")
|
||||
{
|
||||
SECTION("Passing nullptr for module, is successful.")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
auto size = DetourGetSizeOfPayloads(nullptr);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( size == sizeof(CPrivateStuff) );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr is the same as current module.")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
auto mod = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
|
||||
auto nullSize = DetourGetSizeOfPayloads(nullptr);
|
||||
auto modSize = DetourGetSizeOfPayloads(mod);
|
||||
|
||||
REQUIRE( modSize == nullSize );
|
||||
}
|
||||
|
||||
SECTION("Passing a module with no payload, results in exe marked invalid.")
|
||||
{
|
||||
auto mod = GetModuleHandleW(L"ntdll.dll");
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto size = DetourGetSizeOfPayloads(mod);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_EXE_MARKED_INVALID );
|
||||
REQUIRE( size == 0 );
|
||||
}
|
||||
|
||||
SECTION("Passing stack, results in error")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
int value;
|
||||
auto mod = reinterpret_cast<HMODULE>(&value);
|
||||
|
||||
auto size = DetourGetSizeOfPayloads(mod);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_BAD_EXE_FORMAT );
|
||||
REQUIRE( size == 0 );
|
||||
}
|
||||
|
||||
SECTION("Corrupt image DOS header magic, results in bad exe format error")
|
||||
{
|
||||
ImageCorruptor corruptor(&__ImageBase);
|
||||
corruptor.ModifyDosMagic(0xDEAD);
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto mod = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
auto size = DetourGetSizeOfPayloads(mod);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_BAD_EXE_FORMAT );
|
||||
REQUIRE( size == 0 );
|
||||
}
|
||||
|
||||
SECTION("Corrupt image NT header signature, results in invalid signature error")
|
||||
{
|
||||
ImageCorruptor corruptor(&__ImageBase);
|
||||
corruptor.ModifyNtSignature(0xDEADBEEF);
|
||||
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
auto mod = reinterpret_cast<HMODULE>(&__ImageBase);
|
||||
auto size = DetourGetSizeOfPayloads(mod);
|
||||
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_EXE_SIGNATURE );
|
||||
REQUIRE( size == 0 );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourFindPayload", "[module]")
|
||||
{
|
||||
SECTION("Passing empty guid, fails.")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
HMODULE module {};
|
||||
GUID guid {};
|
||||
DWORD data {};
|
||||
|
||||
auto payload = DetourFindPayload(module, guid, &data);
|
||||
|
||||
REQUIRE( payload == nullptr );
|
||||
REQUIRE( data == 0 );
|
||||
REQUIRE( GetLastError() == ERROR_INVALID_HANDLE );
|
||||
}
|
||||
|
||||
SECTION("Passing nullptr for module with correct GUID, is successful.")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
HMODULE module {};
|
||||
DWORD data {};
|
||||
|
||||
auto payload = DetourFindPayload(module, TEST_PAYLOAD_GUID, &data);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( payload != nullptr );
|
||||
REQUIRE( data == TEST_PAYLOAD_SIZE );
|
||||
|
||||
char* szPayloadMessage = reinterpret_cast<char*>(payload);
|
||||
REQUIRE_THAT( szPayloadMessage, Catch::Matchers::Contains("123") );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourFindPayloadEx", "[module]")
|
||||
{
|
||||
SECTION("Passing empty guid, fails.")
|
||||
{
|
||||
SetLastError(NO_ERROR);
|
||||
|
||||
GUID guid {};
|
||||
DWORD data {};
|
||||
auto payload = DetourFindPayloadEx(guid, &data);
|
||||
|
||||
REQUIRE( payload == nullptr );
|
||||
REQUIRE( data == 0 );
|
||||
|
||||
// This returns different values on different versions of windows.
|
||||
//
|
||||
REQUIRE( (GetLastError() == ERROR_MOD_NOT_FOUND || GetLastError() == ERROR_INVALID_HANDLE) );
|
||||
}
|
||||
|
||||
SECTION("Finding module with correct GUID, is successful.")
|
||||
{
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
|
||||
DWORD data {};
|
||||
auto payload = DetourFindPayloadEx(TEST_PAYLOAD_GUID, &data);
|
||||
|
||||
REQUIRE( GetLastError() == NO_ERROR );
|
||||
REQUIRE( payload != nullptr );
|
||||
REQUIRE( data == TEST_PAYLOAD_SIZE );
|
||||
|
||||
char* szPayloadMessage = reinterpret_cast<char*>(payload);
|
||||
REQUIRE_THAT( szPayloadMessage, Catch::Matchers::Contains("123") );
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourCopyPayloadToProcessEx", "[module]")
|
||||
{
|
||||
// {44FA1CE0-1DA5-4AFC-946E-F96890C38673}
|
||||
static constexpr GUID guid = { 0x44fa1ce0, 0x1da5, 0x4afc, { 0x94, 0x6e, 0xf9, 0x68, 0x90, 0xc3, 0x86, 0x73 } };
|
||||
static constexpr std::uint32_t data = 0xDEADBEEF;
|
||||
|
||||
SECTION("Passing NULL process handle, results in error")
|
||||
{
|
||||
const auto ptr = DetourCopyPayloadToProcessEx(NULL, guid, &data, sizeof(data));
|
||||
REQUIRE(GetLastError() == ERROR_INVALID_HANDLE);
|
||||
REQUIRE(ptr == nullptr);
|
||||
}
|
||||
|
||||
SECTION("Writing to own process, results in valid pointer")
|
||||
{
|
||||
const auto ptr = reinterpret_cast<std::uint32_t*>(DetourCopyPayloadToProcessEx(GetCurrentProcess(), guid, &data, sizeof(data)));
|
||||
REQUIRE(GetLastError() == NO_ERROR);
|
||||
REQUIRE(*ptr == data);
|
||||
}
|
||||
|
||||
SECTION("Writing to different process, can be read with ReadProcessMemory")
|
||||
{
|
||||
// create a suspended copy of ourself to do things with.
|
||||
TerminateOnScopeExit process{};
|
||||
REQUIRE(SUCCEEDED(CreateSuspendedCopy(process)));
|
||||
|
||||
const auto ptr = DetourCopyPayloadToProcessEx(process.information.hProcess, guid, &data, sizeof(data));
|
||||
REQUIRE(GetLastError() == NO_ERROR);
|
||||
REQUIRE(ptr != nullptr);
|
||||
|
||||
std::uint32_t retrieved_data{};
|
||||
REQUIRE(ReadProcessMemory(process.information.hProcess, ptr, &retrieved_data, sizeof(retrieved_data), nullptr));
|
||||
REQUIRE(retrieved_data == data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourFindRemotePayload", "[module]")
|
||||
{
|
||||
SECTION("Passing NULL process handle, results in error")
|
||||
{
|
||||
const auto ptr = DetourFindRemotePayload(NULL, TEST_PAYLOAD_GUID, nullptr);
|
||||
REQUIRE(GetLastError() == ERROR_INVALID_HANDLE);
|
||||
REQUIRE(ptr == nullptr);
|
||||
}
|
||||
|
||||
SECTION("Finding null GUID from own process, results in error")
|
||||
{
|
||||
const GUID guid{};
|
||||
|
||||
const auto ptr = DetourFindRemotePayload(GetCurrentProcess(), guid, nullptr);
|
||||
REQUIRE(GetLastError() == ERROR_MOD_NOT_FOUND);
|
||||
REQUIRE(ptr == nullptr);
|
||||
}
|
||||
|
||||
SECTION("Finding null GUID from different process, results in error")
|
||||
{
|
||||
// create a suspended copy of ourself to do things with.
|
||||
TerminateOnScopeExit process{};
|
||||
REQUIRE(SUCCEEDED(CreateSuspendedCopy(process)));
|
||||
|
||||
const GUID guid{};
|
||||
const auto ptr = DetourFindRemotePayload(process.information.hProcess, guid, nullptr);
|
||||
REQUIRE(GetLastError() == ERROR_MOD_NOT_FOUND);
|
||||
REQUIRE(ptr == nullptr);
|
||||
}
|
||||
|
||||
SECTION("Finding valid GUID from own process, results in valid pointer")
|
||||
{
|
||||
DWORD size = 0;
|
||||
const auto ptr = reinterpret_cast<std::uint32_t*>(DetourFindRemotePayload(GetCurrentProcess(), TEST_PAYLOAD_GUID, &size));
|
||||
REQUIRE(GetLastError() == NO_ERROR);
|
||||
REQUIRE(ptr != nullptr);
|
||||
REQUIRE(size == TEST_PAYLOAD_SIZE);
|
||||
|
||||
char* szPayloadMessage = reinterpret_cast<char*>(ptr);
|
||||
REQUIRE_THAT(szPayloadMessage, Catch::Matchers::Contains("123"));
|
||||
}
|
||||
|
||||
SECTION("Finding valid GUID from different process, can be read with ReadProcessMemory")
|
||||
{
|
||||
// create a suspended copy of ourself to do things with.
|
||||
TerminateOnScopeExit process{};
|
||||
REQUIRE(SUCCEEDED(CreateSuspendedCopy(process)));
|
||||
|
||||
DWORD size = 0;
|
||||
const auto ptr = DetourFindRemotePayload(process.information.hProcess, TEST_PAYLOAD_GUID, &size);
|
||||
REQUIRE(GetLastError() == NO_ERROR);
|
||||
REQUIRE(ptr != nullptr);
|
||||
REQUIRE(size == TEST_PAYLOAD_SIZE);
|
||||
|
||||
SIZE_T bytesRead = 0;
|
||||
char szPayloadMessage[TEST_PAYLOAD_SIZE];
|
||||
REQUIRE(ReadProcessMemory(process.information.hProcess, ptr, &szPayloadMessage, TEST_PAYLOAD_SIZE, &bytesRead));
|
||||
REQUIRE(bytesRead == TEST_PAYLOAD_SIZE);
|
||||
REQUIRE_THAT(szPayloadMessage, Catch::Matchers::Contains("123"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("DetourRestoreAfterWith", "[module]")
|
||||
{
|
||||
// TODO: Needs to be written.
|
||||
}
|
||||
|
||||
TEST_CASE("DetourRestoreAfterWithEx", "[module]")
|
||||
{
|
||||
// TODO: Needs to be written.
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user