Sovrascrivere un indirizzo di memoria

Se vuoi risposte certe su cosa fa il compilatore devi disassemblare il codice.
Io faccio solo supposizioni, va visto cosa succede realmente a livello di assembly.

Ho dato un'occhiata al disassemblato.
Nella prima versione (quella originale), nella funzione foo le ultime 2 istruzioni sono il caricamento del valore del punto di rientro della funzione bar.
Nella seconda versione (quella con 2 print) vengono invece utilizzati 2 registri addizionali, r16 e r17, che vengono salvati nello stack ad inizio routine e recuperati a fine routine. Il problema è che vengono recuperati DOPO la manipolazione dell'indirizzo di rientro. Probabile che siano invertit, a questo punto. Cioè che il PC modificato finisca nei registri suddetti mentre nei registri vada il PC originale.

In base alle info di @leo a proposito di variabili del compilatore avr che rendono disponibili info sul sistema, ho cercato altre info su SP e similari.
Leggendo qui a proposito del noto freeram:
http://jeelabs.org/2011/05/22/atmega-memory-use/

C'e' un bello schema tipo quello di @leo e info sulle varie variabili.

__brkval punta all'inizio dello heap, SP alla fine dello stack ma c'e' anche RAMEND che punta all'inizio della ram.
Perciò (ma vale per una sola unica funzione, non se funzione dentro ad altra funzione!!!) se RAMEND-SP ottieni quanto occupato di stack in quella funzione. Le info da documentazione della Atmel, pdf -> avr-libc-user-manual-1.8.0.pdf

Nell'esempio (continuo con quello di @gammon), prova a vedere quanto occupato omettendo x e y, poi metti solo x e poi x e y

void foo ()
{ Serial.println("in foo. ");
  //volatile int x=0; //,y=0;
  Serial.println(RAMEND,HEX);
  Serial.println(SP,HEX);
  Serial.println(RAMEND-SP);
  // corrupt return address
  //unsigned int *stack = (unsigned int *) SP;
  //*(stack+1) = (unsigned int) bar;
}

volatile serve per impedire al compilatore di ottimizzare, ignorando x e y che non vengono in realtà usate.
Da me ottengo sulla UNO (su Mega simile, 21FF invece di 8FF):

var.... RAMEND. SP..... diff...
noloc 8FF 8F5 10
con x 8FF 8F1 14
con y 8FF 8EF 16

In teoria quella differenza ti dice se ci sono variabili locali e quanto occupano. Però tra nessuna variabile e 1 variabile locale ci sono 4 byte invece di 2 per la x come mi aspettavo, essendo x una word. Da 1 word per x aggiungendo altra word y aumenta l'occupazione dello stack di 2 ovvero una word come atteso.

nid69ita:
Però tra nessuna variabile e 1 variabile locale ci sono 4 byte invece di 2 per la x come mi aspettavo,

Credo (ma non ho visto il disassemblato, parlo per ipotesi) per via del fatto che, avendo dichiarato la var come "volatile", non la ottimizza per cui il compilatore deve assegnare ad un registro l'indirizzo della memoria dove salvare la variabile e poi andarci a scrivere il valore. Per tale operazione deve prima salvare lo stato del registro che andrà ad usare e poi recuperarli in fondo, ed ecco quindi il consumo di 4 byte: penso che i 2 byte extra siano 2 operazioni di push/pop. Probabilmente per la variabile "y" non usa un altro registro ma recupera lo stesso usato in precedenza, ed ecco allora che si spiega il motivo per cui lo sketch cresce di solo 2 byte, non necessitando quindi di un'altra coppia di push/pop.

Ho qualche dubbio...
Supponendo di avere un codice così:

#define F_CPU=16000000
#define ARDUINO 100
#include "Arduino.h"

void setup();
void loop();
void readSerialString ();
void printHacked();

void setup(){
	unsigned long val;
	Serial.begin(115200);
	val = (unsigned long) printHacked;
	Serial.print("Address of printHacked is "); 
	Serial.println(val,HEX);
	printHacked();
	Serial.println("Hello World");
}

void loop(){
	readSerialString();
	printf("-\n");
	delay(1500);
}

void readSerialString () {
	char buffer[4];
	strcpy(buffer,"1234123456\xa8");
}

void printHacked(){
	Serial.println("Hacked!\n");
}

Qui è l'assembly di readSerialString

void readSerialString () {
     1a8:	cf 93       	push	r28
     1aa:	df 93       	push	r29
     1ac:	00 d0       	rcall	.+0      	; 0x1ae <_Z16readSerialStringv+0x6>
     1ae:	0f 92       	push	r0
     1b0:	cd b7       	in	r28, 0x3d	; 61
     1b2:	de b7       	in	r29, 0x3e	; 62
	char buffer[4];
	strcpy(buffer,"1234123456\xa8\x00\x00\x00");
     1b4:	ce 01       	movw	r24, r28
     1b6:	01 96       	adiw	r24, 0x01	; 1
     1b8:	60 e3       	ldi	r22, 0x30	; 48
     1ba:	72 e0       	ldi	r23, 0x02	; 2
     1bc:	0e 94 1a 08 	call	0x1034	; 0x1034 <strcpy>
}
     1c0:	0f 90       	pop	r0
     1c2:	0f 90       	pop	r0
     1c4:	0f 90       	pop	r0
     1c6:	0f 90       	pop	r0
     1c8:	df 91       	pop	r29
     1ca:	cf 91       	pop	r28
     1cc:	08 95       	ret

Alla fine ci sono 6 pop! Non ne aspettavo 6! :fearful:
Ho provato anche ad aggiungere e togliere qualche variabile nella funzione, ma rimangono sempre questi sei pop.
Avevo pensato quindi che 4 bytes erano per buff[4] e 6 bytes per i pop, quindi 10 bytes, ma strcpy(buffer,"1234123456\xa8\x00\x00\x00") entra nel loop, come si vede qui sotto. Ed anche qui mi sarei aspettata un segmentation fault nel peggiore dei casi, ma non un loop :fearful:

es of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHaced isAddressˁpri]*­•‘isAddress of prntHacAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of printHacked isAddress of

P.S.: Grazie a tutti per l'aiuto e per darmi tutte queste informazioni!

Occhio che aggiungere variabili locali nella funzione non basta, devi usarle in qualche modo altrimenti il compilatore ottimizza il codice e non le considera.
Per questo avevo usato volatile, che però sembra avere il difetto di causare il push dei registri Rxx ulteriori come detto da @leo .

Osserva il disassemblato:

DarkCoffee:
Qui è l'assembly di readSerialString

void readSerialString () {

1a8: cf 93       push r28
    1aa: df 93       push r29
    1ac: 00 d0       rcall .+0       ; 0x1ae <_Z16readSerialStringv+0x6>
    1ae: 0f 92       push r0
    1b0: cd b7       in r28, 0x3d ; 61
    1b2: de b7       in r29, 0x3e ; 62
char buffer[4];
strcpy(buffer,"1234123456\xa8\x00\x00\x00");
    1b4: ce 01       movw r24, r28
    1b6: 01 96       adiw r24, 0x01 ; 1
    1b8: 60 e3       ldi r22, 0x30 ; 48
    1ba: 72 e0       ldi r23, 0x02 ; 2
    1bc: 0e 94 1a 08 call 0x1034 ; 0x1034
}
    1c0: 0f 90       pop r0
    1c2: 0f 90       pop r0
    1c4: 0f 90       pop r0
    1c6: 0f 90       pop r0
    1c8: df 91       pop r29
    1ca: cf 91       pop r28
    1cc: 08 95       ret



Alla fine ci sono 6 pop! Non ne aspettavo 6! :fearful:

Nota questo:

 1ac:	00 d0       	rcall	.+0      	; 0x1ae <_Z16readSerialStringv+0x6>
     1ae:	0f 92       	push	r0

Siccome sono 4 caratteri nel char, la funzione viene chiamata 4 volte, quindi sono 4 push r0.
Alla fine vengono ritirati fuori i 4 r0 e poi i 2 registri di inizio subruotine, per un totale di 6 pop.

Grazie leo72!

Sto cercando di capire bene cosa succede e come fare per modificare questo return address.
Sto perdendo un pochino la speranza.

Ho guardado il manuale di Atmel, ho controllato i registri, il pc, l'SP. Pensavo che bastava sovrascrivere lo stack usando il buffer, ma non credo più riesco ad usare questa tecnica. O calcolo male la quantità di bytes, che non credo, oppure si deve fare in un altro modo.

Ho provato a giocare con la "Processor window" di Atmel http://127.0.0.1:47873/help/1-7248/ms.help?method=page&id=ATMELSTUDIOUSERGUIDEATMELSTUDIO.DEBUG.VIEWS.PROCESSORVIEW&product=ATMELStudio&productversion=6.0&locale=en-US&topiclocale=EN-US&topicversion=0&SQM=2
cambiando i valori alla fine della funzione interessata, ma niente.
Non so più cosa pensare...

non ho capito una cosa, a te serve pari pari quello che ha fatto Gammon sulla UNO ?
Se e' quello che ti serve deve per forza essere possibile farlo, solo con indirizzi diversi.
Io partirei con l'analisi del codice di Gammon su una UNO, hai una UNO ? se non l'hai prenditi solo il micro, lo trovi nei negozi, e te lo monti su breadboard, e fai le prove con Atmel studio.
Ho visto che lavorando con i breakpoint roesci ad analizzare i valori dei registri, di SP, di PC, quindi partendo dalla uno con il codice di Gammon dovresti riuscire ad approfondire

Grazie Testato per l'aiuto che mi stai dando!
No, no, non voglio farlo pari pari.
L'obiettivo è quello di modificare il return address e posso usare tutti i metodi (che credo ne siano pochi).
Siccome l'avevo già fatto per l'x86 volevo farlo in modo simile anche per l'avr così capivo bene le differenze.
Adesso provo ad aggiustarmi quello di Gammon per la mia board e vedere dove sbaglio.

certo, il mio consiglio e' appunto partire da quello, perche' riuscire a fare quello che fa gammon sulla mega e' cmq un passo avanti, e poi con visual studio dovresti riuscire ad approfondire, tecnicamente non riesco a darti una mano piu' di questo, sei piu' pratico tu di me :slight_smile:

C'era un post di "low" (mi pare era così l'username) che mi ero riproposta di leggerlo con calma dopo cena, ma non lo trovo più...

Grazie mille Lock!
Domani lo leggo con calma, sopratutto se riesco a risolvere questo errore che è comparso all'improvviso:

avrdude: stk500v2_ReceiveMessage(): timeout

@lock: non c'è bisogno di ricopiare più di una volta tutto il resto del testo del tuo post, hai allungato questo thread più di un lenzuolo :wink:

Grazie Lock! Quante informazioni utili!

Non ancora ti posso stare dietro praticamente perchè sto ancora lottando con quell'errore maledetto!
E' iniziato tutto con un atmega1280. Stavo cercando di raggiungere il ret addr ed ad un certo punto ha iniziato a darmi "avrdude: stk500_getsync(): not in sync: resp=0x00", ho veramente cercato di risolverlo in ogni modo, ma niente.
Precisamente era un APM 1 (Ardupilot) che monta un atmega1280. Per fargli il debug usava un JTAG non ufficiale che dovrebbe essere simile ad un jtagice mkII.
Dopo un mese buono ho parlato con la persona che mi segue all'università e mi ha dato un APM 2.5 che monta un atmega2560.
Tutto bene, stavo provando con voi a fare l'esercizio, ma ad un certo punto anche lei ha iniziato a darmi "timeout", che dovrebbe essere lo stesso errore della atmega1280.

Ieri sera finalmente sono riuscita a risolvere per l'atmega1280 scoprendo che faceva il buffer overflow delle pagine della flash memory (guarda questo post che ho aggiunto qui: [SOLVED]avrdude: stk500_getsync(): not in sync: resp=0x00 (Arduino Nano r3.0 - #9 by system - IDE 1.x - Arduino Forum

Ora vorrei provare a fare la stessa cosa con la atmega2560.

Secondo te questo errore che ho preso è relativo all'esercizio che sto facendo?
Perchè qualche dubbio mi è venuto...

Grazie ancora tantissimo per l'aiuto e le informazioni!

Ma quando hai quest'errore di sync nemmeno con ICSP riesci a cancellare la flash ?

Non ho un icsp, volevo compare un avrisp, ma non ho molto tempo per la spedizione.
Mi hanno dato questo all'inizio:

Solo che non capisco come collegarlo con l'APM 2.5 in modo che funziona come isp.

Finalmente superato!
Non ci posso credere!
Lo so sono off-topic, ma sono troppo contenta!
Ora ritorno al maledetto ret address con i vostri consigli. Grazie!

la connessione ICSP e' a sinistra della usb, il tuo programmatore dovrebbe andare bene, oppure usa un arduino se ce l'hai programmato con lo sketch ArduinoISP
http://wiki.ardupilot-mega.googlecode.com/git/images/APM2/APM2_main_components.jpg

Si si avevo collegato bene i fili, solo che con avrdude mettevo "-ctagmkII" mentre dovevo mettere "-cjtag2isp".
Mentre per lAPM 1 ho risolto come ho scritto nel post linkato sopra :slight_smile: