Loader un binaire Arduino dans IDA

Bon, maintenant que l’on sait comment prendre un dump d’un Arduino, Il ne faut pas croire qu’on peut juste ouvrir le binaire dans IDA.

Ca n’est pas aisé de faire manger ce binaire à IDA. Pour ma part déjà, j’ai un IDA qui ne connait pas le cpu atmel AVR ATmega328 que l’on retrouve sur le Arduino Uno.

Heureusement il suffit d’indiquer les spécifications de ce contrôleur dans le fichier /cfg/avr.cfg et IDA retrouve miraculeusement ses petits. On trouve tous ces paramètres dans la documentation de référence ATmega48A/PA/88A/PA/168A/PA/328/P chez Atmel.

J’ai donc recopié et modifié la configuration du 168p et ca semble aller. Voici ma conf, j’espère que je n’ai pas fait d’erreurs. Si qqun en trouve merci de me le dire.

.ATmega328
; Ida avr.cfg (c) THANATOS

SUBARCH=5

RAM=2048
ROM=32768
EEPROM=1024

; MEMORY MAP
; Memory configuration A

area DATA GPWR_        0x0000:0x0020   General Purpose Working Registers
area DATA FSR_         0x0020:0x0060   I/O registers
area DATA EXTIO_       0x0060:0x0100   I/O registers
area DATA I_SRAM       0x0100:0x08ff   Internal SRAM

; Interrupt and reset vector assignments
entry __RESET        0x0000   External Pin, Power-on Reset, Brown-out Reset, Watchdog Reset, and JTAG AVR Reset
entry INT0_          0x0004   External Interrupt Request 0
entry INT1_          0x0008   External Interrupt Request 1
entry PCINT0_        0x000C   Pin Change Interrupt Request 0
entry PCINT1_        0x0010   Pin Change Interrupt Request 1
entry PCINT2_        0x0014   Pin Change Interrupt Request 2
entry WDT            0x0018   Watchdog Time-out Interrupt
entry TIMER2_COMPA   0x001C   Timer/Counter2 Compare Match A
entry TIMER2_COMPB   0x0020   Timer/Counter2 Compare Match B
entry TIMER2_OVF     0x0024   Timer/Counter2 Overflow
entry TIMER1_CAPT    0x0028   Timer/Counter1 Compare Match
entry TIMER1_COMPA   0x002C   Timer/Counter1 Compare Match A
entry TIMER1_COMPB   0x0030   Timer/Counter1 Compare Match A
entry TIMER2_OVF     0x0034   Timer/Counter1 Overflow
entry TIMER0_COMPA   0x0038   Timer/Counter0 Compare Match A
entry TIMER0_COMPB   0x003c   Timer/Counter0 Compare Match B
entry TIMER0_OVF     0x0040   Timer/Counter0 Overflow
entry SPI_STC        0x0044   Serial Transfer Complete
entry USART0_RXC     0x0048   USART Rx Complete
entry USART0_UDRE    0x004c   USART Data Register Empty
entry USART0_TXC     0x0050   USART Tx Complete
entry ADC            0x0054   ADC Conversion Complete
entry EE_READY       0x0058   EEPROM Ready
entry ANALOG_COMP    0x005c   Analog Comparator
entry TWI            0x0060   2-wire Serial Interface
;entry SPM_READY      0x0064   Store Program Memory Ready

; INPUT/OUTPUT PORTS
UBRR1L           0x0000   USART1 Baud Rate Register Low Byte
UCSR1B           0x0001   USART Control and Status Register B
UCSR1B.RXCIE      7   RX Complete Interrupt Enable
UCSR1B.TXCIE      6   TX Complete Interrupt Enable
UCSR1B.UDRIE      5   USART Data Register Empty Interrupt Enable
UCSR1B.RXEN       4   Receiver Enable
UCSR1B.TXEN       3   Transmitter Enable
UCSR1B.UCSZ2      2   Character Size
UCSR1B.RXB8       1   Receive Data Bit 8
UCSR1B.TXB8       0   Transmit Data Bit8
UCSR1A           0x0002   USART Control and Status Register A
UCSR1A.RXC        7   USART Receive Complete
UCSR1A.TXC        6   USART Transmit Complete
UCSR1A.UDRE       5   USART Data Register Empty
UCSR1A.FE         4   Frame Error
UCSR1A.DOR        3   Data OverRun
UCSR1A.PE         2   Parity Error
UCSR1A.U2X        1   Double the USART Transmission Speed
UCSR1A.MPCM       0   Multi-processor Communication Mode
UDR1             0x0003   USART I/O Data Register
OCDR             0x0004   On-chip Debug Register
PINE             0x0005   Port E Input Pins Address
PINE.PINE2        2
PINE.PINE1        1
PINE.PINE0        0
DDRE             0x0006   Port E Data Direction Register
DDRE.DDE2         2   Port E Data Direction Register bit 2
DDRE.DDE1         1   Port E Data Direction Register bit 1
DDRE.DDE0         0   Port E Data Direction Register bit 0
PORTE            0x0007   Port E Data Register
PORTE.PORTE2      2   Port E Data Register bit 2
PORTE.PORTE1      1   Port E Data Register bit 1
PORTE.PORTE0      0   Port E Data Register bit 0
ACSR             0x0008   Analog Comparator Control and Status Register
ACSR.ACD          7   Analog Comparator Disable
ACSR.ACBG         6   Analog Comparator Bandgap Select
ACSR.ACO          5   Analog Comparator Output
ACSR.ACI          4   Analog Comparator Interrupt Flag
ACSR.ACIE         3   Analog Comparator Interrupt Enable
ACSR.ACIC         2   Analog Comparator Input Capture Enable
ACSR.ACIS1        1   Analog Comparator Interrupt Mode Select 1
ACSR.ACIS0        0   Analog Comparator Interrupt Mode Select 0
UBRR0L           0x0009   USART0 Baud Rate Register Low Byte
UCSR0B           0x000A   USART Control and Status Register B
UCSR0B.RXCIE      7   RX Complete Interrupt Enable
UCSR0B.TXCIE      6   TX Complete Interrupt Enable
UCSR0B.UDRIE      5   USART Data Register Empty Interrupt Enable
UCSR0B.RXEN       4   Receiver Enable
UCSR0B.TXEN       3   Transmitter Enable
UCSR0B.UCSZ2      2   Character Size
UCSR0B.RXB8       1   Receive Data Bit 8
UCSR0B.TXB8       0   Transmit Data Bit8
UCSR0A           0x000B   USART Control and Status Register A
UCSR0A.RXC        7   USART Receive Complete
UCSR0A.TXC        6   USART Transmit Complete
UCSR0A.UDRE       5   USART Data Register Empty
UCSR0A.FE         4   Frame Error
UCSR0A.DOR        3   Data OverRun
UCSR0A.PE         2   Parity Error
UCSR0A.U2X        1   Double the USART Transmission Speed
UCSR0A.MPCM       0   Multi-processor Communication Mode
UDR0             0x000C   USART I/O Data Register
SPCR             0x000D   SPI Control Register-
SPCR.SPIE         7   SPI Interrupt Enable
SPCR.SPE          6   SPI Enable
SPCR.DORD         5   Data Order
SPCR.MSTR         4   Master/Slave Select
SPCR.CPOL         3   Clock Polarity
SPCR.CPHA         2   Clock Phase
SPCR.SPR1         1   SPI Clock Rate Select 1
SPCR.SPR0         0   SPI Clock Rate Select 0
SPSR             0x000E   SPI Status Register
SPSR.SPIF         7   SPI Interrupt Flag
SPSR.WCOL         6   Write COLlision Flag
SPSR.SPI2X        0   Double SPI Speed Bit
SPDR             0x000F   SPI Data Register
PIND             0x0010   Port D Input Pins Address
PIND.PIND7        7
PIND.PIND6        6
PIND.PIND5        5
PIND.PIND4        4
PIND.PIND3        3
PIND.PIND2        2
PIND.PIND1        1
PIND.PIND0        0
DDRD             0x0011   Port D Data Direction Register
DDRD.DDD7         7   Port D Data Direction Register bit 7
DDRD.DDD6         6   Port D Data Direction Register bit 6
DDRD.DDD5         5   Port D Data Direction Register bit 5
DDRD.DDD4         4   Port D Data Direction Register bit 4
DDRD.DDD3         3   Port D Data Direction Register bit 3
DDRD.DDD2         2   Port D Data Direction Register bit 2
DDRD.DDD1         1   Port D Data Direction Register bit 1
DDRD.DDD0         0   Port D Data Direction Register bit 0
PORTD            0x0012   Port D Data Register
PORTD.PORTD7      7   Port D Data Register bit 7
PORTD.PORTD6      6   Port D Data Register bit 6
PORTD.PORTD5      5   Port D Data Register bit 5
PORTD.PORTD4      4   Port D Data Register bit 4
PORTD.PORTD3      3   Port D Data Register bit 3
PORTD.PORTD2      2   Port D Data Register bit 2
PORTD.PORTD1      1   Port D Data Register bit 1
PORTD.PORTD0      0   Port D Data Register bit 0
PINC             0x0013   Port C Input Pins Address
PINC.PINC7        7
PINC.PINC6        6
PINC.PINC5        5
PINC.PINC4        4
PINC.PINC3        3
PINC.PINC2        2
PINC.PINC1        1
PINC.PINC0        0
DDRC             0x0014   Port C Data Direction Register
DDRC.DDC7         7   Port C Data Direction Register bit 7
DDRC.DDC6         6   Port C Data Direction Register bit 6
DDRC.DDC5         5   Port C Data Direction Register bit 5
DDRC.DDC4         4   Port C Data Direction Register bit 4
DDRC.DDC3         3   Port C Data Direction Register bit 3
DDRC.DDC2         2   Port C Data Direction Register bit 2
DDRC.DDC1         1   Port C Data Direction Register bit 1
DDRC.DDC0         0   Port C Data Direction Register bit 0
PORTC            0x0015   Port C Data Register
PORTC.PORTC7      7   Port C Data Register bit 7
PORTC.PORTC6      6   Port C Data Register bit 6
PORTC.PORTC5      5   Port C Data Register bit 5
PORTC.PORTC4      4   Port C Data Register bit 4
PORTC.PORTC3      3   Port C Data Register bit 3
PORTC.PORTC2      2   Port C Data Register bit 2
PORTC.PORTC1      1   Port C Data Register bit 1
PORTC.PORTC0      0   Port C Data Register bit 0
PINB             0x0016   Port B Input Pins Address
PINB.PINB7        7
PINB.PINB6        6
PINB.PINB5        5
PINB.PINB4        4
PINB.PINB3        3
PINB.PINB2        2
PINB.PINB1        1
PINB.PINB0        0
DDRB             0x0017   Port B Data Direction Register
DDRB.DDB7         7   Port B Data Direction Register bit 7
DDRB.DDB6         6   Port B Data Direction Register bit 6
DDRB.DDB5         5   Port B Data Direction Register bit 5
DDRB.DDB4         4   Port B Data Direction Register bit 4
DDRB.DDB3         3   Port B Data Direction Register bit 3
DDRB.DDB2         2   Port B Data Direction Register bit 2
DDRB.DDB1         1   Port B Data Direction Register bit 1
DDRB.DDB0         0   Port B Data Direction Register bit 0
PORTB            0x0018   Port B Data Register
PORTB.PORTB7      7   Port B Data Register bit 7
PORTB.PORTB6      6   Port B Data Register bit 6
PORTB.PORTB5      5   Port B Data Register bit 5
PORTB.PORTB4      4   Port B Data Register bit 4
PORTB.PORTB3      3   Port B Data Register bit 3
PORTB.PORTB2      2   Port B Data Register bit 2
PORTB.PORTB1      1   Port B Data Register bit 1
PORTB.PORTB0      0   Port B Data Register bit 0
PINA             0x0019   Port A Input Pins Address
PINA.PINA7        7
PINA.PINA6        6
PINA.PINA5        5
PINA.PINA4        4
PINA.PINA3        3
PINA.PINA2        2
PINA.PINA1        1
PINA.PINA0        0
DDRA             0x001A   Port A Data Direction Register
DDRA.DDA7         7   Port A Data Direction Register bit 7
DDRA.DDA6         6   Port A Data Direction Register bit 6
DDRA.DDA5         5   Port A Data Direction Register bit 5
DDRA.DDA4         4   Port A Data Direction Register bit 4
DDRA.DDA3         3   Port A Data Direction Register bit 3
DDRA.DDA2         2   Port A Data Direction Register bit 2
DDRA.DDA1         1   Port A Data Direction Register bit 1
DDRA.DDA0         0   Port A Data Direction Register bit 0
PORTA            0x001B   Port A Data Register
PORTA.PORTA7      7   Port A Data Register bit 7
PORTA.PORTA6      6   Port A Data Register bit 6
PORTA.PORTA5      5   Port A Data Register bit 5
PORTA.PORTA4      4   Port A Data Register bit 4
PORTA.PORTA3      3   Port A Data Register bit 3
PORTA.PORTA2      2   Port A Data Register bit 2
PORTA.PORTA1      1   Port A Data Register bit 1
PORTA.PORTA0      0   Port A Data Register bit 0
EECR             0x001C   The EEPROM Control Register
EECR.EERIE        3   EEPROM Ready Interrupt Enable
EECR.EEMWE        2   EEPROM Master Write Enable
EECR.EEWE         1   EEPROM Write Enable
EECR.EERE         0   EEPROM Read Enable
EEDR             0x001D   EEPROM Data Register
EEARL            0x001E   EEPROM Address Register Low Byte
EEARH            0x001F   The EEPROM Address Register High
EEARH.EEAR8       8   EEPROM Address 8
UCSR0C           0x0020   USART Control and Status Register C (page 180)
UCSR0C.URSEL      7   Register Select
UCSR0C.UMSEL      6   USART Mode Select
UCSR0C.UPM1       5   Parity Mode 1
UCSR0C.UPM0       4   Parity Mode 0
UCSR0C.USBS       3   Stop Bit Select
UCSR0C.UCSZ1      2   Character Size 1
UCSR0C.UCSZ0      1   Character Size 0
UCSR0C.UCPOL      0   Clock Polarity
WDTCR            0x0021   Watchdog Timer Control Register
WDTCR.WDCE        4   Watchdog Change Enable
WDTCR.WDE         3   Watchdog Enable
WDTCR.WDP2        2   Watchdog Timer Prescaler 2
WDTCR.WDP1        1   Watchdog Timer Prescaler 1
WDTCR.WDP0        0   Watchdog Timer Prescaler 0
OCR2             0x0022   Output Compare Register
TCNT2            0x0023   Timer/Counter2
ICR1L            0x0024   Input Capture Register Low Byte
ICR1H            0x0025   Input Capture Register High Byte
ASSR             0x0026   Asynchronous Status Register
ASSR.AS2          3   Asynchronous Timer/Counter2
ASSR.TCN2UB       2   Timer/Counter2 Update Busy
ASSR.OCR2UB       1   Output Compare Register2 Update Busy
ASSR.TCR2UB       0   Timer/Counter Control Register2 Update Busy
TCCR2            0x0027   Timer/Counter Control Register
TCCR2.FOC2        7   Force Output Compare
TCCR2.WGM20       6   Waveform Generation Mode 0
TCCR2.COM21       5   Compare Match Output Mode 1
TCCR2.COM20       4   Compare Match Output Mode 0
TCCR2.WGM21       3   Waveform Generation Mode 1
TCCR2.CS22        2   Clock Select 2
TCCR2.CS21        1   Clock Select 1
TCCR2.CS20        0   Clock Select 0
OCR1BL           0x0028   Output Compare Register B Low Byte
OCR1BH           0x0029   Output Compare Register B High Byte
OCR1AL           0x002A   Output Compare Register A Low Byte
OCR1AH           0x002B   Output Compare Register A High Byte
TCNT1L           0x002C   Counter Register Low Byte
TCNT1H           0x002D   Counter Register High Byte
TCCR1B           0x002E   Timer/Counter1 Control Register B
TCCR1B.ICNC1      7   Input Capture Noise Canceler
TCCR1B.ICES1      6   Input CaptureEdgeSelect
TCCR1B.WGM13      4   Waveform Generation Mode 3
TCCR1B.WGM12      3   Waveform Generation Mode 2
TCCR1B.CS12       2   Clock Select 2
TCCR1B.CS11       1   Clock Select 1
TCCR1B.CS10       0   Clock Select 0
TCCR1A           0x002F   Timer/Counter1 Control Register A
TCCR1A.COM1A1     7   Compare Output Mode for channel A 1
TCCR1A.COM1A0     6   Compare Output Mode for channel A 0
TCCR1A.COM1B1     5   Compare Output Mode for channel B 1
TCCR1A.COM1B0     4   Compare Output Mode for channel B 0
TCCR1A.FOC1A      3   Force Output Compare for channel A
TCCR1A.FOC1B      2   Force Output Compare for channel B
TCCR1A.WGM11      1   Waveform Generation Mode 1
TCCR1A.WGM10      0   Waveform Generation Mode 0
SFIOR            0x0030   Special Function IO Register
SFIOR.TSM         7   Timer/Counter Synchronization Mode
SFIOR.XMBK        6   External Memory Bus Keeper Enable
SFIOR.XMM2        5   External Memory High Mask 2
SFIOR.XMM1        4   External Memory High Mask 1
SFIOR.XMM0        3   External Memory High Mask 0
SFIOR.PUD         2   Pull-up Disable
SFIOR.PSR2        1   Prescaler Reset Timer/Counter2
SFIOR.PSR310      0   Prescaler Reset Timer/Counter3, Timer/Counter1, and Timer/Counter0
OCR0             0x0031   Timer/Counter0 Output Compare Register
TCNT0            0x0032   Timer/Counter0
TCCR0            0x0033   Timer/Counter Control Register
TCCR0.FOC0        7   Force Output Compare
TCCR0.WGM00       6   Waveform Generation Mode 0
TCCR0.COM01       5   Compare Match Output Mode 1
TCCR0.COM00       4   Compare Match Output Mode 0
TCCR0.WGM01       3   Waveform Generation Mode 1
TCCR0.CS02        2   Clock Select 2
TCCR0.CS01        1   Clock Select 1
TCCR0.CS00        0   Clock Select 0
MCUCSR           0x0034   MCU Control and Status Register
MCUCSR.JTD        7   JTAG Interface Disable
MCUCSR.SM2        5   Sleep Mode Select Bit 2
MCUCSR.JTRF       4   JTAG Reset Flag
MCUCSR.WDRF       3   Watchdog Reset Flag
MCUCSR.BORF       2   Brown-out Reset Flag
MCUCSR.EXTRF      1   External Reset Flag
MCUCSR.PORF       0   Power-on Reset Flag
MCUCR            0x0035   MCU Control Register
MCUCR.SRE         7   External SRAM/XMEM Enable
MCUCR.SRW10       6   Wait State Select Bit
MCUCR.SE          5   Sleep Enable
MCUCR.SM1         4   Sleep Mode Select Bit 1
MCUCR.ISC11       3   Interrupt Sense Control 1 Bit 1
MCUCR.ISC10       2   Interrupt Sense Control 1 Bit 0
MCUCR.ISC01       1   Interrupt Sense Control 0 Bit 1
MCUCR.ISC00       0   Interrupt Sense Control 0 Bit 0
EMCUCR           0x0036   Extended MCU Control Register
EMCUCR.SM0        7   Sleep Mode Select Bit 0
EMCUCR.SRL2       6   Wait State Sector Limit 2
EMCUCR.SRL1       5   Wait State Sector Limit 1
EMCUCR.SRL0       4   Wait State Sector Limit 0
EMCUCR.SRW01      3   Wait-state Select Bits for Lower Sector 1
EMCUCR.SRW00      2   Wait-state Select Bits for Lower Sector 0
EMCUCR.SRW11      1   Wait-state Select Bits for Upper Sector
EMCUCR.ISC2       0   Interrupt Sense Control 2
SPMCR            0x0037   Store Program Memory Control Register
SPMCR.SPMIE       7   SPM Interrupt Enable
SPMCR.RWWSB       6   Read-While-Write Section Busy
SPMCR.RWWSRE      4   Read-While-Write Section Read Enable
SPMCR.BLBSET      3   Boot Lock Bit Set
SPMCR.PGWRT       2   Page Write
SPMCR.PGERS       1   Page Erase
SPMCR.SPMEN       0   Store Program Memory Enable
TIFR             0x0038   Timer/Counter Interrupt Flag Register
TIFR.TOV1         7   Timer/Counter1, Overflow Flag
TIFR.OCF1A        6   Timer/Counter1, Output Compare A Match Flag
TIFR.OCF1B        5   Timer/Counter1, Output Compare B Match Flag
TIFR.OCF2         4   Output Compare Flag 2
TIFR.ICF1         3   Timer/Counter1, Input Capture Flag
TIFR.TOV2         2   Timer/Counter2 Overflow Flag
TIFR.TOV0         1   Timer/Counter0 Overflow Flag
TIFR.OCF0         0   Output Compare Flag 0
TIMSK            0x0039   Timer/Counter Interrupt Mask Register
TIMSK.TOIE1       7   Timer/Counter1, Overflow Interrupt Enable
TIMSK.OCIE1A      6   Timer/Counter1, Output Compare A Match Interrupt Enable
TIMSK.OCIE1B      5   Timer/Counter1, Output Compare B Match Interrupt Enable
TIMSK.OCIE2       4   Timer/Counter2 Output Compare Match Interrupt Enable
TIMSK.TICIE1      3   Timer/Counter1, Input Capture Interrupt Enable
TIMSK.TOIE2       2   Timer/Counter2 Overflow Interrupt Enable
TIMSK.TOIE0       1   Timer/Counter0 Overflow Interrupt Enable
TIMSK.OCIE0       0   Timer/Counter0 Overflow Flag
GIFR             0x003A   General Interrupt Flag Register
GIFR.INTF1        7   External Interrupt Flag 1
GIFR.INTF0        6   External Interrupt Flag 0
GIFR.INTF2        5   External Interrupt Flag 2
GIFR.PCIF1        4   Pin Change Interrupt Flag 1
GIFR.PCIF0        3   Pin Change Interrupt Flag 0
GICR             0x003B   General Interrupt Control Register
GICR.INT1         7   External Interrupt Request 1 Enable
GICR.INT0         6   External Interrupt Request 0 Enable
GICR.INT2         5   External Interrupt Request 2 Enable
GICR.PCIE1        4   Pin Change Interrupt Enable 1
GICR.PCIE0        3   Pin Change Interrupt Enable 0
GICR.IVSEL        1   Interrupt Vector Select
GICR.IVCE         0   Interrupt Vector Change Enable
UCSR1C           0x003C   USART Control and Status Register C (page 180)
UCSR1C.URSEL      7   Register Select
UCSR1C.UMSEL      6   USART Mode Select
UCSR1C.UPM1       5   Parity Mode 1
UCSR1C.UPM0       4   Parity Mode 0
UCSR1C.USBS       3   Stop Bit Select
UCSR1C.UCSZ1      2   Character Size 1
UCSR1C.UCSZ0      1   Character Size 0
UCSR1C.UCPOL      0   Clock Polarity

SPL              0x003D   Stack Pointer Low
SPL.SP7           7
SPL.SP6           6
SPL.SP5           5
SPL.SP4           4
SPL.SP3           3
SPL.SP2           2
SPL.SP1           1
SPL.SP0           0
SPH              0x003E   Stack Pointer High
SPH.SP15          15
SPH.SP14          14
SPH.SP13          13
SPH.SP12          12
SPH.SP11          11
SPH.SP10          10
SPH.SP9           9
SPH.SP8           8
SREG             0x003F   Status Register
SREG.I            7   Global Interrupt Enable
SREG.T            6   Bit Copy Storage
SREG.H            5   Half Carry Flag
SREG.S            4   Sign Bit
SREG.V            3   Two's Complement Overflow Flag
SREG.N            2   Negative Flag
SREG.Z            1   Zero Flag
SREG.C            0   CarryFlag

Mais c’est pas tout. Ca serait trop simple. Si on charge un binaire on se retrouve avec des access à des variable qui sont non initialisées. C’est donc fortement pénalisant pour comprendre le code. D’autant que pour une simple led, il est de bon ton sur arduino de faire comme l’exemple et d’utiliser des variables :)

/*
  Blink
  Turns on an LED on for one second, then off for one second, repeatedly.

  This example code is in the public domain.
 */

// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);     
}

// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);               // wait for a second
}

Même si on a identifié la procédure setup et la procédure pinmode, la valeur de LED mise dans r24 restera un mystère.
avrnoref

Pour comprendre ce qui nous arrive, il faut un peut s’intéresser à comment marchent les cpu AVR. Ces CPUs sont basés sur une architecture Harvard, ca veut dire qu’il y a une mémoire pour le programme (la ROM) qui est complètement séparée de la RAM où sont les variables. La rom subsiste en cas de coupure d’électricité, mais évidemment pas la ram. Dans cette architecture, les programmes peuvent utiliser des variables en RAM, la ROM étant Read Only (ou presque, car cela se flash tout de même.)

Donc, la lecture/écriture c’est dans la ram. A coté de cela en plus il y a un espace mémoire nommé EEPROM qui permet d’écrire et de lire des data qui survivrons en cas de coupure électrique, un genre de disque dur quoi. Ici je ne m’en sert pas.

Grosso modo, pour ne pas perdre les quelques uns d’entre nous qui comprennent le x86 et qui ont connus le code en 16 Bits. c’est comme ca : Il y a 3 Segments. Un “CS” la Rom, Un “DS” la RAM, et un Extra “ES” l’eeprom. Ces segments ne se chevauchent pas, et celui de la RAM est remplis de 0 à chaque power on.

Dans le cas du AtMega328 on a 2k de Ram, 32k de Rom et 1K d’eeprom.

AVR_memory_01_lrg

Mais pour désassembler le code AVR c’est «chiant». Tous les programmes utilisent des références à des variables en RAM, mais la ram n’est pas initialisée chez nous, on n’a que un dump de la ROM. Alors dans la vrai vie, comment ca fonctionne;

Si on utilise l’environnement de développement Arduino ou simplement GCC pour avr, il y à une procédure nommée “__do_copy_data” qui au démarrage du programme s’occupe de recopier les variables initialisée de la ROM vers la RAM.

La ram est organisée de cette façons sur le AtMega328 :

0x0000:0x0020   General Purpose Working Registers
0x0020:0x0060   I/O registers
0x0060:0x0100   I/O registers extended
0x0100:0x08ff   Internal SRAM

Ouais je sais c’est chelou, Les 32 registres du CPU (R0 à R31) Sont en fait de la ram, après cela de 0x20 à 0x100 il y a les registres d’entrée sortie. C’est tous des registres un peu spéciaux par exemple en 0x5F c’est SREG que l’on connait nous sur x86 sous le nom de FLAGS.

C’est Seulement à partir de 0x100 on à de la vrai ram ou l’on peut faire ce que l’on veut. Mais voila, comment faire pour initialiser cette ram dans IDA et pouvoir reverser tranquille. GCC à la compilation pose les variables initialisées au cul du code, et donc pour chaque programme l’emplacement en ROM des variables change de place.

Et bien il faut “simplement” reverser “__do_copy_data” pour trouver l’emplacement des variables et leur taille. Si j’ai le courage, je pondrais un jour un python automatique.

Tout commence dans la fonction __RESET qu’IDA connait grace à notre cfg custom. (Sinon, elle est pointée par un saut au tout début de la rom). La première étape est de reseter les flags, et d’initialiser l’offset de la stack. La stack est sur 16 bits, et son pointeur est découpé en 2 registres 8 bits. SPH pour la partie haute du stack pointeur partie haute et SPL pour la partie basse. On retrouve ces deux pointeurs en RAM dans la partie I/0 Register 0x3D pour SPH et 0x3E pour SPH, mais ici aussi notre cfg fait un miracle.

ROM:0062                 ; public __RESET
ROM:0062 __RESET:                                ; CODE XREF: TIMER1_COMPB_0j
ROM:0062
ROM:0062 ; FUNCTION CHUNK AT ROM:04D2 SIZE 00000002 BYTES
ROM:0062
ROM:0062                 clr     r1
ROM:0063                 out     SREG, r1        ; Flags = 0
ROM:0064                 ser     r28             ; mov r28,0xff Set all bit
ROM:0065                 ldi     r29, 8          ; ldi= load immediate, mov quoi
ROM:0066                 out     SPH, r29        ; Stack point High
ROM:0067                 out     SPL, r28        ; Stack Pointer Low
ROM:0067                                         ; Now SP = 0x8ff

Alors c’est un truc auquel il va falloir s’habituer les enfants, les CPUs AVR aiment les couples de registres 8 Bits. Il y a même 3 couples célèbres qui font des registres 16 Bits. Le couple r26,r27 qui est nommé le registre X. r28,29 qui est Y et r30,r31 qui est Z. Ils appellent ca les registres indirects.

Nous voila enfin dans la partie juteuse. le couple r26,r27 est l’adresse de destination en RAM, le couple R30,31 est l’adresse sources de nos variables. Dans cet exemple c’est en 0x9a8.

ROM:0068                 ldi     r17, 1
ROM:0069                 ldi     r26, 0
ROM:006A                 ldi     r27, 1          ; X = 0x100
ROM:006B                 ldi     r30, 0xA8
ROM:006C                 ldi     r31, 9          ; Z = 0x9a8
ROM:006D                 rjmp    loop_end??      ; Relatif Jump

A partir de là c’est partis pour recopier byte à byte jusqu’a ce que X pointe 0x122.

ROM:006E loop_copy:                              ; CODE XREF: __RESET+10j
ROM:006E                 lpm     r0, Z+          ; en clair : Mov r0,[Z] ; Inc Z
ROM:006F                 st      X+, r0          ; mov [X],r0 ; Inc X
ROM:0070
ROM:0070 loop_end??:                             ; CODE XREF: __RESET+Bj
ROM:0070                 cpi     r26, 0x22
ROM:0071                 cpc     r27, r17        ; X = 0x122 ? double test part haute et basse.
ROM:0072                 brne    loop_copy       ; Si X != 0x122 on saute.
ROM:0073                 ldi     r17, 1
ROM:0074                 ldi     r26, 0x22       ; On remet X = 0x122
ROM:0075                 ldi     r27, 1
ROM:0076                 rjmp    loc_78

On a donc bien compris, l’initialisation copie 0x22 Bytes depuis ROM:0x9a8 vers RAM:0x100. Et bien on va se créer un fichier “RAM” identique au programme qui se serait déjà fait “initialiser” et on l’utilisera darns IDA.

Etape 1 on crée un pad de bytes à 0 qui à une taille de 0x100 (256 en décimal).

$ dd if=/dev/zero of=/tmp/RAM.bin count=256 bs=1
154+0 records in
154+0 records out
154 bytes (154 B) copied, 0.000453577 s, 340 kB/s

Ensuite on extrait de la rom nos 0x22 bytes tant convoités et on les colle au cul de la ram.

$ dd if=/tmp/RepulsCAT.cpp.bin skip=2472 bs=1 count=34 >> /tmp/RAM.bin
34+0 records in
34+0 records out
34 bytes (34 B) copied, 0.000128219 s, 265 kB/s

Et enfin on pad le reste de cette ram avec du 0. ( 2Ko – (256 + 34)) = 1758

$ dd if=/dev/zero bs=1 count=1758 >> /tmp/RAM.bin
1758+0 records in
1758+0 records out
1758 bytes (1.8 kB) copied, 0.0037651 s, 467 kB/s

Et on se retrouve alors avec un joli fichier représentant une ram initialisée.

$ ls -l /tmp/RAM.bin
-rw-r--r-- 1 thanat0s thanat0s 2048 Jan 23 10:42 /tmp/RAM.bin
$ hexdump -C /tmp/RAM.bin
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000100  41 41 41 41 41 41 41 41  41 41 00 03 00 02 00 04  |AAAAAAAAAA......|
00000110  00 00 00 00 00 ae 03 53  04 44 03 75 03 55 03 9e  |.......S.D.u.U..|
00000120  03 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000800

Ne reste plus qu’a la mettre en place dans IDA en lieu et place de cette ram non initialisée. Pour cela dans IDA on fait : View/Open Subview/Segment. On delete le segment nommé RAM.

delram

On fait FILE/Load File/Load additionnal File et on sélectionne notre RAM.bin. On charge le segment en 0x10000 tout le reste à 0 et on décoche “Code Segment”

Il ne reste plus qu’a éditer les attributs du segment pour faire joli et changer le segment name en RAM et le segment class en DATA et désormais, les variables sont initialisée !! Tous est vachement plus clair.

after

Voila, il n’y a plus qu’a désosser maintenant. le grand fichier des opcodes est là.

Have Fun !

This entry was posted in Reverse and tagged , , , . Bookmark the permalink.

3 Responses to Loader un binaire Arduino dans IDA

  1. Pingback: Riscure RHme2: FridgeJIT – Reverse Engineering Challenge – Capture the Swag

  2. Matthieu says:

    Salut et merci pour ton tuto. Désolé de up après si longtemps le sujet …
    Je l’ai suivi avec attention afin de créer un fichier RAM cohérent avec mon programme.
    Alors premier point, le fichier .cfg que j’utilise est légèrement différent du tien (dispo ici: https://github.com/SilverBut/avr_helper).
    Ensuite, au moment de créer le fichier RAM, je respecte bien la taille et les registres de mon dump mais après avoir remplacer ce segment dans IDA ça ne change pas grand chose au niveau de l’initialisation des variables. Es-ce que j’ai manqué une étape après le changement de segment?
    PS : dans ta commande $ dd if=/dev/zero of=/tmp/RAM.bin count=256 bs=1
    tu as comme outputs :
    154+0 records in
    154+0 records out

    ça devrait pas être 256 ?

    Merci,
    Matt

Leave a Reply

Your email address will not be published. Required fields are marked *

AlphaOmega Captcha Classica  –  Enter Security Code