• No se han encontrado resultados

RESULTADOS

In document UNIVERSIDAD NACIONAL DE TRUJILLO (página 33-48)

As of x86, function execution result is usually returned 1 in the EAX register. If it is byte type or character (char) —then in the lowest register EAX part— AL. If function returns float number, the FPU register ST(0) is to be used instead. In ARM, result is usually returned in the R0 register.

By the way, what if returning value of the main() function will be declared not as int but as void?

so-called startup-code is calling main() roughly as:

push envp push argv push argc call main push eax call exit

In other words:

exit(main(argc,argv,envp));

If you declare main() as void and nothing will be returned explicitly (by return statement), then something random, that was stored in the EAX register at the moment of the main() finish, will come into the sole exit() function argument. Most likely, there will be a random value, leaved from your function execution. So, exit code of program will be pseudorandom.

I can illustrate this fact. Please notice, the main() function has void type:

#include <stdio.h>

void main() {

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

};

Let‘s compile it in Linux.

GCC 4.8.1 replaced printf() to puts() (we saw this before: 2.3.3), but that‘s OK, sinceputs() returns number of char-acters printed, just like printf(). Please notice that EAX is not zeroed before main() finish. This means, EAX value at the main() finish will contain what puts() leaved there.

Listing 8.1: GCC 4.8.1 .LC0:

.string "Hello, world!"

main:

push ebp

mov ebp, esp and esp, -16 sub esp, 16

mov DWORD PTR [esp], OFFSET FLAT:.LC0

call puts

1See also: MSDN: Return Values (C++): http://msdn.microsoft.com/en-us/library/7572ztz4.aspx

61

CHAPTER 8. ONE MORE WORD ABOUT RESULTS RETURNING.

Let‘s back to the fact the returning value is leaved in theEAX register. That is why old C compilers cannot create functions capable of returning something not fitting in one register (usually type int) but if one needs it, one should return information via pointers passed in function arguments. Now it is possible, to return, let‘s say, whole structure, but still it is not very pop-ular. If function must return a large structure, caller must allocate it and pass pointer to it via first argument, transparently for programmer. That is almost the same as to pass pointer in first argument manually, but compiler hide this.

Small example:

Macro name for internal variable passing pointer to structure is $T3853 here.

This example can be rewritten using C99 language extensions:

struct s {

62

CHAPTER 8. ONE MORE WORD ABOUT RESULTS RETURNING.

int a;

int b;

int c;

};

struct s get_some_values (int a) {

return (struct s){.a=a+1, .b=a+2, .c=a+3};

};

Listing 8.3: GCC 4.8.1 _get_some_values proc near

ptr_to_struct = dword ptr 4

a = dword ptr 8

mov edx, [esp+a]

mov eax, [esp+ptr_to_struct]

lea ecx, [edx+1]

mov [eax], ecx lea ecx, [edx+2]

add edx, 3 mov [eax+4], ecx mov [eax+8], edx retn

_get_some_values endp

As we may see, the function is just filling fields in the structure, allocated by caller function. So there are no performance drawbacks.

63

CHAPTER 9. POINTERS

Chapter 9

Pointers

Pointers are o_en used to return values from function (recall scanf() case (6)). For example, when function should return two values.

9.1 Global variables example

#include <stdio.h>

void f1 (int x, int y, int *sum, int *product) {

*sum=x+y;

*product=x*y;

};

int sum, product;

void main() {

f1(123, 456, &sum, &product);

printf ("sum=%d, product=%d\n", sum, product);

};

This compiling into:

Listing 9.1: Optimizing MSVC 2010 (/Ox /Ob0) COMM _product:DWORD

COMM _sum:DWORD

$SG2803 DB ‘sum=%d, product=%d‘, 0aH, 00H

_x$ = 8 ; size = 4

_y$ = 12 ; size = 4

_sum$ = 16 ; size = 4

_product$ = 20 ; size = 4

_f1 PROC

mov ecx, DWORD PTR _y$[esp-4]

mov eax, DWORD PTR _x$[esp-4]

lea edx, DWORD PTR [eax+ecx]

imul eax, ecx

mov ecx, DWORD PTR _product$[esp-4]

push esi

mov esi, DWORD PTR _sum$[esp]

mov DWORD PTR [esi], edx mov DWORD PTR [ecx], eax

pop esi

ret 0

_f1 ENDP

64

9.1. GLOBAL VARIABLES EXAMPLE CHAPTER 9. POINTERS

_main PROC

push OFFSET _product push OFFSET _sum

push 456 ; 000001c8H

push 123 ; 0000007bH

call _f1

mov eax, DWORD PTR _product mov ecx, DWORD PTR _sum

push eax

push ecx

push OFFSET $SG2803

call DWORD PTR __imp__printf

add esp, 28 ; 0000001cH

xor eax, eax

ret 0

_main ENDP

Let‘s see this in OllyDbg: fig. 9.1. At first, global variables addresses are passed intof1(). We can click―Follow in dump‖ on the stack element, and we will see a place in data segment allocated for two variables. These variables are cleared, because non-initialized data (BSS1) are cleared before execution begin. They are residing in data segment, we can be sure it is so, by pressing Alt-M and seeing memory map: fig. 9.5.

Let‘s trace (F7) until execution off1()fig. 9.2. Two values are seen in the stack 456 (0x1C8) and 123 (0x7B), and two global variables addresses as well.

Let‘s trace until the end off1(). At the window at le_ we see how calculation results are appeared in the gloval variables fig. 9.3.

Now values of global variables are loaded into registers for passing into printf(): fig. 9.4.

Figure 9.1: OllyDbg: global variables addresses are passing into f1()

1Block Started by Symbol

65

9.1. GLOBAL VARIABLES EXAMPLE CHAPTER 9. POINTERS

Figure 9.2: OllyDbg: f1()is started

Figure 9.3: OllyDbg: f1()finishes

66

9.2. LOCAL VARIABLES EXAMPLE CHAPTER 9. POINTERS

Figure 9.4: OllyDbg: global variables addresses are passed into printf()

Figure 9.5: OllyDbg: memory map

9.2 Local variables example

Let‘s rework our example slightly:

Listing 9.2: now variables are local void main()

{

int sum, product; // now variables are here

f1(123, 456, &sum, &product);

printf ("sum=%d, product=%d\n", sum, product);

};

f1()function code will not changed. Only main() code will:

Listing 9.3: Optimizing MSVC 2010 (/Ox /Ob0)

_product$ = -8 ; size = 4

_sum$ = -4 ; size = 4

_main PROC

; Line 10

sub esp, 8

; Line 13

67

9.2. LOCAL VARIABLES EXAMPLE CHAPTER 9. POINTERS lea eax, DWORD PTR _product$[esp+8]

push eax

lea ecx, DWORD PTR _sum$[esp+12]

push ecx

push 456 ; 000001c8H

push 123 ; 0000007bH

call _f1

; Line 14

mov edx, DWORD PTR _product$[esp+24]

mov eax, DWORD PTR _sum$[esp+24]

push edx

push eax

push OFFSET $SG2803

call DWORD PTR __imp__printf

; Line 15

xor eax, eax

add esp, 36 ; 00000024H

ret 0

Let‘s again take a look into OllyDbg. Local variable addresses in the stack are0x35FCF4 and 0x35FCF8. We see how these are pushed into the stack: fig. 9.6.

f1()is started. Random garbage are at 0x35FCF4 and 0x35FCF8 so far fig. 9.7.

f1()finished. There are 0xDB18 and 0x243 now at 0x35FCF4 and 0x35FCF8 addresses, these values are f1()function result.

Figure 9.6: OllyDbg: addresses of local variables are pushed into the stack

68

9.3. CONCLUSION CHAPTER 9. POINTERS

Figure 9.7: OllyDbg: f1()starting

Figure 9.8: OllyDbg: f1()finished

9.3 Conclusion

f1()can return results to any place in memory, located anywhere. This is essence and usefulness of pointers.

By the way, C++ references works just in the same way. Read more about them: (29.3).

69

CHAPTER 10. CONDITIONAL JUMPS

Chapter 10

In document UNIVERSIDAD NACIONAL DE TRUJILLO (página 33-48)

Documento similar