汇编探究二


将汇编代码还原成高级代码

使用工具idahopper,我这里使用的是ida,ida有64位和32位两个版本,这里使用64位

将编译包拖到ida中,按住control+滚动鼠标可控制窗口大小

funcAdd函数

一步一步粗略转换成OC语言

int var4 = wo;
int var8 = w1;
printf("test") // 获取常量test
int global = x30; // 全能局变量
int w1 = var4;
int w8 = var8;
int w8 = w1 + w8;
int w1 = x30;
int w8 = w8 + w1;
int varc = wo;
return x8

自下而上逐渐精简最后变为

printf("test") // 获取常量test
return var4 + var8 + global;

可见这个funcAdd函数是一个含有两个基础类型变量返回值也为基础类型的一个函数

还原高级代码的过程,通常并不知道参数类型,这要参照上下文及使用到的寄存器来粗略判断

还原后要判断还原的是否正确可以将还原后的包拖到ida中,查看汇编代码和还原前的是否的一致

常用标识指令

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

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

if语句

int global = 16;

void function(int a, int b) {
    if (a > global) {
        global = a;
    } else {
        global = b;
    }
}

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

循环

do while循环

int sum = 0;
int i = 0;
do {
    sum += i;
} while (i < 100);

while循环

int sum = 0;
int i = 0;
while (i < 100) {
sum += i;
}

for循环

 for (int i = 0; i < 100; i++) {
    printf("hello world");
}

switch循环

  • 在switch语句的分支比较少的时候(少于4的时候没有意义)没有必要使用此结构,相当于if
  • 各个分支常量的差值较大的时候,编译器会在效率还是内存进行取舍,这个时候编译器还是会编译成类似于if,else的结构
  • 在分支比较多的时候:在编译的时候会生成一个表(跳转表每个地址四个字节)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void func (int a) {
switch (a) {
case 1:
printf("this is one");
break;
case 2:
printf("this is two");
break;
case 3:
printf("this is three");
break;
default:
printf("this is else");
break;
}
}


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

void func (int a) {
    switch (a) {
        case 1:
            printf("this is one");
            break;
        case 2:
            printf("this is two");
            break;
        case 3:
            printf("this is three");
            break;
        case 4:
            printf("this is four");
            break;
        case 5:
            printf("this is five");
            break;
        default:
            printf("this is else");
            break;
    }
}

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

编译器优化

上面看到的这些汇编代码,都是在DEBUG调试状态下的代码,可以发现会有很多冗余代码,寄存器和内存的各种读取等,其实在RELEASE模式下🍎对我们的汇编代码做了一层优化,如果想在DEBUG模式下也做一层优化需要额外设置

测试代码

int main(int argc, char * argv[]) {
    int a = 10;
    int b = 20;
    int c = a + b;
    NSLog(@"%d",c);

    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

优化前

优化后

寄存器与多线程

注意,寄存器的读写其实是线程不安全的,但是,我们的操作系统在切换线程的时候,对当前线程在使用的寄存器进行了一层保护,操作系统内部有一个结构体,记录在使用的寄存器状态,当再次线程切换回来的时候会去获取使用到的寄存器的值。而这一切操作系统已经帮我们搞定了,不需要人为干预

指针相关

编译器决定了指针不能做乘法和除法。指针加减的结果,其实就是由针指向的数据类型宽度决定的,它的运算单位是数据类型的宽度

int main(int argc, char * argv[]) {
    int *a;
       int b = 6;
    a = &b;
    }
}

int main(int argc, char * argv[]) {
    char *a;
    char b = *a;
    return 1;
}


如有任何疑问或问题请联系我:fishnewsdream@gmail.com,欢迎交流,共同提高!

Objective-C/Swift技术开发交流群201556264,讨论何种技术并不受限,欢迎各位大牛百家争鸣!

微信公众号OldDriverWeekly,欢迎关注并提出宝贵意见

老司机iOS周报,欢迎关注或订阅

刚刚在线工作室,欢迎关注或提出建设性意见!

刚刚在线论坛, 欢迎踊跃提问或解答!

如有转载,请注明出处,谢谢!

本站总访问量 本文总阅读量