自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

iOS符號表恢復(fù)&逆向支付寶

移動開發(fā) iOS
本文介紹了恢復(fù)符號表的技巧,并且利用該技巧實現(xiàn)了在 Xcode 中對目標程序下符號斷點調(diào)試,該技巧可以顯著地減少逆向分析時間。在文章的最后,作者以支付寶為例,展示出通過在 UIAlertView 的 show 方法處下斷點,從而獲得支付寶的調(diào)用棧的過程。

推薦序

本文介紹了恢復(fù)符號表的技巧,并且利用該技巧實現(xiàn)了在 Xcode 中對目標程序下符號斷點調(diào)試,該技巧可以顯著地減少逆向分析時間。在文章的***,作者以支付寶為例,展示出通過在 UIAlertView 的 show 方法處下斷點,從而獲得支付寶的調(diào)用棧的過程。

本文涉及的代碼也開源在:https://github.com/tobefuturer/restore-symbol,歡迎 Star 和提 Issue。感謝作者授權(quán)發(fā)表。

作者介紹:楊君,中山大學(xué)計算機系研究生,iOS 開發(fā)者,擅長領(lǐng)域 iOS 安全和逆向工程,個人博客:http://blog.imjun.net 。

前言

符號表歷來是逆向工程中的 “必爭之地”,而 iOS 應(yīng)用在上線前都會裁去符號表,以避免被逆向分析。

本文會介紹一個自己寫的工具,用于恢復(fù) iOS 應(yīng)用的符號表。

直接看效果 , 支付寶恢復(fù)符號表后的樣子:   

 

文章有點長,請耐心看到***,亮點在***。

為什么要恢復(fù)符號表

逆向工程中,調(diào)試器的動態(tài)分析是必不可少的,而 Xcode + lldb 確實是非常好的調(diào)試利器 , 比如我們在 Xcode 里可以很方便的查看調(diào)用堆棧,如上面那張圖可以很清晰的看到支付寶登錄的 RPC 調(diào)用過程。

實際上,如果我們不恢復(fù)符號表的話,你看到的調(diào)試頁面應(yīng)該是下面這個樣子: 

 

同一個函數(shù)調(diào)用過程,Xcode 的顯示簡直天差地別。

原因是,Xcode 顯示調(diào)用堆棧中符號時,只會顯示符號表中有的符號。為了我們調(diào)試過程的順利,我們有必要把可執(zhí)行文件中的符號表恢復(fù)回來。

符號表是什么

我們要恢復(fù)符號表,首先要知道符號表是什么,他是怎么存在于 Mach-O 文件中的。

符號表儲存在 Mach-O 文件的 __LINKEDIT 段中,涉及其中的符號表(Symbol Table)和字符串表(String Table)。

這里我們用 MachOView 打開支付寶的可執(zhí)行文件,找到其中的 Symbol Table 項。 

 

符號表的結(jié)構(gòu)是一個連續(xù)的列表,其中的每一項都是一個 struct nlist。

  1. //  位于系統(tǒng)庫 <macho-o/nlist.h> 頭文件中 
  2. struct nlist {   
  3.   union {   
  4.     // 符號名在字符串表中的偏移量 
  5.     uint32_t n_strx;   
  6.   } n_un; 
  7.   uint8_t n_type; 
  8.   uint8_t n_sect; 
  9.   int16_t n_desc;   
  10.   // 符號在內(nèi)存中的地址,類似于函數(shù)指針 
  11.   uint32_t n_value; 
  12. };  

這里重點關(guān)注***項和***一項,***項是符號名在字符串表中的偏移量,用于表示函數(shù)名,***一項是符號在內(nèi)存中的地址,類似于函數(shù)指針(這里只說明大概的結(jié)構(gòu),詳細的信息請參考官方 Mach O 文件格式的文檔)。

也就是說如果我們知道了符號名和內(nèi)存地址的對應(yīng)關(guān)系,我們是可以根據(jù)這個結(jié)構(gòu)來逆向構(gòu)造出符號表數(shù)據(jù)的。

知道了如何構(gòu)造符號表,下一步就是收集符號名和內(nèi)存地址的對應(yīng)關(guān)系了。

獲取 OC 方法的符號表

因為 OC 語言的特性,編譯器會將類名、函數(shù)名等編譯進***的可執(zhí)行文件中,所以我們可以根據(jù) Mach-O 文件的結(jié)構(gòu)逆向還原出工程里的所有類,這也就是大名鼎鼎的逆向工具 class-dump 了。class-dump 出來的頭文件里是有函數(shù)地址的: 

 

所以我們只要對 class-dump 的源碼稍作修改,即可獲取我們要的信息。

符號表恢復(fù)工具

整理完數(shù)據(jù)格式,又理清了數(shù)據(jù)來源,我們就可以寫工具了。

實現(xiàn)過程就不詳細說明了,工具開源在我的 Github 上了,鏈接:

https://github.com/tobefuturer/restore-symbol

我們來看看怎么用這個工具:

1. 下載源碼編譯

  1. git clone --recursive https://github.com/tobefuturer/restore-symbol.git 
  2. cd restore-symbol && make 
  3. ./restore-symbol  

2. 恢復(fù) OC 的符號表,非常簡單

  1. ./restore-symbol ./origin_AlipayWallet -o ./AlipayWallet_with_symbol 

origin_AlipayWallet 為 Clutch 砸殼后,沒有符號表的 Mach-O 文件

-o 后面跟輸出文件位置

3. 把 Mach-O 文件重簽名打包,看效果

文件恢復(fù)符號表后,多出了 20M 的符號表信息   

 

Xcode 里查看調(diào)用棧 

 

可以看到,OC 函數(shù)這部分的符號已經(jīng)恢復(fù)了,函數(shù)調(diào)用棧里已經(jīng)能看出大致的調(diào)用過程了,但是支付寶里,采用了 block 的回調(diào)形式,所以還有很大一部分的符號沒能正確顯示。

下面我們就來看看怎么樣恢復(fù)這部分 block 的符號。

獲取 block 的符號信息

還是同樣的思路,要恢復(fù) block 的符號信息,我們必須知道 block 在文件中的儲存形式。

block 在內(nèi)存中的結(jié)構(gòu)

首先,我們先分析下運行時,block 在內(nèi)存中的存在形式。block 在內(nèi)存中是以一個結(jié)構(gòu)體的形式存在的,大致的結(jié)構(gòu)如下:

  1. struct __block_impl {   
  2.   /** 
  3.   block 在內(nèi)存中也是類 NSObject 的結(jié)構(gòu)體, 
  4.   結(jié)構(gòu)體開始位置是一個 isa 指針 
  5.   */ 
  6.   Class isa;   
  7.   /** 這兩個變量暫時不關(guān)心 */ 
  8.   int flags;  int reserved;   
  9.   /** 
  10.   真正的函數(shù)指針??! 
  11.   */ 
  12.   void (*invoke)(...); 
  13.   ... 
  14.  

說明下 block 中的 isa 指針,根據(jù)實際情況會有三種不同的取值,來表示不同類型的 block:

  1. _NSConcreteStackBlock

棧上的 block,一般 block 創(chuàng)建時是在棧上分配了一個 block 結(jié)構(gòu)體的空間,然后對其中的 isa 等變量賦值。

     2._NSConcreteMallocBlock

堆上的 block,當 block 被加入到 GCD 或者被對象持有時,將棧上的 block 復(fù)制到堆上,此時復(fù)制得到的 block 類型變?yōu)榱? _NSConcreteMallocBlock。

     3._NSConcreteGlobalBlock

全局靜態(tài)的 block,當 block 不依賴于上下文環(huán)境,比如不持有 block 外的變量、只使用 block 內(nèi)部的變量的時候,block 的內(nèi)存分配可以在編譯期就完成,分配在全局的靜態(tài)常量區(qū)。

第 2 種 block 在運行時才會出現(xiàn),我們只關(guān)注 1、3 兩種,下面就分析這兩種 isa 指針和 block 符號地址之間的關(guān)聯(lián)。

block isa 指針和符號地址之間的關(guān)聯(lián)

分析這部分需要用到 IDA 這個反匯編軟件 , 這里結(jié)合兩個實際的小例子來說明:

1._NSConcreteStackBlock

假設(shè)我們的源代碼是這樣很簡單的一個 block:

  1. @implementation ViewController 
  2.  
  3. - (void)viewDidLoad { 
  4.     int t = 2; 
  5.     void (^ foo)() = ^(){ 
  6.         NSLog(@"%d", t); //block 引用了外部的變量 t 
  7.     }; 
  8.     foo(); 
  9.  
  10. @end  

編譯完后,實際的匯編長這個樣子: 

 

 

 

實際運行時,block 的構(gòu)造過程是這樣:

  • 為 block 開辟??臻g
  • 為 block 的 isa 指針賦值(一定會引用全局變量:_NSConcreteStackBlock)
  • 獲取函數(shù)地址,賦值給函數(shù)指針

所以我們可以整理出這樣一個特征:

重點來了 !!!

凡是代碼里用到了棧上的 block,一定會獲取__NSConcreteStackBlock作為 isa 指針,同時會緊接著獲取一個函數(shù)地址,那個函數(shù)地址就是 block 的函數(shù)地址。

結(jié)合下面這個圖,仔細理解上面這句話

(這張圖和上面那張圖是同一個文件,不過裁掉了符號表) 

 

 

 

利用這個特征,逆向分析時我們可以做如下推斷:

在一個 OC 方法里發(fā)現(xiàn)引用了__NSConcreteStackBlock這個變量,那么在這附近,一定會出現(xiàn)一個函數(shù)地址,這個函數(shù)地址就是這個 OC 方法里的一個 block。

比如上面圖中,我們發(fā)現(xiàn) viewDidLoad 里,引用了__NSConcreteStackBlock, 同時緊接著加載了 sub_100049D4 的函數(shù)地址,那我們就可以認定 sub_100049D4 是 viewDidLoad 里的一個 block, sub_100049D4 函數(shù)的符號名應(yīng)該是 viewDidLoad_block.

2. _NSConcreteGlobalBlock

全局的靜態(tài) block,是那種不引用 block 外變量的 block,他因為不引用外部變量,所以他可以在編譯期就進行內(nèi)存分配操作,也不用擔心 block 的復(fù)制等等操作,他存在于可執(zhí)行文件的常量區(qū)里。

不太理解的話,看個例子:

我們把源代碼改成這樣:

  1. @implementation ViewController 
  2.  
  3. - (void)viewDidLoad { 
  4.  
  5.     void (^ foo)() = ^(){ 
  6.         //block 不引用外部的變量 
  7.         NSLog(@"%d", 123); 
  8.     }; 
  9.     foo(); 
  10.  
  11. @end  

那么在編譯后會變成這樣: 

 

 

 

那么借鑒上面的思路,在逆向分析的時候,我們可以這么推斷

  • 在靜態(tài)常量區(qū)發(fā)現(xiàn)一個 _NSConcreteGlobalBlock 的引用
  • 這個地方必然存在一個 block 的結(jié)構(gòu)體數(shù)據(jù)
  • 在這個結(jié)構(gòu)體第 16 個字節(jié)的地方會出現(xiàn)一個值,這個值是一個 block 的函數(shù)地址

3. block 的嵌套結(jié)構(gòu)

實際在使用中,可能會出現(xiàn) block 內(nèi)嵌 block 的情況:

  1. - (void)viewDidLoad { 
  2.   dispatch_async(background_queue ,^{ 
  3.     ... 
  4.     dispatch_async(main_queue, ^{ 
  5.       ...      
  6.     }); 
  7.   }); 
  8.  

所以這里 block 就出現(xiàn)了父子關(guān)系,如果我們將這些父子關(guān)系收集起來,就可以發(fā)現(xiàn),這些關(guān)系會構(gòu)成圖論里的森林結(jié)構(gòu),這里可以簡單用遞歸的深度優(yōu)先搜索來處理,詳細過程不再描述。

block 符號表提取腳本(IDA+python)

整理上面的思路,我們發(fā)現(xiàn)搜索過程依賴于 IDA 提供各種引用信息,而 IDA 是提供了編程接口的,可以利用這些接口來提取引用信息。

IDA 提供的是 Python 的 SDK,***完成的腳本也放在倉庫里 search_oc_block/ida_search_block.py (https://github.com/tobefuturer/restore-symbol/blob/master/search_oc_block/ida_search_block.py。

提取 block 符號表

這里簡單介紹下怎么使用上面這個腳本:

  1. 用 IDA 打開支付寶的 Mach-O 文件
  2. 等待分析完成! 可能要一個小時
  3. Alt + F7 或者 菜單欄 File -> Script file... 

 

 

 

    4.等待腳本運行完成,預(yù)計 30s 至 60s,運行過程中會有這樣的彈窗 

 

 

 

   5.彈窗消失即 block 符號表提取完成

   6.在 IDA 打開文件的目錄下 , 會輸出一份名為block_symbol.json的 json 格式 block 符號表 

 

 

  

 

 

 

恢復(fù)符號表 & 實際分析

用之前的符號表恢復(fù)工具,將 block 的符號表導(dǎo)入 Mach-O 文件

  1. ./restore-symbol ./origin_AlipayWallet -o ./AlipayWallet_with_symbol -j block_symbol.json 

-j 后面跟上之前得到的 json 符號表

***得到一份同時具有 OC 函數(shù)符號表和 block 符號表的可執(zhí)行文件

這里簡單介紹一個分析案例 , 你就能體會到這個工具的強大之處了。

1.在 Xcode 里對 -[UIAlertView show] 設(shè)置斷點 

 

 

 

2.運行程序,并在支付寶的登錄頁面輸入手機號和 錯誤的密碼 ,點擊登錄

3.Xcode 會在 ‘密碼錯誤’ 的警告框彈出時停下,左側(cè)會顯示出這樣的調(diào)用棧

一張圖看完支付寶的登錄過程 

 

 

 

項目開源地址:

https://github.com/tobefuturer/restore-symbol

歡迎大家在上面提各種 Issues,或者有問題也可以直接 Email(tobefuturer@gmail.com)。

責(zé)任編輯:龐桂玉 來源: iOS開發(fā)by唐巧
相關(guān)推薦

2021-01-25 14:13:26

iOS支付寶支付

2021-09-09 15:30:28

鴻蒙HarmonyOS應(yīng)用

2014-11-17 10:52:56

支付寶去阿里化

2009-09-17 12:15:28

互聯(lián)網(wǎng)

2018-03-27 12:02:31

央行支付寶紅包

2018-02-25 11:22:14

SDK代碼接口

2011-04-21 11:27:42

Firefox支付寶

2009-11-23 10:02:22

PHP支付寶接口

2009-12-14 16:31:00

Linux安裝支付寶

2017-12-18 18:23:09

支付寶掃碼賺錢支付寶套路

2013-10-31 11:24:53

支付寶漏洞支付寶漏洞

2009-08-26 16:07:09

支付寶網(wǎng)上購物安全邁克菲

2013-10-11 09:41:01

Windows 8.1支付寶

2023-11-28 08:53:15

2021-05-12 07:10:55

支付寶數(shù)字人民幣移動應(yīng)用

2015-05-28 09:52:21

光纖支付寶

2013-06-20 10:45:10

余額寶支付寶大數(shù)據(jù)

2013-11-28 11:15:43

微信支付寶支付戰(zhàn)爭

2011-12-28 21:43:40

蘋果

2020-08-14 11:00:41

支付寶
點贊
收藏

51CTO技術(shù)棧公眾號