Secure Kernel: Difference between revisions
No edit summary |
No edit summary |
||
Line 41: | Line 41: | ||
This small stub clears everything in f00d-mem in the region 0x00800100-0x8080FF0. | This small stub clears everything in f00d-mem in the region 0x00800100-0x8080FF0. | ||
Unknown why last 0x10 bytes are not cleared. | Unknown why last 0x10 bytes are not cleared. | ||
== F00D messages == | |||
These are sent to ARM using the lower 16-bits at 0xE0000000. | |||
When ARM has read it, it is set to 0. | |||
ARM can write a 32-bit response to 0xE0000010. | |||
For ARM->F00D, bit0 is used to indictate the message was written by ARM. | |||
<pre> | |||
1 = Request succeded | |||
4 = Debug string | |||
0x101 = Main init started | |||
0x102 = Sm can be loaded/resumed | |||
0x103 = Sm resumed successfully | |||
0x104 = Sm was shut down | |||
0x106 = Main shutting down | |||
0x107 = Suspend beginning | |||
0x108 = Ready for suspending, when using the async version. | |||
0x8016 = Error: Invalid address range | |||
0x802F = Error: Failed to init E003, E006. | |||
</pre> | |||
== Debug prints == | |||
secure_kernel supports tracing to a buffer. | |||
Enabled by -7FF8h($gp) being non-zero. | |||
Out-buf address is stored in -7FF4($gp). | |||
It writes in a loop, 16 bytes at a time, inserting a null-terminator at buf[15] each "line". | |||
After out-buf is written, writes 0x20000 to 0xE0000000. | |||
This will either signal ARM or disable ARM communications. | |||
Then inf-loop, this is a panic function. | |||
=== Print types === | |||
Addresses are xored with a stack cookie that's fixed for all functions. | |||
<pre> | |||
SuspendEncrypt BadAddr: 00000003 | |||
SWI 1 BadAddr: 00000004 | |||
SWI 1 UnreachableCode: 00000006 | |||
SWI 7 UnreachableCode: 00000007 | |||
Addrcheck IntegerOverflow Food: 0000000B | |||
Addrcheck IntegerOverflow Kernel: 0000000B | |||
Addrcheck IntegerOverflow Module: 0000000C | |||
IRQ register func w irq enabled: 0000000E | |||
Force exit dmac w irq enabled: 0000000F | |||
Crypto irq enabled: 00000010 | |||
Resuming suspendbuf w irq enabled: 00000012 | |||
Creating suspendbuf w irq enabled: 00000013 | |||
Reset: 00000014 <xored-exception-lr> <xored-exception-pc> <exception-lr> | |||
RI: 00000015 <xored-exception-pc> | |||
ZDIV: 00000016 <xored-exception-pc> | |||
Trace: 00000017 <func-addr> | |||
SWI 7: 00000018 <xored-r1> | |||
DMAC when updating suspendbuf key: 00000019 <ret-val> | |||
DMAC cmac bad enum: 0000001A <enum-value> | |||
Bad enum to suspend AES-CBC function: 0000001B <enum-value> | |||
Generating new suspendbuf key failed: 0000001C | |||
Creating suspendbuf w irq8 module-registered: 0000001D | |||
Addrcheck IntegerOverflow Tz: 0000001E | |||
Addrcheck IntegerOverflow Tz2: 0000001F | |||
Bad ptr to suspend AES-CBC function: 00000020 | |||
IRQ register func w bad irq number: 00000022 | |||
Recieved unknown IRQ: 00000023 | |||
</pre> | |||
== SWI == | |||
When swi-handler is entered stack is set to 0x00807CC0. | |||
It stores context (all regs) on this stack. | |||
Then stack is set to 0x00807C40 from which it loads SPRs $tp, $gp, $sp. | |||
After SWI has been executed, it restores context from first stack. | |||
in_r4 = Syscall number, starting with 1. | |||
There are only 8 syscalls. | |||
Syscall numbers >= 8 are ignored by the handler and returns error 0x800F032C. | |||
== IRQ == | |||
When irq-handler is entered stack is set to 0x00807B40. | |||
Then it reads irq number from control bus space using the ldcb instruction. | |||
Irq number must be < 12, otherwise panic. | |||
Then it sets up a special "user irq" context: | |||
sp = 0x0080B000 | |||
tp = userctx.tp | |||
gp = userctx.gp | |||
<rest same as kernel irq context> | |||
And calls the function with r1=irq_id. | |||
The table is empty in the dump. | |||
Above holds for all irqs except 8, which is special, see below. | |||
Just before calling the callback, interrupts are enabled again. | |||
This leads to reentracy vulnerabilities. | |||
== IRQ8 == | |||
IRQ8 is the 8th interrupt. | |||
This is the one triggered when ARM sends a request to F00D using 0xE0000010. | |||
And secure_kernel has a handler for this one. | |||
== IRQ9 == | |||
This interrupt is sent when ARM writes to sm cmd registers (0xE0000014 and maybe more?). It's handled inside every sm module. | |||
Revision as of 02:22, 28 February 2018
Entrypoint
The entrypoint of secure_kernel is +0x100. First thing it does is disable irq. Then it zeroes the bss segment 8 bytes at a time.
It sets up $sp to 0x808FF0, and $gp to 0x80F8A8. Then it sets dbg::TraceEnableFlag to *0xE005003C.
In the dump trace was enabled, and buf_ptr was 0x4002C160.
Then it sets up cfg:
* Clears bit4 (EVM), this moves exception vectors to 0x00800000. * Sets bit3 (IVM), this enables interrupt table at 0x00800030.
Then it copies a jmp instruction to the reset-vector, unsure why.
Then it calls a function that sets up icache: If icache size is 0, function just returns without doing anything. It also bails if icache line width is < 2 or >= 5 (reserved values).
Then it calls a function at 0x400B0, which is an uncached code that does:
- Enable icache.
- ORs 0x400 into CFG (this is "reserved" according to datasheet!).
Then it calls a function that just iterates through an empty table of function pointers. C++ object initialization?
Then it calls main().
Then it calls a function that just iterates through an empty table of function pointers, again. C++ object dtors.
Finally goes into death-mode:
Writes a jump instruction to inf-loop to the reset-vector. This works because jmp instruction encoding contains an abs address.
Sets $lp = inf_sleep_loop.
Then it sets up args and jumps to a small stub at 0x008000E0. This small stub clears everything in f00d-mem in the region 0x00800100-0x8080FF0. Unknown why last 0x10 bytes are not cleared.
F00D messages
These are sent to ARM using the lower 16-bits at 0xE0000000. When ARM has read it, it is set to 0.
ARM can write a 32-bit response to 0xE0000010. For ARM->F00D, bit0 is used to indictate the message was written by ARM.
1 = Request succeded 4 = Debug string 0x101 = Main init started 0x102 = Sm can be loaded/resumed 0x103 = Sm resumed successfully 0x104 = Sm was shut down 0x106 = Main shutting down 0x107 = Suspend beginning 0x108 = Ready for suspending, when using the async version. 0x8016 = Error: Invalid address range 0x802F = Error: Failed to init E003, E006.
Debug prints
secure_kernel supports tracing to a buffer.
Enabled by -7FF8h($gp) being non-zero. Out-buf address is stored in -7FF4($gp). It writes in a loop, 16 bytes at a time, inserting a null-terminator at buf[15] each "line".
After out-buf is written, writes 0x20000 to 0xE0000000. This will either signal ARM or disable ARM communications. Then inf-loop, this is a panic function.
Print types
Addresses are xored with a stack cookie that's fixed for all functions.
SuspendEncrypt BadAddr: 00000003 SWI 1 BadAddr: 00000004 SWI 1 UnreachableCode: 00000006 SWI 7 UnreachableCode: 00000007 Addrcheck IntegerOverflow Food: 0000000B Addrcheck IntegerOverflow Kernel: 0000000B Addrcheck IntegerOverflow Module: 0000000C IRQ register func w irq enabled: 0000000E Force exit dmac w irq enabled: 0000000F Crypto irq enabled: 00000010 Resuming suspendbuf w irq enabled: 00000012 Creating suspendbuf w irq enabled: 00000013 Reset: 00000014 <xored-exception-lr> <xored-exception-pc> <exception-lr> RI: 00000015 <xored-exception-pc> ZDIV: 00000016 <xored-exception-pc> Trace: 00000017 <func-addr> SWI 7: 00000018 <xored-r1> DMAC when updating suspendbuf key: 00000019 <ret-val> DMAC cmac bad enum: 0000001A <enum-value> Bad enum to suspend AES-CBC function: 0000001B <enum-value> Generating new suspendbuf key failed: 0000001C Creating suspendbuf w irq8 module-registered: 0000001D Addrcheck IntegerOverflow Tz: 0000001E Addrcheck IntegerOverflow Tz2: 0000001F Bad ptr to suspend AES-CBC function: 00000020 IRQ register func w bad irq number: 00000022 Recieved unknown IRQ: 00000023
SWI
When swi-handler is entered stack is set to 0x00807CC0. It stores context (all regs) on this stack. Then stack is set to 0x00807C40 from which it loads SPRs $tp, $gp, $sp. After SWI has been executed, it restores context from first stack.
in_r4 = Syscall number, starting with 1.
There are only 8 syscalls. Syscall numbers >= 8 are ignored by the handler and returns error 0x800F032C.
IRQ
When irq-handler is entered stack is set to 0x00807B40. Then it reads irq number from control bus space using the ldcb instruction. Irq number must be < 12, otherwise panic.
Then it sets up a special "user irq" context:
sp = 0x0080B000 tp = userctx.tp gp = userctx.gp <rest same as kernel irq context>
And calls the function with r1=irq_id. The table is empty in the dump.
Above holds for all irqs except 8, which is special, see below.
Just before calling the callback, interrupts are enabled again. This leads to reentracy vulnerabilities.
IRQ8
IRQ8 is the 8th interrupt. This is the one triggered when ARM sends a request to F00D using 0xE0000010. And secure_kernel has a handler for this one.
IRQ9
This interrupt is sent when ARM writes to sm cmd registers (0xE0000014 and maybe more?). It's handled inside every sm module.
Secure Kernel Commands
Handler starts with a switch statement that handles reset commands.
For a command 0x100401 0x10 is size of shared buffer that's used by f00d handler, 0x4 is command ID, 0x1 is validity flag(?).
None of the following 5 commands are enabled/disabled depending on f00d-state. These are all unconditional.
0xB01
Reset ScePervasiveResetReg +0x190 to enable re-writing the mask for the protected memory (keyring) Set mask to 0x10 for slots 0x20E and 0x20F (set in 0xE0070008)and trigger reset with 0 and 1 to device control 0xE0070000
0xC01
Reads protected mem/keyring slot 0x50C. If lower 4 bits are nonzero: Reset ScePervasiveResetReg +0x190 to enable re-writing the mask for the protected memory (keyring) Set mask to 0x10 for slots 0x20E and 0x20F (set in 0xE0070008)and trigger reset with 0 (not 1 this time) to device control 0xE0070000
0xD01
Reads protected mem/keyring slot 0x50C. If lower 4 bits are nonzero: Does same thing as 0xB01.
0xE01
Does same thing as 0xB01. Then reads 3 times from ScePervasiveMisc (0xE3100000). It reads three times, if first read != 0x20, second != 0x30, third != 0x31 then it writes 6 to 0xE0070014.
0xF01: GetEncryptedInfoBlk
It encrypts a block of size 0x80 with key=eeprom_blk_515, and hardcoded iv from .data.
Block looks like this: +0x00: Magic (0xACB4ACB1) +0x04: One +0x08: Random (read from 0xE005003C) +0x0C: Zero +0x10: EEPROM sector 0x511 +0x30: EEPROM sector 0x512 +0x50: EEPROM sector 0x517 +0x70: AES-256-CMAC using key from EEPROM sector 0x514.
It memcpys this encrypted info-blk size 0x80 to 0x4001FF00. Then it programs (u32)1, followed by zeroes, to EEPROM sector 0x516.
After processing, 0xFFFFFFFF is written to 0xE0000010. Then comes the real switch. This one gives different func-ptrs. But before func-ptr is called there's a check in a table-lookup based on f00d-state. So not all cmds are allowed in all states. If not allowed error 0x8029 is sent.
Then it reads lower u16 of f00d mailbox. If all zeroes then it returns 0x802D. Then it reads lower u16 and now wants it to be 0, if not it returns 0x802D.
After this it finally calls the funcptr for cmdhandler. For unknown cmdid it returns 0x802A.
The allowed commands are as follows:
State: 0 Allowed: 0 State: 1 Allowed: 0 State: 2 Allowed: 2 State: 3 Allowed: 78E State: 4 Allowed: 2 State: 5 Allowed: 72 State: 6 Allowed: 42 State: 7 Allowed: 2 State: 8 Allowed: 2 State: 9 Allowed: 0 State: 10 Allowed: 0
todo: Decode these.
0x101: ArmPanic
Sets food-state to 9. This will later cause main() to return, triggering memclr + infloop.
Then it writes 0xF to control bus addr 0. Control bus addr 0 is interrupt controller, but bits don't match architecture doc. Corrupt code? Followed by 3 NOPs. Wat.
0x500201: LoadModule
Reads 0x50 bytes from ArmBuffer into buf. If *(buf+4) and *(buf+8) are not aligned to 4 it fails. Then it TZ-checks addr *(buf+4) size 4. Then it TZ-checks addr *(buf+4) size 8. If any of these 3 checks fail, return code is 0x8016 or panic.
Then it sets food-state to 4.
memcpy(sp+0x68, buf+0x30, 0x20); *(sp+0x58) = *(buf+0x28) *(sp+0x5C) = *(buf+0x2C) *(sp+0x178) = *(buf+0x24) *(sp+0x54) = *(buf+0x20)
Then it calls the big function to load the SM with args (sp, sp+0x50). If it fails then either panics or returns 0x800000FF.
It saves the address for "0x40-buf", without checking it to be in Tz. This is okay because they check before writing to it.
Then it gets the address for the "shared buf", and loads arg0-arg3 from this ptr.
Then it sets epc to 0x0080B000, sp to 0, gp to 0. And uses reti to jump to it.
0x100301: RestoreModule
Reads 0x10 bytes from shared buf. Checks alignment on stuff also. TZ-check on *(buf+4) size 4, *(buf+8) size 8, *(buf+12) size 0x18. On fail sends 0x8016. After that it sets state to 4.
Then it calls the big restore function.
If it fails it sends zeroes module region, sends reply 0x8024, sets state to 3, and sends 0x102. If success, it sets the "0x40-buf" to *(buf+8), sets state to 5, and sends back 0x103.
0x100401: RequestModuleSuspend
Reads 0x10 bytes from shared buf. Verifies alignment on *(buf+4), *(buf+8), *(buf+12). Verifies the following tz-ptrs: *(buf+8) size 4 *(buf+4) size 8 *(buf+12) size 0x18 If bad it returns 8016.
Sets f00d-state to 6. If sm is not ready for suspend, it saves the buf in bss. Also sets the flag that suspend has been requested then returns.
If sm is ready to suspend, it calls the suspend function.
0x501: SubscribeSuspendAsyncEvent
Sets f00d-state to 6. If sm is ready to be suspended, sends 0x108 and move into state 7.
0x601: ForceExitModule
Sets f00d state to 6. Then it calls a function that: * Calls a function that appears to stop DMAC. * Zeroes the module region. * Flushes cache. * Writes some unknown DMAC registers. * Does a soft-reset.
0x80901: SetTraceBuffer
Shared buf: +0: Addr +4: Size
Checks that region is valid TZ, then sets them in the state. On bad addr, it sends error 0x8016, otherwise it sends 1. It also prints "rev %s\n" with "5679", however that only happens for some OTP configs.
0x80A01: SetRevocationList
Reads 8 bytes from "shared buf". Calls the function to set the revocation list. If that function returns error x, the error that gets sent back is x&0xFF. On success, 1 is sent back.
Debug prints
secure_kernel supports tracing to a buffer.
Enabled by -7FF8h($gp) being non-zero. Out-buf address is stored in -7FF4($gp). It writes in a loop, 16 bytes at a time, inserting a null-terminator at buf[15] each "line".
After out-buf is written, writes 0x20000 to 0xE0000000. This will either signal ARM or disable ARM communications. Then inf-loop, this is a panic function.
Print types
Addresses are xored with a stack cookie that's fixed for all functions.
SuspendEncrypt BadAddr: 00000003 SWI 1 BadAddr: 00000004 SWI 1 UnreachableCode: 00000006 SWI 7 UnreachableCode: 00000007 Addrcheck IntegerOverflow Food: 0000000B Addrcheck IntegerOverflow Kernel: 0000000B Addrcheck IntegerOverflow Module: 0000000C IRQ register func w irq enabled: 0000000E Force exit dmac w irq enabled: 0000000F Crypto irq enabled: 00000010 Resuming suspendbuf w irq enabled: 00000012 Creating suspendbuf w irq enabled: 00000013 Reset: 00000014 <xored-exception-lr> <xored-exception-pc> <exception-lr> RI: 00000015 <xored-exception-pc> ZDIV: 00000016 <xored-exception-pc> Trace: 00000017 <func-addr> SWI 7: 00000018 <xored-r1> DMAC when updating suspendbuf key: 00000019 <ret-val> DMAC cmac bad enum: 0000001A <enum-value> Bad enum to suspend AES-CBC function: 0000001B <enum-value> Generating new suspendbuf key failed: 0000001C Creating suspendbuf w irq8 module-registered: 0000001D Addrcheck IntegerOverflow Tz: 0000001E Addrcheck IntegerOverflow Tz2: 0000001F Bad ptr to suspend AES-CBC function: 00000020 IRQ register func w bad irq number: 00000022 Recieved unknown IRQ: 00000023