aboutsummaryrefslogtreecommitdiffhomepage
path: root/ext/detours/samples/dynamic_alloc/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/detours/samples/dynamic_alloc/main.cpp')
-rw-r--r--ext/detours/samples/dynamic_alloc/main.cpp194
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