xcode反汇编调试iOS模拟器程序

http://blog.csdn.net/hursing/article/details/8697654

(前两节也许你会看得没劲,可直接先看第四节 (四)自动断点应用之NSNotificationCenter http://blog.csdn.net/hursing/article/details/8752235

iOS模拟器程序的实质就是Mac OS X程序,只不过它需要以模拟器为载体来运行显示。故它的反汇编代码指令都是x86 CPU的,不是真机上的arm指令。

研究模拟器程序的反汇编有两个目的,或叫做好处:

一是为了研究深入到iOS系统的类库,你可以较容易地发现私有API,以及看到系统的实现。

二是,很直接地,模拟器调试比真机快。而且,相信大多数人更懂x86指令,各类高校教材的汇编教程都不是arm指令的吧。


首先问题是如何看到反汇编代码:

操作:Xcode顶部菜单->Product->Debug Workflow->Show Disassembly When Debugging打钩

如果是在调试的过程中打钩,则调试窗口会立刻更新显示反汇编代码,如图:


以上主要是为了看到自己写的代码的反汇编情况,当调试进入不是自己写的代码(没有debug symbol)时,无论是否对这个选项打钩,都会显示成反汇编。

例如,在gdb/lldb调试中,break状态下输入

[plain]  view plain copy
  1. b -[UIView addSubview:]  

再continue之后,如果有发生addSubview操作,不论是自己写的还是系统操作的,都会进入反汇编断点:


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

xcode反汇编调试iOS模拟器程序
(一)查看反汇编
(二)看懂反汇编
(三)查看Objective-C函数与参数
(四)自动断点应用之NSNotificationCenter
(五)调试objc_msgSend函数
(六)函数出入口处的处理与局部变量
(七)Debug与Release的区别

(八)反汇编自己的代码来掌握规则



这里当然不会从零开始讲汇编代码了,零基础的话可以看看 王爽 写的书《汇编语言》,请自己找度娘或谷哥要了。

这一节主要讲讲书上没有的东西。


在xcode中看到的汇编语法不是熟悉的intel格式,而是叫AT&T汇编。基本上只要懂intel,at&t会很快上手。两者的区别请参考这篇文章:

AT&T汇编http://blog.csdn.net/bigloomy/article/details/6581754

例如,在前一节的截图中,会看到movl这样的指令,比熟悉的mov多了个l。这个l表示本次操作是长字型(4字节)数据,操作数是32bit。

这里要注意,lldb和gdb看到的结果会有些差别,下面是对同一个C++函数的截图:

这个是lldb:

这个是gdb:


可以看到lldb会比gdb在AT&T的格式表示上更规范化,gdb只会在一些特定的地方才加l。

lldb以十进制表示常量;左侧内存地址省略了0;

gdb在尖括号内多显示了函数内的偏移。

其实最新版的gdb又会显示得不同,但是apple在强推lldb,不再升级gdb了……


如果实际的代码是在处理浮点数,则在反汇编中会看到xmm0,xmm1等的寄存器,如:

这些寄存器叫xmm寄存器,是为SSE指令服务的,所以也叫sse寄存器。操作他们的指令会有ss后缀。度娘给出了更多信息:http://baike.baidu.com/view/2679650.htm

sse寄存器是一组128bit的寄存器,其值有很多种可能性,可通过gdb查看,详情请查看下一节


总结一下,主要明白这几个概念就行:

1. mov xxx, yyy 表示把xxx的值赋给yyy

2. call xxx 表示调用某函数

3. jmp xxx 表示直接跳转到某内存地址,一般是函数的入口地址

4. ret 表示要返回下一层堆栈了,如果函数有返回值,会存在eax寄存器里。

在Objective-C函数的入口处(第一行)加断点,可用esp指针来探查参数。

以esp为基址,往后的偏移分别是:

0:函数执行完毕后的返回地址(不是返回值的地址哦)

4:对象实例的指针,即self指针

8:selector,实际是一个char数组型的字符串,即char*

12:(如果有)第一个参数

…(前一个参数的基址+前一个参数所占的字节数):(如果有)第n个参数


由此,要调试这样一个函数

[cpp]  view plain copy
  1. - (void)para1:(id)arg1 para2:(CGRect)p2 para3:(CGPoint)p3 para4:(id)p4  
  2. {  
  3.     NSLog(@"para1:(id)arg1 para2:(CGRect)p2 para3:(CGPoint)p3 para4:(id)p4");  
  4. }  
  5.   
  6. - (void)viewDidLoad  
  7. {  
  8.     [self para1:[UIApplication sharedApplication] para2:CGRectMake(10, 20, 30, 40) para3:CGPointMake(50, 60) para4:self];  

时,断点后可在gdb这样调试:

po *(id*)($esp+4)                  // 得到对象实例的描述

p (char*)*(SEL*)($esp+8)     // 得到selector的名字

po *(id*)($esp+12)               // 得到p1的description

p *(CGRect*)($esp+16)       // 得到p2的各个成员值,输出结果为:“(CGRect) $1 = origin=(x=10, y=20) size=(width=30, height=40)”

p *(CGPoint*)($esp+32)   // 因为一个CGRect结构体占16字节(4个float),所以是“+32”,即“+16+16”, 得到p3的各个成员值,输出结果为:“(CGPoint) $4 = (x=50, y=60)”

po *(id*)($esp+40)       // 因为一个CGPoint结构体占8字节(2个float),所以是“+40”,即“+32+8”、得到p4的description

[plain]  view plain copy
  1. (gdb) po *(id*)($esp+4)   
  2. <ViewController: 0x757f8c0>  
  3. (gdb) p (char*)*(SEL*)($esp+8)   
  4. $1 = 0xf37b "para1:para2:para3:para4:"  
  5. (gdb) po *(id*)($esp+12)   
  6. <UIApplication: 0x9250000>  
  7. (gdb) p *(CGRect*)($esp+16)   
  8. $2 = {  
  9.   origin = {  
  10.     x = 10,   
  11.     y = 20  
  12.   },   
  13.   size = {  
  14.     width = 30,   
  15.     height = 40  
  16.   }  
  17. }  
  18. (gdb) p *(CGPoint*)($esp+32)  
  19. $3 = {  
  20.   x = 50,   
  21.   y = 60  
  22. }  
  23. (gdb) po *(id*)($esp+40)   
  24. <ViewController: 0x757f8c0>  


由于在第一行,push了ebp,会导致esp被修改,而第二行又立刻把esp的值赋给ebp,所以在经过函数的这些头部时,可以用ebp类似地访问,不过参数的偏移都需要+4。

下面是如何查看浮点型参数。

浮点型参数会通过SSE寄存器来传递,可以在gdb中这样查看:

[cpp]  view plain copy
  1. p $xmm0  

(lldb不能用上面的命令,暂没去研究用什么替代)

例如调试上例中的

[cpp]  view plain copy
  1. CGRectMake(10, 20, 30, 40)  

编译器会把参数反序送入xmm寄存器组,即40传入xmm0,30传入xmm1……

在CGRectMake加断点,执行到下图中的位置时输入命令


命令结果如下:
[plain]  view plain copy
  1. (gdb) p $xmm0  
  2. $1 = {  
  3.   v4_float = {0, 0, 0, 40},   
  4.   v2_double = {0, 5.4811317061554153e-315},   
  5.   v16_int8 = {0 <repeats 12 times>, 66, 32, 0, 0},   
  6.   v8_int16 = {0, 0, 0, 0, 0, 0, 16928, 0},   
  7.   v4_int32 = {0, 0, 0, 1109393408},   
  8.   v2_int64 = {0, 1109393408},   
  9.   uint128 = 35467839930368  
  10. }  
因为SSE寄存器是128位的,gdb并不知道哪些bit是有意义的,所以列出了一些猜测的结果。

我们传入的是CGFloat,即float,故v4_float = {0, 0, 0, 40} 是有意义的。

当传入的是double型,则v2_double有意义。


知道怎么查看后,先看看有什么实际应用,拿NSNotificationCenter来做实践吧。

首先在某个容易进入的地方加断点,并停在那,例如main函数。在gdb或lldb输入命令,手工添加断点。如下图:

输入的命令如下:

[plain]  view plain copy
  1. b -[NSNotificationCenter addObserver:selector:name:object:]  
  2. b -[NSNotificationCenter addObserverForName:object:queue:usingBlock:]  
然后continue运行。很快,NSNotificationCenter的断点就会触发,是由系统的类调用的。因为是命令行添加的断点,所以不会被xcode记录,下次启动也不会再进入,此时我们需要在xcode里再加断点,如下图,在函数第一行的行号栏单击,标上蓝色断点标记。

然后,在断点上右键单击,选择“Edit Breakpoint...“

按照下图设置好:

点击”Done“后完成(另一个函数的照样做就行)。 最后,把Main函数上的断点去掉,然后重启程序。 这时就会看见命令行狂打印log了。



这log有什么用?呵呵,自己挖掘吧,至少你已经发现好多个non-public API了。


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

xcode反汇编调试iOS模拟器程序
(一)查看反汇编
(二)看懂反汇编
(三)查看Objective-C函数与参数
(四)自动断点应用之NSNotificationCenter
(五)调试objc_msgSend函数
(六)函数出入口处的处理与局部变量
(七)Debug与Release的区别

(八)反汇编自己的代码来掌握规则



反汇编调试objective-c,遇到最多的就是objc_msgSend这函数了,本节主要讲讲它的实现以及调试过程的一些技巧。

以UIWebView为例子,看看它在loadRequest时做了什么。

首先必须明白,原始代码中调用

[cpp]  view plain copy
  1. [uiWebViewInstance loadRequest:request]  
的实质是调用了
[cpp]  view plain copy
  1. objc_msgSend(uiWebViewInstance, "loadRequest:"的selector, request)  

编写一个用到UIWebView的demo,主要功能是打开一个网页。

在main函数加断点,触发后,在命令行输入:

[plain]  view plain copy
  1. b -[UIWebView loadRequest:]  
然后continue,直到触发断点,如下图:


用step over跑到第17行,或直接在那加个断点continue到那。旁边的注释已经显示,call的是存储了objc_msgSend这个函数的偏移地址的代码位置。

17行的前三行,可以看出其动作是往栈上传参数,这三个参数原本是放在edx,ecx,eax的,所以如果参数不多,也可以不用esp为基址来查参数。

在当前的命令行输入

[plain]  view plain copy
  1. register read  
来查看各寄存器的值。lldb会把当前值的意义也输出来。但gdb不会,gdb对应的命令是
[plain]  view plain copy
  1. info registers  

结果如下图:


从ecx的内容可以看出,要call的selector的名字同样是loadRequest:,后面会验证。

接着,step into,可以看到:

那里只有一个跳转命令,继续step into,跳到objc_msgSend真正的地址,看到下图所示的代码:


简单点说,这些代码做了这些事:

1. 先判断传来的对象是否nil,是的话,把返回值(esp+4)置0就返回了,不是就进行下一步。即使是void型的函数,也是被当做有返回值的,只是没用。

2. 查找这个method有没有被cache,有就jmpl到cache了的IMP地址;没有就以selector代表的字符串从这个类的method list里找出method来(第13到20行的循环),cache后再jmpl。如果找不到method就抛出异常然后程序崩溃了。

一个小技巧是,如果想省去点好多次step over来跳过查找的步骤,可以在两个jmpl *%eax的地方加断点,然后run到那里(图中的第31行和38行)。如果传来的对象不是nil,最后都会到这来。此时eax里保存的是函数的地址,即objective-c运行时库定义的IMP(指向函数的指针),jump过去就能到目标函数里了。

另外,在调试反汇编时,step over和step into不要用快捷键,要用调试工具栏上的按钮。不知道为什么按快捷键都等于run了……

接着,在jmpl处step into,就到真实的函数了:


证实了刚才ecx是传了selector名字。


在lldb中搜索

[plain]  view plain copy
  1. image lookup -r -s objc_msgSend  
可以看到:
[plain]  view plain copy
  1. 13 symbols match the regular expression 'objc_msgSend' in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.1.sdk/usr/lib/libobjc.A.dylib:  
  2.         Address: libobjc.A.dylib[0x0001708c] (libobjc.A.dylib.__TEXT.__text + 90252)  
  3.         Summary: libobjc.A.dylib`objc_msgSend        Address: libobjc.A.dylib[0x00017104] (libobjc.A.dylib.__TEXT.__text + 90372)  
  4.         Summary: libobjc.A.dylib`objc_msgSendSuper        Address: libobjc.A.dylib[0x0001716c] (libobjc.A.dylib.__TEXT.__text + 90476)  
  5.         Summary: libobjc.A.dylib`objc_msgSendSuper2        Address: libobjc.A.dylib[0x00017468] (libobjc.A.dylib.__TEXT.__text + 91240)  
  6.         Summary: libobjc.A.dylib`objc_msgSendSuper2_debug        Address: libobjc.A.dylib[0x00017338] (libobjc.A.dylib.__TEXT.__text + 90936)  
  7.         Summary: libobjc.A.dylib`objc_msgSendSuper2_stret        Address: libobjc.A.dylib[0x00017478] (libobjc.A.dylib.__TEXT.__text + 91256)  
  8.         Summary: libobjc.A.dylib`objc_msgSendSuper2_stret_debug        Address: libobjc.A.dylib[0x000172c8] (libobjc.A.dylib.__TEXT.__text + 90824)  
  9.         Summary: libobjc.A.dylib`objc_msgSendSuper_stret        Address: libobjc.A.dylib[0x00017460] (libobjc.A.dylib.__TEXT.__text + 91232)  
  10.         Summary: libobjc.A.dylib`objc_msgSend_debug        Address: libobjc.A.dylib[0x000171dc] (libobjc.A.dylib.__TEXT.__text + 90588)  
  11.         Summary: libobjc.A.dylib`objc_msgSend_fpret        Address: libobjc.A.dylib[0x00017480] (libobjc.A.dylib.__TEXT.__text + 91264)  
  12.         Summary: libobjc.A.dylib`objc_msgSend_fpret_debug        Address: libobjc.A.dylib[0x00017488] (libobjc.A.dylib.__TEXT.__text + 91272)  
  13.         Summary: libobjc.A.dylib`objc_msgSend_noarg        Address: libobjc.A.dylib[0x00017254] (libobjc.A.dylib.__TEXT.__text + 90708)  
  14.         Summary: libobjc.A.dylib`objc_msgSend_stret        Address: libobjc.A.dylib[0x00017470] (libobjc.A.dylib.__TEXT.__text + 91248)  
  15.         Summary: libobjc.A.dylib`objc_msgSend_stret_debug  
objc_msgSend会有好多个版本:

objc_msgSend是普通调用,返回值为void、id、指针、int等32bit能表示的数据的objective-c函数会用到它。

objc_msgSend_stret在返回值为结构体时用到,stret表示structure return

objc_msgSend_fpret在返回值为浮点数时用到,fpret表示floating point return

带Super的表示调用父类的函数。


如上节所说,在obj_msgSend的第一行加自动断点 p (char*)*(SEL*)($esp+8) 就会打印多到你不想看的selector名字。注意这个地方不能去po对象,因为po的实质是调用对象的description,这个会再调用obj_msgSend,无限递归就挂了。


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

xcode反汇编调试iOS模拟器程序
(一)查看反汇编
(二)看懂反汇编
(三)查看Objective-C函数与参数
(四)自动断点应用之NSNotificationCenter
(五)调试objc_msgSend函数
(六)函数出入口处的处理与局部变量
(七)Debug与Release的区别

(八)反汇编自己的代码来掌握规则


引用第二节的例子:


函数的入口处,通常都是把esp的值传给ebp保存,然后下面的操作以ebp为基准做偏移量引用。因为esp作为栈指针,push和pop都会自动修改其值,所以用ebp可以不受影响。

还有的常见情形是开头和结尾对应着

[cpp]  view plain copy
  1. subl $8, %esp  
  2. addl $8, %esp  
这里体现着所谓的“局部变量在栈上分配”原则,说明本函数需要用8字节作为局部变量的保存空间。同时因为ebp已指向未subl前的esp地址,所以往下的操作中,-x($ebp)这样的地方是在操作局部变量,其中x为非负整数。不过需要明确的是,未必x字节是被用完作为局部变量的,编译器出于字节对齐的考虑,如果只用了7字节,那么也会分配到8字节。


首先自己写一段代码,加好断点,分别在Build Configuration为Debug和Release下运行,查看反汇编

[cpp]  view plain copy
  1. - (void)test  
  2. {  
  3.     UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(10.0, 20.0f, 30.1, 40)];  
  4.     [button setTitle:@"test" forState:UIControlStateNormal];  
  5.     [button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];  
  6.     [button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];  
  7.     [self.view addSubview:button];  
  8.     [button release];  
  9. }  
debug下的反汇编结果:

[plain]  view plain copy
  1. 0x6a10:  pushl  %ebp  
  2. 0x6a11:  movl   %esp, %ebp  
  3. 0x6a13:  pushl  %ebx  
  4. 0x6a14:  pushl  %edi  
  5. 0x6a15:  pushl  %esi  
  6. 0x6a16:  subl   $108, %esp  
  7. 0x6a19:  calll  0x6a1e                    ; -[ViewController test] + 14 at ViewController.mm:375  
  8. 0x6a1e:  popl   %eax  
  9. 0x6a1f:  movl   12(%ebp), %ecx  
  10. 0x6a22:  movl   8(%ebp), %edx  
  11. 0x6a25:  leal   -40(%ebp), %esi  
  12. 0x6a28:  movl   $10, %edi  
  13. 0x6a2d:  cvtsi2ss%edi, %xmm0  
  14. 0x6a31:  movl   $20, %edi  
  15. 0x6a36:  cvtsi2ss%edi, %xmm1  
  16. 0x6a3a:  movss  44642(%eax), %xmm2  
  17. 0x6a42:  movl   $40, %edi  
  18. 0x6a47:  cvtsi2ss%edi, %xmm3  
  19. 0x6a4b:  movl   %edx, -16(%ebp)  
  20. 0x6a4e:  movl   %ecx, -20(%ebp)  
  21. 0x6a51:  movl   57106(%eax), %ecx  
  22. 0x6a57:  movl   56146(%eax), %edx  
  23. 0x6a5d:  movl   %ecx, (%esp)  
  24. 0x6a60:  movl   %edx, 4(%esp)  
  25. 0x6a64:  movl   %eax, -44(%ebp)  
  26. 0x6a67:  movss  %xmm2, -48(%ebp)  
  27. 0x6a6c:  movss  %xmm3, -52(%ebp)  
  28. 0x6a71:  movl   %esi, -56(%ebp)  
  29. 0x6a74:  movss  %xmm0, -60(%ebp)  
  30. 0x6a79:  movss  %xmm1, -64(%ebp)  
  31. 0x6a7e:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  32. 0x6a83:  movl   -56(%ebp), %ecx  
  33. 0x6a86:  movl   %ecx, (%esp)  
  34. 0x6a89:  movss  -60(%ebp), %xmm0  
  35. 0x6a8e:  movss  %xmm0, 4(%esp)  
  36. 0x6a94:  movss  -64(%ebp), %xmm1  
  37. 0x6a99:  movss  %xmm1, 8(%esp)  
  38. 0x6a9f:  movss  -48(%ebp), %xmm2  
  39. 0x6aa4:  movss  %xmm2, 12(%esp)  
  40. 0x6aaa:  movss  -52(%ebp), %xmm3  
  41. 0x6aaf:  movss  %xmm3, 16(%esp)  
  42. 0x6ab5:  movl   %eax, -68(%ebp)  
  43. 0x6ab8:  calll  0x6900                    ; CGRectMake(float, float, float, float) at CGGeometry.h:269  
  44. 0x6abd:  subl   $4, %esp  
  45. 0x6ac0:  movl   $64, %eax  
  46. 0x6ac5:  movl   $0, %ecx  
  47. 0x6aca:  movl   -44(%ebp), %edx  
  48. 0x6acd:  leal   58762(%edx), %esi  
  49. 0x6ad3:  leal   -40(%ebp), %edi  
  50. 0x6ad6:  movl   56158(%edx), %ebx  
  51. 0x6adc:  movl   -68(%ebp), %edx  
  52. 0x6adf:  movl   %edx, (%esp)  
  53. 0x6ae2:  movl   %ebx, 4(%esp)  
  54. 0x6ae6:  movl   (%edi), %edx  
  55. 0x6ae8:  movl   %edx, 8(%esp)  
  56. 0x6aec:  movl   4(%edi), %edx  
  57. 0x6aef:  movl   %edx, 12(%esp)  
  58. 0x6af3:  movl   8(%edi), %edx  
  59. 0x6af6:  movl   %edx, 16(%esp)  
  60. 0x6afa:  movl   12(%edi), %edx  
  61. 0x6afd:  movl   %edx, 20(%esp)  
  62. 0x6b01:  movl   %eax, -72(%ebp)  
  63. 0x6b04:  movl   %ecx, -76(%ebp)  
  64. 0x6b07:  movl   %esi, -80(%ebp)  
  65. 0x6b0a:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  66. 0x6b0f:  movl   %eax, -24(%ebp)  
  67. 0x6b12:  movl   -24(%ebp), %eax  
  68. 0x6b15:  movl   -44(%ebp), %ecx  
  69. 0x6b18:  movl   56374(%ecx), %edx  
  70. 0x6b1e:  movl   %eax, (%esp)  
  71. 0x6b21:  movl   %edx, 4(%esp)  
  72. 0x6b25:  movl   -80(%ebp), %eax  
  73. 0x6b28:  movl   %eax, 8(%esp)  
  74. 0x6b2c:  movl   $0, 12(%esp)  
  75. 0x6b34:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  76. 0x6b39:  movl   -24(%ebp), %eax  
  77. 0x6b3c:  movl   -44(%ebp), %ecx  
  78. 0x6b3f:  movl   57074(%ecx), %edx  
  79. 0x6b45:  movl   56274(%ecx), %esi  
  80. 0x6b4b:  movl   %edx, (%esp)  
  81. 0x6b4e:  movl   %esi, 4(%esp)  
  82. 0x6b52:  movl   %eax, -84(%ebp)  
  83. 0x6b55:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  84. 0x6b5a:  movl   -44(%ebp), %ecx  
  85. 0x6b5d:  movl   56386(%ecx), %edx  
  86. 0x6b63:  movl   -84(%ebp), %esi  
  87. 0x6b66:  movl   %esi, (%esp)  
  88. 0x6b69:  movl   %edx, 4(%esp)  
  89. 0x6b6d:  movl   %eax, 8(%esp)  
  90. 0x6b71:  movl   $0, 12(%esp)  
  91. 0x6b79:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  92. 0x6b7e:  movl   -24(%ebp), %eax  
  93. 0x6b81:  movl   -16(%ebp), %ecx  
  94. 0x6b84:  movl   -44(%ebp), %edx  
  95. 0x6b87:  movl   56454(%edx), %esi  
  96. 0x6b8d:  movl   56394(%edx), %edi  
  97. 0x6b93:  movl   %eax, (%esp)  
  98. 0x6b96:  movl   %edi, 4(%esp)  
  99. 0x6b9a:  movl   %ecx, 8(%esp)  
  100. 0x6b9e:  movl   %esi, 12(%esp)  
  101. 0x6ba2:  movl   $64, 16(%esp)  
  102. 0x6baa:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  103. 0x6baf:  movl   -16(%ebp), %eax  
  104. 0x6bb2:  movl   -44(%ebp), %ecx  
  105. 0x6bb5:  movl   56350(%ecx), %edx  
  106. 0x6bbb:  movl   %eax, (%esp)  
  107. 0x6bbe:  movl   %edx, 4(%esp)  
  108. 0x6bc2:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  109. 0x6bc7:  movl   -24(%ebp), %ecx  
  110. 0x6bca:  movl   -44(%ebp), %edx  
  111. 0x6bcd:  movl   56354(%edx), %esi  
  112. 0x6bd3:  movl   %eax, (%esp)  
  113. 0x6bd6:  movl   %esi, 4(%esp)  
  114. 0x6bda:  movl   %ecx, 8(%esp)  
  115. 0x6bde:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  116. 0x6be3:  movl   -24(%ebp), %eax  
  117. 0x6be6:  movl   -44(%ebp), %ecx  
  118. 0x6be9:  movl   56138(%ecx), %edx  
  119. 0x6bef:  movl   %eax, (%esp)  
  120. 0x6bf2:  movl   %edx, 4(%esp)  
  121. 0x6bf6:  calll  0xe56a                    ; symbol stub for: objc_msgSend  
  122. 0x6bfb:  addl   $108, %esp  
  123. 0x6bfe:  popl   %esi  
  124. 0x6bff:  popl   %edi  
  125. 0x6c00:  popl   %ebx  
  126. 0x6c01:  popl   %ebp  
  127. 0x6c02:  ret      
release下的反汇编结果:
[plain]  view plain copy
  1. 0x5310:  pushl  %ebp  
  2. 0x5311:  movl   %esp, %ebp  
  3. 0x5313:  pushl  %ebx  
  4. 0x5314:  pushl  %edi  
  5. 0x5315:  pushl  %esi  
  6. 0x5316:  subl   $44, %esp  
  7. 0x5319:  calll  0x531e                    ; -[ViewController test] + 14 at ViewController.mm:377  
  8. 0x531e:  popl   %edi  
  9. 0x531f:  movl   46610(%edi), %eax  
  10. 0x5325:  movl   45650(%edi), %ecx  
  11. 0x532b:  movl   %ecx, 4(%esp)  
  12. 0x532f:  movl   %eax, (%esp)  
  13. 0x5332:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  14. 0x5337:  movl   $1092616192, -32(%ebp)  
  15. 0x533e:  movl   $1101004800, -28(%ebp)  
  16. 0x5345:  movl   $1106300109, -24(%ebp)  
  17. 0x534c:  movl   $1109393408, -20(%ebp)  
  18. 0x5353:  movl   45662(%edi), %ecx  
  19. 0x5359:  movsd  -32(%ebp), %xmm0  
  20. 0x535e:  movsd  -24(%ebp), %xmm1  
  21. 0x5363:  movsd  %xmm1, 16(%esp)  
  22. 0x5369:  movsd  %xmm0, 8(%esp)  
  23. 0x536f:  movl   %ecx, 4(%esp)  
  24. 0x5373:  movl   %eax, (%esp)  
  25. 0x5376:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  26. 0x537b:  movl   %eax, %esi  
  27. 0x537d:  movl   45878(%edi), %eax  
  28. 0x5383:  leal   48262(%edi), %ecx  
  29. 0x5389:  movl   %ecx, 8(%esp)  
  30. 0x538d:  movl   %eax, 4(%esp)  
  31. 0x5391:  movl   %esi, (%esp)  
  32. 0x5394:  movl   $0, 12(%esp)  
  33. 0x539c:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  34. 0x53a1:  movl   46578(%edi), %eax  
  35. 0x53a7:  movl   45778(%edi), %ecx  
  36. 0x53ad:  movl   %ecx, 4(%esp)  
  37. 0x53b1:  movl   %eax, (%esp)  
  38. 0x53b4:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  39. 0x53b9:  movl   45890(%edi), %ecx  
  40. 0x53bf:  movl   %eax, 8(%esp)  
  41. 0x53c3:  movl   %ecx, 4(%esp)  
  42. 0x53c7:  movl   %esi, (%esp)  
  43. 0x53ca:  movl   $0, 12(%esp)  
  44. 0x53d2:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  45. 0x53d7:  movl   45898(%edi), %eax  
  46. 0x53dd:  movl   45958(%edi), %ecx  
  47. 0x53e3:  movl   %ecx, 12(%esp)  
  48. 0x53e7:  movl   8(%ebp), %ebx  
  49. 0x53ea:  movl   %ebx, 8(%esp)  
  50. 0x53ee:  movl   %eax, 4(%esp)  
  51. 0x53f2:  movl   %esi, (%esp)  
  52. 0x53f5:  movl   $64, 16(%esp)  
  53. 0x53fd:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  54. 0x5402:  movl   45854(%edi), %eax  
  55. 0x5408:  movl   %eax, 4(%esp)  
  56. 0x540c:  movl   %ebx, (%esp)  
  57. 0x540f:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  58. 0x5414:  movl   45858(%edi), %ecx  
  59. 0x541a:  movl   %esi, 8(%esp)  
  60. 0x541e:  movl   %ecx, 4(%esp)  
  61. 0x5422:  movl   %eax, (%esp)  
  62. 0x5425:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  63. 0x542a:  movl   45642(%edi), %eax  
  64. 0x5430:  movl   %eax, 4(%esp)  
  65. 0x5434:  movl   %esi, (%esp)  
  66. 0x5437:  calll  0xa78e                    ; symbol stub for: objc_msgSend  
  67. 0x543c:  addl   $44, %esp  
  68. 0x543f:  popl   %esi  
  69. 0x5440:  popl   %edi  
  70. 0x5441:  popl   %ebx  
  71. 0x5442:  popl   %ebp  
  72. 0x5443:  ret    
单从行数上看,debug是127行,release是72行,差距很大。这当然是编译器优化的结果,特别对于一些分支多的、带循环的源码时,反汇编和源码的实际动作执行顺序都可能不同。

可以看到:

debug版在0x6ab8调用CGRectMake之前做了好多多余动作,往返访问xmm寄存器。debug版的浮点数,像30.1这样的数值才被真正当做浮点,10.0和20.0f都被当整数了。而release版都是立即数。反汇编中会把float所占的4字节空间的值显示为十进制数,需要自行转换实际的值,转换方法参见http://blog.csdn.net/hursing/article/details/8688862

CGRectMake的实质是个inline函数,在debug版还存在单独的函数代码入口位置,但release版就没有了。

函数的开头表示debug版要用108字节作为局部变量空间,而release版只需要44字节。

这样的区别还有很多,可通过反汇编自己的代码来观察到。当调试进入系统的代码时,可以看出都是release版的,即debug版链接的仍是release版的静态库。


掌握了基本技巧后,基本上已不难理解所有的反汇编结果。授之以鱼不如授之以渔:

通过观察自己写的代码的反汇编来掌握各种代码的反汇编结果,从而逆向推测系统代码的源码。

调试自己写的代码时,可以不断切换查看源码和反汇编来定位代码执行到何处


这里用分别用两个很简单的C++和Objective-C类来做示例:

[cpp]  view plain copy
  1. class TestC {  
  2.     int m_var;  
  3.       
  4. public:  
  5.     int getVar();  
  6.     void setVar(int var);  
  7. };  
  8.   
  9. @interface TestOC : NSObject  
  10. {  
  11.     int m_var;  
  12. }  
  13.   
  14. - (void)setVar:(int)var;  
  15.   
  16. @end  
  17.   
  18. int g_var = 0;  
  19. int *g_pVar = &g_var;  
  20.   
  21. void TestC::setVar(int var)  
  22. {  
  23.     int l_var = 0;  
  24.     l_var = var;  
  25.     m_var = var;  
  26.     g_var = var;  
  27.     *g_pVar = var;  
  28. }  
  29.   
  30. @implementation TestOC  
  31.   
  32. - (void)setVar:(int)var  
  33. {  
  34.     int l_var = 0;  
  35.     l_var = var;  
  36.     m_var = var;  
  37.     g_var = var;  
  38.     *g_pVar = var;  
  39. }  
  40.   
  41. @end  
在外部,这样调用:

[cpp]  view plain copy
  1. - (void)viewDidLoad  
  2. {  
  3.     TestC test;  
  4.     test.setVar(100);  
  5.     TestOC *t = [[TestOC new] autorelease];  
  6.     [t setVar:100];  
  7.     .......  
分别在两个类的setVar加断点,看反汇编结果。

[cpp]  view plain copy
  1. WebViewResearch`TestC::setVar(int) at TestClass.mm:15:  
  2. 0x8bda:  pushl  %ebp  
  3. 0x8bdb:  movl   %esp, %ebp  
  4. 0x8bdd:  calll  0x8be2                    ; TestC::setVar(int) + 8 at TestClass.mm:15  
  5. 0x8be2:  popl   %eax  
  6. 0x8be3:  movl   12(%ebp), %ecx  
  7. 0x8be6:  movl   8(%ebp), %edx  
  8. 0x8be9:  movl   %ecx, (%edx)  
  9. 0x8beb:  movl   %ecx, 30406(%eax)  
  10. 0x8bf1:  movl   29170(%eax), %eax  
  11. 0x8bf7:  movl   %ecx, (%eax)  
  12. 0x8bf9:  popl   %ebp  
  13. 0x8bfa:  ret    
[cpp]  view plain copy
  1. WebViewResearch`-[TestOC setVar:] at TestClass.mm:25:  
  2. 0x8bfb:  pushl  %ebp  
  3. 0x8bfc:  movl   %esp, %ebp  
  4. 0x8bfe:  pushl  %esi  
  5. 0x8bff:  calll  0x8c04                    ; -[TestOC setVar:] + 9 at TestClass.mm:29  
  6. 0x8c04:  popl   %eax  
  7. 0x8c05:  movl   29372(%eax), %edx  
  8. 0x8c0b:  movl   16(%ebp), %ecx  
  9. 0x8c0e:  movl   8(%ebp), %esi  
  10. 0x8c11:  movl   %ecx, (%esi,%edx)  
  11. 0x8c14:  movl   %ecx, 30372(%eax)  
  12. 0x8c1a:  movl   29136(%eax), %eax  
  13. 0x8c20:  movl   %ecx, (%eax)  
  14. 0x8c22:  popl   %esi  
  15. 0x8c23:  popl   %ebp  
  16. 0x8c24:  ret     
以上是release版的结果。可以看到,编译器做了优化,没有实际用处的局部变量l_var直接被省略到了,既不为它分配空间,连对它的赋值语句都没要。

对于操作成员变量、全局变量,没法直观地看出来,需要自己计算好各个偏移,才会明白那些带括号的间接寻址的操作。这些麻烦的事情可以借助IPA Pro来看(后面会有一系列来讲)。


下面是调用两个类的setVar函数的反汇编语句。

[cpp]  view plain copy
  1. 0x441e:  leal   -16(%ebp), %eax  
  2. 0x4421:  movl   %eax, (%esp)  
  3. 0x4424:  movl   $100, 4(%esp)  
  4. 0x442c:  calll  0x8bda                    ; TestC::setVar(int) at TestClass.mm:15  
[cpp]  view plain copy
  1. 0x445b:  movl   45819(%esi), %ecx  
  2. 0x4461:  movl   %ecx, 4(%esp)  
  3. 0x4465:  movl   %eax, (%esp)  
  4. 0x4468:  movl   $100, 8(%esp)  
  5. 0x4470:  calll  0x97c0                    ; symbol stub for: objc_msgSend  

可以看到C++能更直接地看出下一步的去向,OC则需要知道是哪个类的对象以及Selector(可用register read来查看)。

对比类的函数以及被调用处的行数,也可以间接地表明,Objective-C的效率会比C++慢一点,但也差不了多少。


Apple也有一些帮助反汇编调试的文档:

http://developer.apple.com/library/ios/#technotes/tn2239/_index.html


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Xcode是苹果公司开发的一款集成开发环境(IDE),用于开发iOS和macOS应用程序。要创建iOS模拟器,您可以按照以下步骤进行操作: 1. 首先,确保您已经安装了Xcode。您可以在App Store中搜索Xcode并进行安装。 2. 打开Xcode后,选择“创建新项目”或使用快捷键Command + Shift + N。 3. 在弹出的窗口中,选择“iOS”选项,并选择您想要创建的应用程序类型,例如“Single View App”。 4. 在下一步中,您需要为您的项目选择名称、组织标识符和语言。 5. 在“设备”下拉菜单中,选择“iOS Simulator”。 6. 在“iOS Simulator”下拉菜单中,选择您想要模拟的iOS设备版本。 7. 点击“下一步”并选择您想要保存项目的位置。 8. 最后,点击“创建”按钮,Xcode将为您创建一个新的iOS模拟器项目。 现在,您可以使用Xcode创建的iOS模拟器来运行和测试您的应用程序。请注意,您可以在Xcode中选择不同的iOS设备版本来模拟不同的设备和操作系统环境。 #### 引用[.reference_title] - *1* [xcode ios 模拟器安装运行](https://blog.csdn.net/weixin_50408263/article/details/126480094)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Xcode模拟iPhone教程!](https://blog.csdn.net/weixin_43219158/article/details/87627576)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值