In this challenge I will be taking a look at the ASM instructions from a 64-bit ELF executable (if/then) using both Intel and AT&T syntax. After figuring out what each instruction does to the best of my ability, I will write a C program to reflect what I think is happening in the ASM instructions. These challenges are purely for practice, and will help you understand the basics of reverse engineering, as well as C and ASM.
Below you will see the ASM instructions for the C program (if/then), and the challenge is to write the C program after looking at the ASM instructions below. The left side is Intel Syntax, where as the right side is AT&T syntax.
Note: I recommend learning both Intel and AT&T syntax as seen below
Note: Make note of the difference between the Intel and AT&T syntax above. With this smaller program you can start to see the difference between the two (i.e. mov vs movl, etc...)
These are some of the notes I took while checking the code above, starting with line 14 (main).
In the above instructions, I can see that 2 values are being set within main (10, 20) to fixed values. Each value looks to be 4 bytes in size and since I can see the value being set, I know these 2 values are both integers and not pointers.
You can also see in the above instructions, that the call to printf is using a PLT (Procedure Linkage Table) which is denoted by the @PLT suffix after the function name being used. The call to printf is setup like the following in 64-bit programming ABI (as seen in lines 31-34 and lines 40-43.)
The LC0 and LC1 labels are listed before main as seen in the ASM instructions at the top of the post, and their values are listed below for reference.
Depending on the arguments provided to printf, the ASM instructions may utilize more registers for integer/pointers, or for float/double values passed as arguments.
After parsing each instruction from main in the above code, I wrote the following C program to re-construct the ASM instructions.
Next is to compile the code to see if it works, and then run objdump on the binary to see how close our disassembly is to the instructions we started with.
Awesome! It worked out without error, so lets double check our accuracy with objdump as seen below.
As you can see, the instructions are not identical to what we started with, but you can see that they are DAMN close… I will call this a success, and I hope you learned a lot as well.