ARM9Loader -- Technical Details and Discussion

Discussion in '3DS - Homebrew Development and Emulators' started by Selver, Jan 6, 2016.

  1. Selver

    Selver 13,5,1,14,9,14,7,12,5,19,19

    Dec 22, 2015
    This thread is for an in-depth technical review of the ARM9Loader hack.

    These first posts will be edited to reflect updated information from community members. Credit will be given for corrections in the changelog, unless they were given to me via PM under condition of anonymity.

    Table of Contents:

    Introduction, BootRom, ARM9Loader Theory
    ARM9Loader v1
    ARM9Loader v2 (1/3 and 2/3)
    ARM9Loader v2 (3/3), Conclusions
    OTP dumping progress post / summary
    Plailect's OTP Dumping Guide (external link)
    Arm9LoaderHax source code (GitHub link)
    Summary of differences from prior *hax (1/3)
    Summary of differences from prior *hax (2/3)
    Summary of differences from prior *hax (3/3)

    The description is initially based on the presentation by plutoo at 32C3, on December 27, 2015, in combination with information from the 3DS HomeBrew site,, some of my own ideas, and lots of my own ignorance thrown in. (In other words, errors are likely!)

    The end goal is for a deep understanding of how and why the security broke down. Interestingly, if Nintendo had fixed some well-documented low-impact security issues (e.g., not clearing RAM on reboot, March 2014; etc.), then ARM9Loader might not have been practically exploitable.

    The jobs of the BOOTROM include to set some super-secret encryption keys, load firmware into memory, validate it's cryptographic signature, and BRANCH (JMP) to the firmware's entry point.

    The 3DS includes both a primary firmware (FIRM0) and a second firmware (FIRM1). The second firmware (FIRM1) is used when the first firmware (FIRM0) fails the cryptographic signature check for any reason. This makes sense, as it provides a fail-safe for situations that cannot be entirely eliminated (e.g., power-loss during update FIRM0, NAND sector corruption, etc.).

    Conceptually, the BootRom does the following:
    1. Lots of top-secret stuff first
    2. Load FIRM0 firmware from NAND (firmware partition) into memory
    3. Check the cryptographic signature of the loaded FIRM0 in memory
    4. If the cryptographic signature is verified, jump to FIRM0's entrypoint
    5. Else....
    6. Load FIRM1 firmware from NAND into memory
    7. Check the cryptographic signature of the loaded FIRM1 in memory
    8. If the cryptographic signature is verified, jump to FIRM1's entrypoint
    9. Else... PANIC (no towel)

    Minor BootROM flaw
    If the cryptographic signature of a firmware fails validation, the bootrom does not clear the memory that was written to when loading that (invalid) firmware. This allows FIRM0 to be used to load attacker-controlled contents ... up to the size of the largest Nintendo-signed firmware (because header must be signed before loading that size firmware).

    So, if FIRM1 is smaller in size than FIRM0, then the attacker can still control the contents of at least some memory during FIRM1's loading.

    ARM9Loader Theory
    a.k.a. How it is supposed to work

    ARM9Loader is an additional layer of security which was added only to the N3DS. The ARM9Loader is part of the firmware binary, and thus will only execute if the firmware itself was properly signed. The purposes of the ARM9Loader include using additional console-unique data to:
    • further decrypt the KERNEL9 binary (using OTP & sector 150)
    • perform additional key initialization prior to handing control to Process9
    The general theory of ARM9Loader operation:
    1. Calculate OTP_HASH
      • SHA256 hash of the OTP areas
      • As you know, the OTP registers are console-unique data
      • OTP appears to be *THE* foundational secret for ARM9Loader
      • OTP_HASH was not cleared in some early firmware...
    2. Read encrypted sector 150 (0x96)
      • Decrypt the sector using the OTP_HASH
      • Use a 16-byte blob as Normal-key for keyslot 0x11
    3. Generate dependent keys using that keyslot
      • Dependent keys are generated using a cryptographically secure one-way function
      • Thus, the exposure of any dependent keys would not expose the parent key
      • For example, first 16 bytes used for keyslots 0x15 and 0x18
    4. Verify the keyslot (e.g., keyslot 0x11) by encrypting a fixed test-vector
      • This prevents accidental use of a corrupted NAND sector
    5. Clear the keyslot (e.g., keyslot 0x11)
      • This prevents any later use of the keyslot, such as by ARM9, to regenerate the dependent keys
    6. Decrypt the ARM9 binary
    7. Jump to the ARM9 entrypoint
    Last edited by Selver, Jul 22, 2016

  2. Selver

    Selver 13,5,1,14,9,14,7,12,5,19,19

    Dec 22, 2015
    Arm9Loader v1

    There are three versions of Arm9Loader (as of firmware 11.0.0-33), named v1, v1.1, and v2. Each is described in turn...

    ARM9Loader v1 exploit

    Arm9Loader version 1 is found on N3DS firmware versions < 9.5.9-X.

    Version 1 uses a portion of sector 150 as an encrypted key (Key #1). This is only mentioned as contrast with version 2, because neither Kernel9 nor ARM9Loader v1 clear keyslot 0x11.

    Because the ARM9Loader and Kernel9 fail to clear keyslot 0x11, all of keyslot 0x11's dependent keys can be regenerated as soon as ARM9 execution is attained. As you can deduce, once a keyslot is in use by software to store data, the keyslot's initialization vectors (e.g., KeyX, KeyY) cannot easily be changed without risk of breaking software that relied upon that keyslot.

    Attackers gained access to keys that depend on keyslot 0x11, for firmware 9.5.0-X and lower.

    Neither the OTP nor the SHA256 hash of the OTP was exposed. Therefore, a v.Next firmware could simply generate a different initialization value for a (currently unused) keyslot, and depend on that new value. This is exactly what occurred, in fact, for 9.6.0-X, which used another keys from the special NAND sector, still decrypted by the hash of the OTP. At first glance, one might think that sector 150 would provide the basis for many keys.

    ARMLoader v1.1
    v1.1 comes only with N3DS firmware version 9.5.9-X. The only fix is that it clears keyslot 0x11.
    Last edited by Selver, Sep 19, 2016 - Reason: First line to match contents, for featured posts summary
  3. Yangarang

    Yangarang GBAtemp Regular

    Nov 14, 2015
    Commenting to see further comments/updates
  4. TR_mahmutpek

    TR_mahmutpek GBAtemp Advanced Fan

    Jul 28, 2015
    This means new kernel exploits coming?
    Pacheko17 likes this.
  5. Selver

    Selver 13,5,1,14,9,14,7,12,5,19,19

    Dec 22, 2015

    Arm9Loader v2 (1/3)

    Version 2 is found on N3DS firmware versions 9.6.0-X through at least 11.0.0-X (most current at time of posting).

    As noted above, the bootrom won't branch to the firmware entry point unless the firmware was properly cryptographically signed by Nintendo. Arm9Loader is part of the firmware, but the remainder of the Arm9 firmware is encrypted. Arm9Loader reads an additional key from the NAND sector 150 to decrypt the encrypted Arm9 firmware.

    With version 2, Arm9Loader has a bug where it does not perform the validation of the resulting decrypted key against a known test vector. As a result, if sector 150 of the NAND contains invalid data, even with 100% valid signed FIRM partition contents, Arm9Loader will decrypting the firmware into garbage due to the incorrect key and (even though the decrypted data is cryptographically random) still branch to the entrypoint address.

    Presumption: The FIRM1/FIRM0 loading is part of the bootrom, and thus cannot be updated without a hardware update.

    This results in what is technically called "Cryptographically Random Data" being executed, instead of the actual firmware. Now, by itself, this wouldn't seem to be useful, because the probability that the decrypted instructions would result in an exploit are 1/(an astronomically large number).

    However, the 3DS has two tiny weaknesses, which greatly magnify the above bug.
    #1. The BOOTROM does not clear the memory used by FIRM0 when it fails to be validated
    #2. The RAM is not cleared between reboots (known for over a year)

    How does #1 help an attacker?
    Consider having FIRM0 containing valid header data, but otherwise filled with NOP sleds and a payload at the end of what will be loaded. The attacker can select the precise instructions in the payload, as they are loaded directly by the BOOTROM. Of course, this is normally not useful, as the digital signature will then be invalid, and thus the firmware won't execute.

    Now add in a totally valid FIRM1, but one which is smaller in size than FIRM0. FIRM1 will also load into memory, but all the instructions from FIRM0 that were beyond FIRM1's valid size will remain in memory (they were loaded as part of the failed digital signature validation for FIRM0). Again, this also would normally not be useful, because FIRM1 (being a digitally signed file) will contain official firmware....

    However, because Arm9Loader version 2 fails to verify the key (from sector 150) that it uses to decrypt the firmware is valid, that allows the key to be modified by an attacker. The key from sector 150 is encrypted using the SHA256 hash of the console-unique OTP. The attacker will be unable to predict the decrypted key, because the attacker does not know either the OTP nor the SHA256 hash of the OTP. Even so, the attacker can force a random 0x11 key (and relying subkeys) to be generated. This will result in the wrapped firmware being decrypted to cryptographically random data, and then (!) ARM9Loader will BRANCH right into the middle of that cryptographically random data.

    Ok, but that still doesn't seem really useful, right? I mean, branching into cryptographically random instructions is useless, right? Especially when it requires a unique brute-forced key to be determined for each console, right?

    ... to be continued ...

    ARM9Loader v2 -- 2/3

    [Note that this section was written very early January 2015 ... before downgrading to obtain the OTP was possible ... and thus considers if brute-force per-console attacks were practical]

    As noted above, the size of FIRM0 >> the size of FIRM1, which caused the BOOTROM to load attacker-selected instructions into memory. That's the target space for a randomly-decrypted instruction stream to BRANCH into. Alternatively, with a HARDMOD, the attacker could modify the special sector of NAND at or near the time that they write all memory, presumptively with attack code at the end of memory, plus either NOP Sleds or BRANCH sleds to increase the chance a random BRANCH instruction will get to the attack code. Because memory is not initialized to zero on reboot, this memory remains as set by the attacker. (On failure, attacker could modify the special NAND sector and reboot, or try all steps again.)

    The probability of an exception (invalid memory dereference, invalid instruction) for any randomly-generated instruction is relatively close to 1/8. Thus, each boot will execute (on average) eight random instructions before throwing an exception. (Sum from N=1 to infinity of 7/(8**N) == 8, per Wolfram Alpha).

    The probability of one of those eight random instructions being a BRANCH instruction is about one in eight (bits 27-25 == 101b), of which ~1/2 will have the right CONDition bits set, presuming randomness. Thus, a rough estimate is a 50% chance that a BRANCH instruction will execute for any random decryption key.

    Will that BRANCH instruction point to valid memory? Surprisingly, the answer is, quite likely yes. The branch instruction is +/- 32MB (24 bits). Can an attacker force FIRM0 to be at least 32MB greater in size than FIRM1? Presumably yes, as the digital signature need not be valid for FIRM0. This is actually a limiting factor, as the header must have a valid digital signature, which limits available FIRM sizes.

    So, there is approximately a 50% chance (per boot with random decryption key) of a valid BRANCH instruction being executed. Some portion will jump to another location that's filled with cryptographically random instructions (within FIRM1's size). How big is the firmware image in memory? 50% * (1 - (sizeof firmware)/256MB) == chance of hitting memory left over from prior boot, and running the exploit code.

    Update: Per @173210, the ARM9 partition in 9.5 NATIVE_FIRM is 566784 bytes. This results in:
    50% * (1 - (566784 / 1048576)) = .5 * (1 - .5405) = .5 * .46 = 23 % chance.

    Wow... this is actually much more serious than I would have initially thought.
    With a hardmod, it appears this would only take about eight writes (16 if restoring the NAND sector and resetting memory with BRANCH sled and attack code each time), before a value is found that will reliably jump to exploit code. Once found, that value can reliably be re-used on that N3DS.

    Update #2: Actually, what is important is not the size of FIRM0, but how much larger it is than FIRM1. This is because most of FIRM0 is overwritten by FIRM1, leaving a much more narrow target for the JMP. However, that JMP can be to any location, because the very next instruction left over from FIRM0 can, itself, be a JMP instruction. Rather academic at this point, but...

    Last edited by Selver, Sep 7, 2016 - Reason: added update #2 to correct overly optimistic estimates
  6. peteruk

    peteruk GBAtemp Maniac

    Jun 26, 2015
    thanks for posting this, very interesting read
  7. Selver

    Selver 13,5,1,14,9,14,7,12,5,19,19

    Dec 22, 2015

    ARM9Loader v2 exploit (3/3)

    OK, so based on the above, an attacker could do the following:

    1. Hardmod of N3DS that's running ARM9Loader v2
    2. Backup NAND using hardmod
    3. Boot, use exploit such MemChunkHax2 to...
      • Put attack code at the end of memory
      • Fill memory addresses with BRANCH sled to the attack code
    4. Halt everything for a period of time
      • Prevents console sending commands to eMMC
      • During the halt period, external commands overwrite the special NAND sector to result in a corrupt KEY#2
    5. After the halt period of time, reboot (leaving memory contents intact)
    6. Allow exploit time to work, wait for the BRANCH into attack code...
    7. If it fails, restore original value of special NAND sector
      • Could presume memory not substantially modified by prior run, and just modify the special nand sector again
    8. Lather, rinse, repeat....

    But why bother, especially when other hacks already exist?

    • MemChunkHax2 + Downgrade to ARM9Loader v1 + firmlaunch-hax ==> generating all <= v9.5.0-X firmware keyX values
    • ARM9Loader v2 + arm9loaderhax ==> pre-ARM9Kernel ARM9 code execution, dumping of RSA keyslots, calculating 6.x save, calculating the 7.x NCCH keys, (per @AHP_person , ARM9Loader locks the OTP registers before jumping to the garbage decrypted firmware, so no OTP access :sad: ), ...
    • Decrypted OTP registers ==> ??? Because SHA256 hash of the OTP is used to decrypt the special NAND sector, could knowing the OTP data allow one to create a desired value for the decrypted key ???

    OTP register access has been disabled early on by Kernel9 (or now ARM9Loader) since firmware 3.0.0-X.
    Last edited by Selver, Jul 22, 2016
  8. AHP_person

    AHP_person GBAtemp Fan

    Nov 2, 2014
    United States
    This is fairly accurate, although, the OTP registers are unfortunately locked before the jump to the arm9 binary.

    There's not much else to complain about in your rundown. It's nice to see people putting this info out there, so nice work! :D
  9. peteruk

    peteruk GBAtemp Maniac

    Jun 26, 2015
    I am forever the optimist and am hoping by the sharing of these kind of informations will perhaps one day lead to either an emuNAND above 9.5 for N3DS users or even a direct CFW through sysNAND

    I can live in hope and these type of threads can only help
  10. Ekaitz

    Ekaitz Pokémon Master

    Jun 13, 2010
    Even it is released, hardmode seems mandatory.
    Anyway, that would be great at least to get new keys.
  11. Selver

    Selver 13,5,1,14,9,14,7,12,5,19,19

    Dec 22, 2015
    Thank you for this correction, AHP_Person. I've updated with attribution.
    AHP_person likes this.
  12. TR_mahmutpek

    TR_mahmutpek GBAtemp Advanced Fan

    Jul 28, 2015
    Will we have n3ds emunand 9.5+?

    Sorry for this noob question...

    — Posts automatically merged - Please don't double post! —

    Edit: Dont troll with blue moon card!
  13. guitarheroknight

    guitarheroknight 1.6180339887

    Nov 9, 2014
    Grand Line
    TR_mahmutpek and peteruk like this.
  14. peteruk

    peteruk GBAtemp Maniac

    Jun 26, 2015
    You seem very certain, i hope your right, it's been a long time coming :)
  15. guitarheroknight

    guitarheroknight 1.6180339887

    Nov 9, 2014
    Grand Line
    I am because I saw it, I mean at least I think I did. There was a YouTube video showing off the capabilities of ntrhax from the SALT team or whatever they wish to call themselves and they had emunand on the latest version on a New 3DS. Now if a amateur group like them could figure out how to achieve it Im sure that the veterans like GW will surely able to do exactly the same, maybe even a community member who knows but its comming all right ;) Thats what she said.
    peteruk likes this.
  16. Vappy

    Vappy GBAtemp Advanced Maniac

    May 23, 2012
    This would allow for emuNAND 9.6+, as long as it's launched from sysNAND 9.6+. And then if Nintendo change to a different keyslot with an update, you'll be roadblocked again.
    Suiginou and peteruk like this.
  17. Suiginou

    Suiginou (null)

    Jun 26, 2012
    Gambia, The
    pc + 8
    Wouldn't having a short arm9loaderhax payload that simply loads other NATIVE_FIRMs into memory ands runs their boot process solve the problem?
    peteruk likes this.
  18. peteruk

    peteruk GBAtemp Maniac

    Jun 26, 2015
    Ah yes, i saw that video too :)

    Yep this is the big problem, not sure i woould really want to upgrade my sysnand to 9.6 either, unless i had a hard mod, we need someone reliable in the UK doing N3DS mods i think
  19. Syphurith

    Syphurith Beginner

    Mar 8, 2013
    Xi'an, Shaanxi Province
    Thanks for posting this. Quite a lot of details related. @AlbertoSONIC @motezazer @173210 @Steveice10 @TuxSH @d0k3
    Sorry for bothering all of you. However I have some obstacles about it to share.
    1,This deserves a HARD-MOD. Since only with Hard-mod can you overwrite the raw NAND sections, doing the modification. - At least currently
    2.Decrytion attempts, and yes OTP. I don't know how they dumped the OTP for N3DS. Also the exploit <=2.2 is for O3DS. You must control what it produces!
    3.Raw NAND access. Your code would be loaded before FIRM (if you firmlaunch it) so no fread9/fwrite9/... Even dumping something needs different APIs then.
    4.Proper keys. You know you need it decrypted correctly to do a firmlaunch, or yes, an older decrypted already firm.
    And, the actual result for an enhanced arm9loader hax is CFW on 9.3+ SysNAND. Not the EmuNAND directly, orz.

    — Posts automatically merged - Please don't double post! —

    Well i have something to append.
    1.FIRM section is 0xEF000 size, and is in .firm format, so not a bootrom, sorry for that. For my O3DS no size difference between FIRM0 and FIRM1.
    And the max ever possible space for it is 4MB.
    2.You can not run code on NAND due to its complexity of altering data on it. So those must be loaded to memory first.
    3.However the slides may have a side-effect on it. There is another exploit, that after HARD-RESET, memory isn't cleared.
    So you might set it up before the calling, temporarily. I doubt if that is from ntrcardhax.
    Well but on the video it is cold booted. So you can leave this alone now.. Orz.
    Last edited by Syphurith, Jan 6, 2016
    clank and peteruk like this.
  20. 173210

    173210 GBAtemp Regular

    Jan 22, 2014
    The real exception vector is located in bootrom and to jump the another vector at the top of ARM9 RAM.
    If arm9loader doesn't initialize the vector in RAM, we can use it to execute code.

    The loaded FIRM will be copied in ARM9 internal memory, so you should assume the space is 1MB instead of 256MB.
    The size of the ARM9 partition in 9.5 NATIVE_FIRM is 566784 bytes (553.5KB).
    ITCM is close to ARM9 memory, but I don't take it into account because it's used by bootrom.
    So, 50 * (1 - 566784 / 1048576) = 23 %
    Hmm, not so bad.

    If you take exception into account, the calculation will be more complex. I won't do that because I'm not good at math. :P

    It's easy if you have ARM9 access. In rxTools, call tmio_writesectors(TMIO_DEV_NAND, ....). But hardmod is necessary for multiple attacks.

    Reading/writing NAND is quite a easy because NAND is initialized to read FIRM, but you don't have filesystem.

    You can load anything from NAND.

    — Posts automatically merged - Please don't double post! —

    Wait, if you can control the exception vector, any code except calling itself or resetting ARM9 should be OK. The probability is very high.
    Selver, Syphurith and Gocario like this.