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

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

本文主要講解編譯器的優(yōu)化以及指針的匯編

編譯器優(yōu)化

設(shè)置

可在項(xiàng)目的BuildSetting->Optimization Level中找到咸产,一般的優(yōu)化方案選擇FS(Fastest,Smallest)

優(yōu)化設(shè)置

案例分析

有以下代碼

int main(int argc, char * argv[]) {
    
    int a = 1;
    int b = 2;
}
  • 在沒有優(yōu)化情況下的匯編如下


    未優(yōu)化匯編
  • 將優(yōu)化方案從None改成FS仿村,匯編如下
    已優(yōu)化匯編

修改1:main中調(diào)用

int sum(int a, int b){
    return a + b;
}


int main(int argc, char * argv[]) {
    
    sum(1, 2);
}

查看此時(shí)是否有1锐朴,2,發(fā)現(xiàn)也沒有sum有關(guān)的匯編代碼蔼囊,其優(yōu)化原則是這樣的:去掉sum對(duì)程序運(yùn)行的結(jié)果沒有影響【原則1:對(duì)結(jié)果沒有任何影響的代碼會(huì)被編譯器優(yōu)化】

修改1-匯編查看

修改2:打印sum的結(jié)果

int sum(int a, int b){
    return a + b;
}


int main(int argc, char * argv[]) {
    int a = sum(1, 2);
    printf(@"%d", a);
}

此時(shí)如果優(yōu)化掉sum焚志,對(duì)執(zhí)行結(jié)果是有影響∥饭模可以發(fā)現(xiàn)sum函數(shù)被優(yōu)化掉了酱酬,優(yōu)化成了一個(gè)結(jié)果

修改2-匯編查看

指針

在OC中,無論參數(shù)還是返回值云矫,如果傳入/返回的是對(duì)象膳沽,這些對(duì)象都是指針,下面來看看指針的反匯編

void func(){
    int *a;
    printf("%lu", sizeof(a)); //sizeof不是函數(shù)让禀,是一個(gè)符號(hào)
}

int main(int argc, char * argv[]) {
    func();
}

分析以上代碼的匯編代碼


指針分析

指針常識(shí)

指針自增自減運(yùn)算

void func(){
    int *a;
    a = (int *)100;
    a++;
    printf("%d", a); 
}
<!--打印結(jié)果-->
104
  • 問題:a++之后挑社,a是多少?

    • 是104巡揍,因?yàn)?code>指針的自增自減和指向的數(shù)據(jù)類型寬度有關(guān)痛阻,即因?yàn)閍指向的數(shù)據(jù)是int,int的寬度是4個(gè)字節(jié)
  • 修改1腮敌、如果將int改成char阱当,結(jié)果是多少呢俏扩?

    • 因?yàn)閍指向的數(shù)據(jù)是char,char的寬度是1個(gè)字節(jié)
void func(){
    char *a;
    a = (char *)100;
    a++;
    printf("%d", a); 
}

<!--打印結(jié)果-->
101
  • 修改2:如果int改成int呢弊添,結(jié)果是多少录淡?*
    • 因?yàn)閍指向的數(shù)據(jù)是int*,是一個(gè)指針油坝,指針的寬度是8個(gè)字節(jié)
void func(){
    int **a;
    a = (int **)100;
    a++;//指針的自增自減和執(zhí)行的數(shù)據(jù)類型寬度有關(guān)
    printf("%d", a); 
}

<!--打印結(jié)果-->
108
  • 修改3:如果將 a++ 改成 a = a + 1 呢嫉戚?
    • 自增自減和編譯器有關(guān)!
    • 注:這里的+1免钻,加的是一個(gè)步長(zhǎng)彼水,而a的步長(zhǎng)是(int*)即8個(gè)字節(jié)
void func(){
    int **a;
    a = (int **)100;
//    a++;//指針的自增自減和執(zhí)行的數(shù)據(jù)類型寬度有關(guān)
    a = a + 1;
    printf("%d", a); 
}

<!--打印結(jié)果-->
108

指針和指針求差值

有以下代碼,兩個(gè)指針相減的結(jié)果是多少极舔?

void func(){
    int *a;
    a = (int *)100;
    int *b;
    b = (int *)200;
    int x = a - b;
    printf("%d", x); 
}

<!--打印結(jié)果-->
-25
  • 因?yàn)?(100-200)/int的寬度 = -100/4 = -25

  • 注:指針的運(yùn)算單位是執(zhí)行的數(shù)據(jù)類型的寬度

  • 問題:指針是否可以 if-else 比大小?

    • 可以的,我們知道類型是可以相互轉(zhuǎn)換的链瓦,但是結(jié)構(gòu)體和基本類型是不能相互轉(zhuǎn)換的
    • 任何類型都可以使用&(取地址符號(hào))取值
void func(){
    int *a;
    a = (int *)100;
    int *b;
    b = (int *)200;
    if (a > b) {
        printf("a > b");
    }else{
        printf("a <= b");
    }
}

<!--打印結(jié)果-->
a <= b

指針的反匯編

定義一個(gè)指針拆魏,并賦值,查看其匯編代碼

void func(){
    int *a;
    int b = 10;
    a = &b;
}

以下是代碼運(yùn)行的匯編以及分析


指針的反匯編分析

指針數(shù)組

問題1:通過下面這種方式能否將數(shù)組中的數(shù)組正確取出慈俯?

void func(){
    int arr[5] = {1, 2, 3, 4, 5};
    for (int i = 0; i < 5 ; i++) {
//        printf("%d", arr[i]);
        printf("%d",*(arr + i));//這里使用arr++不可以渤刃,因?yàn)榫幾g器不允許
    }
}

運(yùn)行發(fā)現(xiàn)是可以的,所以有以下對(duì)等關(guān)系:

  • 對(duì)等關(guān)系:int *a == arr[0] == arr

問題2:如果將arr賦值給指針a贴膘,for循環(huán)中 a++是否可以卖子?

void func(){
    int arr[5] = {1, 2, 3, 4, 5};
    int *a = arr;
    for (int i = 0; i < 5 ; i++) {
//        printf("%d", arr[i]);
        printf("%d",*(a++));
    }
}

運(yùn)行發(fā)現(xiàn)也是可以的

指針的基本用法

  • 定義一個(gè)指針,并從指針中取值
void func(){
    char *p1;
    char c = *p1;//取p1的值
}

運(yùn)行發(fā)現(xiàn)刑峡,崩潰報(bào)錯(cuò):原因是以0作為地址取值洋闽,以下是對(duì)應(yīng)的匯編分析


指針的基本用法-01

*如果修改成 (p1+0) 的形式呢?

void func(){
    char *p1;
    char c = *p1;//取p1的值
    char d = *(p1+0);
}

查看匯編代碼:

  • p1指針指向 sp+0x8棧區(qū)域里的值突梦,相當(dāng)于 p1 -> (X8)0x0
  • c 相當(dāng)于取X8的值诫舅,即 [X8]
  • d 同樣的是取X8的值,即 [X8]
    指針的基本用法-02

*如果改成 (p1+1) 的形式呢宫患?

void func(){
    char *p1;
    char c = *p1;//取p1的值
    char d = *(p1+1);
}

查看匯編:

  • p1指針指向 sp+0x8棧區(qū)域里的值刊懈,相當(dāng)于 p1 -> (X8)0x0
  • c 相當(dāng)于取X8的值,即 [X8]
  • d 同樣的是取X8的值娃闲,即 [X8+0x1]
    指針的基本用法-03

如果將 char類型 改成 int類型 呢虚汛?

void func(){
    int *p1;
    int c = *p1;//取p1的值
    int d = *(p1+1);
}

以下是func函數(shù)的匯編,此時(shí)d是取[x8+0x4]地址的值皇帮,因?yàn)閕nt是4個(gè)字節(jié)卷哩,0x4就是int類型的步長(zhǎng)

指針的基本用法-04

如果將 int 類型改成 int 類型呢?*

void func(){
    int **p1;
    int *c = *p1;//取p1的值
    int *d = *(p1+1);
}
  • 查看匯編玲献,此時(shí)d是取[x8+0x8]地址的值殉疼,因?yàn)閕nt*是一個(gè)指針梯浪,占8個(gè)字節(jié),0x8就是int*類型的步長(zhǎng)

    指針的基本用法-05

  • int** 需要拉伸多少個(gè)字節(jié)瓢娜?
    實(shí)際需要3x8=24字節(jié)挂洛,由于是匯編是16字節(jié)對(duì)齊,所以需要sub減0x20

  • 此時(shí)多增加一個(gè)int* p2 眠砾,椔簿ⅲ空間拉伸多少字節(jié)?
    發(fā)現(xiàn)仍然是0x20

    指針的基本用法-06

  • 再多增加一個(gè)char c1呢褒颈?
    此時(shí)超過了32柒巫,所以需要再多拉伸16字節(jié)

    指針的基本用法-07

多級(jí)指針

1、二級(jí)指針

有以下代碼

void func(){
    int **p1;
    int c = **p1;
}

運(yùn)行崩潰谷丸,查看其匯編

  • x8 取的是 *p1(即一級(jí)指針的地址)的值

  • w9 取的是 **p1(即二級(jí)指針的地址) 的值

    多級(jí)指針-01

2堡掏、多級(jí)指針加法運(yùn)算

void func(){
    char **p1;
//    char c = p1+2; //此時(shí)的+2是 +0x10(執(zhí)行數(shù)據(jù)類型是char*)
    char c = *(*(p1 + 2) + 2);//最外層的+2,是加0x2(執(zhí)行數(shù)據(jù)類型是char)
}
  • 此時(shí)c中的 +2+0x10(執(zhí)行數(shù)據(jù)類型是char*

  • 此時(shí)d中最外層的+2刨疼,是加0x2(執(zhí)行數(shù)據(jù)類型是char

如果是下面這種形式呢泉唁?

void func(){
    char **p1;
//    char c = p1+2; //此時(shí)的+2是 +0x10(執(zhí)行數(shù)據(jù)類型是char*)
//    char c = *(*(p1 + 2) + 2);//最外層的+2,是加0x2(執(zhí)行數(shù)據(jù)類型是char)
    char c2 = p1[1][2]; //與上面等價(jià)
}
  • p1[1] 此時(shí)的1表示0x8(類型是char*)

  • p1[1][2] 此時(shí)的2表示 0x2(類型是char)

    多級(jí)指針-02

    從匯編結(jié)果來看p1[1][2]*(*(p1 + 2) + 2)是等價(jià)的

總結(jié)

  • 編譯器優(yōu)化:

    • 1揩慕、設(shè)置:BuildSetting->Optimization Level

    • 2亭畜、優(yōu)化原則:對(duì)結(jié)果沒有任何影響的代碼會(huì)被編譯器優(yōu)化

    • 3、編譯器優(yōu)化迎卤,本質(zhì)是LLVM的優(yōu)化過程拴鸵,實(shí)際上優(yōu)化的是匯編代碼(可以理解為匯編指令會(huì)減少

  • 指針:

    • 1、指針的自增自減和指向的數(shù)據(jù)類型寬度有關(guān)蜗搔,是按照指向的數(shù)據(jù)類型來運(yùn)算的(即指針的寬度 - 步長(zhǎng)

    • 2劲藐、指針的運(yùn)算單位是指向的數(shù)據(jù)類型的寬度

    • 3、指針可以通過if-else比大小碍扔,因?yàn)轭愋褪强梢韵嗷マD(zhuǎn)換的

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘩燥,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子不同,更是在濱河造成了極大的恐慌厉膀,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件二拐,死亡現(xiàn)場(chǎng)離奇詭異服鹅,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)百新,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門企软,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人饭望,你說我怎么就攤上這事仗哨⌒瓮ィ” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵厌漂,是天一觀的道長(zhǎng)萨醒。 經(jīng)常有香客問我,道長(zhǎng)苇倡,這世上最難降的妖魔是什么富纸? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮旨椒,結(jié)果婚禮上晓褪,老公的妹妹穿的比我還像新娘。我一直安慰自己综慎,他們只是感情好涣仿,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寥粹,像睡著了一般变过。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涝涤,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音岛杀,去河邊找鬼阔拳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛类嗤,可吹牛的內(nèi)容都是我干的糊肠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼遗锣,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼货裹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起精偿,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤弧圆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后笔咽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體搔预,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年叶组,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拯田。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡甩十,死狀恐怖船庇,靈堂內(nèi)的尸體忽然破棺而出吭产,到底是詐尸還是另有隱情,我是刑警寧澤鸭轮,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布臣淤,位于F島的核電站,受9級(jí)特大地震影響张弛,放射性物質(zhì)發(fā)生泄漏荒典。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一吞鸭、第九天 我趴在偏房一處隱蔽的房頂上張望寺董。 院中可真熱鬧,春花似錦刻剥、人聲如沸遮咖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽御吞。三九已至,卻和暖如春漓藕,著一層夾襖步出監(jiān)牢的瞬間陶珠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工享钞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留揍诽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓栗竖,卻偏偏與公主長(zhǎng)得像暑脆,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子狐肢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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