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.
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.
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.
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.
Voila, il n’y a plus qu’a désosser maintenant. le grand fichier des opcodes est là.
Have Fun !
Pingback: Riscure RHme2: FridgeJIT – Reverse Engineering Challenge – Capture the Swag
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
Bien expliqué! Jai fait lextructeur de RAM en python
https://github.com/Sfeeen/Arduino-ATMEGA328-binary-reversing