Skip to content

Commit ab9183a

Browse files
author
dev-aozhimin
committedJul 13, 2017
[Add] Add _nbs_getInstanceImpOf
1 parent df2780f commit ab9183a

File tree

1 file changed

+87
-3
lines changed

1 file changed

+87
-3
lines changed
 

‎README.md

+87-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
## 前言
1010

11-
有关 APM 的技术文章非常多,但大部分文章都只是浅尝辄止,并未对实现细节进行深挖。本文旨在通过剖析 SDK 具体实现细节,揭露知名 APM 厂商的 iOS SDK 背后的秘密。
11+
有关 APM 的技术文章非常多,但大部分文章都只是浅尝辄止,并未对实现细节进行深挖。本文旨在通过剖析 SDK 具体实现细节,揭露知名 APM 厂商的 iOS SDK 背后的秘密。分析的 APM SDK 有**听云**, **OneAPM****Firebase Performance Monitoring** 等。
1212

1313
## 页面渲染时间
1414

@@ -188,6 +188,90 @@ void +[_priv_NBSUIAgent hook_viewDidLoad:](void * self, void * _cmd, void * arg2
188188
189189
```
190190

191-
`hook_viewDidLoad:` 方法中的参数 `arg2` 即是要 hook 的 `ViewController` 的类,获取 `arg2` 的类名并赋给 `rbx` 寄存器,然后利用 `rbx` 构造字符串 `nbs_%s_viewDidLoad`,如 `nbs_XXViewController_viewDidLoad`,获得该字符串的 selector 后赋给 `var_C0`,下面几句中的 `__NSConcreteStackBlock` 是创建的存储栈的 block 对象,这个 block 之后会通过 `imp_implementationWithBlock` 方法获取到 IMP 函数指针,`_nbs_Swizzle_orReplaceWithIMPs` 是实现方法交换的函数,参数依次为:`arg2``ViewController` 的类;`@selector(viewDidLoad)``viewDidLoad` 的 selector;var_C0 是 `nbs_%s_viewDidLoad` 的 selector,`r14` 是第二个 `__NSConcreteStackBlock` 的 IMP;var_D0 是第一个 `__NSConcreteStackBlock` 的 IMP。
191+
`hook_viewDidLoad:` 方法中的参数 `arg2` 即是要 hook 的 `ViewController` 的类,获取 `arg2` 的类名并赋给 `rbx` 寄存器,然后利用 `rbx` 构造字符串 `nbs_%s_viewDidLoad`,如 `nbs_XXViewController_viewDidLoad`,获得该字符串的 selector 后赋给 `var_C0`,下面几句中的 `__NSConcreteStackBlock` 是创建的存储栈的 block 对象,这个 block 之后会通过 `imp_implementationWithBlock` 方法获取到 IMP 函数指针,`_nbs_Swizzle_orReplaceWithIMPs` 是实现方法交换的函数,参数依次为:`arg2``ViewController` 的类;`@selector(viewDidLoad)``viewDidLoad` 的 selector;`var_C0``nbs_%s_viewDidLoad` 的 selector,`r14` 是第二个 `__NSConcreteStackBlock` 的 IMP;`var_D0` 是第一个 `__NSConcreteStackBlock` 的 IMP。
192192

193-
`hook_viewDidLoad:` 的整个逻辑大致清楚了,不过这里有个问题为什么不直接交换两个 IMP,而是要先构造两个 block,然后交换两个 block 的 IMP呢?原因是需要将 `ViewController` 的父类也就是 `class_getSuperclass` 的结果作为参数传递给交换后的方法,这样交换的两个 selector 签名的参数个数不一致,需要通过构造 block 去巧妙的解决这个问题,而事实上第二个 `__NSConcreteStackBlock` 的执行的就是 `_priv_NBSUIHookMatrix``nbs_jump_viewDidLoad:superClass:` 方法,正如之前所说的,这个方法的参数中有 `superClass`,至于为什么需要这个参数,稍后再做介绍。
193+
`hook_viewDidLoad:` 的整个逻辑大致清楚了,不过这里有个问题为什么不直接交换两个 IMP,而是要先构造两个 block,然后交换两个 block 的 IMP呢?原因是需要将 `ViewController` 的父类也就是 `class_getSuperclass` 的结果作为参数传递给交换后的方法,这样交换的两个 selector 签名的参数个数不一致,需要通过构造 block 去巧妙的解决这个问题,而事实上第一个 `__NSConcreteStackBlock` 的执行的就是 `_priv_NBSUIHookMatrix``nbs_jump_viewDidLoad:superClass:` 方法,正如之前所说的,这个方法的参数中有 `superClass`,至于为什么需要这个参数,稍后再做介绍。
194+
195+
为什么第二个 `__NSConcreteStackBlock` 的执行的是 `nbs_jump_viewDidLoad:superClass:` 方法呢?取消勾选 Hopper 的 `Remove potentially dead code` 选项,代码如下:
196+
197+
```
198+
void +[_priv_NBSUIAgent hook_viewDidLoad:](void * self, void * _cmd, void * arg2) {
199+
rsi = _cmd;
200+
rdi = self;
201+
r12 = _objc_msgSend;
202+
rax = [_priv_NBSUIHookMatrix class];
203+
rsi = @selector(nbs_jump_viewDidLoad:superClass:);
204+
rdi = rax;
205+
var_D8 = _nbs_getInstanceImpOf();
206+
rdi = arg2;
207+
rsi = @selector(viewDidLoad);
208+
var_D0 = _nbs_getInstanceImpOf();
209+
rbx = class_getName(arg2);
210+
r14 = class_getSuperclass(arg2);
211+
LODWORD(rax) = 0x0;
212+
rax = [NSString stringWithFormat:@"nbs_%s_viewDidLoad", rbx];
213+
rax = [rax retain];
214+
var_B8 = rax;
215+
var_C0 = NSSelectorFromString(rax);
216+
var_60 = 0xc0000000;
217+
var_5C = 0x0;
218+
var_58 = ___37+[_priv_NBSUIAgent hook_viewDidLoad:]_block_invoke;
219+
var_50 = ___block_descriptor_tmp;
220+
var_48 = var_D8;
221+
var_40 = @selector(viewDidLoad);
222+
var_38 = var_D0;
223+
var_30 = r14;
224+
r12 = objc_retainBlock(__NSConcreteStackBlock);
225+
var_D0 = imp_implementationWithBlock(r12);
226+
r13 = _objc_release;
227+
rax = [r12 release];
228+
var_A8 = 0xc0000000;
229+
var_A4 = 0x0;
230+
var_A0 = ___37+[_priv_NBSUIAgent hook_viewDidLoad:]_block_invoke_2;
231+
var_98 = ___block_descriptor_tmp47;
232+
var_90 = rbx;
233+
var_88 = var_D8;
234+
var_80 = @selector(viewDidLoad);
235+
var_78 = r14;
236+
var_70 = arg2;
237+
rbx = objc_retainBlock(__NSConcreteStackBlock);
238+
r14 = imp_implementationWithBlock(rbx);
239+
rax = [rbx release];
240+
rax = _nbs_Swizzle_orReplaceWithIMPs(arg2, @selector(viewDidLoad), var_C0, r14, var_D0);
241+
rax = [var_B8 release];
242+
rsp = rsp + 0xb8;
243+
rbx = stack[2047];
244+
r12 = stack[2046];
245+
r13 = stack[2045];
246+
r14 = stack[2044];
247+
r15 = stack[2043];
248+
rbp = stack[2042];
249+
return;
250+
}
251+
```
252+
253+
再来看 `_nbs_getInstanceImpOf` 的代码:
254+
255+
```
256+
void _nbs_getInstanceImpOf() {
257+
rax = class_getInstanceMethod(rdi, rsi);
258+
method_getImplementation(rax);
259+
return;
260+
}
261+
```
262+
`_nbs_getInstanceImpOf` 函数的作用很明显,获取 `rdi` 类中 `rsi` selector 的 IMP,读者会发现在 `hook_viewDidLoad:` 方法中共调用了两次 `_nbs_getInstanceImpOf`,第一次 `rdi``_priv_NBSUIHookMatrix` 类,`rdx``@selector(nbs_jump_viewDidLoad:superClass:)`,第二次 `rdi``ViewController` 类,`rdx``@selector(viewDidLoad)`
263+
264+
接下来看第一个 `__NSConcreteStackBlock`,也就是会调用 `nbs_jump_viewDidLoad:superClass:` 的 block,代码如下:
265+
266+
```
267+
int ___37+[_priv_NBSUIAgent hook_viewDidLoad:]_block_invoke(int arg0, int arg1) {
268+
r8 = *(arg0 + 0x20);
269+
rax = *(arg0 + 0x28);
270+
rdx = *(arg0 + 0x30);
271+
rcx = *(arg0 + 0x38);
272+
rax = (r8)(arg1, rax, rdx, rcx, r8);
273+
return rax;
274+
}
275+
```
276+
277+
`r8` 寄存器是 `nbs_jump_viewDidLoad:superClass:` 的 IMP,这段代码只是调用这个 IMP。IMP 函数的参数与 `nbs_jump_viewDidLoad:superClass:` 相同。

0 commit comments

Comments
 (0)
Please sign in to comment.