美文网首页
汇编(五)

汇编(五)

作者: 浅墨入画 | 来源:发表于2021-04-16 23:36 被阅读0次

一. switch(上)

我们继续汇编(四)switch内容开始

// main.m文件代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

void funcA(int a){
    switch (a) {
        case 1:
            printf("打坐");
            break;
        case 2:
            printf("加红");
            break;
        case 3:
            printf("加蓝");
            break;
        default:
            printf("啥都不干");
            break;
    }
}
int main(int argc, char * argv[]) {
    funcA(1);
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
004--选择`funcA:
    0x1009b2114 <+0>:   sub    sp, sp, #0x20             ; =0x20 
    0x1009b2118 <+4>:   stp    x29, x30, [sp, #0x10]
    0x1009b211c <+8>:   add    x29, sp, #0x10            ; =0x10 
    0x1009b2120 <+12>:  stur   w0, [x29, #-0x4]
->  0x1009b2124 <+16>:  ldur   w8, [x29, #-0x4]
    // w8寄存器与 1比较,相等跳转0x1009b2158,否则跳转0x1009b2138
    0x1009b2128 <+20>:  cmp    w8, #0x1                  ; =0x1 
    0x1009b212c <+24>:  str    w8, [sp, #0x8]
    0x1009b2130 <+28>:  b.eq   0x1009b2158               ; <+68> at main.m
    // 这里的跳转0x1009b2138 不写也会执行下面代码0x1009b2138
    0x1009b2134 <+32>:  b      0x1009b2138               ; <+36> at main.m
    0x1009b2138 <+36>:  ldr    w8, [sp, #0x8]
    0x1009b213c <+40>:  cmp    w8, #0x23                 ; =0x23 
    0x1009b2140 <+44>:  b.eq   0x1009b2178               ; <+100> at main.m
    0x1009b2144 <+48>:  b      0x1009b2148               ; <+52> at main.m
    0x1009b2148 <+52>:  ldr    w8, [sp, #0x8]
    0x1009b214c <+56>:  cmp    w8, #0xc8                 ; =0xc8 
    0x1009b2150 <+60>:  b.eq   0x1009b2168               ; <+84> at main.m
    0x1009b2154 <+64>:  b      0x1009b2188               ; <+116> at main.m
    // 取出常量,执行printf打印
    0x1009b2158 <+68>:  adrp   x0, 1
    0x1009b215c <+72>:  add    x0, x0, #0xf6d            ; =0xf6d  
    0x1009b2160 <+76>:  bl     0x1009b258c               ; symbol stub for: printf
    0x1009b2164 <+80>:  b      0x1009b2194               ; <+128> at main.m:27:1
    // 取出常量,执行printf打印
    0x1009b2168 <+84>:  adrp   x0, 1
    0x1009b216c <+88>:  add    x0, x0, #0xf74            ; =0xf74 
    0x1009b2170 <+92>:  bl     0x1009b258c               ; symbol stub for: printf
    0x1009b2174 <+96>:  b      0x1009b2194               ; <+128> at main.m:27:1
    // 取出常量,执行printf打印
    0x1009b2178 <+100>: adrp   x0, 1
    0x1009b217c <+104>: add    x0, x0, #0xf7b            ; =0xf7b 
    0x1009b2180 <+108>: bl     0x1009b258c               ; symbol stub for: printf
    0x1009b2184 <+112>: b      0x1009b2194               ; <+128> at main.m:27:1
    0x1009b2188 <+116>: adrp   x0, 1
    0x1009b218c <+120>: add    x0, x0, #0xf82            ; =0xf82 
    0x1009b2190 <+124>: bl     0x1009b258c               ; symbol stub for: printf
    0x1009b2194 <+128>: ldp    x29, x30, [sp, #0x10]
    0x1009b2198 <+132>: add    sp, sp, #0x20             ; =0x20 
    0x1009b219c <+136>: ret    
// 如果是三个case,底层实际上执行的是if else

修改main.m代码如下

#import <UIKit/UIKit.h>
#import "AppDelegate.h"
void funcA(int a){
    switch (a) {//
        case 1:
            printf("打坐");
            break;
        case 2:
            printf("加红");
            break;
        case 3:
            printf("加蓝");
            break;
        case 5:
            printf("打怪");
            break;
        default:
            printf("啥都不干");
            break;
    }
}

int main(int argc, char * argv[]) {
    funcA(4);
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
004--选择`funcA:
    0x10427e0ec <+0>:   sub    sp, sp, #0x20             ; =0x20 
    0x10427e0f0 <+4>:   stp    x29, x30, [sp, #0x10]
    0x10427e0f4 <+8>:   add    x29, sp, #0x10            ; =0x10 
    // 参数入栈,用w8来进行比较
    0x10427e0f8 <+12>:  stur   w0, [x29, #-0x4]
->  0x10427e0fc <+16>:  ldur   w8, [x29, #-0x4]
    0x10427e100 <+20>:  subs   w8, w8, #0x1              ; =0x1 
    0x10427e104 <+24>:  mov    x9, x8
    0x10427e108 <+28>:  ubfx   x9, x9, #0, #32
    0x10427e10c <+32>:  cmp    x9, #0x4                  ; =0x4 
    0x10427e110 <+36>:  str    x9, [sp]
    0x10427e114 <+40>:  b.hi   0x10427e170               ; <+132> at main.m
    0x10427e118 <+44>:  adrp   x8, 0
    0x10427e11c <+48>:  add    x8, x8, #0x188            ; =0x188 
    0x10427e120 <+52>:  ldr    x11, [sp]
    0x10427e124 <+56>:  ldrsw  x10, [x8, x11, lsl #2]
    0x10427e128 <+60>:  add    x9, x8, x10
    0x10427e12c <+64>:  br     x9
    0x10427e130 <+68>:  adrp   x0, 1
    0x10427e134 <+72>:  add    x0, x0, #0xf69            ; =0xf69 
    0x10427e138 <+76>:  bl     0x10427e588               ; symbol stub for: printf
    0x10427e13c <+80>:  b      0x10427e17c               ; <+144> at main.m:29:1
    0x10427e140 <+84>:  adrp   x0, 1
    0x10427e144 <+88>:  add    x0, x0, #0xf70            ; =0xf70 
    0x10427e148 <+92>:  bl     0x10427e588               ; symbol stub for: printf
    0x10427e14c <+96>:  b      0x10427e17c               ; <+144> at main.m:29:1
    0x10427e150 <+100>: adrp   x0, 1
    0x10427e154 <+104>: add    x0, x0, #0xf77            ; =0xf77 
    0x10427e158 <+108>: bl     0x10427e588               ; symbol stub for: printf
    0x10427e15c <+112>: b      0x10427e17c               ; <+144> at main.m:29:1
    0x10427e160 <+116>: adrp   x0, 1
    0x10427e164 <+120>: add    x0, x0, #0xf7e            ; =0xf7e 
    0x10427e168 <+124>: bl     0x10427e588               ; symbol stub for: printf
    0x10427e16c <+128>: b      0x10427e17c               ; <+144> at main.m:29:1
    0x10427e170 <+132>: adrp   x0, 1
    0x10427e174 <+136>: add    x0, x0, #0xf85            ; =0xf85 
    0x10427e178 <+140>: bl     0x10427e588               ; symbol stub for: printf
    0x10427e17c <+144>: ldp    x29, x30, [sp, #0x10]
    0x10427e180 <+148>: add    sp, sp, #0x20             ; =0x20 
    0x10427e184 <+152>: ret    
image.png

0x10427e100 <+20>: subs w8, w8, #0x1 ; =0x1 subs 减的话有可能溢出,下面进行验证

void funcA(int a){
    switch (a) {//
        case 5:
            printf("打坐");
            break;
        case 6:
            printf("加红");
            break;
        case 7:
            printf("加蓝");
            break;
        case 8:
            printf("打怪");
            break;
        default:
            printf("啥都不干");
            break;
    }
}

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

// 汇编代码
004--选择`funcA:
    0x10040e0f0 <+0>:   sub    sp, sp, #0x20             ; =0x20 
    0x10040e0f4 <+4>:   stp    x29, x30, [sp, #0x10]
    0x10040e0f8 <+8>:   add    x29, sp, #0x10            ; =0x10 
    0x10040e0fc <+12>:  stur   w0, [x29, #-0x4]
    // 给w8 赋值为4,把之前的值清干净
->  0x10040e100 <+16>:  ldur   w8, [x29, #-0x4]
    // 这里减的是5,就会溢出
    0x10040e104 <+20>:  subs   w8, w8, #0x5              ; =0x5 
    // 执行完上面一行,x8   unsigned long 0x00000000ffffffff
    // x8给到x9,x9也变成0x00000000ffffffff
    0x10040e108 <+24>:  mov    x9, x8
    // ubfx指令是指,把x9寄存器由低往高数32位,其他位清0,这里保留的是 ffffffff,相当于把高32位清0
    // 因为这里只取了低32位的值,所以要把高32位清0
    0x10040e10c <+28>:  ubfx   x9, x9, #0, #32
    // 把高32位清掉再来比较,防止比较出现问题
    0x10040e110 <+32>:  cmp    x9, #0x3                  ; =0x3 
    0x10040e114 <+36>:  str    x9, [sp]
    0x10040e118 <+40>:  b.hi   0x10040e174               ; <+132> at main.m
... 

0x10040e10c <+28>: ubfx x9, x9, #0, #32,下面验证ubfx是把x9寄存器由低往高数32位,其他位清0

执行前 执行后

上面两张图可以看出ubfx是把x9寄存器由低往高数32位,其他位清0

继续修改代码如下

void funcA(int a){
    switch (a) {
        case 4:
            printf("打坐");
            break;
        case 6:
            printf("加红");
            break;
        case 7:
            printf("加蓝");
            break;
        case 8:
            printf("打怪");
            break;
        default:
            printf("啥都不干");
            break;
    }
}
int main(int argc, char * argv[]) {
    funcA(5);
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

// 汇编代码如下
004--选择`funcA:
    0x1009860ec <+0>:   sub    sp, sp, #0x20             ; =0x20 
    0x1009860f0 <+4>:   stp    x29, x30, [sp, #0x10]
    0x1009860f4 <+8>:   add    x29, sp, #0x10            ; =0x10 
    0x1009860f8 <+12>:  stur   w0, [x29, #-0x4]
    0x1009860fc <+16>:  ldur   w8, [x29, #-0x4]
    0x100986100 <+20>:  subs   w8, w8, #0x4              ; =0x4 
    // 传进来参数是5,减去4之后  x8 x9 都为1
    0x100986104 <+24>:  mov    x9, x8
    // x9寄存器高32位清0,这里x9值还是1
->  0x100986108 <+28>:  ubfx   x9, x9, #0, #32
    // 这里是 1跟4比较,小于继续执行0x100986118
    0x10098610c <+32>:  cmp    x9, #0x4                  ; =0x4 
    0x100986110 <+36>:  str    x9, [sp]
    // 无符号大于就执行0x100986170,0x100986170跳的就是default条件
    0x100986114 <+40>:  b.hi   0x100986170               ; <+132> at main.m
    //  1<4 执行这一块汇编代码,这里的x8都是一些连续的负数,可以看下图
    0x100986118 <+44>:  adrp   x8, 0
    0x10098611c <+48>:  add    x8, x8, #0x188            ; =0x188 
    // 通过上面可以看出x11的值为1
    0x100986120 <+52>:  ldr    x11, [sp]
    // x11寄存器的值先左移两位得到4,4在加上x8寄存器的值 最后赋值给x10寄存器,x10的值为-84看下图
    0x100986124 <+56>:  ldrsw  x10, [x8, x11, lsl #2]
    // x8加上x10的值赋值给x9寄存器
    0x100986128 <+60>:  add    x9, x8, x10
    // br  b是跳转指令,r就是register,br表示根据寄存器里面的值作为地址值进行跳转,这里表示跳转到x9寄存器中存的地址值
    // 通过计算得到x9的地址为0x0000000100986170,也就是执行default的逻辑
    0x10098612c <+64>:  br     x9
    // 这里是一个case
    0x100986130 <+68>:  adrp   x0, 1
    0x100986134 <+72>:  add    x0, x0, #0xf69            ; =0xf69 
    0x100986138 <+76>:  bl     0x100986588               ; symbol stub for: printf
    0x10098613c <+80>:  b      0x10098617c               ; <+144> at main.m:29:1
    // 一个case
    0x100986140 <+84>:  adrp   x0, 1
    0x100986144 <+88>:  add    x0, x0, #0xf70            ; =0xf70 
    0x100986148 <+92>:  bl     0x100986588               ; symbol stub for: printf
    0x10098614c <+96>:  b      0x10098617c               ; <+144> at main.m:29:1
    // 一个case
    0x100986150 <+100>: adrp   x0, 1
    0x100986154 <+104>: add    x0, x0, #0xf77            ; =0xf77 
    0x100986158 <+108>: bl     0x100986588               ; symbol stub for: printf
    0x10098615c <+112>: b      0x10098617c               ; <+144> at main.m:29:1
    // 一个case
    0x100986160 <+116>: adrp   x0, 1
    0x100986164 <+120>: add    x0, x0, #0xf7e            ; =0xf7e 
    0x100986168 <+124>: bl     0x100986588               ; symbol stub for: printf
    0x10098616c <+128>: b      0x10098617c               ; <+144> at main.m:29:1
    0x100986170 <+132>: adrp   x0, 1
    0x100986174 <+136>: add    x0, x0, #0xf85            ; =0xf85 
    0x100986178 <+140>: bl     0x100986588               ; symbol stub for: printf
    0x10098617c <+144>: ldp    x29, x30, [sp, #0x10]
    0x100986180 <+148>: add    sp, sp, #0x20             ; =0x20 
    0x100986184 <+152>: ret    
image.png image.png image.png

二. switch(下)

// 代码修改如下,查看汇编代码
void funcA(int a){
    switch (a) {
        case 5:
            printf("打坐");
            break;
        case 6:
            printf("加红");
            break;
        case 7:
            printf("加蓝");
            break;
        case 8:
            printf("打怪");
            break;
        default:
            printf("啥都不干");
            break;
    }
}
int main(int argc, char * argv[]) {
    funcA(5);
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

004--选择`funcA:
    0x10026a0f0 <+0>:   sub    sp, sp, #0x20             ; =0x20 
    0x10026a0f4 <+4>:   stp    x29, x30, [sp, #0x10]
    0x10026a0f8 <+8>:   add    x29, sp, #0x10            ; =0x10 
    0x10026a0fc <+12>:  stur   w0, [x29, #-0x4]
->  0x10026a100 <+16>:  ldur   w8, [x29, #-0x4]
    // 通过修改不同case值查看汇编,发现这里减的是最小的case值
    // 编译器在编译funcA的时候必然会知道最小的case值是多少,却不一定知道参数a的值,因为参数a的值有可能是从网络获取
    0x10026a104 <+20>:  subs   w8, w8, #0x5              ; =0x5 
    0x10485e108 <+24>:  mov    x9, x8
    // 高32位清0
    0x10485e10c <+28>:  ubfx   x9, x9, #0, #32
    // x9 与 #0x3进行比较,3是最大case值与最小case值的差值(通过修改不同case值,查看汇编发现)
    // 下面三行的算法: x9为传入的参数减去最小case得到,3为最大case值与最小case的差值,如果x9无符号大于3直接跳转default执行
    0x10485e110 <+32>:  cmp    x9, #0x3                  ; =0x3 
    0x10485e114 <+36>:  str    x9, [sp]
    // 这里请思考一个问题,为什么是无符号大于?不能是有符号吗?
    // 如果x9的值为负数的话,就会小于3,依然会去比较case。如果是如符号大于的话,就会避免负数的情况,因为无符号的话,计算机会把负数转化成一个非常大的数
    0x10485e118 <+40>:  b.hi   0x10485e174               ; <+132> at main.m
    // 如果执行到这里,就说明参数a一定在 最大case 与 最小case之间
    // 下面两行得到地址为0x10485e18c,这个地址就是ret地址0x10485e188后面的4个字节,代码的后面就有0x10485e18c这样一串负数,这串负数就是一张表格
    0x10485e11c <+44>:  adrp   x8, 0
    0x10485e120 <+48>:  add    x8, x8, #0x18c      
    // 直接通过一轮运算,拿出参数减去最小case值之后的值
    0x10485e124 <+52>:  ldr    x11, [sp]
    // 左移2位之后乘以x11的差值,再加上x8的地址,这里实际上计算的是偏移量值赋给x10,这里为什么是左移2位?因为表里是以4个字节为单位的
    0x10485e128 <+56>:  ldrsw  x10, [x8, x11, lsl #2]
    // 通过偏移量找到不同case对应的地址进行跳转执行
    0x10485e12c <+60>:  add    x9, x8, x10
    0x10485e130 <+64>:  br     x9
    0x10485e134 <+68>:  adrp   x0, 1
    0x10485e138 <+72>:  add    x0, x0, #0xf69            ; =0xf69 
    0x10485e13c <+76>:  bl     0x10485e588               ; symbol stub for: printf
    0x10485e140 <+80>:  b      0x10485e180               ; <+144> at main.m:29:1
    0x10485e144 <+84>:  adrp   x0, 1
    0x10485e148 <+88>:  add    x0, x0, #0xf70            ; =0xf70 
    0x10485e14c <+92>:  bl     0x10485e588               ; symbol stub for: printf
    0x10485e150 <+96>:  b      0x10485e180               ; <+144> at main.m:29:1 
    0x10485e154 <+100>: adrp   x0, 1
    0x10485e158 <+104>: add    x0, x0, #0xf77            ; =0xf77 
    0x10485e15c <+108>: bl     0x10485e588               ; symbol stub for: printf
    0x10485e160 <+112>: b      0x10485e180               ; <+144> at main.m:29:1
    0x10485e164 <+116>: adrp   x0, 1
    0x10485e168 <+120>: add    x0, x0, #0xf7e            ; =0xf7e 
    0x10485e16c <+124>: bl     0x10485e588               ; symbol stub for: printf
    0x10485e170 <+128>: b      0x10485e180               ; <+144> at main.m:29:1
    0x10485e174 <+132>: adrp   x0, 1
    0x10485e178 <+136>: add    x0, x0, #0xf85            ; =0xf85 
    0x10485e17c <+140>: bl     0x10485e588               ; symbol stub for: printf
    0x10485e180 <+144>: ldp    x29, x30, [sp, #0x10]
    0x10485e184 <+148>: add    sp, sp, #0x20             ; =0x20 
    0x10485e188 <+152>: ret    

这里能通过表头地址0x10485e18c减去不同偏移量计算,是因为switch代码分支是连续的,创建的这张表里放入偏移量就可以了,最终通过偏移量找到对应的跳转地址(通过内存空间换取时间)

image.png

小结

  • 先将参数减去最小case值得到差值
  • 再获取到最小case值与最大case值的差值范围
  • 如果参数与最小case的差值,超出最小case值与最大case差值范围,就执行default,否则继续执行
  • 参数与最小case值的差值,映射到表中偏移量。通过表中偏移量计算出地址去执行。注意⚠️这个表里存的是偏移差值(原因有两点:1 地址太长 2 地址在运行的时候才会知道虚拟地址,如果这里存偏移地址的话仍然要加aslr,不如直接算偏移值)
Switch

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

三. 编译器优化

创建空工程 002--编译器优化,编译器优化等级可以在这里配置,debug环境下默认不优化,release环境下默认最快最小模式。其中最后面两种优化方式 Fastest, Aggressive... 是比较偏激的优化方式,用内存空间换时间

image.png
// main.m代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

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

// 编译器优化模式为None,汇编代码如下
002--编译器优化`main:
    0x1041be1d4 <+0>:   sub    sp, sp, #0x40             ; =0x40 
    0x1041be1d8 <+4>:   stp    x29, x30, [sp, #0x30]
    0x1041be1dc <+8>:   add    x29, sp, #0x30            ; =0x30 
    0x1041be1e0 <+12>:  stur   wzr, [x29, #-0x4]
    0x1041be1e4 <+16>:  stur   w0, [x29, #-0x8]
    0x1041be1e8 <+20>:  stur   x1, [x29, #-0x10]
    0x1041be1ec <+24>:  mov    w8, #0x1   // 参数a=1
    0x1041be1f0 <+28>:  stur   w8, [x29, #-0x14]
    0x1041be1f4 <+32>:  mov    w8, #0x2  // 参数b=2
    0x1041be1f8 <+36>:  str    w8, [sp, #0x18]
->  0x1041be1fc <+40>:  ldur   w0, [x29, #-0x8]
    0x1041be200 <+44>:  ldur   x1, [x29, #-0x10]
    0x1041be204 <+48>:  adrp   x9, 7
    0x1041be208 <+52>:  add    x9, x9, #0x398            ; =0x398 
    0x1041be20c <+56>:  ldr    x9, [x9]
    0x1041be210 <+60>:  str    w0, [sp, #0x14]
    0x1041be214 <+64>:  mov    x0, x9
...

// 编译器优化模式为Fastest, Smallest,汇编代码如下,发现没有看到参数1 和 参数2 ,没用到的局部变量或者全局变量,编译器会删掉
002--编译器优化`main:
    0x10434a470 <+0>:   stp    x22, x21, [sp, #-0x30]!
    0x10434a474 <+4>:   stp    x20, x19, [sp, #0x10]
    0x10434a478 <+8>:   stp    x29, x30, [sp, #0x20]
    0x10434a47c <+12>:  add    x29, sp, #0x20            ; =0x20 
    0x10434a480 <+16>:  mov    x19, x1
    0x10434a484 <+20>:  mov    x20, x0
->  0x10434a488 <+24>:  nop    
    0x10434a48c <+28>:  ldr    x0, #0x6f14               ; (void *)0x0000000104351430: AppDelegate
    0x10434a490 <+32>:  bl     0x10434a570               ; symbol stub for: objc_opt_class
    0x10434a494 <+36>:  bl     0x10434a528               ; symbol stub for: NSStringFromClass
    0x10434a498 <+40>:  mov    x29, x29
    0x10434a49c <+44>:  bl     0x10434a594               ; symbol stub for: objc_retainAutoreleasedReturnValue
    0x10434a4a0 <+48>:  mov    x21, x0
    0x10434a4a4 <+52>:  mov    x0, x20
    0x10434a4a8 <+56>:  mov    x1, x19
... 
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int sum(int a,int b){
    return  a + b;
}
int main(int argc, char * argv[]) {
    int a = sum(1,2);
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

// 编译器优化模式为Fastest, Smallest,汇编代码如下,没有发现调用sum函数,因为int a = sum(1,2);代码对程序的执行结果没有影响,编译器会优化删掉
002--编译器优化`main:
    0x1045f2470 <+0>:   stp    x22, x21, [sp, #-0x30]!
    0x1045f2474 <+4>:   stp    x20, x19, [sp, #0x10]
    0x1045f2478 <+8>:   stp    x29, x30, [sp, #0x20]
    0x1045f247c <+12>:  add    x29, sp, #0x20            ; =0x20 
    0x1045f2480 <+16>:  mov    x19, x1
    0x1045f2484 <+20>:  mov    x20, x0
->  0x1045f2488 <+24>:  nop    
    0x1045f248c <+28>:  ldr    x0, #0x6f14               ; (void *)0x00000001045f9430: AppDelegate
    0x1045f2490 <+32>:  bl     0x1045f2570               ; symbol stub for: objc_opt_class
    0x1045f2494 <+36>:  bl     0x1045f2528               ; symbol stub for: NSStringFromClass
    0x1045f2498 <+40>:  mov    x29, x29
    0x1045f249c <+44>:  bl     0x1045f2594               ; symbol stub for: objc_retainAutoreleasedReturnValue
...
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

int sum(int a,int b){
    return  a + b;
}
int main(int argc, char * argv[]) {
    int a = sum(1,2);
    NSLog(@"%d",a);
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

// 编译器优化模式为Fastest, Smallest,汇编代码如下
002--编译器优化`main:
    0x102bea438 <+0>:   sub    sp, sp, #0x40             ; =0x40 
    0x102bea43c <+4>:   stp    x22, x21, [sp, #0x10]
    0x102bea440 <+8>:   stp    x20, x19, [sp, #0x20]
    0x102bea444 <+12>:  stp    x29, x30, [sp, #0x30]
    0x102bea448 <+16>:  add    x29, sp, #0x30            ; =0x30 
    0x102bea44c <+20>:  mov    x19, x1
    0x102bea450 <+24>:  mov    x20, x0
    // 直接计算出结果3,把sun函数优化掉
    0x102bea454 <+28>:  mov    w8, #0x3
    // w8寄存器入栈
    0x102bea458 <+32>:  str    x8, [sp]
    0x102bea45c <+36>:  adr    x0, #0x1bcc               ; @"%d"
    0x102bea460 <+40>:  nop    
    // NSLog系统函数,参数通过栈传递
    0x102bea464 <+44>:  bl     0x102bea50c               ; symbol stub for: NSLog
->  0x102bea468 <+48>:  nop 
... 

四. 指针的基础常识

创建空工程 003--指针

// main.m代码如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

void func(){
    int *a; //指针的宽度是8字节
    printf("%lu",sizeof(a)); 
}
int main(int argc, char * argv[]) {
    func();
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 打印结果是8(lldb) 

// 汇编代码如下
003--指针`func:
    0x100f72194 <+0>:  sub    sp, sp, #0x20             ; =0x20 
    0x100f72198 <+4>:  stp    x29, x30, [sp, #0x10]
    0x100f7219c <+8>:  add    x29, sp, #0x10            ; =0x10 
    0x100f721a0 <+12>: adrp   x0, 1
    0x100f721a4 <+16>: add    x0, x0, #0xf8d            ; =0xf8d 
    // sp的值给到x8,x8就相当于栈
->  0x100f721a8 <+20>: mov    x8, sp
    // 指针的宽度是8
    0x100f721ac <+24>: mov    x9, #0x8
    // x9放入栈
    0x100f721b0 <+28>: str    x9, [x8]
    0x100f721b4 <+32>: bl     0x100f725ac               ; symbol stub for: printf
    0x100f721b8 <+36>: ldp    x29, x30, [sp, #0x10]
    0x100f721bc <+40>: add    sp, sp, #0x20             ; =0x20 
    0x100f721c0 <+44>: ret 
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

void func(){
    int *a;
    a = (int *)100;
    a++; //指针的自增自减和执行的数据类型宽度有关
    printf("%d",a);
}
int main(int argc, char * argv[]) {
    func();
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 打印结果是104

// 如果func函数修改如下,打印结果就是101
char *a;
a = (char *)100;
a++;
printf("%d",a);

// func函数修改如下,打印结果就是108
int **a;
a = (int **)100;
a++;
printf("%d",a);

// func函数修改如下,打印结果就是108
int **a;
a = (int **)100;
a = a + 1;
printf("%d",a);

小结

  • 指针的自增自减与编译器有关,所以指针最好使用 a = a + 1,别使用a++
  • 指针的加减都是和指向的数据类型宽度有关
// main.m代码修改如下,来查看指针的减法运算
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

void func(){
    int *a;
    a = (int *)100;
    int *b;
    b = (int *)200;
    int x = a - b;
    printf("%d",x);
}
int main(int argc, char * argv[]) {
    func();
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 100 - 200为 -100,-100再除以整型的宽度结果为 -25
// 打印结果是-25

// 指针也可以用来比大小
int *a;
a = (int *)100;
int *b;
b = (int *)200;
if (a > b) {
    printf("a > b");
} else {
    printf("a <= b");
}

小结

  • 指针的运算单位是执行的数据类型宽度
  • 数据类型是可以强制转换的,结构体的基本类型是不能强制转换的
  • 任何的变量都可以使用取地址符号作为地址,来寻址取值

五. 指针的反汇编形式

// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

void func(){
    int* a;
    int b = 10;
    a = &b;
    printf("hello");
}
int main(int argc, char * argv[]) {
    func();
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

// 查看汇编代码如下
003--指针`func:
    0x10076618c <+0>:  sub    sp, sp, #0x20             ; =0x20 
    0x100766190 <+4>:  stp    x29, x30, [sp, #0x10]
    0x100766194 <+8>:  add    x29, sp, #0x10            ; =0x10 
    0x100766198 <+12>: add    x8, sp, #0x4              ; =0x4 
    // w9的值为0xa,也就是10
    0x10076619c <+16>: mov    w9, #0xa
    // w9入栈,相当于10变成局部变量
    0x1007661a0 <+20>: str    w9, [sp, #0x4]
    // x8指向a的地址,说明栈区现在有一个变量,指向局部变量的地址,所以x8是一个指针变量,从x8~x10这一段是一个地址,保存的是指针
->  0x1007661a4 <+24>: str    x8, [sp, #0x8]
    0x1007661a8 <+28>: adrp   x0, 1
    0x1007661ac <+32>: add    x0, x0, #0xf89            ; =0xf89 
    0x1007661b0 <+36>: bl     0x1007665a8               ; symbol stub for: printf
    0x1007661b4 <+40>: ldp    x29, x30, [sp, #0x10]
    0x1007661b8 <+44>: add    sp, sp, #0x20             ; =0x20 
    0x1007661bc <+48>: ret  
// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

void func(){
    //注意⚠️ int *a == &arr[0] == arr  这几个是对等的关系!
    int arr[5] = {1,2,3,4,5};
    for (int i = 0; i < 5; i++) {
        printf("%d",*(arr + i));
    }
}
int main(int argc, char * argv[]) {
    func();
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
// 打印结果是12345

//func函数这样写,打印结果也是12345
int arr[5] = {1,2,3,4,5};
int * a = arr;

for (int i = 0; i < 5; i++) {
     printf("%d",*(a++));
}

六. 指针的基本用法

// main.m代码修改如下
#import <UIKit/UIKit.h>
#import "AppDelegate.h"

void func(){
    // p1是个指针,c是取p1里面的值
    char * p1;
    char c = *p1; 
}
int main(int argc, char * argv[]) {
    func();
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

// 汇编代码如下
003--指针`func:
    0x1025b61c0 <+0>:  sub    sp, sp, #0x10             ; =0x10 
    // 栈里面作为一个地址取出一个值给到x8,这个值是0
    0x1025b61c4 <+4>:  ldr    x8, [sp, #0x8]
    // 用0地址再去取值,就报了错
->  0x1025b61c8 <+8>:  ldrb   w9, [x8]
    0x1025b61cc <+12>: strb   w9, [sp, #0x7]
    0x1025b61d0 <+16>: add    sp, sp, #0x10             ; =0x10 
    0x1025b61d4 <+20>: ret  

// 运行上面工程直接崩溃,野指针异常Thread 1: EXC_BAD_ACCESS (code=1, address=0x0),崩溃在用0地址去取值报错
// 上面func函数内容修改如下运行
char * p1;
char c = *p1;
char d = *(p1 + 0);

// 汇编代码如下
003--指针`func:
    0x10494a1b4 <+0>:  sub    sp, sp, #0x10             ; =0x10 
    // p1指针指向[sp, #0x8]里面的值,[sp, #0x8]里面值全是0,相当于p1指向(X8)0x0
    // char c = *p1;与 char d = *(p1 + 0); 都是从(X8)0x0里面取值
    0x10494a1b8 <+4>:  ldr    x8, [sp, #0x8]
    // 程序崩溃在这一行
->  0x10494a1bc <+8>:  ldrb   w9, [x8]
    0x10494a1c0 <+12>: strb   w9, [sp, #0x7]
    // (p1 + 0) 还用的是sp+8偏移地址
    0x10494a1c4 <+16>: ldr    x8, [sp, #0x8]
    0x10494a1c8 <+20>: ldrb   w9, [x8]
    0x10494a1cc <+24>: strb   w9, [sp, #0x6]
    0x10494a1d0 <+28>: add    sp, sp, #0x10             ; =0x10 
    0x10494a1d4 <+32>: ret 
// 上面func函数内容修改如下运行
char * p1;  //指针 --> (X8)0x0
char c = *p1; //从[x8]取值
char d = *(p1 + 1); // 从[x8,#0x1]取值,char类型的宽度是1

// 汇编代码如下
003--指针`func:
    0x1047121b4 <+0>:  sub    sp, sp, #0x10             ; =0x10 
    0x1047121b8 <+4>:  ldr    x8, [sp, #0x8]
->  0x1047121bc <+8>:  ldrb   w9, [x8]
    0x1047121c0 <+12>: strb   w9, [sp, #0x7]
    0x1047121c4 <+16>: ldr    x8, [sp, #0x8]
    // 根据数据类型宽度添加地址取值
    0x1047121c8 <+20>: ldrb   w9, [x8, #0x1]
    0x1047121cc <+24>: strb   w9, [sp, #0x6]
    0x1047121d0 <+28>: add    sp, sp, #0x10             ; =0x10 
    0x1047121d4 <+32>: ret 

// 代码修改如下地址就会加4,根据数据类型宽度相加
int * p1;  //指针 --> (X8)0x0
int c = *p1; //[x8]
int d = *(p1 + 1); // [x8,#0x4]

// 代码修改如下地址就会加8
int ** p1;  //指针 --> (X8)0x0
int *c = *p1; //[x8]
int *d = *(p1 + 1); // [x8,#0x8]
// 这里栈需要拉伸多少字节? 上面3个指针分别需要8字节,根据16字节对齐原则这里栈需要拉伸32字节,0x1027fe1b0 <+0>:  sub    sp, sp, #0x20 

// 代码修改如下,4个指针32字节,栈刚好拉伸32字节
int ** p1;  //指针 --> (X8)0x0
int *p2;
int *c = *p1; //[x8]
int *d = *(p1 + 1); // [x8,#0x1]

下面需要注意多级指针的取值

// 上面func函数内容修改如下运行
int ** p1;
char c = **p1;

// 汇编代码如下
003--指针`func:
    0x104dae1bc <+0>:  sub    sp, sp, #0x10             ; =0x10 
    // 从栈里取出的初始值
    0x104dae1c0 <+4>:  ldr    x8, [sp, #0x8]
    // 用[x8]作为地址去取值,下面两次ldr [x8],说明是指针的指针也就是二级指针
->  0x104dae1c4 <+8>:  ldr    x8, [x8]
    0x104dae1c8 <+12>: ldr    w9, [x8]
    0x104dae1cc <+16>: strb   w9, [sp, #0x7]
    0x104dae1d0 <+20>: add    sp, sp, #0x10             ; =0x10 
    0x104dae1d4 <+24>: ret 
// 上面func函数内容修改如下运行
char ** p1; // char** 指向的是 char* 指针,数据宽度是8
char c = *(*(p1 + 2) + 2); //char* 指向的是char,数据宽度是1
char c2 = p1[1][2]; //这一行代码分别偏移#0x8字节 #0x2字节

// 汇编代码如下
003--指针`func:
    0x1022b21bc <+0>:  sub    sp, sp, #0x10             ; =0x10 
    0x1022b21c0 <+4>:  ldr    x8, [sp, #0x8]
    // 第一次加#0x10,因为指针指向的数据类型是char* ,所以是以8为宽度加2*8=16字节,偏移16字节
->  0x1022b21c4 <+8>:  ldr    x8, [x8, #0x10]
    // *(p1 + 2) 取出来之后数据类型是char,所以是以1为宽度加1*2=2字节,偏移8字节
    0x1022b21c8 <+12>: ldrb   w9, [x8, #0x2]
    0x1022b21cc <+16>: strb   w9, [sp, #0x7]
    0x1022b21d0 <+20>: add    sp, sp, #0x10             ; =0x10 
    0x1022b21d4 <+24>: ret 

相关文章

  • <安全攻防之汇编基础>

    &关于汇编基础请点击 <汇编一> <汇编二> <汇编三> <汇编四> <汇编五> <汇编六> <汇编七> <汇编八...

  • 汇编(五)

    编写一个完整的汇编 汇编语言由2类指令组成 汇编指令如mov、add、sub等有对应的机器指令,可以被编译为机器指...

  • 汇编五

    Loop指令 loop指令和cx寄存器配合使用,用于循环操作类似高级语言的for,while使用格式 loop指令...

  • 汇编(五)

    一. 编写一个完整的汇编 汇编语言由2类指令组成:assume cs: codecode segmentmov a...

  • 汇编(五)

    编写一个完整的汇编 汇编语言由2类指令组成 汇编指令如mov、add、sub等有对应的机器指令,可以被编译为机器指...

  • 汇编(五)

    一. switch(上) 我们继续汇编(四)[https://www.jianshu.com/p/accd4749...

  • Swift十 (一: 多态汇编原理? 二: final函数 三

    一: 多态汇编原理?二: final函数三: 初始化器四: 重写override五: 自动继承 一:多态汇编原理?...

  • 汇编五、函数

    函数参数 ARM64下,函数的参数是放到x0到x7(wo-w7)这八个寄存器中 如果寄存器的个数超过八个,就会把超...

  • Swift复习四&五

    内存分析 结构体 类 值类型 引用类型 一: 汇编发展 二: 汇编语言种类 三: 常见指令 四: 寄存器 五: m...

  • iOS逆向学习(arm64汇编入门)

    iOS汇编 iOS汇编语音有很多钟。常见的有8086汇编、arm汇编、x86汇编等等。 arm汇编 iOS的架构从...

网友评论

      本文标题:汇编(五)

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