Skip to content

Instantly share code, notes, and snippets.

@xhruso00
Created February 19, 2019 22:17
Show Gist options
  • Save xhruso00/69313d5492142ebe86a4da69091fa102 to your computer and use it in GitHub Desktop.
Save xhruso00/69313d5492142ebe86a4da69091fa102 to your computer and use it in GitHub Desktop.
点我达iOS首页
iOS Block探究
2017/12/28
###
iOS Block探究
Block 结构体
对应的结构体定义如下:
struct Block_descriptor {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
const char *signature; // IFF (1<<30)
} *descriptor;
struct Block_layout {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
isa 指针(Block也是一个对象);
flags 用bit位表示一些Block的附加信息(判断Block类型、判断Block引用计数、判断Block是否需要执行辅助函数);
reserved 保留变量;
invoke 函数指针,指向具体的 Block 实现的函数调用地址;
descriptor Block 的附加描述信息; Block详述参考
我们需要注意的是:
`void (*invoke)(void *, ...)`
指向 Block 具体实现地址
`const char *signature`
表示 Block 函数签名的字符串
`在64位系统上,指针类型的大小是8个字节,而int是4个字节`
Block 的内存管理
在 Objective-C 语言中,一共有 3 种类型的 Block:
_NSConcreteGlobalBlock 全局的静态不会访问外部局部变量(无外部变量或者全局变量)
_NSConcreteStackBlock 保存在栈中,当函数返回时会被销毁
_NSConcreteMallocBlock 保存在堆中,当引用计数为 0 时会被销毁
如下示例:
#import "ViewController.h"
@interface ViewController ()
@property (copy, nonatomic) void (^tryBlock)(void);
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//__block ViewController *weakSelf = self;
self.tryBlock = ^{
// NSLog(@"%@", weakSelf.tryBlock); // _NSConcreteMallocBlock
};
NSLog(@"%@", self.tryBlock); // _NSConcreteGlobalBlock
}
MRC 模式下 Block 的内存地址在栈区,防止被销毁而使用修饰词copy,将 Block 存放到堆区(Copy修饰词将 Block 从栈区复制到堆区)
ARC 模式下系统会默认对 Block 进行copy操作
Block 逆向处理
使用hopper对 App 的二进制文件进行反汇编获得 Block 的实现代码
0000000100026190 ldr x19, [x8, #0x878] "setConfirmeBlock:",@selector(setConfirmeBlock:)
0000000100026194 mov x0, x20
0000000100026198 bl imp___stubs__objc_retain
000000010002619c mov x20, x0
00000001000261a0 mov x2, sp
00000001000261a4 mov x0, x21
00000001000261a8 mov x1, x19
00000001000261ac bl imp___stubs__objc_msgSend
添加断点触发:
(lldb) po $x0
<RiderAlertView: 0x15f843080; frame = (0 0; 375 667); layer = <CALayer: 0x17003d4e0>>
(lldb) po (char *)$x1
"setConfirmeBlock:"
(lldb) po $x2
<__NSStackBlock__: 0x16fde16f0>
根据指针<__NSStackBlock__: 0x16fde16f0>打印内存地址:
(lldb) memory read --size 8 --format x 0x16fde16f0
0x16fde16f0: 0x000000019bf4d088 0x00000000c2000000
0x16fde1700: 0x00000001000421f4 0x000000010072aab0
0x16fde1710: 0x000000015f822170 0x000000015c6e5c70
0x16fde1720: 0x000000015f822170 0x00000001005f15b5
函数指针(invoke)的地址是在第 16 个字节之后,并占用 8 个字节
void *isa; // 8
int flags; // 4
int reserved; // 4
void (*invoke)(void *, ...);
得到函数的地址是0x00000001000421f4,使用下面的命令对地址反汇编可以获得与hopper中完全一样的代码!
(lldb) disassemble --start-address 0x00000001000421f4
要查看 Block 的参数需要找到const char *signature的地址,signature的地址是在descriptor下偏移两个unsiged long和两个指针后的地址(32 个字节后)
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
const char *signature; // IFF (1<<30)
根据descriptor(0x000000010072aab0)获取signature(0x00000001006b5196)
(lldb) memory read --size 8 --format x 0x000000010072aab0
0x10072aab0: 0x0000000000000000 0x0000000000000030 (unsiged long)
0x10072aac0: 0x00000001000422a8 0x00000001000422d0 (两个指针)
0x10072aad0: 0x00000001006b5196 0x0000000000000200
0x10072aae0: 0x0000000000000000 0x0000000000000020
打印字符串类型:
(lldb) p (char *)0x00000001006b5196
(char *) $3 = 0x00000001006b5196 "v8@?0"
(lldb) po [NSMethodSignature signatureWithObjCTypes:"v8@?0"]
<NSMethodSignature: 0x17427be80>
number of arguments = 1
frame size = 224
is special struct return? NO
return value: -------- -------- -------- --------
type encoding (v) 'v'
flags {}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
memory {offset = 0, size = 0}
argument 0: -------- -------- -------- --------
type encoding (@) '@?'
flags {isObject, isBlock}
modifiers {}
frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
memory {offset = 0, size = 8}
针对获取的type encoding字段对比Type Encoding 官方文档我们所分析的Block没有返回值,也没有参数!!
获取 Block 代码源示例
常规 Block
00000001000261f4 dd 0xa9be4ff4 ; DATA XREF=-[MoreViewController loginOutButtonClicked:]+120
00000001000261f8 stp x29, x30, [sp, #0x10]
00000001000261fc add x29, sp, #0x10
0000000100026200 mov x19, x0
0000000100026204 ldr x0, [x19, #0x20]
0000000100026208 adrp x8, #0x1008ae000
000000010002620c ldr x1, [x8, #0x9c8]
0000000100026210 bl imp___stubs__objc_msgSend
0000000100026214 mov x29, x29
0000000100026218 bl imp___stubs__objc_retainAutoreleasedReturnValue
000000010002621c mov x20, x0
0000000100026220 adrp x8, #0x1008ae000
0000000100026224 ldr x1, [x8, #0xa48]
0000000100026228 bl imp___stubs__objc_msgSend
000000010002622c mov x0, x20
0000000100026230 bl imp___stubs__objc_release
0000000100026234 ldr x0, [x19, #0x20]
0000000100026238 adrp x8, #0x1008ad000
000000010002623c ldr x1, [x8, #0xa68]
0000000100026240 bl imp___stubs__objc_msgSend
0000000100026244 mov x29, x29
0000000100026248 bl imp___stubs__objc_retainAutoreleasedReturnValue
000000010002624c mov x20, x0
0000000100026250 ldr x2, [x19, #0x28]
0000000100026254 adrp x8, #0x1008ae000
0000000100026258 ldr x1, [x8, #0xa50]
000000010002625c bl imp___stubs__objc_msgSend
0000000100026260 mov x0, x20
0000000100026264 bl imp___stubs__objc_release
0000000100026268 adrp x8, #0x1008c7000
000000010002626c ldr x0, [x8, #0x1a8]
0000000100026270 adrp x8, #0x1008ad000
0000000100026274 ldr x1, [x8, #0x2a0]
0000000100026278 bl imp___stubs__objc_msgSend
000000010002627c mov x29, x29
0000000100026280 bl imp___stubs__objc_retainAutoreleasedReturnValue
0000000100026284 mov x19, x0
0000000100026288 adrp x8, #0x1008ad000
000000010002628c ldr x1, [x8, #0x558]
0000000100026290 movz w2, #0x4f
0000000100026294 bl imp___stubs__objc_msgSend
0000000100026298 mov x0, x19
000000010002629c ldp x29, x30, [sp, #0x10]
00000001000262a0 ldp x20, x19, [sp]!, #0x20
00000001000262a4 b imp___stubs__objc_release
这段 Block 代码在hopper中是不会生成伪代码的,这就需要我们手动操作了
连接lldb
在bl imp___stubs__objc_msgSend处添加断点
使用po 命令
就上述代码添加断点:
(lldb) br s -a 0x00000000000cc000+0x0000000100026210
Breakpoint 1: where = Rider`_mh_execute_header + 132092, address = 0x00000001000f2210
(lldb) br s -a 0x00000000000cc000+0x0000000100026228
Breakpoint 2: where = Rider`_mh_execute_header + 132116, address = 0x00000001000f2228
(lldb) br s -a 0x00000000000cc000+0x0000000100026240
Breakpoint 3: where = Rider`_mh_execute_header + 132140, address = 0x00000001000f2240
(lldb) br s -a 0x00000000000cc000+0x000000010002625c
Breakpoint 4: where = Rider`_mh_execute_header + 132168, address = 0x00000001000f225c
(lldb) br s -a 0x00000000000cc000+0x0000000100026278
Breakpoint 5: where = Rider`_mh_execute_header + 132196, address = 0x00000001000f2278
(lldb) br s -a 0x00000000000cc000+0x0000000100026294
Breakpoint 6: where = Rider`_mh_execute_header + 132224, address = 0x00000001000f2294
触发第一个断点,执行命令
(lldb) c
Process 10503 resuming
Process 10503 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x00000001000f2210 Rider`_mh_execute_header + 156176
Rider`_mh_execute_header:
-> 0x1000f2210 <+156176>: bl 0x10069660c ; symbol stub for: objc_msgSend
0x1000f2214 <+156180>: mov x29, x29
0x1000f2218 <+156184>: bl 0x10069666c ; symbol stub for: objc_retainAutoreleasedReturnValue
0x1000f221c <+156188>: mov x20, x0
(lldb) po $x0
<MoreViewController: 0x14d6dbd10>
(lldb) po (char *)$x1
"getMainViewController"
(lldb) po $x2
208
(lldb) po $x3
<nil>
(lldb) po $x4
<nil>
触发第二个断点
(lldb) c
Process 10503 resuming
Process 10503 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
frame #0: 0x00000001000f2228 Rider`_mh_execute_header + 156200
Rider`_mh_execute_header:
-> 0x1000f2228 <+156200>: bl 0x10069660c ; symbol stub for: objc_msgSend
0x1000f222c <+156204>: mov x0, x20
0x1000f2230 <+156208>: bl 0x10069663c ; symbol stub for: objc_release
0x1000f2234 <+156212>: ldr x0, [x19, #0x20]
(lldb) po $x0
<MainViewController: 0x14d5b2020>
(lldb) po (char *)$x1
"setOrderListForceRefresh"
(lldb) po $x2
还原成OC代码应该是: [MoreViewController getMainViewController] 与 [MainViewController setOrderListForceRefresh]
这些方法调用在MoreViewController某个 Block 方法中,所以完整的方法实现应该是这样[[self getMainViewController] setOrderListForceRefresh]。
以此类推直到触发第四个断点时打印数据如下:
(lldb) po (char *)$x1
"loginOutRequestWithRequestWidgetId:"
(lldb) po $x0
<MoreViewModel: 0x17067bd80>
(lldb) po $x2
<Button: 0x14d5da9f0; baseClass = UIButton; frame = (0 185; 375 45); clipsToBounds = YES; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x1744366c0>>
(lldb) po $x3
<nil>
触发所有的断点得到OC代码:
[[MoreViewController getMainViewController] setOrderListForceRefresh]
[MoreViewModel loginOutRequestWithRequestWidgetId: Button]
[UMengManager shared] sendUMengEventRequest:79]
对比源码:
alertview.confirmeBlock = ^{
[[self getMainViewController] setOrderListForceRefresh];
[self.viewModel loginOutRequestWithRequestWidgetId:sender];
[[UMengManager shared] sendUMengEventRequest:UMengEventExitLogin];
};
参考: http://www.swiftyper.com/2016/12/16/debuging-objective-c-blocks-in-lldb/ http://linlexus.com/implementation_of_block/
-->
© 2015 点我达iOS TOP
首页
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment