How to call a function pointer call saved from a previous execution fails?

I was curious if it is possible to save function pointers in a file and use at some future point in time when the program exited and started again. For example, my first test program looked something like this pseudocode:

void f(){} typedef void(*Fptr)(); int main() { int i; cin >> i; if (i == 1) { std::ofstream out(/**/); out << &f; } else { std::ifstream in(/**/); Fptr fp; in >> fp; fp(); } } 

This is just the logic of what I wanted to do. I would start it with input 1 , let it exit and start it again using input 2 . Do not consider this to be my real code, as I deleted the original test, because ...

This only works if I do not change the directory where the executable is located!

Adding a new file to the directory (presumably deleting the file too) and moving the executable file somewhere new will cause fp(); to fail fp(); . The new function address will be a different value.

So, I did a new test that calculates the difference between the old function pointer and the address of the current function. Applying this offset to the old function pointer and calling it gives the correct function call, no matter what I do in the directory.

I am sure it is UB. However, as deleting a reference to a null pointer will cause segfault, UB is pretty consistent.

Besides overwriting data with garbage and assuming functions are not loaded in the DLL, how likely is this method to succeed? How does it still not work?

+8
c ++ undefined-behavior
source share
2 answers

As already mentioned, this problem is caused by "randomization of the address space layout" (ASLR). This randomization is performed for each module (i.e., for each executable image). This means that if all of your functions are contained in your .exe, they are guaranteed to have the same offset from the base of the module. If any function is in the DLL, then the same applies to the base of the DLL module. It is important that the relative addresses of the modules remain unchanged, since otherwise it would be impossible to find entry points and DLL functions.

In Windows environment:

In Visual Studio (and MSVC), ASLR is enabled by default, but you can disable it in the option "Linker> Advanced> Randomized Base Address" (/ DYNAMICBASE: NO on the command line). By disabling this option, functions will always have the same address.

You can also determine the offset at runtime. The module base address can be obtained using GetModuleHandle() (the module descriptor is actually the base address). In doing so, you can work with relative addresses for pointers.

 uintptr_t base_address = (uintptr_t)GetModuleHandle(NULL); uintptr_t offset = (uintptr_t)&f - base_address; out << offset; in >> offset; fp = (Fptr)(offset + base_address); fp(); 
+2
source share

The function pointer will ONLY work if the program is loaded from the same address every time. The modern OS has a "randomization of address space", which leads to a random movement of the actual address of the code, data and stack - to avoid attacks that change the return address, because it is impossible to find out the address "return to" if it is chosen randomly.

There are settings to turn off random changes.

Obviously, it will also not work if the code is changed, which is located between the beginning of the code section in which the called function is located.

The pointer is converted to void * , which should be possible - it is obvious that the contents of the file will not work on another OS or processor architecture, but I see no particular reason why this does not work.

However, a more portable way would be to save the sequence number of the operation used, rather than a function pointer. Then do the following:

 for(;;) switch(sequence) { case 1: f(); sequence++; break; case 2: g(); sequence++; break; } ... } 

In case of failure save sequence (or sequence - 1 ).

The above assumes that the functions f and g throw an exception or use longjmp to exit [or that ... checks for errors].

Other than that, I don’t see the technical reason why

+1
source share

All Articles