[iOS逆向工程] 在汇编语言调试中获取当前实例句柄

在分析Safari行为的时候想到要用objective-c的特性随时可以语义化的查看一下UIView的各种状态,比如在UIView方法内部设了个断点,想看一下当前视图结构。只要得到当前实例的句柄就可以了。查了一些资料,记录一下。

函数参数的传递


iOS Simulator里应用是跑在32bits模式下的(在Activity Monitor可以看到),依据<<Mac OS Debug Magic>>里关于Intel 32bits参数传递的定义:

Table 2 : Accessing parameters on Intel 32-bit
(64bits是先使用通用寄存器来传递参数,不足时再使用其它方式)
What GDB Syntax
return address *(int*)$esp
first parameter *(int*)($esp+4)
second parameter *(int*)($esp+8)
... and so on  
如果已经进入到函数体,也就是过函数参数处理部分(开场位置, prologue), 参数就要到调用帧寄存器(frame register)ebp中去获取了。

Table 3 : Accessing parameters after the prologue

What GDB Syntax
previous frame *(int*)$ebp
return address *(int*)($ebp+4)
first parameter *(int*)($ebp+8)
second parameter *(int*)($ebp+12)
... and so on  
返回值存放在EAX寄存器中。


既然知道参数是如何存储的,那C++和Objective-C是如何传递当前实例的句柄的呢?

答案在同一份文档的这里:

当在汇编语言下调试Cocoa代码,请记住以下运行时特性:

  • The Objective-C compiler adds two implicit parameters to each method, the first of which is a pointer to the object being called (self).

  • The second implicit parameter is the method selector (_cmd). In Objective-C this is of type SEL; in GDB you can print this as a C string.

  • The Objective-C runtime dispatches methods via a family of C function. The most commonly seen is objc_msgSend, but some architectures use objc_msgSend_stret for methods which returns structures, and some architectures useobjc_msgSend_fpret for methods that return floating point values. There are also equivalent functions for calling super (objc_msgSendSuper and so on).

  • The first word of any Objective-C object (the isa field) is a pointer to the object's class.

就是函数调用时第一个参数就是操作对应的对象(如果自己的方法,就是self了), 第二参数就是selector method.

实战

分析一下CALayer::addSublayer中的执行情况,
[iOS逆向工程] 在汇编语言调试中获取当前实例句柄


在这里使用lldb memory指令:
(lldb) me read -s4 -fx -c4 `$esp`
0xb01dd28c: 0x0000a07c 0x0929e170 0x04e7aad3 0x1386d750

再调用一个检查一下句柄:
(lldb) po [0x0929e170 description] 或者
(lldb) po [`*(int*)($esp+4)` description]
(id) $102 = 0x0929e210 <CALayer: 0x929e170>
 (*注意, `不是单引号!  如果这个对象是UIView, 使用recursiveDescription你就知道它的威力了! )

(lldb) po [*(0x0929e170) isHidden]
(id) $105 = 0x00000001 [no Objective-C description available]

这样就可以调用其它方法进行操作了。

再次验证一下第二个参数:
(lldb) me read -s4 -fs 0x4e7aad3 
或者 
(lldb) me read -s4 -fs `*(int*)($esp+8)`

0x04e7aad3: "addSublayer:"

0x04e7aae0: NULL

用一个检查UIView层次的实例来展示它的强大:

(lldb) po [[`*(int*)($esp+4)` superview] recursiveDescription]

(id) $7 = 0x0719f940 <UIWebSelectionView: 0x107d74e0; frame = (0 0; 0 0); layer = <CALayer: 0x107d7620>>

   | <UIView: 0x107d7770; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x107d77d0>>

   | <UIWebSelectionOutline: 0x759b340; frame = (-2 -2; 4 4); userInteractionEnabled = NO; layer = <CALayer: 0x75963e0>>

   |    | <UIView: 0x759b3f0; frame = (0 0; 0 0); layer = <CALayer: 0x759d580>>

   |    | <UIView: 0x759da60; frame = (0 0; 0 0); layer = <CALayer: 0x759bfb0>>

   |    | <UIView: 0x759be40; frame = (0 0; 0 0); layer = <CALayer: 0x759db30>>

   |    | <UIView: 0x718d150; frame = (0 0; 0 0); layer = <CALayer: 0x719f650>>

附注:
*如果是C++, 则第一个参数就是this,所以也很方便查。而函数返回值放在EAX中,在ret位置设个断点,也可以很方便查看。
*这个调试过程最好使用LLDB, 它对Objective-C的支持要好很多。

Reference:


转载请注明出处: http://blog.csdn.net/horkychen


上一篇:在 vue cli3 的项目中配置双服务,模拟 ajax 分页请求


下一篇:反射与工厂设计模式 | 带你学《Java语言高级特性》之八十三