A7V600 BIOS Reverse engineering

Reversed engineering BIOS version : Award 1008

Assembleur de fou

Au démarrage de la machine, la première instruction exécutée est celle placée en 0xF000FFF0, qui contient généralement un far jmp

Avant que le BIOS vérifie qu'il y ait de la RAM, aucune mémoire n'est accessible, toute l'exécution se fait directement depuis la ROM du BIOS (mappée en mémoire en F000) : il n'y a pas de pile !

Pour appeler une fonction, on simule une pile lors de la compilation, exemple :

  F000E0DF  BCE5E0            mov sp, 0xe0e5  // prepare call
  F000E0E2  E910F2            jmp 0xf210      // (relative)
  F000E0E5  E7E0              dw 'e0e7'       // return data
  F000E0E7  3C5A              cmp al,0x5a

Toujours sans pile, il est difficile de sauvegarder les registres avant un appel à une fonction. Comme l'exécution se fait en mode réel 16bits, on n'utilise que rarement la partie haute des registres 32bits, ils servent alors à la sauvegarde, exemple :

  bswap esp         // echange les octets de poids faibles avec les octets de poids forts
  ...               // un code qui n'utilise que des registres 16 bits ...
  bswap esp         // on restaure l'état initial

Désassemblage du BIOS, avec annotations partielles, chercher "ENTRY_POINT"

Flashage du BIOS ASUS

La première question est comment flasher le BIOS sous Linux ? Le projet LinuxBios a developpé un petit utilitaire de flashage du BIOS. Problème : sur la carte mère ASUS A7V600, ce programme ne marche pas. Le BIOS peut être lu, mais pas modifié. Un certain nombre de protections logicielles doit être contourné.

L'accès à la mémoire flash du BIOS est très protégée. Ce document explique comment contourner les protections anti-flash sur les cartes mères ASUS.

Côté Southbridge

Le southbridge protège généralement le flashage du BIOS. C'est le cas sur cette carte mère. Le VT8237 permet de protéger l'accès en écriture à la mémoire flash. Par défaut, l'utilitaire flash_and_burn de LinuxBios? ne parvient même pas à détecter le type de puce du BIOS. Le type et sous-type est toujours FFh. Après recherche, le code suivant permet de désactiver la protection côté SouthBridge.

  static int enable_flash_vt8237(struct pci_dev *dev, char *name)
  {
        unsigned char val;
  
        val = pci_read_byte(dev, 0x40);
        val |= 0x10;
        pci_write_byte(dev, 0x40, val);
  
        if (pci_read_byte(dev, 0x40) != val) {
                printf("tried to set 0x%x to 0x%x on %s failed (WARNING ONLY)\n",
                       0x40, val, name);
                return -1;
        }
        return 0;
  }

Avec les documentations sous NDA, il s'avère que le bit 4 du registre 40h du périphérique PCI 17.0 permet de faire basculer la protection "ROM Write".

En exécutant ce code avant flash_and_burn, il permet au programme de bien reconnaître la puce du BIOS. En l'occurence une "pm49fl002". Mais toujours aucune écriture possible ...

Côté carte mère

Une autre protection est donc activée quelque part ... au niveau de la carte mère, du southbridge, ou autre. La première idée qui vient pour trouver comment désactiver les protection logicielles consiste à aller regarder comment font les logiciels de flashage. Et en particulier celui livré avec la carte mère. Malheureusement, le désassemblage est très difficile, puisque c'est un outil semi-graphique et que la plupart du code contient des routines graphiques et de saisie. De plus, une tentative de recherche sur le code de désactivation logicielle du northbridge ne donne rien ...

En réalité, le code de (dé-)protection est installé en mémoire et est appelé par les programmes de flashage.

Sur les cartes mères ASUS, le BIOS installe en mémoire une routine de (dé-)protection. Pour permettre de la retrouver, elle est repérée par une en-tête particulière:

  struct AsusFlashHeader
  {
    char sign[16];            /* "ASUS_FLASH" */
    unsigned char version[2];
    unsigned long enableP;   
    unsigned long disableP;
  };

La signature "ASUS_FLASH" permet de faire une recherche exhaustive en mémoire sur le segment F000h (réel). Les pointeurs enableP et disableP pointent respectivement sur le code de protection et le code de déprotection.

Le fichier find_asus_flash.cc permet de trouver les adresses mémoires des routines de protection et déprotection.
  roxorus:/home/ten0k/projets/LinuxBIOSv2/util/flash_and_burn# ./find_asus_flash 
  ASUS Flash version 1.3 disable : 0xF0003580 enable : 0xF000358F

Les routines se trouvent donc dans le segment F000h. Ces adresses sont indiquées sous forme de segment:offset, typique du mode réel. Cela correspond en mode protégée aux adressent Fxxxxh Il y a sans doute un moyen de les appeler directement. En désassemblant le segment F0000h, on trouve facilement les codes.

  dd if=/dev/mem skip=`printf "%d" 0xF0000` of=segF000.bin bs=1 count=65535
  ndisasm -o 0xF0000 segF000.bin > segF000.asm

La routine de déprotection est la suivante :

  @ENABLE_E42C:   
  00005C23  BA2CE4            mov dx,0xe42c
  00005C26  EC                in al,dx
  00005C27  24FE              and al,0xfe ;  bit 1 = 0
  00005C29  EE                out dx,al
  00005C2A  C3                ret

Mettre à 0 le bit 1 du port 0xE42C

  @ENABLE_ROM_WRITES:     
  00005C08  66B840880080      mov eax,0x80008840 ; 17.0 reg 40
  00005C0E  BAF80C            mov dx,0xcf8
  00005C11  66EF              out dx,eax
  00005C13  B2FC              mov dl,0xfc
  00005C15  EC                in al,dx
  00005C16  0C10              or al,0x10
  00005C18  EE                out dx,al
  00005C19  C3                ret

Mettre à 1 le bit 4 du registre 40h du PCI 17.0

  @ENABLE_372:    
  00005EC1  BA7203            mov dx,0x372
  00005EC4  EC                in al,dx
  00005EC5  0C04              or al,0x4 ;  bit 2 = 1
  00005EC7  EE                out dx,al
  00005EC8  C3                ret

Mettre à 1 le bit 2 du port 0x372

Le routine de protection est la suivante :

On retrouve bien le code de protection du southbridge. En revanche, on trouve aussi des accès aux port 0xE42C et 0x372 ...

Problème de démarrage

D'après la documentation officielle d'Intel, la première instruction executée est celle située en mémoire à l'adresse FFFFFFF0 (F000:FFF0 en mode réel). Il y a généralement un far jmp vers les premières routines.

EAX contient l'état d'initialisation du processeur. 1 si erreur.

Note: Sous linux, la commande reboot doit relancer le processeur. Il semble que soit executée aussi l'instruction à l'adresse FFFFFFF0. Comment distinguer alors le code de reboot du code de démarrage ?

Sur la carte A7V600, il semble qu'il faille d'autres conditions pour faire booter la machine. En effet, après essais, une mémoire flash vide (remplie de FFh) ne contenant qu'un jmp vers une routine faisant un beep ne fait ... rien. La modification d'un BIOS d'origine en incrustant un beep semble pourtant marcher. Il semble donc que la carte mère (quel chipset ??) lise des données ou execute un code de préinitialisation AVANT la première instruction officielle !

En mettant un beep dans une image de BIOS officielle et en vidant des blocs mémoire par dichotomie, apparait un bloc nécessaire en F000:7C00:

  0x21, 0x31, 0x24, 0x71, 0x5b, 0x7b, 0x02, 0x09, 0x21, 0x31, 0x25, 0x71, 0x5b, 0x7b, 0x02, 0x09,
  0x21, 0x31, 0x25, 0x71, 0x5b, 0x7b, 0x02, 0x09, 0x21, 0x31, 0x26, 0x71, 0x5b, 0x7b, 0x02, 0x09,
  0x22, 0x19, 0x23, 0xc9, 0x9e, 0x7b, 0x02, 0x09, 0x22, 0x19, 0x24, 0xc9, 0x9e, 0x7b, 0x02, 0x09,
  0x21, 0x19, 0x25, 0xc9, 0x9a, 0x7b, 0x02, 0x09, 0x21, 0x19, 0x26, 0xc9, 0x9a, 0x7b, 0x02, 0x09,
  0x21, 0x21, 0x20, 0x59, 0x5b, 0x7b, 0x02, 0x09, 0x21, 0x21, 0x21, 0x59, 0x5b, 0x7b, 0x02, 0x09,
  0x21, 0x21, 0x21, 0x59, 0x5b, 0x7b, 0x02, 0x09, 0x21, 0x21, 0x22, 0x59, 0x5b, 0x7b, 0x02, 0x09,
  0x21, 0x29, 0x22, 0xe9, 0x5b, 0x7b, 0x02, 0x09, 0x21, 0x29, 0x23, 0xe9, 0x5b, 0x7b, 0x02, 0x09,
  0x21, 0x29, 0x23, 0xe9, 0x5b, 0x7b, 0x02, 0x09, 0x21, 0x29, 0x24, 0xe9, 0x5b, 0x7b, 0x02, 0x09,
  0x24, 0x11, 0x22, 0x39, 0xea, 0x2b, 0x02, 0x09, 0x6b, 0x11, 0x20, 0x39, 0xa6, 0x4b, 0x02, 0x09,
  0x6a, 0x11, 0x21, 0x39, 0xa6, 0x4b, 0x02, 0x09, 0x6a, 0x11, 0x22, 0x39, 0x9e, 0x53, 0x02, 0x09,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xd9, 0x30, 0x24, 0x71, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x30, 0x25, 0x71, 0x5f, 0x53, 0x02, 0x09,
  0xd9, 0x30, 0x25, 0x71, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x30, 0x26, 0x71, 0x5f, 0x53, 0x02, 0x09,
  0xda, 0x18, 0x23, 0xc9, 0xa2, 0x53, 0x02, 0x09, 0xda, 0x18, 0x24, 0xc9, 0xa2, 0x53, 0x02, 0x09,
  0xda, 0x18, 0x25, 0xc9, 0xa2, 0x53, 0x02, 0x09, 0xda, 0x18, 0x26, 0xc9, 0xa2, 0x53, 0x02, 0x09,
  0xda, 0x20, 0x20, 0x59, 0x63, 0x53, 0x02, 0x09, 0xda, 0x20, 0x21, 0x59, 0x63, 0x53, 0x02, 0x09,
  0xda, 0x20, 0x21, 0x59, 0x63, 0x53, 0x02, 0x09, 0xda, 0x20, 0x22, 0x59, 0x63, 0x53, 0x02, 0x09,
  0xd9, 0x28, 0x22, 0xe9, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x28, 0x23, 0xe9, 0x5f, 0x53, 0x02, 0x09,
  0xd9, 0x28, 0x23, 0xe9, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x28, 0x24, 0xe9, 0x5f, 0x53, 0x02, 0x09,
  0x24, 0x01, 0x22, 0x19, 0xe9, 0x2b, 0x02, 0x49, 0x6b, 0x11, 0x20, 0x39, 0xa6, 0x2b, 0x02, 0x09,
  0x6b, 0x11, 0x21, 0x39, 0xa6, 0x2b, 0x02, 0x09, 0x6a, 0x11, 0x22, 0x39, 0xa2, 0x2b, 0x02, 0x09,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xd9, 0x30, 0x24, 0x71, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x30, 0x25, 0x71, 0x5f, 0x53, 0x02, 0x09,
  0xd9, 0x30, 0x25, 0x71, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x30, 0x26, 0x71, 0x5f, 0x53, 0x02, 0x09,
  0xda, 0x18, 0x23, 0xc9, 0xa2, 0x53, 0x02, 0x09, 0xda, 0x18, 0x24, 0xc9, 0xa2, 0x53, 0x02, 0x09,
  0xda, 0x18, 0x25, 0xc9, 0xa2, 0x53, 0x02, 0x09, 0xda, 0x18, 0x26, 0xc9, 0xa2, 0x53, 0x02, 0x09,
  0xda, 0x20, 0x20, 0x59, 0x63, 0x53, 0x02, 0x09, 0xda, 0x20, 0x21, 0x59, 0x63, 0x53, 0x02, 0x09,
  0xda, 0x20, 0x21, 0x59, 0x63, 0x53, 0x02, 0x09, 0xda, 0x20, 0x22, 0x59, 0x63, 0x53, 0x02, 0x09,
  0xd9, 0x28, 0x22, 0xe9, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x28, 0x23, 0xe9, 0x5f, 0x53, 0x02, 0x09,
  0xd9, 0x28, 0x23, 0xe9, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x28, 0x24, 0xe9, 0x5f, 0x53, 0x02, 0x09,
  0x24, 0x01, 0x22, 0x19, 0xe9, 0x2b, 0x02, 0x49, 0x6b, 0x11, 0x20, 0x39, 0xa6, 0x2b, 0x02, 0x09,
  0x6b, 0x11, 0x21, 0x39, 0xa6, 0x2b, 0x02, 0x09, 0x6a, 0x11, 0x22, 0x39, 0xa2, 0x2b, 0x02, 0x09,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xd9, 0x30, 0x24, 0x71, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x30, 0x25, 0x71, 0x5f, 0x53, 0x02, 0x09,
  0xd9, 0x30, 0x25, 0x71, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x30, 0x26, 0x71, 0x5f, 0x53, 0x02, 0x09,
  0xda, 0x18, 0x23, 0xc9, 0xa2, 0x53, 0x02, 0x09, 0xda, 0x18, 0x24, 0xc9, 0xa2, 0x53, 0x02, 0x09,
  0xda, 0x18, 0x25, 0xc9, 0xa2, 0x53, 0x02, 0x09, 0xda, 0x18, 0x26, 0xc9, 0xa2, 0x53, 0x02, 0x09,
  0xda, 0x20, 0x20, 0x59, 0x63, 0x53, 0x02, 0x09, 0xda, 0x20, 0x21, 0x59, 0x63, 0x53, 0x02, 0x09,
  0xda, 0x20, 0x21, 0x59, 0x63, 0x53, 0x02, 0x09, 0xda, 0x20, 0x22, 0x59, 0x63, 0x53, 0x02, 0x09,
  0xd9, 0x28, 0x22, 0xe9, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x28, 0x23, 0xe9, 0x5f, 0x53, 0x02, 0x09,
  0xd9, 0x28, 0x23, 0xe9, 0x5f, 0x53, 0x02, 0x09, 0xd9, 0x28, 0x24, 0xe9, 0x5f, 0x53, 0x02, 0x09,
  0x24, 0x01, 0x22, 0x19, 0xe9, 0x2b, 0x02, 0x49, 0x6b, 0x11, 0x20, 0x39, 0xa6, 0x2b, 0x02, 0x09,
  0x6b, 0x11, 0x21, 0x39, 0xa6, 0x2b, 0x02, 0x09, 0x6a, 0x11, 0x22, 0x39, 0xa2, 0x2b, 0x02, 0x09,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

Et en F000:FFD0, un autre :

00 7C FF FF 2E 8B C0 2E 8B C0 2E 8B C0 2E 8B C0

La signification de ces données reste un mystère ...