某系統漏洞挖掘之固件分析
本篇章以提取加密的固件的文件系統為目標,從系統的啟動到內核解密到文件系統解密的加載做一個調試。
并對系統內核以及文件系統進行提取,為后面漏洞挖掘做鋪墊。以及提升自身對linux系統逆向知識面。
環境搭建
在官網可以看到有兩個版本可供下載。一個是gho一個是img,這里兩個都下載了。

從提供的下載包可以看出這是軟路由的系統,沒有硬件限制,所以下面用vmware進行了安裝。

配置好后,進入后臺管理界面如下:

初始環境安裝完后,發現系統并沒有提供輸入。所以需要上qemu進行調試將文件系統提取出來。
文件系統提取
使用qemu運行該系統:

對img進行進行提取,在grub中的menu.list可以看出initrd對應的是root.gz。

可以看到root.gz是屬于加密的。

GRUB調試
linux大概的啟動過程:
注:具體細節請參考GRUB源碼。
00.BIOS:
尋找啟動設備并將設備的第0個扇區載入到0x7c00處,即載入MBR
01.MRB:
從0x7c00處開始執行,并將第1個扇區載入到0x2000,并跳轉到0x2000處繼續執行,這里為加載Stage1.5。
02.Stage1.5:
將第1個扇區之后的幾個扇區裝載到0x2200,并跳轉到0x2200處繼續執行,并加載Stage2加載到0x8000處后跳轉到0x8200處開始執行。
03.Stage2:
讀取配置文件,根據配置文件進行執行操作,其中包括了內核鏡像(這里是bzImage)的加載,當內核鏡像加載完畢后會有3個地址需要關注。 1.linux_data_tmp_addr指向bzImage數據。 2.LINUX_BZIMAGE_ADDR指向壓縮后的kernel數據。 3.linux_data_real_addr指向部分bzImage數據從linux_data_tmp_addr開始將部分數據拷貝到linux_data_real_addr并切換到實模式跳轉到linux_data_real_addr+200處執行。
使用qemu+ida進行啟動調試:
./qemu-system-i386 -s -S -m 512 -drive file=Wowfk.img,format=raw,index=0
MBR
使用IDA附加,并在0x7c00處下斷后斷下,可以看到是磁盤的第0個扇區的數據。

隨后將第1個扇區載入到0x70000處。

將第0x70000的數據復制到0x2000即Stage1.5然后跳轉到0x2000處繼續執行。

Stage1.5
循環從第2個扇區開始載入到0x70000

接著將數據復制到0x2200+index處

數據載入完后跳轉到0x2200處繼續執行

在經過一系列的初始化后,便開始將Stage2載入到0x8000,大小是0x400
注:圈起來的函數是grub_read,參考源碼:/stage2/stage1_5.c:cmain

剩余的Stage2數據載入到0x8400,并跳轉到0x8200處執行
call near ptr unk_2360在源碼中對應的是/stage2/asm.S:chain_stage2用于轉移ip0x8200的代碼位于:/stage2/asm.S:_start

在asm.S:_start結尾調用了init_bios_info

init_bios_info最后跳轉到了00027E9C(/stage2/stage2.c:cmain)處繼續執行

最后開始解析配置文件尋找對應的處理函數進行調用,如下是kernel命令對應的。

/stage2/builtins.c中對應的kernel_func函數,最終調用load_image



load_image中在將bzImage的前0x2000大小的數據讀取到0x66000處后,再從0x66000處復制到linux_data_tmp_addr(0x5CFD30)中。

繼續讀取之后的0x1800字節,保存到linux_data_tmp_addr+0x2000處。

接著將0x3800之后的所有數據讀取到LINUX_BZIMAGE_ADDR(0x100000)。

...
當到了boot命令后,便進入boot_func函數。


kernel_type是3,最終調用big_linux_boot函數。


在/stage2/asm.S:big_linux_boot中可以清晰的看到
從linux_data_tmp_addr將0x9400大小的數據復制到(linux_data_real_addr)0x90000

切換到實模式后,通過jmp far跳轉到90200處執行,改變了cs=9020 ip=0

那么真實的ip=cs*16+ip=90200,顯然16位的寄存器存不下該值,所以通過來段寄存器的方式去執行。
且ida并不能去識別這樣的進程環境,導致出現了反匯編的窗口指向了錯誤的頁面但能F7 F8。

解決這樣情況的辦法就是將cs清零,將ip修改成正確的地址即可,但是遇到ip被改變的需要修改回原來的樣子再執行。所以等一手正版人員給ida提一個issues,非常感謝!

經過一頓反復的下斷調試,雖然其中對應的是linux源碼中的/boot/main.c,但因為ida的問題還是很難調試,最后切換到保護模式跳轉進入LINUX_BZIMAGE_ADDR(0x100000)

至此,就進入了內核,GRUB的過程結束。
內核調試
注:該處執行的代碼均在/arch/x86/boot/compressed/head_32.S中
從LINUX_BZIMAGE_ADDR處往后執行,會將加密的內核數據從LINUX_BZIMAGE_ADD復制到0x17bb000,接著并跳轉到0x17bb000+0x4C7EDC執行

隨后將內核解密,并將該快內存dump

使用vmlinux-to-elf將內核程序提取出來

等待將內核解壓后開始調用parse_elf函數

完成后會將內核程序的+0x1000處填充到0x1000000,并跳轉到該處執行。

在0x1000000往下執行,重新將內核復制到0x81000000處,并跳轉到0x818B6CEB

經過了漫長的調試,配合linux源碼看,最終定位到了0x81BDC9CE處,調用的函數具體是干嘛的我也不知道,只知道eax是指向root.gz解密后的數據,edx為長度。

最后寫腳本將所有數據進行dump:

成功dump出文件系統:

總結
1、ida對此類的調試環境,反匯編支持并不友好。望一個好心人提一個issues!
2、在保護模式和實模式的切換間16位和32位的調試,對系統底層有了一個模糊的了解。
3、心一定要穩,不要浮躁,F8一定不能快!