iOS逆向 04:OC反汇编(下)

iOS 底层原理 + 逆向 文章汇总

本文主要理解OC对象反汇编,以及block常见类型的反汇编

OC反汇编

创建一个Person类,并在main函数中初始化一个Person对象

@interface Person : NSObject

@property(nonatomic, copy) NSString *name;
@property(nonatomic, assign) int age;

+ (instancetype)person;

@end

@implementation Person

+ (instancetype)person{
    return [[self alloc] init];
}

@end

<!--main.m中-->
int main(int argc, char * argv[]) {
    
    Person *p = [Person person];

    return 0;
}
  • 运行,查看其汇编代码


    OC反汇编-汇编代码

1、静态调试

通过adrp+add获取地址,分别读取x0,x1

  • 读取x0,读出来是Person:x 0x100c68eb0 + po 0x0100c68f98
    OC静态调试-01
  • 读取x1,读取出来是person方法:x 0x100c68e88 + p (SEL)0x01c019aef5
    OC静态调试-02

2、动态调试
通过一步一步执行汇编,来验证x0、x1是否如静态调试的结果一致?

OC动态调试-01

通过调试发现,是一致的,其实这里的x0、x1就是 objc_msgSend的隐藏参数(id self,SEL _cmd)

下面我们继续调试汇编

  • 点击step into,直接进入[Person person]方法(注意:这里不同iOS版本,多看到的汇编代码是有所区别的)

    • 从这里看到ios13.4系统的alloc、init并不会走objc_msgSend


      OC动态调试-02
    • ios11版本中,可以看到objc_msgSend,其本质是在调用init方法
      OC动态调试-03

      动态调试进行验证,结果如下所示,是一致的
      OC动态调试-04

      查看此时的x0,已经是一个实例对象,因为alloc开辟了内存,已经分配了空间,具体的内部实现可以查看这篇文章iOS-底层原理 02:alloc & init & new 源码分析
      OC动态调试-05

      疑问:为什么版本不同,调用不一样呢?
    • 在不同的版本下,系统在运行时是不一样的。因为系统对alloc 、init进行了优化
  • 接着往下看,点击step out 跳出[Person person],此时返回值在x0中


    OC动态调试-06
  • 执行到bl ... objc_storeStrong,objc_storeStrong是OC中用strong修饰的对象底层都是调用这个函数,详情可以看这篇文章iOS-底层原理 10:strong&copy&weak底层分析 以及 方法签名和attribute简写含义
    疑问:我们此时并没有使用strong修饰?:此时的局部变量p在此时就相当于一个强引用,是默认的。且这个方法执行完成后,相当于销毁p

    OC动态调试-07

    查看此时的x0、x1,相当于objc_storeStrong(&p,nil),将nil进行retain,将nil等于p(即 p=nil),p进行释放
    OC动态调试-08

  • 查看objc_storeStrong源码

  • 目的:对一个strong修饰的对象进行retain +1,对一个老的对象进行release

    • 为什么是指针? 因为函数是值传递,而函数内部需要修改p的值
/*
 - id *location 指向对象的指针  本质上是 &p(即局部变量地址)
 - id obj 对象
 目的:对一个strong修饰的对象进行retain +1,对一个老的对象进行release
 为什么是指针? 因为函数是值传递,而函数内部需要修改p的值
 */
void
objc_storeStrong(id *location, id obj)
{
    //prev 相当于p ,因为location是 &p
    id prev = *location;
    //第二个参数 == 第一个参数,直接return
    if (obj == prev) {
        return;
    }
    //retain+1
    objc_retain(obj);
    //修改p的值,指向第二个对象
    *location = obj;
    //释放老对象
    objc_release(prev);
}

相当于

Person *p = p1;
p = p2;//此时p1释放,p2retain+1

所以以上汇编中的objc_storeStrong(&p,nil)的实现代码如下

objc_storeStrong(&p,nil){
    id prev = p;
    if nil == p{
        return;
    }
    objc_retain(nil);
    p = nil;//指针指向nil
    objc_release(p);//释放堆空间
}
  • 下面来进行动态验证,发现Person对象指向nil
    OC动态调试-09

[[self alloc] init] 优化过程

  • 在最初的版本(iOS9)中,相当于两次消息发送 objc_msgSend
  • iOS11版本 是一次消息发送 objc_alloc + objc_msgSend
  • iOS13.5.1以上版本,已经没有objc_msgSend,而是objc_alloc_init
    iOS13版本

以上是LLDB动态调试Person *p = [Person person]; //objc_msgSend x0,x1

通过工具看复杂的OC代码

在上述OC代码的基础上增加一些代码,然后再来静态分析

int main(int argc, char * argv[]) {
    
    Person *p = [Person person]; //objc_msgSend x0,x1
    p.name = @"CJL";
    p.age = 18;

    return 0;
}
  • CMD + B 编译程序,生成mach-o文件,并找到该文件

  • 通过Hopper反汇编mach-o文件,main函数的分析如下


    hopper-01
  • 双击objc_cls_ref_Person,查看p的地址,是000000010000ce88,是在Data段

    hopper-02

    通过MachOView打开mach-o分析,查找000000010000ce88,与Hopper中的显示是一致的
    hopper-03

  • 双击@selector(person),查看person方法的反汇编

    hopper-04

    双击0x10000cc68
    hopper-05

    双击“person”,地址为 0x10000752a
    hopper-06

    在mach中查找0x10000752a,所有方法的name都在CString
    hopper-07

Block反汇编

定义一个block

int main(int argc, char * argv[]) {
    
    void(^block)(void) = ^(){
        NSLog(@"block");
    };
    block();

    return 0;
}

反汇编分析block的目的是想快速定位block的invoke,因为invoke中是实现代码,以下是block的汇编代码

Block反汇编-01

  • 查看x0是什么?:是一个__block_literal_global,是一个全局静态block(即block不引用block外部变量,在编译时期就可以确定内存的分配等操作,存在于可执行文件的常量区),其他详情也可查看iOS-底层原理 30:Block底层原理这篇文章
    Block反汇编-02

    以下是源码中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
};

然后动态调试查看block的内存结构


Block反汇编-03
  • 是否可以通过hopper查看 adrp + add 是一个block?
    答案是可以的


    Block反汇编-04
    • 双击___block_literal_global
      Block反汇编-05
    • 双击0x0000000100006838,查看invoke
      Block反汇编-06
    • 双击0x0000000100008008,查看descriptor,和Block的源码结构类似
      Block反汇编-07

如果block引用了外部变量呢?

定义一个block,其中block引用了外部变量,查看此时的汇编代码

int main(int argc, char * argv[]) {
    int a = 10;
    void(^block)(void) = ^(){
        NSLog(@"block -- %d", a);
    };
    block();

    return 0;
}

1、lldb调试

  • 以下是代码的汇编


    引用外部变量-01
  • 验证是否是block的isa指针


    引用外部变量-02
    • adrp x10, 2 获取指针地址
    • ldr x10, [x10]:取值
  • 查看此时block的内存,找到invoke(由于invoke是代码实现,所以需要由dis -s(将代码的汇编打印出来)查看)

    引用外部变量-03

2、静态分析

  • 通过hopper静态分析如下,以下是main函数的反汇编


    静态分析-01
  • 双击___main_block_invoke,跳转至invoke的具体实现(并没有在main函数中,是单独的实现)

    静态分析-02

  • 双击___block_descriptor_36_e5_v8�?0l,是一个单独的描述

    静态分析-03

总结

  • [[self alloc] init] 优化过程

    • 在最初的版本(iOS9)中,相当于两次消息发送 objc_msgSend

    • iOS11版本 是一次消息发送 objc_alloc + objc_msgSend

    • iOS13.5.1以上版本,已经没有objc_msgSend,而是objc_alloc_init

  • 反汇编分析方式:

    • 通过LLDB动态调试

    • 通过Hopper + MachOView 静态分析

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容