iOS 匯編基礎(chǔ)(三)還原高級代碼之循環(huán)和判斷

一 if 和 if else

1. if
int g = 30;
void func(int a,int b){
   if (a >b) {
     g = a;
   }
 }

int main(int argc, char * argv[]) {
    func(10, 20);
   return 0;
}

上面func的匯編代碼如下:

067A0 _func                                   ; CODE XREF: _main+28↓p

 var_8           = -8
 var_4           = -4
 SUB             SP, SP, #0x10 //拉伸椞趾校空間
STR             W0, [SP,#0x10+var_4] //把w0寫入內(nèi)存 int var_4=w0
STR             W1, [SP,#0x10+var_8] //把w1寫入內(nèi)存 int var_8 =w1
LDR             W0, [SP,#0x10+var_4] //把w0從內(nèi)存var_4中讀出來 int w0= var_4
LDR             W1, [SP,#0x10+var_8] //把w1從內(nèi)存var_8中讀出來 int w1= var_8
 CMP             W0, W1  //比較w0和w1
B.LE            loc_1000067CC  //如果w0<=w1那么跳轉(zhuǎn)到loc_1000067CC處執(zhí)行
ADRP            X8, #_g@PAGE 
ADD             X8, X8, #_g@PAGEOFF  //以上兩句獲取全局變量g的儲存地址
LDR             W9, [SP,#0x10+var_4]  //int w9=var_4
STR             W9, [X8]  // 把w9讀到x8的內(nèi)存地址中去拇舀,即 *x8=w9

 loc_1000067CC                          
                ADD             SP, SP, #0x10  //恢復棧 棧平衡
                RET    //返回的時候沒有任何操作以及沒有對x0進行操作,所以沒有返回值

main函數(shù)的匯編如下:

 _main
__text:00000001000068F8
__text:00000001000068F8 var_10          = -0x10
__text:00000001000068F8 var_8           = -8
__text:00000001000068F8 var_4           = -4
__text:00000001000068F8 var_s0          =  0
__text:00000001000068F8
__text:00000001000068F8                 SUB             SP, SP, #0x20
__text:00000001000068FC                 STP             X29, X30, [SP,#0x10+var_s0]
__text:0000000100006900                 ADD             X29, SP, #0x10
__text:0000000100006904                 MOV             W8, #0xA
__text:0000000100006908                 MOV             W9, #0x14
__text:000000010000690C                 STUR            WZR, [X29,#var_4]
__text:0000000100006910                 STR             W0, [SP,#0x10+var_8]
__text:0000000100006914                 STR             X1, [SP,#0x10+var_10]
__text:0000000100006918                 MOV             X0, X8
__text:000000010000691C                 MOV             X1, X9
__text:0000000100006920                 BL              _func
__text:0000000100006924                 MOV             W8, #0
__text:0000000100006928                 MOV             X0, X8
__text:000000010000692C                 LDP             X29, X30, [SP,#0x10+var_s0]
__text:0000000100006930                 ADD             SP, SP, #0x20
__text:0000000100006934                 RET
__text:0000000100006934 ; End of function _main
  • 因為在main中調(diào)用func之前MOV X0, X8 和 MOV X1, X9 x0-x7保存參數(shù)芹枷,w是x的低32位,所以func有兩個參數(shù)阴幌。
  • 在main中再沧,因為MOV W8, #0xAMOV W9, #0x14所以func的兩個參數(shù)分別是#0xA和#0x14換算成10進制為10和20提鸟,以上得到void func(int a, int b)main中調(diào)用func(10,20)
  • 在func的匯編中SP, SP, #0x10這是拉伸棧空間宠进,拉伸#0x10即16個字節(jié)。拉伸多少椕牯幔空間和局部變量參數(shù)以及是否保護x29,x30有關(guān)材蹬。
  • 根據(jù)func中的注釋的分析,得到:
void func(int a, int b)
{
  int var_4=w0;
  int var_8 =w1;
  int w0= var_4;
  int w1= var_8;
  if (w0 <= w1}
  {
      return;
  }
  g = *x8;
  int w9=var_4;
  *x8 = w9;
}  由此獲得 void func(int a,intb){ if (a>b){g = a} }
2. if else
int  func(){
    int a=10;
    int b=20;
    if (a >b) {
        g = a;
    }
    else{
         g=b;
    }
    return g;
}

匯編如下:

 var_8           = -8
var_4           = -4
SUB             SP, SP, #0x10   //拉伸16字節(jié)椓吡停空間
MOV             W8, #0x14   //int w8 = 0x14即int w8=20;
MOV             W9, #0xA   //int w9 = 10;
STR             W9, [SP,#0x10+var_4]    
STR             W8, [SP,#0x10+var_8]
LDR             W8, [SP,#0x10+var_4]
LDR             W9, [SP,#0x10+var_8]   //以上為w8,w9進行內(nèi)存保護
CMP             W8, W9  // if(w8 <= w9)執(zhí)行l(wèi)oc_1000068E0處代碼
B.LE            loc_1000068E0
ADRP            X8, #_g@PAGE
ADD             X8, X8, #_g@PAGEOFF  //獲取全局變量g
LDR             W9, [SP,#0x10+var_4]
STR             W9, [X8]    //x8地址的內(nèi)容為var_4的內(nèi)容
B               loc_1000068F0  //跳轉(zhuǎn)到loc_1000068F0處執(zhí)行
---------------------------------------------------------------------------
 loc_1000068E0                         
ADRP            X8, #_g@PAGE
ADD             X8, X8, #_g@PAGEOFF //獲取全局變量g儲存到x8的地址中
LDR             W9, [SP,#0x10+var_8]
 STR             W9, [X8]  //x8的內(nèi)容為var_8的內(nèi)容

 loc_1000068F0                          
ADRP            X8, #_g@PAGE
ADD             X8, X8, #_g@PAGEOFF //獲取全局變量g儲存到x8的地址中
LDR             W0, [X8] //x8的內(nèi)容為x0的內(nèi)容
ADD             SP, SP, #0x10 //棧平衡
RET

  • 在func中堤器,沒有對x0-x7進行保護,main中x0-x7也沒有入棧末贾,那么func沒有參數(shù)
  • func中闸溃,RET之前對w0進行了操作,所以有返回值拱撵。函數(shù)返回值一般為x0(w0是x0的低32位.返回值為x8,x8=g,那么返回值為g
  • 還可以看出g進行了賦值操作辉川,賦值給var_4和var_8,那么g的類型為var_4和var_8的類型拴测。
  • 還可得出var_4和var_8就是局部變量 W8和W9员串,w8=20;w9 = 10;
    以上還原成高級代碼為:
int func()
{
  int w8 = 20;
  int w9=10;
  if(w8 > w9)
  {
    g=w8;
   }
  else{
    g = w9;
  }
  *x8 = g;
  int w0=*x8;
  return w0;
}
總結(jié):CMP 和B.LE(小于等于)再加上B組成if else,只有cmp和B組成if等判斷語句

二 常見的cmp(Compare)比較指令

CMP 把一個寄存器的內(nèi)容和另一個寄存器的內(nèi)容或立即數(shù)進行比較昼扛。但不存儲結(jié)果寸齐,只是正確的更改標志。
一般CMP做完判斷后會進行跳轉(zhuǎn)抄谐,后面通常會跟上B指令渺鹦!

  • BL 標號:跳轉(zhuǎn)到標號處執(zhí)行
  • BL 標號:比較結(jié)果是小于等于,執(zhí)行標號蛹含,否則不跳轉(zhuǎn)
  • B.GT 標號:比較結(jié)果是大于(greater than)毅厚,執(zhí)行標號,否則不跳轉(zhuǎn)
  • B.GE 標號:比較結(jié)果是大于等于(greater than or equal to)浦箱,執(zhí)行標號吸耿,否則不跳轉(zhuǎn)
  • B.EQ 標號:比較結(jié)果是等于祠锣,執(zhí)行標號,否則不跳轉(zhuǎn)
  • B.HI 標號:比較結(jié)果是無符號大于咽安,執(zhí)行標號伴网,否則不跳轉(zhuǎn)

三 循環(huán)

1. do while
void funa(int a)
{
    do{
        a++;
    }while (a<=g);
    /*
 var_4           = -4
 SUB             SP, SP, #0x10
 STR             W0, [SP,#0x10+var_4]  //參數(shù)w0入棧 int var_4 = w0;
loc_1000068D4 
 LDR             W8, [SP,#0x10+var_4]  //int w8= var_4;
ADD             W8, W8, #1     //int w8=w8+1;
STR             W8, [SP,#0x10+var_4]  //int var_4 = w8;
 ADRP            X8, #_g@PAGE
ADD             X8, X8, #_g@PAGEOFF  // *x8 = g
 LDR             W9, [SP,#0x10+var_4]  //int w9= var_4;
LDR             W10, [X8]  //w10=*x8;
CMP             W9, W10  
 B.LE            loc_1000068D4 // if(w9 <=w10)返回去執(zhí)行l(wèi)oc_1000068D4
ADD             SP, SP, #0x10 //棧平衡
 RET
     */
    
}
  • main的匯編中由MOV W8, #1 ;MOV X0, X8 ; BL _funa可以看出funa的參數(shù)為int x0=1。
  • 先執(zhí)行l(wèi)oc_1000068D4妆棒,再判斷CMP和 B.LE澡腾,符合條件就再回去執(zhí)行l(wèi)oc_1000068D4,由此可以得出為do while循環(huán)
2. while
void funb(int b){
    while (b<g) {
        b++;
    }
    /*
 var_4           = -4
 SUB             SP, SP, #0x10
STR             W0, [SP,#0x10+var_4]
 loc_1000068D0                           
 ADRP            X8, #_g@PAGE
 ADD             X8, X8, #_g@PAGEOFF
 LDR             W9, [SP,#0x10+var_4]
 LDR             W10, [X8]
 CMP             W9, W10
B.GE            loc_1000068F8
 LDR             W8, [SP,#0x10+var_4]
ADD             W8, W8, #1
 STR             W8, [SP,#0x10+var_4]
B               loc_1000068D0
 ---------------------------------------------------------------------------
 loc_1000068F8                      
 ADD             SP, SP, #0x10
RET
     */
}
  • 大于等于的時候執(zhí)行loc_1000068F8 恢復棧平衡糕珊,然后RET动分,當不符合條件小于的時候執(zhí)行loc_1000068D0,然后循環(huán)红选±焦可以判斷出是while(<){...}
3. for循環(huán)
void func(int a){
    int sum = 0;
    for (int i=0;i<a; i++) {
        sum +=i;
    }
}

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

func的匯編代碼是:

 var_C           = -0xC
 var_8           = -8
 var_4           = -4

SUB             SP, SP, #0x10
STR             W0, [SP,#0x10+var_4]
STR             WZR, [SP,#0x10+var_8]
 STR             WZR, [SP,#0x10+var_C]

 loc_1000068C8                          
LDR             W8, [SP,#0x10+var_C]
 LDR             W9, [SP,#0x10+var_4]
CMP             W8, W9
 B.GE            loc_1000068F8
 LDR             W8, [SP,#0x10+var_C]
LDR             W9, [SP,#0x10+var_8]
ADD             W8, W9, W8
STR             W8, [SP,#0x10+var_8]
LDR             W8, [SP,#0x10+var_C]
ADD             W8, W8, #1
STR             W8, [SP,#0x10+var_C]
B               loc_1000068C8
 ---------------------------------------------------------------------------

 loc_1000068F8                          
ADD             SP, SP, #0x10
RET
  • for循環(huán)和while的匯編代碼基本是一樣的,for和while的效率是一樣的

四 Swicth

1. case選擇條件連續(xù)且分支小于等于3時

代碼如下:


void funA(int a){
    switch (a) {
        case 1:
            printf("1");
            break;
        case 2:
            printf("2");
            break;
        case 3:
            printf("3");
            break;
        default:
            printf("default");
            break;
    }
}

int main(int argc, char * argv[]) {
    funA(2);
    return 0;
}

xcode動態(tài)匯編分析如下:


  • 首先參數(shù)-1喇肋,最小的分支玛瘸,看等不等于,等于就打印苟蹈,然后結(jié)束函數(shù)糊渊;如果不等于就繼續(xù)參數(shù)-2,減去第二個分支慧脱,看等不等于渺绒,以此類推,直到函數(shù)結(jié)束菱鸥。
  • 相當于if-else直到函數(shù)結(jié)束
2. case選擇條件連續(xù)且分支大于3時

代碼如下:

void funA(int a){
    switch (a) {
        case 1:
            printf("1");
            break;
        case 2:
            printf("2");
            break;
        case 3:
            printf("3");
            break;
        case 4:
            printf("4");
            break;
        default:
            printf("default");
            break;
    }
}
1.png
  • 先減去最大值宗兼,判斷是否是default
  • 再差表,偏移量氮采,在表中查到相應地址殷绍,如下:


    2.png
  • 獲取x8地址0x100a2a844 <+44>: adrp x8, 0
    x8=x8(0x100a2a844)去掉低12位 ->x8=0x100a2a000,然后0左移3位,x8=0x100a2a00; 0x100a2a848 <+48>: add x8, x8, #0x8c8 ; =0x8c8 x8=0x100a2a000+0x8c8 =0x100a2a8c8
  • ldur x9, [x29, #-0x10] x9=x8,參數(shù)為2鹊漠,那么x9=1
  • ldrsw x10, [x8, x9, lsl #2] 因為x9, lsl #2x9左移2位得到偏移量=0x100即4個字節(jié)主到,那么x10=[x8+4]
  • memory read 0x100a2a8c8得到0x100a2a8c8: 94 ff ff ff a8 ff ff ff bc ff ff ff d0 ff ff ff 那么x10=0xffffa8,從右往左讀躯概,一個字節(jié)一個字節(jié)的讀登钥,可以把斷點達到ldrsw x10, [x8, x9, lsl #2] 這一行,然后register read x10驗證娶靡,驗證得到:x10 = 0xffffffffffffffa8
  • 因為x10為負數(shù)牧牢,負數(shù)算法:取反加一(0xff-0xa8+1)得到所以add x8, x10, x8x8=x8-0x58= 0x100a2a8c8-0x58= 0x100a2a870
  • 0x100f6e858 <+64>: br x8會跳轉(zhuǎn)到0x100f6e870 <+88>: adrp x0, 1繼續(xù)執(zhí)行,可以xcode斷點到0x100f6e858 <+64>: br x8這一行,然后單步走一個ni進行驗證塔鳍;
switch總結(jié):

1伯铣、假設(shè)switch語句的分支比較少的時候(例如3,少于4的時候沒有意義)沒有必要使用此結(jié)構(gòu)轮纫,相當于if腔寡。
2、各個分支常量的差值較大的時候蜡感,編譯器會在效率還是內(nèi)存進行取舍蹬蚁,這個時候編譯器還是會編譯成類似于if恃泪,else的結(jié)構(gòu)郑兴。
3、在分支比較多的時候:在編譯的時候會生成一個表(跳轉(zhuǎn)表每個地址四個字節(jié))贝乎。

五 查看全局或者常量

int sum(int a, int b){
    printf("string");
    return a + 3*g;
}
  • 常量printf("string");找到string
0x100aee7d0 <+20>: adrp   x0, 1
0x100aee7d4 <+24>: add    x0, x0, #0xf18            ; =0xf18 
0x100aee7d8 <+28>: bl     0x100aeebe8               ; symbol stub for: printf

1情连、x0=0x100aee7d0去掉低12位 0x=0x100aee000;1左移3位得到0x1000,0x=0x100aef000
2览效、0x=0x+0xf18即0x=0x100aeff18
3却舀、用p (char*)0x100aeff18查看得到(char *) $1 = 0x0000000100aeff18 "string"

  • 全局變量也一樣

六 編譯器優(yōu)化

  • 選擇優(yōu)化策略:build setting -> optimization level ->debug ->fastest smallest然后編譯
int sum(int a, int b){
    printf("string");
    return a +b;
}
int main(int argc, char * argv[]) {
    int c = sum(1, 2);
    return 0;
}
 0x10050ab44 <+0>:  stp    x29, x30, [sp, #-0x10]!
    0x10050ab48 <+4>:  mov    x29, sp
    0x10050ab4c <+8>:  adr    x0, #0x13cc               ; "string"
    0x10050ab50 <+12>: nop    
    0x10050ab54 <+16>: bl     0x10050abd0               ; symbol stub for: printf
->  0x10050ab58 <+20>: mov    w0, #0x0
    0x10050ab5c <+24>: ldp    x29, x30, [sp], #0x10
    0x10050ab60 <+28>: ret    
  • 由于int c的值沒有用到,編譯器直接去掉了垃圾代碼
int main(int argc, char * argv[]) {
    int c = sum(1, 2);
    NSLog(@"%d",c);
    return 0;
}

就算用到了int c锤灿,編譯器會直接把結(jié)果賦給c:

  0x1001e2b10 <+0>:  sub    sp, sp, #0x20             ; =0x20 
    0x1001e2b14 <+4>:  stp    x29, x30, [sp, #0x10]
    0x1001e2b18 <+8>:  add    x29, sp, #0x10            ; =0x10 
    0x1001e2b1c <+12>: adr    x0, #0x13fc               ; "string"
    0x1001e2b20 <+16>: nop    
    0x1001e2b24 <+20>: bl     0x1001e2bc4               ; symbol stub for: printf
->  0x1001e2b28 <+24>: orr    w8, wzr, #0x3
    0x1001e2b2c <+28>: str    x8, [sp]
    0x1001e2b30 <+32>: adr    x0, #0x1508               ; @"%d"
    0x1001e2b34 <+36>: nop    
    0x1001e2b38 <+40>: bl     0x1001e2ba0               ; symbol stub for: NSLog
    0x1001e2b3c <+44>: mov    w0, #0x0
    0x1001e2b40 <+48>: ldp    x29, x30, [sp, #0x10]
    0x1001e2b44 <+52>: add    sp, sp, #0x20             ; =0x20 
    0x1001e2b48 <+56>: ret    

六 多線程之間共用寄存器挽拔,會對當前寄存器進行保護,操作系統(tǒng)會對內(nèi)存進行保護但校,以防止資源搶奪螃诅。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市状囱,隨后出現(xiàn)的幾起案子术裸,更是在濱河造成了極大的恐慌,老刑警劉巖亭枷,帶你破解...
    沈念sama閱讀 212,029評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袭艺,死亡現(xiàn)場離奇詭異,居然都是意外死亡叨粘,警方通過查閱死者的電腦和手機猾编,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來升敲,“玉大人袍镀,你說我怎么就攤上這事《澄睿” “怎么了苇羡?”我有些...
    開封第一講書人閱讀 157,570評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我设江,道長锦茁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評論 1 284
  • 正文 為了忘掉前任叉存,我火速辦了婚禮码俩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歼捏。我一直安慰自己稿存,他們只是感情好,可當我...
    茶點故事閱讀 65,650評論 6 386
  • 文/花漫 我一把揭開白布瞳秽。 她就那樣靜靜地躺著瓣履,像睡著了一般。 火紅的嫁衣襯著肌膚如雪练俐。 梳的紋絲不亂的頭發(fā)上袖迎,一...
    開封第一講書人閱讀 49,850評論 1 290
  • 那天,我揣著相機與錄音腺晾,去河邊找鬼燕锥。 笑死,一個胖子當著我的面吹牛悯蝉,可吹牛的內(nèi)容都是我干的归形。 我是一名探鬼主播,決...
    沈念sama閱讀 39,006評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼鼻由,長吁一口氣:“原來是場噩夢啊……” “哼暇榴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嗡靡,我...
    開封第一講書人閱讀 37,747評論 0 268
  • 序言:老撾萬榮一對情侶失蹤跺撼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后讨彼,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歉井,經(jīng)...
    沈念sama閱讀 44,207評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,536評論 2 327
  • 正文 我和宋清朗相戀三年哈误,在試婚紗的時候發(fā)現(xiàn)自己被綠了哩至。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,683評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜜自,死狀恐怖菩貌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情重荠,我是刑警寧澤箭阶,帶...
    沈念sama閱讀 34,342評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響仇参,放射性物質(zhì)發(fā)生泄漏嘹叫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,964評論 3 315
  • 文/蒙蒙 一诈乒、第九天 我趴在偏房一處隱蔽的房頂上張望罩扇。 院中可真熱鬧,春花似錦怕磨、人聲如沸喂饥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽员帮。三九已至,卻和暖如春滩届,著一層夾襖步出監(jiān)牢的瞬間集侯,已是汗流浹背被啼。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評論 1 266
  • 我被黑心中介騙來泰國打工帜消, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人浓体。 一個月前我還...
    沈念sama閱讀 46,401評論 2 360
  • 正文 我出身青樓泡挺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親命浴。 傳聞我的和親對象是個殘疾皇子娄猫,可洞房花燭夜當晚...
    茶點故事閱讀 43,566評論 2 349

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

  • 8086匯編 本筆記是筆者觀看小甲魚老師(魚C論壇)《零基礎(chǔ)入門學習匯編語言》系列視頻的筆記蝎困,在此感謝他和像他一樣...
    Gibbs基閱讀 37,140評論 8 114
  • 常用指令 cmp(Compare)比較指令 把一個寄存器的內(nèi)容和另一個寄存器的內(nèi)容或立即數(shù)進行比較。但不存儲結(jié)果倍啥,...
    危險地帶_淺笑閱讀 904評論 0 1
  • bl和ret指令 bl標號 將下一條指令的地址放入lr(x30)寄存器 轉(zhuǎn)到標號處執(zhí)行指令 注意:當我們遇到bl指...
    struggle3g閱讀 7,801評論 0 1
  • 關(guān)鍵時刻禾乘,第一時間送達! 問題種類 時間復雜度 在集合里數(shù)據(jù)量小的情況下時間復雜度對于性能的影響看起來微乎其微虽缕。但...
    C9090閱讀 887評論 0 1
  • 桂花的名字說來倒不高雅始藕,《紅樓夢》里,曹老把它給了夏金桂,夏金桂何許人也伍派?薛蟠的老婆弟塞, 心機陰狠,手段毒辣拙已,是生...
    簡彤閱讀 337評論 0 0