Reputation: 25687
I am trying to understand low-level memory manager in C especially Stack. As I was told, when a function is called, a return address was push on a stack. Then local variables is located after that.
So I write a small program to investigate this. Here is my program:
#include <stdio.h>
void TestStack();
void DoTestStack() {
char x1 = 1;
char x2 = 2;
char x3 = 3;
char x4 = 4;
char *x = &x4;
printf("TestStack: %08X\n", (&TestStack));
printf("\n");
int i;
x = &x4;
for(i = 0; i < 32; i++)
printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));
printf("\n");
printf("x1: %02X\n", x1);
printf("x2: %02X\n", x2);
printf("x3: %02X\n", x3);
printf("DONE!!!\n");
}
void TestStack() {
DoTestStack();
}
void main() {
TestStack() ;
}
Basically, it investigate all memory before and after the position x4 is located. which should well cover the position of the return address.
But I can't seems to find any byte that resembles the return address at all.
Here is my result:
TestStack: 08048B49
00: 00000004 : 00000004
01: 00000003 : FFFFFFBF
02: 00000002 : FFFFFFAC
03: 00000001 : FFFFFFED
04: 00000004 : 0000001C
05: FFFFFFC3 : 00000000
06: FFFFFFB9 : 00000000
07: 00000000 : 00000000
08: FFFFFFF4 : 00000008
09: FFFFFFBF : 00000000
10: FFFFFFB9 : FFFFFF90
11: 00000000 : FFFFFFBD
12: 00000038 : 00000020
13: FFFFFFED : 00000000
14: FFFFFFAC : 00000000
15: FFFFFFBF : 00000000
16: 00000054 : 00000000
17: FFFFFF8B : 00000000
18: 00000004 : FFFFFFFF
19: 00000008 : 00000000
20: 00000045 : 00000008
21: 00000073 : 00000000
22: FFFFFFA7 : 00000000
23: 00000000 : 00000000
24: 00000020 : 00000017
25: FFFFFFBD : 00000008
26: FFFFFF90 : 00000004
27: 00000000 : FFFFFF8C
28: 00000048 : FFFFFFCF
29: FFFFFFED : 00000008
30: FFFFFFAC : 00000004
31: FFFFFFBF : FFFFFF8A
x1: 01
x2: 02
x3: 03
DONE!!!
What am I missing here? Can someone please explain.
I am on Ubuntu 9.10 anyway.
Thanks in advance. :-D
Upvotes: 1
Views: 444
Reputation: 5955
A good idea if you want to know how this all works is to run your application in a debugger. It's really cut's the crap to figure out what's going on.
Any debugger is OK, however I know windbg best so here's some pointers on where to get started at a slightly modified version of your code, like Greg suggested, you need an int*.
CHANGE:
char *x = &x4;
TO:
int *x = &i;
DELETE:
x = &x4;
MOVE(first variable, before x1):
int i;
NEW CHANGE(much easeir to read, and doing as previously (x-i) is random/not currently in-scope values):
printf("%02d: %08x : %08x\n", i, x + i, *(x + i));
This change will show effectivly the stack-frame address and the value.
In windbg, get the calls, memory, threads and command windows up so you can see them all. Compile your C code with a compiler, I used MSVC (you can get it for trial for free), compile with the "Visual Studio 20## Command Prompt" with "cl /Zi your.c". Load up Windbg (debugging tools for Windows), press Ctrl+E or use "open executable".
Load the following windows so you can see them all at one time, Calls, Locals, Memory, Threads and Command.
At the command window, put a breakpoint on your DoTestStack with "bu DoTestStack".
start debugging with "g" command.
After you get to the breakpoint, use "p" to single step, you should get the source popped up also, or you can watch the output of your application, after it's running in the for loop, go back to Windbg. Get the memory window up and set it to "Pointers and Symbols" for an address type in "i", it should have the debug information from the compile (/Zi) and will give you a listing of "Pointers and symbols" starting from a pointer to the address of i.
The output should be identical to the test code (after you do the changes I suggested), if you continue to hit p, you will see also that in the memory window you can observe the value for i changing per execution, however as the printf is now printing other values, you will simply see it at the original "0" on the output ;).
You can alternativly use the windbg command dds (note the value for i is 0xb as it's in the middle of the for loop)
0:000> dds i
0018ff24 0000000b
0018ff28 02586bf9
0018ff2c 0018ff24
0018ff30 0018ff38
0018ff34 00401118 a!TestStack+0x8 [c:\temp\a.c @ 33]
0018ff38 0018ff40
0018ff3c 00401128 a!main+0x8 [c:\temp\a.c @ 35]
0018ff40 0018ff88
0018ff44 00401435 a!__tmainCRTStartup+0xf8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crt0.c @ 257]
0018ff48 00000001
0018ff4c 003c1e48
This is identical (exept no symbol support) as the modified test code;
00: 0018ff24 : 00000000
01: 0018ff28 : 02586bf9
02: 0018ff2c : 0018ff24
03: 0018ff30 : 0018ff38
04: 0018ff34 : 00401118
05: 0018ff38 : 0018ff40
06: 0018ff3c : 00401128
07: 0018ff40 : 0018ff88
08: 0018ff44 : 00401435
09: 0018ff48 : 00000001
10: 0018ff4c : 003c1e48
Upvotes: 1
Reputation: 24140
You're looking at individual chars, then casting those to 32-bit integers, which is confusing you. The return address is in the least significant bytes of these four lines:
16: 00000054 : 00000000
17: FFFFFF8B : 00000000
18: 00000004 : FFFFFFFF
19: 00000008 : 00000000
I.e. your return address is 0x08048b54.
Try this instead:
uint32_t *x;
x = (uint32_t *)&x4;
for(i = 0; i < 32; i++)
printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));
Upvotes: 6
Reputation: 993085
In this line:
printf("%02d: %08X : %08X\n", i, *(x + i), *(x - i));
the x
pointer is a character pointer, which after dereferencing gets promoted to an integer with sign extension, which causes all your output values to be either 000000xx or FFFFFFxx depending on the value of bit 7.
Instead, what you probably want to do is use an int
pointer to scan through the stack values, instead of a char
pointer.
Upvotes: 4