• Aucun résultat trouvé

MSVC + OllyDbg

Dans le document Reverse Engineering for Beginners (Page 132-141)

printf() with several arguments

GCC + GDB

7.1 Simple example

7.1.3 MSVC + OllyDbg

Let’s try this example in OllyDbg. Let’s load it and keep pressing F8 (step over) until we reach our executable file instead ofntdll.dll. Scroll up untilmain() appears. Click on the first instruction (PUSH EBP), press F2 (set a breakpoint), then F9 (Run). The breakpoint will be triggered whenmain()begins.

Let’s trace to the point where the address of the variablexis calculated:

Figure 7.1: OllyDbg: The address of the local variable is calculated Right-click theEAXin the registers window and then select “Follow in stack”. This address will appear in the stack window. The red arrow has been added, pointing to the variable in the local stack. At that moment this location contains some garbage (0x6E494714). Now with the help of PUSHinstruction the address of this stack element is going to be stored to the same stack on the next position. Let’s trace with F8 until thescanf()execution completes. During thescanf()execution, we input, for example, 123, in the console window:

Figure 7.2: User input in the console window

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

scanf()completed its execution already:

Figure 7.3: OllyDbg:scanf()executed

scanf()returns 1 inEAX, which implies that it has read successfully one value.

If we look again at the stack element corresponding to the local variable it now contains0x7B(123).

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

Later this value is copied from the stack to theECXregister and passed toprintf():

Figure 7.4: OllyDbg: preparing the value for passing toprintf()

GCC

Let’s try to compile this code in GCC 4.4.1 under Linux:

main proc near

var_20 = dword ptr -20h var_1C = dword ptr -1Ch

var_4 = dword ptr -4

push ebp mov ebp, esp

and esp, 0FFFFFFF0h sub esp, 20h

mov [esp+20h+var_20], offset aEnterX ; "⤦

Ç Enter X:"

call _puts

mov eax, offset aD ; "%d"

lea edx, [esp+20h+var_4]

mov [esp+20h+var_1C], edx mov [esp+20h+var_20], eax call ___isoc99_scanf mov edx, [esp+20h+var_4]

mov eax, offset aYouEnteredD___ ; "You ⤦ Ç entered %d...\n"

mov [esp+20h+var_1C], edx mov [esp+20h+var_20], eax call _printf

mov eax, 0

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

leave retn

main endp

GCC replaced theprintf()call with call toputs(). The reason for this was explained in (3.4.3 on page 22).

As in the MSVC example—the arguments are placed on the stack using theMOV instruction.

By the way

By the way, this simple example is a demonstration of the fact that compiler trans-lates list of expressions in C/C++-block into sequential list of instructions. There are nothing between expressions in C/C++, and so in resulting machine code, there are nothing between, control flow slips from one expression to the next one.

7.1.4 x64

The picture here is similar with the difference that the registers, rather than the stack, are used for arguments passing.

MSVC

Listing 7.1: MSVC 2012 x64 _DATA SEGMENT

$SG1289 DB 'Enter X:', 0aH, 00H

$SG1291 DB '%d', 00H

$SG1292 DB 'You entered %d...', 0aH, 00H _DATA ENDS

lea rcx, OFFSET FLAT:$SG1289 ; 'Enter X:' call printf

lea rdx, QWORD PTR x$[rsp]

lea rcx, OFFSET FLAT:$SG1291 ; '%d' call scanf

mov edx, DWORD PTR x$[rsp]

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

lea rcx, OFFSET FLAT:$SG1292 ; 'You entered %d...' call printf

Listing 7.2: Optimizing GCC 4.4.6 x64 .LC0:

mov edi, OFFSET FLAT:.LC0 ; "Enter X:"

call puts

lea rsi, [rsp+12]

mov edi, OFFSET FLAT:.LC1 ; "%d"

xor eax, eax call __isoc99_scanf

mov esi, DWORD PTR [rsp+12]

mov edi, OFFSET FLAT:.LC2 ; "You entered %d...\n"

xor eax, eax

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

.text:00000044 A9 A0 ADR R0, aEnterX ⤦

Ç ; "Enter X:\n"

.text:00000046 06 F0 D3 F8 BL __2printf

.text:0000004A 69 46 MOV R1, SP

.text:0000004C AA A0 ADR R0, aD ⤦

Ç ; "%d"

.text:0000004E 06 F0 CD F8 BL __0scanf

.text:00000052 00 99 LDR R1, [SP,#8+⤦

Ç var_8]

.text:00000054 A9 A0 ADR R0, ⤦

Ç aYouEnteredD___ ; "You entered %d...\n"

.text:00000056 06 F0 CB F8 BL __2printf

.text:0000005A 00 20 MOVS R0, #0

.text:0000005C 08 BD POP {R3,PC}

In order forscanf()to be able to read item it needs a parameter—pointer to an int. int is 32-bit, so we need 4 bytes to store it somewhere in memory, and it fits exactly in a 32-bit register. A place for the local variablexis allocated in the stack andIDAhas named itvar_8. It is not necessary, however, to allocate a such since SP(stack pointer) is already pointing to that space and it can be used directly. So, SP’s value is copied to theR1register and, together with the format-string, passed toscanf(). Later, with the help of theLDRinstruction, this value is moved from the stack to theR1register in order to be passed toprintf().

ARM64

Listing 7.3: Non-optimizing GCC 4.9.1 ARM64 1 .LC0:

8 ; subtract 32 from SP, then save FP and LR in stack frame:

9 stp x29, x30, [sp, -32]!

10 ; set stack frame (FP=SP)

11 add x29, sp, 0

12 ; load pointer to the "Enter X:" string:

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

13 adrp x0, .LC0

14 add x0, x0, :lo12:.LC0

15 ; X0=pointer to the "Enter X:" string 16 ; print it:

17 bl puts

18 ; load pointer to the "%d" string:

19 adrp x0, .LC1

20 add x0, x0, :lo12:.LC1

21 ; find a space in stack frame for "x" variable (X1=FP+28):

22 add x1, x29, 28

23 ; X1=address of "x" variable

24 ; pass the address to scanf() and call it:

25 bl __isoc99_scanf

26 ; load 32-bit value from the variable in stack frame:

27 ldr w1, [x29,28]

28 ; W1=x

29 ; load pointer to the "You entered %d...\n" string

30 ; printf() will take text string from X0 and "x" variable from X1 (or W1)

There is 32 bytes are allocated for stack frame, which is bigger than it needed.

Perhaps, some memory aligning issue? The most interesting part is finding space for thexvariable in the stack frame (line 22). Why 28? Somehow, compiler decided to place this variable at the end of stack frame instead of beginning. The address is passed toscanf(), which just stores the user input value in the memory at that address. This is 32-bit value of typeint. The value is fetched at line 27 and then passed toprintf().

7.1.6 MIPS

A place in the local stack is allocated for thexvariable, and it is to be referred as

$sp+ 24. Its address is passed toscanf(), and the user input values is loaded using the LW (“Load Word”) instruction and then passed toprintf().

Listing 7.4: Optimizing GCC 4.4.5 (assembly output)

$LC0:

.ascii "Enter X:\000"

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

addiu $4,$4,%lo($LC0) ; branch delay slot

; call scanf():

lw $28,16($sp) lui $4,%hi($LC1)

lw $25,%call16(__isoc99_scanf)($28)

; set 2nd argument of scanf(), $a1=$sp+24:

addiu $5,$sp,24 jalr $25

addiu $4,$4,%lo($LC1) ; branch delay slot

; call printf():

lw $28,16($sp)

; set 2nd argument of printf(),

; load word at address $sp+24:

lw $5,24($sp)

lw $25,%call16(printf)($28) lui $4,%hi($LC2)

jalr $25

addiu $4,$4,%lo($LC2) ; branch delay slot

; function epilogue:

addiu $sp,$sp,40 ; branch delay slot IDA displays the stack layout as follows:

Listing 7.5: Optimizing GCC 4.4.5 (IDA) .text:00000000 main:

.text:00000000

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

.text:00000000 lui $gp, (__gnu_local_gp >> ⤦ Ç 16)

.text:00000004 addiu $sp, -0x28

.text:00000008 la $gp, (__gnu_local_gp & 0⤦

Ç xFFFF)

.text:0000000C sw $ra, 0x28+var_4($sp)

.text:00000010 sw $gp, 0x28+var_18($sp)

; call puts():

.text:00000014 lw $t9, (puts & 0xFFFF)($gp⤦

Ç )

.text:00000018 lui $a0, ($LC0 >> 16) # "⤦

Ç Enter X:"

.text:0000001C jalr $t9

.text:00000020 la $a0, ($LC0 & 0xFFFF) # ⤦ Ç "Enter X:" ; branch delay slot

; call scanf():

.text:00000024 lw $gp, 0x28+var_18($sp)

.text:00000028 lui $a0, ($LC1 >> 16) # "%d⤦

Ç "

.text:0000002C lw $t9, (__isoc99_scanf & 0⤦

Ç xFFFF)($gp)

; set 2nd argument of scanf(), $a1=$sp+24:

.text:00000030 addiu $a1, $sp, 0x28+var_10 .text:00000034 jalr $t9 ; branch delay slot .text:00000038 la $a0, ($LC1 & 0xFFFF) # ⤦

Ç "%d"

; call printf():

.text:0000003C lw $gp, 0x28+var_18($sp)

; set 2nd argument of printf(),

; load word at address $sp+24:

.text:00000040 lw $a1, 0x28+var_10($sp)

.text:00000044 lw $t9, (printf & 0xFFFF)(⤦

Ç $gp)

.text:00000048 lui $a0, ($LC2 >> 16) # "⤦

Ç You entered %d...\n"

.text:0000004C jalr $t9

.text:00000050 la $a0, ($LC2 & 0xFFFF) # ⤦ Ç "You entered %d...\n" ; branch delay slot

; function epilogue:

.text:00000054 lw $ra, 0x28+var_4($sp)

; set return value to 0:

.text:00000058 move $v0, $zero

CHAPTER 7. SCANF() CHAPTER 7. SCANF()

; return:

.text:0000005C jr $ra

.text:00000060 addiu $sp, 0x28 ; branch delay⤦

Ç slot

Dans le document Reverse Engineering for Beginners (Page 132-141)

Documents relatifs