PDA

View Full Version : call_function_raw(), BinaryBuffer functions, dlopen/dlsym/dlclose



kung foo man
17th January 2015, 07:36
BinaryBuffer example:




buf = memory_malloc(128);
bb = binarybuffer_new(buf);
binarybuffer_write(bb, "i", 100);
binarybuffer_write(bb, "f", 0.0123);
binarybuffer_write(bb, "s", "hello");
binarybuffer_write(bb, "d", 0.12345);
binarybuffer_write(bb, "c", "#");
binarybuffer_write(bb, "v", (0.10,0.20,0.30));
binarybuffer_seek(bb, 0);
i = binarybuffer_read(bb, "i");
f = binarybuffer_read(bb, "f");
s = binarybuffer_read(bb, "s");
d = binarybuffer_read(bb, "d");
c = binarybuffer_read(bb, "c");
v = binarybuffer_read(bb, "v");
printf("bb=% i=% f=% s=% d=% c=% v=%\n", bb, i, f, s, d, c, v);
memory_free(buf);
binarybuffer_free(bb);


Output:

bb=164778640 i=100 f=0.012 s=hello d=0.123 c=# v=(0.10, 0.20, 0.30)

Use cases:

prepare arguments for call_function_raw()
parse any binary data, e.g. read player entity data ingame without recompiling libcod (ingame CheatEngine anyone?)


The strings are actually malloc'ed and only the pointer is saved as 4-byte-value in the binarybuffer. Internally they are saved in a list, so binarybuffer_free() can free them.

Dynamic Library example:



libc = dlopen("libc.so.6");
libc_printf = dlsym(libc, "printf");
printf("libc=% libc_printf=%\n", libc, libc_printf);
dlclose(libc);


Output:

libc=-144867328 libc_printf=-146018720

Use cases:

get function addresses for call_function_raw()



call_function_raw() example:



buf = memory_malloc(128);
bb = binarybuffer_new(buf);
binarybuffer_write(bb, "s", "printf: int=%i float=%f string=%s double=%g char=%c vector=(%.2f,%.2f,%.2f)\n");
binarybuffer_write(bb, "i", 100);
binarybuffer_write(bb, "f", 0.0123);
binarybuffer_write(bb, "s", "hello");
binarybuffer_write(bb, "d", 0.12345);
binarybuffer_write(bb, "c", "#");
binarybuffer_write(bb, "v", (0.10,0.20,0.30));

libc_printf = dlsym(0, "printf");
call_function_raw(libc_printf, "s.ifsdcfff", buf);

memory_free(buf);
binarybuffer_free(bb);



Output:

printf: int=100 float=0.012300 string=hello double=0.12345 char=# vector=(0.10,0.20,0.30)

Use cases:

use all kind of C functions (e.g. from libc, but also from IDA etc.) without recompiling libcod, though atm some shitty prework todo


It's called "raw", because I wanna do a normal call_function() later, with much less bloat code to prepare the arguments. Kinda automatically, based on stackGetNumberOfParams()/stackGetParamType()

Regarding: call_function_raw(libc_printf, "s.ifsdcfff", buf);


Type
Explanation


s
string, 4 bytes (just a pointer)


.
varargs kicking in, like printf(char *msg, ...). Needed, because C handles floats like doubles in varargs-call-convention


i
int, 4 bytes


f
float, 4 bytes


d
double, 8 bytes


c
char, 1 byte






Inb4 ideas, tellz0r!

GitHub Commit: https://github.com/kungfooman/libcod/commit/5a62c2392d2a77b4ae70a4985d8ff9127dc15f16
Download of precompiled binaries: http://killtube.org/downloads/libcod/2015.01.17/

kung foo man
17th January 2015, 18:21
Ok, a "nicer" call_function, done with CoDScript only:



call_function(function, signature, a, b, c, d, e, f, g, h, i, j, k, l) {
args = [];
args[args.size] = a;
args[args.size] = b;
args[args.size] = c;
args[args.size] = d;
args[args.size] = e;
args[args.size] = f;
args[args.size] = g;
args[args.size] = h;
args[args.size] = i;
args[args.size] = j;
args[args.size] = k;
args[args.size] = l;

buf = memory_malloc(128);
bb = binarybuffer_new(buf);

signatureClean = ""; // without the . (varargs-marker)
for (ii=0; ii<signature.size; ii++)
if (signature[ii] != ".")
signatureClean += signature[ii];

signature_pos = 0;
for (ii=0; ii<args.size; ii++) {
//printf("ii=% type=%\n", ii, getType(args[ii]));

needGuessType = ii >= signatureClean.size;
//printf("ii=%, sigClean.size=% needGuessType=%\n", ii, signatureClean.size, needGuessType);
if ( ! needGuessType) {
// don't guess type when it's given in signature
binarybuffer_write(bb, signatureClean[ii], args[ii]);
continue;
}

type = "";
switch (getType(args[ii])) {
case "INT": type = "i"; break;
case "STRING": type = "s"; break;
case "FLOAT": type = "f"; break;
}
signature += type; // only add missing types to signature
binarybuffer_write(bb, type, args[ii]); // add data with our guessed type
}

//printf("Function: % Signature: %\n", function, signature);
ret = call_function_raw(function, signature, buf);

memory_free(buf);
binarybuffer_free(bb);

return ret;
}


Call like:



call_function(libc_printf, "s.", "hi!!! int=%d float=%f string=%s\n", 123, 0.345, "yo");


Output (with debug messages):



ii=0 type=STRING
ii=1 type=INT
ii=2 type=FLOAT
ii=3 type=STRING
ii=4 type=UNDEFINED
ii=5 type=UNDEFINED
ii=6 type=UNDEFINED
ii=7 type=UNDEFINED
ii=8 type=UNDEFINED
ii=9 type=UNDEFINED
ii=10 type=UNDEFINED
ii=11 type=UNDEFINED
Function: -146088352 Signature: s.ifs
hi!!! int=123 float=0.345000 string=yo


Now we only need to declare the "needed" signature "s.", for printf(char *msg, ...), because we can figure out the rest dynamically, based on getType(args[ii]).

Limitation:

We can't use %c (1 byte char) or %g (8 bytes double) without explicit signature, because these types don't exist in CoDScript itself, so there is no way to tell binarybuffer_write() about the type-information only with getType().

For that case, you need to make the signature explicit:



call_function(libc_printf, "s.ifsdc", "hi!!! int=%d float=%f string=%s double=%g char=%c\n", 123, 0.345, "yo", 0.123, "#");

Output:


hi!!! int=123 float=0.345000 string=yo double=0.123 char=#


Return values:

Just added this in new commit: https://github.com/kungfooman/libcod/commit/35c1d3361b9a0af00f90f0314ddff68c1ba77ba7
Binaries are updated: http://killtube.org/downloads/libcod/2015.01.17/



ret = call_function(libc_printf, "s.", "asd\n");
printf("printed chars: %\n", ret);


Output:


asd
printed chars: 4


Basically that's just eax as int though atm. Some converting based on signature would be nice, like "s.)i" for "return an int".