The first bit of code we look at is as follows(its called ifelse.c on that site):
gcc -o ifelse -ggdb -O0 -fno-builtin-printf ifelse.c
Lets step through the assembly. First the prolog and space allocation:
0x08048384
0x08048388
0x0804838b
0x0804838e
0x0804838f
0x08048391
0x08048392
Then the (a>0) bit:
0x08048395
Then the bit which has the if-else logic. The instruction jns stands for (Jump if no sign), which effectively means 'positive'. So if the value of a is positive you jump straight to 0x080483a9, else you go through the instructions from 0x08048399 to 0x080483a7.
0x08048399
0x0804839b
0x080483a2
0x080483a7
0x080483a9
That leaves just the final printf. The if-else loop terminates just 1 instruction before that. Which means...
0x080483b5
0x080483bc
0x080483c1
0x080483c4
0x080483c5
0x080483c6
0x080483c9
..all of this are the bits after the if-else loop. The program prints the 'Leaving main' bit and exits. Simple eh?
-------------------------------------------------------------------------------------------------------------------------------------
Now for an example on while. We use this code.
We compile this as usual - gcc -o while -O0 -fno-builtin-printf -ggdb while.c and then open it up with gdb.
You see the usual prolog which I won't go into. You then see a line allocating space on the stack as follows.
0x08048392
At this point I'd just like to make one small point. These numbers (0x24 in this case)...you will see that there's just one local variable(int i) in the code. So why should 0x24 be allocated? Well..the only explanation is because of the "%d\n" in the printf statement below. The moment you comment that out and recompile, the line changes to 0x10..meaning I allocate only for int i. Something like this:
0x08048362
Although I'm still not sure why its 0x10. It should be lesser, logically speaking. But maybe that's how gcc deals with stuff. More clarity in later blogs maybe..once I'm clearer ;). Moving on then..
The next line is for the i=0 bit.
0x08048395
Now the part for the while starts. Note a jmp straight away? You thought there should be a cmpl instruction..didn't you? Me too. Lets see what is happening. Here's all the relevant assembly lines:
0x0804839c
0x0804839e
0x080483a1
0x080483a5
0x080483ac
0x080483b1
0x080483b5
0x080483b9
0x080483bb
There's a jump to
Now lets look at a for loop. We use this code.
Compile as usual using - gcc -o for -O0 -fno-builtin-printf -ggdb for.c and open it up in gdb. If you remember the code for the while, you'll notice that the assembly for this program is exactly similar to that of the while loop! That's because the for and the while loops are just two different ways of doing the same things. I'm not explaining anything here because all that I said during the while loop is true here as well. Lets look at a do-while now.
We use the following code for a do-while
Now lets have a little bit of fun. Try and think of what a do-while does. How is it different from a while? It guarantees at least one pass of the loop; because the comparison happens after the first run through. So that means, unlike the while loop example where we jumped to a cmpl 0x9 immediately after initializing i to zero, we will call printf and increment i atleast once before comparing. Makes sense? Lets look at an assembly snippet and confirm our thoughts. Here it is:
0x08048395
0x0804839c
0x0804839f
0x080483a3
0x080483aa
0x080483af
0x080483b3
0x080483b7
Bulls eye! There is a printf at 0x080483aa and a cmpl at 0x080483b3 . Give yourself a pat if you guessed that right :)
Before moving on though, a quick note on the gcc arguments. The -O0 stands for .. "Don't use any gcc optimizations". That didn't impact this program as such, but I'll use it going forward; just so gcc does not cause funny problems. The -f stands for "Don't use any arguments when you see printf in my code". Without this, if you run gcc without the -f, the printf gets converted into a "puts" function call, which caused me a lot of pain. A nice blog over here speaks about similar problems.
The immediate next though obviously is - What other gcc optimizations are there? How many of them are relevant? Well, here's a list. I really don't know which ones are relevant..at this point. I'm learning as much as you, as we go along. As and when I find something relevant, I'll introduce it.
Thought about looking through more sample programs on all the topics over at our parent site. However I then thought that we now know the basics and will be able to step through newer and more complex data structures as and when they come along. We just won't take each and every data structure right now. You can have a look at Chapter 7; it talks about GUI Debuggers and its advantages. I've used the free version of IDA a little..but since I hadn't any clue about any basics - I couldn't understand how to use it well. We'll use the GUI debuggers when we have a genuine need for them; i.e when we look at more complex programs.
The rest of the content online sadly is kinda incomplete so I won't be referring to that any more and will start taking up small vulnerable C programs to understand things better.
Until next time...So long!