OC反汇编
FYPerson * p = [FYPerson person];//objc_msgSend x0 , x1
image.png
从汇编我们可以看出x0和x1分别是id 和 SEL,也就是oc方法的两个隐藏参数
image.png
我现在手机是iOS14系统,可以看到这个版本已经进行了优化,将直接调用objc_alloc_init,而不是alloc消息发送。
image.png
调用完之后可以看到x0已经是一个oc对象
image.png
这句代码之后会对强引用+1,又会release,我们看看内部如何实现
image.png
给strong修饰的对象进行return+1,给旧对象进行release-1
p的指针 &p, 而x1此时为nil
在方法中就会将p = nil, 然后释放 p指向的内存区域
为什么要释放呢,看这里
image.png
因为这里并没有自动释放池包裹。
通过工具反汇编
image.png通过hopper我们可以清晰的看到方法的调用以及参数
双击
objc_cls_ref_FYPerson
进入image.png
可以看到OBJC_CLASS$_FYPerson 是一个类对象,我们再通过mach-o看一下这个对象的位置
image.png
可以看到这个类对象就存在data段里
同样sel也一样方式查看
image.png
双击person
image.png
image.png
hopper通过读mach-o就能找到字符串,这也就是hopper可以还原的原因
block
在逆向分析时,我们想知道block的实现也就是找到invoke,block可以作为参数,也可以直接执行,但是大部分都是作为参数回调
底层结构
#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
// requires BLOCK_HAS_COPY_DISPOSE
BlockCopyFunction copy;
BlockDisposeFunction dispose;
};
#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
// requires BLOCK_HAS_SIGNATURE
const char *signature;
const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
struct Block_layout {
void *isa;
volatile int32_t flags; // contains ref count
int32_t reserved;
BlockInvokeFunction invoke;
struct Block_descriptor_1 *descriptor;
// imported variables
};
int a = 10;
void(^block)(void) = ^() {
NSLog(@"block--%d",a);
};
block();
image.png
可以看到,使用了外部变量,此时是一个栈block,
调用完objc_retainBlock之后,变为了堆block,这里可以参考我之前的关于oc-block的文章
image.png
通过反汇编工具就可以很方便的分析,如果不通过这个工具就要在mach-o中找到地址,才能知道是个block,所以反汇编工具非常实用,我们接下来看看这个工具的调用流程
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[]) {
// FYPerson * p = [FYPerson person];//objc_msgSend x0 , x1
// p.name = @"boy";
// p.age = 18;
//
int a = 10;
void(^block)(void) = ^() {
NSLog(@"block--%d",a);
};
if (a > 0) {
test();
}else{
test1();
}
block();
return 0;
}
流程图
image.png伪代码
image.png
不好读,可以通过使用IDA,这个东西既可以静态分析,也可以动态调试,但是这个东西很贵。mac的破解版本不太好找,所以用虚拟机,使用windows版本。
关于汇编的内容就先写到这里,想更多的了解汇编推荐看一看 王爽的汇编语言这本书,非常推荐
总结:
- cpu是64位还是32位的,看吞吐量,也就是数据总线的宽度。
- 进制
1024 = 1k 数量单位
1024B = 1KB 容量单位
进制转换 - 寄存器
64位有32个通用寄存器,x、w
pc 指令指针寄存器
sp 栈顶
fp 栈底 x29,函数嵌套的时候通过fp为参照 - 栈
sub sp 拉伸栈空间,16字节对齐
ldr (load register)读
str(store register)取
b 改变pc,bl 改变lr x30
ret 读lr 跳转 - 函数调用栈
拉伸,栈平衡 之后局部变量就没有了
x0寄存器为返回值,参数最好不要超过8个
cpsr 状态寄存器
NZCV, 32位 ,高四位 - if while switch
cmp 减法,不影响目标寄存器,影响NZCV
b.le b.ge等等 - adrp (address page)
寻址
结合add
低12位清零,+ n左移3位 + add
网友评论