Post

Reverse Engineering for Beginners : GOTO operator

Reverse Engineering for Beginners : GOTO operator

1.17 GOTO operator

GOTOoperator

Let's first understand what GOTO is in C quickly

It simply jumps to a specific place in the code without any condition or comparison or anything

The author started saying that theGOTO operator is often considered an anti-pattern, and the meaning of anti-pattern is a famous or widespread solution

But in reality, it's a bad solution and causes more problems than it solves

This is a simple example:

C

#include <stdio.h> // include the standard I/O header; this directive includes the header file for standard input/output functions

int main() // program entry point; this is the main function where program execution begins
{
    printf("begin\\n"); // print "begin" followed by a newline; this calls printf to output the string
    goto exit; // jump to the label 'exit'; this performs an unconditional jump to the specified label
    printf("skip me!\\n"); // print "skip me!" followed by a newline; this call is skipped due to the goto
exit: // label for the jump target; this is the point where execution jumps to
    printf("end\\n"); // print "end" followed by a newline; this calls printf to output the string
};

  

And this is what came out with MSVC 2012:

Listing 1.107: MSVC 2012

Assembly

$SG2934 DB 'begin', 0aH, 00H ; define string "begin\n" in data segment; this allocates a null-terminated string
$SG2936 DB 'skip me!', 0aH, 00H ; define string "skip me!\n" in data segment; this allocates a null-terminated string
$SG2937 DB 'end', 0aH, 00H ; define string "end\n" in data segment; this allocates a null-terminated string
_main PROC ; start of main procedure
    push ebp ; push EBP onto stack; save base pointer
    mov ebp, esp ; move ESP to EBP; set up base pointer
    push OFFSET $SG2934 ; 'begin' ; push address of "begin\n" onto stack
    call _printf ; call printf function
    add esp, 4 ; add 4 to ESP; clean up stack after call
    jmp SHORT $exit$3 ; jump to label $exit$3; unconditional jump
    push OFFSET $SG2936 ; 'skip me!' ; push address of "skip me!\n" (skipped code)
    call _printf ; call printf (skipped)
    add esp, 4 ; clean up stack (skipped)
$exit$3: ; label for jump target
    push OFFSET $SG2937 ; 'end' ; push address of "end\n"
    call _printf ; call printf
    add esp, 4 ; clean up stack
    xor eax, eax ; XOR EAX with itself; set EAX to 0 (return value)
    pop ebp ; pop EBP; restore base pointer
    ret 0 ; return from function with 0
_main ENDP ; end of main procedure

  

The goto instruction was simply transformed into a JMP instruction, which has the same effect:

An unconditional jump to another place.

The second printf() cannot be executed except by human intervention, either using a debugger or by patching the code

And this can be a simple and useful exercise in patching.

We open the resulting executable in x32dbg (he did it on Hiew but I will do it on x32dbg)

First, we compile the C code with this command:

Assembly

cl /Od /Zi teest.c ; compile teest.c with optimization disabled (/Od) and debug info (/Zi); this command compiles the C file into an executable with debugging symbols

  

And run the exe file on x32dbg and go to main and set Breakpoint at the beginning of Main

4_1

The instruction that jmp 0x001071FF which is this

4_2

This is supposed to go to the function that goes to Exit

Now we will try to cancel the jmp operation so that it prints what's after it without going to Exit

And we will do this in two ways:

  1. We change the Assembly from jmp 0x001071FF to NOP by selecting it and pressing Space and changing it to Nop like this
4_3

And it will run the program normally and output like this

4_4
  1. We change the Hex value and we do as follows:

We select the jmp instruction then Right Click then Follow in dump then Selected Address

4_5

Then we see the value in Hex it will be like this

4_6

We change the value 07 to 00 which is the same as nop exactly

4_7

And we start doing F8 until ret

4_8

1.17.1 Dead code

Deadcode

The second printf() call is also called “dead code” in compiler terms.

And this means that this code will never be executed.

That's why, when you compile this example with optimizations enabled,

The compiler removes the dead code, and leaves no trace of it:

Listing 1.109: Optimizing MSVC 2012

Assembly

$SG2981 DB 'begin', 0aH, 00H ; define "begin\n"; string constant
$SG2983 DB 'skip me!', 0aH, 00H ; define "skip me!\n"; unused string constant
$SG2984 DB 'end', 0aH, 00H ; define "end\n"; string constant
_main PROC ; start of main
    push OFFSET $SG2981 ; 'begin' ; push "begin\n"
    call _printf ; call printf
    push OFFSET $SG2984 ; 'end' ; push "end\n"
$exit$4: ; label (no jump here since optimized)
    call _printf ; call printf
    add esp, 8 ; clean up 8 bytes (two pushes)
    xor eax, eax ; set EAX to 0
    ret 0 ; return 0
_main ENDP ; end of main

  

But, the compiler forgot to remove the string "skip me!"

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

Trending Tags