@Matteo34
Una cosa che puoi fare per accedere al contenuto della funzione (i bytes delle istruzioni) è quella di usare un puntatore a un char, castando a
char*
la funzione.
C:
#include <stdio.h>
#include <stdint.h>
void print_hello() {
printf("Hello World");
}
int main() {
printf("Address of function: %p\n", &print_hello);
char *func = (char*) &print_hello;
int index = 0;
uint8_t byte;
do {
byte = (uint8_t) *(func+index);
printf("%X ", byte);
index++;
} while(byte != 0xC3);
return 0;
}
In output ti restituisce:
Codice:
55 89 E5 83 EC 18 C7 4 24 44 40 40 0 E8 1A 10 0 0 90 C9 C3
0xC3 è l'istruzione
RET
.
Visto da Olly:
Visualizza allegato 396349
In merito alla tua domanda invece dipende. Se viene usata una locazione di memoria si trova sullo stack, e di norma l'accesso avviene usando il registro
EBP
.
Considera questo codice, giusto per fare un esempio:
C:
#include <stdio.h>
#include <stdint.h>
int sum_array(int *array, int size) {
int sum = 0;
for(int i=0; i<size; i++)
sum += array[i];
return sum;
}
int main() {
int array[10] = {1,2,3,4,5,6,7,8,9,9};
printf("Sum: %d\n", sum_array(array,10));
return 0;
}
Riporto dell'assembly altrimenti sarebbe impossibile spiegare cosa accade ?
Siccome è un array inizializzato staticamente, si trova tutto sullo stack:
Codice:
00401609 MOV DWORD PTR SS:[LOCAL.10],1
00401611 MOV DWORD PTR SS:[LOCAL.9],2
00401619 MOV DWORD PTR SS:[LOCAL.8],3
00401621 MOV DWORD PTR SS:[LOCAL.7],4
00401629 MOV DWORD PTR SS:[LOCAL.6],5
00401631 MOV DWORD PTR SS:[LOCAL.5],6
00401639 MOV DWORD PTR SS:[LOCAL.4],7
00401641 MOV DWORD PTR SS:[LOCAL.3],8
00401649 MOV DWORD PTR SS:[LOCAL.2],9
00401651 MOV DWORD PTR SS:[LOCAL.1],9
00401659 MOV DWORD PTR SS:[LOCAL.15],0A
00401661 LEA EAX,[LOCAL.10]
00401665 MOV DWORD PTR SS:[LOCAL.16],EAX
L'array qui inizia da LOCAL.10 (che è in realtà
ESP+0x3C
) e termina a LOCAL.1, e viene inizializzato int per int (DWORD). A parte questo, in LOCAL.15 viene memorizzata la dimensione (il secondo parametro della funzione, 10 elementi). LOCAL.15 è
ESP+4
.
LOCAL.10 è l'elemento all'indice 0, il cui indirizzo viene memorizzato in EAX (
LEA EAX,[LOCAL.10]
).
Quindi in questo caso viene usato un registro e subito dopo viene assegnato alla locazione LOCAL.16 (ESP). In sostanza è il parametro della funzione (l'array).
In soldoni: vengono memorizzati sullo stack, sono i parametri passati alla funzione.
La funzione che effettua la somma è questa:
Codice:
004015C0 PUSH EBP
004015C1 MOV EBP,ESP
004015C3 SUB ESP,10
004015C6 MOV DWORD PTR SS:[LOCAL.1],0
004015CD MOV DWORD PTR SS:[LOCAL.2],0
004015D4 JMP SHORT 004015EE
004015D6 /MOV EAX,DWORD PTR SS:[LOCAL.2]
004015D9 |LEA EDX,[EAX*4]
004015E0 |MOV EAX,DWORD PTR SS:[ARG.1]
004015E3 |ADD EAX,EDX
004015E5 |MOV EAX,DWORD PTR DS:[EAX]
004015E7 |ADD DWORD PTR SS:[LOCAL.1],EAX
004015EA |ADD DWORD PTR SS:[LOCAL.2],1
004015EE |MOV EAX,DWORD PTR SS:[LOCAL.2]
004015F1 |CMP EAX,DWORD PTR SS:[ARG.2]
004015F4 \JL SHORT 004015D6
004015F6 MOV EAX,DWORD PTR SS:[LOCAL.1]
004015F9 LEAVE
004015FA RETN
Lasciando perdere i dettagli, l'accesso a ogni singolo elemento avviene qui:
Codice:
004015D6 |> /8B45 F8 /MOV EAX,DWORD PTR SS:[LOCAL.2]
004015D9 |. |8D1485 00000000 |LEA EDX,[EAX*4]
004015E0 |. |8B45 08 |MOV EAX,DWORD PTR SS:[ARG.1]
004015E3 |. |01D0 |ADD EAX,EDX
004015E5 |. |8B00 |MOV EAX,DWORD PTR DS:[EAX]
004015E7 |. |0145 FC |ADD DWORD PTR SS:[LOCAL.1],EAX
LOCAL.2 è l'indice (l'indice i del for), quindi viene calcolato EAX*4 e il risultato è salvato poi in EDX. Non è altro che un calcolo per ottenere l'indice dell'elemento: i=0 -> 0*4, i=1 -> 1*4, i=2 -> 2*4...
ARG.1 è il primo parametro, l'indirizzo dell'array. A questo indirizzo viene sommato il valore di EDX, così da ottenere l'elemento in quella posizione.
Il risultato è l'elemento alla posizione i dell'array; LOCAL.1 è la variabile somma.
L'accesso non avviene mai usando solo ed esclusivamente lo stack (posto che anche l'indirizzo degli elementi sullo stack è mantenuto in un registro), ma inevitabilmente come dice pabloski risiedono in memoria e spostati nei registri in casi specifici.