[AT&T Samsung Galaxy S 4 Android Development] jeboo:
z-patch

Well, I'm back again with another essay on removing the annoying module authentication that Sammy uses across the S4 phones. This tutorial will focus on those phones with either loki-vulnerable BLs (MDB, MDL, etc) or unlocked BLs (i9505).

It seems with the last few releases, Sammy has started tying the kernel modules to the BLs, instituting a TZ-based authentication system. We will focus on bypassing this "protection" by modifying the kernel binary itself. This is useful when we dont have source yet to build our own versions, as is the current case with KK. This also allows us to use newer stock kernels without having to update the BLs to match.

*** This is strictly intended for developers, do NOT try any of the below for fear of bricking! ***

1. Confirm modules are being rejected by BL-based authentication.

Simple, try to turn on WiFi and keep on eye on dmesg/logcat. You should see some module-related errors (dmesg | grep 'tzapp').

2. How to extract the kernel binary

In post #1, I mention this but figure it's worth detailing for future use! To deconstruct boot.img is pretty trivial, I use the Adi_Pat package and simply run 'extract.sh boot.img' (you may need to update some of the binaries in that package). This will separate out zImage and ramdisk (expanded) into their respective folders.

zImage is composed of some important loader code and the gzip compressed kernel binary. We need to extract the kernel and then decompress it.

This is a snippet of a script Entropy512 used for s2 unpacking..

pos=`grep -P -a -b -m 1 --only-matching '\x1F\x8B\x08' zImage | cut -f 1 -d :`
echo "Extracting gzip'd kernel image from file: zImage (start = $pos)"

if [ ! -z $pos ]; then
echo "Dumping compressed image"
dd if=zImage of=zImage_unpacked.gz bs=1 skip=$pos 2>/dev/null >/dev/null
echo "Unzipping compressed image"
gunzip -qf zImage_unpacked.gz
fi

3. Analyze the unpacked kernel binary.

Now load up zImage_unpacked into IDA, set to ARM CPU and base address 0xc0008000. Post #1 pretty much details how to isolate the exact ARM code to be patched. I patched things a little differently this time (this is MLD ATT KK leak):

ROM:C00C91C8 00 50 E0 E3 MOV R5, 0xFFFFFFFF ; <<<< [COLOR="Red"]This returns -1, indicating authentication failed[/COLOR]

The patch is trivial, we simply change R5 to equal 0 and the function will always return successful.

4. Patch and restore zImage, or not?

While the patch is easy, recompressing the kernel and popping it back into zImage is not. The zImage structure is very finicky, so I thought of a better way. Since we know the kernel binary in zImage is decompressed before the kernel is jumped to, why not inject a little patch after realtime decompression. This way we avoid having to decompress, patch, recompress to size, etc..So what we need to do is load up zImage (which has the loader code) in IDA and find the relevant code/injection point.

I will just tell you that arch/arm/boot/compressed/head.S from the kernel src pretty much lays it out for us.

mov	r1, sp	@ malloc space above stack
add	r2, sp, #0x10000	@ 64k max
mov	r3, r7
[COLOR="Red"]bl	decompress_kernel[/COLOR]
bl	cache_clean_flush
bl	cache_off
mov	r0, #0	@ must be zero
mov	r1, r7	@ restore architecture number
mov	r2, r8	@ restore atags pointer
ARM(	mov	pc, r4	)	@ call kernel	<<<< [COLOR="red"]Remember R4 here![/COLOR]
THUMB(	bx	r4	)	@ entry point is always ARM

in misc.c

void
decompress_kernel(unsigned long output_start, unsigned long free_mem_ptr_p,
unsigned long free_mem_ptr_end_p,
int arch_id)
{
int ret;

output_data	= (unsigned char *)output_start;
free_mem_ptr	= free_mem_ptr_p;
free_mem_end_ptr	= free_mem_ptr_end_p;
__machine_arch_type	= arch_id;

arch_decomp_setup();

putstr("Uncompressing Linux...");
ret = do_decompress(input_data, input_data_end - input_data,
output_data, error);
if (ret)
error("decompressor returned an error");
else
putstr(" done, booting the kernel.\n");
}

So we need a little codespace for our patch, I see 2 log messages that really serve no meaningful purpose. Let's look at the ARM of decompress_kernel in zImage:

ROM:00000928 00 00 8F E0 ADD R0, PC, R0 ; "Uncompressing Linux..."
ROM:0000092C 00 20 81 E5 STR R2, [R1]
ROM:00000930 68 20 9F E5 LDR R2, =0x1C
ROM:00000934 02 20 94 E7 LDR R2, [R4,R2]
ROM:00000938 00 30 82 E5 STR R3, [R2]
ROM:0000093C D7 FF FF EB BL sub_8A0
ROM:00000940 5C 30 9F E5 LDR R3, =8
ROM:00000944 05 20 A0 E1 MOV R2, R5
ROM:00000948 03 00 94 E7 LDR R0, [R4,R3]
ROM:0000094C 54 30 9F E5 LDR R3, =0
ROM:00000950 03 10 94 E7 LDR R1, [R4,R3]
ROM:00000954 50 30 9F E5 LDR R3, =0x14
ROM:00000958 01 10 60 E0 RSB R1, R0, R1
ROM:0000095C 03 30 94 E7 LDR R3, [R4,R3]
ROM:00000960 A5 08 00 EB BL sub_2BFC	; <<<< decompress_kernel
ROM:00000964 00 00 50 E3 CMP R0, #0	; all code below is essentially replaceable
ROM:00000968 03 00 00 0A BEQ loc_97C
ROM:0000096C 3C 00 9F E5 LDR R0, =(aDecompressorRe - 0x978)
ROM:00000970 00 00 8F E0 ADD R0, PC, R0 ; "decompressor returned an error"
ROM:00000974 38 40 BD E8 LDMFD SP!, {R3-R5,LR}; <<<< this is important, as it restores R4 which has our kernel base address
ROM:00000978 CC FF FF EA B sub_8B0
ROM:0000097C ; ---------------------------------------------------------------------------
ROM:0000097C loc_97C ; CODE XREF: sub_8F4+74j
ROM:0000097C 30 00 9F E5 LDR R0, =(aDoneBootingThe - 0x988)
ROM:00000980 00 00 8F E0 ADD R0, PC, R0 ; " done, booting the kernel.\n"
ROM:00000984 38 40 BD E8 LDMFD SP!, {R3-R5,LR}
ROM:00000988 C4 FF FF EA B sub_8A0
ROM:00000988 ; End of function sub_8F4

5. With our patch points figured out in zImage and the unpacked kernel, we need to generate our new ARM code and make the changes!

- With R4 representing our kernel base address, we need to calculate a relative offset to patch: C00C91C8 - C0008000 = 0xC11C8

- To generate ARM code, you can write it up in a simple, unformatted .s file and compile with arm-as.

- So our patch @964 in zImage above will be the following:

ROM:00000960 A5 08 00 EB BL sub_8020ABFC ; decompress_kernel
ROM:00000964 38 40 BD E8 LDMFD SP!, {R3-R5,LR}	; restore registers, most impt R4
ROM:00000968 0C 00 9F E5 LDR R0, loc_0000097C	; our relative offset, 0xC118C
ROM:0000096C 00 10 84 E0 ADD R1, R4, R0	; base address + offset
ROM:00000970 A0 00 A0 E3 MOV R0, #0xA0	; this one byte fixes the R5 instruction mentioned above
ROM:00000974 02 00 C1 E5 STRB R0, [R1,#2] ; actual patch!
ROM:00000978 1E FF 2F E1 BX LR	; leave
ROM:0000097C C8 11 0C 00

And that's it, hex edit that code starting @0x964 into the zImage, repack the zImage+ramdisk into boot.img/lok and we're done! I call it z-patching for lack of a better name :)

I was verbose with this post since it can be applied to pretty much any kernel downstream of an unlocked/compromised BL.