Archos 5 First Stage Bootloader

From ArchosDocs

Jump to: navigation, search

Description

The first stage bootloader (boot0) is located at offset 0 in flash memory. After boot, it's the first piece of code to be executed. When disassembling, you should map the file at offset 0x08000000. Download your copy of the IDA database, or the signature file for this stage here.

In short, here's what you need to know: Boot0 sets up parts of the memory, then jumps to the next stage verification function check_boot1() (reverse engineering was focused on this function). The verification is done using the RSA key located at 0x10034 in flash memory. If the verification succeeds, execution continues at 0x8030100 (in boot1), otherwise execution is directed to an infinite loop. Most of the function names you see below were taken from avos since most of the signature checking code is exactly the same; it was most likely compiled from the same source code.

The Code

int g_flash_address; // address is 0x4020004c
 
void set_flash_address(int address) {
g_flash_address = address;
}
 
// At some point in boot0, this function is called. The rest is uninteresting.
void check_boot1()
{
set_flash_address(0x8000000);
 
if(verify_ZMfX(0x8030000) == 0) {
// Jump to address 0x8030100
}
 
while(1) {
// verification failed, loop forever...
}
}
 
// verify that the data at address is validly signed, and has the magic ZMfX
int verify_ZMfX(void *address) {
 
return verify_hash(NULL, address, 0x5a4d6658 /* "ZMfX" */, 0x8000000);
}
 
// return the address of the key at 0x10034 relatively to the base of flash memory
int get_10034_key() {
return g_flash_address + 0x10034;
}
 
struct Biginteger {
int bits;
char data[260];
};
 
struct Key {
Biginteger a; // 0
Biginteger b; // 264
};
 
struct RSAContext {
Key key;
Biginteger sign; // rsa-encrypted md5 hash
} mpk_key; // address is 0x40201374
 
void Biginteger_Init(Biginteger *bigint)
{
memset(&bigint->data, 0, 260);
bigint->bits = 0;
 
}
 
int Bigkey_Init(Key *key)
{
Biginteger_Init(&key->a);
Biginteger_Init(&key->b);
return 0;
}
 
// Loads the data from key_data into the Key structure
int Key_LoadMPK(Key *key, char *key_data)
{
int counter;
 
Bigkey_Init(key);
 
if(key_data == NULL)
return 1;
 
 
key->a.bits = (*(uint16_t *)key_data) << 4;
key->b.bits = (*(uint16_t *)&key_data[130]) << 4;
 
if(key->a.bits > 1024) // unsigned
return 1;
 
if(key->b.bits > 1024) // unsigned
return 1;
 
counter = 0;
do {
int offset, j;
 
j = (uint16_t)(counter++);
Cond = j < (key->a.bits >> 4);
if(!Cond) break;
 
offset = (j << 1);
*(uint16_t *)&key->a.data[offset] = *(uint16_t *)&key_data[offset+2];
}
 
counter = 0;
do {
int offset, j;
 
j = (uint16_t)(counter++);
Cond = j < (key->b.bits >> 4);
if(!Cond) break;
 
offset = (j << 1);
*(uint16_t *)key->a.data[offset] = *(uint16_t *)&key_data[offset+132];
}
 
return 0;
}
 
// implementation of memcmp as found in boot0
int memcmp(char *s1, char *s2, int size) {
int i, r = 0;
 
for(i=0;i != size;i++) {
if(s1[i] == s2[i])
r += 1;
}
 
return r;
}
 
// This is where the magic happens.
int verify_hash(void *key, void *address, int magic, int flash_address)
{
if(*(uint32_t *)address != magic)
return 1;
 
if(key == NULL) // key is null, try to get default key
key = get_10034_key();
 
if(key == NULL) // key is still null, bail out
return 1;
 
// load the key data into a proper Key structure
if(Key_LoadMPK(&mpk_key.key, key) != 0)
return 1;
 
// Load the signature data into a Biginteger structure
Biginteger_Init(&mpk_key.sign);
mkp_key.data.bits = 1024;
memcpy(&mpk_key.sign.data, &address[8], 128);
 
// Decipher the signature using the key
RSADecipher(&mpk_key.key, &mpk_key.sign);
 
// Start address of the signed data.
addr1 = address+136;
 
// make sure the data to be checksumed is inside flash memory
if(addr1 < flash_address)
return 1;
if(addr1 >= flash_address+0x400000)
return 1;
 
// load claimed image size
size = *(uint32_t *)(address+140);
if(size == 0)
return 1;
 
// make sure the claimed image size is inside flash memory
size = size - 136;
if(size == 0)
return 1;
if(addr1+size > flash_address+0x400000)
return 1;
 
// checksum boot1
MD5Digest(1, 0, 0, 0);
MD5Digest(2, addr1, size, 0);
MD5Digest(3, 0, 0, SP);
 
// leftover debug output.
nullsub_2("verified hash", SP);
nullsub_2("reference hash", &mkp_key.sign);
 
// Return 0 if the verified hash is the same as the reference hash.
return (memcmp(SP, &mpk_key.sign, 16) != 0);
}
Personal tools