虚拟内存
早期的操作系统
早期的操作系统,并没有虚拟内存的概念。系统由进程直接访问内存中的物理地址,这种方式存在严重的安全隐患。内存中的不同进程,可以计算出它们的物理地址,可以跨进程访问,可以随意进行数据的篡改
早期的程序也比较小,在运行时,会将整个程序全部加载到内存中。但随着软件的发展,程序越来越大,而且还有大型游戏的诞生,导致内存越来越吃紧。这就是早期系统中,为什么经常可以到内存不足提示框
虚拟内存系统
而现代的操作系统都引入了虚拟内存,进程持有的虚拟地址(
Virtual Address
)会经过内存管理单元(Memory Mangament Unit
)的转换变成物理地址,然后再通过物理地址访问内存操作系统以页为单位管理内存,在
iOS
系统中,一页为16KB
。所以虚拟地址和物理地址的映射表,也称之为页表。页表存储在内存中,有了页表,就可以将程序和物理内存完全阻隔开早期的系统,将程序全部加载到内存中。程序越大,它的功能越多,这会造成一些并未使用到的功能,也被加载进内存,造成内存的大量浪费
现代的操作系统进行了更合理的优化,例如
iOS
系统中,当进程被加载时,虚拟内存中会开辟4G
的空间(假空间),用于存放MachO
、堆区、栈区。但物理内存中,并未真的分配。当数据加载到页表中,系统会配合CPU
进行地址翻译,然后载入到物理内存中。地址翻译的过程,由CPU
上的内存管理单元(MMU
)完成页表中记录了内存页的状态、虚拟内存和物理内存的对应关系。其中状态分为:未分配(
Unallocated
)、未缓存(Uncached
)和已缓存(Cached
)
- 未分配的内存页,是没有被进程申请使用的,也就是空闲的虚拟内存,不占用虚拟内存磁盘的任何空间
- 未缓存的内存页,仅在虚拟内存中,没有被物理内存缓存
- 已缓存的内存页,同时存在于虚拟内存和物理内存中
缺页中断
当程序访问未被缓存的内存页时,就会触发缺页中断
- 部分情况下,被访问的页面已经加载到物理内存中,但页表中并不存在该对应关系,这时只需要在页表中建立虚拟内存到物理内存的关系即可
- 其他情况下,操作系统需要将磁盘上未被缓存的虚拟页加载到物理内存中
页面替换
物理内存的空间是有限的,当内存中没有空间时,操作系统会从选择合适的物理内存页驱逐回磁盘,为新的内存页让出位置,选择待驱逐页的过程在操作系统中叫做页面替换
虚拟内存解决的问题
数据存储在虚拟内存中,地址是连续的。但在实际的物理内存中,地址是随机存储的。虚拟内存的出现,将程序和物理内存完全阻隔开,解决了安全问题。页表中只加载程序所使用到的部分功能,避免内存浪费的现象,也解决了内存不足问题
虚拟内存引发的问题
程序的代码在不修改的情况下,每次加载到虚拟内存中的地址都是一样的,这样的方式并不安全。为了解决地址固定的问题,出现了
ASLR
技术
ASLR
ASLR
(Address space layout randomization
):是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的大部分主流的操作系统已经实现了
ASLR
Linux
:Linux
已在内核版本2.6.12
中添加ASLR
Windows
:Windows Server 2008
、Windows 7
、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
)的支持
Chisel
Chisel
是lldb
的指令集合,可帮助开发者调试iOS
应用程序。详情可查看 官方文档安装
chisel
brew update
brew install chisel
打开
~/.lldbinit
,添加以下指令:
command script import /usr/local/opt/chisel/libexec/fbchisellldb.py
案例1:
查看
View
的视图结构
lldb
中,使用pviews
指令
pviews
-------------------------
<UIWindow: 0x104b08cd0; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x280429830>; layer = <UIWindowLayer: 0x280a5ac00>>
| <UITransitionView: 0x104a0bfa0; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x280a4e8e0>>
| | <UIDropShadowView: 0x104b0b870; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x280a5ffc0>>
| | | <UIView: 0x104a06750; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x280a4ede0>>
| | | | <UIButton: 0x104a0b710; frame = (94 350; 31 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x280a4eea0>>
| | | | | <UIButtonLabel: 0x104b26050; frame = (0 6; 31 18); text = ''; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282960e10>>
| | | | <UIButton: 0x104b0b040; frame = (192 350; 31 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x280a5d000>>
| | | | | <UIButtonLabel: 0x104b248c0; frame = (0 6; 31 18); text = ''; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282960910>>
| | | | <UIButton: 0x104b0b450; frame = (302 350; 31 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x280a5dd80>>
| | | | | <UIButtonLabel: 0x104b10c60; frame = (0 6; 31 18); text = ''; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282960280>>
指定
View
下的视图结构
pviews self.view
指定
View
的上层视图结构
pviews -u self.view
案例2:
查看控制器的视图结构
lldb
中,使用pvc
指令
pvc
-------------------------
<ViewController 0x104a09fd0>, state: appeared, view: <UIView 0x104a06750>
案例3:
查看指定类的结构
lldb
中,使用pclass
指令
pclass self
-------------------------
ViewController
| UIViewController
| | UIResponder
| | | NSObject
案例4:
查看指定对象的方法列表
lldb
中,使用pmethod
指令```
pmethod self
Class Methods: No methods were found
Instance Methods:
- (void)setModels:(id)arg0
- (void)viewDidLoad
- (void)touchesBegan:(id)arg0 withEvent:(id)arg1
- (void).cxx_destruct
- (id)models ```
案例5:
查看指定对象的成员属性
lldb
中,使用pinternals
指令
pinternals self
-------------------------
(ViewController) $41 = {
UIViewController = {
UIResponder = {
NSObject = {
isa = ViewController
}
}
}
_models = nil
}
案例6:
寻找视图的所属控制器
lldb
中,使用fvc
指令
fvc -v 0x104b26050
-------------------------
Found the owning view controller.
<ViewController: 0x104a09fd0>
案例7:
寻找指定控件
lldb
中,使用fv
指令
fv UIButton
-------------------------
0x104a0b710 UIButton
0x104b26050 UIButtonLabel
0x104b0b040 UIButton
0x104b248c0 UIButtonLabel
0x104b0b450 UIButton
0x104b10c60 UIButtonLabel
案例8:
指令的使用帮助
```
pviews —help
Usage: [options]
Options: -h, —help show this help message and exit -u, —up Print only the hierarchy directly above the view, up to its window. -d DEPTH, —depth=DEPTH Print only to a given depth. 0 indicates infinite depth. -w WINDOW, —window=WINDOW Specify the window to print a description of. Check which windows exist with “po (id)[[UIApplication sharedApplication] windows]”. -s, —short Print a short description of the view -m, —medium Print a medium description of the view
> 案例9:
> 让指定控件闪烁,可快速找到视图的位置
> `lldb`中,使用`flicker`指令
>
flicker 0x113e0ad60
> 案例10:
> 交互式搜索视图
> `lldb`中,使用`vs`指令
>
vs 0x113e0ad60
Use the following and (q) to quit. (w) move to superview (s) move to first subview (a) move to previous sibling (d) move to next sibling (p) print the hierarchy
> - `w`:移动到父视图
> - `s`:移动到第一个子视图
> - `a`:移动到上一个同级
> - `d`:移动到下一个同级
> - `p`:打印视图结构
> - `q`:退出调试状态
#####LLDB
> `LLDB`是`aliases/regexes`和`Python`的脚本集合,可帮助开发者进行调试。详情可查看 [官方文档](https://github.com/DerekSelander/LLDB)
> 克隆`LLDB`
>
git clone https://github.com/DerekSelander/LLDB.git
> 打开`~/.lldbinit`,添加以下指令:
>
command script import /Users/LLDB/lldb_commands/dslldb.py
> 案例1:
> 查找`UIView`的所有实例和子类
> `lldb`中,使用`search`指令
>
search UIView
> 案例2:
> 找到指定类的方法列表
> `lldb`中,使用`methods`指令
>
methods UIViewController
in UIViewController: Class Methods:
+ (id) fallback_debugHierarchyChildGroupingID; (0x1047fcae4)
+ (id) fallback_debugHierarchyAdditionalGroupingIDs; (0x1047fcaf0)
+ (id) fallback_debugHierarchyObjectsInGroupWithID:(id)arg1 onObject:(id)arg2 outOptions:(id*)arg3; (0x1047fcb64)
+ (id) fallback_debugHierarchyPropertyDescriptions; (0x1047fcca4)
+ (id) fallback_debugHierarchyValueForPropertyWithName:(id)arg1 onObject:(id)arg2 outOptions:(id*)arg3 outError:(id*)arg4; (0x1047fdd98)
+ (void) initialize; (0x1a0433148)
…
> 找到方法地址,可以对其设置断点
>
b -a 0x1a0433148
Breakpoint 2: where = UIKitCore`+[UIViewController initialize], address = 0x00000001a0433148
> 案例3:
> 找回方法的符号
> `lldb`中,使用`sbt`指令
>
sbt
frame #0 : 0x1c8afd8c4 libsystem_kernel.dylibmach_msg_trap + 8
frame #1 : 0x1c8afccc8 libsystem_kernel.dylib
mach_msg + 72
frame #2 : 0x19e23774c CoreFoundation__CFRunLoopServiceMachPort + 376
frame #3 : 0x19e231bd0 CoreFoundation
CFRunLoopRun + 1176
frame #4 : 0x19e231200 CoreFoundationCFRunLoopRunSpecific + 572
frame #5 : 0x1b432c598 GraphicsServices
GSEventRunModal + 160
frame #6 : 0x1a0af7004 UIKitCore-[UIApplication _run] + 1052
frame #7 : 0x1a0afc5d8 UIKitCore
UIApplicationMain + 164
frame #8 : 0x10270e088 001—LLDB调试`_lldb_unnamed_symbol15$$001—LLDB调试 … unresolved womp womp + 140
frame #9 : 0x19df10598 libdyld.dylib`start + 4
```
总结
虚拟内存
- 所有程序的内存访问,都是通过虚拟地址访问的
- 页表存储在内存中,记录状态、虚拟地址和物理地址的映射关系
- 数据以页为单位加载,
iOS
系统一页为16KB
ASLR
- 一种保护技术,在每次加载应用时,系统给一个随机的偏移值
- 定位方法地址:
◦lldb
中,使用image list
指令,找到MachO
的首地址,包含ASLR
◦ 找到方法在文件中的虚拟地址或文件中的偏移地址
◦ 可以使用MachO首地址 + 文件中的偏移地址
◦ 或者使用文件中的虚拟地址 + ASLR
lldb
插件