指针偏移&读取bits信息& class_rw_t

指针偏移

普通内存读取

截屏2020-09-16下午3.34.47.png
截屏2020-09-16下午3.34.36.png

分析:

  • a和b的值都指向了10 ,但是地址不一样,这就是所谓的值拷贝 属于浅拷贝
  • a和b的地址之间相差4个字节,取决于a、和b 的类型

对象内存读取

截屏2020-09-16下午3.43.27.png

截屏2020-09-16下午4.01.36.png

分析:

  • person 、person2 为指针 分别指向两次[LGPerson alloc]开辟的内存地址
  • & person、& person2 分别指向 person 、person2 的指针地址 、为二级指针

数组 通过指针偏移拿到数据

 int a[4] = {1,2,3,4};
 int *p = a;
 NSLog(@"%p - %p - %p - %p", &a, &a[0], &a[1],&a[2]);
 NSLog(@"%p -- %p - %p - %p", p, p+1,  p+2 , p+3);

打印
截屏2020-09-16下午4.21.08.png

分析:

  • &a 和 & &a[0] 地址一样。 数组名的地址拿的 元素首地址
  • 元素之间相差4字节,取决于元素类型
  • 可以通过首地址+偏移量 取出其他元素,偏移量为数组下标

源码分析 objc781

搜索 objc_class : 发现有两份
objc-runtime-old.h(已经废弃不研究)


/// objc-runtime-old.h
struct objc_class : objc_object {
    Class superclass;
    const char *name;
    uint32_t version;
    uint32_t info;
    uint32_t instance_size;
    struct old_ivar_list *ivars;
    struct old_method_list **methodLists;
    Cache cache;
    struct old_protocol_list *protocols;
    // CLS_EXT only
    const uint8_t *ivar_layout;
    struct old_class_ext *ext;
...... 后面省略

objc-runtime-new.h 主要分析bits

/// objc-runtime-new.h
struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const {
        return bits.data();
    }
...... 后面省略

计算cache在 objc_class结构体的位置

  • isa指针 8字节 (objc_class 继承自 objc_object 所以 他派生下来有isa)
  • cache 分析看源码 cache_t是什么(去掉 static修饰的属性,已经方法 不参与计算 太长了下面只贴需要计算的)
#if defined(__arm64__) && __LP64__/// 64 真机
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_HIGH_16
#elif defined(__arm64__) && !__LP64__///非64位的真机
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_LOW_4
#else ///模拟器或者macOS
#define CACHE_MASK_STORAGE CACHE_MASK_STORAGE_OUTLINED
#endif

 ·····省略了static修饰的属性 及 方法
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
    explicit_atomic _buckets; /// 结构体指针类型 8
    explicit_atomic _mask;/// mask_t uint32_t int 类型 4
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
    explicit_atomic _maskAndBuckets;/// uintptr_t  u  unsigned long 8
    mask_t _mask_unused; /// mask_t 4
 #elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
    // _maskAndBuckets stores the mask shift in the low 4 bits, and
    // the buckets pointer in the remainder of the value. The mask
    // shift is the value where (0xffff >> shift) produces the correct
    // mask. This is equal to 16 - log2(cache_size).
    explicit_atomic _maskAndBuckets; uintptr_t long 8
    mask_t _mask_unused; mask_t 4
 
#if __LP64__
    uint16_t _flags;  ///uint 16_t  short   2
#endif
    uint16_t _occupied;  ///2

当前环境为 CACHE_MASK_STORAGE_OUTLINED
_buckets:结构体指针类型所以8字节
_maskuint32_t int 类型 4字节
_flags2字节
_occupied2字节
所以 cache:占用 16 字节

  • 通过上面计算 我们只需要 将 objc_class 首地址 平移 8+8+16+32 的位置 就可以拿到bits内存地址 下面我们在源码环境下 lldb进行调试

源码环境下 lldb进行调试 读取 class_rw_t信息

准备一段代码


@interface LGPerson : NSObject

@property(nonatomic,copy)NSString * name;
@property(nonatomic,copy) NSString * sex;
 
-(void)sayHello;
+(void)sayHappy;

@end

@implementation LGPerson
-(void)sayHello{
    NSLog(@"sayHello");
}
+(void)sayHappy{
    NSLog(@"sayHappy");
}


断言在此


截屏2020-09-17上午12.19.16.png
lldb分析objc_class 里 bits 实例方法.jpg

疑问:我们看到 实例方法 在 类对象里全部打印出来 那么 类方法呢?在元类里吗?快去试试吧

lldb查询类方法的归属.jpg

struct class_rw_t

struct class_rw_t {
.......前面已省略 


    const class_ro_t *ro() const {
        auto v = get_ro_or_rwe();
        if (slowpath(v.is())) {
            return v.get()->ro;
        }
        return v.get();
    }

    void set_ro(const class_ro_t *ro) {
        auto v = get_ro_or_rwe();
        if (v.is()) {
            v.get()->ro = ro;
        } else {
            set_ro_or_rwe(ro);
        }
    }

    const method_array_t methods() const {
        auto v = get_ro_or_rwe();
        if (v.is()) {
            return v.get()->methods;
        } else {
            return method_array_t{v.get()->baseMethods()};
        }
    }

    const property_array_t properties() const {
        auto v = get_ro_or_rwe();
        if (v.is()) {
            return v.get()->properties;
        } else {
            return property_array_t{v.get()->baseProperties};
        }
    }

    const protocol_array_t protocols() const {
        auto v = get_ro_or_rwe();
        if (v.is()) {
            return v.get()->protocols;
        } else {
            return protocol_array_t{v.get()->baseProtocols};
        }
    }
};

上面我们 我们知道了方法的归属 那么 属性 及成员变量 又再哪里呢?

我们看到了
首先继续上面在元类的环境中的调试 const property_array_t properties()


元类环境下 的properties() ro().jpg

在元类的环境下我们并未发现什么线索 但是我猜 属性一定在 properties() 里面 那 成员变量呢?为了更好的验证 我在继续添加两个成员变量 回到类对象 继续探索


@interface LGPerson : NSObject
{
    int _age;
    NSString * _hobby;
}

@property(nonatomic,copy)NSString * name;
@property(nonatomic,copy) NSString * sex;
 
-(void)sayHello;

+(void)sayHappy;

重新断言 调试

lldb分析 属性及成员变量的归属 2.jpg

struct class_ro_t

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
    _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];

    _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            return _swiftMetadataInitializer_NEVER_USE[0];
        } else {
            return nil;
        }
    }

    method_list_t *baseMethods() const {
        return baseMethodList;
    }

    class_ro_t *duplicate() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
            return ro;
        } else {
            size_t size = sizeof(*this);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            return ro;
        }
    }
};

看到这里我们也知道了lldb的强大

LLDB命令(来自ios应用逆向与安全)

  • po: 是 “primobject”的缩写,用于打印OC对象。在这里直接打印了该0C类的类名。如
    果程序重载了 description方法,这里打印的就是description返回的值。
    x/s: x/s $x1 通过 x/s $x1命令将寄存器x1处的内存以字符串的形式显示出来,其对应 的就是调用的方法名
  • bt:通过动态调试还可以获取程序调用的堆栈信息
  • registerread:读取所有寄存器的值。
  • registerread $x0 : 读取某个寄存器的值。
  • registerwriteSx5 1:修改某个寄存器的值。
  • si:跳到当前指令的内部。
  • ni:跳过当前指令。
  • finish:返回上层调用栈。
  • threadreturn:不再执行下面的代码,直接从当前调用栈返回一个值。
  • brlist:査看当前断点列表。
  • brdel:删除当前的所有断点。
  • brdel 1.1.1:删除指定编号的断点。
  • brdis 2.1:使断点2.1失效。
  • brenable2.1:使断点2.1 生效。
  • watchpointsetexpression-wwrite―0xl01801a48:给某个地址设置观察断点,当对该地址的内存进行写操作时就会触发断点。
  • x/10xg 0xl01801a48:读取目标地址的内存指令。这里的 “x”代表用十六进制来显结 果,“g”代表giantword(8字节)大小。所以,“x/10xg”就是用十六进制显7K0x101801a48
    个64位的元素内容。常见的大小格式为“b-byte”( 1字节)“h-halfworrl”
    所指空间的 10(2字节)“w- word”(4字节)“g-giantword”(8字节)。
  • dis-a$pc:反汇编指定地址。这里是pc寄存器所对应的地址。
  • f2:切换到当前调用栈为2的位置,也就是bt中的frame #2。
  • threadinfo:输出当前线程的信息。
  • bptrace-cxxx:满足某个条件之后程序才会中断。

LLDB还有很多命令,我们不可能全部记下来,但是要记住其中两个查找命令,即help和appropos。直接在LLDB命令交互模式下输入“help”,就可以查看所有的LLDB命令。如果想查看某个命令的详细用法,可以使用“help命令名”的形式。例如看看thread命令,

(lldb) help thread
(lldb) help thread
Commands for operating on one or more threads in the current process. 
Syntax: thread
The following subcommands are supported:
 ....

如果没有记住某个命令,只记得其中的关键字,或者想根据关键字去査找相关的LLDB命
可以使用apropos来搜索相关的命令信息。搜索“watch”的相关信息,具体如下

(lldb) apropos watch
The following commands may relate to 'watch':

watchpoint
watchpoint command executed when
watchpoint command add whenever the
--Commands for operating on watchpoints.
Commands for adding, removing and examining LLDB commands
the watchpoint is hit (watchpoint 'commmands').
Add a set of LLDB commands to a watchpoint, to be executed
watchpoint is hit.
watchpoint command delete -- Delete the set of commands from a watchpoint.
....

你可能感兴趣的:(指针偏移&读取bits信息& class_rw_t)