Bambook破解筆記

作者:曾半仙

原文鏈接:[原創]Bambook破解筆記-『智能設備』-看雪安全論壇

感覺這個屬於一個硬體分析的筆記, 比起android版發這裡更合適.

word版不知道怎麼附件傳不上, 只得一點點粘過來

從硬體到軟體

引子

事情的開頭要從一個國產單片機群說起. 群里潛水多年, 經常會遇見一群神人發表這樣的言論:

1 我的代碼檢測到自己被修改後, 就會擦寫自毀

2 我的代碼會把電源連到地上, 讓晶元自毀

3 我的代碼會控制一個接地的gpio引腳輸出VCC, 燒了晶元

4我的代碼會控制2個連在一起的gpio一個輸出VCC一個輸出GND, 燒了晶元

5我的電路會在打開產品外殼時候, 產生高壓擊毀晶元

6 我的電路會在抄板後, 產生短路, 工作不起來

7我的電路板上一些元件是錯的, 標的是電阻其實是電容

看起來大多數程序員都對"燒毀晶元"有特殊的愛好

錦書硬破

可能以後我會寫一些笑話帖子時候, 再繼續這個話題, 今天我們說的是, 改造盛大的一個封閉系統電紙書.

當年, 大部分的eink reader都經過深度定製和限制, 不支持用戶安裝app, 開機自動進入閱讀器主程序. 看書, 設置, 書城, 全部一個閱讀器apk搞定.

這機器型號是SD928, 面向公眾的第一款. 從出錯畫面看, 系統應該是android. 我曾經想要把它的系統dump出來, 然後研究自製固件, 刷機, root. 為此還買了一個碎屏的機子, 準備拆下來用編程器讀取. 但當年我烙鐵焊不下來屏蔽罩, 當時也有辦法就是從背面挖穿, 但完美拆機主義的我, 捨不得破壞完整的電路板.

很多年過去了, 偶然淘寶一看還有人在賣壞機, 電池鼓包. 賣家表示應該還是好的, 遂77元購入, 拆開一看已經鼓得後蓋變形了, 還好電路板沒事. 又起了念頭繼續研究下刷機, 說時遲那時快, 請了熱心網友幫我拆下了eMMC晶元, 又吹到eMMC轉接板上, 插讀卡器, 讀取. 然後從dump下來的分區發現, 這機器的eMMC晶元是當作用戶數據用的, 系統並不從eMMC啟動. 不過eMMC的分區裡面, 存儲的有解密後的固件鏡像. 應該是上次刷機過程殘留的.

既然系統不從eMMC引導, 那麼是從哪裡引導的呢? 在電路板上找到另一塊之前忽略的晶元MT29C2G24MAKJAJC-75, eMCP的, 封裝了DRAM和NAND flash.

通過解壓固件鏡像, 我們可以提取系統的rootfs, 通過分析發現固件由PC端發到設備, 設備從伺服器獲取key, 解密後在/mnt/data/updates目錄得出snda_s.firmware(System), snda_l.firmware(Logo) , snda_k.firmware(Kernel), boot_nontrust.bin(Bootloader) 幾個文件 , 再通過fwupdate和bootloaderupdate工具刷到eMCP中.

官方的固件裡面, adbd是禁用的, usb連接上以後就是一個RNDIS設備, 沒有其他功能.

如果我們能在系統開機後, 執行fwupdate, 我們就可以實現本地刷機了. 之前主板的eMMC拆下後, 量出來了DAT/CLK/CMD等和旁邊的電阻對應的接線關係, 我們可以實現免拆晶元在板燒寫eMMC, 但還缺乏一個入口.

圖1-1 在線燒寫eMMC焊線圖

經過多次嘗試, 發現當在菜單裡面選擇"恢復工廠設定", 系統就會執行/mnt/data/scripts/clear-user-data.sh腳本文件.

同目錄下還有個start.sh, 但它只在刷機下一次進入系統前執行. 以後每次重啟和開機都不會執行. (可能老版本固件也會每次執行吧)

因為我們測試比較頻繁, 所以選擇修改clear-user-data.sh, 去掉該腳本原有的清除用戶數據命令, 並且加上如下幾行

setprop service.adb.tcp.port 5555

/mnt/data/scripts/tinyftp -s 0.0.0.0 -p 3389 -c / > ./ftp.txt &

/sbin/adbd > ./adbd.txt

這樣在系統設置裡面執行"恢復工廠設定"以後, 就會在5555埠提供adbd服務並且在3389開啟ftp服務.

這個tinyftp是從網上的代碼修改的, ndk編譯, 需要設置APP_PLATFORM := android-3.

然後我通過固件解包再打包的方式, 修改init.rc, 生成新的snda_k.firmware重新刷機, 成功後就開啟了adbd服務並且可以通過wifi連接上了.

硬改到軟改

當然, 我不可能讓大家都跑去拆機硬改, 這只是挖出廠家做為"秘密"封閉起來的鏡像和系統架構的攻城車.

有了adb shell以後, 我們可以編譯一些工具進系統測試了. 考慮先試試老的CVE有沒有辦法利用, 查看系統是android 1.5, 內核2.6.28, 找了一個瀏覽器入口的CVE-2010-1807, 影響Android 2.2以前瀏覽器.

錦書默認是個封閉系統但後面新的固件支持閱讀器(SndaBrowser)裡面運行html的widget, 可以用來測試該bug. 我做了一個widget打開這個cve網頁. 再在閱讀器裡面載入.

經過反覆測試和dump SndaBrowser進程, 我修改了Itzhak Zuk的利用代碼, 企圖讓該shellcode在錦書上執行. 最後試驗結果是, 會引發Segmentation fault, 內存裡面滑板代碼也得到了填充, 但並沒有成功跳轉. 具體分析原因當時釘釘是web版沒記錄記不清了, 在此感謝村長的幫助.

圖1-2 SndaBrowser崩潰前的內存dump

不過不用擔心, 別人的洞不好用自己挖也可以. 之前分析它官方PC客戶端跟設備之間通訊的時候, 我們在libSndaEBook.so的onSyncServerReceiveFile函數看到一些可以利用的地方:

此bug有2個子bug, 其一是拼接字元串時候, 用到了filename, 這個字元串我們可以通過自製PC客戶端來控制, 在文件名中傳入分隔符後, system調用可以執行額外指令.

其二是install-bookimage.sh寫的不好.

我們來看一下install-bookimage.sh:

#!/system/bin/sh

echo Decompress book

image to /mnt/data/

tar zxf $1 -C /mnt/data/

echo Install keybase

chmod 777

/mnt/data/key/skb-newrsa-file

/mnt/data/key/skb-newrsa-file

/mnt/data/key/rsa-pair.txt

echo Remove key

directory

rm -r /mnt/data/key/

echo Install book image

success!!

這個腳本做的事情非常簡單, 將PC端發來的文件解壓到iNAND的用戶分區, 然後給/mnt/data/key/skb-newrsa-file加上可執行許可權, 再執行它.

我查看了下, 用戶分區本身沒有這個可執行文件. 也就是說設計上他是上一步釋放出來的. 很顯然, 這是一個比剛才更好的bug, 它不但能夠直接植入文件, 還順便幫你加可執行許可權, 再執行它.

從提示信息來看它應該是廠家為了弄一些"正版圖書大禮包"預留的介面, 不是開放給用戶使用的. 經過分析, 官方的雲梯客戶端, 也沒有這個介面.

這裡跟剛才那個case屬於同一個switch, 可以看到傳送固件也是走的這個介面, 只是封包中包含的fileType欄位不同. 我們可以模仿官方的協議來通信, 或者偷懶在官方的dll上補一下實現我們的需求.

經過分析BambookCore.dll, 我們找到一處給設備傳輸書架信息的ReplaceCatelogFile函數, 它會給設備發一個fileType是12的catelog.xml文件. 注意這裡的0D設備上switch的fileType將是0C.

.text:1000B5C7 0A4 C7 44 24 7C 0D 00 00 00 mov [esp+0A0h+type], 0Dh

.text:1000B5B0 0A4 68 C4 0F 0E 10 push offset aCatalog_xml ; "catalog.xml"

PC端通訊參數

設備端邏輯分支

這個可以結合上面shell腳本的bug, 實現執行我們的代碼. 我做了一個小demo, 把mov補為0E, 然後發送自己構造的tar.gz

我們把tar包裡面key/skb-newrsa-file換成了可執行文件(後來發現腳本也能用), 感覺它這個skb-newrsa-file原本是可執行文件, 作用是給snb文件安裝授權的 (因為不是用戶通過雲中書城下載的私有書).

經過測試成功得到執行, 而且這個bug中開啟的shell是root許可權. 我們設置了adbd的屬性後直接執行adbd, 就可以通過內置的fwupdate進行刷機了. 該工具使用未加密非打包的固件鏡像, 也不用考慮再封包的問題.

由軟入硬

刷機工具和Bootloader

通過分析刷機工具和系統mtd設備, 我們整理出了eMCP的NAND分區劃分:

0x00000000-0x00100000 : "Bootloader" active

0x00100000-0x00400000 : "Kernel"

0x00400000-0x04a00000 : "system"

0x04a00000-0x06300000 : "userdata"

0x06300000-0x06400000 : "logo"

0x06400000-0x06500000 : "Bootloader_backup" factory? mdtblock5

0x06500000-0x06800000 : "Kernel_backup" factory mdtblock6

0x06800000-0x0ae00000 : "system_backup" factory mtdblock7

0x0ae00000-0x0af00000 : "logo_backup" factory mtdblock8

0x0af00000-0x0b200000 : "Kernel_reserve" shadow mtdblock9

0x0b200000-0x0f800000 : "system_reserve" shadow mtdblock10

0x0f800000-0x0f900000 : "logo_reserve" shadow mtdblock11

0x0f900000-0x0f920000 : "flags" mtdblock12

0x0f920000-0x0f940000 : "systeminfo" mtdblock13

0x0f940000-0x0f9c0000 : "EinkFW" mtdblock14

可見Kernel/system/logo都存在了3份拷貝. 最前面是活躍的, 刷機時候fwupdate參數可以控制刷入到backup(出廠預設)或者reserve(正常刷機)的分組, 不會刷新當前活躍的分區.

我們推測Bootloader會在刷完機下次引導時候選擇reserve複製到活躍的分區.

額外提一句, 這裡的分區表是在設計系統時候預先劃分好的, bootloader和系統都使用同樣的硬編碼偏移. 所對應的地址就是NAND的原始地址.

我們先看Bootloader. 根據Marvell的文檔(PXA3xx_TavorP_BootROM_Ref_Manual.pdf), 我們知道它是有結構的, bootrom按照頭部進行裝載. 不是整個裝入內存或者掛載到指令匯流排上從0偏移執行.

可以看出這個Bootloader沒有Trusted標記, 有3個子image, 一個是TIMH, 就是這個頭部本身. 一個是OBMI, 位於flash的0x20000偏移, 會被bootrom載入到內存的0x5C013000, 另一個是OSLO, 位於0x40000會被載入到0x81000000.

那麼OBMI跟OSLO是不是就運行在這2個內存地址呢? 這個我們要再次確認.

查看OBMI的ResetHandler, 它會把自身image複製到0x800007FC位置, 0x80000000位置存放的是ResetHandler地址本身, 這中間的7F8空間可能是被用作變數空間了. 這類的image, 並不像大家想的那樣都是位置無關的, 大部分都是位置相關再加一個檢查/複製/跳轉代碼的.

舉個例子, iBoot的Reset Handler, 根據VectorTable地址我們可以判斷代碼需要運行在9FF00000地址, 通常iBoot的loader會初始化好內存map並將其載入到合適的地址, 但iBoot自身也額外帶了檢查和複製代碼.

當發現自己不在指定位置時候, 就會依次複製代碼/常量/變數到指定的地址.

這些信息可以用來指導我們進一步劃分/增加區段.

代碼2-1 iBoot拷貝片段注意ADR是取PC相對地址.

圖2-2 初步整理後的iBoot區段

有一些晶元, 是支持從flash直接執行的, 不需要佔用內存. 這時候不管是rtOS還是baremetal的image, 都會有一個從鏈接後的image中, 複製一部分已有初值的變數到sram/dram的流程.

具體的內存布局不同, 要看對應的編譯鏈接腳本和安排這一切的程序員.

介紹這些並不是廢話, 在我們分析時候, 進行正確的分段和添加堆的內存區域能提高逆向效率.

例如iBoot我們確定了.data這部分信息, 逆向過程中可以新建RAM段並且載入, 以便觀察一些全局變數的初值.

最後我們確定在OBMI中包含名為Blob的xscale專用bootloader, 運行在80008000. 在flash的偏移是40000.

正常刷機流程中, 應該是fwupdate設置systeminfo分區的裡面的幾個標誌位, 下次啟動時候, bootloader檢測這個標誌位並更新活躍的分區, 再去掉該標誌.

而另一套備份固件, 懷疑需要特殊的按鍵組合觸發. 我們怎麼找這些按鍵的組合呢? 首先這些電子設備, 一般不會像電腦一樣, 還做一套PS2/USB介面來外掛鍵盤. 最為常見的線路接法有GPIO單獨檢測, 行列掃描兩種. 兩種都支持多鍵組合檢測. 該設備cpu型號是PXA310, 根據marvell提供的介紹, 該cpu具有directkey和matrix key兩套鍵盤介面. 它的matrix掃描是片內設備實現的, 並且支持自動掃描, 我們可以直接從Keypad Matrix Key (KPMK) Register讀取有哪幾行被按下, 然後從KPASMKP0~KPASMKP3讀出每行按下的列. 每個寄存器包含2行, 一共64個按鍵都在其中.

通過查找參考手冊裡面的寄存器地址, 我們在IDA中尋找敏感的地址, 找到如下代碼

(參考手冊PXA3xx_DM_vol_III.pdf, Table 254: Keypad Controller Register Summary)

ROM:8000F5FC 41 04 A0 E3+ MOV R0, #0x41500000 ; keypadbase

ROM:8000F604 3D FD FF EB BL func_config_keymatrix_MFPR

各位, 你們一定以為我是二進位搜索00005041來定位的吧, 其實還真不是, 我在整理函數名時候把初始化部分挨個看了一遍, 所有落在外設範圍內的地址我都肉眼觀察了一下.

再說了, XScale是活在沒有thumb2 (主要是MOVW, MOVT)的時代, ARM代碼中很常見的優化會把一個常數用各種移位和算術運算拼出來, 拼不出來才會用2條LDR加一個函數末尾的常數的方式(效率較低). 二進位搜索很難奏效.

不過貼心的IDA裡面默認會將拼接常量的指令合併為一條偽指令. 使用Search immediate value即可. 當然也要注意有時候編譯器在拼接常量的過程中插入了其他指令, 試試文本搜索(針對最後一次拼接的注釋)和搜索常量的一部分或許也有找到的時候.

這裡說句題外話, 在movw/movt中間嵌入svc或者自製的不影響寄存器的call, 也是我以前喜歡的干擾IDA分析方法之一.

ROM:800098F0 2E 19 00 EB BL _DoUpdateFirmware

ROM:800098F4 FA FE FF EB BL func_check_eink_firmware

ROM:800098F8 40 FE FF EB BL func_mayshowlogo

ROM:800098FC 41 04 A0 E3+ MOV R0, #0x41500000 ; kpcbase

ROM:80009904 BB 19 00 EB BL _Key_DoUpdateFirmware

同時我們找到的還有另一個使用該地址的函數, 將會在後面分析.

首先看初始化鍵盤控制寄存器的, 我們根據代碼的配置可以得出需要測量的CPU引腳編號, 再根據引腳編號測量硬體按鍵對應的GPIO.

這裡注釋的內容是跟後面提到的func_config_UART3_and_enable_clock一樣的方式推導出來的.

至於BGA腳位, 參考文檔PXA3xx_EMTS.pdf, 4.1.2.1 PXA310 Processor 13mm2 VF-BGA Ball Map

圖2-6, 翻頁鍵排線座

我們用夾子夾到這些引腳上, 再測試它和哪些cpu引腳相通, 列表記錄.

表2-7 翻頁鍵接線

此表分別記錄了BGA引腳編號 GPIO編號設計中角色

圖2-8, 五向鍵排線座

表2-9 五向鍵接線

這裡翻頁鍵是1*2矩陣, 五向鍵是6*1矩陣.

電路板上有一個區域是窩仔片構成的導航和數字鍵, 我們按照橫向ABC豎向abcdef助記方式, 測試每個鍵處於哪個交點上.

為了測量和描述方便, 我們把橫向稱為行(row), 縱向叫做列(col), 其中行為輸入, 列為輸出. 這也跟Marvell的設計一致.

圖2-10 數字和菜單鍵矩陣相同的大寫字母為同行相同小寫字母為同列

首先我們看看軟體中的col分別對應這些導航和數字的哪幾列

col0=a col2=b col4=c col7=d col5=f

不在這個主板上的五方向鍵其實也佔了單獨一行.

fivekey=row6

A nav/num 789 B14(GPIO119) = row4

B memu/num456/# C15(GPIO118) = row3

C num123*0=E14(GPIO117)=row2

e num258 = Y20(GPIO5 _2)=col6

以下略

整理矩陣表如下

hold 上側C12(GPIO127), 角色KP_DKIN<0>, 它估計設計時候每次刷新一下墨水屏就睡眠, 所以用DirectKey來做中斷喚醒.

音量加減也是矩陣, 音量按鍵右側公共端是行, CPU腳位測得W20(GPIO3_2), 對應row7, 音量減的列為F14, 音量加為a(E13)

然後接著看另一個使用了0x41500000做參數的Key_DoUpdateFirmware函數, 其中kpcbase就是傳遞來的0x41500000, 通過查編程手冊我們可以搞清這些寄存器的用途, 為了方便聽眾老爺我記錄在剛才那張表裡了. 然後讓我們來還原這些按鍵檢測的真面目:

代碼2-12 手動恢復固件片段

我們略過第二層if從它末尾看, 在上面的第一層if包含了以下的判斷: 第一個的true分支根據代碼看是檢查硬體加密晶元, 觸發條件顯然是菜單+8, 第二個if的false分支則是檢測音量+/音量-同時按下, 執行DoReservedFirmwareUpdate.

我們再看上面第一次if那個判斷的false分支, 也就是[找書]+[1]成立, 執行的是DoFactoryFirmwareUpdate

這個g_sysflags實際是systeminfo分區存放的內容. unReserveFWReady和unFactoryFWReady分別是指示普通升級過程的保留分區是否存在數據和出廠固件分區是否存在數據.

我們在系統的fwupdate和對應的so裡面, 同樣可以找到將固件寫到這兩套分區並設置不同的標誌的代碼. 這裡估計是做的防護措施, 一旦活躍的固件不小心損壞了, 售後人員可以直接操作組合鍵先來恢復一下出廠固件, 也不用把一些內部維護工具發到維修站, 以免泄露出去.

當然這個機器, 除了Bootloader級別的組合鍵, 還有進入系統後再檢測的組合鍵.

下面講一下發現的過程. 我假裝無意中從它的system分區發現了一些有意思的apk, 比如FoxconnTest.apk, SndaBrowser.apk. 前者是富士康測試程序, 後者是該閱讀器的主界面.

當初我假設機器的啟動腳本中, 根據某個特殊的按鍵組合, 選擇啟動二者之一. 後來實際分析發現並非如此. 在snda.browser.Browser中發現一個IsDiagMode函數

public boolean IsDiagMode()

{

if (new File("/dev/sagadiag").exists())

{

Log.d("Browser", "!!!!!!!!!!!!!! is in DiagMode");

return true;

}

Log.d("Browser", "!!!!!!!!!!!!!! is not in DiagMode");

return false;

}

他靠設備目錄下是否存在/dev/sagadiag判斷是否是DiagMode.

接著看在onCreate中有如下代碼引用這個函數

this.m_isInDiagMode = IsDiagMode();

this.isInFoxConnDiagMode = getOtherDiagMode().equals("1+7");

這個getOtherDiagMode()就是讀取/proc/driver/diagmode的3位元組內容.

我們可以認為它至少有兩種診斷模式.

然後搜索DiagMode, 找到另一段代碼:

public boolean factoryTestOnKeyUp(int

paramInt, KeyEvent paramKeyEvent)

{

Log.d("Browser", "factoryTestOnKeyUp, keyCode =" + paramInt);

switch (paramInt)

{

default:

return false;

case 8:

startActivity("com.android.mfgri", "com.android.mfgri.MfgRIAct");

return true;

case 9:

startActivity("com.iacs.fa", "com.iacs.fa.SagaFA");

return true;

case 10:

startActivity("com.iac.encrypt", "com.iac.encrypt.encrypt");

return true;

case 11:

startActivity("com.iac", "com.iac.DemoApp");

return true;

}

if (this.isInFoxConnDiagMode) {

startActivity("com.foxconn", "com.foxconn.FoxConnTestActivity");

}

return true;

}

這段代碼是在onKeyUp里調用的

if ((this.m_isInDiagMode) || (this.isInFoxConnDiagMode)) {

return factoryTestOnKeyUp(paramInt, paramKeyEvent);

}

前四個姑且不論, 這個com.foxconn.FoxConnTestActivity正是我們的富士康測試app. 他將在/proc/driver/diagmode 的內容為"1+7"時候啟動測試界面. 也就是說SndaBrowser這裡充當了Launcher.

我們在解開的initramfs和system包中分別搜索diagmode和sagadiag, 發現以下有意思的內容

DIAG_MODE=`cat /proc/driver/diagmode`

if [ "$DIAG_MODE" = "1+5" ]; then

mke2fs -j /dev/block/mmcblk0p2

else

e2fsck -yf /dev/block/mmcblk0p2

tune2fs -j /dev/block/mmcblk0p2

#e2fsck -p /dev/block/mmcblk0p2

tune2fs -m 0 /dev/block/mmcblk0p2

fi

mount -t ext3

/dev/block/mmcblk0p2 /mnt/data

這段是用來選擇格式化/mnt/data或者檢查分區的.

DIAG_MODE_CONTENT=`cat

/proc/driver/diagmode`

if [ -n "$DIAG_MODE_CONTENT" ] && [ "$DIAG_MODE_CONTENT" != "1+9" ] && [ "$DIAG_MODE_CONTENT" != "1+5" ]; then

echo "FA MODE: $DIAG_MODE_CONTENT"

rmmod g_ether

insmod /system/lib/modules/g_file_storage.ko file=/dev/block/mmcblk0

if [ "$DIAG_MODE_CONTENT" = "1+4" ]; then

vcom_foxconn_test

exit 0

fi

else

echo "Snda Init Script"

busybox cp /system/app/snda_instsp.sh /tmp/snda_instsp.sh

chown system.system /tmp/snda_instsp.sh

chmod 777 /tmp/snda_instsp.sh

/tmp/snda_instsp.sh

fi

這一段則是去掉usb口RDNIS共享上網的模塊, 並將eMMC晶元掛載為U盤. 應該是工廠用來複制用戶數據分區的.

從我們這裡和剛才的信息看, 1+7, 1+5, 1+9,可能就是鍵盤上數字鍵的組合.

圖2-19 Foxconn Diagnostic截圖

經過驗證, 成功進入這個Foxconn測試界面.

改造Bootloader計劃

上面我們分析時候, 看到Bootloader有一個輸出信息是

Autoboot aborted

Type "help" to get a list of commands

我懷疑它可能留有調試用的串口, 可以用來查看這些輸出信息.

雖然這個Blob編譯時候因為bootdelay設置為0導致無法進入這個boot console, 但我看了下裡面很多命令都沒有閹割. 包括通過串口/網卡刷寫flash, 聰明的聽眾們是不是猜到這個有什麼用了, 對這個可以預防我們做固件時候變磚. 不過我們已經有更好的方案, 就是找到的組合鍵可以選擇恢復backup或者reserve的固件之一, 只要我們不神經一次性刷壞兩套, 總可以進到系統的.

我們補上進菜單功能的話, 可以不怕一次刷壞兩套備份.

或許有人說了, 刷firmware的幾個分區沒有危險, 如果我們改Bootloader不小心某次改壞了, 它的恢復firmware分區功能無效了, 那怎麼辦, 不要緊, 我們還有JTAG.

JTAG接線的挖掘過程可以看附錄的"尋找測試點"章節.

Jtag介面本身的介紹, 估計大家一搜就是什麼標準專家組, 什麼多晶元串聯接法, 我要說的是沒那麼複雜, 我們改造出來的JTAG刷機跟使用手機開發板/單片機開發板一樣都是一對一的.

TODO:

如何製作刷不死bootloader, 如何找uart

我們在Bootloader的代碼中, 可以看出puts依次調用SerialOutputString, serial_write, serial_driver->write(c).

查看該全局變數賦值位置:

serial_driver = &g_liittleton_serial_driver;

再次查看g_littleton_serial_driver, 它的幾個成員如下:

ROM:8002B5CC 28 7B 01 80+g_liittleton_serial_driver uart3_funcs <func_uart3_init, func_uart3_getch, func_uart3_putch,

ROM:8002B5CC DC 7A 01 80+ ; DATA XREF: _main+38o

ROM:8002B5CC 04 7B 01 80+ ;

ROM:off_800099B0o ...

ROM:8002B5CC B4 7A 01 80+ func_uart3_poll, func_uart3_flush_input,

ROM:8002B5CC 3C 7A 01 80+ func_uart3_flush_output>

為什麼我們那麼確定這個串口是uart3, 因為func_uart3_init裡面調用的配置函數如下:

void func_config_UART3_and_enable_clock()

{

v40E10628 = 0xC041; // GPIO109 = UART3_TXD

v40E1062C = 0x4041; // GPIO110 = UART3_RXD

v4134000C |= 0x800000u; // D0 Mode Clock Enable Register A (D0CKEN_A)

// CKEN23 = 1

// UART3 Clock Enable

}

它這些地址在Marvell的文檔裡面可以查出來是對應什麼外設範圍, 叫做什麼寄存器. 前面2個就是配置"路由", 也就是物理引腳切換到什麼邏輯設備上(時間長了, 此處需要核對手冊).

可以查看手冊PXA3xx_DM_Vol_I.pdf, Table 12: PXA31x Processor Alternate Function Table, Table 16: PXA31x Processor Pad Control Addresses, 4.11.2 Multi-Function Pin Registers (MFPR)

前面一個表是描述每個物理引腳可以連接到的功能, 而第二個表就是控制這些功能的寄存器地址, 最後一張表則是描述這個寄存器地址寫入的值功能. 我們主要關心它最後3bit, 在我們這兩句賦值里, 都是0b001 = Alternate function 1

至於這些func_uart3_init風格函數名是根據功能和Blob網上找到的參考代碼整理的. Bootloader本身當然不帶符號.

我的習慣是如果能確定官方的函數名我就不用func_前綴. 這樣在功能列表打個過濾可以明顯的看出有多少sub_是完全沒搞清楚功能, 多少func_是沒有官方名稱.

我們來看main函數裡面引導部分, 盛大發布的固件設置了boot_delay為0並且刪掉了等待串口字元打斷自動引導的代碼, 不過不知道為啥後面那個命令模式的console功能卻沒刪. 而它那個比較超時時間大於-1的也沒刪. 簡單的改的話, CMN R3這裡改為比較0, 就會次次都進命令模式. 或者改為一個按鍵檢測, 複位時候按住某個鍵則不引導系統, 進命令模式, 當然要修改的盡善盡美, 可能需要補充較多的子函數, 這時候手工補opcode就很煩了. 不過還好, 之前我做過一個insanelinker, 用來補3ds的遊戲跟iBoot的, 能把obj文件嵌入或附加到二進位文件里並解析符號, 當然符號要從IDA導出. 如果用於這個Bootloader, 要將修棧和搬代碼的地方修改修改. 當然這怎麼說也是個TODO, 真有哪位念舊dalao願意接手做自製, 我可以花點時間填上這個坑.

附錄

一些常識

GPIO: 軟體和硬體的橋樑

各位聽眾朋友你們肯定或多或少的聽過驅動, IO這樣的詞語.

如果你做過手機或者嵌入式, 查看過驅動的代碼, 會發現很多時候, 我們操作一個地址, 並不是改變內存, 而是改變管腳輸出電平, 或者操作一個硬體的工作狀態, 或者直接用某種電平組合從外部介面發出一組數據.

這裡有幾個概念, GPIO, soc, peripheral. GPIO通俗來說, 就是可以測量或者輸出高低電平(高阻)的物理引腳. 當然除了少數情況下, 一個在真實世界的物理引腳, 是可以在內部藉由多路開關(路由)連接到不同的功能模塊的. GPIO是功能模塊之一, 你可以認為它跟串口/DAC/定時器這一類設備一樣, 都屬於"資源". 單片機為了區分對指定地址的訪問是存儲器還是外設, 他有一張MemoryMap, 有的是可以配置的, 有的是固定的. 訪問硬體資源的區域, 就是peripheral介面. 其中外設我們沿用的還是幾十年之前的概念, 現代的cpu片內都集成了大量的介面和功能模塊, 我們通常說的就是這些集成在晶元內的片上外設.這種架構就叫做SystemOnChip, 簡稱Soc. 有的晶元甚至集成了CPLD可以讓用戶擴充標準外設甚至編寫非標準的外設. 但是是不是所有的廠商都會把常用電氣介面都給集成在晶元呢? 是不是集成的都是最好的呢?

這一點有一些例外, 比如USB HighSpeed/SuperSpeed, 很多cpu是要靠外部的晶元實現電信號(D + /D - )的編碼解碼的, 有線網卡也是, CAN也是絕大多數需要收發器晶元的.

這些晶元我們通俗的叫他PHY, 多數是對外介面是數據引腳少而頻率高, 對cpu介面數據引腳多而頻率低, 又或者需要特別供電, 或者信號線上有干擾, 這樣的一些應用場景. 也有的是因為用來傳輸信號的方式不同, 舉個例子, 不同項目需要同軸, 雙絞線, 光纖來傳輸, 都做片上集成不說成本, 兼容性肯定眾口難調.

我們研究一套硬體的時候, 在真實世界看到的是引腳編號, 在代碼中看到的是對地址的操作. 如果用戶寫代碼都是一堆讀寫地址, 豈不是逆向的時候光查手冊都累趴下了? 還好很多用戶有使用廠家封裝的外設庫習慣, 我們需要對照廠家的文檔, 通過查看使用參數/或者直接操作peripheral區域的代碼, 逐個整理這些被靜態鏈接到固件中的函數名稱. 一份好的符號將大大有利於我們的分析. 當然, 如果你可以分析出固件編譯和鏈接所用的工具鏈和大致的版本號, 你還可以使用BinDiff等工具, 讓你的函數排查更精確.

形形色色的寄存器

剛才我們提到有些外設寄存器, 是通過地址訪問的, 那這個寄存器跟R0~R15這樣的寄存器, 有什麼區別? 首先, 我們開發和逆向時候遇到的寄存器有三類, 第一就是R0~R15這樣的CPU通用寄存器, 其中浮點寄存器VFP/NEON, 也可以歸為這類. 他們要總結相同點就是能夠直接訪問裡面的數據, 第二類就是特殊寄存器, 無法直接操作, 需要先複製到通用寄存器或者必須從通用寄存器傳值, 有些可以改變協處理器的工作狀態, 有的可以操作內存控制器. 不止是Cortex有, 一些8位16位晶元, 也有特殊寄存器, 比如STC, HCS12x, 不過有些不是專用指令複製而是通過低位地址訪問.

第三類是外設寄存器, 不用嚴格區分片上外設還是片外的, 片內外設, 基本都是通過地址訪問的. 這種地址有別於內存地址, 在逆向中我們需要注意到, 有些寄存器是只寫的, 有些則是寫入後(等待一段時間)會被擦掉某個bit的, 我們反編譯時候, IDA無法識別這個地址的數據寫入和讀出會不一致, 可以參考IDA的說明

Hex-Rays Decompiler: Manual

但我們可能一段地址區域, 裡面只有少數幾個是這樣的寄存器, 把它們分別定義為不同的段很麻煩.

還有一個需要留意的地方, 外設寄存器不一定是和處理器字寬相同. 我簡單想了下, 可能編譯器的對齊訪問的優化可能會意外的對另一個寄存器執行讀取再寫入的操作, 對內存地址無所謂, 對外設地址可能一讀一寫就會出現bug. 不過沒有具體遇到這樣的bug, 也不知道有沒有相關的利用.

可能是我想多了吧.

外設寄存器還有一類(大多是無法掛到地址線的外設), 需要通過介面專屬的協議, 比如IIC, SPI, 8080, 更加間接的讀取. 而有些外設介面是可以掛載到地址直接訪問的.

關於測試點的閑話

零售機上為何會有測試點

常看小說的人都會知道, 棄城撤退時候, 一般都會燒毀糧草, 砸碎攻城器械, 甚至井水下毒, 結合我們引子裡面的話, 是不是有種似曾相似的感覺?

我們沒法用產品來打擊敵人, 但我們也不能資敵, 在我看來, 保留著調試/開發用的功能, 和電路板上關鍵測試點就是一種無法理解的行為. 可能很多搞硬體設計或者固件的不以為然, 我們再防, 別人只要重新做塊板子/找人解密就可以搞定了, 不如做點實際的, 燒毀自身讓破解的噁心一下…

原則上說, 很多測試點都不應該留在零售機上. 應該工程機和零售機上使用不同的電路板布線甚至不同的晶元, 防止用戶將原本用於開發的介面變為分析調試的助手. 比如我就感覺盛大放的這些測試點方便了我.

但實際來看現階段大部分手機和其他嵌入式設備(路由器/穿戴設備/智能家居)都預留了一些串口/SWD/JTAG的測試點/排針座.

手機維修領域的硬體設備, 比如Riff box, JPR, 東海等等, 都有大量機型的測試點接線資料. 很多手機維修小店鋪, 就是靠這個來修磚賺錢的.

非要編一些理由的話, 比如有些故障, 在工程機環境就是不發生, 或者隨著將零售機拆開/吹焊晶元後短時間內就無法再現, 必須要保留這個來檢修/檢測故障, 又或者由於成本考慮, 省略了原型, 開發, 批量階段的硬體投入, 直接用同一套設計, 連測試架都不用重新訂做了.

如何尋找特定的測試點

我們關心的可能是串口(看看是不是有特殊日誌輸出), 也可能是調試介面(JTAG/SWD/BDM). 首先我們需要cpu的文檔, 確定腳位和可重新分配的功能表.

因為很多CPU引腳的JTAG/SWD腳位都是不可路由的, 所以只要有線路圖或者PCB圖就可以很輕易的找到. 我們有了板子,找抄板公司整理出PCB圖紙無疑是最方便的, 當然如果你對整機原理沒有研究的需要的話, 還可以用一類叫做JTAG Finder的工具來定位, 上面提到的幾個手機維修工具都有這種功能.

當然這類工具也有適用範圍, 因為需要滿足一次測試過程儘可能包含所有JTAG信號, 如果主板上測試點非常多, 可能多次組合才能確定下來, 只碰上一兩根時候根據工具的原理和連上的線, 有可能測不出來, 有可能結果錯誤. 影響你分析.

硬體工具列表參考JTAG 引腳自動識別 JTAG Finder, JTAG Pinout Tool, JTAG Pin Finder, JTAG pinout detector, JTAGULATOR, Easy-JTAG, JTAG Enumeration

如果沒有引腳查找工具, 而且cpu封裝是BGA/WCSP這類無引腳封裝, 可以先拆下cpu, 從焊盤中定位目標功能可能對應的引腳, 焊接飛線並測試是否跟PCB上某測試點相通. 這種情況可以見後面章節的示例.

對於引腳裸露的封裝的cpu (DIP, SOP, QFP, 單層QFN也算), 還可以嘗試直接按照資料對應的腳位飛線出引線, 再測試是否是需要的功能.

這裡提醒一句, 絕大多數用了單片機的消費產品, 都燒寫後禁用了讀出. 沒人乖乖的等著你免費抄他們的東西, 老外也不例外.

JTAG/SWD/SWV關係

除了我們說的傳統JTAG(單節點使用6~7條信號線), 還有一類縮減引腳的JTAG協議, 只使用TMS/TCK兩個信號線. SWD也是這種協議. 有一些入門級的Cortex-M晶元甚至只提供SWD引腳, 不提供完整的JTAG引腳. 有的晶元額外提供了SWO引腳, 如果測試中發現它跟SWDIO/SWCLK測試點放在一起, 可能開發者開發過程中用它來輸出一些調試用的業務信息. 因為有時候semihosting和uart都無法滿足速度的要求. 當然正式版固件依然輸出調試信息的可能性不大, 但確實遇到過. 比如不使用#define來徹底移除調試列印, 而是定義一個變數, 通過"只有廠家才知道的"指令/其他配置可以打開日誌. 這樣的靠變數控制隱藏的"秘密日誌"我想很多人分析時候都遇到過.

如何防止晶元的調試功能被利用

幸運的是, 開發階段的晶元可控性跟零售產品之間的矛盾soc廠商已經想到了. 各個soc廠商假設了一些安全需求, 並實現了一些不同的保護功能. 舉例來說: Marvell的PXA3X0系列處理器, 其晶元內置的bootrom, 在trust boot流程中, 會由bootrom本身禁用JTAG介面. 當然這個介面物理上還是存在的, 可以通過發送證書來重新開啟.

有些廠家使用了熔絲, 在零售機出廠前使用某些指令將其熔斷, 物理上斷開內部的硬體調試模塊. 也不排除有些廠家實現方式是用內部EEPROM/flash存儲禁用調試介面的配置, 然後禁止對應外部引腳路由到內部調試模塊. 這樣勉強算是屬於硬體實現, 只是並非物理上的熔斷.

還有些廠家做的不徹底, 調試功能只能在上電後由用戶代碼來禁用, 這就屬於鴕鳥政策.

表5-1 各廠家的保護方式

燒錄, 測試點, 工程模式

提到調試功能很多人都會想到JTAG, 但你看到其他介紹硬體分析的都會煞有其事的告訴這東西是用來測試電路的, 然後就是經典的一個JTAG介面如何掛載多個被測設備接法. 讓有心研究硬體hack的兄弟們知難而退.

半仙告訴你不要沉迷那些高大上的標準, 我們只需要知道現在手裡面拿的產品, 它裡面就是用普通的物理測試點在質檢流程測試是否正常工作, 有的也有一些工程模式或者工程固件來測試周邊的輸入輸出設備是否正常.

換句話說現實中JTAG不是用來"Test"的, 它留在電路板上要麼是廠家用來刷機/維修, 要麼就是早期開發過程調試. 也有很大可能你拿到的設備, 它們的開發人員就不用JTAG/SWD等等模擬介面調試, 這就要看做電路板時候工程師是不是習慣性預留了.

當然越來越多的cpu支持usb發送bootloader/payload進行引導或者燒錄, 開發板廠家也會做好各種Bootloader用SD卡引導, 絕大多數產品研發都不需要接觸燒寫步驟, 哪怕你改了Bootloader也只寫一下SD卡即可, 以後手機領域的開發人員可能逐漸就遠離五花八門的各種模擬器了吧. 可能現在就工控, 穿戴, 還有各種xx杯比賽需要寫裸機程序的, 這些人還會手裡拿著一大把各種廠家的模擬器.

回到測試點的話題, 能找到定義最好的辦法就是有維修手冊或者電路圖泄露. 當然這些只能出現在很流行但又不是最新的設備上.

一些小眾的, 或者太新太老的就只能自己動手了. 如果是雙面板, 可能我們把所有元件都吹下來, 進行抄板是最快的方式. 如果是四層乃至6,8層板, 個人來搞就很容易磨廢掉. 這時候我們可以不打磨只測量. 當然, 我們關心的測試點是屬於cpu/nand的部分.

如果這些晶元是BGA/wlcsp的, 需要預先拆下.

圖3-1 拆掉後的eMCP(左)和CPU(右)

測試點是什麼呢, 看圖說話, 右邊CPU白色方塊旁邊三個金色的銅箔就是. 說到這種BGA的布線, 肯定有人會看到布線規範裡面要求把所有pad全部fanout, 不管你產品運行用的著用不著, 而是要留著品質測試(測試焊接質量?), 但是顯然我國工程師擅長的是把6層板的東西縮到4層, 4層縮到雙面, 所以不要太指望一些我們需要的引腳廠家幫我們做了測試點.

實際如果沒有引出我們也有辦法, 不過這需要我們去驗證到底哪些引腳已經引出了. 剛才提到過抄板和自己找, 這裡就演示一下自己找的例子. 這裡面有個技巧, 可以焊接一個8pin排針, 然後對應8條不同顏色硅膠線(不要太粗30AWG即可), 再用一個8pin排座都短接在一起.

將這些排線空閑的一端選8個焊接到靠近的8個測試點上, 插到排座上再用萬用表通斷檔在晶元引腳上掃過, 發現有導通的, 拔掉排座, 再測導通的是8pin裡面哪一色的pin.

因為有顏色區分, 抄的時候不容易弄錯臨近的點. 甚至我考慮過用74系列+語音晶元簡單做一個多pin測試的, 探通後自動掃描每一pin.

如果是LQFP這樣封裝的, 或者說測試點數量多於關注的引腳位置, 還可以8條導線焊接到cpu/flash引腳上, 反過去掃測試點.

我們把板子放到掃描儀上掃出背面圖, 然後在Photoshop裡面按照顏色選取, 將所有裸露的銅箔部分標記為綠色, 然後列印在A4紙上.

然後按照上述辦法, 每找到一個跟cpu/eMCP晶元存在導通的測試點, 就用筆記錄在該測試點的位置. 遇到對應的是JTAG或者可能存在uart復用的埠, 我們再最終記錄測試.

圖3-2整理中的標記示例

圖3-3 焊接好的JTAG測試點

當然工廠的生產流程, 是會有製作一個測試冶具(支架), 需要在線測試的位置安裝探針, PCBA放置固定後, 自動進行質檢的.

我們自己用直接用細一些的導線或者漆包線焊上即可(我推薦鐵氟龍線和硅膠線駁接的方式)

接著是找UART測試點, 前面我們分析過串口應該是GPIO109和GPIO110, 但在第一次測試中, 我們掃遍了電路板正面所有測試點都沒有導通的. 後來測試鍵盤矩陣揭開窩仔片後還有一些觸點也暴露出來, 果不其然, 其中正好有跟cpu的這2個腳位相通的.

應該只是湊巧被貼住了, 感覺電路板掛在測試架上時候是沒有貼這塊鍵盤的.

圖3-3 找到的UART測試點, 在數字鍵3的右側.

附錄2

我們需要什麼樣的裝備

基礎維修工具

焊台/烙鐵, 常見杜邦線排針排母壓線鉗膠殼等等.

如果需要拆焊和焊接smt的IC, 最好有熱風槍.

如果需要處理引腳較多的BGA, 或者其他尺寸大的ic, 還需要一個預熱台.

萬用電錶有基礎通斷功能的就可以.

如果要抓非標準協議, 可能需要邏輯分析儀, 這種國內有很多山寨selease的, 也有一些是原創的. 如果要分析模擬信號, 還需要示波儀. 別買玩具款的就行, 深度不足的話可能根本無法抓到異常波形.

調試用的模擬器設備

一般來說, soc廠家自己會有生產模擬器, 但有些原廠模擬器便宜, 有的奇貴, 可能上萬, 還不賣給個人.

不過不用怕我們有強大的山寨版, 甚至很多有1:1的高仿, 直接用官方固件能夠自動升級的. 不喜歡盜版的, 也可以考慮第三方的模擬器, 如果不是那麼罕見, 還可以找有免費電路圖提供的, 自己製作.

也有的開發板廠商, 配套模擬器是自己生成的, 這一些有的是開源方案, 有的是簡易的LPT方案, 有些國內廠家也直接給你上山寨的.

比如Marvell的XDB, 現在是想買也買不到. 它很多東西包括資料也只給單位客戶, 個人連開發社區都不能註冊.

不過因為前些年PXA在手機中流行過, 市面上也出現了一些可以調試PXA的JTAG Adapter.

圖4-1 Bus Blaster v4

這是基於FT2232H+CPLD的可編程buffer方案, 軟體支持是openocd, 但bug很多. 開源的就這樣.

圖4-2 並口簡易JTAG

WDT LPT Jtag, 固定buffer方案. 可以配合jflashmm燒寫PXA320的快閃記憶體. 這是某韓國開發板廠家Hybus修改的燒寫程序和配套的電路.

當然我們萬能的山寨jlink, 配合openpxa補丁, 也可以用來調試不過我一定要說, 開源的東西我還沒遇到在我手裡不出bug的.

它調試起來各種bug, dump nand也數據不對. 我腦海中已經出現項目的作者"哎喲編譯通過了連接成功了可以發布了"的畫面.

圖4-3 使用openocd+山寨Jlink-v8連接bambook

sudo openocd -f interface/jlink.cfg -f target/pxa3xx.cfg -c "adapter_khz 1000" -c "nand device pxa3xx.flash pxa3xx pxa3xx.cpu"

圖4-4 使用山寨JLink-v8+openocd讀取PXA310的nor flash快閃記憶體.

nand dump offset size file.bin

其他支持PXA的硬體我還找到有H-Jtag, banyan-U, 其中banyan-U我測試過, 也有一些問題.

還有一個是JtagKey, 同樣基於FT2232的, 開發板廠商Toradex的一些燒錄和調試軟體支持這個轉換器. 這個東西我們可以自己造, 網上有很多電路圖.

附錄3 無用信息刪除

下期預告:

如何防止二進位代碼被dump出來

對於外置flash的機型來說, 除非cpu本身支持代碼加密, 否則拆下nor/NAND/iNAND以後, 放進編程器一讀, bootloader等內容也就直接到手了, 也可以拆下來焊到轉接板或者飛線出來用讀卡器或者單片機dump, 當然這些都是在你還沒有能直接dd mtd分區手段之前的敲門磚.

對於內置flash的來講(例如大部分mcu), 我舉個科普的例子(但不科學), 比如你有台電腦, 你BIOS里禁了USB介面, BIOS設置了密碼, 以防止裡面資料泄露. 但你放在自己家那是有效的, 如果放在別人家呢? 要麼我可以CMOS放電, 要麼我可以直接拔了你的硬碟來dump啊. 這就是傳說中的開蓋讀取. 曾經還有傳言有廠家用EEPROM保存代碼, EPROM保存secure bit, 結果連個人攤子都能用紫外線照一下就能讀了, 熔絲修復打金線什麼的統統不用. 這個傳言是真是假就不說了, 據我所知現在的單片機內部flash亂序存儲, 加密都已經很普遍了, 但對於要克隆你產品的來說, 唯一的區別就是解密工作室開價不同, 只能讓人多花錢, 難以讓人多花時間.

感謝列表

2 感謝Zdi勘誤


推薦閱讀:

自己用黑客技術黑掉學校教務網改成績有多大難度?可行性有多少?
Bash Bug 的危害有多大?
黑客分多少種?

TAG:黑客Hacker | 智能硬件 | 破解者Cracker |