当前位置:主页 > 查看内容

【iOS内功】使用Hopper定位疑难问题

发布时间:2021-04-30 00:00| 位朋友查看

简介:前言 如果Crash堆栈,最后一个方法是在自己工程的源码,问题都比较好解决。但如果最后落到了系统库的方法里,并且系统库还没开源,这时候要定位原因就非常困难了。于是我们只能读汇编代码,或反汇编分析伪代码。下面介绍Hopper Disassembler,它是较常用的的……

前言

如果Crash堆栈,最后一个方法是在自己工程的源码,问题都比较好解决。但如果最后落到了系统库的方法里,并且系统库还没开源,这时候要定位原因就非常困难了。于是我们只能读汇编代码,或反汇编分析伪代码。下面介绍Hopper Disassembler,它是较常用的的反汇编工具。

下载

通过官网下载Hopper工具: www.hopperapp.com/ 。可以先使用试用版,每次可以打开30分钟。

导入

方法1 顶部导航栏,选择File - Read Executable to Disassemble..., 选择要分析的二进制文件.

方法2 直接将可执行文件/动态库/静态库/archive拖到Hopper里

默认就是AArch64(Arm64),直接确认就可以。

通过一个案例看一下一般如何分析

Crash Log基本信息

  1. Date/Time:           2020-11-27 06:37:36 +0000 
  2. OS Version:          iPhone OS 14.2 (18B92) 
  3. Report Version:      104 
  4. Exception Type:  SIGSEGV 
  5. Exception Codes: SEGV_ACCERR at 0x0 
  6. Triggered by Thread:  63  

Crash发生在63号线程,最后的栈桢是_objc_msgSend,它第44行指令出现非法内存访问SEGV_ACCERR,访问了一个内存地址为0x0的指针,0x0明显是一个非法内存地址。下面我们通过查看libobjc的汇编,尝试分析一下0x0到底是从哪里来的。

导入目标文件

打开iOS DeviceSupport路径,在目标系统14.2 (18B92)文件夹里,找到libobjc动态库,将libobjc拖到反编译工具Hopper Disassembler。

  1. libobjc所在路径  
  2. /Users/xxx/Library/Developer/Xcode/iOS DeviceSupport/14.2 (18B92) arm64e/Symbols/usr/lib/libobjc.A.dylib 

定位到Crash指令行

搜索_objc_msgSend,定位到_objc_msgSend的arm汇编代码。方法的首地址是00000001949a60e0,偏移量+44是00000001949a610c,这行就是发生Crash的指令。

反向寻找异常的根源

00000001949a610c add x13, x10, x12, lsl #4

报错的日志显示,访问了非法内存0x0。简单可以理解为某个寄存器的地址是0x0,执行了某一个指令,读取了这个寄存器的值。所以这是一个推理游戏,我们找到0x0地址是从哪里来的。

lsl是逻辑左移指令,add是加法指令。这一行表示将x12左移4位,相当于乘以16,然后加上x10的值,最后赋值给x13寄存器。

  1. x13 <= x10 + (x12 * 16) 

这里读取了两个内存地址的值x10和x12,因为发生了非法内存读取,所以x10或x12其中一个的地址是0x0。

  1. LSL是逻辑左移 
  2. 逻辑左移,右边统一添0。逻辑左移一位:010101010[0] 
  3.  
  4. LSR是逻辑右移 
  5. 逻辑右移,左边统一添0。逻辑右移一位:[0]101010101 

00000001949a6104 eor x12, x1, x1, lsr #7

先看一下x12的来源,看它是否可能是0x0

这里表示x1右移7位,相当于除以128,然后和x1进行异或,结果赋值给x12寄存器,推论x12不太可能为0,那么就是x10为0。

  1. EOR 逻辑异或 
  2. 如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。 

00000001949a6100 and x10, x11, #0xffffffffffff

再看一下x10的来源。x10等于0,x11与上0xffffffffffff结果为0,那x11就是0.

00000001949a60ec and x16, x13, #0x7ffffffffffff8

结合反编译结果 struct objc_class *cls = (struct objc_class *)(isa & 0x7ffffffffffff8);

将isa与上0xffffffff8才能得到对象所属的Class对象,这里x13是isa指针,x16是class对象

  1. struct objc_class : objc_object { 
  2.     struct objc_class * superclass;   //基类信息结构体。 
  3.     cache_t cache;    //方法缓存哈希表 
  4.     //... 其他数据成员忽略。 
  5. }; 
  6.  
  7. struct cache_t { 
  8.     struct bucket_t *buckets;    //缓存方法的哈希桶数组指针,桶的数量 = mask + 1 
  9.     int  mask;        //桶的数量 - 1 
  10.     int  occupied;   //桶中已经缓存的方法数量。 
  11. }; 

读取x16里的地址偏移16个字节(0x10),取出来的值是0。取偏移16个字节的值,通常是取某个对象或结构体的成员变量。从上面一行得出,x16是class对象,偏移16个字节应该是cache对象,那么应该是cache对象为空。

总结

使用反汇编工具是分析疑难问题的基础,今天介绍了Hopper Disassembler工具的使用。有空可以找一些疑难问题来分析,慢慢地就会熟悉反汇编工具和arm64汇编。

【责任编辑:未丽燕 TEL:(010)68476606】
本文转载自网络,原文链接:https://juejin.cn/post/6900725204054605838
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!

推荐图文

  • 周排行
  • 月排行
  • 总排行

随机推荐