oif2003 wrote: ↑17 Dec 2018, 14:47
It seems like you are already on the path of finding a general solution to the problem!
I noticed 1 more strange thing as well: within
MCode initializer function we call
VirtualProtect and declare the size of memory required to cram our mcode into it, we give that memory area
PAGE_EXECUTE_READWRITE abilities,
thats not right by itself - the code has to be just
PAGE_EXECUTE or
PAGE_EXECUTE_READ (to store some strings and other constants right inside
.text code). With the current setting we can do whatever we want including storing our data (dynamically changing variables) right inside it or even dynamically change the whole mcode placeholder's content, replace it with another mcode.
I was tired yday and didnt check it but the simplest hack would be just to allocate a bigger memory region with
GlobalAlloc+
VirtualProtect so all the
relative variables addressed inside
.data offset would be within that area thus making ur code work as is:
Code: Select all
int test(void) {
static int i = 0;
return ++i;
}
2.
But thats a bad practice. better make the caller initialize some structure with data for the calee mcode:
VatSetCapacity(buffer, size) and call the mcode with
&buffer addr as its parameter each time. So the callee stores its intermediate data. Many system "simple 1-task" functions work this way - they dont have any static data, just
alloca allocated stack buffer for local variables.
We might put in that structure addresses of some other DLL functions needed for mcode, the latter makes external DLL calls while being truly portable at the same time.
3.
Hmm I figured out ur code snippet has 2 candidates for
fixups actually. in the screenshot above it has 2
uint32 placeholders (filled with 4 zero bytes) for the DLL/ELF loader to replace them with real value`s address.
Thats a no-go for portable mcodes. These addresses should be relative either to the stack (local vars area) or to the code itself, they shouldn't be absolute. I placed text cursor on the first occurrence of such
fixup placeholder @
0x4D. W/o proper fixups the code just destroys itself (overwrites DWORD @ 0x5A so it never returns).
Ofc we could recreate full loader's logic and patch these fixup placeholders ourselves, but better if we design source code to be portable.
Btw pay ur attention that its 7 machine commands in total. 4 of them r plain redundant. Also it seems TCC mixes 32bit code inside x64 segment (
mov ebp,esp should be
mov rbp,rsp) - it works but is prone to errors for addresses over 32bits and its suboptimal for compiled code size.