• Aucun résultat trouvé

MSVC: x86 + Hiew

Dans le document Reverse Engineering for Beginners (Page 163-170)

printf() with several arguments

GCC + GDB

7.3 scanf() result checking

7.3.4 MSVC: x86 + Hiew

This can also be used as a simple example of executable file patching. We may try to patch the executable so the program would always print the input, no matter what we enter.

Assuming that the executable is compiled against externalMSVCR*.DLL(i.e., with /MDoption)7, we see themain()function at the beginning of the.textsection.

Let’s open the executable in Hiew and find the beginning of the.text section (Enter, F8, F6, Enter, Enter).

We can see this:

Figure 7.12: Hiew:main()function

Hiew findsASCIIZ8 strings and displays them, as it does with the imported func-tions’ names.

7that’s what also called “dynamic linking”

8ASCII Zero (null-terminated ASCII string)

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

Move the cursor to address.00401027(where theJNZinstruction, we have to bypass, is located), press F3, and then type “9090”(, meaning twoNOPs):

Figure 7.13: Hiew: replacingJNZwith twoNOPs

Then press F9 (update). Now the executable is saved to the disk. It will behave as we wanted.

TwoNOPs are probably not the most æsthetic approach. Another way to patch this instruction is to write just 0 to the second opcode byte (jump offset), so thatJNZ will always jump to the next instruction.

We could also do the opposite: replace first byte withEBwhile not touching the second byte (jump offset). We would get an unconditional jump that is always triggered. In this case the error message would be printed every time, no matter the input.

7.3.5 MSVC: x64

Since we work here withint-typed variables, which are still 32-bit in x86-64, we see how the 32-bit part of the registers (prefixed withE-) are used here as well.

While working with pointers, however, 64-bit register parts are used, prefixed with R-.

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

Listing 7.12: MSVC 2012 x64 _DATA SEGMENT

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

$SG2926 DB '%d', 00H

$SG2927 DB 'You entered %d...', 0aH, 00H

$SG2929 DB 'What you entered? Huh?', 0aH, 00H _DATA ENDS

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

lea rdx, QWORD PTR x$[rsp]

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

cmp eax, 1

jne SHORT $LN2@main

mov edx, DWORD PTR x$[rsp]

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

jmp SHORT $LN1@main

$LN2@main:

lea rcx, OFFSET FLAT:$SG2929 ; 'What you entered? ⤦ Ç Huh?'

ARM: Optimizing Keil 6/2013 (Thumb mode)

Listing 7.13: Optimizing Keil 6/2013 (Thumb mode)

var_8 = -8

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

PUSH {R3,LR}

ADR R0, aEnterX ; "Enter X:\n"

BL __2printf

ADR R0, aWhatYouEntered ; "What you entered⤦

Ç ? Huh?\n"

ADR R0, aYouEnteredD___ ; "You entered %d⤦

Ç ...\n"

BL __2printf

B loc_1A

The new instructions here areCMPandBEQ9.

CMPis analogous to the x86 instruction with the same name, it subtracts one of the arguments from the other and updates the conditional flags if needed.

BEQ jumps to another address if the operands were equal to each other, or, if the result of the last computation was 0, or if the Z flag is 1. It behaves asJZin x86.

Everything else is simple: the execution flow forks in two branches, then the branches converge at the point where 0 is written into theR0as a function return value, and then the function ends.

ARM64

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

9(PowerPC, ARM) Branch if Equal

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

7 .LC3:

8 .string "What you entered? Huh?"

9 f6:

10 ; save FP and LR in stack frame:

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

12 ; set stack frame (FP=SP)

13 add x29, sp, 0

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

15 adrp x0, .LC0

16 add x0, x0, :lo12:.LC0

17 bl puts

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

19 adrp x0, .LC1

20 add x0, x0, :lo12:.LC1

21 ; calculate address of x variable in the local stack

22 add x1, x29, 28

23 bl __isoc99_scanf

24 ; scanf() returned result in W0.

25 ; check it:

26 cmp w0, 1

27 ; BNE is Branch if Not Equal

28 ; so if W0<>0, jump to L2 will be occurred

29 bne .L2

30 ; at this moment W0=1, meaning no error 31 ; load x value from the local stack

32 ldr w1, [x29,28]

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

34 adrp x0, .LC2

35 add x0, x0, :lo12:.LC2

36 bl printf

37 ; skip the code, which print

the "What you entered? Huh?" string:

38 b .L3

39 .L2:

40 ; load pointer to the "What you entered? Huh?" string:

41 adrp x0, .LC3

Code flow in this case forks with the use of CMP/BNE (Branch if Not Equal) instruc-tions pair.

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

7.3.7 MIPS

Listing 7.15: Optimizing GCC 4.4.5 (IDA) .text:004006A0 main:

.text:004006C0 la $a0, aEnterX # "⤦

Ç Enter X:"

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

.text:004006C8 lui $a0, 0x40

.text:004006CC la $t9, __isoc99_scanf

.text:004006D0 la $a0, aD # "%d"

.text:004006D4 jalr $t9 ; __isoc99_scanf .text:004006D8 addiu $a1, $sp, 0x28+var_10 #⤦

Ç branch delay slot

.text:004006DC li $v1, 1

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

.text:004006E4 beq $v0, $v1, loc_40070C

.text:004006E8 or $at, $zero # ⤦

Ç branch delay slot, NOP

.text:004006EC la $t9, puts

.text:004006F0 lui $a0, 0x40

.text:004006F4 jalr $t9 ; puts

.text:004006F8 la $a0, aWhatYouEntered # ⤦

Ç "What you entered? Huh?"

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

.text:0040071C la $a0, aYouEnteredD___ # ⤦

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

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

.text:00400724 move $v0, $zero

.text:00400728 jr $ra

.text:0040072C addiu $sp, 0x28

scanf()returns the result of its work in register $V0. It is checked at address 0x004006E4 by comparing the values in $V0 with $V1 (1 was stored in $V1 earlier, at 0x004006DC). BEQ stands for “Branch Equal”. If the two values are equal (i.e., success), the execution jumps to address 0x0040070C.

7.3.8 Exercise

As we can see, the JNE/JNZ instruction can be easily replaced by the JE/JZ and vice versa (or BNE by BEQ and vice versa). But then the basic blocks must also be swapped. Try to do this in some of the examples.

7.4 Exercises

7.4.1 Exercise #1

This code, compiled in Linux x86-64 using GCC is crashing while execution (seg-mentation fault). However, it works in Windows environment compiled by MSVC 2010 x86. Why?

#include <string.h>

#include <stdio.h>

void alter_string(char *s) {

strcpy (s, "Goodbye!");

printf ("Result: %s\n", s);

};

int main() {

alter_string ("Hello, world!\n");

};

Answer:G.1.3 on page 1435.

CHAPTER 8. ACCESSING PASSED ARGUMENTS CHAPTER 8. ACCESSING PASSED ARGUMENTS

Chapter 8

Dans le document Reverse Engineering for Beginners (Page 163-170)

Documents relatifs