亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > iOS > 正文

iOS通過逆向理解Block的內存模型

2020-07-26 02:57:56
字體:
來源:轉載
供稿:網友

前言

正常情況下,通過分析界面以及 class-dump 出來頭文件就能對某個功能的實現猜個八九不離十。但是 Block 這種特殊的類型在頭文件中是看不出它的聲明的,一些有 Block 回調的方法名 dump 出來是類似這樣的:

- (void)FM_GetSubscribeList:(long long)arg1 pageSize:(long long)arg2 callBack:(CDUnknownBlockType)arg3;

因為這種回調看不到它的方法簽名,我們無法知道這個 Block 到底有幾個參數,也不知道它函數體的具體地址,因此在使用 lldb 進行動態調試的時候也是困難重重。我也一度被這個困難所阻擋,以為調用到有 Block 的方法就是進了死胡同,沒辦法繼續跟蹤下去了。我還因此放棄過好幾次對某個功能的分析,特別受挫。

好在,我們還有 Google 這個強大的武器。沒有什么問題是一次 Google 不能解決的。如果有,那就兩次。

這篇文章就來講講如何通過 Block 的內存模型來分析出它的函數體地址,以及函數簽名。

Block 的內存結構

在 LLVM 文檔中,可以看到 Block 的實現規范,其中最關鍵的地方是對于 Block 內存結構的定義:

struct Block_literal_1 { void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor_1 { unsigned long int reserved;  // NULL unsigned long int size;  // sizeof(struct Block_literal_1) // optional helper functions void (*copy_helper)(void *dst, void *src); // IFF (1<<25) void (*dispose_helper)(void *src);  // IFF (1<<25) // required ABI.2010.3.16 const char *signature;    // IFF (1<<30) } *descriptor; // imported variables};

可以看到第一個成員是 isa,說明了 Block 在 Objective-C 當中也是一個對象。我們重點要關注的就是 void (*invode)(void *, ...); 和 descriptor 中的 const char *signature,前者指向了 Block 具體實現的地址,后者是表示 Block 函數簽名的字符串。

實戰

注:本篇文章都是在 64 位系統下進行分析,如果是 32 位系統,整型與指針類型的大小都是與 64 位不一致的,請自行進行修改。

知道了 Block 的內存模型后,就可以直接打開 hopper 和 lldb 進行調試了。

我這里使用了邏輯思維的得到 APP 作為分析的例子。順便說一句,得到上面的內容都相當不錯,很多付費專欄的內容都是很贊的,值得一看。

準備

設備:iPhone 5s iOS 8.2 越獄

usbmuxd

$ tcprelay -t 22:2222 1234:1234Forwarding local port 2222 to remote port 22Forwarding local port 1234 to remote port 1234......

ssh 到 iOS 設備并啟動 debugserver:

$ ssh root@localhost -p 2222iPhone $ debugserver *:1234 -a "LuoJiFM-IOS"ebugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-320.2.89 for arm64.Attaching to process LuoJiFM-IOS...Listening to port 1234 for a connection from *...

本地打開 lldb 并遠程附加進程,進行動態調試:

$ lldb(lldb) process connect connect://localhost:1234

找到偏移地址:

(lldb) image list -o -f [ 0] 0x0000000000074000 /private/var/mobile/Containers/Bundle/Application/D106C0E3-D874-4534-AED6-A7104131B31D/LuoJiFM-IOS.app/LuoJiFM-IOS(0x0000000100074000)[ 1] 0x000000000002c000 /Users/wordbeyond/Library/Developer/Xcode/iOS DeviceSupport/8.2 (12D508)/Symbols/usr/lib/dyld

在 Hopper 下找到需要斷點的地址:

下斷點:

(lldb) br s -a 0x0000000000074000+0x0000000100069700Breakpoint 2: where = LuoJiFM-IOS`_mh_execute_header + 407504, address = 0x00000001000dd700

然后在應用中點擊訂閱 Tab ,此時會命中斷點(如果沒有命中,手動下拉刷新下)。

眾所周知,Objective-C 方法的調用都會轉化成 objc_msgSend 調用,因此單步的時候看到 objc_msgSend 就可以停下來了:

-> 0x1000dd71c <+431900>: bl 0x100daa2bc  ; symbol stub for: objc_msgSend 0x1000dd720 <+431904>: mov x0, x20 0x1000dd724 <+431908>: bl 0x100daa2ec  ; symbol stub for: objc_release 0x1000dd728 <+431912>: mov x0, x21(lldb) po $x0<DataServiceV2: 0x17400cea0>(lldb) po (char *)$x1"FM_GetSubscribeList:pageSize:callBack:"(lldb) po $x4<__NSStackBlock__: 0x16fd88f88>

可以看到,第四個參數是個 StackBlock 對象,但是 lldb 只為我們打印出了它的地址。接下來,就靠我們自己來找出它的函數體地址和函數簽名了。

找出 Block 的函數體地址

要找出 Block 的函數體地址很簡單,根據上面的內存模型,我們只到找到 invoke 這個函數指針的地址,它指向的就是這個 Block 的實現。

在 64 位系統上,指針類型的大小是 8 個字節,而 int 是 4 個字節,如下:

因此,invoke 函數指針的地址就是在第 16 個字節之后。我們可以通過 lldb 的 memory 命令來打印出指定地址的內存,我們上面已經得到了 block 的地址,現在就打印出它的內存內容:

(lldb) memory read --size 8 --format x 0x16fd88f880x16fd88f88: 0x000000019b4d8088 0x00000000c20000000x16fd88f98: 0x00000001000dd770 0x0000000100fc66100x16fd88fa8: 0x000000017444c510 0x00000000000000010x16fd88fb8: 0x000000017444c510 0x0000000000000008

如前所述,函數指針的地址是在第 16 個字節之后,并占用 8 個字節,所以可以得到函數的地址是 0x00000001000dd770。

有了函數地址之后,就可以對這個地址進行反匯編:

(lldb) disassemble --start-address 0x00000001000dd770LuoJiFM-IOS`_mh_execute_header:-> 0x1000dd770 <+431984>: stp x28, x27, [sp, #-96]! 0x1000dd774 <+431988>: stp x26, x25, [sp, #16] 0x1000dd778 <+431992>: stp x24, x23, [sp, #32] 0x1000dd77c <+431996>: stp x22, x21, [sp, #48] 0x1000dd780 <+432000>: stp x20, x19, [sp, #64] 0x1000dd784 <+432004>: stp x29, x30, [sp, #80] 0x1000dd788 <+432008>: add x29, sp, #80  ; =80 0x1000dd78c <+432012>: mov x22, x3

也可以直接在 lldb 當中下斷點:

(lldb) br s -a 0x00000001000dd770Breakpoint 3: where = LuoJiFM-IOS`_mh_execute_header + 407616, address = 0x00000001000dd770

再次運行函數,就可以進到回調的 Block 函數體內了。

但是,大多數情況下,我們并不需要進到 Block 函數體內。在寫 tweak 的時候,我們更需要的是知道這個 Block 回調給了我們哪些參數。

接下來,我們繼續進行探索。

找出 Block 的函數簽名

要找出 Block 的函數簽名,需要通過 descriptor 結構體中的 signature 成員,然后通過它得到一個 NSMethodSignature 對象。

首先,需要找到 descriptor 結構體。這個結構體在 Block 中是通過指針持有的,它的位置正好在 invoke 成員后面,占用 8 個字節??梢詮纳厦娴膬却娲蛴≈锌吹?descriptor 指針的地址是 0x0000000100fc6610。

接下來,就可以通過 descriptor 的地址找到 signature 了。但是,文檔指出并不是每個 Block 都是有方法簽名的,我們需要通過 flags 與 block 中定義的枚舉掩碼進行與判斷。還是在剛剛的 llvm 文檔中,我們可以看到掩碼的定義如下:

enum { BLOCK_HAS_COPY_DISPOSE = (1 << 25), BLOCK_HAS_CTOR =  (1 << 26), // helpers have C++ code BLOCK_IS_GLOBAL =  (1 << 28), BLOCK_HAS_STRET =  (1 << 29), // IFF BLOCK_HAS_SIGNATURE BLOCK_HAS_SIGNATURE = (1 << 30),};

再次使用 memory 命令打印出 flags 的值:

(lldb) memory read --size 4 --format x 0x16fd8a9580x16fd8a958: 0x9b4d8088 0x00000001 0xc2000000 0x000000000x16fd8a968: 0x000dd770 0x00000001 0x00fc6610 0x00000001

由于 ((0xc2000000 & (1 << 30)) != 0),因此我們可以確定這個 Block 是有簽名的。

雖然在文檔中指出并不是每個 Block 都有函數簽名的。但是我們可以在 Clang 源碼 中的 CGBlocks.cpp 查看 CodeGenFunction::EmitBlockLiteral 與 buildGlobalBlock 方法,可以看到每個 Block 的 flags 成員都是被默認設置了 BLOCK_HAS_SIGNATURE。因此,我們可以推斷,所有使用 Clang 編譯的代碼中的 Block 都是有簽名的。
為了找出 signature 的地址,我們還需要確認這個 Block 是否擁有 copy_helper 和 disponse_helper 這兩個可選的函數指針。由于 ((0xc2000000 & (1 << 25)) != 0) ,因此我們可以確認這個 Block 擁有剛剛提到的兩個函數指針。

現在可以總結下:signature 的地址是在 descriptor 下偏移兩個 unsiged long 和兩個指針后的地址,即 32 個字節后?,F在讓我們找出它的地址,并打印出它的字符串內容:

(lldb) memory read --size 8 --format x 0x0000000100fc66100x100fc6610: 0x0000000000000000 0x00000000000000290x100fc6620: 0x00000001000ddb64 0x00000001000ddb700x100fc6630: 0x0000000100dfec18 0x00000000000000010x100fc6640: 0x0000000000000000 0x0000000000000048(lldb) p (char *)0x0000000100dfec18(char *) $4 = 0x0000000100dfec18 "v28@?0q8@"NSDictionary"16B24"

看到這一串亂碼是不是覺得有點崩潰,折騰了半天,怎么打印出這么一串鬼東西,雖然里面有一個熟悉的 NSDictionary,但是其它的東西完全看不懂啊。

不要慌,這確實就是一個函數簽名,只是我們需要通過 NSMethodSignature 找出它的參數類型:

(lldb) po [NSMethodSignature signatureWithObjCTypes:"v28@?0q8@/"NSDictionary/"16B24"]<NSMethodSignature: 0x174672940> number of arguments = 4 frame size = 224 is special struct return? NO return value: -------- -------- -------- -------- type encoding (v) 'v' flags {} modifiers {} frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0} memory {offset = 0, size = 0} argument 0: -------- -------- -------- -------- type encoding (@) '@?' flags {isObject, isBlock} modifiers {} frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8} argument 1: -------- -------- -------- -------- type encoding (q) 'q' flags {isSigned} modifiers {} frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8} argument 2: -------- -------- -------- -------- type encoding (@) '@"NSDictionary"' flags {isObject} modifiers {} frame {offset = 16, offset adjust = 0, size = 8, size adjust = 0} memory {offset = 0, size = 8}  class 'NSDictionary' argument 3: -------- -------- -------- -------- type encoding (B) 'B' flags {} modifiers {} frame {offset = 24, offset adjust = 0, size = 8, size adjust = -7} memory {offset = 0, size = 1}

注意,字符串中的雙引號需要對其進行轉義。

對我們最有用的 type encoding 字段,這些符號對應的解釋可以參考 Type Encoding 官方文檔。

所以,總結來講就是:這個方法沒有返回值,它接受四個參數,第一個是 block (即我們自己的 block 的引用),第二個是 (long long) 類型的,第三個是一個 NSDictionary 對象,第四個是一個 BOOL 值。

最終,我們得到了這個 Block 的函數參數。最初提到的那個方法簽名的完整版就是:

- (void)FM_GetSubscribeList:(long long)arg1 pageSize:(long long)arg2 callBack:(void (^)(long long, NSDictionary *, BOOL)arg3;

小結

因為想使用真實的例子進行演示,所以本文直接使用逆向的動態分析進行說明。其實上面提到的所有過程,都可以直接在 Xcode 通過自己寫的代碼進行操作。通過自己動手分析一遍,比看十篇文章來得更有效果。下次如果面試再有人問到 Block 的實現和內存模型,你就可以跟它侃侃而談了。

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲成年人在线播放| 欧美一区二区大胆人体摄影专业网站| 亚洲女人天堂成人av在线| 91综合免费在线| 日韩免费在线视频| 欧美高清视频在线观看| 欧美性色xo影院| 国产男女猛烈无遮挡91| 57pao成人国产永久免费| 伊人久久大香线蕉av一区二区| 一区二区三区在线播放欧美| 亚洲图片欧洲图片av| 国产精品精品国产| 一道本无吗dⅴd在线播放一区| 久久精品中文字幕免费mv| 欧美日韩xxxxx| 91久久精品美女| 91超碰caoporn97人人| 亚洲国产精品久久久久| 欧美性受xxxx黑人猛交| 国产精品久久久久秋霞鲁丝| 日韩综合视频在线观看| 日韩精品中文字幕视频在线| 亚洲资源在线看| 亚洲性av网站| 国产在线观看一区二区三区| 欧美精品一二区| 欧美一二三视频| 岛国视频午夜一区免费在线观看| 成人国产在线视频| 久久精品91久久久久久再现| 国内精品模特av私拍在线观看| 精品一区电影国产| 2018国产精品视频| 91视频九色网站| 永久免费精品影视网站| 久久精品视频播放| 久久久爽爽爽美女图片| 欧美日韩精品二区| 中文字幕精品一区久久久久| 日韩福利视频在线观看| 亚洲性日韩精品一区二区| 日韩电影第一页| 久久夜色精品国产亚洲aⅴ| 91天堂在线视频| 清纯唯美亚洲激情| 91成人在线播放| 91免费在线视频| 国产精品成人免费视频| 国产欧美在线看| yw.139尤物在线精品视频| 成人激情视频网| 国产精品久久久久久五月尺| 日韩av影视综合网| 欧美成人免费在线视频| 亚洲性av网站| 欧美亚洲午夜视频在线观看| 一区二区三区美女xx视频| 亚洲综合一区二区不卡| 亚洲综合精品一区二区| 亚洲人成网站色ww在线| 成人黄色免费网站在线观看| 日本精品视频在线观看| 欧美巨乳美女视频| 欧美黄色片免费观看| 亚洲免费av网址| 久久九九有精品国产23| 亚洲欧美精品伊人久久| 成人性生交大片免费看视频直播| 久久免费视频观看| 性金发美女69hd大尺寸| 精品一区二区三区四区在线| 国产亚洲激情在线| 国产玖玖精品视频| 这里只有精品视频在线| 亚洲日本欧美日韩高观看| 九色精品免费永久在线| 日韩av在线一区| 国产成人aa精品一区在线播放| 日韩人在线观看| 日韩成人在线视频观看| 最近2019中文字幕一页二页| 欧美激情精品久久久久久蜜臀| 久久综合色88| 91热精品视频| 欧美日韩午夜视频在线观看| 日韩精品久久久久久久玫瑰园| 92看片淫黄大片看国产片| 国产福利精品视频| 精品偷拍各种wc美女嘘嘘| 精品二区三区线观看| 欧美精品18videos性欧| 永久免费精品影视网站| 日本午夜精品理论片a级appf发布| 亚洲精品久久久久久久久久久久久| 日韩大片在线观看视频| 亚洲18私人小影院| 亚洲人成在线观| 国产亚洲精品成人av久久ww| 欧美最猛黑人xxxx黑人猛叫黄| 一区二区三区亚洲| 川上优av一区二区线观看| 一本色道久久综合亚洲精品小说| 欧美日韩国产999| 成人羞羞国产免费| 亚洲一区制服诱惑| 久久久久五月天| 91免费的视频在线播放| 欧美在线一级va免费观看| 在线看欧美日韩| 中文字幕v亚洲ⅴv天堂| 久久久久亚洲精品成人网小说| 亚洲欧美日韩爽爽影院| 亚洲精品在线视频| 亚洲国产高潮在线观看| 国产精品91视频| 国产成人在线一区二区| 亚洲国产精品国自产拍av秋霞| 欧美性色xo影院| 81精品国产乱码久久久久久| 国产日韩欧美自拍| 国产精品美乳一区二区免费| 精品国产欧美成人夜夜嗨| 国产精品ⅴa在线观看h| 日韩精品有码在线观看| 亚洲人成伊人成综合网久久久| 欧美影院在线播放| 91视频免费在线| 日韩欧美精品中文字幕| 夜夜狂射影院欧美极品| 中文字幕精品www乱入免费视频| 久久久久中文字幕2018| 日韩免费黄色av| 日韩电影第一页| 亚洲人永久免费| 精品久久久久久久久久久久久久| 中文日韩在线观看| 亚洲人成五月天| 亚洲女人天堂成人av在线| 欧美日韩亚洲视频| 91午夜理伦私人影院| 亚洲国产天堂久久综合网| 久久全国免费视频| 亚洲人成伊人成综合网久久久| 国产精品午夜国产小视频| 欧美激情2020午夜免费观看| 日韩成人在线视频| 狠狠做深爱婷婷久久综合一区| 国产91在线播放九色快色| 青青青国产精品一区二区| 欧美日韩国产二区| 91福利视频在线观看| 日韩精品视频三区| 久久中国妇女中文字幕| 久久精品一本久久99精品| 尤物yw午夜国产精品视频明星| 国产精品高潮呻吟久久av无限| 国产一区二区av| 色老头一区二区三区在线观看| 亚洲成年人在线播放| 激情成人中文字幕| 77777少妇光屁股久久一区| 亚洲老头同性xxxxx| 日韩在线观看av|