以三星為例,如何對TrustZone進行逆向工程和漏洞利用(上篇)
譯文聲明本文是翻譯文章,文章原作者Daniel Komaromy,文章來源:http://medium.com
原文地址:https://medium.com/taszksec/unbox-your-phone-part-i-331bbf44c30c ; https://medium.com/taszksec/unbox-your-phone-part-ii-ae66e779b1d6
一、前言
本文主要講解了如何對三星的TrustZone進行逆向工程和漏洞利用,受字數所限,將會分為上下兩篇。在上篇中,主要涵蓋了關於架構的基礎知識。儘管這些基礎知識都來源於公開的信息,並沒有不為人知的內容,但它們卻分布在各種出版物上,非常零碎,所以我想將其整合成完整且連貫的內容。本篇文章部分技術細節來源於Trustonic和三星的官方文檔,部分來源於開源軟體,還有一部分來源於此前研究者所公開的研究成果。
在本系列的後面,我還對逆向工程的成果進行了總結,並詳細展現我所發現的漏洞。
二、簡介
目前,已經有很多關於TrustZone的研究。在去年以前,大家主要側重於對高通和華為產品的研究工作。此前這些關於可信執行環境(TEE)的研究都有一個共同的主題,就是其單點失效的特點(Single-point-of-failure Nature)。在這些可信執行環境中,普遍缺乏對特權的分隔,這也就意味著某一個漏洞會直接導致整個系統的淪陷,甚至是反覆淪陷。下面8篇漏洞詳情,就是反覆淪陷最好的證明,大家可以參考閱讀:https://www.blackhat.com/docs/us-14/materials/us-14-Rosenberg-Reflections-on-Trusting-TrustZone.pdf http://bits-please.blogspot.hu/2015/08/full-trustzone-exploit-for-msm8974.html https://www.blackhat.com/docs/us-15/materials/us-15-Shen-Attacking-Your-Trusted-Core-Exploiting-Trustzone-On-Android-wp.pdf https://pacsec.jp/psj14/PSJ2014_Josh_PacSec2014-v1.pdf http://bits-please.blogspot.hu/2016/05/qsee-privilege-escalation-vulnerability.html https://www.slideshare.net/GeekPwnKeen/nick-stephenshow-does-someone-unlock-your-phone-with-nose https://microsoftrnd.co.il/Press%20Kit/BlueHat%20IL%20Decks/GalBeniamini.pdf http://theroot.ninja/disclosures/TRUSTNONE_1.0-11282015.pdf然而,三星所使用的可信執行環境則有所不同。三星的環境是由Trustonic開發,Trustonic是一個專註於研究可信操作系統的公司,他們對可信執行環境解決方案的研發已經進行了15年之久。儘管在去年,Trustonic已經將他們的TEE OS更名為Kinibi,但我還是更習慣使用「T-Base」作為可信執行環境的名稱。同樣,此前Trustonic公司曾使用過MobiCore和Giesecke&Devrient這兩個名稱,儘管如今已經更名,但還是有一些地方會使用舊名稱,請各位讀者注意這一點。最近,我在Ekoparty安全大會上就這一主題發表了演講,我的演講主要聚焦於如何對T-base微內核的逆向工程,以及T-Base的內部工作原理,並沒有側重於講解Trustlets提供的實際功能和攻擊面。這裡的Trustlets是指在可信執行環境中運行的「可信應用程序」,基本上都是安全的操作系統中的用戶空間進程。我演講的視頻請參見: https://www.youtube.com/watch?v=L2Mo8WcmmZo 。我演講的PPT請參見: https://github.com/puppykitten/tbase 。在我發表演講的時候,三星還沒有完成全部漏洞的修復工作,這也是為什麼我會將演講的重點放在逆向工程的原因之一。終於,他們已經在2018年1月的安全通告中,完成了最後一個漏洞的修復。終於,我們可以放心地討論這些漏洞,我也要感謝大家的耐心等待。根據我的經驗,如果你發現某個架構設計得很差,那麼隨之而來的很可能就是一些重大的漏洞,特別是在嵌入式領域。
三、三星的KNOX和T-Base
由於我的研究都是基於三星的環境下完成的,那麼我要解決的第一個問題,就是可信執行環境如何與三星的KNOX安全架構結合在一起。這一點非常重要,因為在早期版本的TrustZone中,它幾乎只用於進行數字版權管理(DRM),所以從終端用戶的角度來看,對其進行攻擊的意義並不是太大。而現在,時代已經改變。針對TrustZone技術,三星公司已經發布了非常多的文檔。其中,有兩篇文檔已經詳細說明了其所具有的功能,我在這裡就不再贅述,大家可以參考閱讀:http://developer.samsung.com/tech-insights/knox/platform-security http://developer.samsung.com/tech-insights/pay/device-side-security可信執行環境主要有下面三種用途:
- 將安全敏感功能移入可信執行環境。這樣一來,即使安卓環境受到了威脅,也不會對可信執行環境產生影響。例如:KeyStore證書、數字版權管理、證書管理、可信PIN、可信UI、移動設備管理遠程認證。
- 可信執行環境會對安卓系統中的功能進行監管,以緩解對Trustlets的漏洞利用嘗試。例如:TIMA任意內核模塊認證繞過、基於Trustlets的Kill-Switch勒索病毒。
- 僅允許從可信執行環境訪問硬體設備,從而緩解針對硬體的攻擊。例如:指紋感測器、用於非接觸式支付的磁信號安全傳輸技術(MST)等。
上述的所有內容,都有一個共同的特點:每一個功能都是由一個(或多個)Trustlet實現。這就充分說明,如果要對實際功能方面進行研究,我們應該關注Trustlet。但如果我們想要了解這些Trustlet的安全性(例如:在安全內核和不安全內核之間,它們是如何相互隔離的),我們就需要深入了解T-Base。
在這裡,我想要說明的是,儘管本文中涉及一些Trustlet的實際功能,但更多是用於向大家展現這些漏洞的存在。四、T-Base的結構
如果你想閱讀廠商關於T-Base的介紹(更側重於產品營銷目的),請參考: https://www.trustonic.com/solutions/trustonic-secured-platforms-tsp/ 。在最一開始,我們可以輕而易舉地通過各種資料來熟知T-Base的結構——既有抽象層面的圖示,又有大量的可用信息,同時還有三星和Trustonic共同公開的源代碼可以參考。
這一切的核心,是安卓進程(應用程序)通過世界共享內存(World Shared Memory)與Trustlet通信。剩下的只有安卓、Linux內核和T-Base內核所提供的抽象層與安全邊界層。
我們從T-Base架構的一張經典圖示開始,來了解T-Base的結構。4.1 ATF:ARM可信固件
首先,我們需要在硬體層面上,連接安全的世界(與不安全的世界。這一點是通過ARM上的SMC指令(Secure Monitor Call)來實現的,它會將執行過程從安全世界切換到不安全世界,就像是SVC將執行過程從EL0切換到EL1一樣。但是,我們並沒有真正地使用這樣的指令跳轉到一個安全世界EL1中的Handler,而是執行跳轉到所謂的監視模式(Monitor Mode,ARM的EL3)之中。就像是一個代理,其目標是將安全世界與不安全世界之間的SMC組織起來。在Trustonic中,它並不是T-Base的一部分,而是單獨實現的。關於ARM EL的更多信息,請參考:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0488d/CHDHJIJG.html 。三星(包括其他使用Trustonic的廠商,例如部分使用聯發科晶元的小米手機)就在使用ATF(ARM可信固件)來實現監控模式。上述實現基本上是參考了ARM的開源文檔。此外,Quarkslab針對ATF逆向方面寫了一篇非常不錯的文章,大家可以參考閱讀:https://blog.quarkslab.com/reverse-engineering-samsung-s6-sboot-part-i.html https://blog.quarkslab.com/reverse-engineering-samsung-s6-sboot-part-ii.html需要注意的一點是,Linux內核不僅要使用SMC調用(通過ATF)來到達T-Base,並且T-Base內核還藉助於SMC調用來實現與ATF的通信。上述內容實際上是一個黑盒問題,我們並不知道其具體原理,將會在下一篇文章中進一步學習探討。在這裡,我要補充一句,不僅ATF會藉助SMC調用來到達T-Base,有些SMC調用也可以讓ATF實現命令。其中一部分是開源ATF代碼中包含的命令,一部分是廠商自定義的命令。但這些並不是此次研究的重點。
4.2 T-Base:微內核
在圖中的右側,就是我們的安全世界,T-Base也在安全世界中。起初,我們認為共包含三個部分:微內核、安全驅動和Trustlet。但在後續研究中,我們意識到這個假設是錯誤的。圖中的「運行管理(Runtime Management)」,實際上既不是Trustlet,也不是驅動,更不是微內核。關於這一點會在本篇文章的後面進行介紹。現在,我們假設在T-Base操作系統中,如果一件事不是由Trustlet和安全驅動來完成,那就一定是微內核完成的。
因此,在我們研究Trustlet之前,我們想首先了解如何通過微內核來實現對Trustlet的管理(例如載入)。
為了充分掌握原理,我們必須對微內核進行逆向工程。三星將T-Base固件存儲在一個不顯眼的位置——打包在sboot映像中。Gal Beniamini已經發表過一種用來提取該映像的方法,詳情請參考他博客文章中「Kinibi Revocation」一節:https://googleprojectzero.blogspot.hu/2017/07/trust-issues-exploiting-trustzone-tees.html ,在本篇文章後面,也會詳細進行介紹。儘管我們必須進行逆向才能知道微內核實現Trustlet管理的具體方式,但我們可以通過公開的文檔了解到微內核相當多的管理介面。T-Base將其稱為MobiCore控制協議(MCP)介面(MCI)。這是建立在SMC調用之上的,接下來我們就做具體的分析。至此,如果你已經閱讀了Gal的文章,那麼想必就一定知道,在三星S8之前,T-Base並沒有在Trustlet載入過程中被用於回滾保護。4.3 T-Base:SMC快速調用
如果你已經習慣了其他的可信執行環境實現,你可能會希望每個管理功能(例如:載入Trustlet、與Trustlet共享內存、打開到Trustlet的連接等)都能是另一個微內核實現的管理程序調用,可以通過將正確的參數傳遞給SMC調用來直接觸發,就像Linux中的ioctl調用一樣。實際上,Trustonic採取了一種完全不同的方式,保證了微內核「微」的這一特點。這一實現,可以從Linux內核源代碼中詳細了解,具體請參閱drivers/gud/gud-exynos8890/MobiCoreDriver/*.c。(附註:源代碼中,驅動程序文件夾名稱為「gud」,就是因為原來的公司名稱是「Giesecke and Devrient」。)在Linux內核中,總共有5個SMC快速調用。其中,MC_FC_INIT用於配置隊列,MC_FC_INFO可以從T-Base中獲取版本信息等信息,MC_FC_SWAP_CPU可用於將T-Base移動到特定的CPU核心中,MC_FC_YIELD和MC_FC_NSIQ用於調度T-Base的運行。fastcall.c源代碼如下:
/* fast call init */
union mc_fc_init {
union mc_fc_generic as_generic;
struct { u32 cmd; u32 base; u32 nq_info; u32 mcp_info; } as_in; struct { u32 resp; u32 ret;u32 flags;
u32 rfu; } as_out;};/* fast call info parameters */union mc_fc_info { union mc_fc_generic as_generic; struct { u32 cmd; u32 ext_info_id; u32 rfu[2];
} as_in;
struct { u32 resp; u32 ret; u32 state; u32 ext_info; } as_out;};#ifdef TBASE_CORE_SWITCHER
/* fast call switch Core parameters */
union mc_fc_swich_core { union mc_fc_generic as_generic;
struct {
u32 cmd; u32 core_id; u32 rfu[2]; } as_in; struct { u32 resp; u32 ret; u32 state; u32 ext_info;
} as_out;
};#endif
fastcall2.c源代碼如下:
int
mc_fc_nsiq(void)
{
mc_fc_yield(void)
{ union mc_fc_generic fc; int ret; memset(&fc, 0, sizeof(fc)); fc.as_in.cmd = MC_SMC_N_YIELD; mc_fastcall(&fc); ret = convert_fc_ret(fc.as_out.ret); if (ret) mc_dev_err("failed: %dn", ret); return ret;}
這些命令都是通過_smc()函數來執行,該函數的作用是將參數存儲在寄存器中,並生成一個SMC。所以我們知道,如果我們想根據VBAR的設置來尋找T-Base微內核的SMC處理程序,那我們應該只能找到這一簡單的實現,後面必須要對微內核中未記錄的解決方案進行逆向工程,來確定代碼實際處理MCP命令的位置所在。當然,上述過程也不是完整的,Linux內核需要在T-Base被響應時得到通知。實際上,這一點是通過中斷來實現的:內核驅動在系統上註冊一個終端,T-Base通過MC_FC_INIT命令獲知到中斷的發生。
4.4 T-Base:MobiCore控制協議
MCP是基於共享緩衝區的協議,因此SMC唯一可以做的,就是設置MCP隊列,通知T-Base隊列中有新的輸入,並安排在安全世界中運行。其中,有兩個隊列:命令隊列是不安全世界可以載入MCP命令的地方;通知隊列是不安全世界可以載入Trustlet標識符的地方,以便通知特定的Trustlet有一條命令在等待它。MCP命令的作用在於:我們可以載入/掛起/恢復Trustlet,並且可以映射/取消映射其他共享內存到Trustlet實例的地址空間。MCP命令列表可以在Linux內核源代碼中找到,具體請參見drivers/gud/gud-exynos8890/MobiCoreDriver/mci/mcimcp.h。
/** Possible MCP Command IDs
enum cmd_id { /** Invalid command ID */ MC_MCP_CMD_ID_INVALID = 0x00, /** Open a session */ MC_MCP_CMD_OPEN_SESSION = 0x01, /** Close an existing session */ MC_MCP_CMD_CLOSE_SESSION = 0x03, /** Map WSM to session */ MC_MCP_CMD_MAP = 0x04, /** Unmap WSM from session */ MC_MCP_CMD_UNMAP = 0x05, /** Prepare for suspend */ MC_MCP_CMD_SUSPEND = 0x06, /** Resume from suspension */ MC_MCP_CMD_RESUME = 0x07, /** Get MobiCore version information */ MC_MCP_CMD_GET_MOBICORE_VERSION = 0x09, /** Close MCP and unmap MCI */ MC_MCP_CMD_CLOSE_MCP = 0x0A, /** Load token for device attestation */ MC_MCP_CMD_LOAD_TOKEN = 0x0B, /** Check that TA can be loaded */ MC_MCP_CMD_CHECK_LOAD_TA = 0x0C, /** Map multiple WSMs to session */ MC_MCP_CMD_MULTIMAP = 0x0D, /** Unmap multiple WSMs to session */ MC_MCP_CMD_MULTIUNMAP = 0x0E,};
儘管該功能非常簡單,但不意味著以安全的方式來實現該功能也是非常簡單的。事實上,幾乎所有的安卓可信執行環境廠商,都存在安全問題,更多詳情可以參見UCSB研究人員發表的《BOOMERANG》文章:https://www.cs.ucsb.edu/~vigna/publications/2017_NDSS_Boomerang.pdf 。從Linux內核的角度來看,這些功能是通過實現mc_user_fops操作(該操作在drivers/gud/gud-exynos8890/MobiCoreDriver/user.c中定義)的/dev/mobicore設備節點過程中暴露的。
4.5 T-Base:Trustlet和安全驅動
接下來,讓我們一起來看看Trustlet。首先要提到的是,在去年,Gal Beniamini的一篇博客文章( https://googleprojectzero.blogspot.hu/2017/07/trust-issues-exploiting-trustzone-tees.html )中對T-Base Trustlet的實現給出了非常棒的觀點,我覺得大家有必要首先閱讀一下。更詳細的內容,請繼續閱讀本文。根據Tim Newsham此前的研究( http://thenewsh.blogspot.hu/2015/02/disassembling-mobicore-trustlets.html ),我們知道,Trustlet二進位文件可以在設備的/system/app/mcRegistry或/data/app/mcRegistry中找到,或者從三星FOTA映像中裝載了sim2img的system.img的APP目錄下找到。我們還需要知道MobiCore載入器格式(MCLF,Truslet和安全驅動的文件格式):在內核源代碼中,請查看諸如gud/gud-exynos8890/MobiCoreDriver/mci/mcloadformat.h這樣路徑下的源文件。這是一個由Gassan Idriss編寫的IDA載入程序,可供參考:https://github.com/ghassani/mclf-ida-loader/blob/master/mclf_loader.py 。此外,Tim Newsham還寫了另一套工具( http://pastebin.com/ea7BG6cj ; http://pastebin.com/DPwcmrK2 ),用於將MCLF轉換成ELF。在Trustlet中區分驅動非常簡單,可以根據MCLF中的版本欄位來判斷。或者還有一種更快的方法:名稱以「ffffffff00」開頭的是Trustlet,名稱以「ffffffffd0」開頭的是安全驅動。另一方面,由於這些名稱是以他們的UUID命名的,而不是一個有意義的名字,因此想要知道哪些二進位文件中隱藏了什麼功能並不容易。我們可以使用逆向工程的方式來實現這一點。儘管其載入過程是在不安全世界中進行的解析,但MCLF格式在安卓內核源代碼中還是有意義的,但更令人驚訝的是,所謂的tlApi和drApi也在某些來源中被定義。我不知道這是否是一個錯誤的定義,並且也不是在每個發布的三星內核源代碼中都能找到該路徑,但至少我在GitHub的源代碼中發現了一個存在的樣例。這些是Trustlet與安全驅動分別用來與T-Base微內核通信的API。其中包括與不安全世界中應用程序進行交互的功能、允許安全世界中進程(Trustlet/安全驅動)相互通信的功能,以及對通常由內核處理的功能的調用(例如映射內存地址空間)。正如Tim Newsham和Gal Beniamini的博客文章中所述,Trustlet通過一個調用門(Call-Gate,類似於ELF的GOT,除了對所有API調用都有單個跳轉目標之外)調用這些API。也就是說,這些調用並沒有編譯成Trustlet二進位文件。由此,我們就又產生了另一個資料都未能解答的疑問——這些API實際上都在哪裡,並且是如何實現的?我們可以推測,這些API實際上會以某種方式被SVC調用包裝,並在T-Base微內核中實現系統調用處理程序。但我們並不清楚其中的細節。同樣,我們也沒有tlApi或drApi中所有調用的信息。此外,Tim的博客文章是幾年之前寫的,其中所列出的API調用列表也是不完整的。關於安全驅動,除了這些頭文件之外,我沒有找到太多的公開資料。就目前而言,理解了安全驅動能夠訪問各種硬體就已經足夠了。此外,加密驅動會與加密引擎進行通信。安全SPI驅動一方面會通過SPI介面與指紋感測器進行通信,另一方面通過SPI介面與安全支付的嵌入式安全元件(Samsung Pay)進行通信。上述所提及內容,均在三星官方文檔( http://developer.samsung.com/tech-insights/pay/device-side-security )中有詳細說明。關於Trustlet和tlApi,我們可以找到一些公開信息,例如Trustonic的Jan-Erik Ekberg的演示( https://usmile.at/sites/default/files/androidsecuritysymposium/presentations2015/Ekberg_AndroidAndTrustedExecutionEnvironments.pdf)。根據他PPT上面的一張截圖,我們可以看到:
Trustlet的基本行為非常簡單。在IPC部分(與安全驅動通信)有些複雜,但就介面的Trustlet而言,只有tlApi調用tlApi_callDriver(),其工作原理與Linux的ioctl有些相似,它將一個命令ID和一個指向命令結構的指針作為參數。這裡也是,並沒有在Linux內核源代碼中體現,但我們在GitHub上卻至少可以找到一個源代碼是使用了這樣的頭文件。
如果你已經閱讀了Jan-Erik的幻燈片,你應該已經了解到有一個名為GP(Global Platform)的標準化可信執行環境,它為不同的可信執行環境創建了一個通用的API。Trustonic支持GP標準,因此他們也有相應的API可以實現通用標準。他們所做的,就是增加了一些特性,將相應內容變成T-Base特定的API。然而比較不錯的是,三星似乎在Trustlet中使用了傳統的API,所以我們不用擔心這一點。五、T-Base與安卓
現在,我們來研究安卓層面。這裡仍然有Trustonic編寫的代碼,他們創建了一個用戶控制項守護進程mcDriverDaemon,向用戶空間的應用程序開放介面。由於某種原因,以前版本的代碼曾經開源過,因此理解這個驅動程序的驅動就變得非常簡單。這個守護進程通過libMcClient.so訪問內核設備節點,其命令清晰地映射到MCP命令中,請參考: https://github.com/Trustonic/trustonic-tee-user-space 。Trustonic這一設計目的在於,只有mcDriverDaemon能夠訪問設備節點,並且通過UNIX本地套接字開放介面。Trustonic將該介面命名為TLC(Trustlet連接器)。在三星的具體場景下,這一情況更為複雜。一方面,設備節點使用DAC和MAC(SELinux)進行限制,但仍有許多特權進程可以對其進行訪問,其中有一些無需通過mcDriverDaemon即可對其進行操作。同樣,UNIX本地套接字只能由特權進程訪問。事實上,三星想要分為直接(libtlc_direct_comm.so)和代理(libtlc_proxy_comm.so)這兩種方式,但有一些進程同時使用了這兩種方式。至此,我們的研究過程還並不完整。很顯然,三星使用這樣的配置,使得安卓系統中一些TrustZone支持的功能能夠完全在特權模式下進行,例如system_server。但還有一種功能,可以將特權進程擴展到應用程序中,完全無需許可權,或是可以通過常規的應用程序來獲取到許可權。這是通過將Binder介面添加到各種特權進程中來實現的。安卓會藉助API來允許進程在其Binder介面上進行訪問控制,因此我們非常希望三星也能夠實現這一點。但是,我們必須要深入進行逆向工程,因為目前沒有相關文檔或相關的現有技術。Gal曾經寫過一個otp_server代理。同樣,還有很多其他的代理。舉例來說,這個例子是針對Samsung Pay場景下實現的: https://www.blackhat.com/docs/eu-17/materials/eu-17-Ma-How-Samsung-Secures-Your-Wallet-And-How-To-Break-It.pdf。在我們的研究中,我專註於使用tlc_server,目前暫時還沒有嘗試過其他情況。
六、與Trustlet的通信
在對這一架構的細節進行研究的過程中,我們注意到一個明顯的漏洞。儘管我們已經知道如何在安卓中設置WSM(TCI緩衝區)來向一個Trustlet發送命令,但是我們並不知道該命令的固定格式,也沒有找到任何用於實現常見任務的指導(或者相關的庫)。那從事實上看,每一個Trustlet都有自己的方式嗎?儘管目前,公開資料中並沒有用於Trustlet通信的通用命令結構,但是Trustonic可能會與合作商分享開發人員指南。那對於我們來說,就只能通過逆向工程來實現了。劇透警告:在最後,通過對Trustlet進行逆向,我發現三星Trustlet的命令看上去是使用了通用頭部格式,但實際上不一定是通用的。並且,對於Trustlet之間的命令有效載荷,它並不會擴展到任意種類的標準結構。
七、小結
接下來,讓我們回顧一下該結構的各個層級:
- 三星Sboot遵循ATF模型。Sboot會載入EL3固件(ATF)和操作系統運行在安全世界的EL0+EL1(T-Base)。在載入完成後,bootloader會將處理器切換成不安全世界模式,並載入應用程序,當然也會載入安卓系統。
- ATF會捕獲到全部世界中EL1的SMC調用,並在二者之間進行代理。
- Linux內核會設置用作MCP命令隊列和通知隊列的共享緩衝區,並通過快速調用(在寄存器中傳遞帶有參數的SMC指令)來初始化與T-Base微內核的通信。
- Linux內核使用另一個快速調用來通知(進行調度)T-Base。T-Base使用Linux內核中註冊的中斷,在處理命令完成後通知不安全世界。
- Linux內核實現了MCP,這些命令允許通過Trustlet實例來載入並共享內存,並通過/dev/mobicore和/dev/mobicore-user設備的ioctl介面對外開放。它包括在WSM範圍之內的完整性檢查,用於確保用戶空間無法請求可能導致不安全世界內部Linux內核損壞/內核特權提升的內存區域。(此前發現,這種健全性的檢查是不充分的。)
- libMcClient.so庫負責ioctl介面的用戶空間實現。
- 用戶空間進程通過不同的libtlc_*共享庫來實現對庫的利用。擁有足夠特權的進程,可以直接使用能夠載入libMcClient.so的變體,其他進程需要通過mcDriverDaemon來獲得訪問許可權。在這裡,通過普通的UNIX套接字暴露了MCP介面,但後面增加了健全性檢查,就可以防止客戶端進行類似於Linux內核的非法MCP行為。
- 套接字仍然受MAC和DAC機制的限制,因此非特權應用程序無法訪問它們。而與之相反,三星增加了各種代理進程(比如tlc_server),通過Binder進一步暴露了mcDriverDaemon介面,從而允許具有足夠許可權的應用程序通過訪問特定的Binder界面的方式來與T-Base進行交互,以及載入Trustlet並與之通信。
八、T-Base映像提取
要提取所有嵌入到Sboot映像中的T-Base固件(例如:微內核、運行時間管理器、tlLibrary、加密驅動等),需要使用字元串標記「t-base」來查找提取表:
ROM:00147000 tbase_extract_table DCB "t-base ",0 ; descriptor struct:
ROM:00147000 ; char name[8]
ROM:00147000 ; int offset
ROM:00147000 ; int size
ROM:00147000 ; char padding[0x10]
ROM:00147000 ;
ROM:00147000 ; real start offset: 0x132000
ROM:00147000 ;
ROM:00147000 ; Mtk: 0->0x147000 -> the Microkernel image itself -> go back 0x15000 from "t-base"
ROM:00147000 ; Image_h: 0x147000 -> 0x148000 -> so thats this
ROM:00147000 ; Rtm: 0x148000 -> RTM is the Run-Time Manager (aka S0CB)
ROM:00147000 ;
ROM:00147000 ; drcrypt: 0x167000 -> MCLF file (Crypto Driver)
ROM:00147000 ; tlproxy: 0x17A000 -> MCLF file (SFS Trustlet)
ROM:00147000 ; sth2: 0x17B000 -> MCLF file (SFS Driver)
ROM:00147000 ; mclib: 0x183000 -> tlLib (runtime library for Trustlets and Drivers)
ROM:00147008 DCD 0
ROM:0014700C DCD 0x5D000
ROM:00147010 ALIGN 0x20
ROM:00147020 aMtk DCB "mtk ",0
ROM:00147028 DCD 0
ROM:0014702C DCD 0x15000
ROM:00147030 ALIGN 0x20
ROM:00147040 aImage_h DCB "image_h",0
ROM:00147048 DCD 0x15000
ROM:0014704C DCD unk_1000
ROM:00147050 ALIGN 0x20
ROM:00147060 aRtm DCB "rtm ",0
ROM:00147068 DCD 0x16000
ROM:0014706C DCD 0x1F000
ROM:00147070 ALIGN 0x20
ROM:00147080 aDrcrypt DCB "drcrypt",0
ROM:00147088 DCD 0x35000
ROM:0014708C DCD 0x13000
ROM:00147090 ALIGN 0x20
ROM:001470A0 aTlproxy DCB "tlproxy",0
ROM:001470A8 DCD 0x48000
ROM:001470AC DCD 0x1000
ROM:001470B0 DCD 0
ROM:001470B4 DCD 0
ROM:001470B8 DCD 0
ROM:001470BC DCD 0
ROM:001470C0 aSth2 DCB "sth2 ",0
ROM:001470C8 DCD 0x49000
ROM:001470CC DCD unk_8000
ROM:001470D0 ALIGN 0x20
ROM:001470E0 aMclib DCB "mclib ",0
ROM:001470E8 DCD 0x51000
ROM:001470EC DCD unk_C000
ROM:001470F0 ALIGN 0x1000
ROM:00148000 S0CB_HDR DCB "S0CB",0 ; Rtm offset points here
ROM:00148005 DCB 0x10, 0, 0
ROM:00148008 DCD unk_C000
ROM:0014800C DCD dword_D000
ROM:00148010 DCD 0
ROM:00148014 DCD 0x11960
ROM:00148018 DCD 0xB295
ROM:0014801C DCD 0x3F
ROM:00148020 DCD 0x6A
ROM:00148024 aMccb DCB "MCCB",0
ROM:00148029 DCD 0
ROM:0014802D DCD 0
九、T-Base SMC
目前,我已經確定了以下T-Base與通過ATF調用的SMC的對應關係:0x1:從不安全世界中進行快速調用,並通過ATF返回到不安全世界中;0xB2000002:將VBAR值發送給ATF(因此ATF知道SMC處理程序的位置);0xB2000003:寫入字元(用於通過ATF記錄消息);0xB2000004:將T-Base初始化狀態發送給ATF。
ROM:00133054 tbase_smc_send_VBAR ; CODE XREF: config_tbase_and_tell_aft_the_vbar+E↑p
ROM:00133054 LDR R0, =0xB2000002
ROM:00133058 MOV R1, #1
ROM:0013305C LDR R2, =0x7F00000 ; normal VBAR address
ROM:00133060 SMC #0
ROM:00133064 BX LR
ROM:00133064 ; End of function tbase_smc_send_VBAR
ROM:00133064
ROM:00133068
ROM:00133068 ; =============== S U B R O U T I N E =======================================
ROM:00133068
ROM:00133068 ; Attributes: noreturn
ROM:00133068
ROM:00133068 tbase_smc_fastcall_status_then_ret_to_nonSW
ROM:00133068 ; CODE XREF: sub_1343AA:loc_1354C8↓p
ROM:00133068 ; translate_MSMBase_to_VA+46↓p
ROM:00133068 LDR R0, =0xB2000004
ROM:0013306C MOV R1, #1
ROM:00133070 MOV R2, #1
ROM:00133074 SMC #0
ROM:00133078 LDR R0, =0x2000001
ROM:0013307C SMC #0
ROM:00133080
ROM:00133080 loc_133080 ; CODE XREF: tbase_smc_fastcall_status_then_ret_to_nonSW:loc_133080↓j
ROM:00133080 B loc_133080
ROM:00133080 ; End of function tbase_smc_fastcall_status_then_ret_to_nonSW
ROM:00133080
ROM:00133084
ROM:00133084 ; =============== S U B R O U T I N E =======================================
ROM:00133084
ROM:00133084
ROM:00133084 tbase_smc_fastcall_status ; CODE XREF: sub_1343AA+31C6↓p
ROM:00133084 LDR R0, =0xB2000004
ROM:00133088 MOV R1, #1
ROM:0013308C MOV R2, #3
ROM:00133090 SMC #0
ROM:00133094 BX LR
ROM:00133094 ; End of function tbase_smc_fastcall_status
ROM:00133094
ROM:00133098
ROM:00133098 ; =============== S U B R O U T I N E =======================================
ROM:00133098
ROM:00133098
ROM:00133098 tbase_smc_fastcall_print
ROM:00133098 MOV R2, R0
ROM:0013309C LDR R0, =0xB2000003
ROM:001330A0 MOV R1, #1
ROM:001330A4 SMC #0
ROM:001330A8 BX LR
ROM:001330A8 ; End of function tbase_smc_fastcall_print
十、T-Base系統調用
.tbase_mem_data:07F0D86C ; ===========================================================================
.tbase_mem_data:07F0D86C
.tbase_mem_data:07F0D86C ; Segment type: Pure data
.tbase_mem_data:07F0D86C AREA .tbase_mem_data, DATA, ALIGN=0
.tbase_mem_data:07F0D86C ; ORG 0x7F0D86C
.tbase_mem_data:07F0D86C syscall_table DCD svc_0_nop+1 ; DATA XREF: invoke_syscall_from_table+40↑o
.tbase_mem_data:07F0D86C ; invoke_syscall_from_table:syscall_table_ptr↑o
.tbase_mem_data:07F0D870 DCD svc_1_init_process+1
.tbase_mem_data:07F0D874 DCD svc_2_nop+1
.tbase_mem_data:07F0D878 DCD svc_3_nop+1
.tbase_mem_data:07F0D87C DCD svc_4+1 ; Did not find this invoked anywhere in {RTM,tlLib}
.tbase_mem_data:07F0D880 DCD svc_5_start_process+1
.tbase_mem_data:07F0D884 DCD svc_exit+1
.tbase_mem_data:07F0D888 DCD svc_mmap+1
.tbase_mem_data:07F0D88C DCD svc_8_munmap+1
.tbase_mem_data:07F0D890 DCD svc_9_start_thread+1
.tbase_mem_data:07F0D894 DCD svc_A_stop_thread+1
.tbase_mem_data:07F0D898 DCD svc_B_return_0xD+1
.tbase_mem_data:07F0D89C DCD svc_C_modify_thread_registers+1
.tbase_mem_data:07F0D8A0 DCD svc_D_mprotect+1
.tbase_mem_data:07F0D8A4 DCD svc_E_resume_thread+1
.tbase_mem_data:07F0D8A8 DCD svc_F+1
.tbase_mem_data:07F0D8AC DCD svc_10_set_thread_prio+1
.tbase_mem_data:07F0D8B0 DCD svc_11_ipc+1
.tbase_mem_data:07F0D8B4 DCD svc_12_int_attach+1
.tbase_mem_data:07F0D8B8 DCD svc_13_int_detach+1
.tbase_mem_data:07F0D8BC DCD svc_14_sigwait+1
.tbase_mem_data:07F0D8C0 DCD svc_15_signal+1
.tbase_mem_data:07F0D8C4 DCD svc_16+1 ; Did not find this invoked anywhere in {RTM,tlLib}
.tbase_mem_data:07F0D8C8 DCD svc_tbase_smc_fastcall_input+1
.tbase_mem_data:07F0D8CC DCD svc_18_log_char+1
.tbase_mem_data:07F0D8D0 DCD svc_19_get_secure_timestamp+1
.tbase_mem_data:07F0D8D4 DCD svc_1A_control+1 ; includes a lot, such as:
.tbase_mem_data:07F0D8D4 ; - driver shmem map/unmap
.tbase_mem_data:07F0D8D4 ; - get/set exception info
.tbase_mem_data:07F0D8D4 ; - get MCP queue info
.tbase_mem_data:07F0D8D4 ; - get IPCH phys address values
.tbase_mem_data:07F0D8D4 ; - cache control
.tbase_mem_data:07F0D8D4 ; - virt2phys, phys2virt translation
.tbase_mem_data:07F0D8D4 ; - set custom fastcall, call custom fastcall
.tbase_mem_data:07F0D8D4 ;
.tbase_mem_data:07F0D8D4 ; Known sub-handlers:
.tbase_mem_data:07F0D8D4 ;
.tbase_mem_data:07F0D8D4 ; -0x8F: getting/setting fastcall configuration values
.tbase_mem_data:07F0D8D4 ; - 0xC: get S0CB PA
.tbase_mem_data:07F0D8D4 ; - 0xA: notify (nSW - trigger interrupt)
.tbase_mem_data:07F0D8D4 ; - 0xB: notify driver (drTriggerIntr)
.tbase_mem_data:07F0D8D4 ; - 0xD: get fc_init perm flags
.tbase_mem_data:07F0D8D4 ; - 0x1: set exception info
.tbase_mem_data:07F0D8D4 ; - 0x2: get fault info
.tbase_mem_data:07F0D8D4 ; - 0x4,0x5,0x6,0x7: get MCP queue info
.tbase_mem_data:07F0D8D4 ; (mci_buffer_addr, nq_length, mcp_queue_addr, mcp_queue_len)
.tbase_mem_data:07F0D8D4 ; - 0x9: map mcp cmd queue (in kernel)
.tbase_mem_data:07F0D8D4 ; -0x90 -> more control
.tbase_mem_data:07F0D8D4 ; - 5: read info for exception
.tbase_mem_data:07F0D8D4 ; - 7: translate VA to PA
.tbase_mem_data:07F0D8D4 ; -0x91 virt-to-phys and also virt-to-phys64
.tbase_mem_data:07F0D8D4 ; -0x92 -> I-cache clean/invalidate, D-cache clean/invalidate
.tbase_mem_data:07F0D8D4 ; -0x81:map, 0x83: unmap, 0x85:map.
.tbase_mem_data:07F0D8D4 ; - Difference in 81/85: map in
十一、RTM IPC
請注意,在這裡我們稱RTM為「S0CB」,是由於其文件格式的魔術值(Magic Value)。根據Sboot固件中的提示,更加準確的一個名稱應該是RTM(Run Time Manager,運行時間管理器)。這是T-Base中一個強大的用戶空間進程,始終最先啟動,並負責啟動和管理所有其他進程(Trustlet)。與Linux上的init類似,它負責三個功能:
- 實現MCP;
- 當請求從不安全世界到達時,通知Trustlet;
- 實現T-Base的IPC機制。其中,MCP命令都可以從公開渠道獲知。對於IPC,前12個IPC命令可以在這裡找到源代碼:https://searchcode.com/codesearch/view/42497996/ 。在這裡,我參考了相關的命名約定,對於其餘部分,具體如下:
/** Possible message types/event types of the system. */
typedef
enum {
/***************/ MSG_RQ = 1, /**< Request; client -> server; */ MSG_RS = 2, /**< Response; server -> client */ MSG_RD = 3, /**< Ready; server -> IPCH */ MSG_NOT = 4, /**< Notification; client -> IPCH; */ MSG_CLOSE_TRUSTLET = 5, /**< Close Trustlet; MSH -> IPCH; IPCH -> all servers */ MSG_CLOSE_TRUSTLET_ACK = 6, /**< Close Trustlet Ack; servers -> IPCH */ MSG_MAP = 7, /**< Map; driver <-> IPCH; */ MSG_ERR_NOT = 8, /**< Error Notification; EXCH/SIQH -> IPCH; */ MSG_CLOSE_DRIVER = 9, /**< Close driver; MSH -> IPCH; IPCH -> driver */ MSG_CLOSE_DRIVER_ACK = 10, /**< Close driver Ack; driver -> IPCH; IPCH -> MSH */ MSG_GET_DRIVER_VERSION = 11, /**< GetDriverVersion; client <--> IPCH */ MSG_GET_DRAPI_VERSION = 12, /**< GetDrApiVersion; driver <--> IPCH */ MSG_SET_NOTIFICATION_HANDLER = 13, // Driver <-> IPCH MSG_DRV_NOT = 15, // Driver -> Trustlet MSG_SET_FASTCALL_HANDLER = 16, // Driver <-> IPCH MSG_GET_CLIENT_ROOT_AND_SP_ID = 17, // Driver <-> IPCH MSG_CALL_FASTCALL = 25, // DRIVER -> IPCH -> Kernel MSG_GET_CLI_UUID = 26, // Driver <-> IPCH MSG_RQ_2 = 27, // Client -> Server MSG_MAP_VA_DIRECT = 31, // DRIVER <-> IPCH MSG_MAP_PY_DIRECT = 32, // DRIVER <-> IPCH MSG_UNMAP_PY_DIRECT = 33, // DRIVER <-> IPCH MSG_GET_MEM_TYPE = 34, // DRIVER <-> IPCH MSG_UNMAP_VA_DIRECT = 35
// DRIVER <-> IPCH} message_t;
十二、tlApi
ROM:07D0BE38
tlApi_syscall_table
DCD
tlApiNOP+1 ; 0ROM:07D0BE38
DCD
tlApiGetVersion+1 ; 1ROM:07D0BE38
DCD
tlApiGetMobicoreVersion+1; 2ROM:07D0BE38
DCD
tlApiGetPlatformInfo+1; 3ROM:07D0BE38
DCD
tlApiExit+1 ; 4ROM:07D0BE38
DCD
tlApiLogvPrintf+1 ; 5ROM:07D0BE38
DCD
tlApiWaitNotification+1; 6ROM:07D0BE38
DCD
tlApiNotify+1 ; 7ROM:07D0BE38
DCD
tlApi_callDriver+1 ; 8ROM:07D0BE38
DCD
tlApiWrapObjectExt+1; 9ROM:07D0BE38
DCD
tlApiUnwrapObjectExt+1; 10ROM:07D0BE38
DCD
tlApiGetSuid+1 ; 11ROM:07D0BE38
DCD
tlApiSecSPICmd+1 ; 12ROM:07D0BE38
DCD
tlApiCrAbort+1 ; 13ROM:07D0BE38
DCD
tlApiRandomGenerateData+1; 14ROM:07D0BE38
DCD
tlApiGenerateKeyPair+1; 15ROM:07D0BE38
DCD
tlApiCipherInitWithData+1; 16ROM:07D0BE38
DCD
tlApiCipherUpdate+1 ; 17ROM:07D0BE38
DCD
tlApiCipherDoFinal+1; 18ROM:07D0BE38
DCD
tlApiSignatureInitWithData+1; 19ROM:07D0BE38
DCD
tlApiSignatureUpdate+1; 20ROM:07D0BE38
DCD
tlApiSignatureSign+1; 21ROM:07D0BE38
DCD
tlApiSignatureVerify+1; 22ROM:07D0BE38
DCD
tpiApiMessageDigestInitWithData+1; 23ROM:07D0BE38
DCD
tlApiMessageDigestUpdate+1; 24ROM:07D0BE38
DCD
tlApiMessageDigestDoFinal+1; 25ROM:07D0BE38
DCD
tlApiGetVirtMemType+1; 26ROM:07D0BE38
DCD
tlApiDeriveKey+1 ; 27ROM:07D0BE38
DCD
tlApiMalloc+1 ; 28ROM:07D0BE38
DCD
tlApiRealloc+1 ; 29ROM:07D0BE38
DCD
tlApiFree+1 ; 30(...)ROM:07D0BE38
DCD
tlApiGetIDs+1 ; 43(...)ROM:07D0BE38
DCD
tlApiRandomGenerateData_wrap+1; 83ROM:07D0BE38
DCD
tlApiCrash+1 ; 84ROM:07D0BE38
DCD
tlApiEndorse+1 ; 85ROM:07D0BE38
DCD
tlApiTuiGetScreenInfo+1; 86ROM:07D0BE38
DCD
tlApiTuiOpenSession+1; 87ROM:07D0BE38
DCD
tlApiTuiCloseSession+1; 88ROM:07D0BE38
DCD
tlApiTuiSetImage+1 ; 89ROM:07D0BE38
DCD
tlApiTuiGetTouchEvent+1; 90ROM:07D0BE38
DCD
tlApiTuiGetTouchEventsLoop+1; 91ROM:07D0BE38
DCD
tlApiDrmProcessContent+1; 92ROM:07D0BE38
DCD
tlApiDrmOpenSession+1; 93ROM:07D0BE38
DCD
tlApiDrmCloseSession+1; 94ROM:07D0BE38
DCD
tlApiDrmCheckLink+1 ; 95ROM:07D0BE38
DCD
tlApiDeriveKey_wrapper+1; 96ROM:07D0BE38
DCD
tlApiUnwrapObjectExt_wrapper+1; 97ROM:07D0BE38
DCD
tlApiGetSecureTimestamp+1; 98
十三、drApi
ROM:07D0C114 ; int
drApi_syscall_table[62]
ROM:07D0C114
drApi_syscall_table
DCD
drApiGetVersion+1 ; 0ROM:07D0C114 ; DATA
XREF: get_syscall_fn+30↑o
ROM:07D0C114 ; ROM:off_7D0A9F8↑o
ROM:07D0C114
DCD
drApiExit+1 ; 1ROM:07D0C114
DCD
drApiMapPhys+1 ; 2ROM:07D0C114
DCD
drApiUnmap+1 ; 3ROM:07D0C114
DCD
drApiMapPhysPage4KBWithHardware+1; 4ROM:07D0C114
DCD
drApiMapClient+1 ; 5ROM:07D0C114
DCD
drApiMapClientAndParams+1; 6ROM:07D0C114
DCD
drApiAddrTranslateAndCheck+1; 7ROM:07D0C114
DCD
drApiGetTaskid+1 ; 8ROM:07D0C114
DCD
drApiTaskidGetThreadid+1; 9ROM:07D0C114
DCD
drApiGetLocalThreadId+1; 10ROM:07D0C114
DCD
drApiStartThread+1 ; 11ROM:07D0C114
DCD
drApiStopThread+1 ; 12ROM:07D0C114
DCD
drApiResumeThread+1 ; 13ROM:07D0C114
DCD
drApiThreadSleep+1 ; 14ROM:07D0C114
DCD
drApiSetThreadPriority+1; 15ROM:07D0C114
DCD
drApiIntrAttach+1 ; 16ROM:07D0C114
DCD
drApiIntrDetach+1 ; 17ROM:07D0C114
DCD
drApiWaitForIntr+1 ; 18ROM:07D0C114
DCD
drApiTriggerIntr+1 ; 19ROM:07D0C114
DCD
drApiIpcWaitForMessage+1; 20ROM:07D0C114
DCD
drApiIpcCallToIPCH+1; 21ROM:07D0C114
DCD
drApiIpcSignal+1 ; 22ROM:07D0C114
DCD
drApiIpcSigWait+1 ; 23ROM:07D0C114
DCD
drApiNotify+1 ; 24ROM:07D0C114
DCD
drApiSystemCtrl+1 ; 25ROM:07D0C114
DCD
sub_7D0A2CE+1 ; 26ROM:07D0C114
DCD
drApiVirt2Phys+1 ; 27ROM:07D0C114
DCD
drApiCacheDataClean+1; 28ROM:07D0C114
DCD
drApiCacheDataCleanAndInvalidate+1; 29ROM:07D0C114
DCD
drApiNotifyClient+1 ; 30ROM:07D0C114
DCD
drApiThreadExRegs+1 ; 31ROM:07D0C114
DCD
drApiInstallFc+1 ; 32ROM:07D0C114
DCD
drApiIpcUnknownMessage+1; 33ROM:07D0C114
DCD
drApiIpcUnknownException+1; 34ROM:07D0C114
DCD
drApiGetPhysMemType+1; 35ROM:07D0C114
DCD
drApiGetClientRootAndSpId+1; 36ROM:07D0C114
DCD
drApiCacheDataCleanRange+1; 37ROM:07D0C114
DCD
drApiCacheDataCleanAndInvalidateRange+1; 38ROM:07D0C114
DCD
drApiMapPhys64+1 ; 39ROM:07D0C114
DCD
drApiMapPhys64_2+1 ; 40ROM:07D0C114
DCD
drApiVirt2Phys64+1 ; 41ROM:07D0C114
DCD
drApiGetPhysMemType64+1; 42ROM:07D0C114
DCD
drApiUpdateNotificationThread+1; 43ROM:07D0C114
DCD
drApiRestartThread+1; 44ROM:07D0C114
DCD
drApiGetSecureTimestamp+1; 45ROM:07D0C114
DCD
drApiFastCall+1 ; 46ROM:07D0C114
DCD
drApiGetClientUuid+1; 47ROM:07D0C114
DCD
sub_7D0A2AC+1 ; 48ROM:07D0C114
DCD
drApiMapVirtBuf+1 ; 49ROM:07D0C114
DCD
drApiUnmapPhys2+1 ; 50ROM:07D0C114
DCD
drApiMapPhys2+1 ; 51ROM:07D0C114
DCD
drApiUnmapVirtBuf2+1; 52ROM:07D0C114
DCD
sub_7D07D76+1 ; 53ROM:07D0C114
DCD
sub_7D0A558+1 ; 54ROM:07D0C114
DCD
sub_7D0A574+1 ; 55ROM:07D0C114
DCD
sub_7D0A5D6+1 ; 56ROM:07D0C114
DCD
sub_7D0A738+1 ; 57ROM:07D0C114
DCD
sub_7D0A1BA+1 ; 58ROM:07D0C114
DCD
sub_7D0A1C8+1 ; 59ROM:07D0C114
DCD
sub_7D0A5F4+1 ; 60ROM:07D0C114
DCD
sub_7D0A60C+1 ; 61
十四、沙盒機制
- 內核和RTM都會維持其自身到Trustlet或驅動的進程實例映射,並維持進程的虛擬地址空間映射。
- 內核會對SVC調用者的類型進行檢查,限制於驅動。RTM也會對IPC調用者的類型進行檢查,同樣限制於驅動。上述並不是一個核心機制,但必須針對每個案例正確執行。這一點,類似於Linux上的access_ok()檢查。
- 這二者共同防止了將其他Trustlet的虛擬內存或任何物理內存(包括不安全世界)映射到驅動的可能性。但驅動仍然不能映射RWX內存,也不能在自己的代碼頁映射任何內容。
- 驅動程序可以使用SVC,從內核獲取並得到發出當前請求Trustlet的UUID。這樣可以用來過濾哪些客戶端(Trustlet)應該被允許訪問驅動的功能。
- 驅動可以使用SVC,將不安全世界或安全世界的物理內存映射到其地址空間,也可以使用SVC來註冊自定義的快速調用處理器,使其能夠直接從安全世界的EL1執行代碼。實際上,這使得驅動擁有和安全世界EL1一樣的特權。
十五、漏洞利用緩解措施(Trustlet和驅動)
- 分為RX代碼頁和RW數據頁;
- 使棧/bss段/堆之間沒有界限;
- 映射到Trustlet的WSM和映射到驅動的Trustlet內存,應該存儲於獨立的內存區域中;
- 關閉Stack Canaries保護機制,關閉ASLR保護機制。
原文鏈接:
[1] https://medium.com/taszksec/unbox-your-phone-part-i-331bbf44c30c[2] https://medium.com/taszksec/unbox-your-phone-part-ii-ae66e779b1d6推薦閱讀:
※如何優雅地做一個使用OS X的黑客?
※青蛙旅行 — Unity3d類安卓遊戲逆向分析初探
※威鋒網友寫的 360 App 逆向分析結果可信么?
※CTF逆向題中的編程技巧
TAG:逆向工程 |