7.2 KiB
Executable File
iBoot RE (64 bits)
We focus on 64 bit iBoot cause we should:)
In the beginning
The Loading Address
Bootloaders in general and iBoot in particular expect to run at a specific, predefined, addresses. Such an address called the loading address (reset vector) of a bootloader. When loading the bootloader into IDA you need to supply the loading address, so IDA will be able to resolve data and code references properly.
Most Apple bootloaders start with a relocation code which compares the loading address with the current address and moves the bootloader if needed. Looking at this relocation code we can extract the loading address of the particular bootloader.
So, we start by using 0 as the loading address until we know better.
Here is an iBoot 64 bit example loaded at 0:
ROM:000 loc_0
ROM:000
ROM:000 ADRP X0, #loc_0@PAGE
ROM:004 ADD X0, X0, #loc_0@PAGEOFF <=== X0 has the current page address
ROM:008 LDR X1, =0x83D37B000 <=== this is the loading address
ROM:00C BL sub_17634
ROM:010 CMP X1, X0 <=== are we running at loading address
ROM:014 B.EQ loc_44 <=== if yes: continue booting normally
ROM:018 MOV X30, X1 <=== else: set LR to loading address
ROM:01C LDR X2, =0x83D413440
ROM:020 LDR X3, =0x83D37B000
ROM:024 SUB X2, X2, X3 <=== calculate len
ROM:028
ROM:028 loc_28 <=== copy loop
ROM:028 LDP X3, X4, [X0]
ROM:02C STP X3, X4, [X1]
ROM:030 ADD X0, X0, #0x10
ROM:034 ADD X1, X1, #0x10
ROM:038 SUBS X2, X2, #0x10
ROM:03C B.NE loc_28
ROM:040 RET <=== done copying -> return to loading address (LR)
ROM:044 ; -------------------------------------------------------------
ROM:044
ROM:044 loc_44 <=== rest of the iboot
ROM:044 MSR #6, #0xF ; MSR DAIFSET, #0XF
ROM:048 ADRP X30, #loc_AC8@PAGE
ROM:04C ADD X30, X30, #loc_AC8@PAGEOFF
ROM:050 ADRP X10, #loc_1F000@PAGE
ROM:054 ADD X10, X10, #loc_1F000@PAGEOFF
ROM:058 MSR #6, c12, c0, #0, X10 ; MSR VBAR_EL3, X10
So, the loading address in this example is: 0x83D37B000. With this knowledge we can load the iboot to IDA with a proper loading address.
For 32 bits iboot the relocation code works the same, but it's not the first code. The iboot starts with an exception vector.The first vector (reset) branches to the relocation code and it's the same from there.
Other bootloader: LLB, iBEC, IBSS have the same relocation code and hence the loading address available for grabbing.
Beyond the loading address
After relocation of the iBoot to the proper running place, the boot continues. Setting SP pointers, exception tables, etc. Below is the rest of the first function that runs on boot. Between the comments and the labels you should have no troubles following the code flow.
......... [ relocation code as shown above ]
ROM:83D07B044
ROM:83D07B044 ; mask exceptions
ROM:83D07B044 MSR #6, #0xF ; MSR DAIFSET, #0XF
ROM:83D07B048 ; set the next function to be executed on return
ROM:83D07B048 ADRP X30, #_start@PAGE
ROM:83D07B04C ADD X30, X30, #_start@PAGEOFF
ROM:83D07B050 ; setup exceptions table base address
ROM:83D07B050 ADRP X10, #exception_table@PAGE
ROM:83D07B054 ADD X10, X10, #exception_table@PAGEOFF
ROM:83D07B058 MSR #6, c12, c0, #0, X10 ; MSR VBAR_EL3, X10
ROM:83D07B05C ; clear 512K of memory
ROM:83D07B05C LDR X10, =base_addr_for_stack_and_stuff
ROM:83D07B060 LDR X11, =0x80000 ; 512K
ROM:83D07B064 ADD X11, X11, X10
ROM:83D07B068 MOV X12, #0
ROM:83D07B06C loop__clear_512K
ROM:83D07B06C STP X12, X12, [X10]
ROM:83D07B070 ADD X10, X10, #0x10
ROM:83D07B074 CMP X10, X11
ROM:83D07B078 B.NE loop__clear_512K
ROM:83D07B07C ; set base address for stack and stuff
ROM:83D07B07C LDR X10, =base_addr_for_stack_and_stuff
ROM:83D07B080 ADD X10, X10, #1,LSL#12 ; (base + 4K)
ROM:83D07B084 ; set EL3 SP to (base + 4K)
ROM:83D07B084 MOV SP, X10
ROM:83D07B088 MSR #5, #0 ; MSR SPSEL, #0
ROM:83D07B08C ADD X10, X10, #2,LSL#12 ; ((base + 4K) + 8K)
ROM:83D07B090 ; set EL0 SP to (base + 12K)
ROM:83D07B090 MOV SP, X10
ROM:83D07B094 ADD X20, X10, #2,LSL#12 ; (((base + 4K) + 8K) + 8K)
ROM:83D07B098 ; zero additional space
ROM:83D07B098 LDR X10, =_iboot_end ; from this addr
ROM:83D07B09C LDR X11, =fb_mbe ; to this addr
ROM:83D07B0A0 MOV X12, #0xF
ROM:83D07B0A4 BIC X12, X11, X12 ; end addr aligned to 16
ROM:83D07B0A8 MOV X13, #0
ROM:83D07B0AC ; clear more space, aligned 16
ROM:83D07B0AC loop__clear_more__aligned
ROM:83D07B0AC STP X13, X13, [X10],#0x10
ROM:83D07B0B0 CMP X10, X12
ROM:83D07B0B4 B.NE loop__clear_more__aligned
ROM:83D07B0B8 CMP X11, X12
ROM:83D07B0BC B.EQ loc_83D07B0CC
ROM:83D07B0C0 ; clear more space, the rest (unaligned)
ROM:83D07B0C0 loop__clear_more__unaligned
ROM:83D07B0C0 STR W13, [X10],#4
ROM:83D07B0C4 CMP X10, X11
ROM:83D07B0C8 B.NE loop__clear_more__unaligned
ROM:83D07B0CC
ROM:83D07B0CC loc_83D07B0CC
ROM:83D07B0CC ; set Exception handlers SP to (base + 20K)
ROM:83D07B0CC ADRP X11, #sp_for_exception_handlers@PAGE
ROM:83D07B0D0 ADD X11, X11, #sp_for_exception_handlers@PAGEOFF
ROM:83D07B0D4 STR X20, [X11]
ROM:83D07B0D8 RET
Notes:
- MSR DAIFSET, #0XF (mask all exceptions) This is very early stage and we don't want to be interrupted before we can handle exceptions properly.
- _start We are running a kind of a 'reset vector' code now and this is the next function we execute by setting its address in the LR register and running with the RET instruction.
- exception_table
The address that goes into the VBAR_EL3 register is the beginning of the EL3 exception table.
ARMv8 is very different from ARMv7 in how the exception table looks and works. Some of the differences are:- Instead of vector of dwords it has 0x80 bytes of code for each exception
- There are multiple entries for the same exception depending on the circumstances of where the exception is taken from