readme update

This commit is contained in:
Triss 2022-04-09 22:10:12 +02:00
parent cb7386bc6f
commit 60a5e08c7e
1 changed files with 47 additions and 24 deletions

View File

@ -5,40 +5,51 @@ Tools to try to dump the MSP430FR BSL, mainly targetting the [MSP430FR5994
## The idea
The MSP430FR bootloader ('BSL') resides at 0x1000, this memory cannot be read,
and user code can only jump to 0x1000 or 0x1002 to run certian functions of the
BSL. Though, it is very likely that when the CPU is running from inside this
memory region, it can access this memory as data, because that's often needed
to store eg. complex integer values, structs, lookup tables, and so on.
The MSP430FR bootloader ('BSL') resides at `0x1000`. This memory cannot be
read, and user code can only jump to `0x1000` or `0x1002`, called the "Z-area",
to run certian functions of the BSL. Though, it is very likely that when the
CPU is running from inside this memory region, it can access this memory as
data, as that is often needed to store eg. structs, lookup tables, and so on.
Several other "execute-only" memory implementations function in a similar way,
such as the Nintendo GameBoy Advance and DS boot ROMS ("BIOS"es, citation
below), as well as in other systems analyzed by Schink and Obermaier,
publication also linked below.
The BSL (according to [the datasheet
](https://www.ti.com/lit/ug/slau550aa/slau550aa.pdf)) doesn't disable
interrupts. That means that you can still jump to it, and then interrupt it to
jump to code controlled by the user. As this is an interrupt, it can inspect
interrupts. That means that, while the BSL is executing, it is possible to
interrupt this execution flow to
jump to code controlled by the user. An interrupt can inspect and modify
the registers of the BSL code at the time when the interrupt happened, as well
as the stack contents. Having a timer at the same frequency as the CPU, and
having it dump the register+stack contents after a while, and doing that over
and over again while advancing the time-until-interrupt delay by one cycle
every iteration, you can get a view of what the CPU is doing while executing
this hidden code (also, the MSP430 CPU is multicycling and uses variable-length
instructions, so instruction timings and pc deltas can also be used as a side
channel to figure out which instruction is which).
having it dump the register and stack contents after a certain interval increasing
by one cycle every iteration, it is possible to trace the instruction flow of
the CPU, as well as which registers and stack contents it is accessing, and how,
even though the code itself is not visible. Furthermore, the MSP430 CPU uses a
variable-length instruction set and instructions can use a variable amount of
cycles, therefore these traces can also be used to infer more infromation about
which instructions are executed, as the `pc` CPU register will never point to
the middle of an instruction, and will only advance to the next instruction
depending on how long the current instruction takes to execute.
Function epilogues typically first pop a bunch of values off the stack and load
these back into registers, and then return. Additionally, if you're lucky, you
may also find a memory read right before the end of such an epilogue.
Function epilogues typically first pop a number of values off the stack and
load these back into registers, and then return. By controlling the stack
pointer value, these can be used as a way to perform arbitrary reads. However,
as we are targetting nonwritable memory, an interrupt needs to happen before
the return occurs, otherwise CPU execution becomes very unpredictable.
You can find these epilogues by staring at many, many execution traces
(obtained from these timer interrupts) and thinking really hard (this is the
hard, time-consuming and labor-intensive part).
Then, by timing a DMA transfer such that it happens after a function is called
but before it returns, it is possible to overwrite the memory popped off the
stack when an epilogue executes, thereby gaining control of a few register
values, and the program counter. Then you could redirect it to another piece of
code that does the memory read before returning. As you now (hopefully) have
control over the address it reads from, you can use this as an arbitrary read
to read one word of the BSL, then return to use code to do the next iteration.
Alternatively, by timing an interrupt or a DMA transfer such that it happens
after a function is called but before it returns, it is possible to overwrite
the memory popped off the stack when an epilogue executes, thereby gaining
control of a few register values as well as the program counter. Then, CPU
execution can be redirected to another code snippet performing the memory read
before returning. With control over the address it reads from, this can be used
as an arbitrary read to read one word of the BSL, then return to use code to do
the next iteration.
The "using interrupts to figure out what execute-only code is doing" trick was
first (afaik) used by Martin Korth to find such a gadget inside the Nintendo DS
@ -64,7 +75,8 @@ described near the end, the article is quite large.
1. When the CPU is executing the BSL, it can perform data accesses to other BSL
areas. Thus, if an arbitrary read gadget is found, it can be used to dump
the entire BSL region.
the entire BSL region. This is the same issue as present in the Nintendo
DS ARM7 boot ROM.
1. The routine at `0x1002` provides such a gadget, *as indicatd in SLAU550AA*.
1. The BSL execution is allowed to be interrutped, thus the instruction flow
can be traced by dumping CPU register values throughout the BSL execution.
@ -101,3 +113,14 @@ described near the end, the article is quite large.
1. DMA: can a DMA transfer be used to change the stack contents during BSL
execution? (Most likely, just like interrupts can, I simply haven't checked.)
## Hashes
This is the hash of the memory region `0x1000` to `0x17FF`, on an MSP430FR5994,
with BSL 00.08.35.B3:
| Hash function | value |
| MD5 | `4bb3bb753face80fffe1fef7a762884a` |
| SHA-1 | `1b4c13e006121a9b1c1ebcd4fbc6ec7c96cc017f` |
| SHA-256 | `e4d0d171013f847a357eebe5467bcd413ecb41dc01424b7e4ee636538d820766` |
| SHA-512 | `fed28a7e9643a551789075b79d9b04fa6e8cdca74d783c1c3830ece07e5c9141dda9532b3c442416a1ddab90d752e679c6918c0d5333ac6da9fd23ab6c33d1bb` |