Skip to content

Commit 4fbf885

Browse files
committed
Update the whole library, rename functions and macros
Changes to the library interface: * Function names (`libdetour_*' -> `detour_*'). * Structure names (`libdetour_*' -> `detour_*'). * Decreased size of `detour_ctx_t' structure (remove `jmp_bytes' array). * Use lowercase name for 'windows.h' header.
1 parent 4482f31 commit 4fbf885

File tree

3 files changed

+198
-120
lines changed

3 files changed

+198
-120
lines changed

src/libdetour.c

+56-42
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* libdetour.c - Simple C/C++ library for detour hooking in Linux and Windows.
33
* See: https://github.com/8dcc/libdetour
4+
*
45
* Copyright (C) 2024 8dcc
56
*
67
* This program is free software: you can redistribute it and/or modify it
@@ -22,7 +23,7 @@
2223

2324
#include <stdint.h>
2425
#include <stdbool.h>
25-
#include <string.h>
26+
#include <string.h> /* memcpy() */
2627

2728
/*----------------------------------------------------------------------------*/
2829

@@ -37,104 +38,117 @@
3738
* a: ff e0 jmp rax
3839
*/
3940
#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 };
4142
#define JMP_BYTES_OFF 1 /* Offset inside the array where the ptr should go */
4243
#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 };
4546
#define JMP_BYTES_OFF 2 /* Offset inside the array where the ptr should go */
4647
#endif
4748

4849
/*----------------------------------------------------------------------------*/
4950

50-
#ifdef __unix__
51+
#if defined(__unix__)
5152
#include <unistd.h> /* sysconf() */
5253
#include <sys/mman.h> /* mprotect() */
5354

5455
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);
6264

6365
uint32_t new_flags = PROT_READ | PROT_EXEC;
6466
if (enable_write)
6567
new_flags |= PROT_WRITE;
6668

67-
if (mprotect(page, page_size, new_flags) == -1)
69+
if (mprotect(prev_page, page_size, new_flags) == -1)
6870
return false;
6971

7072
return true;
7173
}
72-
#elif defined _WIN32
73-
#include <Windows.h> /* VirtualProtect() */
74+
#elif defined(_WIN32) /* !defined(__unix__) */
75+
#include <windows.h> /* VirtualProtect() */
7476

7577
static bool protect_addr(void* ptr, bool enable_write) {
7678
DWORD old_flags;
7779
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);
7981
}
80-
#else
82+
#else /* !defined(_WIN32) */
8183
#error "libdetour: This systems is not supported"
82-
#endif
84+
#endif /* !defined(_WIN32) */
8385

8486
/*----------------------------------------------------------------------------*/
8587

86-
void libdetour_init(libdetour_ctx_t* ctx, void* orig, void* hook) {
88+
void detour_init(detour_ctx_t* ctx, void* orig, void* hook) {
8789
ctx->detoured = false;
8890
ctx->orig = orig;
8991
ctx->hook = hook;
9092

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+
*/
9397
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*));
10398
}
10499

105-
bool libdetour_add(libdetour_ctx_t* ctx) {
106-
/* Already detoured, nothing to do */
100+
bool detour_enable(detour_ctx_t* ctx) {
107101
if (ctx->detoured)
108102
return true;
109103

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+
*/
111109
if (!protect_addr(ctx->orig, true))
112110
return false;
113111

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+
*/
118129
if (!protect_addr(ctx->orig, false))
119130
return false;
120131

121132
ctx->detoured = true;
122133
return true;
123134
}
124135

125-
bool libdetour_del(libdetour_ctx_t* ctx) {
126-
/* Not detoured, nothing to do */
136+
bool detour_disable(detour_ctx_t* ctx) {
127137
if (!ctx->detoured)
128138
return true;
129139

130-
/* See util.c */
140+
/*
141+
* See `detour_enable'.
142+
*/
131143
if (!protect_addr(ctx->orig, true))
132144
return false;
133145

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+
*/
135150
memcpy(ctx->orig, ctx->saved_bytes, sizeof(ctx->saved_bytes));
136151

137-
/* Restore old protection */
138152
if (!protect_addr(ctx->orig, false))
139153
return false;
140154

src/libdetour.h

+63-31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/*
22
* libdetour.h - Simple C/C++ library for detour hooking in Linux and Windows.
33
* See: https://github.com/8dcc/libdetour
4+
*
45
* Copyright (C) 2024 8dcc
56
*
67
* This program is free software: you can redistribute it and/or modify it
@@ -23,53 +24,84 @@
2324
#include <stdint.h>
2425
#include <stdbool.h>
2526

26-
#ifdef __i386__
27-
#define LIBDETOUR_JMP_SZ_ 7 /* Size of jmp instructions in 32-bit */
28-
#else
29-
#define LIBDETOUR_JMP_SZ_ 12 /* Size of jmp instructions in 64-bit */
30-
#endif
27+
#if defined(__i386__)
28+
#define LIBDETOUR_JMP_SZ_ 7 /* Size of `jmp' instructions in 32-bit */
29+
#elif defined(__x86_64__) /* !defined(__i386__) */
30+
#define LIBDETOUR_JMP_SZ_ 12 /* Size of `jmp' instructions in 64-bit */
31+
#else /* !defined(__x86_64__) */
32+
#error "libdetour: The current architecture is not supported"
33+
#endif /* !defined(__x86_64__) */
3134

3235
typedef struct {
3336
bool detoured;
3437
void* orig;
3538
void* hook;
36-
uint8_t jmp_bytes[LIBDETOUR_JMP_SZ_];
3739
uint8_t saved_bytes[LIBDETOUR_JMP_SZ_];
38-
} libdetour_ctx_t;
40+
} detour_ctx_t;
3941

4042
/*----------------------------------------------------------------------------*/
4143

42-
void libdetour_init(libdetour_ctx_t* ctx, void* orig, void* hook);
43-
bool libdetour_add(libdetour_ctx_t* ctx);
44-
bool libdetour_del(libdetour_ctx_t* ctx);
44+
/*
45+
* Initialize the specified `detour_ctx_t' structure with the specified original
46+
* and hook functions. Although the structure is initialized, the `hook'
47+
* function is not yet hooked to `orig'; use `detour_add' for that.
48+
*
49+
* Naturally, the `orig' function should not be hooked when calling
50+
* `detour_init'.
51+
*/
52+
void detour_init(detour_ctx_t* ctx, void* orig, void* hook);
53+
54+
/*
55+
* Enable the detour hook associated to the specified `detour_ctx_t' structure.
56+
* The function returns `true' if the function was hooked successfully, or
57+
* `false' otherwise.
58+
*/
59+
bool detour_enable(detour_ctx_t* ctx);
60+
61+
/*
62+
* Disable the detour hook associated to the specified `detour_ctx_t' structure.
63+
* The function returns `true' if the function was unhooked successfully, or
64+
* `false' otherwise.
65+
*/
66+
bool detour_disable(detour_ctx_t* ctx);
4567

4668
/*----------------------------------------------------------------------------*/
4769

48-
/* Declare the prototype of the original function */
49-
#define LIBDETOUR_DECL_TYPE(FUNCRET, FUNCNAME, ...) \
70+
/*
71+
* Declare the prototype of the original function, used when calling the
72+
* original.
73+
*/
74+
#define DETOUR_DECL_TYPE(FUNCRET, FUNCNAME, ...) \
5075
typedef FUNCRET (*libdetour_##FUNCNAME##_t)(__VA_ARGS__)
5176

52-
/* Remove detour hook, call original, detour again.
53-
* Keep in mind that:
54-
* - The returned value of the original function is not stored. If the
55-
* function is not void, and you care about the return value, use
56-
* LIBDETOUR_ORIG_GET() instead.
57-
* - FUNCNAME should be the same name passed to LIBDETOUR_DECL_TYPE, without
58-
* the prefix or subfix added by the macro ("libdetour_X_t") */
59-
#define LIBDETOUR_ORIG_CALL(CTX_PTR, FUNCNAME, ...) \
60-
do { \
61-
libdetour_del(CTX_PTR); \
62-
((libdetour_##FUNCNAME##_t)((CTX_PTR)->orig))(__VA_ARGS__); \
63-
libdetour_add(CTX_PTR); \
77+
/*
78+
* Call the original function named FUNCNAME, using its associated
79+
* `detour_ctx_t' structure. It does this by unhooking the original, calling it,
80+
* and hooking again.
81+
*
82+
* The returned value of the original function is not stored. If the function
83+
* does not return void, and you care about the return value, use
84+
* `DETOUR_ORIG_GET' instead.
85+
*
86+
* The FUNCNAME argument should be the same symbol passed to `DETOUR_DECL_TYPE',
87+
* without prefixes or suffixes.
88+
*/
89+
#define DETOUR_ORIG_CALL(CTX_PTR, FUNCNAME, ...) \
90+
do { \
91+
detour_disable(CTX_PTR); \
92+
((libdetour_##FUNCNAME##_t)((CTX_PTR)->orig))(__VA_ARGS__); \
93+
detour_enable(CTX_PTR); \
6494
} while (0)
6595

66-
/* Same as LIBDETOUR_ORIG_CALL, but accepts an extra parameter for storing the
67-
* returned value of the original function */
68-
#define LIBDETOUR_ORIG_GET(CTX_PTR, OUT_VAR, FUNCNAME, ...) \
69-
do { \
70-
libdetour_del(CTX_PTR); \
71-
OUT_VAR = ((libdetour_##FUNCNAME##_t)((CTX_PTR)->orig))(__VA_ARGS__); \
72-
libdetour_add(CTX_PTR); \
96+
/*
97+
* Same as `DETOUR_ORIG_CALL', but accepts an extra parameter for storing the
98+
* returned value of the original function.
99+
*/
100+
#define DETOUR_ORIG_GET(CTX_PTR, OUT_VAR, FUNCNAME, ...) \
101+
do { \
102+
detour_disable(CTX_PTR); \
103+
OUT_VAR = ((libdetour_##FUNCNAME##_t)((CTX_PTR)->orig))(__VA_ARGS__); \
104+
detour_enable(CTX_PTR); \
73105
} while (0)
74106

75107
#endif /* LIBDETOUR_H_ */

0 commit comments

Comments
 (0)