一、虚拟内存
- 早期的操作系统
- 早期的操作系统,并没有虚拟内存的概念.系统由进程直接访问内存中的物理地址,这种方式存在严重的安全隐患.内存中的不同进程,可以计算出他们的物理地址,可以跨进程访问,可以随意进行数据的篡改.
- 早期的程序也比较小,在运行时,会将整个程序全部加载到内存中.但随着软件的发展,程序越来越大,而且还有大型游戏的诞生,导致内存越来越吃紧.这就是早期系统中,为什么经常可以看到内存不足提示框.
- 虚拟内存系统
- 而现代的操作系统都引入了虚拟内存,进程持有的虚拟地址(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:@"汉堡"];
}
- 由此得出
- 偏移地址为: 639c
- 虚拟内存地址为 000000010000639c
- 回到工程、断点调试、image list 得出当前的ASLR地址为 0x0000000100bb4000
- 对函数地址下断点: 0x10000639c+0xbb4000
-
- 接下来设置一个变量a、调试时断点在如下位置、此时a = 10, 控制台取a的虚拟内存地址 0x0000000100708e70
-
- 再重新获取image list 得到ASLR 0x0000000100700000
- 两者差值为 0x8E70
- 将程序MachO文件拖动到MachOView中查找该地址、可以看到
三、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)
(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 found
Instance Methods:
- (Class)class
- (void)dealloc
- (bool)_isKVOA
- pinternals self: 查看指定对象的成员属性
(lldb) pinternals 0x10b89a400
(WCUITextField) $55 = {
UITextField = {
UIControl = {
UIView = {
UIResponder = {
NSObject = {
isa = WCUITextField{...}
}
}
}
}
}
m_bRestrictShareMenu = false
m_fPlaceholderFontSize = 18
}
- fvc -v 0x15bd10dd0: 寻找视图的所属控制器
(lldb) fvc -v 0x10b860a00
Found the owning view controller.
<WCAccountMainLoginViewController: 0x10b860a00>
(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
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 脚本地址