Hi all,
after playing around with Arduino a bit I'm kind of lost in the internals of the Arduino Nano and its AVR (ATmega328P) processor.
I'm mainly interested in IT-Security, so I tried to port some x86 attacks to avr. One of the most common attacks is to overwrite some return- or function-pointer, and that's kind of what my question is about.
Given the following two functions:
__attribute__((noinline, used)) void win() {
Serial.println(F("\nYou Win!\n\n"));
}
__attribute__((noinline, used)) void dontwin() {
Serial.println(F("\nYou Lose!\n\n"));
}
I created a sample attack with a buffer overflow in a struct:
struct funs {
char fun_name[16];
void (*fun_addr)(void);
}__attribute__((packed));
__attribute__((noinline)) void buffer_overflow() {
Serial.print("win at ");
Serial.println((size_t) &win, HEX);
struct funs funstr = {0};
funstr.fun_addr = dontwin;
read_string_blocking(funstr.fun_name, 64);
(funstr.fun_addr)();
return;
}
Where read_string_blocking is a helper method I wrote, that reads bytes from serial until \r\n (discarding both characters) and copies it to the given buffer (up to given length), while not writing a trailing nullbyte.
This makes overwriting bytes easier.
So what I found out with this (and tons of debug prints, that I omitted for readability) is, that the dontwin function lies at 0x02DD and the win function at 0x02D9.
But when I take a look at the .ELF that Arduino generates, the functions are at 0x05BA and 0x05B2 respectively. Also when reading the flash memory, the functions lie where the ELF says and not where Arduino says.
Obviously 0x2DD is exactly half of 0x05BA, which makes kind of sense because one shouldn't be able to jump to odd addresses anyways (since it's word aligned I guess).
So I could imagine that the ICALL instruction first multiplies the operand by two (or shifts it to the left). This would increase the space addressable by ICALL instruction by 2.
Interestingly the datasheet does not address this "feature" at all. So could it also be a feature introduced by gcc?
Could someone confirm my thoughts (and give reference) or am I on the wrong track and something else fishy is going on?
Also a follow up question: If this is true, does it also apply to Return Addresses and the RET Instruction? Or what jumps/calls and pointers does it apply to?
Thanks!