Bushwhacking Your Way Around A Bootloader: Rebecca ".BX" Shapiro 2018.06.16
Bushwhacking Your Way Around A Bootloader: Rebecca ".BX" Shapiro 2018.06.16
bootloader
Rebecca ".bx" Shapiro
2018.06.16
1 / 25
Meet Das U-Boot bootloader
[user@boot-dev ~]$ cloc u-boot/
13518 text files.
12700 unique files.
4701 files ignored.
3 / 25
The existential question.
* Flashback to 2012 *
The Turing-complete ELF-metadata weird machine
a b ra i n f u c k t o E L F- m e t a d a t a c o m p i l e r @ h t t p s : / / g i t h u b . c o m / b x / e l f - b f - t o o l s
3 / 25
The existential question.
3 / 25
The existential question.
3 / 25
The existential question.
3 / 25
This talk for those in a hurry
Introduce goals and case study
Generalizing about bootloaders
Debugging U-Boot (as according to U-Boot)
My instrumentation toolsuite
An attempt at something better
Techniques for identifying code <–> data
A test drive through a simple example
4 / 25
Properties of a bootloader
They load and prepare images for execution
They initialize resources/hardware
Sometimes they self-relocate
In general, bootloaders
5 / 25
Let's begin exploring U-Boot
6 / 25
How to debug a bootloader
(with GDB and QEMU)
(gdb) target remote | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
Remote debugging using | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
0x40014000 in ?? ()
(gdb) file u-boot-spl
Reading symbols from u-boot-spl...done.
(gdb) break jump_to_image_no_args
Breakpoint 1 at 0x4020127c: file arch/arm/cpu/armv7/omap-common/boot-common.c,
line 229.
(gdb) c
Continuing.
Breakpoint 1, jump_to_image_no_args (spl_image=0x80000000 <spl_image>)
at arch/arm/cpu/armv7/omap-common/boot-common.c:229
(gdb) si
0x80100000 in ?? ()
(gdb) file u-boot
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "u-boot"? (y or n) y
Reading symbols from u-boot...done.
Error in re-setting breakpoint 1: Function "jump_to_image_no_args" not
defined.
(gdb) break relocate_done
Breakpoint 2 at 0x801020b4: file arch/arm/lib/relocate.S, line 134.
(gdb) c
Continuing.
Breakpoint 2, relocate_done () at arch/arm/lib/relocate.S:134
134 in arch/arm/lib/relocate.S
(gdb) break board_init_r
Breakpoint 3 at 0x80104e7c: file common/board_r.c, line 957.
(gdb) c
Continuing.
^Comap_gpmc_write: bad SDRAM idle mode 3
omap_i2c_write: Bad register 0x0000cc
7 / 25
How to debug a bootloader
(with GDB and QEMU)
(gdb) target remote | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
Remote debugging using | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
0x40014000 in ?? ()
(gdb) file u-boot-spl
Reading symbols from u-boot-spl...done.
(gdb) break jump_to_image_no_args
Breakpoint 1 at 0x4020127c: file arch/arm/cpu/armv7/omap-common/boot-common.c,
line 229.
(gdb) c
Continuing.
Breakpoint 1, jump_to_image_no_args (spl_image=0x80000000 <spl_image>)
at arch/arm/cpu/armv7/omap-common/boot-common.c:229
(gdb) si
0x80100000 in ?? ()
(gdb) file u-boot
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "u-boot"? (y or n) y
Reading symbols from u-boot...done.
Error in re-setting breakpoint 1: Function "jump_to_image_no_args" not
defined.
(gdb) break relocate_done
Breakpoint 2 at 0x801020b4: file arch/arm/lib/relocate.S, line 134.
(gdb) c
Continuing.
Breakpoint 2, relocate_done () at arch/arm/lib/relocate.S:134
134 in arch/arm/lib/relocate.S
(gdb) break board_init_r
Breakpoint 3 at 0x80104e7c: file common/board_r.c, line 957.
(gdb) c
Continuing.
^Comap_gpmc_write: bad SDRAM idle mode 3
omap_i2c_write: Bad register 0x0000cc
7 / 25
How to debug a bootloader
(with GDB and QEMU)
(gdb) target remote | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
Remote debugging using | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
0x40014000 in ?? ()
(gdb) file u-boot-spl
Reading symbols from u-boot-spl...done.
(gdb) break jump_to_image_no_args
Breakpoint 1 at 0x4020127c: file arch/arm/cpu/armv7/omap-common/boot-common.c,
line 229.
(gdb) c
Continuing.
Breakpoint 1, jump_to_image_no_args (spl_image=0x80000000 <spl_image>)
at arch/arm/cpu/armv7/omap-common/boot-common.c:229
(gdb) si
0x80100000 in ?? ()
(gdb) file u-boot
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "u-boot"? (y or n) y
Reading symbols from u-boot...done.
Error in re-setting breakpoint 1: Function "jump_to_image_no_args" not
defined.
(gdb) break relocate_done
Breakpoint 2 at 0x801020b4: file arch/arm/lib/relocate.S, line 134.
(gdb) c
Continuing.
Breakpoint 2, relocate_done () at arch/arm/lib/relocate.S:134
134 in arch/arm/lib/relocate.S
(gdb) break board_init_r
Breakpoint 3 at 0x80104e7c: file common/board_r.c, line 957.
(gdb) c
Continuing.
^Comap_gpmc_write: bad SDRAM idle mode 3
omap_i2c_write: Bad register 0x0000cc
7 / 25
How to debug a bootloader
(with GDB and QEMU)
(gdb) target remote | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
Remote debugging using | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
0x40014000 in ?? ()
(gdb) file u-boot-spl
Reading symbols from u-boot-spl...done.
(gdb) break jump_to_image_no_args
Breakpoint 1 at 0x4020127c: file arch/arm/cpu/armv7/omap-common/boot-common.c,
line 229.
(gdb) c
Continuing.
Breakpoint 1, jump_to_image_no_args (spl_image=0x80000000 <spl_image>)
at arch/arm/cpu/armv7/omap-common/boot-common.c:229
(gdb) si
0x80100000 in ?? ()
(gdb) file u-boot
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "u-boot"? (y or n) y
Reading symbols from u-boot...done.
Error in re-setting breakpoint 1: Function "jump_to_image_no_args" not
defined.
(gdb) break relocate_done
Breakpoint 2 at 0x801020b4: file arch/arm/lib/relocate.S, line 134.
(gdb) c
Continuing.
Breakpoint 2, relocate_done () at arch/arm/lib/relocate.S:134
134 in arch/arm/lib/relocate.S
(gdb) break board_init_r
Breakpoint 3 at 0x80104e7c: file common/board_r.c, line 957.
(gdb) c
Continuing.
^Comap_gpmc_write: bad SDRAM idle mode 3
omap_i2c_write: Bad register 0x0000cc
7 / 25
How to debug a bootloader
(with GDB and QEMU)
(gdb) target remote | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
Remote debugging using | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
0x40014000 in ?? ()
(gdb) file u-boot-spl
Reading symbols from u-boot-spl...done.
(gdb) break jump_to_image_no_args
Breakpoint 1 at 0x4020127c: file arch/arm/cpu/armv7/omap-common/boot-common.c,
line 229.
(gdb) c
Continuing.
Breakpoint 1, jump_to_image_no_args (spl_image=0x80000000 <spl_image>)
at arch/arm/cpu/armv7/omap-common/boot-common.c:229
(gdb) si
0x80100000 in ?? ()
(gdb) file u-boot
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "u-boot"? (y or n) y
Reading symbols from u-boot...done.
Error in re-setting breakpoint 1: Function "jump_to_image_no_args" not
defined.
(gdb) break relocate_done
Breakpoint 2 at 0x801020b4: file arch/arm/lib/relocate.S, line 134.
(gdb) c
Continuing.
Breakpoint 2, relocate_done () at arch/arm/lib/relocate.S:134
134 in arch/arm/lib/relocate.S
(gdb) break board_init_r
Breakpoint 3 at 0x80104e7c: file common/board_r.c, line 957.
(gdb) c
Continuing.
^Comap_gpmc_write: bad SDRAM idle mode 3
omap_i2c_write: Bad register 0x0000cc
7 / 25
How to debug a bootloader
(with GDB and QEMU)
(gdb) target remote | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
Remote debugging using | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
0x40014000 in ?? ()
(gdb) file u-boot-spl
Reading symbols from u-boot-spl...done.
(gdb) break jump_to_image_no_args
Breakpoint 1 at 0x4020127c: file arch/arm/cpu/armv7/omap-common/boot-common.c,
line 229.
(gdb) c
Continuing.
Breakpoint 1, jump_to_image_no_args (spl_image=0x80000000 <spl_image>)
at arch/arm/cpu/armv7/omap-common/boot-common.c:229
(gdb) si
0x80100000 in ?? ()
(gdb) file u-boot
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "u-boot"? (y or n) y
Reading symbols from u-boot...done.
Error in re-setting breakpoint 1: Function "jump_to_image_no_args" not
defined.
(gdb) break relocate_done
Breakpoint 2 at 0x801020b4: file arch/arm/lib/relocate.S, line 134.
(gdb) c
Continuing.
Breakpoint 2, relocate_done () at arch/arm/lib/relocate.S:134
134 in arch/arm/lib/relocate.S
(gdb) break board_init_r
Breakpoint 3 at 0x80104e7c: file common/board_r.c, line 957.
(gdb) c
Continuing.
^Comap_gpmc_write: bad SDRAM idle mode 3
omap_i2c_write: Bad register 0x0000cc
7 / 25
How to debug a bootloader
(with GDB and QEMU)
(gdb) target remote | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
Remote debugging using | qemu-system-arm -gdb stdio -M beaglexm -sd sd.img -S
0x40014000 in ?? ()
(gdb) file u-boot-spl
Reading symbols from u-boot-spl...done.
(gdb) break jump_to_image_no_args
Breakpoint 1 at 0x4020127c: file arch/arm/cpu/armv7/omap-common/boot-common.c,
line 229.
(gdb) c
Continuing.
Breakpoint 1, jump_to_image_no_args (spl_image=0x80000000 <spl_image>)
at arch/arm/cpu/armv7/omap-common/boot-common.c:229
(gdb) si
0x80100000 in ?? ()
(gdb) file u-boot
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "u-boot"? (y or n) y
Reading symbols from u-boot...done.
Error in re-setting breakpoint 1: Function "jump_to_image_no_args" not
defined.
(gdb) break relocate_done
Breakpoint 2 at 0x801020b4: file arch/arm/lib/relocate.S, line 134.
(gdb) c
Continuing.
Breakpoint 2, relocate_done () at arch/arm/lib/relocate.S:134
134 in arch/arm/lib/relocate.S
(gdb) break board_init_r
Breakpoint 3 at 0x80104e7c: file common/board_r.c, line 957.
(gdb) c
Continuing.
^Comap_gpmc_write: bad SDRAM idle mode 3
omap_i2c_write: Bad register 0x0000cc
7 / 25
How to debug a self-relocating bootloader
As ac c ord in g to U-Boot
(q u oted from d oc /R EADME.arm-reloc ation )
start debugger
[hs@pollux u-boot]$ arm-linux-gdb u-boot
connect to target
(gdb) target remote localhost:4444
discard symbol-file
(gdb) symbol-file
Discard symbol table from `/home/hs/celf/u-boot/u-boot'? (y or n) y
No symbol file now.
8 / 25
How to debug a self-relocating bootloader
As ac c ord in g to U-Boot
(q u oted from d oc /R EADME.arm-reloc ation )
start debugger
[hs@pollux u-boot]$ arm-linux-gdb u-boot
connect to target
(gdb) target remote localhost:4444
discard symbol-file
(gdb) symbol-file
Discard symbol table from `/home/hs/celf/u-boot/u-boot'? (y or n) y
No symbol file now.
8 / 25
"Demystifying" magic numbers
Program received signal SIGSTOP, Stopped (signal).
0x8ff17f18 in serial_getc () at serial_mxc.c:192
192 while (__REG(UART_PHYS + UTS) & UTS_RXEMPTY);
(gdb)
=> bdinfo
rch_number = XXXXXXXXXX
boot_params = XXXXXXXXXX
DRAM bank = XXXXXXXXXX
-> start = XXXXXXXXXX
-> size = XXXXXXXXXX
ethaddr = XXXXXXXXXX
ip_addr = XXXXXXXXXX
baudrate = XXXXXXXXXX
TLB addr = XXXXXXXXXX
relocaddr = 0x8ff08000
^^^^^^^^^^
reloc off = XXXXXXXXXX
irq_sp = XXXXXXXXXX
sp start = XXXXXXXXXX
FB base = XXXXXXXXXX
or interrupt execution by any means and re-load the symbols at the location
specified by gd->relocaddr -- this is only valid after board_init_f.
9 / 25
C C BY- N C 2 . 0 a l e x w h i t e / / fl i c k r
Can we do better?
10 / 25
Toolsuite overview
Featuring:
Static and dynamic analysis
Instrumentation (via GDB)
Mediates all memory writes
Language to express (and enforce)
memory write polices
32-bit ARM only (for now)
11 / 25
Relocation reconnaissance
Tools at
https://typedregions.com
12 / 25
Identifying relocation phases
Bl ock wri te operati on
($ip, offset in image, destination, size, call stack)
13 / 25
U-Boot's static call graph (for reference)
Generated using IDA Pro (and then simplified by hand)
14 / 25
Calltrace of successful U-Boot execution
Example output from U-Boot SPL execution
> save_boot_params {arch/ahrm/cpu/armv7/omap-common/lowlevel_init.S::22}
15 / 25
Calltrace and write data combined
16 / 25
Other magic numbers
switch(beagle_revision()) { argument = 0x0000 << 16;
case REVISION_C4: err = mmc_send_cmd(MMC_CMD55, argument, resp);
if (identify_xm_ddr() == NUMONYX_MCP) { if (err == 1) {
__raw_writel(0x4, SDRC_CS_CFG); /* 512MB/bank */ mmc_card_cur->card_type = SD_CARD;
__raw_writel(SDP_SDRC_MDCFG_0_DDR_NUMONYX_XM, SDRC_MCFG_0); } else {
mmc_card_cur->card_type = MMC_CARD;
17 / 25
Other magic numbers
switch(beagle_revision()) { argument = 0x0000 << 16;
case REVISION_C4: err = mmc_send_cmd(MMC_CMD55, argument, resp);
if (identify_xm_ddr() == NUMONYX_MCP) { if (err == 1) {
__raw_writel(0x4, SDRC_CS_CFG); /* 512MB/bank */ mmc_card_cur->card_type = SD_CARD;
__raw_writel(SDP_SDRC_MDCFG_0_DDR_NUMONYX_XM, SDRC_MCFG_0); } else {
mmc_card_cur->card_type = MMC_CARD;
17 / 25
Other magic numbers
switch(beagle_revision()) { argument = 0x0000 << 16;
case REVISION_C4: err = mmc_send_cmd(MMC_CMD55, argument, resp);
if (identify_xm_ddr() == NUMONYX_MCP) { if (err == 1) {
__raw_writel(0x4, SDRC_CS_CFG); /* 512MB/bank */ mmc_card_cur->card_type = SD_CARD;
__raw_writel(SDP_SDRC_MDCFG_0_DDR_NUMONYX_XM, SDRC_MCFG_0); } else {
mmc_card_cur->card_type = MMC_CARD;
th at tran sforms
17 / 25
Other magic numbers
switch(beagle_revision()) { argument = 0x0000 << 16;
case REVISION_C4: err = mmc_send_cmd(MMC_CMD55, argument, resp);
if (identify_xm_ddr() == NUMONYX_MCP) { if (err == 1) {
__raw_writel(0x4, SDRC_CS_CFG); /* 512MB/bank */ mmc_card_cur->card_type = SD_CARD;
__raw_writel(SDP_SDRC_MDCFG_0_DDR_NUMONYX_XM, SDRC_MCFG_0); } else {
mmc_card_cur->card_type = MMC_CARD;
th at tran sforms
in to
CM_FCLKEN_IVA2,RW,W,32,0x00000000,0x48004000,Table 3-93. IVA2_CM Register Summary
CM_CLKEN_PLL_IVA2,RW,W,32,0x00000004,0x48004004,Table 3-93. IVA2_CM Register Summary
CM_IDLEST_IVA2,R,C,32,0x00000020,0x48004020,Table 3-93. IVA2_CM Register Summary
CM_IDLEST_PLL_IVA2,R,C,32,0x00000024,0x48004024,Table 3-93. IVA2_CM Register Summary
CM_AUTOIDLE_PLL_IVA2,RW,W,32,0x00000034,0x48004034,Table 3-93. IVA2_CM Register Summary 17 / 25
Devising code and data relationships
18 / 25
A simpler example
19 / 25
Hello, world!
#include <stdio.h>
#include <string.h>
void do_nothing() {}
void say_hello() {
printf("Hello, world\n");
}
void modify_memory() {
for (int i = 0; i <= SIZE; i++) {
memory[i] = 'A';
}
}
20 / 25
Calltrace of Hello, world!
> _libcstartmain {arm-linux-gnueabihf-glibc/src/glibc-2.27/csu/libc-start.c::137}
21 / 25
Checking for unexpected writes
1. Configure instrumentation suite to work with sample
2. Run sample through instrumentation suite's static analysis
3. Construct policy to target "substages" and memory regions of interest
4. Import policy
5. Execute dynamic analysis
6. Use post-analysis to highlight policy violations
22 / 25
Checking for unexpected writes
1. Configure instrumentation suite to work with sample
2. Run sample through instrumentation suite's static analysis
3. Construct policy to target "substages" and memory regions of interest
4. Import policy
5. Execute dynamic analysis
6. Use post-analysis to highlight policy violations
22 / 25
An attempt at a demo
23 / 25
Thank you