Post

Reverse Engineering for Beginners (CH1.10 Almost empty function)

Almost Empty Function


The author began by saying that this is a real code example he found in Boolector.

C

// Forward declaration of the function. This function exists in another file:
int boolector_main (int argc, char **argv);

// The executable program
int main (int argc, char **argv)
{
    return boolector_main (argc, argv);
}

  

Here, the author talked about a very simple function, almost empty, called main(). All it really does is call another function named boolector_main and passes it the same parameters.

In other words, instead of writing all the code inside main, he made main just an intermediary that calls boolector_main and returns its result.

The author mentioned that the reason could be that the actual function (boolector_main) is stored in another file or library (like a DLL or a shared library).

This is useful, for example, when the main program is being tested by a test suite, and that test suite needs to call main just like the system normally does.

Then he explained that when this code is compiled before optimization, it looks like this:

Assembly

main:
    push  rbp                  ; save base pointer of previous stack frame
    mov   rbp, rsp             ; set base pointer for current stack frame
    sub   rsp, 16              ; allocate 16 bytes on the stack for local variables
    mov   DWORD PTR -4[rbp], edi  ; move first argument (argc) to local variable
    mov   QWORD PTR -16[rbp], rsi ; move second argument (argv) to local variable
    mov   rdx, QWORD PTR -16[rbp] ; prepare rsi argument for call
    mov   eax, DWORD PTR -4[rbp]  ; prepare edi argument for call
    mov   rsi, rdx             ; move argv to rsi register
    mov   edi, eax             ; move argc to edi register
    call  boolector_main       ; call the actual main function
    leave                       ; restore previous stack frame
    ret                         ; return to caller

  

But let’s take a look at the optimized version:

Listing 1.46: Optimizing GCC 8.2 x64 (Assembly Output with Comments)

Assembly

main:
    jmp boolector_main    ; jump directly to the address of boolector_main, skipping prologue/epilogue

  

Very simply, the stack and registers are untouched, and boolector_main() receives the same arguments. So all we need to do is just pass the execution flow to another address.

This is quite similar to the concept of a thunk function — a function that only performs redirection or forwarding.

This post is licensed under CC BY 4.0 by the author.

Trending Tags