Lets review a few things we’ve seen so far in assembly. We have seen creating a stack frame, calling a function and passing arguments to that function, taking user data and validating the input.
In this module we are going to take a look at a simple text based menu. It’s not going to be pretty but it doesn’t need to be, we just want to see what it looks like in the code. We are going to take a break from gdb and see what Hopper shows us. Hopper is not free but you can get a demo version with some limitations here.
In this module I am just going to show the interesting bits of assembly. This is because the output is larger than is useful. So you have an exercise already
Exercise: Grab the binary from the github repo and load it into hopper or whichever disassembler you are using. Start breaking down the program. The first section should look familiar to you and you should be able to write down what’s going on.
This is the start of the main procedure/function in Hopper. The first thing we can see is that there is a list of offsets defined as variables right after the beginning of the procedure. The variables are used in the output rather than the offsets themselves.
Lets jump to something that isn’t just setting up some print statements and reading user input.
We have a control flow construct here. We are comparing values and then jumping to different areas of the code based on the information we gave the program. Following along in your own disassembly you see this snippet is directly after we are prompted for user input.
Exercise: Pull up your copy of Reverse Engineering for Beginners and see if you can identify what control flow structure this snippet corresponds to.
Lets see where that first test instruction takes us:
Looks like we jump to the end of the main function. It returns an error message if we take the jump. Looking at the next compare block we see the same target. These two snippets are checking if we input a number between 1 and 4 inclusive.
- jle – Jump less equal. This instruction invokes the jump if we have a negative number or zero.
- jg – jump if greater. This instruction jumps if the value is greater than four.
One cool feature of Hopper is that we can look at the decompiler output.
Right at the top of the snippet is our input check. We have that the program is validating our input. If we are in bounds we move the value of input to eax and start checking what the value is.
If we jump back up to the disassembly we see that the first check is comparing to 2. That matches our decompiler here as well. Since we know we are looking at a text based menu we can infer that the program is using compare statements to choose which branch of the program to take given the menu choice.
We can see there is a call to a function add that is taking stack locations as arguments. But before we dig too deep into the function calls you should be seeing something that makes you stop and hit up a reference. There are a bunch of instructions beginning with f. Lets take a closer look at that portion of the assembly and dissect it.
Lets go through each of these instructions:
- fld – Load floating point valuepushes the operand onto the FPU register stack.
- fxch – Exchange register contents.
- fstp – Store floating point value.
This tells us that we are dealing with floating point numbers. Floating point numbers are dealt with differently than integers. Specifically there is a special stack construction to deal with them called the FPU stack. There are 8 registers for the FPU stack, s0 through s7. You can read more about the FPU stack here. We are looking at moving around floating point data to call a function.
Now that we know what this is lets take a look at the add() function:
As we can guess the purpose of this function is to add two numbers together. There is a new instruction here:
- fadd – floating point addition.
In our case we have only one operand so we are adding the operand to the s0 register and storing the result in the s0 register. Note that we aren’t doing anything with eax after the addition. Which should lead you to think we aren’t returning this functions value in the eax register. It is in fact stored in a floating point register to be returned.
Exercise: Write a simple function to add two integers that matches the source code for module 8 and compare the disassembly to this disassembly.
Word of Warning:
A word of warning about decompilers is that they aren’t infallible. If you read the documentation for floating point values you will find that it says the floating point value is returned from a function on the FPU. But if we go to Hopper’s decompiler window for add() we see that it says the return value is in eax. Which one is right? I’m inclined to go with the documentation for x86 architecture over what Hopper is telling me.
Exercise: Compare this decompiled function with the function in the source code and figure out where the differences originate from.
In this module we have seen a simple text menu that calls functions based on your choice. We have also had our first exposure to floating point values and how they are dealt with in x86 architecture.
Moving forward we are going to be working with programs of increasing complexity. This means that the bulk of the reverse engineering is going to be transferred to your responsibility. I will be focusing on what’s new and interesting in the assembly with a view that the rest can be done by you with a little work. Next module we will jump the complexity up a notch and it may take more than one module to get through.