Smartisan是手機中為數不多傾心于工業設計和用戶體驗的。老羅跨界過猛,也難免導致其最初的想法和現實存在差距。bootloader到底鎖還是不鎖,甚至曾被一個T1用戶弄上法庭來質問。
當然,能從認為加鎖是對系統的不自信,到后來發現解鎖是安全隱患,絕對是個進步(loser口中的打臉)。技術層面來說,究竟T系列手機的bootloader能不能解鎖呢?答案是,能。或者說,本來不能,但由于bootloader里存在的兩個漏洞,恰好可解。
分析bootloader
正像Smartisan OS本身,其ROM目錄結構也是極簡的。firmware-update目錄下emmc_appsboot.mbn就是bootloader鏡像。由于是ELF格式,不需要更多的處理,就能逆向出不錯的代碼結構。無論是T1還是T2,bootloader的代碼差不多,下面的分析選擇的是T2的2.6版的ROM。
和很多高通芯片的手機一樣,T2的bootloader是基于高通開源的lk。所以參考源碼,可以很快梳理出bootloader的執行流程。啟動后,根據按鍵組合,決定是否進入recovery,如果繼續留在bootloader模式,就會注冊一系列fastboot command,循環等待用戶輸入,決定下一步動向,如圖1。
圖1.注冊fastboot command
顯然,control_flag為0的話,cmd_table中只有前四條命令被注冊,后續命令就都無法使用了。通過觀察cmd_table(如圖2),可以發現那些真正令人激動的函數(比如oem unlock)都在比較靠后的位置上。
圖2.fastboot可以注冊的命令列表
在搞清楚control_flag這個全局標記到底何去何從之前,不如先探探這僅存四條命令的究竟。reboot,reboot-bootloader命令正像他們的名字一樣無趣,flash看起來就很有故事了。
執行flash命令時,如果control_flag為0,那就只能寫一個名為security的分區。而control_flag為1時,所有其他分區就都可以寫了,如圖3所示:
圖3.寫分區時的判斷
聯想之前fastboot command注冊的過程,control_flag為0時,絕大部分功能無效,且分區不可寫,control_flag應該就是is_allow_unlock,即bootloader是否上鎖的標記。系統啟動時,is_allow_unlock默認置0。當flash了security分區后,is_allow_unlock會有一次賦值操作,并且一旦賦值為1,就會提示解鎖成功,如圖4所示:
圖4.對security分區的檢測,判斷是否可以解鎖
分析到這里基本可以肯定,T2提供了解鎖功能,關鍵是寫入security分區的內容是否能夠經得住考驗。
解鎖bootloader
verify_security()函數比較復雜,涉及很多密碼學算法的演繹。好在它使用的是openssl的標準庫函數,識別起來有章可循。security分區內容采用的是RSA+MD5簽名校驗。合理的猜測是,官方本來設計的解鎖流程其他廠商類似,即用戶提交手機的序列號等信息,然后通過unlock時輸入廠商給的解鎖碼(根據序列號計算出來的簽名信息),實現解鎖。只不過這一次解鎖碼是通過寫入security分區實現輸入。
security[128](security分區第128字節)是RSA初始化函數選擇的依據,security[129]作為序列號長度。然后factory[5](factory分區的第5字節)起始的序列號作為MD5的計算依據,得到的hash值和security[0-127]簽名信息驗證的結果做比,相同返回1,否則返回0。這幾乎是每個簽名驗證的都在用的標準化流程,采用的算法成熟,且由openssl實現(難怪發布會幾百萬門票錢捐給了openssl),基本不會有瑕疵。由于bootloader只存放了公鑰e,沒有私鑰d,手機用戶自己是沒辦法構造出128字節的簽名信息的。
不過,由于代碼上一些不大不小的問題,我們恰好可以繞過這些限制,構造出和序列號無關的通用解鎖碼。首先在RSA初始化時,如圖5和6,當security[128]為66和67以外的數值時,初始化函數被選擇為sub_F924A90。
圖5.根據security[128]指定的函數來初始化RSA密鑰
圖6.RSA密鑰初始化
跟進sub_F924A90后,可以看見圖6所示的密鑰填充,BN_bin2bn是openssl的庫函數,用于將內存中存放的Big-Endian字符數組轉化為Bignum類型,方便RSA的內部計算。私鑰d填寫的是偽數值,但p和q都填寫的是真值。側面說明寫這段代碼的人不太了解RSA,畢竟其安全性完全依賴于大數分解的NP難,而現在n的兩個素數因子p和q都給了,雖然本意是加快計算速度,但私鑰d也就因而可以從公鑰e推出來了,d=e-1mod (p-1)(q-1),這就導致了第一個邏輯漏洞,用于偽造簽名。
接下來,如圖7,完成了RSA的初始化以后,會接著從factory分區讀取數據:
圖7. 讀取factory
分區,得到序列號,然后計算MD5
究竟從factory分區讀取多少字節是可控的,由security[129]決定。讀取出來正常應該是一串字母開頭后接一串數字的序列號,MD5后得到一串16字節的hash。最后利用RSA的公鑰驗證security[0-127]的128字節簽名是否屬于hash。
由于security[129]完全可控,就導致了第二個邏輯漏洞。如果該數指定為0,則MD5是針對一個空字符串進行計算的,計算結果總是d41d8cd98f00b204e9800998ecf8427e。所以無論是哪臺手機,factory分區內容如何,簽名驗證將總是針對常量進行。只要構造該常量的簽名寫入security分區,就能夠完成解鎖。
為了減少padding,encoding等一系列開發可能造成的不確定性,在生成解鎖碼時,同樣采用openssl的代碼實現,示例如下:
#include
#include
#include
#include
#include
unsignedcharm3_n[128] = {\
0xA4,0x0C, 0x69, 0x70, 0x25, 0x4F, 0x36, 0x49, 0x8E,\
0x83,0x4B, 0x74, 0x9A, 0x75, 0xC9, 0xF4, 0x7F, 0xE5,\
0x62,0xA8, 0xDE, 0x11, 0x13, 0x03, 0x57, 0x89, 0x31,\
0xCB,0x58, 0x84, 0xC8, 0x26, 0xBA, 0x2B, 0x60, 0xB5,\
0xB8, 0xA5, 0xD9, 0xBD, 0x27, 0x48, 0x3D,0x33, 0x38,\
0xA1,0x72, 0x62, 0x64, 0x87, 0x5E, 0x71, 0xF4, 0x1F,\
0xCB,0x68, 0x83, 0x92, 0xEA, 0x4B, 0xFF, 0x06, 0x38,\
0xAF,0xD5, 0x65, 0x55, 0x94, 0x04, 0x91, 0x88, 0xF7,\
0xA4,0x57, 0x72, 0x29, 0xFE, 0xEA, 0xB1, 0x27, 0x25,\
0xC1,0x12, 0x7D, 0x16, 0x6F, 0x13, 0xAF, 0xE2, 0x00,\
0x8D,0x5E, 0xA4, 0x0A, 0xB6, 0xF3, 0x71, 0x97, 0xC0,\
0xB0,0x60, 0xF5, 0x7C, 0x7F, 0xAA, 0xC4, 0x64, 0x20,\
0x3F,0x52, 0x0A, 0xA3, 0xC3, 0xEF, 0x18, 0xB6, 0x45,\
0x7D,0x72, 0x1E, 0xE2, 0x61, 0x0C, 0xD0, 0xD9, 0x1D,\
0xD0,0x5B\
};
unsignedcharm3_e[1] = {3};
unsignedcharm3_d[128] = {\
0x6d,0x5d,0x9b,0xa0,0x18,0xdf,0x79,0x86,0x5f,0x02,0x32,0x4d,0xbc,0x4e,0x86,0xa2,\
0xff,0xee,0x41,0xc5,0xe9,0x60,0xb7,0x57,0x8f,0xb0,0xcb,0xdc,0xe5,0xad,0xda,0xc4,\
0x7c,0x1c,0xeb,0x23,0xd0,0x6e,0x91,0x28,0xc4,0xda,0xd3,0x77,0x7b,0x16,0x4c,0x41,\
0x98,0x5a,0x3e,0xf6,0xa2,0xbf,0xdc,0xf0,0x57,0xb7,0x46,0xdd,0x54,0xae,0xd0,0x74,\
0x27,0xaa,0xad,0xf9,0xb9,0x33,0x8f,0x29,0x3b,0xf2,0xee,0x97,0x03,0x0b,0x5c,0xfc,\
0x92,0x95,0x6f,0x05,0xcd,0xbf,0x1c,0x77,0x16,0xce,0xd9,0x13,0xfb,0xf2,0x8f,0x74,\
0x09,0xca,0x78,0xf0,0xc7,0x4a,0xc2,0xc5,0xed,0x58,0xc1,0xfa,0xa1,0x6f,0x64,0x26,\
0x73,0x75,0x73,0x97,0x21,0xb4,0x01,0x13,0xad,0xd7,0xd5,0xbc,0x22,0x75,0x00,0xcb,\
};
intmain(intargc,char*argv[]) {
MD5_CTX md5ctx;
unsignedchardigest[MD5_DIGEST_LENGTH];
unsignedcharsigret[128];
unsignedintsiglen;
unsignedchartestdata;
MD5_Init(&md5ctx);
MD5_Update(&md5ctx, &testdata, 0);
MD5_Final(digest, &md5ctx);
RSA *rsa =RSA_new();
rsa->n =BN_bin2bn(m3_n, 128, rsa->n);
rsa->e =BN_bin2bn(m3_e, 1, rsa->e);
rsa->d =BN_bin2bn(m3_d, 128, rsa->d);
RSA_sign(4,digest, 16, sigret, &siglen, rsa);
FILE *fp =fopen("security.img","wb");
fwrite(sigret, siglen, 1, fp);
fwrite("\\x40\\x00", 2, 1, fp);
fclose(fp);
return0;
}
刷入security.img后,手機就可以解鎖了。雖然上述分析是基于T2的ROM,T1也完全適用。如圖8所示,T1刷入security.img同樣可以解鎖。
圖8. T1刷入security.img后解鎖
圖9.T2刷入security.img后解鎖
And Then Some
2014年老羅在微博上提過關于bootloader方面的打算,“官方會提供 boot loader,方便你刷機,只是刷機后會失保”,所以初代ROM里的確如我們所見保留了解鎖bootloader的功能。2016年有人因為提供解鎖而狀告Smartisan,老羅勝訴后說道“我在微博上說過做bootloader,但技術部門因安全考慮否決了,我代表我自己道歉。”,所以肯定是取消了該功能。盡管官方從來沒有發布過任何解鎖的方法,底層代碼倒是可以清晰反映出這段經歷。
對于T1和T2,2.6.7是最后一個可以解鎖的ROM版本號,2.6.8開始,fastboot command列表被改寫為圖10所示內容,大部分指令被閹:
圖10. 2.6.8后的版本中fastboot已經沒有什么實質功能了
所以如果要解鎖3.x的Smartisan OS,可以下載2.6.7的ROM完成降級,畢竟舊版本的ROM同樣帶有簽名,使用recovery時允許刷入手機。更新到舊版的bootloader后,再用fastboot flash security security.img進行解鎖。解鎖后,每次升級用第三方無簽名驗證的recovery,更新除bootloader以外的模塊即可。這樣即便最新系統暫時沒有公開的內核漏洞,也能root。
一般的Android手機,只要有簽名認證的老版本bootloader里有漏洞,在系統沒有開啟限制(比如SW_ID)時,總可以通過降級,解鎖,然后升級回新系統,刷入supersu的方式root。
本文為企業推廣,本網站不做任何建議,僅提供參考,作為信息展示!
推薦閱讀:萊蕪信息港
網友評論
請登錄后進行評論|
0條評論
請文明發言,還可以輸入140字
您的評論已經發表成功,請等候審核
小提示:您要為您發表的言論后果負責,請各位遵守法紀注意語言文明