Need help disassembling [closed] - c

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
I need your help. Here is the source code of my program. I need to understand what manipulations are being done by the score1, score2, score3 and score4 functions.
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <pwd.h>
7 #include <unistd.h>
8
9 #include "score.h"
10
(gdb)
11 int main(int argc, char *argv[])
12 {
13 int i, j, k, l, s;
14 struct passwd *pw;
15 char cmd[1024];
16
17 /* Make sure that we have exactly 5 arguments: the name of the executable, and 4 numbers */
18 if (argc != 5) {
19 printf("Usage: %s i j k l\n where i,j,k,l are integers.\n Try to get as high a score as you can.\n", argv[0]);
20 exit(8);
(gdb)
21 }
22
23 initialize();
24
25 /* Convert the inputs to ints */
26 i = atoi(argv[1]);
27 j = atoi(argv[2]);
28 k = atoi(argv[3]);
29 l = atoi(argv[4]);
30
(gdb)
31 printf("You entered the integers %d, %d, %d, and %d.\n", i, j, k, l);
32 s = score1(i) + score2(j) + score3(k) + score4(l);
33
34 printf("Your score is %d.\n", s);
35 if (s > 0) {
36 pw = getpwuid(getuid());
37
38 printf("Thank you!\n");
40 system(cmd);
I have started disassemble the code like the following:
(gdb) disas score1
Dump of assembler code for function score1:
0x080488b0 <score1+0>: push %ebp
0x080488b1 <score1+1>: mov %esp,%ebp
0x080488b3 <score1+3>: cmpl $0xe1e4,0x8(%ebp)
0x080488ba <score1+10>: setne %al
0x080488bd <score1+13>: movzbl %al,%eax
0x080488c0 <score1+16>: sub $0x1,%eax
0x080488c3 <score1+19>: and $0xa,%eax
0x080488c6 <score1+22>: pop %ebp
0x080488c7 <score1+23>: ret
(gdb) disas score2
Dump of assembler code for function score2:
0x080488c8 <score2+0>: push %ebp
0x080488c9 <score2+1>: mov %esp,%ebp
0x080488cb <score2+3>: mov 0x8049f88,%eax
0x080488d0 <score2+8>: sub $0x2,%eax
0x080488d3 <score2+11>: mov %eax,0x8049f88
0x080488d8 <score2+16>: cmp 0x8(%ebp),%eax
0x080488db <score2+19>: setne %al
0x080488de <score2+22>: movzbl %al,%eax
0x080488e1 <score2+25>: sub $0x1,%eax
0x080488e4 <score2+28>: and $0xa,%eax
0x080488e7 <score2+31>: pop %ebp
0x080488e8 <score2+32>: ret
(gdb) disas score3
Dump of assembler code for function score3:
0x080488e9 <score3+0>: push %ebp
0x080488ea <score3+1>: mov %esp,%ebp
0x080488ec <score3+3>: mov 0x8(%ebp),%eax
0x080488ef <score3+6>: and $0xf,%eax
0x080488f2 <score3+9>: mov 0x8048e00(,%eax,4),%eax
0x080488f9 <score3+16>: pop %ebp
0x080488fa <score3+17>: ret
(gdb) disas score4
Dump of assembler code for function score4:
0x080488fb <score4+0>: push %ebp
0x080488fc <score4+1>: mov %esp,%ebp
0x080488fe <score4+3>: push %ebx
0x080488ff <score4+4>: mov 0x8(%ebp),%eax
0x08048902 <score4+7>: movzwl %ax,%edx
0x08048905 <score4+10>: mov %eax,%ecx
0x08048907 <score4+12>: shr $0x10,%ecx
0x0804890a <score4+15>: lea 0x0(,%edx,8),%eax
0x08048911 <score4+22>: sub %edx,%eax
0x08048913 <score4+24>: cmp %ecx,%eax
0x08048915 <score4+26>: jne 0x8048920 <score4+37>
0x08048917 <score4+28>: mov $0x8000ffff,%ebx
0x0804891c <score4+33>: test %edx,%ecx
0x0804891e <score4+35>: jne 0x8048940 <score4+69>
0x08048920 <score4+37>: mov %ecx,%eax
0x08048922 <score4+39>: xor %edx,%eax
0x08048924 <score4+41>: cmp $0xf00f,%eax
0x08048929 <score4+46>: jne 0x804893b <score4+64>
0x0804892b <score4+48>: mov %ecx,%eax
0x0804892d <score4+50>: or %edx,%eax
0x0804892f <score4+52>: mov $0xa,%ebx
0x08048934 <score4+57>: cmp $0xf42f,%eax
---Type <return> to continue, or q <return> to quit---
0x08048939 <score4+62>: je 0x8048940 <score4+69>
0x0804893b <score4+64>: mov $0x0,%ebx
0x08048940 <score4+69>: mov %ebx,%eax
0x08048942 <score4+71>: pop %ebx
0x08048943 <score4+72>: pop %ebp
0x08048944 <score4+73>: ret
I've started examining score2.
What I have done is:
(
gdb) x 0x8049f88
0x8049f88 <secret>: "Чй"
(gdb) disas 0x8049f88
Dump of assembler code for function secret:
0x08049f88 <secret+0>: dec %dl
0x08049f8a <secret+2>: add %al,(%eax)
End of assembler dump.
And I'm lost here.
Here's what I think happens so far (See comments):
(gdb) disas score2
Dump of assembler code for function score2:
0x080488c8 <score2+0>: push %ebp
0x080488c9 <score2+1>: mov %esp,%ebp 'Copy %esp into %ebp
0x080488cb <score2+3>: mov 0x8049f88,%eax 'executing: decrement and add
0x080488d0 <score2+8>: sub $0x2,%eax ' subtract $0x2 from %eax (How can I figure out what $0x2
0x080488d3 <score2+11>: mov %eax,0x8049f88 'Have no idea what this does
0x080488d8 <score2+16>: cmp 0x8(%ebp),%eax compare of %ebp to %eax (why %ebp has 0x8 preceding it?)
0x080488db <score2+19>: setne %al 'I have no idea what this does
0x080488de <score2+22>: movzbl %al,%eax
0x080488e1 <score2+25>: sub $0x1,%eax
0x080488e4 <score2+28>: and $0xa,%eax
0x080488e7 <score2+31>: pop %ebp
0x080488e8 <score2+32>: ret
If you could help me understand what kind of transformations score2 performs to an integer and what commands can I run in gdb that could help me, I would really appreciate it and would try to figure rest of it(score1-3) by myself. I'm just lost here.

There's only really 2 things you need to know to understand a disassembly. The first thing you need to know is all the instructions and addressing modes support by the CPU and how they work. The second thing is the syntax used by the assembler/disassembler. Without being familiar with either of these things you will get nowhere.
For an example of "you will get nowhere", here's score2:
0x080488c8 <score2+0>: push %ebp ;Save EBP
0x080488c9 <score2+1>: mov %esp,%ebp ;EBP = address of stack frame
0x080488cb <score2+3>: mov 0x8049f88,%eax ;EAX = the data at address 0x8049f88
0x080488d0 <score2+8>: sub $0x2,%eax ;EAX = EAX - 2
0x080488d3 <score2+11>: mov %eax,0x8049f88 ;The value at address 0x8049f88 = eax
0x080488d8 <score2+16>: cmp 0x8(%ebp),%eax ;Compare the int at offset 8 in the stack frame with EAX
0x080488db <score2+19>: setne %al ;If the int at offset 8 in the stack frame wasn't equal to EAX, set AL to 0, otherwise set AL to 1
0x080488de <score2+22>: movzbl %al,%eax ;Zero-extend AL to EAX (so EAX = 0 or 1)
0x080488e1 <score2+25>: sub $0x1,%eax ;Decrease EAX (so EAX = -1 or 0)
0x080488e4 <score2+28>: and $0xa,%eax ;EAX = EAX AND 0x0A (so EAX = 0xA or 0)
0x080488e7 <score2+31>: pop %ebp ;Restore previous EBP
0x080488e8 <score2+32>: ret ;Return
Converting back into C, this might look something like:
int score2(int something) {
some_global_int -= 2;
if(some_global_int == something) return 0;
else return 0x0A;
}
Of course I only slapped this together in 5 minutes, and haven't double checked anything or tested anything, so it could be wrong.
After reading the above "score2" code, are you any closer to understanding the disassembly of any of the other functions?
Based on your initial attempt at commenting score2, you should either ask someone to do all the work for you (and learn nothing, and have no way of knowing if that person is right or wrong), or ask for the best place to learn 80x86 assembly (and AT&T syntax).

I'm assuming you're given some kind of compiled library with the score functions in it, and you're trying to reverse engineer it as some kind of homework project. In that case, I suggest you start familiarizing yourself with the standard C calling convention cdecl.
Basically, esp points to the stack, on which the arguments to the function are pushed before it's called, so a C function first moves esp into ebp and then it can access the arguments by subtracting values from ebp and dereferencing the resulting address. It uses ebp for this purpose so it can still modify esp in order to add more local variables on the stack without losing track of where the arguments are stored.
Anyway, here's an overview of score2 to help get you started:
(gdb) disas score2
Dump of assembler code for function score2:
0x080488c8 <score2+0>: push %ebp
0x080488c9 <score2+1>: mov %esp,%ebp ; This just saves a copy of the top of our stack to read arguments with
0x080488cb <score2+3>: mov 0x8049f88,%eax ; Load a value from a memory location (the number is a memory address, probably to a global variable)
0x080488d0 <score2+8>: sub $0x2,%eax ; Subtract 2
0x080488d3 <score2+11>: mov %eax,0x8049f88 ; Store the new value into the same memory location
0x080488d8 <score2+16>: cmp 0x8(%ebp),%eax ; Compare the first argument of the function to that value
0x080488db <score2+19>: setne %al ; Sets the lower byte of eax to 1 if they don't match
0x080488de <score2+22>: movzbl %al,%eax ; Sets al to eax, zeroing the upper bytes so eax is just 1 or 0 now
0x080488e1 <score2+25>: sub $0x1,%eax ; Subtract 1 from eax
0x080488e4 <score2+28>: and $0xa,%eax ; eax = eax & 0xa
0x080488e7 <score2+31>: pop %ebp
0x080488e8 <score2+32>: ret ; Return eax
So that means there is some kind of global variable stored at 0x8049f88
(let's call it x), and score2 literally translates to:
int score2(int n) {
x -= 2;
if (n == x)
n = 1;
else
n = 0;
n--;
n = n & 0xa;
return n;
}
EDIT: Brendan's example is the same, but probably looks more like the original code. Look over it a few times and compare it to the assembly output.
The next step is now to see what's in the variable at 0x8049f88. Try running awatch *0x8049f88 inside of gdb to make it stop on every access and also print *0x8049f88 to see what's stored there.
You should also run set disassembly-flavor intel if you're not too familiar with assembly language. The syntax will then match the examples you're more likely to find on the Internet.

I presume you don't have access to the source code of the functions, and for the puzzle or homework you are supposed to try to find numbers to get a big score. You probably should edit your question to display contents of score.h, or just the relevant portions if it's quite lengthy. Also, note that disassembly at 0x8049f88 doesn't make sense. Instead use gdb's x command to display that location, and edit accordingly.
While you can attack the problem via disassembly (as above) you can also try using a different main program, that reports the results of individual score?() calls, and that loops some of them through a series of values looking for big values.
With score2(), looping within main() won't work, because score2() subtracts 2 from a word in memory. So, if you wanted to try out a lot of inputs, you'd need to call the program with different arguments in a shell code loop. Eg, if you are using bash:
for i in {1..1000}; do testScore2 $i; done
where testScore2 is a main program that only runs score2() with its parameter and reports the result.
Of course, because score2() can produce only two different results, as explained in detail in two previous answers, it won't actually make sense to test score2() with more than two argument values. I showed the shell code above because you might want to use such a technique with some of the other score functions.

Related

Difficulty understanding logic in disassembled binary bomb phase 3

I have the following assembly program from the binary-bomb lab. The goal is to determine the keyword needed to run the binary without triggering the explode_bomb function. I commented my analysis of the assembly for this program but I am having trouble piecing everything together.
I believe I have all the information I need, but I still am unable to see the actual underlying logic and thus I am stuck. I would greatly appreciate any help!
The following is the disassembled program itself:
0x08048c3c <+0>: push %edi
0x08048c3d <+1>: push %esi
0x08048c3e <+2>: sub $0x14,%esp
0x08048c41 <+5>: movl $0x804a388,(%esp)
0x08048c48 <+12>: call 0x80490ab <string_length>
0x08048c4d <+17>: add $0x1,%eax
0x08048c50 <+20>: mov %eax,(%esp)
0x08048c53 <+23>: call 0x8048800 <malloc#plt>
0x08048c58 <+28>: mov $0x804a388,%esi
0x08048c5d <+33>: mov $0x13,%ecx
0x08048c62 <+38>: mov %eax,%edi
0x08048c64 <+40>: rep movsl %ds:(%esi),%es:(%edi)
0x08048c66 <+42>: movzwl (%esi),%edx
0x08048c69 <+45>: mov %dx,(%edi)
0x08048c6c <+48>: movzbl 0x11(%eax),%edx
0x08048c70 <+52>: mov %dl,0x10(%eax)
0x08048c73 <+55>: mov %eax,0x4(%esp)
0x08048c77 <+59>: mov 0x20(%esp),%eax
0x08048c7b <+63>: mov %eax,(%esp)
0x08048c7e <+66>: call 0x80490ca <strings_not_equal>
0x08048c83 <+71>: test %eax,%eax
0x08048c85 <+73>: je 0x8048c8c <phase_3+80>
0x08048c87 <+75>: call 0x8049363 <explode_bomb>
0x08048c8c <+80>: add $0x14,%esp
0x08048c8f <+83>: pop %esi
0x08048c90 <+84>: pop %edi
0x08048c91 <+85>: ret
The following block contains my analysis
5 <phase_3>
6 0x08048c3c <+0>: push %edi // push value in edi to stack
7 0x08048c3d <+1>: push %esi // push value of esi to stack
8 0x08048c3e <+2>: sub $0x14,%esp // grow stack by 0x14 (move stack ptr -0x14 bytes)
9
10 0x08048c41 <+5>: movl $0x804a388,(%esp) // put 0x804a388 into loc esp points to
11
12 0x08048c48 <+12>: call 0x80490ab <string_length> // check string length, store in eax
13 0x08048c4d <+17>: add $0x1,%eax // increment val in eax by 0x1 (str len + 1)
14 // at this point, eax = str_len + 1 = 77 + 1 = 78
15
16 0x08048c50 <+20>: mov %eax,(%esp) // get val in eax and put in loc on stack
17 //**** at this point, 0x804a388 should have a value of 78? ****
18
19 0x08048c53 <+23>: call 0x8048800 <malloc#plt> // malloc --> base ptr in eax
20
21 0x08048c58 <+28>: mov $0x804a388,%esi // 0x804a388 in esi
22 0x08048c5d <+33>: mov $0x13,%ecx // put 0x13 in ecx (counter register)
23 0x08048c62 <+38>: mov %eax,%edi // put val in eax into edi
24 0x08048c64 <+40>: rep movsl %ds:(%esi),%es:(%edi) // repeat 0x13 (19) times
25 // **** populate malloced memory with first 19 (edit: 76) chars of string at 0x804a388 (this string is 77 characters long)? ****
26
27 0x08048c66 <+42>: movzwl (%esi),%edx // put val in loc esi points to into edx
***** // at this point, edx should contain the string at 0x804a388?
28
29 0x08048c69 <+45>: mov %dx,(%edi) // put val in dx to loc edi points to
***** // not sure what effect this has or what is in edi at this point
30 0x08048c6c <+48>: movzbl 0x11(%eax),%edx // edx = [eax + 0x11]
31 0x08048c70 <+52>: mov %dl,0x10(%eax) // [eax + 0x10] = dl
32 0x08048c73 <+55>: mov %eax,0x4(%esp) // [esp + 0x4] = eax
33 0x08048c77 <+59>: mov 0x20(%esp),%eax // eax = [esp + 0x20]
34 0x08048c7b <+63>: mov %eax,(%esp) // put val in eax into loc esp points to
***** // not sure what effect these movs have
35
36 // edi --> first arg
37 // esi --> second arg
38 // compare value in esi to edi
39 0x08048c7e <+66>: call 0x80490ca <strings_not_equal> // store result in eax
40 0x08048c83 <+71>: test %eax,%eax
41 0x08048c85 <+73>: je 0x8048c8c <phase_3+80>
42 0x08048c87 <+75>: call 0x8049363 <explode_bomb>
43 0x08048c8c <+80>: add $0x14,%esp
44 0x08048c8f <+83>: pop %esi
45 0x08048c90 <+84>: pop %edi
46 0x08048c91 <+85>: ret
Update:
Upon inspecting the registers before strings_not_equal is called, I get the following:
eax 0x804d8aa 134535338
ecx 0x0 0
edx 0x76 118
ebx 0xffffd354 -11436
esp 0xffffd280 0xffffd280
ebp 0xffffd2b8 0xffffd2b8
esi 0x804a3d4 134521812
edi 0x804f744 134543172
eip 0x8048c7b 0x8048c7b <phase_3+63>
eflags 0x282 [ SF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x63 99
and I get the following disassembled pseudocode using Hopper:
I even tried using both the number found in eax and the string seen earlier as my keyword but neither of them worked.
The function makes a modified copy of a string from static storage, into a malloced buffer.
This looks weird. The malloc size is dependent on strlen+1, but the memcpy size is a compile-time constant? Your decompilation apparently shows that address was a string literal so it seems that's fine.
Probably that missed optimization happened because of a custom string_length() function that was maybe only defined in another .c (and the bomb was compiled without link-time optimization for cross-file inlining). So size_t len = string_length("some string literal"); is not a compile-time constant and the compiler emitted a call to it instead of being able to use the known constant length of the string.
But probably they used strcpy in the source and the compiler did inline that as a rep movs. Since it's apparently copying from a string literal, the length is a compile-time constant and it can optimize away that part of the work that strcpy normally has to do. Normally if you've already calculated the length it's better to use memcpy instead of making strcpy calculate it again on the fly, but in this case it actually helped the compiler make better code for that part than if they'd passed the return value of string_length to a memcpy, again because string_length couldn't inline and optimize away.
<+0>: push %edi // push value in edi to stack
<+1>: push %esi // push value of esi to stack
<+2>: sub $0x14,%esp // grow stack by 0x14 (move stack ptr -0x14 bytes)
Comments like that are redundant; the instruction itself already says that. This is saving two call-preserved registers so the function can use them internally and restore them later.
Your comment on the sub is better; yes, grow the stack is the higher level semantic meaning here. This function reserves some space for locals (and for function args to be stored with mov instead of pushed).
The rep movsd copies 0x13 * 4 bytes, incrementing ESI and EDI to point past the end of the copied region. So another movsd instruction would copy another 4 bytes contiguous with the previous copy.
The code actually copies another 2, but instead of using movsw, it uses a movzw word load and a mov store. This makes a total of 78 bytes copied.
...
# at this point EAX = malloc return value which I'll call buf
<+28>: mov $0x804a388,%esi # copy src = a string literal in .rodata?
<+33>: mov $0x13,%ecx
<+38>: mov %eax,%edi # copy dst = buf
<+40>: rep movsl %ds:(%esi),%es:(%edi) # memcpy 76 bytes and advance ESI, EDI
<+42>: movzwl (%esi),%edx
<+45>: mov %dx,(%edi) # copy another 2 bytes (not moving ESI or EDI)
# final effect: 78-byte memcpy
On some (but not all) CPUs it would have been efficient to just use rep movsb or rep movsw with appropriate counts, but that's not what the compiler chose in this case. movzx aka AT&T movz is a good way to do narrow loads without partial-register penalties. That's why compilers do it, so they can write a full register even though they're only going to read the low 8 or 16 bits of that reg with a store instruction.
After that copy of a string literal into buf, we have a byte load/store that copies a character with buf. Remember at this point EAX is still pointing at buf, the malloc return value. So it's making a modified copy of the string literal.
<+48>: movzbl 0x11(%eax),%edx
<+52>: mov %dl,0x10(%eax) # buf[16] = buf[17]
Perhaps if the source hadn't defeated constant-propagation, with high enough optimization level the compiler might have just put the final string into .rodata where you could find it, trivializing this bomb phase. :P
Then it stores pointers as stack args for string compare.
<+55>: mov %eax,0x4(%esp) # 2nd arg slot = EAX = buf
<+59>: mov 0x20(%esp),%eax # function arg = user input?
<+63>: mov %eax,(%esp) # first arg slot = our incoming stack arg
<+66>: call 0x80490ca <strings_not_equal>
How to "cheat": looking at the runtime result with GDB
Some bomb labs only let you run the bomb online, on a test server, which would record explosions. You couldn't run it under GDB, only use static disassembly (like objdump -drwC -Mintel). So the test server could record how many failed attempts you had. e.g. like CS 3330 at cs.virginia.edu that I found with google, where full credit requires less than 20 explosions.
Using GDB to examine memory / registers part way through a function makes this vastly easier than only working from static analysis, in fact trivializing this function where the single input is only checked at the very end. e.g. just look at what other arg is being passed to strings_not_equal. (Especially if you use GDB's jump or set $pc = ... commands to skip past the bomb explosion checks.)
Set a breakpoint or single-step to just before the call to strings_not_equal. Use p (char*)$eax to treat EAX as a char* and show you the (0-terminated) C string starting at that address. At that point EAX holds the address of the buffer, as you can see from the store to the stack.
Copy/paste that string result and you're done.
Other phases with multiple numeric inputs typically aren't this easy to cheese with a debugger and do require at least some math, but linked-list phases that requires you to have a sequence of numbers in the right order for list traversal also become trivial if you know how to use a debugger to set registers to make compares succeed as you get to them.
rep movsl copies 32-bit longwords from address %esi to address %edi, incrementing both by 4 each time, a number of times equal to %ecx. Think of it as memcpy(edi, esi, ecx*4).
See https://felixcloutier.com/x86/movs:movsb:movsw:movsd:movsq (it's movsd in Intel notation).
So this is copying 19*4=76 bytes.

Smashing the stack example3 ala Aleph One

I've reproduced Example 3 from Smashing the Stack for Fun and Profit on Linux x86_64. However I'm having trouble understanding what is the correct number of bytes that should be incremented to the return address in order to skip past the instruction:
0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
which is where I think the x = 1 instruction is. I've written the following:
#include <stdio.h>
void fn(int a, int b, int c) {
char buf1[5];
char buf2[10];
int *ret;
ret = buf1 + 24;
(*ret) += 7;
}
int main() {
int x;
x = 0;
fn(1, 2, 3);
x = 1;
printf("%d\n", x);
}
and disassembled it in gdb. I have disabled address randomization and compiled the program with the -fno-stack-protector option.
Question 1
I can see from the disassembler output below that I want to skip past the instruction at address 0x0000000000400595: both the return address from callq <fn> and the address of the movl instruction. Therefore, if the return address is 0x0000000000400595, and the next instruction is 0x000000000040059c, I should add 7 bytes to the return address?
0x0000000000400572 <+0>: push %rbp
0x0000000000400573 <+1>: mov %rsp,%rbp
0x0000000000400576 <+4>: sub $0x10,%rsp
0x000000000040057a <+8>: movl $0x0,-0x4(%rbp)
0x0000000000400581 <+15>: mov $0x3,%edx
0x0000000000400586 <+20>: mov $0x2,%esi
0x000000000040058b <+25>: mov $0x1,%edi
0x0000000000400590 <+30>: callq 0x40052d <fn>
0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)
0x000000000040059c <+42>: mov -0x4(%rbp),%eax
0x000000000040059f <+45>: mov %eax,%esi
0x00000000004005a1 <+47>: mov $0x40064a,%edi
0x00000000004005a6 <+52>: mov $0x0,%eax
0x00000000004005ab <+57>: callq 0x400410 <printf#plt>
0x00000000004005b0 <+62>: leaveq
0x00000000004005b1 <+63>: retq
Question 2
I notice that I can add 5 bytes to the return address in place of 7 and achieve the same result. When I do so, am I not jumping into the middle of the instruction 0x0000000000400595 <+35>: movl $0x1,-0x4(%rbp)? In which case, why does this not crash the program, like when I add 6 bytes to the return address in place of 5 bytes or 7 bytes.
Question 3
Just before buffer1[] on the stack is SFP, and before it, the return address.
That is 4 bytes pass the end of buffer1[]. But remember that buffer1[] is
really 2 word so its 8 bytes long. So the return address is 12 bytes from
the start of buffer1[].
In the example by Aleph 1, he/she calculates the offset of the return address as 12 bytes from the start of buffer1[]. Since I am on x86_64, and not x86_32, I need to recalculate the offset to the return address. When on x86_64, is it the case that buffer1[] is still 2 words, which is 16 bytes; and the SFP and return address are 8 bytes each (as we're on 64 bit) and therefore the return address is at: buf1 + (8 * 2) + 8 which is equivalent to buf1 + 24?
The first, and very important, thing to note: all numbers and offsets are very compiler-dependent. Different compilers, and even the same compiler with different settings, can produce drastically different assemblies. For example, many compilers can (and will) remove buf2 because it's not used. They can also remove x = 0 as its effect is not used and later overwritten. They can also remove x = 1 and replace all occurences of x with a constant 1, etc, etc.
That said, you absolutely need to make numbers for a specific assembly you're getting on your specific compiler and its settings.
Question 1
Since you provided the assembly for main(), I can confirm that you need to add 7 bytes to the return address, which would normally be 0x0000000000400595, to skip over x=1 and go to 0x000000000040059c which loads x into register for later use. 0x000000000040059c - 0x0000000000400595 = 7.
Question 2
Adding just 5 bytes instead of 7 will indeed jump into middle of instruction. However, this 2-byte tail of instruction happen (by pure chance) to be another valid instruction code. This is why it doesn't crash.
Question 3
This is again very compiler and settings dependent. Pretty much everything can happen there. Since you didn't provide disassembly, I can only make guesses. The guess would be the following: buf and buf2 are rounded up to the next stack unit boundary (8 bytes on x64). buf becomes 8 bytes, and buf2 becomes 16 bytes. Frame pointers are not saved to stack on x64, so no "SFP". That's 24 bytes total.

Disassembly of a C function

I was trying to understand disassembled code of the following function.
void func(char *string) {
printf("the string is %s\n",string);
}
The disassembled code is given below.
1) 0x080483e4 <+0>: push %ebp
2) 0x080483e5 <+1>: mov %esp,%ebp
3) 0x080483e7 <+3>: sub $0x18,%esp
4) 0x080483ea <+6>: mov $0x80484f0,%eax
5) 0x080483ef <+11>: mov 0x8(%ebp),%edx
6) 0x080483f2 <+14>: mov %edx,0x4(%esp)
7) 0x080483f6 <+18>: mov %eax,(%esp)
8) 0x080483f9 <+21>: call 0x8048300 <printf#plt>
Could anyone tell me what lines 4-7 means (not the literal explanation). Also why 24 bytes are allocated on stack on line 3?
Basically what happens here:
4) 0x080483ea <+6> : mov $0x80484f0,%eax
Load the address of "the string is %s\n" into eax.
5) 0x080483ef <+11>: mov 0x8(%ebp),%edx
Move the argument string into edx.
6) 0x080483f2 <+14>: mov %edx,0x4(%esp)
Push the value of edx or string into the stack, second argument of printf
7) 0x080483f6 <+18>: mov %eax,(%esp)
Push the value of eax or "the string is %s\n" into the stack, first argument of printf, and then it will call printf.
sub $0x18,%esp is not necessary since the function has no local variables, gcc seems to like making extra space but honestly I don't know why.
The stack is a continuous region of memory that starts at a higher address and ends at esp. Whenever you need your stack to grow, you subtract from esp. Every function can have a frame on the stack. It is the part of the stack that the function owns and is responsible for cleaning after it is done. It means, when the function starts it decreases esp to create its frame. When it ends it increases it back. ebp usually points to the beginning of your frame.
Initially this function pushs ebp to tha stack so that it can be stored when the function ends, sets esp = ebp to mark the begin of its frame and allocate 28 bytes. Why 28? For alignment. It already allocated 4 bytes for the ebp. 4 + 28 = 32.
The lines 4-7 will prepare the call to printf. It expects its arguments to be on the frame of the caller. When we read mov 0x8(%ebp), %edx, we are taking our argument char* string from the caller's frame. printf will do the same.
Note that your assembly is missing a leave and a ret instructions to clear the stack and return to the caller.

Buffer overflow example not working on Debian 2.6

I am trying to make the buffer exploitation example (example3.c from http://insecure.org/stf/smashstack.html) work on Debian Lenny 2.6 version. I know the gcc version and the OS version is different than the one used by Aleph One. I have disabled any stack protection mechanisms using -fno-stack-protector and sysctl -w kernel.randomize_va_space=0 arguments. To account for the differences in my setup and Aleph One's I introduced two parameters : offset1 -> Offset from buffer1 variable to the return address and offset2 -> how many bytes to jump to skip a statement. I tried to figure out these parameters by analyzing assembly code but was not successful. So, I wrote a shell script that basically runs the buffer overflow program with simultaneous values of offset1 and offset2 from (1-60). But much to my surprise I am still not able to break this program. It would be great if someone can guide me for the same. I have attached the code and assembly output for consideration. Sorry for the really long post :)
Thanks.
// Modified example3.c from Aleph One paper - Smashing the stack
void function(int a, int b, int c, int offset1, int offset2) {
char buffer1[5];
char buffer2[10];
int *ret;
ret = (int *)buffer1 + offset1;// how far is return address from buffer ?
(*ret) += offset2; // modify the value of return address
}
int main(int argc, char* argv[]) {
int x;
x = 0;
int offset1 = atoi(argv[1]);
int offset2 = atoi(argv[2]);
function(1,2,3, offset1, offset2);
x = 1; // Goal is to skip this statement using buffer overflow
printf("X : %d\n",x);
return 0;
}
-----------------
// Execute the buffer overflow program with varying offsets
#!/bin/bash
for ((i=1; i<=60; i++))
do
for ((j=1; j<=60; j++))
do
echo "`./test $i $j`"
done
done
-- Assembler output
(gdb) disassemble main
Dump of assembler code for function main:
0x080483c2 <main+0>: lea 0x4(%esp),%ecx
0x080483c6 <main+4>: and $0xfffffff0,%esp
0x080483c9 <main+7>: pushl -0x4(%ecx)
0x080483cc <main+10>: push %ebp
0x080483cd <main+11>: mov %esp,%ebp
0x080483cf <main+13>: push %ecx
0x080483d0 <main+14>: sub $0x24,%esp
0x080483d3 <main+17>: movl $0x0,-0x8(%ebp)
0x080483da <main+24>: movl $0x3,0x8(%esp)
0x080483e2 <main+32>: movl $0x2,0x4(%esp)
0x080483ea <main+40>: movl $0x1,(%esp)
0x080483f1 <main+47>: call 0x80483a4 <function>
0x080483f6 <main+52>: movl $0x1,-0x8(%ebp)
0x080483fd <main+59>: mov -0x8(%ebp),%eax
0x08048400 <main+62>: mov %eax,0x4(%esp)
0x08048404 <main+66>: movl $0x80484e0,(%esp)
0x0804840b <main+73>: call 0x80482d8 <printf#plt>
0x08048410 <main+78>: mov $0x0,%eax
0x08048415 <main+83>: add $0x24,%esp
0x08048418 <main+86>: pop %ecx
0x08048419 <main+87>: pop %ebp
0x0804841a <main+88>: lea -0x4(%ecx),%esp
0x0804841d <main+91>: ret
End of assembler dump.
(gdb) disassemble function
Dump of assembler code for function function:
0x080483a4 <function+0>: push %ebp
0x080483a5 <function+1>: mov %esp,%ebp
0x080483a7 <function+3>: sub $0x20,%esp
0x080483aa <function+6>: lea -0x9(%ebp),%eax
0x080483ad <function+9>: add $0x30,%eax
0x080483b0 <function+12>: mov %eax,-0x4(%ebp)
0x080483b3 <function+15>: mov -0x4(%ebp),%eax
0x080483b6 <function+18>: mov (%eax),%eax
0x080483b8 <function+20>: lea 0x7(%eax),%edx
0x080483bb <function+23>: mov -0x4(%ebp),%eax
0x080483be <function+26>: mov %edx,(%eax)
0x080483c0 <function+28>: leave
0x080483c1 <function+29>: ret
End of assembler dump.
The disassembly for function you provided seems to use hardcoded values of offset1 and offset2, contrary to your C code.
The address for ret should be calculated using byte/char offsets: ret = (int *)(buffer1 + offset1), otherwise you'll get hit by pointer math (especially in this case, when your buffer1 is not at a nice aligned offset from the return address).
offset1 should be equal to 0x9 + 0x4 (the offset used in lea + 4 bytes for the push %ebp). However, this can change unpredictably each time you compile - the stack layout might be different, the compiler might create some additional stack alignment, etc.
offset2 should be equal to 7 (the length of the instruction you're trying to skip).
Note that you're getting a little lucky here - the function uses the cdecl calling convention, which means the caller is responsible for removing arguments off the stack after returning from the function, which normally looks like this:
push arg3
push arg2
push arg1
call func
add esp, 0Ch ; remove as many bytes as were used by the pushed arguments
Your compiler chose to combine this correction with the one after printf, but it could also decide to do this after your function call. In this case the add esp, <number> instruction would be present between your return address and the instruction you want to skip - you can probably imagine that this would not end well.

My overflow code does not work

The code below is from the well-known article Smashing The Stack For Fun And Profit.
void function(int a, int b, int c) {
char buffer1[5];
char buffer2[10];
int *ret;
ret = buffer1 + 12;
(*ret)+=8;
}
void main() {
int x;
x=0;
function(1,2,3);
x=1;
printf("%d\n",x);
}
I think I must explain my target of this code.
The stack model is below. The number below the word is the number of bytes of the variable in the stack. So, if I want to rewrite RET to skip the statement I want, I calculate the offset from buffer1 to RET is 8+4=12. Since the architecture is x86 Linux.
buffer2 buffer1 BSP RET a b c
(12) (8) (4) (4) (4) (4) (4)
I want to skip the statement x=1; and let printf() output 0 on the screen.
I compile the code with:
gcc stack2.c -g
and run it in gdb:
gdb ./a.out
gdb gives me the result like this:
Program received signal SIGSEGV, Segmentation fault.
main () at stack2.c:17
17 x = 1;
I think Linux uses some mechanism to protect against stack overflow. Maybe Linux stores the RET address in another place and compares the RET address in the stack before functions return.
And what is the detail about the mechanism? How should I rewrite the code to make the program output 0?
OK,the disassemble code is below.It comes form the output of gdb since I think is more easy to read for you.And anybody can tell me how to paste a long code sequence?Copy and paste one by one makes me too tired...
Dump of assembler code for function main:
0x08048402 <+0>: push %ebp
0x08048403 <+1>: mov %esp,%ebp
0x08048405 <+3>: sub $0x10,%esp
0x08048408 <+6>: movl $0x0,-0x4(%ebp)
0x0804840f <+13>: movl $0x3,0x8(%esp)
0x08048417 <+21>: movl $0x2,0x4(%esp)
0x0804841f <+29>: movl $0x1,(%esp)
0x08048426 <+36>: call 0x80483e4 <function>
0x0804842b <+41>: movl $0x1,-0x4(%ebp)
0x08048432 <+48>: mov $0x8048520,%eax
0x08048437 <+53>: mov -0x4(%ebp),%edx
0x0804843a <+56>: mov %edx,0x4(%esp)
0x0804843e <+60>: mov %eax,(%esp)
0x08048441 <+63>: call 0x804831c <printf#plt>
0x08048446 <+68>: mov $0x0,%eax
0x0804844b <+73>: leave
0x0804844c <+74>: ret
Dump of assembler code for function function:
0x080483e4 <+0>: push %ebp
0x080483e5 <+1>: mov %esp,%ebp
0x080483e7 <+3>: sub $0x14,%esp
0x080483ea <+6>: lea -0x9(%ebp),%eax
0x080483ed <+9>: add $0x3,%eax
0x080483f0 <+12>: mov %eax,-0x4(%ebp)
0x080483f3 <+15>: mov -0x4(%ebp),%eax
0x080483f6 <+18>: mov (%eax),%eax
0x080483f8 <+20>: lea 0x8(%eax),%edx
0x080483fb <+23>: mov -0x4(%ebp),%eax
0x080483fe <+26>: mov %edx,(%eax)
0x08048400 <+28>: leave
0x08048401 <+29>: ret
I check the assemble code and find some mistake about my program,and I have rewrite (*ret)+=8 to (*ret)+=7,since 0x08048432 <+48>minus0x0804842b <+41> is 7.
Because that article is from 1996 and the assumptions are incorrect.
Refer to "Smashing The Modern Stack For Fun And Profit"
http://www.ethicalhacker.net/content/view/122/24/
From the above link:
However, the GNU C Compiler (gcc) has evolved since 1998, and as a result, many people are left wondering why they can't get the examples to work for them, or if they do get the code to work, why they had to make the changes that they did.
The function function overwrites some place of the stack outside of its own, which is this case is the stack of main. What it overwrites I don't know, but it causes the segmentation fault you see. It might be some protection employed by the operating system, but it might as well be the generated code just does something wrong when wrong value is at that position on the stack.
This is a really good example of what may happen when you write outside of your allocated memory. It might crash directly, it might crash somewhere completely different, or if might not crash at all but instead just do some calculation wrong.
Try ret = buffer1 + 3;
Explanation: ret is an integer pointer; incrementing it by 1 adds 4 bytes to the address on 32bit machines.

Resources