美文网首页
汇编基础(九)switch汇编分析

汇编基础(九)switch汇编分析

作者: struggle3g | 来源:发表于2018-05-29 02:39 被阅读14次

    引言

    最近工作比较忙,没怎么去研究汇编的内容,这么多天,感觉有点生疏,有的时候累死累活很晚才回来,还要在学习别的东西,在写一些总结,力不从心,就算是强迫自己去写去学习,我认为效率也是低下的。这种情况,既影响了第二天的工作,学习的时候也不会太过专注,只为一味的应付了事,学习是一个枯燥、兴趣使向、有目的性的

    一些概念上的内容,其实光看光听、光说不练,基本上也就相当于拍脑袋做事情,正儿八经的研究应该是:构思->过程->验证 往往复复每次都会有新的体会

    switch 注意事项

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

    switch高级代码 <4选项

    void funcC(int a ){
        switch (a) {
            case 0:
                printf("yuanying");  //元婴
                break;
            case 1:
                printf("fenshen");  //分身
                break;
            case 2:
                printf("dujie");   //渡劫
                break;
            default:
                printf("qiongsi");    //穷死
                break;
        }
    }
    int main(int argc, char * argv[]) {
        funcC(2);
        return 0;
    }
    

    汇编代码

    汇编Switch`funcC:
        0x10475a82c <+0>:   sub    sp, sp, #0x30             ; =0x30 
        0x10475a830 <+4>:   stp    x29, x30, [sp, #0x20]
        0x10475a834 <+8>:   add    x29, sp, #0x20            ; =0x20 
        0x10475a838 <+12>:  stur   w0, [x29, #-0x4]
    ->  0x10475a83c <+16>:  ldur   w0, [x29, #-0x4]
        0x10475a840 <+20>:  mov    x8, x0
        0x10475a844 <+24>:  stur   w8, [x29, #-0x8]
        0x10475a848 <+28>:  cbz    w0, 0x10475a878           ; <+76> at main.m:17
        0x10475a84c <+32>:  b      0x10475a850               ; <+36> at main.m
        0x10475a850 <+36>:  ldur   w8, [x29, #-0x8]
        0x10475a854 <+40>:  subs   w9, w8, #0x1              ; =0x1 
        0x10475a858 <+44>:  stur   w9, [x29, #-0xc]
        0x10475a85c <+48>:  b.eq   0x10475a88c               ; <+96> at main.m:20
        0x10475a860 <+52>:  b      0x10475a864               ; <+56> at main.m
        0x10475a864 <+56>:  ldur   w8, [x29, #-0x8]
        0x10475a868 <+60>:  subs   w9, w8, #0x2              ; =0x2 
        0x10475a86c <+64>:  str    w9, [sp, #0x10]
        0x10475a870 <+68>:  b.eq   0x10475a8a0               ; <+116> at main.m:23
        0x10475a874 <+72>:  b      0x10475a8b4               ; <+136> at main.m:26
        0x10475a878 <+76>:  adrp   x0, 1
        0x10475a87c <+80>:  add    x0, x0, #0xf10            ; =0xf10 
        0x10475a880 <+84>:  bl     0x10475abe0               ; symbol stub for: printf
        0x10475a884 <+88>:  str    w0, [sp, #0xc]
        0x10475a888 <+92>:  b      0x10475a8c4               ; <+152> at main.m:29
        0x10475a88c <+96>:  adrp   x0, 1
        0x10475a890 <+100>: add    x0, x0, #0xf19            ; =0xf19 
        0x10475a894 <+104>: bl     0x10475abe0               ; symbol stub for: printf
        0x10475a898 <+108>: str    w0, [sp, #0x8]
        0x10475a89c <+112>: b      0x10475a8c4               ; <+152> at main.m:29
        0x10475a8a0 <+116>: adrp   x0, 1
        0x10475a8a4 <+120>: add    x0, x0, #0xf21            ; =0xf21 
        0x10475a8a8 <+124>: bl     0x10475abe0               ; symbol stub for: printf
        0x10475a8ac <+128>: str    w0, [sp, #0x4]
        0x10475a8b0 <+132>: b      0x10475a8c4               ; <+152> at main.m:29
        0x10475a8b4 <+136>: adrp   x0, 1
        0x10475a8b8 <+140>: add    x0, x0, #0xf27            ; =0xf27 
        0x10475a8bc <+144>: bl     0x10475abe0               ; symbol stub for: printf
        0x10475a8c0 <+148>: str    w0, [sp]
        0x10475a8c4 <+152>: ldp    x29, x30, [sp, #0x20]
        0x10475a8c8 <+156>: add    sp, sp, #0x30             ; =0x30 
        0x10475a8cc <+160>: ret    
    

    代码分析

    1. 0x10475a82c 拉伸栈空间
    2. 0x10475a830 保存fp(栈底)、LR(回main函数,funcC函数调用完成以后执行的代码)
    3. 0x10475a834 x29 = sp + #0x20
    4. 0x10475a838 w0 存入到 【x29 - 0x4】
    5. 0x10475a83c 将【x29 - 0x4】的值读入到w0
    6. 0x10475a840 x8 = x0
    7. 0x10475a844 w8存入进【x29 - 0x4】
    8. 0x10475a848 判断w0是否为0 如果为0 直接跳转执行0x10475a878中的方法,不跳转直接向下执行 因为结果是2
      • 0x10475a84c 跳转0x10475a850的方法 0x10475a848 中没有跳转
      • 0x10475a850 w8 = 【x29 - 0x8】
      • 0x10475a854 w9 = w8 - #0x1 修改标记位
      • 0x10475a858【x29- 0x8】 = w9
      • 0x10475a85c 上面的subs是否为0 ,就是等于的意思,等于 执行标记地址的方法的方法0x10475a88c
        • 如果等于执行0x10475a88c adrp x0, 1
        • 0x10475a890 add x0, x0, #0xf19
        • 0x10475a894 bl 0x10475abe0 执行printf函数打印
        • 0x10475a898 保存w0 [sp, #0x8] = w0
        • 0x10475a89c b 0x10475a8c4 跳转(准备返回跳出该函数)
      • 0x10475a860 向下执行0x10475a864标记的方法
      • 0x10475a864 w8 = [x29, #-0x8]
      • 0x10475a868 w9 = w8 - #0x2 修改标记位
      • 0x10475a86c 【sp + 0x10】 = w9
      • 0x10475a870 w9 = w8 - #0x2 是否为0 为0 执行标记地址中的方法 0x10475a8a0 不为0直接往下走
        • 0x10475a874 上面的结果没有跳转,执行标记地址(0x10475a8b4)中的方法
        • 下面的内容
        • 0x10475a8b4 <+136>: adrp x0, 1
        • 0x10475a8b8 <+140>: add x0, x0, #0xf27 ; =0xf27
        • 0x10475a8bc <+144>: bl 0x10475abe0 ; symbol stub for: printf
          以上是找到一个常量、并打印
          以下是0x10475a870 执行的标记地址中的方法
        • 0x10475a8a0 <+116>: adrp x0, 1
        • 0x10475a8a4 <+120>: add x0, x0, #0xf21 ; =0xf21
        • 0x10475a8a8 <+124>: bl 0x10475abe0
          以上是找到常量或局部变量
        • 0x10475a8ac [sp, #0x4] = w0
        • 0x10475a8b0 执行标记地址中的方法0x10475a8c4
    9. 0x10475a878 <+76>: adrp x0, 1
    10. 0x10475a87c <+80>: add x0, x0, #0xf10 ; =0xf10
      以上找到常量或者局部变量
    11. 0x10475a880 printf打印
    12. 0x10475a884 [sp, #0xc] = w0
    13. 0x10475a888 跳转到0x10475a8c4 直接返回之前函数准备
    • 0x10475a8c0 <+148>: str w0, [sp] 保存 [sp] = w0
    • 0x10475a8c4 <+152>: ldp x29, x30, [sp, #0x20] 恢复 fp lr
    • 0x10475a8c8 <+156>: add sp, sp, #0x30 ; =0x30 恢复栈空间
    • 0x10475a8cc <+160>: ret 返回

    Switch超过4个选项汇编高级代码还原

    高级代码

    - (void)TheSelector:(NSInteger )Index{
        switch (Index) {
            case 0:
            {
            }
                break;
            case 1:
            {
            }
                break;
            case 2:
            {
            }
                break;
            case 3:
            {
            }
                break;
            case 4:
            {
            }
                break;
            case 5:
            {
            }
                break;
            default:
                break;
        }
    }
    
    
    高级代码还原`-[ViewController TheSelector:]:
       0x10427e69c <+0>:  sub    sp, sp, #0x30             ; =0x30 
       0x10427e6a0 <+4>:  str    x0, [sp, #0x28]
       0x10427e6a4 <+8>:  str    x1, [sp, #0x20]
       0x10427e6a8 <+12>: str    x2, [sp, #0x18]
    ->  0x10427e6ac <+16>: ldr    x0, [sp, #0x18]
       0x10427e6b0 <+20>: mov    x1, x0
       0x10427e6b4 <+24>: subs   x0, x0, #0x5              ; =0x5 
       0x10427e6b8 <+28>: str    x1, [sp, #0x10]
       0x10427e6bc <+32>: str    x0, [sp, #0x8]
       0x10427e6c0 <+36>: b.hi   0x10427e6f4               ; <+88> at ViewController.m:51
       0x10427e6c4 <+40>: adrp   x8, 0
       0x10427e6c8 <+44>: add    x8, x8, #0x700            ; =0x700 
       0x10427e6cc <+48>: ldr    x9, [sp, #0x10]
       0x10427e6d0 <+52>: ldrsw  x10, [x8, x9, lsl #2]
       0x10427e6d4 <+56>: add    x8, x10, x8
       0x10427e6d8 <+60>: br     x8
       0x10427e6dc <+64>: b      0x10427e6f8               ; <+92> at ViewController.m:54
       0x10427e6e0 <+68>: b      0x10427e6f8               ; <+92> at ViewController.m:54
       0x10427e6e4 <+72>: b      0x10427e6f8               ; <+92> at ViewController.m:54
       0x10427e6e8 <+76>: b      0x10427e6f8               ; <+92> at ViewController.m:54
       0x10427e6ec <+80>: b      0x10427e6f8               ; <+92> at ViewController.m:54
       0x10427e6f0 <+84>: b      0x10427e6f8               ; <+92> at ViewController.m:54
       0x10427e6f4 <+88>: b      0x10427e6f8               ; <+92> at ViewController.m:54
       0x10427e6f8 <+92>: add    sp, sp, #0x30             ; =0x30 
       0x10427e6fc <+96>: ret  
    

    分开分析

    • 0x10427e69c <+0>: sub sp, sp, #0x30 ; =0x30
      拉伸栈空间
    • 0x10427e6a0 <+4>: str x0, [sp, #0x28]
      0x10427e6a4 <+8>: str x1, [sp, #0x20]
      0x10427e6a8 <+12>: str x2, [sp, #0x18]
      0x10427e6ac <+16>: ldr x0, [sp, #0x18]

      这三句的代码,将x0、x1、x2存入到栈中,然后在把x2存入栈中的值,读出来存到x0,这个过程的含义,大概可以这么理解,因为在OC当中调用方法,是调用一个方法_objc_msgSend,它的三个参数分别是Self、_TheSelector、以及传过来的值,所以最后要读出来x2的值在存入到x0

    • 0x10427e6b0 <+20>: mov x1, x0

      将将x0的值赋值给x1

    • 0x10427e6b4 <+24>: subs x0, x0, #0x5 ; =0x5

    x0 = x0 - #0x5 得出的结果将会修改标记位

    • 0x10427e6b8 <+28>: str x1, [sp, #0x10]
      0x10427e6bc <+32>: str x0, [sp, #0x8]

      x1 、x0存入到栈中

    • 0x10427e6c0 <+36>: b.hi 0x10427e6f4

      如果标记位也就是ubs x0, x0, #0x5 x0>#0x5 直接跳转到标记中的地址,当然根据调试我们发现并没有往下走,而且根据我们写的高级代码来说 3肯定没有5大

    • 0x10427e6c4 <+40>: adrp x8, 0
      0x10427e6c8 <+44>: add x8, x8, #0x700 ; =0x700
      ldr x9, [sp, #0x10]

      获取一个全局变量、或者一个常量
      x8得到的结果是-36 x8 中的地址是 猛的一看就是负数


      0x10427e6cc <+48>: ldr x9, [sp, #0x10]

      0x10427e6b8 <+28>: str x1, [sp, #0x10]
      0x10427e6cc <+48>: ldr x9, [sp, #0x10] 因为x1是参数3,x9 = 3


      0x10427e6d0 <+52>: ldrsw x10, [x8, x9, lsl #2]
      0x10427e6d4 <+56>: add x8, x10, x8

    x10 = [x8, x9, lsl #2]内存地址中的值
    [x8, x9, lsl #2] 以x8为基准值 + (x9的值左移2位 )
    以x8地址为基准值 + (3的值左移2位 )
    以x8地址为基准值 + (3的值左移2位 )0011 变成 1100 8+4 = 12
    以x8地址为基准值 + 12
    x10 = 0xffffffe8



    直接跳转到指定的case中


    结论

    • 其实核心在于
      0x10427e6c4 <+40>: adrp x8, 0
      0x10427e6c8 <+44>: add x8, x8, #0x700
      中拿到的x8,0x100d4e700

    **switch中的选项>=4个的话,中间间隔不是很长的话,会直接连续的在栈中创建一个表,每4个字节代表一个数值,比如:case 1、2、3
    内存创建的表抽象的对应这些地址:xx FF FF FF = 0 、xx FF FF FF = 1 、xx FF FF FF = 2 、xx FF FF FF = 3,当然最后你需要计算出真正的偏移的位置 **

    相关文章

      网友评论

          本文标题:汇编基础(九)switch汇编分析

          本文链接:https://www.haomeiwen.com/subject/xzdujftx.html