With the caveat that this method is utterly unportable as T.C. mentions, and the additional caveat that it almost certainly will not work if optimizations are turned on, you can read the return address from the stack by reading off the end of a buffer, as in the example below.
int main()
{
f1();
return 0;
}
void f1()
{
f2();
}
void f2() {
char buf[4];
printf("%p\n", *(void**)(buf + 8));
}
Note that the number 8
above will vary based on operating system, architecture, and compiler padding, so you will most likely have to try a variety of different numbers to make it work.
The choice of 8
for the example assumes padding up to a 4-byte boundary, and 4-byte pointers on a 32-bit system.
You also have to make sure optimizations are turned off.
The reason why this works at all is because the structure of the stack immediately after a function call looks kind of like this.
|Return Address|
|Saved Frame Pointer|
|Local Variables|
Observe that the return address is at a higher address than the Local Variables. That is the reason that reading past the end of a buffer will allow you to potentially read the return address.
The reason why this breaks with optimizations is that compiler might decide to inline one or both functions, or realize one of them is doing nothing at all and optimize the function call away entirely, which blows aside the assumptions about the stack.