|
1 | 1 | /*
|
2 | 2 | * libdetour.c - Simple C/C++ library for detour hooking in Linux and Windows.
|
3 | 3 | * See: https://github.com/8dcc/libdetour
|
| 4 | + * |
4 | 5 | * Copyright (C) 2024 8dcc
|
5 | 6 | *
|
6 | 7 | * This program is free software: you can redistribute it and/or modify it
|
|
22 | 23 |
|
23 | 24 | #include <stdint.h>
|
24 | 25 | #include <stdbool.h>
|
25 |
| -#include <string.h> |
| 26 | +#include <string.h> /* memcpy() */ |
26 | 27 |
|
27 | 28 | /*----------------------------------------------------------------------------*/
|
28 | 29 |
|
|
37 | 38 | * a: ff e0 jmp rax
|
38 | 39 | */
|
39 | 40 | #ifdef __i386__
|
40 |
| -static uint8_t def_jmp_bytes[] = { 0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0 }; |
| 41 | +static uint8_t jmp_bytes[] = { 0xB8, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0 }; |
41 | 42 | #define JMP_BYTES_OFF 1 /* Offset inside the array where the ptr should go */
|
42 | 43 | #else
|
43 |
| -static uint8_t def_jmp_bytes[] = { 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, |
44 |
| - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0 }; |
| 44 | +static uint8_t jmp_bytes[] = { 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, |
| 45 | + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0 }; |
45 | 46 | #define JMP_BYTES_OFF 2 /* Offset inside the array where the ptr should go */
|
46 | 47 | #endif
|
47 | 48 |
|
48 | 49 | /*----------------------------------------------------------------------------*/
|
49 | 50 |
|
50 |
| -#ifdef __unix__ |
| 51 | +#if defined(__unix__) |
51 | 52 | #include <unistd.h> /* sysconf() */
|
52 | 53 | #include <sys/mman.h> /* mprotect() */
|
53 | 54 |
|
54 | 55 | static bool protect_addr(void* ptr, bool enable_write) {
|
55 |
| - /* For more information, see: |
56 |
| - * https://8dcc.github.io/reversing/challenge10.html#c-translation */ |
57 |
| - long page_size = sysconf(_SC_PAGESIZE); |
58 |
| - long page_mask = ~(page_size - 1); |
59 |
| - uintptr_t next_page = ((uintptr_t)ptr + page_size - 1) & page_mask; |
60 |
| - uintptr_t prev_page = next_page - page_size; |
61 |
| - void* page = (void*)prev_page; |
| 56 | + /* |
| 57 | + * For more information, see: |
| 58 | + * https://8dcc.github.io/reversing/challenge10.html#c-translation |
| 59 | + */ |
| 60 | + const long page_size = sysconf(_SC_PAGESIZE); |
| 61 | + const long page_mask = ~(page_size - 1); |
| 62 | + const uintptr_t next_page = ((uintptr_t)ptr + page_size - 1) & page_mask; |
| 63 | + void* prev_page = (void*)(next_page - page_size); |
62 | 64 |
|
63 | 65 | uint32_t new_flags = PROT_READ | PROT_EXEC;
|
64 | 66 | if (enable_write)
|
65 | 67 | new_flags |= PROT_WRITE;
|
66 | 68 |
|
67 |
| - if (mprotect(page, page_size, new_flags) == -1) |
| 69 | + if (mprotect(prev_page, page_size, new_flags) == -1) |
68 | 70 | return false;
|
69 | 71 |
|
70 | 72 | return true;
|
71 | 73 | }
|
72 |
| -#elif defined _WIN32 |
73 |
| -#include <Windows.h> /* VirtualProtect() */ |
| 74 | +#elif defined(_WIN32) /* !defined(__unix__) */ |
| 75 | +#include <windows.h> /* VirtualProtect() */ |
74 | 76 |
|
75 | 77 | static bool protect_addr(void* ptr, bool enable_write) {
|
76 | 78 | DWORD old_flags;
|
77 | 79 | DWORD new_flags = enable_write ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
|
78 |
| - return VirtualProtect(ptr, sizeof(def_jmp_bytes), new_flags, &old_flags); |
| 80 | + return VirtualProtect(ptr, sizeof(jmp_bytes), new_flags, &old_flags); |
79 | 81 | }
|
80 |
| -#else |
| 82 | +#else /* !defined(_WIN32) */ |
81 | 83 | #error "libdetour: This systems is not supported"
|
82 |
| -#endif |
| 84 | +#endif /* !defined(_WIN32) */ |
83 | 85 |
|
84 | 86 | /*----------------------------------------------------------------------------*/
|
85 | 87 |
|
86 |
| -void libdetour_init(libdetour_ctx_t* ctx, void* orig, void* hook) { |
| 88 | +void detour_init(detour_ctx_t* ctx, void* orig, void* hook) { |
87 | 89 | ctx->detoured = false;
|
88 | 90 | ctx->orig = orig;
|
89 | 91 | ctx->hook = hook;
|
90 | 92 |
|
91 |
| - /* Store the first N bytes of the original function, where N is the size of |
92 |
| - * the jmp instructions */ |
| 93 | + /* |
| 94 | + * Store the first N bytes of the original function, where N is the size of |
| 95 | + * the `jmp' instructions. |
| 96 | + */ |
93 | 97 | memcpy(ctx->saved_bytes, orig, sizeof(ctx->saved_bytes));
|
94 |
| - |
95 |
| - /* Default jmp bytes */ |
96 |
| - memcpy(ctx->jmp_bytes, &def_jmp_bytes, sizeof(def_jmp_bytes)); |
97 |
| - |
98 |
| - /* JMP_BYTES_OFF is defined below def_jmp_bytes, and it changes depending |
99 |
| - * on the arch. |
100 |
| - * We use "&hook" and not "hook" because we want the address of |
101 |
| - * the func, not the first bytes of it like before. */ |
102 |
| - memcpy(&ctx->jmp_bytes[JMP_BYTES_OFF], &hook, sizeof(void*)); |
103 | 98 | }
|
104 | 99 |
|
105 |
| -bool libdetour_add(libdetour_ctx_t* ctx) { |
106 |
| - /* Already detoured, nothing to do */ |
| 100 | +bool detour_enable(detour_ctx_t* ctx) { |
107 | 101 | if (ctx->detoured)
|
108 | 102 | return true;
|
109 | 103 |
|
110 |
| - /* See util.c */ |
| 104 | + /* |
| 105 | + * Enable write permissions on the specified address. The `protect_addr' |
| 106 | + * function is defined above as a static function, depending on the current |
| 107 | + * OS. |
| 108 | + */ |
111 | 109 | if (!protect_addr(ctx->orig, true))
|
112 | 110 | return false;
|
113 | 111 |
|
114 |
| - /* Copy our jmp instruction with our hook address to the orig */ |
115 |
| - memcpy(ctx->orig, ctx->jmp_bytes, sizeof(ctx->jmp_bytes)); |
116 |
| - |
117 |
| - /* Restore old protection */ |
| 112 | + /* |
| 113 | + * Write the address of the `hook' function to the array for the encoded |
| 114 | + * `jmp' instruction, at the `JMP_BYTES_OFF' offset (which changes depending |
| 115 | + * on the architecture at compile-time). |
| 116 | + */ |
| 117 | + memcpy(&jmp_bytes[JMP_BYTES_OFF], &(ctx->hook), sizeof(void*)); |
| 118 | + |
| 119 | + /* |
| 120 | + * Copy the whole (now filled) `jmp_bytes' array to the start of the body |
| 121 | + * the target function (`ctx->orig'). This new `jmp' instruction will be |
| 122 | + * responsible for the actual hook. |
| 123 | + */ |
| 124 | + memcpy(ctx->orig, jmp_bytes, sizeof(jmp_bytes)); |
| 125 | + |
| 126 | + /* |
| 127 | + * Restore the old protection for this address. |
| 128 | + */ |
118 | 129 | if (!protect_addr(ctx->orig, false))
|
119 | 130 | return false;
|
120 | 131 |
|
121 | 132 | ctx->detoured = true;
|
122 | 133 | return true;
|
123 | 134 | }
|
124 | 135 |
|
125 |
| -bool libdetour_del(libdetour_ctx_t* ctx) { |
126 |
| - /* Not detoured, nothing to do */ |
| 136 | +bool detour_disable(detour_ctx_t* ctx) { |
127 | 137 | if (!ctx->detoured)
|
128 | 138 | return true;
|
129 | 139 |
|
130 |
| - /* See util.c */ |
| 140 | + /* |
| 141 | + * See `detour_enable'. |
| 142 | + */ |
131 | 143 | if (!protect_addr(ctx->orig, true))
|
132 | 144 | return false;
|
133 | 145 |
|
134 |
| - /* Restore the bytes that were at the start of orig (we saved on init) */ |
| 146 | + /* |
| 147 | + * Restore the bytes we saved inside the `detour_ctx_t' structure in |
| 148 | + * `detour_init'. |
| 149 | + */ |
135 | 150 | memcpy(ctx->orig, ctx->saved_bytes, sizeof(ctx->saved_bytes));
|
136 | 151 |
|
137 |
| - /* Restore old protection */ |
138 | 152 | if (!protect_addr(ctx->orig, false))
|
139 | 153 | return false;
|
140 | 154 |
|
|
0 commit comments