-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathself_patch.c
More file actions
154 lines (134 loc) · 5.3 KB
/
Copy pathself_patch.c
File metadata and controls
154 lines (134 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
* self_patch.c — Runtime Self-Patching Demonstration
*
* Demonstrates a program that modifies its own code at runtime:
* 1. Defines a function get_value() that returns 42
* 2. Prints the original return value
* 3. Locates get_value's machine code in memory
* 4. Finds the "return 42" instruction and patches the immediate operand
* from 42 (0x2a) to 99 (0x63)
* 5. Calls get_value() again — it now returns 99
*
* The key challenge is that the .text section is normally read-only.
* We use mprotect() to temporarily make it writable, apply the patch,
* then restore the original protection.
*
* Compile: gcc -o self_patch self_patch.c -O0
* -O0 is critical: optimizations may inline get_value or use a register
* move instead of the expected instruction encoding.
*
* Run: ./self_patch
*
* Platform: x86-64 Linux
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <unistd.h>
/*
* Target function. At -O0 on x86-64, this compiles to something like:
*
* push rbp
* mov rbp, rsp
* mov eax, 0x2a ; 0x2a = 42 decimal — the immediate we'll patch
* pop rbp
* ret
*
* We'll scan for the byte sequence containing 0x2a and patch it.
* To ensure we patch the right thing, we look for the exact pattern:
* b8 2a 00 00 00 ; mov eax, 42
* and replace 0x2a with 0x63 (99).
*/
int __attribute__((noinline)) get_value(void)
{
return 42;
}
/* Sentinel function — used to estimate get_value's length */
void __attribute__((noinline)) after_get_value(void)
{
__asm__ volatile("nop");
}
int main(void)
{
printf("=== Runtime Self-Patching Demo ===\n\n");
/* Step 1: Call the original function */
int original = get_value();
printf("[1] Original get_value() = %d\n", original);
/* Step 2: Locate the function in memory */
unsigned char *func_ptr = (unsigned char *)(uintptr_t)get_value;
unsigned char *func_end = (unsigned char *)(uintptr_t)after_get_value;
size_t func_len = (size_t)(func_end - func_ptr);
printf("[2] get_value is at %p (approx %zu bytes)\n", (void *)func_ptr, func_len);
/* Print the function's machine code */
printf(" Machine code: ");
for (size_t i = 0; i < func_len && i < 32; i++)
printf("%02x ", func_ptr[i]);
if (func_len > 32) printf("...");
printf("\n");
/* Step 3: Find the "mov eax, 42" instruction (b8 2a 00 00 00) */
unsigned char *patch_site = NULL;
unsigned char pattern[] = { 0xb8, 0x2a, 0x00, 0x00, 0x00 }; /* mov eax, 42 */
for (size_t i = 0; i + sizeof(pattern) <= func_len; i++) {
if (memcmp(func_ptr + i, pattern, sizeof(pattern)) == 0) {
patch_site = func_ptr + i + 1; /* point to the immediate operand */
printf("[3] Found 'mov eax, 42' at offset +%zu\n", i);
break;
}
}
if (!patch_site) {
/* Try alternative encoding: some compilers use mov with different
* register encodings. Try scanning for just the 0x2a immediate
* after any mov-to-eax variant. */
unsigned char alt_pattern[] = { 0xc7, 0xc0, 0x2a, 0x00, 0x00, 0x00 };
for (size_t i = 0; i + sizeof(alt_pattern) <= func_len; i++) {
if (memcmp(func_ptr + i, alt_pattern, sizeof(alt_pattern)) == 0) {
patch_site = func_ptr + i + 2;
printf("[3] Found 'mov eax, 42' (alt encoding) at offset +%zu\n", i);
break;
}
}
}
if (!patch_site) {
fprintf(stderr, "ERROR: Could not find patch site.\n");
fprintf(stderr, " This may happen with aggressive optimization.\n");
fprintf(stderr, " Recompile with: gcc -O0 -o self_patch self_patch.c\n");
return 1;
}
/* Step 4: Make the page writable */
long page_size = sysconf(_SC_PAGESIZE);
uintptr_t page_start = (uintptr_t)patch_site & ~(page_size - 1);
printf("[4] Making code page at %p writable...\n", (void *)page_start);
if (mprotect((void *)page_start, page_size,
PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
perror("mprotect (RWX)");
return 1;
}
/* Step 5: Apply the patch — change 42 to 99 */
printf("[5] Patching: 0x%02x (42) -> 0x%02x (99)\n", *patch_site, 0x63);
*patch_site = 0x63; /* 99 in hex */
/* Step 6: Restore read-execute protection */
if (mprotect((void *)page_start, page_size, PROT_READ | PROT_EXEC) != 0) {
perror("mprotect (RX)");
return 1;
}
printf("[6] Restored code page to read-execute\n");
/* Step 7: Call the patched function */
int patched = get_value();
printf("[7] Patched get_value() = %d\n\n", patched);
/* Verify */
if (patched == 99) {
printf("SUCCESS: Function return value changed from %d to %d\n", original, patched);
printf(" without recompiling or restarting the program.\n\n");
printf("This technique is used in:\n");
printf(" - Hot-patching systems (Linux livepatch, Windows Hotpatch)\n");
printf(" - Debugger breakpoints (INT3 injection)\n");
printf(" - Dynamic instrumentation (DTrace, SystemTap)\n");
printf(" - Game trainers and memory editors\n");
} else {
printf("UNEXPECTED: get_value() returned %d (expected 99)\n", patched);
return 1;
}
return 0;
}