[Galaxy S II General] adg2012:
A. About this write-up, and some disclaimers

This write-up describes how to use unix command line tools to manually convert a stock SGS2 kernel into an insecure one (i.e. set ro.secure=0 in the default.prop file so that adbd starts as root), with absolutely minimal changes to the stock kernel. Most of this has to do with unpacking and repacking the stock zImage file (it is packaged in a fairly complex way).

This is primarily a record of my own learning experience on how to do the above so that the modified insecure zImage kernel file differs from the stock original zImage file at only a few bytes. If you want to learn how to do this yourself, then read on.

This is not a rooting guide. If you want a prebuilt insecure kernel to flash your phone, go to top developer Odia's thread:

http://xdaforums.com/showthread.php?t=1101671

Thanks to Odia, many such prebuilt insecure kernels are available for download.

This is also not a full unpack-pack guide for zImage --- we will not even unpack the initramfs tree. Such guides and scripts are available on this forum, e.g., from Chenglu:

http://xdaforums.com/showthread.php?t=1294436

This write-up is only intended to share my experience with others who may be curious to learn how to set ro.secure=0 in stock i9100 kernels.

IMPORTANT DISCLAIMER:

THE PROCEDURE BELOW IS NOT GUARANTEED TO WORK AND CONSISTS OF MANY STEPS AND CHECKS WHICH ARE HIGHLY ERROR PRONE. FLASHING A KERNEL CAN HAVE NASTY CONSEQUENCES, SUCH AS BRICKING YOUR PHONE PERMANENTLY. IF YOU ARE NOT WILLING TO TAKE SUCH RISKS AT YOUR OWN OR IF YOU ARE IN DOUBT, DO NOT PROCEED --- I WILL NEITHER BE RESPONSIBLE NOR BE ABLE TO HELP OUT IF YOU GET IN TROUBLE AFTER FOLLOWING THIS WRITE-UP.

==========================================

B. Outline of procedure, and prerequisites

1. To convert a stock secure kernel zImage file to insecure mode, we will extract the default.prop file from the initramfs tree included in it (a small text file, few hundred bytes at most), set "ro.secure=0" in the file (and possibly make some less critical changes), then put it back in the zImage kernel file. The new zImage file will be very close to the original stock, differing minimally in only in that it will now allow adbd to start as root. (You can then install a setuid su binary manually via adb.) Most of the work involves unraveling the structure of the complexly packaged zImage file.

2. Prerequsites: I will assume you are familiar with:

Android rooting via insecure adb method (ro.insecure=0 in default.prop)

Using basic unix command line tools (in pipelines)

The gzip program and its options (man gzip)

Basic perl regular expressions for use with grep (man grep, man perlre)

Basics of hex/octal/binary arithmetic, byte ordering (endian-ness)

The hexdump program (or xxd or hd); full hex-editor not needed

How to cut out a part of a file (using head/tail or dd, see below)

How to overwrite part of a file (a segment of consecutive databytes inside the file) using the dd command with the "conv=notrunc,nocreat" option

3. Outline of procedure:

(a) The zImage file is a concatenation of four pieces:

Piece 1 Piece 2 Piece 3 Piece 4
zImage = Head.ARM + Image.gz + Tail.ARM + zImage.EndPad
|--------- zImage.ARM ---------|

(b) Head.ARM is few kilobytes, Tail.ARM only 50-60 bytes, and zImage.EndPad consists of null bytes to fill up the 8MB kernel partition (but in stock Samsung kernels it may contain signature at the end, AFAIK). So almost everything is in Image.gz (several megabytes).

(c) We will separate (cut out) these four pieces, uncompress Image.gz into a file "Image", make changes to it, compress it back, and finally cat the pieces back to make a new "insecure" zImage file. Note that the file "Image" (= decompressed Image.gz) contains the default.prop file as a substring, since in SGS2 kernels the initramfs tree is included as an uncompressed cpio archive.

(d) One issue is that the decompression-modification-recompression of Image.gz may change its size (if it gets smaller we can perhaps pad with zero bytes but if it gets bigger we will have a real problem). We really want to keep the size of Image.gz the same as it was before the mod, and it can generally be achieved by trying further (less critical) modifications to the default.prop file until it compresses back (with gzip -9 -n) to the original size. This is of course not guaranteed to work, but I was able to do it for every SGS2 stock kernel that I tried. In fact, with luck the recompressed Image.gz file (and hence the whole zImage) can be made identical to the original one except for a few bytes!

4. Summary of steps:

STEP 1: Split off Piece 4, i.e. spilt zImage = zImage.ARM + zImage.EndPad

STEP 2: Find the start/end locations of Image.gz inside zImage.ARM

STEP 3: Split zImage.ARM = Head.ARM + Image.gz + Tail.ARM

STEP 4: Backup Image.gz, uncompress into "Image", backup Image

STEP 5: Locate the file default.prop in Image, and yank it out from Image

STEP 6: Edit default.prop, overwrite part of Image with it, and recompress

STEP 7: If size of new compressed Image is different, repeat Step 6

STEP 8: Concatenate all pieces back to form new insecure zImage

Be careful with all steps and keep comparing modified files with cmp -b -l. Make a small error and you will corrupt the kernel (with possibly nasty consequences like hard bricking your SGS2).

=======================================

C. Background reference information:

1. Structure of a stock GT-I9100 kernel zImage file

As mentioned above, the zImage file is a concatenation of four pieces:

Piece 1 Piece 2 Piece 3 Piece 4
zImage = Head.ARM + Image.gz + Tail.ARM + zImage.EndPad
|--------- zImage.ARM ---------|

From inside zImage.ARM, we can yank out the Image.gz part (Piece 2) and uncompress it into a file named "Image", which in turn consists of three parts:

---------- Uncompressed Image.gz -----------
Image = Kernel + Initramfs-cpio + Additional-payload

In stock SGS2 kernels, the initramfs cpio archive is uncompressed. This makes the default.prop file a substring of the Image file, so we can access it there and modify "ro.secure=1" to "ro.secure=0".

2. Important offsets/bytes stored in the ARM zImage header

Offset in zImage Size Value Description
---------------- ------- -------------- --------------------------------
0x24 4 bytes 0x016F2818 Magic sign for ARM Linux zImage
0x28 4 bytes start address The address zImage.ARM starts at
0x2C 4 bytes end address The address zImage.ARM ends at

** Note ** Above four-byte values are stored in little-endian format **

3. How a GZIP file begins and ends (RFC 1952 GZIP File Format Spec v.4.3)

A gzip of a file/data:

Begins with the three-byte string '\x1f\x8b\x08', and

Ends with 4-bytes encoding the size of the uncompressed data (in little-endian order)

4. How to dump a chunk of consecutive bytes from a file using head/tail or dd:

Example: Suppose m=200 and n=350 are byte-offsets within a file "foo". Then we can dump out the 350-200=150 bytes starting from offset 200 and up to offset 350 using any the following commands:

head -c $n foo | tail -c +$((1 + $m))

or:

tail -c +$((1 + $m)) foo | head -c $(($n - $m))

or:

dd if=foo bs=1 skip=$m count=$(($n - $m))

Notice that while most programs count byte offsets starting from 0 at the beginning of file, tail counts offsets starting from 1. Also, be careful when using the low-level command dd in a pipeline, since it may not flush/sync buffers in async non-blocking mode.

============================================

D. Detailed steps (worked out example for stock kernel XWLP7)

STEP 1: Split off zImage.EndPad (Piece 4) from zImage file, so that zImage = zImage.ARM + zImage.EndPad

According to ARM boot / image loading specs, the four bytes at offset 44 = 0x2c store (in little endian byte order) the offset at which the ARM loadable zImage part ends. The following example shows how this information can be used to split the stock zImage (XWLP7) into zImage.ARM + zImage.EndPad:

head -c 48 zImage | tail -c 4 | hexdump -C
00000000 70 e1 4b 00 |p.K.|
00000004

head -c $((0x004be170)) zImage > zImage.ARM
tail -c +$((1 + 0x004be170)) zImage > zImage.EndPad

Then verify that this splitting is actually valid.

STEP 2: Find where Image.gz begins and ends within zImage.ARM

Gzip compressed files begin with the string '\x1f\x8b\x08' and end with four bytes representing the uncompressed size (in little endian byte order), so we will find where the gzipped part begins and ends by searching for these strings.

grep -P -a -b -m 1 --only-matching '\x1f\x8b\x08' zImage.ARM | \
cut -f1 -d: | head -1
18100

Thus the first 18100 bytes form Head.ARM (rest being Image.gz + Tail.ARM).

Since gunzip can uncompress compressed data even with "trailing garbage", at this point we can find the uncompressed Image size:

tail -c +$((1+18100)) zImage.ARM | gzip -dc | wc -c

gzip: stdin: decompression OK, trailing garbage ignored
10031748

We need the uncompressed image size in hex, which is 0x00991284, and in little-endian byte order this is the string '\x84\x12\x99\x00'.

grep -P -a -b -m 1 --only-matching '\x84\x12\x99\x00' zImage.ARM | \
cut -f1 -d: | tail -1
4972850

Adding 4 gives 4972850 + 4 = 4972854 as the offset where the gzipped part ends (this is where the 50-60 byte long Tail.ARM begins).

STEP 3: Split zImage.ARM = Head.ARM + Image.gz + Tail.ARM

Recalling that the gzipped part begins at offset 18100 and ends at offset 4972854, we can do the splitting by:

head -c 18100 zImage.ARM > Head.ARM
head -c 4972854 zImage.ARM | tail -c +$((1+18100)) > Image.gz
tail -c +$((1+4972854)) zImage.ARM > Tail.ARM
gzip -vt Image.gz # Make sure there is no error or warning
rm zImage.ARM

We now have completely split the original zImage file into four parts:

zImage = Head.ARM + Image.gz + Tail.ARM + zImage.EndPad

Note: Running "gzip -vt Image.gz" should return no errors / warnings. Also, verify that the parts concat back to the original:

cat Head.ARM Image.gz Tail.ARM zImage.EndPad | cmp zImage -

STEP 4: Backup Image.gz, uncompress into "Image", backup Image

cp Image.gz Image-orig.gz
gunzip -v Image.gz # There should be no errors/warnings
cp Image Image-orig

STEP 5: Locate the file default.prop in Image, and yank it out from Image

The file "Image" contains kernel code, a cpio archive of the initramfs tree, and additional payload following it:

Image = Kernel + Initramfs.cpio + Additional-Payload

Edit: The following code was modified for improved compatibility with grep -P.

Since Samsung stock SGS2 kernels do not compress the cpio archive, the "Image" file now contains the default.prop file as a substring. To locate it, run:

for n in $(grep -P -a -b -o 'default\.prop\x00' Image | cut -f1 -d:); do \
if tail -c +$n Image | head -c 200 | grep -q -P -a 'ro\.secure=\d'; then echo $n; fi; \
done
1852926

This is the offset at which the string "default.prop" --- the name of the file --- is stored in the initramfs cpio archive. One or more zero bytes follow this string, after which the real file contents begin. The second grep in the middle line above makes sure that the string "ro.secure=<digit>" occurs within 200 bytes following this occurrence of the string "default.prop".

Since the default.prop file is at most a couple of hundred bytes, we can view the section of Image containing it with hexdump as:

head -c $((1852926 + 200)) Image | hexdump -C | tail -16
001c45e0 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
001c45f0 30 30 30 30 30 44 30 30 30 30 30 30 30 30 64 65 |00000D00000000de|
001c4600 66 61 75 6c 74 2e 70 72 6f 70 00 00 23 0a 23 20 |fault.prop..#.# |
001c4610 41 44 44 49 54 49 4f 4e 41 4c 5f 44 45 46 41 55 |ADDITIONAL_DEFAU|
001c4620 4c 54 5f 50 52 4f 50 45 52 54 49 45 53 0a 23 0a |LT_PROPERTIES.#.|
001c4630 72 6f 2e 73 65 63 75 72 65 3d 31 0a 72 6f 2e 61 |ro.secure=1.ro.a|
001c4640 6c 6c 6f 77 2e 6d 6f 63 6b 2e 6c 6f 63 61 74 69 |llow.mock.locati|
001c4650 6f 6e 3d 30 0a 72 6f 2e 64 65 62 75 67 67 61 62 |on=0.ro.debuggab|
001c4660 6c 65 3d 30 0a 70 65 72 73 69 73 74 2e 73 79 73 |le=0.persist.sys|
001c4670 2e 75 73 62 2e 63 6f 6e 66 69 67 3d 6d 74 70 0a |.usb.config=mtp.|
001c4680 30 37 30 37 30 31 30 30 30 30 30 33 30 32 30 30 |0707010000030200|
001c4690 30 30 38 31 45 44 30 30 30 30 30 33 45 38 30 30 |0081ED000003E800|
001c46a0 30 30 30 33 45 38 30 30 30 30 30 30 30 31 34 46 |0003E8000000014F|
001c46b0 37 41 44 34 38 45 30 30 30 30 30 44 44 44 30 30 |7AD48E00000DDD00|
001c46c0 30 30 30 30 30 33 |000003|
001c46c6

The offsets in the left column are all relative to the Image file.

Note carefully that the default.prop file begins at offset 0x001c460c (following the null byte(s) after the file name "default.prop") and it ends at the newline character (0x0a) at offset 0x001c467f, making it a total of 116 bytes. Thus we can extract the file as:

tail -c +$((1+0x001c460c)) Image | head -c 116 > default.prop

STEP 6: Edit default.prop, overwrite part of Image with it, and recompress

At a minimum, edit default.prop to set ro.secure=0, without changing the file size (important), and overwrite the corresponding section of the Image file with the edited version:

vim default.prop # Do not change file size

dd if=default.prop of=Image bs=1 count=116 \
seek=$((0x001c460c)) conv=nocreat,notrunc

# Verify differences byte-by-byte
cmp -l -b Image-orig Image
1852987 61 1 60 0

# Re-compress modified Image
cat Image | gzip -c -9 -n > Image.gz
ls -l Image.gz

STEP 7: If size of new compressed Image is different, repeat Step 6

The files Image.gz and Image-orig.gz should have identical size. If they do not, then repeat Step 6 to further edit the default.prop file (without changing its size) and try again until the files have the same size. (In this example, just changing ro.secure=0 produced a differently sized Image.gz, but further changing ro.allow.mock.location to 1 produced a identically sized Image.gz.)

Once the files Image.gz and Image-orig.gz are equal in size, compare them byte-by-byte and see how many bytes differ:

cmp -l -b Image.gz Image-orig.gz | wc -l
3855968

Even if the compressed file sizes are the same, they may actually differ on a large number of bytes (as the above example shows a difference of in 3.9 million bytes). Keep repeating Step 6 by making small changes in the default.prop file until Image-orig.gz and Image.gz differ only on a few bytes. In this example, I also had to set ro.debuggable=1 (in addition to ro.allow.mock.location=1) to achieve this:

cmp -l -b Image.gz Image-orig.gz | wc -l
6

STEP 8: Concatenate all pieces back to form new insecure zImage

mv zImage zImage-orig
cat Head.ARM Image.gz Tail.ARM zImage.EndPad > zImage
cmp -l -b zImage-orig zImage | wc -l
6

If everything is done right, there should only be a few bytes of difference between the original stock kernel zImage-orig and our insecure kernel zImage (unless you are really unlucky).

====================================

E. Credits

This is an old android rooting method and I take no credit for it. Most of this information are available on the net and on this forum (but not necessarily exactly in this form). The following were especially helpful in finding out the internals of rooting and the structure of GT-I9100 stock zImage:

General android rooting guide by cjix:

http://cjix.info/blog/misc/how-rooting-works-a-technical-explanation-of-the-android-rooting-process/

ARM Linux booting guide by Vincent Sanders:

http://www.simtec.co.uk/products/SWLINUX/files/booting_article.html

GZIP RFC by Peter Deutsch:

http://www.gzip.org/zlib/rfc-gzip.html

SGS2 kernel zImage initramfs pack-unpack guide/scripts by Chenglu:

http://xdaforums.com/showthread.php?t=1294436

https://github.com/xiaolu/galaxys2_kernel_repack

GT-I9100 kernel build scripts by Samsung Open Source:

http://opensource.samsung.com

In addition, many well-known xda developers who have done a tremendous amount of work helping the community are:

Chainfire

Odia

lyriquidperfection

Intratech (for providing many stock kernels/roms)

And many others