I've seen from a Numberphile video (https://youtu.be/1S0aBV-Waeo) a way to run a buffer overflow, and I wanted to try it out.
I have written a piece of code, which is identical to the one shown in the video except for the size of "buffer", but, if I give in input a string bigger than the size of "buffer", I am not getting a segmentation fault, as it was shown in the video; can someone explain why?
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv){
char buffer[50];
strcpy(buffer, argv[1]);
return 0;
}
Edit:
By the way, as I've seen in the comments that this is a determinating thing, I am using th GCC compiler.
I am not getting a segmentation fault, as it was shown in the video; can someone explain why?
The program has undefined behavior as you're inputting a string bigger than the size of buffer and from strcpy documentation:
To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C string as source (including the terminating null character), and should not overlap in memory with source.
(emphasis mine)
Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely(or make conclusions based) on the output of a program that has undefined behavior. The program may just crash.
So the output that you're seeing(maybe seeing) is a result of undefined behavior. And as I said don't rely on the output of a program that has UB. The program may just crash.
So the first step to make the program correct would be to remove UB. Then and only then you can start reasoning about the output of the program.
1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.
If I am correct that you wanted to understand what happened in your specific case, you could improve your question by providing the version of the compiler, the arguments you passed to the compiler, the arguments you passed to your program, and the output of your program. That way, you would have a Minimal Reproducible Example and we would understand better what your specific case is.
For example, I use GCC 9.4.0:
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Here is what happened when I compiled without optimization and passed a string with 55 characters as an argument to the program:
$ gcc -o bufferoverflow bufferoverflow.c
$ ./bufferoverflow 1234567890123456789012345678901234567890123456789012345
$
So, even though the number of bytes copied into the buffer, 56 including the terminator, should cause a write past the end of the buffer, the program ran without any error that is visible by simply looking at standard error or standard output.
Here is what happened when I ran the same executable but passed a 57 character string in the command line.
$ ./bufferoverflow 123456789012345678901234567890123456789012345678901234567
*** stack smashing detected ***: terminated
Aborted (core dumped)
$
One way to understand what happened in the case with the 55 character string is to run it again using using gdb, which can be started as shown:
$ gdb bufferoverflow
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bufferoverflow...
(No debugging symbols found in bufferoverflow)
(gdb)
Now lets see why passing a 55 character string as the first argument didn't result in an obvious failure:
(gdb) break main
Breakpoint 1 at 0x1169
(gdb) r 1234567890123456789012345678901234567890123456789012345
Starting program: /home/tim/bufferoverflow 1234567890123456789012345678901234567890123456789012345
Breakpoint 1, 0x0000555555555169 in main ()
(gdb) x/23i main
=> 0x555555555169 <main>: endbr64
0x55555555516d <main+4>: push %rbp
0x55555555516e <main+5>: mov %rsp,%rbp
0x555555555171 <main+8>: sub $0x50,%rsp
0x555555555175 <main+12>: mov %edi,-0x44(%rbp)
0x555555555178 <main+15>: mov %rsi,-0x50(%rbp)
0x55555555517c <main+19>: mov %fs:0x28,%rax
0x555555555185 <main+28>: mov %rax,-0x8(%rbp)
0x555555555189 <main+32>: xor %eax,%eax
0x55555555518b <main+34>: mov -0x50(%rbp),%rax
0x55555555518f <main+38>: add $0x8,%rax
0x555555555193 <main+42>: mov (%rax),%rdx
0x555555555196 <main+45>: lea -0x40(%rbp),%rax
0x55555555519a <main+49>: mov %rdx,%rsi
0x55555555519d <main+52>: mov %rax,%rdi
0x5555555551a0 <main+55>: callq 0x555555555060 <strcpy#plt>
0x5555555551a5 <main+60>: mov $0x0,%eax
0x5555555551aa <main+65>: mov -0x8(%rbp),%rcx
0x5555555551ae <main+69>: xor %fs:0x28,%rcx
0x5555555551b7 <main+78>: je 0x5555555551be <main+85>
0x5555555551b9 <main+80>: callq 0x555555555070 <__stack_chk_fail#plt>
0x5555555551be <main+85>: leaveq
0x5555555551bf <main+86>: retq
From the above disassembly we can see that main+60 is just after the call to strcpy. We can also see, by looking at main+45 and main+52 that the buffer is at %rbp-0x40. We can continue to that point and look at what happened to the buffer:
(gdb) b *(main+60)
Breakpoint 2 at 0x5555555551a5
(gdb) c
Continuing.
Breakpoint 2, 0x00005555555551a5 in main ()
(gdb) x/56bx $rbp-0x40
0x7fffffffdf90: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38
0x7fffffffdf98: 0x39 0x30 0x31 0x32 0x33 0x34 0x35 0x36
0x7fffffffdfa0: 0x37 0x38 0x39 0x30 0x31 0x32 0x33 0x34
0x7fffffffdfa8: 0x35 0x36 0x37 0x38 0x39 0x30 0x31 0x32
0x7fffffffdfb0: 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x30
0x7fffffffdfb8: 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38
0x7fffffffdfc0: 0x39 0x30 0x31 0x32 0x33 0x34 0x35 0x00
So we can see that, in spite of the fact that when we ran with this string earlier without gdb we didn't notice any obvious error, in fact the buffer overflow did occur. We simply didn't notice that it had. To understand why we didn't notice, one only has to look at the disassembly to see that the next used address on the stack is at %rbp-8 which is 56 bytes after %rbp-0x40. So the overflow went onto memory that was not in use.
The same disassembly shows why we get the stack smashing detected message when we run the program with the 57 character string. In that case, we clobber part of the 8-byte value at %rbp-8 which is used (at main+19, main+28, main+65, main+69 and main+78) as a check for whether the stack got corrupted during the call to main. So the reason we see that particular error with that particular input is that the 8-byte value at %rbp-8 was the only part of the stack that we clobbered that was actually used after we clobbered it and the message in question was as a result of noticing that those 8 bytes had changed.
Even if you did not compile your program exactly the way I did, and even if you did not use exactly the same input, I hope I have given you some solid ideas about how to understand the behavior in your case.
Related
I'm reading a textbook which shows assembly code based on C code:
C code:
void echo()
{
char buf[8];
otherFunction(buf);
}
assembly code:
echo:
subq $24, %rsp //Allocate 24 bytes on stack, but why allocate 24 instead of 8 bytes?
movq %rsp, %rdi //Compute buf as %rsp
call otherFunction
I don't understand why stack pointer %rsp is decremented by 24 bytes. I only assign 8 bytes' buffer as char buf[8];, and there no callee saved registers to push on stack, shouldn't the instruction be
subq $8, %rsp
Allocating an extra 16 bytes of stack space is a GCC missed optimization that pops up occasionally. I don't know why it happens, but it's reproducible with GCC10.1 -O3. Clang doesn't do it, it just reserves 8 bytes (with a dummy push). Example on Godbolt, where -fno-stack-protector -fno-pie is the default, unlike GCC in many GNU/Linux distros.
Even int buf; / foo(&buf) results in over-allocation.
My wild guess is that there's something GCC doesn't optimize away until after it's already decided it needs more than 8 bytes of space (and thus needs 24). Hopefully this good MCVE will let GCC devs find an fix that bug, if it's easily fixable.
Feel free to report this as a GCC missed-optimization bug (https://gcc.gnu.org/bugzilla/); I looked recently but didn't find an existing one.
You're correct that allocating 8 bytes would be enough for char buf[8] and re-align RSP by 16 before the call, as required by the x86-64 System V ABI (Why does System V / AMD64 ABI mandate a 16 byte stack alignment?).
GCC is not trying to maintain 32-byte stack alignment or anything. The default for -mpreferred-stack-boundary is the minimum allowed by the ABI, 4 (2^4 = 16).
AFAIK the stack must be 16 byte aligned for function calls but I have no clue as to why 24 bytes were allocated and not only 16.
There are some questions about this on SO already. Why does GCC 6 assume data is 16-byte aligned?
and on GCCs bugzilla
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838
I'm analyzing a core post-mortem via disassemble output of gdb. I'm new to this, so I'm still growing in my understanding of what I'm looking at. One immediate confusion for me is that as I go between frames and look at disassemble output, I don't see callq commands as the command being run as I would expect for all the non-frame 0 frames. Shouldn't each frame leading up to frame 0 be calling a function?
(gdb) f 0
(gdb) disassemble
...
=> 0x0000000001b0af10 <+16>: mov (%rdi),%rdx
...
End of assembler dump.
(gdb) info registers rdi
rdi 0x0 0
Makes sense: the crash happened due to a null ptr dereference. Now lets go up a fame and see the disassemble output there:
(gdb) up
(gdb) disassemble
...
=> 0x0000000001b1c01b <+315>: test %al,%al
...
What? The frame above is running test? Shouldn't it be calling the function disassembled in frame 0? What am I misunderstanding?
This is x64 assembly generated from GCC 4.8 compiling C++ code.
What am I misunderstanding?
On x86 (and x86_64), the CALL instruction pushes the address of the next instruction onto stack, and jumps to the called function.
When you go up, the current instruction is the one that will be executed after the frame you just stepped up from returns.
Do x/i $pc-5 if you want to see the actual CALL (note: the -5 works for most, but not all CALLs. See Peter Cordes comment below).
Is GDB capable of changing certain program tasks? such as from "jle"(Jump less than or equal to) to "jge"(jump greater than or equal to).
from: 0x0000000000400563 <+45>: jle 0x400547 <main+17>
to: 0x0000000000400563 <+45>: jge 0x400547 <main+17>
Is GDB capable of changing memory arithmetics?
What is "memory arithmetic"?
such as from "jle"(Jump less than or equal to) to "jge"(jump greater than or equal to).
Your question is exceedingly unclear. I think you are asking: can I change a program to perform JGE instead of current JLE instruction in GDB?
If that is your question, the answer is yes.
Find the opcode that is currently being used with disas/r 0x400563,0x400564 command.
Change the opcode to that of JGE with an assignment to the instruction opcode.
Example:
(gdb) disas/r 0x400496,0x400497
Dump of assembler code from 0x400496 to 0x400497:
0x0000000000400496 <main(int, char**)+15>: 7e 07 jle 0x40049f <main(int, char**)+24>
End of assembler dump.
The opcode is 0x7E. The opcode for JGE is 0x7D (table).
Let's patch. You can do that for a running process:
(gdb) start
(gdb) set *(char*)0x400496 = 0x7d
(gdb) disas/r 0x400496,0x400497
Dump of assembler code from 0x400496 to 0x400497:
0x0000000000400496 <main(int, char**)+15>: 7d 07 jge 0x40049f <main(int, char**)+24>
End of assembler dump.
Or you can update the binary on disk, but invoking gdb --write a.out.
Is it possible to examine memory as instruction (x/i) the way I can see both asm and raw instructions in hex (like with disassemble /r)?
Sometimes I want to disassemble some part of memory which GDB refuses to disassemble saying: "No function contains specified address".
The only option is then x/i, but I would like to see exactly what hex values are translated to what instructions.
I want to disassemble some part of memory which GDB refuses to disassemble saying: "No function contains specified address".
The disas/r 0x1234,0x1235 will work even when GDB can not determine function boundaries. Example:
(gdb) disas/r 0x0000000000400803
No function contains specified address.
(gdb) disas/r 0x0000000000400803,0x000000000040080f
Dump of assembler code from 0x400803 to 0x40080f:
0x0000000000400803: e8 b8 fd ff ff callq 0x4005c0 <system#plt>
0x0000000000400808: 48 81 45 f0 00 10 00 00 addq $0x1000,-0x10(%rbp)
End of assembler dump.
I am looking into gdb, for breakpoint implementation. For ease I took the very first GDB release 2.51 (released 1988). I see the break point insert data as -
#define BREAKPOINT {0x4e, 0x4f}
what is 0x4e and 0x4f ?
The 0x4E 0x4F is the Motorola 68000 machine instruction for "TRAP #15". The TRAP instruction forces an exception to occur, and on certain platforms trap #15 is defined as a breakpoint exception. This is why 0x4E and 0x4F appear in your GDB 2.51 source for handling platforms such as sun3.
References:
This Motorola M68000 Family Programmer's Reference Manual contains the details of the TRAP instruction on page 4-188. Specifically, the instruction is represented by the 12-bit value 010011100100 followed by the 4-bit "vector" (in this case, 1111). So "TRAP #15" is represented by 0x4E 0x4F. Sun specifically uses vector 15 for breakpoint/tracing -- a Google search reveals numerous comments and source code examples.