diff options
Diffstat (limited to 'ext/detours/samples/dynamic_alloc/main.cpp')
-rw-r--r-- | ext/detours/samples/dynamic_alloc/main.cpp | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/ext/detours/samples/dynamic_alloc/main.cpp b/ext/detours/samples/dynamic_alloc/main.cpp new file mode 100644 index 0000000..a61232f --- /dev/null +++ b/ext/detours/samples/dynamic_alloc/main.cpp @@ -0,0 +1,194 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Detours Test Program +// +// Microsoft Research Detours Package +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This is a test program to test the DetourAllocateRegionWithinJumpBounds +// API, that dynamically allocates an executable region adjacent to a given +// address so that we can use the region as a detour function. +// +// This test program detours the function `target_function`. Instead of +// simply specifying a code segment as a detour function, we specify a +// dynamically-allocated region into which we copy the code, altering the +// return value, from the assembly function `CodeTemplate` as a template. +// +#define _CRT_SECURE_NO_WARNINGS +#include <iostream> +#include <functional> +#include <assert.h> +#include <windows.h> +#include <detours.h> + +extern "C" { + void *CodeTemplate(); + void *CodeTemplate_End(); +} + +void Log(PCSTR format, ...) { + char linebuf[1024]; + va_list v; + va_start(v, format); + wvsprintfA(linebuf, format, v); + va_end(v); + OutputDebugStringA(linebuf); +} + +// This is a target function to be detoured. When detoured, it's expected to +// return a non-nullptr value. +void *target_function() { + std::cout << '+' << __FUNCTION__ << std::endl; + return nullptr; +} + +// Helper function to sandwich a given function between `DetourTransactionBegin` +// and `DetourTransactionCommit`/`DetourTransactionAbort`. +bool DetourTransaction(std::function<bool()> callback) { + LONG status = DetourTransactionBegin(); + if (status != NO_ERROR) { + Log("DetourTransactionBegin failed with %08x\n", status); + return status == NO_ERROR; + } + + if (callback()) { + status = DetourTransactionCommit(); + if (status != NO_ERROR) { + Log("DetourTransactionCommit failed with %08x\n", status); + } + } + else { + status = DetourTransactionAbort(); + if (status == NO_ERROR) { + Log("Aborted transaction.\n"); + } + else { + Log("DetourTransactionAbort failed with %08x\n", status); + } + } + return status == NO_ERROR; +} + +// This class manages one dynamically-allocated region that is allocated by +// the Detours API `DetourAllocateRegionWithinJumpBounds`, to which we can +// push binary data sequentially to use it as a detour function. +class CodeRegionFactory final { + template <typename T> + static const T *at(const void *base, uint32_t offset) { + return + reinterpret_cast<const T*>( + reinterpret_cast<const uint8_t*>(base) + offset); + } + + template <typename T> + static T *at(void *base, uint32_t offset) { + return + reinterpret_cast<T*>( + reinterpret_cast<uint8_t*>(base) + offset); + } + + void *region_ = nullptr; + uint8_t *current_ = nullptr, + *current_end_ = nullptr; + +public: + CodeRegionFactory(const void *source) { + DWORD new_region_size = 0; + auto new_region_address = + DetourAllocateRegionWithinJumpBounds(source, &new_region_size); + if (new_region_address) { + region_ = current_ = at<uint8_t>(new_region_address, 0); + current_end_ = current_ + new_region_size; + } + else { + Log("Cannot find a region near %p\n", source); + } + } + + ~CodeRegionFactory() { + if (region_ + && !VirtualFree(region_, 0, MEM_RELEASE)) { + Log("VirtualFree failed - %08x\n", GetLastError()); + } + } + + // Pushes binary data to the region if there is enough space, and returns + // the start address of a copy in the region if succeeded. + void *PushTemplate(const void *start, + const void *end) { + auto diff = at<uint8_t>(end, 0) - at<uint8_t>(start, 0); + if (diff < 0 || current_ + diff > current_end_) + return nullptr; + auto start_pos = current_; + memcpy(start_pos, start, diff); + current_ += diff; + return start_pos; + } +}; + +int main(int, char**) { + std::cout << "1. target_function() without Detour" << std::endl; + auto ret = target_function(); + std::cout << ret << std::endl; + assert(!ret); + + CodeRegionFactory factory(target_function); + + void *detour_destination, + *detour_target = reinterpret_cast<void*>(target_function); + + // Fill the allocated page with as many instances as possible of the code + // template, and pick the last instance + while (auto p = factory.PushTemplate(CodeTemplate, + CodeTemplate_End)) { + detour_destination = p; + } + + bool is_detoured = false; + DetourTransaction([&]() { + PDETOUR_TRAMPOLINE trampoline = nullptr; + void *target = nullptr, + *detour = nullptr; + auto status = DetourAttachEx(&detour_target, + detour_destination, + &trampoline, + &target, + &detour); + if (status != NO_ERROR) { + Log("DetourAttachEx failed - %08x\n", status); + return false; + } + is_detoured = true; + std::cout + << "detour: " << target << " --> " << detour + << " (trampoline: " << trampoline << " )" + << std::endl; + return true; + }); + + // Attach failed for some reason. Bail out. + if (!is_detoured) + return 1; + + std::cout << "2. target_function() with Detour" << std::endl; + ret = target_function(); + std::cout << ret << std::endl; + assert(ret); // The return value is cracked by the detour function + + DetourTransaction([&]() { + auto status = DetourDetach(&detour_target, detour_destination); + if (status != NO_ERROR) { + Log("DetourDetach failed - %08x\n", status); + return false; + } + return true; + }); + + std::cout << "3. target_function() without Detour" << std::endl; + ret = target_function(); + std::cout << ret << std::endl; + assert(!ret); + + return 0; +}
\ No newline at end of file |