1

After reading a number of blog posts, forums, and watching tutorials I figured I would start learning to reverse software the old fashion way. Creating simple C files and looking at their disassembly. In my quest to truly understand reversing, I thought also comparing optimized and unoptimized code would be beneficial. While looking through I came across a couple lines of code that appear to do nothing.

I would love if someone can explain what the "mov"[es] in the unoptimized code is doing. All of these were disassembled using Hopper v4.

C Code:

 #include <stdio.h>

 int main(int arg, char** arg) {
    printf("Hello World!\n");
    return 0;
 }

Unoptimized Code (gcc -m32) :

; Variables:
        ;    arg_4: 12
        ;    arg_0: 8
        ;    var_4: -4
        ;    var_8: -8
        ;    var_C: -12
        ;    var_10: -16
        ;    var_18: -24
push       ebp
mov        ebp, esp
sub        esp, 0x18
call       _main+11
pop        eax                            ; CODE XREF=_main+6

-- What purpose do these moves serve? --
mov        ecx, dword [ebp+arg_4]
mov        edx, dword [ebp+arg_0]
--                                    --
lea        eax, dword [eax-0x1f5b+0x1fa6] ; "Hello World!\\n"
-- And what do these moves also serve? --
mov        dword [ebp+var_4], 0x0
mov        dword [ebp+var_8], edx
mov        dword [ebp+var_C], ecx
--                                    --
mov        dword [esp+0x18+var_18], eax  ; method imp___symbol_stub__printf
call       imp___symbol_stub__printf
xor        ecx, ecx                           
mov        dword [ebp+var_10], eax
mov        eax, ecx                                    
add        esp, 0x18                           
pop        ebp
ret

Optimized Code (gcc -m32 -O3):

push       ebp
mov        ebp, esp
sub        esp, 0x8
call       _main+11
pop        eax                             ; CODE XREF=_main+6
lea        eax, dword [eax-0x1f6b+0x1f9e]  ; "Hello World!"
mov        dword [esp+0x8+var_8], eax      ; "%s" for imp___symbol_stub__puts
call       imp___symbol_stub__puts
xor        eax, eax
add        esp, 0x8
pop        eep
ret
3
  • most often the seemungly junk instructions correspond to src line information. and unused argumentd main tajes two args and you are not using them but you askex that the code be as is so comipler lets you to be the boss and provides you the args as is in unoptimsef builds on optimising it simply discards those unused args and comes straight to the point
    – blabb
    Aug 11, 2017 at 3:01
  • int main(int arg, char** arg) should be int main(int argc, char **argv)
    – julian
    Aug 11, 2017 at 22:15
  • Thanks. The "arg" parameters were simple typos as I was unable to copy and paste from the terminal to the post's text box. But good to clear it up for anyone who may peruse this post. Aug 13, 2017 at 3:26

1 Answer 1

2

First of all, I would advise you to read about the SystemV ABI for i386 and amd64. You can find the documents here:

These documents define as precisely as possible how a compiler coder should translate some C/C++ code into i386/amd64 assembly code for a Unix-like system.

They are extremely important documents and you should refer to it as often as possible because they contain a lot of answers for most of your questions.

Now, back to your original question, in your case the main differences between the two codes is that gcc has optimized data movements in the memory as we will see.

First code snippet

-- What purpose do these moves serve? --
mov        ecx, dword [ebp+arg_4]
mov        edx, dword [ebp+arg_0]
--                                    --

Here, ecx and edx are loaded with the arguments of main (very likely argc and argv).

Note that, none of argc and argv are of any use in the main() function. But, the compiler does not know about it because it did not performed dead-code/dead-variables analysis at this level of optimization. Of course, this code will be removed when the appropriate analysis will be performed.

Second code snippet

-- And what do these moves also serve? --
mov        dword [ebp+var_4], 0x0
mov        dword [ebp+var_8], edx
mov        dword [ebp+var_C], ecx
--                                    --

Here, the program seems to store the arguments in the local memory frame (below ebp). Note that the arguments are above ebp and the automatic variables below (we say automatic variable for the variable which are within the function's scope).

Of course, these data movements are totally unnecessary, but the compiler just apply a default template for starting a function which transfer a copy of the arguments in the local memory stack-frame. And, once again, when the compiler realize that these variables are of no use, then these moves will disappear.

1
  • Thank you for the reference material as well as the good explanation. I will definitely give it a look and have it be my first point of reference. Aug 13, 2017 at 3:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.