blob: 5bda38b9ce7637512dbeac9d31685308a8943631 (
plain)
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
|
#define _GNU_SOURCE // Required to unlock RTLD_DEFAULT so we can search all loaded memory
#include <ctype.h>
#include <dlfcn.h> // Gives us dlsym() to look up memory addresses by string names
#include <stddef.h>
void hello_world(const char* action) {
// __func__ is secretly injected by the compiler and holds the string "hello_world".
// We use raw pointers here (src and dst) instead of array indices because
// stepping the memory address forward directly (*dst++) generates faster machine code.
const char* src = __func__;
char result[64];
char* dst = result;
int capitalize = 1;
// We walk the 'src' pointer forward until we hit the null terminator.
// The (dst - result) < 60 check guarantees we never write past the end of
// our 64-byte array, preventing stack smashing if the function name is unusually long.
while (*src && (dst - result) < 60) {
if (*src == '_') {
*dst++ = ' ';
capitalize = 1;
} else {
*dst++ = capitalize ? toupper(*src) : *src;
capitalize = 0;
}
src++;
}
// Cap off the string with a bang and the required null terminator
*dst++ = '!';
*dst++ = '\n';
*dst = '\0';
// Declare a function pointer that takes a format string and variadic arguments
int (*dynamic_eval)(const char*, ...);
// This looks unhinged, but it is the official POSIX-compliant way to use dlsym.
// ISO C strictly forbids casting a raw data pointer (void*) directly to a
// function pointer. To bypass the compiler warning, we take the address of our
// function pointer, cast THAT to a void**, and dereference it to write the address.
*(void **)(&dynamic_eval) = dlsym(RTLD_DEFAULT, action);
if (dynamic_eval) {
dynamic_eval("%s", result);
}
}
int main(void) {
hello_world("printf");
return 0;
}
|