iOS 逆向开发05:判断&循环&Switch

iOS 逆向开发 文章汇总

目录

  • 一、cmp(Compare)比较指令
  • 二、循环
  • 三、Switch
    3.1 分支较少的Switch、3.2 分支较多的Switch、3.3 Switch建表查询总结、3.4 Switch总结


一、cmp(Compare)比较指令

   CMP 把一个寄存器的内容和另一个寄存器的内容或立即数进行比较。但不存储结果,只是正确的更改标志。
   一般CMP做完判断后会进行跳转,后面通常会跟上B指令!

  • BL 标号:跳转到标号处执行
  • B.GT 标号:比较结果是大于(greater than),执行标号,否则不跳转
  • B.GE 标号:比较结果是大于等于(greater than or equal to),执行标号,否则不跳转
  • B.HI 标号:比较结果是无符号大于,执行标号,否则不跳转
  • B.HS 标号:比较结果是无符号大于等于,执行标号,否则不跳转
  • B.LT 标号:比较结果是*小于,执行标号,否则不跳转
  • B.LE 标号:比较结果是*小于等于(less than or equal to),执行标号,否则不跳转
  • B.LO 标号:比较结果是无符号小于,执行标号,否则不跳转
  • B.LS 标号:比较结果是无符号小于等于,执行标号,否则不跳转
  • B.EQ 标号:比较结果是等于,执行标号,否则不跳转
  • B.NE 标号:比较结果是*不等于,执行标号,否则不跳转

b.?? 后面跟着的是else中的代码

int g = 12;

void func(int a, int b) {
    if (a > b) {
        g = a;
    } else {
        g = b;
    }
}

int main(int argc, char * argv[]) {
  
    func(1, 2);
    
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

Hopper中可以看到if判断中的代码:

                     _func:
0000000100001f00         sub        sp, sp, #0x10                     ; CODE XREF=_main+32
0000000100001f04         str        w0, [sp, #0xc]
0000000100001f08         str        w1, [sp, #0x8]
0000000100001f0c         ldr        w8, [sp, #0xc]
0000000100001f10         ldr        w9, [sp, #0x8]

// 比较w8, w9
0000000100001f14         cmp        w8, w9
0000000100001f18         b.le       loc_100001f30(le:小于等于--跳到else中-->因此下面是大于)

if (w8 > w9)
0000000100001f1c         ldr        w8, [sp, #0xc]
0000000100001f20         adrp       x9, #0x100009000
0000000100001f24         add        x9, x9, #0x478                    ; _g
0000000100001f28         str        w8, x9
0000000100001f2c         b          loc_100001f40

else
                     loc_100001f30:
0000000100001f30         ldr        w8, [sp, #0x8]                    ; CODE XREF=_func+24
0000000100001f34         adrp       x9, #0x100009000
0000000100001f38         add        x9, x9, #0x478                    ; _g
0000000100001f3c         str        w8, x9

                     loc_100001f40:
0000000100001f40         add        sp, sp, #0x10                     ; CODE XREF=_func+44
0000000100001f44         ret
                        ; endp

cmp执行的是减法、修改标记寄存器
汇编最后没有通过x0寄存器上个栈中的地址存储返回值,那么函数就没有返回值



二、循环

do-while循环:

int main(int argc, char * argv[]) {
    int nSum = 0;
    int i = 0;
    do {
        nSum = nSum + 1;
        I++;
    } while (i < 100);

    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
                     _main:
0000000100001f80         sub        sp, sp, #0x40
0000000100001f84         stp        x29, x30, [sp, #0x30]
0000000100001f88         add        x29, sp, #0x30
0000000100001f8c         stur       wzr, [x29, #-0x4]
0000000100001f90         stur       w0, [x29, #-0x8]
0000000100001f94         stur       x1, [x29, #-0x10]
0000000100001f98         stur       wzr, [x29, #-0x14]
0000000100001f9c         str        wzr, [sp, #0x18]

                     loc_100001fa0:
0000000100001fa0         ldur       w8, [x29, #-0x14]                           ; CODE XREF=_main+64
0000000100001fa4         add        w8, w8, #0x1
0000000100001fa8         stur       w8, [x29, #-0x14]
0000000100001fac         ldr        w8, [sp, #0x18]
0000000100001fb0         add        w8, w8, #0x1
0000000100001fb4         str        w8, [sp, #0x18]
0000000100001fb8         ldr        w8, [sp, #0x18]
0000000100001fbc         cmp        w8, #0x64
0000000100001fc0         b.lt       loc_100001fa0
.....

while循环:

int main(int argc, char * argv[]) {
    int nSum = 0;
    int i = 0;

    while (i < 100) {
        nSum = nSum + 1;
        I++;
    };
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
0000000100001f7c         sub        sp, sp, #0x40
0000000100001f80         stp        x29, x30, [sp, #0x30]
0000000100001f84         add        x29, sp, #0x30
0000000100001f88         stur       wzr, [x29, #-0x4]
0000000100001f8c         stur       w0, [x29, #-0x8]
0000000100001f90         stur       x1, [x29, #-0x10]
0000000100001f94         stur       wzr, [x29, #-0x14]
0000000100001f98         str        wzr, [sp, #0x18]

                     loc_100001f9c:
0000000100001f9c         ldr        w8, [sp, #0x18]                             ; CODE XREF=_main+68
0000000100001fa0         cmp        w8, #0x64
0000000100001fa4         b.ge       loc_100001fc4

0000000100001fa8         ldur       w8, [x29, #-0x14]
0000000100001fac         add        w8, w8, #0x1
0000000100001fb0         stur       w8, [x29, #-0x14]
0000000100001fb4         ldr        w8, [sp, #0x18]
0000000100001fb8         add        w8, w8, #0x1
0000000100001fbc         str        w8, [sp, #0x18]
0000000100001fc0         b          loc_100001f9c

                     loc_100001fc4:
....

for循环和while循环一样。



三、Switch

3.1 分支较少的Switch

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


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

汇编如下:

004--选择`funcA:
    0x10292df84 <+0>:   sub    sp, sp, #0x20             ; =0x20 
    0x10292df88 <+4>:   stp    x29, x30, [sp, #0x10]
    0x10292df8c <+8>:   add    x29, sp, #0x10            ; =0x10 

    // 参数入栈
    0x10292df90 <+12>:  stur   w0, [x29, #-0x4]
->  0x10292df94 <+16>:  ldur   w8, [x29, #-0x4]

    // 和1比较
    0x10292df98 <+20>:  cmp    w8, #0x1                  ; =0x1 
    0x10292df9c <+24>:  str    w8, [sp, #0x8]
    0x10292dfa0 <+28>:  b.eq   0x10292dfc8               ; ---> case1
    0x10292dfa4 <+32>:  b      0x10292dfa8               ; ---> 和2比较

    // 和2比较
    0x10292dfa8 <+36>:  ldr    w8, [sp, #0x8]
    0x10292dfac <+40>:  cmp    w8, #0x2                  ; =0x2 
    0x10292dfb0 <+44>:  b.eq   0x10292dfd8               ; ---> case2
    0x10292dfb4 <+48>:  b      0x10292dfb8               ; ---> 和3比较

    // 和3比较
    0x10292dfb8 <+52>:  ldr    w8, [sp, #0x8]
    0x10292dfbc <+56>:  cmp    w8, #0x3                  ; =0x3 
    0x10292dfc0 <+60>:  b.eq   0x10292dfe8               ; ---> case3
    0x10292dfc4 <+64>:  b      0x10292dff8               ; ---> default

    //case 1:
    0x10292dfc8 <+68>:  adrp   x0, 2
    0x10292dfcc <+72>:  add    x0, x0, #0xdfd            ; =0xdfd 
    0x10292dfd0 <+76>:  bl     0x10292e384               ; symbol stub for: printf
    0x10292dfd4 <+80>:  b      0x10292e004               ; <+128> at main.m:30:1

    //case 2:
    0x10292dfd8 <+84>:  adrp   x0, 2
    0x10292dfdc <+88>:  add    x0, x0, #0xe03            ; =0xe03 
    0x10292dfe0 <+92>:  bl     0x10292e384               ; symbol stub for: printf
    0x10292dfe4 <+96>:  b      0x10292e004               ; <+128> at main.m:30:1

    //case 3:
    0x10292dfe8 <+100>: adrp   x0, 2
    0x10292dfec <+104>: add    x0, x0, #0xe09            ; =0xe09 
    0x10292dff0 <+108>: bl     0x10292e384               ; symbol stub for: printf
    0x10292dff4 <+112>: b      0x10292e004               ; <+128> at main.m:30:1

    //default
    0x10292dff8 <+116>: adrp   x0, 2
    0x10292dffc <+120>: add    x0, x0, #0xe0f            ; =0xe0f 
    0x10292e000 <+124>: bl     0x10292e384               ; symbol stub for: printf

    0x10292e004 <+128>: ldp    x29, x30, [sp, #0x10]
    0x10292e008 <+132>: add    sp, sp, #0x20             ; =0x20 
    0x10292e00c <+136>: ret    

3.2 分支较多的Switch

添加case:

case 4:
            printf("--4--");
            break;
004--选择`funcA:
->  0x104621f64 <+0>:   sub    sp, sp, #0x20             ; =0x20 
    0x104621f68 <+4>:   stp    x29, x30, [sp, #0x10]
    0x104621f6c <+8>:   add    x29, sp, #0x10            ; =0x10 

    // 参数入栈
    0x104621f70 <+12>:  stur   w0, [x29, #-0x4]
    0x104621f74 <+16>:  ldur   w8, [x29, #-0x4]

    // 参数-1(减去最小的case),subs会影响状态寄存器
    0x104621f78 <+20>:  subs   w8, w8, #0x1              ; =0x1 
    0x104621f7c <+24>:  mov    x9, x8                    ; 将x9=x8(注意是x8,不是上面的w8)
    0x104621f80 <+28>:  ubfx   x9, x9, #0, #32           ; 将x9高32位清零
    0x104621f84 <+32>:  cmp    x9, #0x3                  ; 和3(最大case与最小case的差值)比较
    0x104621f88 <+36>:  str    x9, [sp]
    0x104621f8c <+40>:  b.hi   0x104621fe8               ; (参数-最小case和差值比较)hi:无符号大于--->default
    0x104621f90 <+44>:  adrp   x8, 1
    0x104621f94 <+48>:  add    x8, x8, #0x0              ; 这两行得到x8=0x104622000即函数结束后的地址
    0x104621f98 <+52>:  ldr    x11, [sp]
    0x104621f9c <+56>:  ldrsw  x10, [x8, x11, lsl #2]    ; 在表中找出偏移值
    0x104621fa0 <+60>:  add    x9, x8, x10               ; x8加上偏移值(负数)得到下面case或default的地址
    0x104621fa4 <+64>:  br     x9                        ; 直接跳转到计算出的case地址

    //case 1:
    0x104621fa8 <+68>:  adrp   x0, 2
    0x104621fac <+72>:  add    x0, x0, #0xdfd            ; =0xdfd 
    0x104621fb0 <+76>:  bl     0x104622384               ; symbol stub for: printf
    0x104621fb4 <+80>:  b      0x104621ff4               ; --->跳出Switch

    //case 2:
    0x104621fb8 <+84>:  adrp   x0, 2
    0x104621fbc <+88>:  add    x0, x0, #0xe03            ; =0xe03 
    0x104621fc0 <+92>:  bl     0x104622384               ; symbol stub for: printf
    0x104621fc4 <+96>:  b      0x104621ff4               ; --->跳出Switch

    //case 3:
    0x104621fc8 <+100>: adrp   x0, 2
    0x104621fcc <+104>: add    x0, x0, #0xe09            ; =0xe09 
    0x104621fd0 <+108>: bl     0x104622384               ; symbol stub for: printf
    0x104621fd4 <+112>: b      0x104621ff4               ; --->跳出Switch

    //case 4:
    0x104621fd8 <+116>: adrp   x0, 2
    0x104621fdc <+120>: add    x0, x0, #0xe0f            ; =0xe0f 
    0x104621fe0 <+124>: bl     0x104622384               ; symbol stub for: printf
    0x104621fe4 <+128>: b      0x104621ff4               ; --->跳出Switch

    // default
    0x104621fe8 <+132>: adrp   x0, 2
    0x104621fec <+136>: add    x0, x0, #0xe15            ; =0xe15 
    0x104621ff0 <+140>: bl     0x104622384               ; symbol stub for: printf

    0x104621ff4 <+144>: ldp    x29, x30, [sp, #0x10]
    0x104621ff8 <+148>: add    sp, sp, #0x20             ; =0x20 
    0x104621ffc <+152>: ret    

ubfx命令解读:ubfx x9, x9, #0, #32
将x9中第#0位到第 #32位取出来赋值给x9其余位用0补足-----即高32位清零。

ldrsw命令解读:ldrsw x10, [x8, x11, lsl #2]
第一步:x11, lsl #2--->x11左移两位(即乘以4:表中是4个字节为单位的)
第二步:x8 加上 第一步的结果 (x8指向函数结束后的地址,保存了几个负数)
第三步:取出[]中地址的值保存到x10--->得到偏移值

br x9:跳转到x9寄存器中保存的地址,不改变lr(x30)寄存器
br x9等价于:b [x9]


验证ubfx清零的是高32位:


查看函数结束后的地址中的数据表

4个负数表示差值3+1(如果case是连续的表中就没有default的偏移值,如果不连续表中有一个是default的偏移值???)
x8即负数表的地址加上表的值即能得到相应case的地址
Switch 中case和default分支的代码段是连续的

3.3 Switch建表查询总结

  1. 先将参数减去最小case -->x9
  2. x9和差值(最大case-最小case)比较,如果无符号大于--->default
  3. 通过表,直接计算出分支地址


3.4 Switch总结

  1. 假设switch语句的分支比较少的时候(例如3,少于4的时候没有意义)没有必要使用建表查询结构,相当于if。
  2. 在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节),以空间换取时间
  3. 分支数量大于3但各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是可能会编译成类似于if,else的结构。

你可能感兴趣的:(iOS 逆向开发05:判断&循环&Switch)