host-aprom/docs/DRM.md
2024-08-13 03:21:30 +01:00

3.9 KiB

DRM

DJ DAO implemented two forms of DRM within the TASOLLER firmware.

  1. The bootloader on the Host and LED microcontrollers make slight alterations to the uploaded binary
  2. The stock Host firmware reads 4 bytes of memory from the bootloader firmware to validate it

Host DRM

Magic number

Flash address 100FF8 (LDROM+FF8) must contain the value 5555A320. The stock bootloader contains the following code to write this magic value:

#define BOOTLOADER_MAGIC ((uint32_t)0x5555A320)
#define BOOTLOADER_MAGIC_ADDR ((uint32_t)0x100FF8)

void main(void) {
    SYS_UnlockReg();
    SYS_Init();

    FMC_Open();
    g_apromSize = GetApromSize();
    GetDataFlashInfo(&g_dataFlashAddr, &g_dataFlashSize);

    uint32_t u32FMCCheck;
    FMC_Write(BOOTLOADER_MAGIC_ADDR, BOOTLOADER_MAGIC);
    FMC_Read(BOOTLOADER_MAGIC_ADDR, &u32FMCCheck);
    if (u32FMCCheck != BOOTLOADER_MAGIC) {
        FMC_Erase_User(BOOTLOADER_MAGIC_ADDR);
        FMC_Write(BOOTLOADER_MAGIC_ADDR, BOOTLOADER_MAGIC);
    }

    // ...
}

Stock APROM firmware then contains

void SYS_Bootloader_Check(void) {
    FMC_Open();
    while (FMC_Read(BOOTLOADER_MAGIC_ADDR) != BOOTLOADER_MAGIC)
        ;
    FMC_Close();
}

void main(void) {
    SYS_UnlockReg();
    SYS_Init();
    SYS_Bootloader_Check();

    // ...
}

In our custom APROM, defining ENABLE_BOOTLOADER_CHECK (tasoller.h) will perform this check. Our custom bootloader (not included in this repository) uses a custom scatter file to include this magic value, safely asserting that the code does not overflow into these bytes.

Firmware modification

The following bytes in the uploaded firmware are modified before writing them to flash:

Offset XOR value
C0 FF
C1 FF
C2 FF
CC 2F

These values are specifically chosen as the end of the vector table. The code at this offset is part of ARMC5's initialisation code, with __main being the entrypoint jumped to after register initialisation.

__main:
_main_stk:
    LDR     R0, =__initial_sp   ; Offset C0~C1
    MOV     SP, R0              ; Offset C2~C3

_main_scatterload
    BL      __scatterload_rt2   ; Offset C4~C7

__main_after_scatterload:
_main_clock:
_main_cpp_init:
_main_init:
    LDR     R0, =main           ; Offset C8~C9
    BR      R0                  ; Offset CA~CB

    DCD     main                ; Offset CC~CF, auto-generated by =main

Note that our custom firmware is compiled using GCC rather than ARMC5. Our linkerscript is not configured to place any initialisation code after the vectors, and as such this protection mechanism would be likely to cause random crashes rather than a complete inability to execute the firmware.

Rather than implement this as a post-processing step after compiling firmware, our linkerscript is configured to place the compile timestamp within this region, and we account for the DRM modifications when comparing timestamps.

LED DRM

Magic number

Flash address 1011F8 (LDROM+11F8) must contain the value 5555AAAA. The stock bootloader contains the following code to write this magic value:

#define BOOTLOADER_MAGIC ((uint32_t)0x5555AAAA)
#define BOOTLOADER_MAGIC_ADDR ((uint32_t)0x1011F8)

void main(void) {
    SYS_UnlockReg();
    SYS_Init();

    FMC->ISPCTL |= FMC_ISPCTL_ISPEN_Msk;
    g_apromSize = GetApromSize();
    GetDataFlashInfo(&g_dataFlashAddr, &g_dataFlashSize);

    FMC_Write_User(BOOTLOADER_MAGIC_ADDR, BOOTLOADER_MAGIC)

    // ...
}

No corresponding check appears to be implemented in the LED APROM firmware, however.

Firmware modification

The following bytes in the uploaded firmware are modified before writing them to flash:

Offset XOR value
C0 FF
C1 FF
C2 FF

The disassembly at these offsets is the same as with the host APROM. Likewise, we opt to bypass this protection by placing the compile timestamp in this region.