> 文章列表 > 15、虚拟内存LLDB高级调试

15、虚拟内存LLDB高级调试

15、虚拟内存LLDB高级调试

一、虚拟内存

  • 早期的操作系统
    • 早期的操作系统,并没有虚拟内存的概念.系统由进程直接访问内存中的物理地址,这种方式存在严重的安全隐患.内存中的不同进程,可以计算出他们的物理地址,可以跨进程访问,可以随意进行数据的篡改.
    • 早期的程序也比较小,在运行时,会将整个程序全部加载到内存中.但随着软件的发展,程序越来越大,而且还有大型游戏的诞生,导致内存越来越吃紧.这就是早期系统中,为什么经常可以看到内存不足提示框.
  • 虚拟内存系统
    • 而现代的操作系统都引入了虚拟内存,进程持有的虚拟地址(Virtual Address)会经过内存管理单元(Memory Management Unit)的转换变成物理地址,然后再通过物理地址访问内存.
    • 操作系统以页为单位管理内存,在iOS系统中,一页为16KB.所以虚拟地址和物理地址的映射表,也称之为页表.页表存储在内存中,有了页表,就可以将程序和物理内存完全隔断开.
    • 早期的系统,将程序全部加载到内存中.程序越大,它的功能越多,这会造成一些并未使用到的功能,也被加载进内存,造成内存的大量浪费
    • 现代的操作系统进行了更合理的优化,例如iOS系统中,当进程被加载时,虚拟内存中会开辟4G的空间(假空间),用于存放MachO、堆区、栈区.但物理内存中,并未真的分配.当数据加载到页表中,系统会配合CPU进行地址翻译,然后载入到物理内存中.地址翻译的过程,由CPU上的内存管理单元(MMU)完成
    • 页表中记录了内存页的状态、虚拟内存和物理内存的对应关系.其中状态分为:未分配(Unallocated)、未缓存(Uncached)、和已缓存(Cached)
      • 未分配的内存页,是没有被进程申请使用的,也就是空闲的虚拟内存,不占用虚拟内存磁盘的任何空间.
      • 未缓存的内存页,仅在虚拟内存中,没有被物理内存缓存.
      • 已缓存的内存页,同时存在于虚拟内存和物理内存中.
  • 缺页中断
    • 当程序访问未被缓存的内存页时,就会触发缺页中断
    • 部分情况下,被访问的页面已经加载到物理内存中,但页表中并不存在该对应关系,这时只需要在页表中建立虚拟内存到物理内存的关系即可
    • 其他情况下,操作系统需要将磁盘上未被缓存的虚拟页加载到物理内存中
  • 页面替换
    • 物理内存的空间是有限的,当内存中没有空间时,操作系统会重新选择合适的物理内存页驱逐回磁盘,为新的内存页让出位置,选择待驱逐页的过程在操作系统中叫做页面替换
  • 虚拟内存解决的问题
    • 数据存储在虚拟内存中,地址是连续的.但在实际的物理内存中,地址是随机存储的.虚拟内存的出现,将程序和物理内存完全隔离开,解决了安全问题.
    • 页表中只加载程序所使用到的部分功能,避免内存浪费的现象,也解决了内存不足问题.
  • 虚拟内存引发的问题
    • 程序的代码在不修改的情况下,每次加载到虚拟内存中的地址都是一样的,这样的方式并不安全.为了解决地址固定的问题,出现了ASLR技术

二、ASLR

2.1 ASLR简介

  • ASLR(Address Space Layout Randomization): 是一种针对缓存区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的.
  • 大部分主流的操作系统已经实现了ASLR
    • Linux: Linux已在内核版本2.6.12中添加ASLR
    • Windows: Windows Server 2008、Windows7、Windows Vista、Windows Server 2008 R2,默认情况下启用ASLR,但它仅适用于动态链接库和可执行文件
    • Mac OS X: Apple在Mac OS X Leopard10.5(2007年十月发行)中某些库导入了随机地址偏移,但其实现并没有提供ASLR所定义的完整保护能力.而 Mac OS X Lion10.7 则对所有的应用程序均提供了ASLR支持.Apple 宣称为应用程序改善了这项技术的支持,能让32及64位的应用程序避开更多此类攻击.从OS X Mountain Lion10.8开始,核心及核心扩充(kext)与zones在系统启动时也会随机配置.
    • iOS(iPhone 、iPod touch、iPad): Apple 在iOS4.3内导入了ASLR.
    • Android: Android 4.0 提供地址空间配置随机加载(ASLR),以帮助保护系统和第三方应用程序免受由于内存管理问题的攻击,在Android 4.1中加入地址无关代码(Position-Independent Code)的支持

2.2 ASLR练习

  • 创建个App类型的Demo、快快一写、编译运行、放入Hopper
- (void)eatWithObject:(NSString *)objc {NSLog(@"吃到了 : %@",objc);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {[self eatWithObject:@"汉堡"];
}
  • 搜索关键函数 eatWithObject

  • 由此得出
    • 偏移地址为: 639c
    • 虚拟内存地址为 000000010000639c
    • 回到工程、断点调试、image list 得出当前的ASLR地址为 0x0000000100bb4000
    • 对函数地址下断点: 0x10000639c+0xbb4000

    • 断点下来、命中函数地址.
  • 接下来设置一个变量a、调试时断点在如下位置、此时a = 10, 控制台取a的虚拟内存地址 0x0000000100708e70

    • 再重新获取image list 得到ASLR 0x0000000100700000
    • 两者差值为 0x8E70
    • 将程序MachO文件拖动到MachOView中查找该地址、可以看到

    • 0x8E70位置为10、得证.

三、LLDB高级调试

3.1 Chisel

  • chisel : Chisel是一组LLDB用于协助调试 iOS 应用程序的命令。根据安装步骤安装完毕后
  • 在调试时使用:
(lldb) p [self.view recursiveDescription]
(__NSCFString *) $0 = 0x00000002834a5ef0 @"<UIView: 0x15be061d0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x283aecbc0>>\\n   | <UIButton: 0x15be10e20; frame = (0 0; 200 200); opaque = NO; layer = <CALayer: 0x283aecb40>>\\n   |    | <UIButtonLabel: 0x15be10b00; frame = (73 89.3333; 54 21.6667); text = 'Button'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2819e5450>>"
// 指定View下的视图结构
(lldb) pviews self.view
<UIView: 0x15be061d0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x283aecbc0>>| <UIButton: 0x15be10e20; frame = (0 0; 200 200); opaque = NO; layer = <CALayer: 0x283aecb40>>|    | <UIButtonLabel: 0x15be10b00; frame = (73 89.3333; 54 21.6667); text = 'Button'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2819e5450>>       ^
// 指定View的上层视图结构
(lldb) pviews -u self.view
<UIWindow: 0x15bd090f0; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x2834b7d50>; layer = <UIWindowLayer: 0x283ae82e0>>| <UIView: 0x15be061d0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x283aecbc0>>
// 查看当前显示View的视图结构
(lldb) pviews
<UIWindow: 0x15bd090f0; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x2834b7d50>; layer = <UIWindowLayer: 0x283ae82e0>>| <UIView: 0x15be061d0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x283aecbc0>>|    | <UIButton: 0x15be10e20; frame = (0 0; 200 200); opaque = NO; layer = <CALayer: 0x283aecb40>>|    |    | <UIButtonLabel: 0x15be10b00; frame = (73 89.3333; 54 21.6667); text = 'Button'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2819e5450>>
(lldb)
  • pvc: 查看控制器的视图结构
(lldb) pvc
<MMUINavigationController 0x10b8bec00>, state: disappeared, view: <UILayoutContainerView 0x10b04b7d0> not in the window| <WCAccountLoginFirstViewController 0x10c1e4800>, state: disappeared, view: <UIView 0x10b051f80> not in the window+ <MMUINavigationController 0x10c1c5e00>, state: appeared, view: <UILayoutContainerView 0x10b18d570>, presented with: <_UIFullscreenPresentationController 0x10b06b7f0>|    | <WCAccountMainLoginViewController 0x10b860a00>, state: appeared, view: <UIView 0x10b18f570>
<MMUINavigationController 0x10b8bec00>, state: disappeared, view: <UILayoutContainerView 0x10b04b7d0> not in the window| <WCAccountLoginFirstViewController 0x10c1e4800>, state: disappeared, view: <UIView 0x10b051f80> not in the window+ <MMUINavigationController 0x10c1c5e00>, state: appeared, view: <UILayoutContainerView 0x10b18d570>, presented with: <_UIFullscreenPresentationController 0x10b06b7f0>|    | <WCAccountMainLoginViewController 0x10b860a00>, state: appeared, view: <UIView 0x10b18f570>
  • pclass 0x10b860a00: 查看指定类的结构
(lldb) pclass 0x10b860a00
WCAccountMainLoginViewController| WCAccountBaseViewController|    | MMUIViewController|    |    | UIViewController|    |    |    | UIResponder|    |    |    |    | NSObject
  • pmethods 0x10b860a00 : 查看指定对象的方法列表
(lldb) pmethods 0x10b860a00
Class Methods:
No methods were foundInstance Methods:
- (Class)class
- (void)dealloc
- (bool)_isKVOA
  • pinternals self: 查看指定对象的成员属性
(lldb) pinternals 0x10b89a400
(WCUITextField) $55 = {UITextField = {UIControl = {UIView = {UIResponder = {NSObject = {isa = WCUITextField{...}}}}}}m_bRestrictShareMenu = falsem_fPlaceholderFontSize = 18
}
  • fvc -v 0x15bd10dd0: 寻找视图的所属控制器
(lldb) fvc -v 0x10b860a00
Found the owning view controller.
<WCAccountMainLoginViewController: 0x10b860a00>
  • fv UIButton: 寻找指定控件
(lldb) fv UIButton
0x15bd10dd0 UIButton
0x15bd0fdf0 UIButtonLabel
  • flicker 0x10b89a400: 让指定控件闪烁,可快速找到视图的位置
(lldb) flicker 0x10b89a400
  • 交互式搜索视图:使用vs指令
    • w: 移动到父视图
    • s: 移动到第一个子视图
    • a: 移动到上一个同级
    • d: 移动到下一个同级
    • p: 打印视图结构
    • q: 退出调试状态

3.2 DSLLDB

  • LLDB是aliases/regexes和Python的脚本集合,可帮助开发者进行调试。详情可查看 官方文档
  • 下载LLDB
git clone https://github.com/DerekSelander/LLDB.git
  • 打开~/.lldbinit,添加以下指令:
command script import /Users/LLDB/lldb_commands/dslldb.py
  • 用法说明:
    • 查找UIView的所有实例和子类: search UIView
    • 找到指定类的方法列表: methods UIViewController
    • 找回方法的符号: sbt

四、总结

  • 虚拟内存
    • 所有程序的内存访问都是通过虚拟地址访问的
    • 系统有一张页表,虚拟地址和物理地址的映射表
    • 数据加载、以页为单位加载的
  • ASLR
    • 一种保护技术,在每次加载应用的时候,系统给一个随机的偏移值
    • 定位方法地址,下内存断点
      • 1、LLDB、image list找到MachO的首地址
      • 2、找到方法在文件中的虚拟地址、文件中的偏移地址
      • 3、获取到方法的虚拟地址
        • 3.1、MachO首地址 + 文件中的偏移地址
        • 3.2、MachO中的虚拟地址+ASLR(是不包含PageZero的)
  • LLDB插件
    • chisel、DSLLDB
    • LLDB官方文档,有相关API.通过Python脚本自定义命令
    • 加载插件:通过 .lldbinit文件去加载插件, command script import 脚本地址