美文网首页
汇编终章

汇编终章

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

    一. OC的反汇编

    创建空工程 001--OC方法的本质,在工程中创建Person类

    // Person.h文件内容
    #import <Foundation/Foundation.h>
    NS_ASSUME_NONNULL_BEGIN
    @interface Person : NSObject
    @property(nonatomic, copy) NSString * name;
    @property(nonatomic, assign) int age;
    +(instancetype)person;
    @end
    NS_ASSUME_NONNULL_END
    
    // Person.m文件内容
    #import "Person.h"
    @implementation Person
    +(instancetype)person {
        return [[self alloc] init];
    }
    @end
    
    // main.m文件内容如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    #import "Person.h"
    int main(int argc, char * argv[]) {
        Person *p = [Person person];
        return  0;
    }
    
    // 运行工程,查看汇编代码
    001--OC方法的本质`main:
        0x102aaa164 <+0>:   sub    sp, sp, #0x30             ; =0x30 
        0x102aaa168 <+4>:   stp    x29, x30, [sp, #0x20]
        0x102aaa16c <+8>:   add    x29, sp, #0x20            ; =0x20 
        // wzr指令 把0保存入栈
        0x102aaa170 <+12>:  stur   wzr, [x29, #-0x4]
        0x102aaa174 <+16>:  stur   w0, [x29, #-0x8]
        0x102aaa178 <+20>:  str    x1, [sp, #0x10]
        // 下面两行汇编,通过地址拿到一个变量,注意这里拿到的是一个指针
        0x102aaa17c <+24>:  adrp   x8, 7
        0x102aaa180 <+28>:  add    x8, x8, #0x540            ; =0x540 
        // 把x8值取出来给到x0,可以看出x8是一个指针,x0是id类型,也就是结构体指针
    ->  0x102aaa184 <+32>:  ldr    x0, [x8]
        // 下面三行汇编又是拿到一个指针,把值取出来给到x1,x1是sel类型
        0x102aaa188 <+36>:  adrp   x8, 7
        0x102aaa18c <+40>:  add    x8, x8, #0x530            ; =0x530 
        0x102aaa190 <+44>:  ldr    x1, [x8]
        // objc_msgSend函数 第一个参数是id类型,第二个参数是sel类型,两个参数分别是上面的x0 x1
        0x102aaa194 <+48>:  bl     0x102aaa4e4               ; symbol stub for: objc_msgSend
        // 执行到这里,创建的OC实例对象已经返回
        0x102aaa198 <+52>:  mov    x29, x29
        0x102aaa19c <+56>:  bl     0x102aaa508               ; symbol stub for: objc_retainAutoreleasedReturnValue
        // x8相当于取&p,作为objc_storeStrong第一个参数
        0x102aaa1a0 <+60>:  add    x8, sp, #0x8              ; =0x8 
        // sp偏移8放的是p对象
        0x102aaa1a4 <+64>:  str    x0, [sp, #0x8]
        0x102aaa1a8 <+68>:  stur   wzr, [x29, #-0x4]
        0x102aaa1ac <+72>:  mov    x0, x8
        // 下面两行汇编可以看出x1为nil
        0x102aaa1b0 <+76>:  mov    x8, #0x0
        0x102aaa1b4 <+80>:  mov    x1, x8
        // objc_storeStrong强引用函数,Person *p = [Person person];局部变量p就相当于有一个强引用在引用[Person person],这一次的objc_storeStrong不一定是让引用计数count+1,如果这里的代码对外引用count会+1,而[Person person]并没有对外引用
        // 下面栈平衡会让实例对象销毁,objc_storeStrong函数既能让引用计数加一,也能让对象销毁
        0x102aaa1b8 <+84>:  bl     0x102aaa520               ; symbol stub for: objc_storeStrong
        0x102aaa1bc <+88>:  ldur   w0, [x29, #-0x4]
        0x102aaa1c0 <+92>:  ldp    x29, x30, [sp, #0x20]
        0x102aaa1c4 <+96>:  add    sp, sp, #0x30             ; =0x30 
        0x102aaa1c8 <+100>: ret    
    

    通过静态调试来进行计算

    • 通过读x0寄存器拿到类对象Person
    image.png
    • 同样的方法拿到x1寄存器,拿到OC -> person方法
    image.png image.png

    通过对外调试进行验证

    image.png

    小结

    • 通过objc_msgSend汇编代码分析,就能还原出所有OC方法的调用,x1 ~ x8寄存器能还原方法的参数
    • 注意如果有的方法是用C语言写的,不一定能还原出来,但是OC的方法就能还原

    继续执行就跳入[Person person]方法内,这里发现只有objc_alloc_init方法,并没有objc_msgSend方法

    001--OC方法的本质`+[Person person]:
        0x102dbe068 <+0>:  sub    sp, sp, #0x20             ; =0x20 
        0x102dbe06c <+4>:  stp    x29, x30, [sp, #0x10]
        0x102dbe070 <+8>:  add    x29, sp, #0x10            ; =0x10 
        0x102dbe074 <+12>: str    x0, [sp, #0x8]
        0x102dbe078 <+16>: str    x1, [sp]
    ->  0x102dbe07c <+20>: ldr    x0, [sp, #0x8]
        0x102dbe080 <+24>: bl     0x102dbe4c0               ; symbol stub for: objc_alloc_init
        0x102dbe084 <+28>: ldp    x29, x30, [sp, #0x10]
        0x102dbe088 <+32>: add    sp, sp, #0x20             ; =0x20 
        // 把创建好的对象返回
        0x102dbe08c <+36>: b      0x102dbe4cc               ; symbol stub for: objc_autoreleaseReturnValue
    

    早期在iOS11系统还是会调用objc_msgSend方法,可以找一部iOS11系统的手机进行验证,如下图所示

    • objc_alloc函数的返回值是x0寄存器,作为objc_msgSend函数的第一个参数,这时的x0已经是一个实例person对象
    • objc_msgSend函数的第二个参数是下面取到的x1
    • objc_msgSend函数实际上在调用init函数
    截屏2021-04-17 上午11.52.19.png 截屏2021-04-17 上午11.59.47.png

    为什么系统版本不同调用不同呢?
    不同的系统版本在运行时是不同的,iOS11系统优化了alloc方法,alloc方法不再执行objc_msgSend方法,但是init依然执行objc_msgSend方法

    小结

    • 早期的系统执行alloc init方法( [[self alloc] init]; ) 相当于两次消息发送,每次都是调用objc_msgSend方法(可以使用iOS9系统的手机进行验证)
    • iOS11系统对alloc 方法进行了优化,进行一次消息发送,先调用objc_alloc 再调用objc_msgSend
    • iOS14系统对alloc init方法进行了优化,不用调用消息发送,只调用objc_alloc_init
    image.png image.png image.png

    获取 objc4-818.2,我们搜索objc_storeStrong能够看到源码

    void
    // 第一个参数 指向对象的指针,第二个参数 对象
    // 第一个参数实际上是 &p,局部变量的指针 p的地址,第二个参数实际上是nil
    objc_storeStrong(id *location, id obj)
    {
        // prev相当于上面的p对象
        id prev = *location;
        if (obj == prev) {
            return;
        }
        objc_retain(obj);
        *location = obj;
        // 相当于release(p) 把堆内存释放
        objc_release(prev);
    }
    
    // 函数目的是给 一个 strong修饰的对象进行retain+1,对老的对象进行release
    // 这里实际上是这样调用的 objc_storeStrong(&p, nil),最终结果就是让p释放
    

    二. 工具反汇编

    修改main.m代码如下,编译工程 -> Products -> 选中001--OC方法的本质.app -> Show in Finder -> 显示包内容 -> 找到可执行文件 -> 使用Hopper工具打开 用Hopper工具来分析MachO文件

    // main.m文件内容如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    #import "Person.h"
    int main(int argc, char * argv[]) {
        Person *p = [Person person];
        p.name = @"hello";
        p.age = 18;
        return  0;
    }
    
    // 查看main函数汇编代码如下
                         _main:
    0000000100006128         sub        sp, sp, #0x30
    000000010000612c         stp        x29, x30, [sp, #0x20]
    0000000100006130         add        x29, sp, #0x20
    0000000100006134         stur       wzr, [x29, #-0x4]
    0000000100006138         stur       w0, [x29, #-0x8]
    000000010000613c         str        x1, [sp, #0x10]
    // 取地址的指令,右边告诉了是Objc  Class类型的引用,objc_cls_ref_Person中ref表示引用,objc_cls_ref_Person表示Person类对象
    // 这里拿到的是静态区或常量区的东西
    0000000100006140         adrp       x8, #0x10000d000
    0000000100006144         add        x8, x8, #0x550                              ; objc_cls_ref_Person
    0000000100006148         ldr        x0, x8
    // 下面两行是个sel对象,表示类方法 person
    000000010000614c         adrp       x8, #0x10000d000
    0000000100006150         add        x8, x8, #0x530                              ; @selector(person)
    0000000100006154         ldr        x1, x8
    // imp表示实现,stubs__objc_msgSend表示符号名字是 objc_msgSend, 这里表示调用的是一个函数,这个函数名字是objc_msgSend
    0000000100006158         bl         imp___stubs__objc_msgSend
    000000010000615c         mov        x29, x29
    0000000100006160         bl         imp___stubs__objc_retainAutoreleasedReturnValue
    0000000100006164         add        x8, sp, #0x8
    // x0是objc_msgSend方法的返回值,也就是p对象
    0000000100006168         str        x0, [sp, #0x8]
    000000010000616c         ldr        x0, [sp, #0x8]
    // 方法是setName
    0000000100006170         adrp       x9, #0x10000d000
    0000000100006174         add        x9, x9, #0x538                              ; @selector(setName:)
    0000000100006178         ldr        x1, x9
    // 参数是hello
    000000010000617c         adrp       x2, #0x100008000
    0000000100006180         add        x2, x2, #0x28                               ; @"hello"
    0000000100006184         str        x8, sp
    // objc_msgSend方法第一个参数是p对象,第二个参数是setName,第三个参数是hello
    0000000100006188         bl         imp___stubs__objc_msgSend
    000000010000618c         ldr        x0, [sp, #0x8]
    0000000100006190         adrp       x8, #0x10000d000
    0000000100006194         add        x8, x8, #0x540                              ; @selector(setAge:)
    0000000100006198         ldr        x1, x8
    000000010000619c         movz       w2, #0x12
    00000001000061a0         bl         imp___stubs__objc_msgSend
    00000001000061a4         stur       wzr, [x29, #-0x4]
    00000001000061a8         ldr        x0, sp
    00000001000061ac         movz       x8, #0x0
    00000001000061b0         mov        x1, x8
    00000001000061b4         bl         imp___stubs__objc_storeStrong
    00000001000061b8         ldur       w0, [x29, #-0x4]
    00000001000061bc         ldp        x29, x30, [sp, #0x20]
    00000001000061c0         add        sp, sp, #0x30
    00000001000061c4         ret
                            ; endp
    

    编译器是怎么知道objc_cls_ref_Person是一个Person类对象呢?
    这里与常量区有关,在macho中分析汇编代码的时候,通过地址就能找到对应的字符串,根据字符串类型就能判断出具体是什么,这就是反汇编工具能够还原名字的原因。
    Hopper工具,MacOView工具都是根据特定格式读取macho文件,根据macho存放不同位置代表不同意义来进行还原

    Hopper工具中双击objc_cls_ref_Person查看

    image.png image.png

    也可以把可执行文件拖入 MachOView工具分析,这里可以看出来类对象Person在Data段

    image.png

    类方法@selector(person)也可以双击进去查看

    image.png

    MachOView工具中查看

    image.png

    三. Block的反汇编

    3.1 Blokc反汇编一,不引用外部变量

    // main.m文件内容如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    int main(int argc, char * argv[]) {
        void(^block)(void) = ^() {
            NSLog(@"block");
        };
        block();
        return  0;
    }
    
    // 查看main函数汇编代码如下
    001--OC方法的本质`main:
        0x104886100 <+0>:  sub    sp, sp, #0x30             ; =0x30 
        0x104886104 <+4>:  stp    x29, x30, [sp, #0x20]
        0x104886108 <+8>:  add    x29, sp, #0x20            ; =0x20 
        // 保存0入栈
        0x10488610c <+12>: stur   wzr, [x29, #-0x4]
        // 保存w0入栈
        0x104886110 <+16>: stur   w0, [x29, #-0x8]
        // 保存x1入栈
        0x104886114 <+20>: str    x1, [sp, #0x10]
        // x0就是block,使用反汇编工具Hopper,可以直观看到这里是 ___block_literal_global
        0x104886118 <+24>: adrp   x0, 2
        0x10488611c <+28>: add    x0, x0, #0x28             ; =0x28 
    ->  0x104886120 <+32>: bl     0x1048864e4               ; symbol stub for: objc_retainBlock
        0x104886124 <+36>: add    x8, sp, #0x8              ; =0x8 
    ...   
    
    // GlobalBlock的实现,libclosure-74-master 源码中可以查看,block是一个结构体
    struct Block_layout {
        void *isa;
        volatile int32_t flags; // contains ref count
        int32_t reserved;
        BlockInvokeFunction invoke;
        struct Block_descriptor_1 *descriptor;
        // imported variables
    };
    
    image.png 截屏2021-04-17 下午4.28.54.png

    下面找到可执行文件,使用Hopper工具查看汇编代码将会更加直观

    • 双击___block_literal_global如下图
    截屏2021-04-17 下午4.38.59.png
    • 双击block的invoke地址如下图
    image.png
    • 也可以点击block的descriptor地址查看,会发现___block_literal_global的实现与描述信息在一块,与block结构体源码很像

    3.2 Blokc反汇编二,引用外部变量

    // main.m文件内容如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    int main(int argc, char * argv[]) {
        int a = 10;
        void(^block)(void) = ^() {
            NSLog(@"block--%d",a);
        };
        block();
        return  0;
    }
    
    // 查看main函数汇编代码如下
    001--OC方法的本质`main:
        0x1001860b4 <+0>:   sub    sp, sp, #0x60             ; =0x60 
        0x1001860b8 <+4>:   stp    x29, x30, [sp, #0x50]
        0x1001860bc <+8>:   add    x29, sp, #0x50            ; =0x50 
        0x1001860c0 <+12>:  stur   wzr, [x29, #-0x4]
        0x1001860c4 <+16>:  stur   w0, [x29, #-0x8]
        0x1001860c8 <+20>:  stur   x1, [x29, #-0x10]
        // w8的值为10
        0x1001860cc <+24>:  mov    w8, #0xa
        0x1001860d0 <+28>:  stur   w8, [x29, #-0x14]
        // x9指向局部变量所以是一个指针
        0x1001860d4 <+32>:  add    x9, sp, #0x8              ; =0x8 
        // 下面两行汇编就是block 的isa指针,可以看下图
        0x1001860d8 <+36>:  adrp   x10, 2
        0x1001860dc <+40>:  ldr    x10, [x10]
    ->  0x1001860e0 <+44>:  str    x10, [sp, #0x8]
        0x1001860e4 <+48>:  mov    w8, #-0x40000000
        0x1001860e8 <+52>:  str    w8, [sp, #0x10]
        0x1001860ec <+56>:  str    wzr, [sp, #0x14]
        // 下面两行汇编就是block的invoke
        0x1001860f0 <+60>:  adrp   x10, 0
        0x1001860f4 <+64>:  add    x10, x10, #0x144          ; =0x144 
        0x1001860f8 <+68>:  str    x10, [x9, #0x10]
        // 下面两行汇编就是block的descriptor
        0x1001860fc <+72>:  adrp   x10, 2
        0x100186100 <+76>:  add    x10, x10, #0x10           ; =0x10 
        0x100186104 <+80>:  str    x10, [x9, #0x18]
    ... 
    
    image.png

    通过静态分析,取到block的invoke汇编代码

    image.png

    使用Hopper工具来进行分析

    • 双击___main_block_invoke查看block的实现
    image.png
    • 双击 ___block_descriptor_36_e5_v8�?0查看block的描述信息,会发现这一次block的实现与描述并不在一块

    3.2 Blokc反汇编三

    // main.m文件内容如下
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    
    void test3(){NSLog(@"123");}
    void test2(){test3();}
    void test1(){test2();}
    void test(){
        int a = 2;
        if (a < 0) {
            test1();
        }else{
            test2();
        }
    }
    
    int main(int argc, char * argv[]) {
        int a = 10;
        void(^block)(void) = ^() {
            NSLog(@"block--%d",a);
        };
        if (a > 0) {
            test();
        }else{
            test1();
        }
        block();
        return  0;
    }
    

    编译工程找到可执行文件,使用Hopper打开,

    • 这个时候如果使用汇编代码分析的话就比较麻烦,此时我们可是查看流程来分析
    image.png
    • 也可以使用伪代码查看将会直观一些
    image.png

    这里Hopper还原出的伪代码可读性不是很好,使用IDA反汇编,里面的伪代码会更加直观,IDA不仅可以做静态分析,还可以动态调试,只是IDA反汇编工具比较昂贵,这里使用Hopper稳定一些。

    总结

    经过一段时间的学习,逆向学习最重要的汇编篇章结束了。后面我们开始学习加解密篇章,欢迎大家一起探讨...

    相关文章

      网友评论

          本文标题:汇编终章

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