Well, I've been digging around process command line arguments as well as environment variables a bit these last days, especially looking up the way main
function arguments were pushed onto the stack.
So far I've got it that a certain _libc_start_main()
function is responsible of setting up everything the main()
function needs in terms of parameters before actually calling it.
Without getting into much details, I've noticed that when debugging a simple main
program, the main
stack frame is different whether we look it up in radare2
or gdb
.
For instance, let's take this minimalist C program :
int main (int argc, char *argv[], char *envp[])
{
}
And simply debug it without any additional parameters :
With GDB
After setting a breakpoint on the first assembly instruction of main
(I couldn't dump the stack frame without running the program),
what I got in gdb
is something very sensible, as one can see :
(gdb) x/3xw $esp
0xffffcfbc: 0xf7db7b41 0x00000001 0xffffd054
# ^ ^ ^
# PC (somewhere argc argv
# in __libc_start_main())
Now by actually inspecting the pointed out memory regions for argv
:
(gdb) x/2xw 0xffffd054 # argv
0xffffd054: 0xffffd1ef 0x00000000
# ^ ^
# argv[0] argv[argc]
# (another pointer)
(gdb) x/s 0xffffd1ef # argv[0]
0xffffd1ef: "<path>/argvonstack32"
# ^
# Exepected program name
So what was basically pushed onto the main
stack frame, for both argv
and envp
(even if I didn't show the dump for envp
for simplicity's sake) is exactly what we were entitled to expect from a debugger, that is a pointer to pointer to char
(as stated in the main
function signature).
With Radare2
Without setting any breakpoint, and directly inspecting the stack frame without running the program, radare2
shows a different stack frame :
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF comment
0xffa63d10 0100 0000 1953 a6ff 0000 0000 2953 a6ff .....S......)S.. ; esp
^ ^ ^
argc argv[0] argv[1]
When inspecting argv[0]
:
- offset - 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF comment
0xffa65319 2e2f 6172 6776 6f6e 7374 6163 6b33 3200 ./argvonstack32.
0xffa65329 5348 454c 4c3d 2f62 696e 2f62 6173 6800 SHELL=/bin/bash.
That shows that radare2
skipped the first pointer indirection of argv
and pushed the list argv[0]...argv[argc]
directly onto the main
stack frame.
What explains such a difference ?
PS: As you can see, the only difference there is between my usage of radare2
and gdb
was that I runned the program in gdb
while didn't need to actually run it on radare2
to dump the main
stack frame memory.