Bootloader

From Vita Developer wiki
Jump to navigation Jump to search

On the vita there are several bootloaders (stage bootloaders).

One (or probably 2 stages boot) is used to boot to the TEE (Secure world) and one to load the kernel in the normal world.

Some bootloaders are obfuscated via ARZL algorithm.

SLB2 Image

SLB2 is the format used to store the encrypted bootloaders on the device.

ENC Files

Offset Size Description
0x0 0x4 0x64B2C8E5 magic
0x4 0x4 Offset to data
0x8 0x4 Unknown
0xC 0x4 Unknown/Zero
0x10 0x4 Data size
0x14 0xC Unknown
0x20 0x20 Hash ?
0x40 0x10 Version in ASCII
0x50 0x90 Zero
0xE0 Until Data Encrypted Header

The last 0x340 bytes is a footer that likely contains a signed MAC of the plaintext data.

ARZL

ARZL is a (standard ?) compressed format which is used by the secure bootloader to load the non-secure bootloader and the secure kernel.

Obfuscation

The raw decompressed ARZL output is obfuscated. Although there are three versions of the obfuscation, the basic operation is the same. The obfuscation is just bit swaps as well as some deterministic changes using information from the offset.

int arzl_deobfuscate(unsigned char *buffer, int len)
{
  unsigned char *buf, *bufend;
  uint32_t data;
  int change_stride;

  buf = buffer;
  bufend = &buffer[len];

  do
  {
    data = *(uint32_t *)buf;
    buf += 4;
    change_stride = (data & 0xF800F800) >> 27;
    if ( (data & 0xF800F800) == 0xF800F000 )
    {
      data = (((data >> 16) & 0xFFC007FF) | ((data & 0x7FF) << 11)) - ((buf - buffer) >> 1);
      *((uint32_t *)buf - 1) = ((((data & 0x7FF) << 16) | 0xF800F000) & 0xFFFFF800) | ((data >> 11) & 0x7FF);
    }
    else if ( change_stride == 30 )
    {
      buf -= 2;
    }
  }
  while ( bufend > buf );
}

Version 1

The only change is that the offset information is added instead of subtracted.

int arzl_deobfuscate(unsigned char *buffer, int len)
{
  unsigned char *buf, *bufend;
  uint32_t data;
  int change_stride;

  buf = buffer;
  bufend = &buffer[len];

  do
  {
    data = *(uint32_t *)buf;
    buf += 4;
    change_stride = (data & 0xF800F800) >> 27;
    if ( (data & 0xF800F800) == 0xF800F000 )
    {
      data = (((data >> 16) & 0xFFC007FF) | ((data & 0x7FF) << 11)) + ((buf - buffer) >> 1);
      *((uint32_t *)buf - 1) = ((((data & 0x7FF) << 16) | 0xF800F000) & 0xFFFFF800) | ((data >> 11) & 0x7FF);
    }
    else if ( change_stride == 30 )
    {
      buf -= 2;
    }
  }
  while ( bufend > buf );
}

Version 2

Version 2 is the same as version 0 but in addition, there's an additional operation to swap two nibbles in certain conditions. The condition is found through a learning process and may be overfitted.

int arzl_deobfuscate(unsigned char *buffer, int len)
{
  unsigned char *buf, *bufend;
  uint32_t data;
  int change_stride;

  buf = buffer;
  bufend = &buffer[len];

  do
  {
    data = *(uint32_t *)buf;
    buf += 4;
    change_stride = (data & 0xF800F800) >> 27;
    if ( (data & 0xF800F800) == 0xF800F000 )
    {
      data = (((data >> 16) & 0xFFC007FF) | ((data & 0x7FF) << 11)) + ((buf - buffer) >> 1);
      *((uint32_t *)buf - 1) = ((((data & 0x7FF) << 16) | 0xF800F000) & 0xFFFFF800) | ((data >> 11) & 0x7FF);
    }
    else if ( (data & 0x8000FBF0) == 0x0000F2C0 )
    {
      data = (data & 0xF0FFFFF0) | ((data & 0xF) << 24) | ((data >> 24) & 0xF);
      *((uint32_t *)buf - 1) = data;
    }
    else if ( change_stride == 30 )
    {
      buf -= 2;
    }
  }
  while ( bufend > buf );
}