In this module we are going to build off our hello reversing program. We are going to move our print operation out of the main function into a second function. Then we are going to try some different options for arguments and information.
Lets take a look at some binaries. This first example is from using the mod5gcc32 binary from the Github repo.
Here we have gdb set up with a break point at the main function. Lets go through the assembly instructions that we see here. The first is extending the memory in the stack by 4 bytes. Then we call a function called func. After func is called we move 0 into eax and reset esp to its previous location. Then we return to the previous calling function that started our program.
Lets take a look at the func() function:
This is the break point at the func function. First thing that should jump out is we are calling puts just like we did last assembly module. Recall that puts takes a string pointer argument and it gets pushed onto the stack right before we call puts.
Lets do a little analysis here. There are no arguments pushed onto the stack before we call func() in main. Since in x86 architecture passes the arguments on the stack we have that there are no arguments for func(). But puts() takes an argument, that means the argument must be created in func() itself. But this set up is a little different than the previous execution of puts().
mov DWORD PTR [ebp – 0xc], 0x80484d0: This instruction is moving the address 0x80484d0 into the memory location ebp-0xc. That is 12 bytes below the ebp register address. Lets find out exactly where that address is.
Look at the register display above the code section. We see EBP: 0xFFFFDA48 which is the value in EBP. 0xc = 12 in decimal so we are subtracting 12 from that value. If we remove 8 we get 0xFFFFDA40 and we can follow the standard rule for subtraction, if we subtract one more we get 0xFFFFDA3f. So now we just need to subtract another three to have 0xFFFFDA3C. That’s how I end up thinking about it to do it in my head. The really easy way is to just use the command
print 0xffffda48 - 0xc and gdb will print the result out for you.
We can use the `stepi` instruction to step exactly one instruction and the mov will have completed. Lets take a look at the memory address and see what’s there:
There’s our address right where we expected to find it. Before we go any further ask yourself what do you expect to find at the address we just moved into place? Lets take a look at the memory location and see what we find:
If you read through the last module those top 8 bytes should be familiar to you. That’s our “Hello REversing!” string. If we go back and look at our func() disassembly we see that we extend the stack by 12-bytes then push this value on the stack right before calling puts(). This is our argument for puts().
We see the functionality but we aren’t necessarily seeing the full picture yet. Lets take a look at the whole func() disassembly. We can take a look at it by using the command
disass func in gdb:
Now we see the full disassembly for func(). Starting with the frame prologue and ending with the frame epilogue.
Exercise: We know the value of ebp in func() go through all of the assembly manipulations of esp and determine the size of the stack after each movement of esp. Pay close attention to if we are increasing or decreasing the size of the stack. Then compare each movement of esp with the size of the pieces of data placed on the stack.
Now lets take a look at a second version of this same functionality. By functionality I mean the program has the equivalent result but accomplishes it in a slightly different way.
Same set up as last time. Break on main and break on func(). This time main looks different though. You should recognize the same set up we saw in func() last program. We have our address for the string stored in the memory at ebp – 0xc. Then we are passing that value to func() at <main + 30>. What this tells us is that our message is allocated in the main function and sent to func() as an argument.
Lets see what our func() function looks like this time around:
That’s a little different, we are still referencing an offset from ebp but it’s a positive offset now. We know from the definition of puts() that this is our string argument. If you want to verify just go to the memory and repeat what we did previously to examine the contents of the address. Why do you think we are looking at a different place now?
It turns out that we have placed the value 0x80484e0 on the stack twice. The first time we placed it in memory at ebp – 0xc the second time we placed it on the stack with a push operation which we will have to calculate the address of. To do that calculation we need to see the whole stack frame construction of main().
When we start the stack frame ebp = 0xFFFFDA58 and so does esp. Then we push ecx onto the stack below ebp which has the effect of moving esp 4 bytes. At this stage esp = 0xFFFFDA54. Now we subtract 0x14 and end up at 0xFFFFDA40. Then we subtract another 0xc bytes and land at esp = 0xFFFFDA34. We then push the address on the stack which subtracts 0x4 from the address held in esp leaving us at esp = 0xFFFFDA30.
Now we go back to looking at the func() address. In func() ebp = 0xFFFFDA28. Our address reference for the string is ebp + 0x8 = 0xFFFFDA28 + 0x8 = 0xFFFFDA30. We are actually accessing the same memory address we have just changed the reference point by moving to a different stack frame.
Now we know what it looks like when we pass arguments between functions in x86 architecture. In this beginning portion we need to be extra careful and spend the time to really figure out what’s happening. Take a look at the 64-bit version and compare how things happen. Next time we will take a look at prompting the user for input and using that input in our program.