732 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
		
		
			
		
	
	
			732 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
|  | //////////////////////////////////////////////////////////////////////////////
 | ||
|  | //
 | ||
|  | //  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.
 | ||
|  | } | ||
|  | 
 | ||
|  | 
 |