//weibo: @少仲
技術背景:
Android手機獲得Root權限,可以讓/system和/data分區獲得讀寫的權限.這兩個分區的權限配置,一般在根分區的init.rc文件中,修改這個文件可永久獲得root權限.衆所周知,市面上絕大部分的Android手機文件系統有三個分區,分別是/,/system,/data.根分區(/)是打包爲ramdisk.img後,再與kernel的zImage打包爲boot.img. boot.img在EMMC/NAND中以RAW DATA的形式存在,且除使用燒寫工具外,無法讀寫.正因如此,根分區(/)在每次開機時都會從存儲器中加載到RAM, 所以根分區(/)是難以不被刷機破解的.
那麼如何破解?
獲得boot.img,解壓boot.img得到ramdisk.img, 再由ramdisk.img解壓得到root目錄(/),修改其中的init.rc文件,再打包,最終得到新的boot.img.最後利用燒寫工具boot.img燒寫到手機即可.(具體可參考AOSP/mkbootimg)
淺析boot分區結構
具體分析:
一. 提取kernel文件
(1)boot在哪裏?通過下載官方的rom包,解壓縮後可以看到裏面的boot.img文件然後分解
關於rom包,簡單介紹下里面的文件:
boot.img Linux內核和基本文件系統的內核包
system.img 系統的/system目錄
recovery.img系統恢復程序所用的鏡像
userdata.img 系統的/data目錄
/data/app 用戶應用程序
/system/app系統應用
/system/fonts 字體
/system/media 開關機動畫
/system/media/audio其他音頻,鬧鈴,提示音等
META-INF 刷機腳本信息和簽名
/system/etc系統配置文件夾,恢復出廠設置後都從這裏和build.prop調用配置來覆蓋出錯的配置,裏面init.d文件夾可以放入腳本,腳本名字前加數字表示優先級
build.prop手機信息
(2.1)boot在哪裏?真機中提取(這裏以Samsung Galaxy S4 爲例)
adb shell進入真機, ls -l/dev/block/platform/msm_sdcc.1/by-name
msm 代表高通的芯片
這個msm_sdcc.1是外接的SD卡掛載的目錄,by-name指的是這個sd卡分區的名稱
現在可以通過dd命令將boot.img提取出來
dd if=/dev/block/mmcblk0p20 of=/sdcard/boot.img
(2.2)boot在哪裏?真機中提取(這裏以Lenovo A300t爲例) 另一種查找boot.img的方法
首先介紹下手機分區信息:
主要有mtd分區和emmc分區
一般刷機包簽名文件夾裏的刷機腳本那裏有updater-script文件.
例如:
format("ext4","EMMC", "/dev/block/platform/xxxx", "0","/system")是EMMC分區的,
format("yaffs2","MTD","/dev/block/platform/xxxx", "0", "/system")是MTD分區的
MTD是用於訪問memory設備(ROM、flash)的Linux的子系統.MTD的主要目的是爲了使新的memory設備的驅動更加簡單
EMMC 結構由一個嵌入式存儲解決方案組成,帶有MMC (多媒體卡)接口、快閃存儲器設備及主控制器—— 所有都在一個小型的BGA 封裝.接口速度高達每秒52MB,EMMC具有快速、可升級的性能
cat /proc/mtd
可以通過同樣的方式,使用dd命令,將boot.img文件提取出來.之後我們可以通過adb pull的方式把從真機中提取的boot.img文件提取到本地.之後用split_boogimg.pl 將提取的boot.img分解出來,可以得到boot.img-kernel
二. 逆向分析kernel文件:
一般來說,這個kernel內核文件都是一個gzip的壓縮格式,偶爾也會有其他的壓縮算法.比如三星S5和MX4的內核壓縮算法是lzop
爲什麼是這樣的情況呢?簡要說下:
Kernel被載入內存後是以壓縮的狀態存放在磁盤之上的,他頭上的一段代碼就是爲了初始化並且解壓縮的一段代碼.這段代碼把自身壓縮的一部分代碼解壓出來.放到真正內核所在的位置上.所以他所用的壓縮算法要從他頭上那段來指定的.所以如果今後廠商使用一些自己的壓縮算法,那麼需要更多逆向分析的.
我們可以通過binwalk工具來判斷是gzip的壓縮格式還是lzop.
如果是lzop可以直接從第二段的地址dd出來然後使用命令解壓縮.
如果是gzip的壓縮格式則使用下面的腳本文件解壓.
#!/bin/bash
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
現在可以將它導入到IDA中分析,但是導入IDA後發現效果很不理想
這是因爲我們需要符號表.但是android的linux內核是一個平面結構,它沒有符號表.如果大家編譯過linux內核可以發現編譯好的zImage被打包到boot.img文件中去,下面有個巨大的vmlinux,它放到gdb裏面去調試可以看到完整的符號表和調試信息.刷入手機的版本是沒有的.
但是在/proc/kallsyms可以提供所有的kernel symbol
cat /proc/kallsyms 會發現地址都是0
爲什麼地址都是0呢?
因爲有一個patch 在/proc/sys/kernel/kptr_restrict默認爲1,就隱藏了symbol
只要我們將其置0就可以正常打印
如果我們之前在刷機的過程中放進去一個su授權文件,那麼我們就可以
echo 0 > /proc/sys/kernel/kptr_restrict
cat /proc/kallsyms > /data/local/tmp/syms.txt
但是我們目的是爲了root,這裏如果沒有su文件呢?
我們還有其他的方法.kallsyms既然可以打印symbol,那麼說明symbol肯定在內核之中的.它就在__ksymtab裏面.我們肯定可以知道一些常見的內核函數,比如kallsyms_lookup_name.我們可以在這個內核的二進制文件中找它的字符串.找到它的相對位置後,然後拿它的地址去搜索
我們可以搜到一個結構
從0xC0008000開始暴力搜索連續若干個具有上面的特徵的內存,就可以找到內核的導出符號表,有了符號地址後,手動在ida中添加地址,就可以分析了