-
-
Save lpereira/5062388 to your computer and use it in GitHub Desktop.
/* | |
* Partial applied functions in C | |
* Leandro Pereira <[email protected]> | |
*/ | |
#include <assert.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <stdint.h> | |
#include <stdbool.h> | |
#include <sys/mman.h> | |
struct Partial { | |
void *caller; | |
size_t caller_len; | |
}; | |
#define PARAMETER_CONSTANT 0xFEEDBEEF | |
#define FUNCTION_CONSTANT 0xABAD1DEA | |
static bool | |
patch_pointer(void *code_addr, size_t code_len, void *look_for, void *patch_with) | |
{ | |
unsigned char *code = code_addr; | |
intptr_t look = (intptr_t)look_for; | |
do { | |
if (*((intptr_t *)code) == look) { | |
union { | |
unsigned char octet[sizeof(void *)]; | |
void *ptr; | |
} patch; | |
patch.ptr = patch_with; | |
code[0] = patch.octet[0]; | |
code[1] = patch.octet[1]; | |
code[2] = patch.octet[2]; | |
code[3] = patch.octet[3]; | |
return true; | |
} | |
code++; | |
} while (code_len--); | |
return false; | |
} | |
/* | |
* Make sure this function is always declared before partial_new(). | |
*/ | |
static void | |
partial_template_function(void) | |
{ | |
((void (*)(void *))FUNCTION_CONSTANT)((void *)PARAMETER_CONSTANT); | |
} | |
/* | |
* Make sure no functions get declared here. | |
*/ | |
struct Partial * | |
partial_new(void (*func)(void *data), void *data) | |
{ | |
struct Partial *t; | |
if (!func) return NULL; | |
t = calloc(1, sizeof(*t)); | |
if (!t) return NULL; | |
t->caller_len = (size_t)((intptr_t)partial_new - (intptr_t)partial_template_function); | |
assert(t->caller_len > 0); | |
/* | |
* Map it as RW, without EXEC bits, and only turn it on after pointers | |
* have been patched up | |
*/ | |
t->caller = mmap(0, t->caller_len, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
if (t->caller == MAP_FAILED) goto no_mmap; | |
/* | |
* Copy the code the compiler generated to call an arbitrary function with | |
* an arbitrary parameter | |
*/ | |
memcpy(t->caller, partial_template_function, t->caller_len); | |
/* | |
* Change the arbitrary constants to the ones passed as arguments to this | |
* function. This thing will only work on 32-bit, little endian | |
*/ | |
if (!patch_pointer(t->caller, t->caller_len, (void *)FUNCTION_CONSTANT, func)) goto no_patch; | |
if (!patch_pointer(t->caller, t->caller_len, (void *)PARAMETER_CONSTANT, data)) goto no_patch; | |
/* Make sure no one can change this later, and that we can execute it */ | |
mprotect(t->caller, t->caller_len, PROT_EXEC | PROT_READ); | |
return t; | |
no_patch: | |
munmap(t->caller, t->caller_len); | |
no_mmap: | |
free(t); | |
return NULL; | |
} | |
void | |
partial_del(struct Partial *t) | |
{ | |
if (!t) return; | |
munmap(t->caller, t->caller_len); | |
free(t); | |
} | |
void | |
(*partial_to_function(struct Partial *t))() | |
{ | |
if (!t) return NULL; | |
return (void (*)(void *))(t)->caller; | |
} | |
static void | |
test(void *data) | |
{ | |
printf("it worked! received data: %p\n", data); | |
} | |
static void | |
another_test(void *data) | |
{ | |
printf("another test with different data: %p\n", data); | |
} | |
int main(void) | |
{ | |
struct Partial *t, *t2; | |
void (*func)(); | |
void (*func2)(); | |
t = partial_new(test, (void *)0x12341337); | |
if (!t) goto end; | |
t2 = partial_new(another_test, (void *)0xbebac0ca); | |
if (!t2) goto end; | |
func = (void *)partial_to_function(t); | |
if (!func) goto end; | |
func2 = partial_to_function(t2); | |
if (!func2) goto end; | |
/* | |
* Now pass `func` around as a callback. When it is called, the | |
* parameter passed to partial_new() will be passed to the original | |
* function. Magic. | |
*/ | |
atexit(func); | |
/* Or call it directly. */ | |
func2(); | |
/* | |
* atexit() callbacks are called in the dtor, so make sure partial_del(t) | |
* does not run. | |
*/ | |
partial_del(t2); | |
end: | |
return 0; | |
} |
#define FUNCTION_CONSTANT 0xABAD1DEA
You have good taste and you should feel good.
Hahaha :) Reminds me of the good ol' IA32 asm code, this kind of tricks was just used over and over - because of, no choice.
Well nice piece !
Now you say you wouldn't recommend it (I wouldn't either) but with some more work, I think it's even packageable in a (dangerous) library. Just have to make freaking sure you get the good length for partial_template_functions (why not use labels in the code for that ?) and also be a lil' more generic for IA32/64, big or little endian, and you're good to go !
BUT you could also use a global.
👍
Nice trick.
I don't see the point/need of this cast inside partial_to_function:
return (void (*)(void *))(t)->caller;
if you keep the cast shouldn't it be:
return (void (*)())(t)->caller;
how can this possibly be useful?