iOS逆向 04:OC反匯編(下)

iOS 底層原理 + 逆向 文章匯總

本文主要理解OC對(duì)象反匯編浩姥,以及block常見類型的反匯編

OC反匯編

創(chuàng)建一個(gè)Person類揭蜒,并在main函數(shù)中初始化一個(gè)Person對(duì)象

@interface Person : NSObject

@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) int age;

+ (instancetype)person;

@end

@implementation Person

+ (instancetype)person{
    return [[self alloc] init];
}

@end

<!--main.m中-->
int main(int argc, char * argv[]) {
    
    Person *p = [Person person];

    return 0;
}
  • 運(yùn)行洪囤,查看其匯編代碼


    OC反匯編-匯編代碼

1毙石、靜態(tài)調(diào)試

通過adrp+add獲取地址救赐,分別讀取x0谴古,x1

  • 讀取x0撞羽,讀出來是Person:x 0x100c68eb0 + po 0x0100c68f98
    OC靜態(tài)調(diào)試-01
  • 讀取x1掏缎,讀取出來是person方法:x 0x100c68e88 + p (SEL)0x01c019aef5
    OC靜態(tài)調(diào)試-02

2皱蹦、動(dòng)態(tài)調(diào)試
通過一步一步執(zhí)行匯編,來驗(yàn)證x0御毅、x1是否如靜態(tài)調(diào)試的結(jié)果一致根欧?

OC動(dòng)態(tài)調(diào)試-01

通過調(diào)試發(fā)現(xiàn),是一致的端蛆,其實(shí)這里的x0凤粗、x1就是 objc_msgSend的隱藏參數(shù)(id self,SEL _cmd)

下面我們繼續(xù)調(diào)試匯編

  • 點(diǎn)擊step into今豆,直接進(jìn)入[Person person]方法(注意:這里不同iOS版本嫌拣,多看到的匯編代碼是有所區(qū)別的)

    • 從這里看到ios13.4系統(tǒng)的alloc、init并不會(huì)走objc_msgSend


      OC動(dòng)態(tài)調(diào)試-02
    • ios11版本中呆躲,可以看到objc_msgSend异逐,其本質(zhì)是在調(diào)用init方法
      OC動(dòng)態(tài)調(diào)試-03

      動(dòng)態(tài)調(diào)試進(jìn)行驗(yàn)證,結(jié)果如下所示插掂,是一致的
      OC動(dòng)態(tài)調(diào)試-04

      查看此時(shí)的x0灰瞻,已經(jīng)是一個(gè)實(shí)例對(duì)象腥例,因?yàn)閍lloc開辟了內(nèi)存,已經(jīng)分配了空間酝润,具體的內(nèi)部實(shí)現(xiàn)可以查看這篇文章iOS-底層原理 02:alloc & init & new 源碼分析
      OC動(dòng)態(tài)調(diào)試-05

      疑問:為什么版本不同燎竖,調(diào)用不一樣呢?
    • 在不同的版本下要销,系統(tǒng)在運(yùn)行時(shí)是不一樣的构回。因?yàn)橄到y(tǒng)對(duì)alloc 、init進(jìn)行了優(yōu)化
  • 接著往下看疏咐,點(diǎn)擊step out 跳出[Person person],此時(shí)返回值在x0中


    OC動(dòng)態(tài)調(diào)試-06
  • 執(zhí)行到bl ... objc_storeStrong,objc_storeStrong是OC中用strong修飾的對(duì)象底層都是調(diào)用這個(gè)函數(shù)纤掸,詳情可以看這篇文章iOS-底層原理 10:strong&copy&weak底層分析 以及 方法簽名和attribute簡(jiǎn)寫含義
    疑問:我們此時(shí)并沒有使用strong修飾浑塞?:此時(shí)的局部變量p在此時(shí)就相當(dāng)于一個(gè)強(qiáng)引用借跪,是默認(rèn)的。且這個(gè)方法執(zhí)行完成后缩举,相當(dāng)于銷毀p

    OC動(dòng)態(tài)調(diào)試-07

    查看此時(shí)的x0垦梆、x1,相當(dāng)于objc_storeStrong(&p仅孩,nil)托猩,將nil進(jìn)行retain,將nil等于p(即 p=nil)辽慕,p進(jìn)行釋放
    OC動(dòng)態(tài)調(diào)試-08

  • 查看objc_storeStrong源碼

  • 目的:對(duì)一個(gè)strong修飾的對(duì)象進(jìn)行retain +1京腥,對(duì)一個(gè)老的對(duì)象進(jìn)行release

    • 為什么是指針? 因?yàn)楹瘮?shù)是值傳遞溅蛉,而函數(shù)內(nèi)部需要修改p的值
/*
 - id *location 指向?qū)ο蟮闹羔? 本質(zhì)上是 &p(即局部變量地址)
 - id obj 對(duì)象
 目的:對(duì)一個(gè)strong修飾的對(duì)象進(jìn)行retain +1公浪,對(duì)一個(gè)老的對(duì)象進(jìn)行release
 為什么是指針? 因?yàn)楹瘮?shù)是值傳遞船侧,而函數(shù)內(nèi)部需要修改p的值
 */
void
objc_storeStrong(id *location, id obj)
{
    //prev 相當(dāng)于p 欠气,因?yàn)閘ocation是 &p
    id prev = *location;
    //第二個(gè)參數(shù) == 第一個(gè)參數(shù),直接return
    if (obj == prev) {
        return;
    }
    //retain+1
    objc_retain(obj);
    //修改p的值镜撩,指向第二個(gè)對(duì)象
    *location = obj;
    //釋放老對(duì)象
    objc_release(prev);
}

相當(dāng)于

Person *p = p1;
p = p2;//此時(shí)p1釋放预柒,p2retain+1

所以以上匯編中的objc_storeStrong(&p,nil)的實(shí)現(xiàn)代碼如下

objc_storeStrong(&p袁梗,nil){
    id prev = p;
    if nil == p{
        return;
    }
    objc_retain(nil);
    p = nil;//指針指向nil
    objc_release(p);//釋放堆空間
}
  • 下面來進(jìn)行動(dòng)態(tài)驗(yàn)證宜鸯,發(fā)現(xiàn)Person對(duì)象指向nil
    OC動(dòng)態(tài)調(diào)試-09

[[self alloc] init] 優(yōu)化過程

  • 在最初的版本(iOS9)中,相當(dāng)于兩次消息發(fā)送 objc_msgSend
  • iOS11版本 是一次消息發(fā)送 objc_alloc + objc_msgSend
  • iOS13.5.1以上版本遮怜,已經(jīng)沒有objc_msgSend淋袖,而是objc_alloc_init
    iOS13版本

以上是LLDB動(dòng)態(tài)調(diào)試Person *p = [Person person]; //objc_msgSend x0,x1

通過工具看復(fù)雜的OC代碼

在上述OC代碼的基礎(chǔ)上增加一些代碼锯梁,然后再來靜態(tài)分析

int main(int argc, char * argv[]) {
    
    Person *p = [Person person]; //objc_msgSend x0即碗,x1
    p.name = @"CJL";
    p.age = 18;

    return 0;
}
  • CMD + B 編譯程序焰情,生成mach-o文件,并找到該文件

  • 通過Hopper反匯編mach-o文件剥懒,main函數(shù)的分析如下


    hopper-01
  • 雙擊objc_cls_ref_Person烙样,查看p的地址,是000000010000ce88蕊肥,是在Data段

    hopper-02

    通過MachOView打開mach-o分析,查找000000010000ce88蛤肌,與Hopper中的顯示是一致的
    hopper-03

  • 雙擊@selector(person)壁却,查看person方法的反匯編

    hopper-04

    雙擊0x10000cc68
    hopper-05

    雙擊“person”,地址為 0x10000752a
    hopper-06

    在mach中查找0x10000752a裸准,所有方法的name都在CString
    hopper-07

Block反匯編

定義一個(gè)block

int main(int argc, char * argv[]) {
    
    void(^block)(void) = ^(){
        NSLog(@"block");
    };
    block();

    return 0;
}

反匯編分析block的目的是想快速定位block的invoke展东,因?yàn)閕nvoke中是實(shí)現(xiàn)代碼,以下是block的匯編代碼

Block反匯編-01

  • 查看x0是什么炒俱?:是一個(gè)__block_literal_global盐肃,是一個(gè)全局靜態(tài)block(即block不引用block外部變量,在編譯時(shí)期就可以確定內(nèi)存的分配等操作权悟,存在于可執(zhí)行文件的常量區(qū))砸王,其他詳情也可查看iOS-底層原理 30:Block底層原理這篇文章
    Block反匯編-02

    以下是源碼中block的定義,是一個(gè)結(jié)構(gòu)體
struct Block_layout{
    void *isa;
    volatile int32_t flags; //contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    //imported variables
};

然后動(dòng)態(tài)調(diào)試查看block的內(nèi)存結(jié)構(gòu)


Block反匯編-03
  • 是否可以通過hopper查看 adrp + add 是一個(gè)block峦阁?
    答案是可以的


    Block反匯編-04
    • 雙擊___block_literal_global
      Block反匯編-05
    • 雙擊0x0000000100006838谦铃,查看invoke
      Block反匯編-06
    • 雙擊0x0000000100008008,查看descriptor榔昔,和Block的源碼結(jié)構(gòu)類似
      Block反匯編-07

如果block引用了外部變量呢驹闰?

定義一個(gè)block,其中block引用了外部變量撒会,查看此時(shí)的匯編代碼

int main(int argc, char * argv[]) {
    int a = 10;
    void(^block)(void) = ^(){
        NSLog(@"block -- %d", a);
    };
    block();

    return 0;
}

1嘹朗、lldb調(diào)試

  • 以下是代碼的匯編


    引用外部變量-01
  • 驗(yàn)證是否是block的isa指針


    引用外部變量-02
    • adrp x10, 2 獲取指針地址
    • ldr x10, [x10]:取值
  • 查看此時(shí)block的內(nèi)存,找到invoke(由于invoke是代碼實(shí)現(xiàn)诵肛,所以需要由dis -s(將代碼的匯編打印出來)查看)

    引用外部變量-03

2屹培、靜態(tài)分析

  • 通過hopper靜態(tài)分析如下,以下是main函數(shù)的反匯編


    靜態(tài)分析-01
  • 雙擊___main_block_invoke曾掂,跳轉(zhuǎn)至invoke的具體實(shí)現(xiàn)(并沒有在main函數(shù)中惫谤,是單獨(dú)的實(shí)現(xiàn))

    靜態(tài)分析-02

  • 雙擊___block_descriptor_36_e5_v8??0l,是一個(gè)單獨(dú)的描述

    靜態(tài)分析-03

總結(jié)

  • [[self alloc] init] 優(yōu)化過程

    • 在最初的版本(iOS9)中珠洗,相當(dāng)于兩次消息發(fā)送 objc_msgSend

    • iOS11版本 是一次消息發(fā)送 objc_alloc + objc_msgSend

    • iOS13.5.1以上版本溜歪,已經(jīng)沒有objc_msgSend,而是objc_alloc_init

  • 反匯編分析方式:

    • 通過LLDB動(dòng)態(tài)調(diào)試

    • 通過Hopper + MachOView 靜態(tài)分析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末许蓖,一起剝皮案震驚了整個(gè)濱河市蝴猪,隨后出現(xiàn)的幾起案子调衰,更是在濱河造成了極大的恐慌,老刑警劉巖自阱,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嚎莉,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沛豌,警方通過查閱死者的電腦和手機(jī)趋箩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來加派,“玉大人叫确,你說我怎么就攤上這事∩纸酰” “怎么了竹勉?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)娄琉。 經(jīng)常有香客問我次乓,道長(zhǎng),這世上最難降的妖魔是什么孽水? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任票腰,我火速辦了婚禮,結(jié)果婚禮上匈棘,老公的妹妹穿的比我還像新娘丧慈。我一直安慰自己,他們只是感情好主卫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布逃默。 她就那樣靜靜地躺著,像睡著了一般簇搅。 火紅的嫁衣襯著肌膚如雪完域。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天瘩将,我揣著相機(jī)與錄音吟税,去河邊找鬼。 笑死姿现,一個(gè)胖子當(dāng)著我的面吹牛肠仪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播备典,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼异旧,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了提佣?” 一聲冷哼從身側(cè)響起吮蛹,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤荤崇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后潮针,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體术荤,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年每篷,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瓣戚。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡焦读,死狀恐怖带兜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吨灭,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布刑巧,位于F島的核電站喧兄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏啊楚。R本人自食惡果不足惜吠冤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恭理。 院中可真熱鬧拯辙,春花似錦、人聲如沸颜价。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽周伦。三九已至夕春,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間专挪,已是汗流浹背及志。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寨腔,地道東北人速侈。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像迫卢,于是被迫代替她去往敵國(guó)和親倚搬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • OC的反匯編 案例1:OC的方法調(diào)用打開Person.h靖避,寫入以下代碼:#import <Foundation/F...
    帥駝駝閱讀 697評(píng)論 0 3
  • OC的反匯編 筆記暫時(shí)提交, 日后修正 1. OC方法本質(zhì) objc_msgSend(id , sel), 匯編打...
    為了自由的白菜閱讀 397評(píng)論 0 1
  • 前言 本篇文章是匯編這一部分的最終章了潭枣,主要講解4部分內(nèi)容: 編譯器優(yōu)化 指針 OC反匯編 Block反匯編 一比默、...
    深圳_你要的昵稱閱讀 281評(píng)論 0 1
  • 一. OC的反匯編 創(chuàng)建空工程 001--OC方法的本質(zhì),在工程中創(chuàng)建Person類 通過靜態(tài)調(diào)試來進(jìn)行計(jì)算 通過...
    淺墨入畫閱讀 172評(píng)論 0 6
  • 編譯器優(yōu)化 局部變量&全局變量 在不進(jìn)行優(yōu)化的情況下: 改成Fastest、Smallest模式谐岁,a和b都被優(yōu)化掉...
    HotPotCat閱讀 555評(píng)論 1 3