虚拟内存
早期的操作系统
早期的操作系统,并没有虚拟内存的概念。系统由进程直接访问内存中的物理地址,这种方式存在严重的安全隐患。内存中的不同进程,可以计算出它们的物理地址,可以跨进程访问,可以随意进行数据的篡改
早期的程序也比较小,在运行时,会将整个程序全部加载到内存中。但随着软件的发展,程序越来越大,而且还有大型游戏的诞生,导致内存越来越吃紧。这就是早期系统中,为什么经常可以到内存不足提示框
虚拟内存系统
而现代的操作系统都引入了虚拟内存,进程持有的虚拟地址(
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 <UIButton: 0x113e0ad60; frame = (94 350; 31 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x282601d60>>
w
:移动到父视图s
:移动到第一个子视图a
:移动到上一个同级d
:移动到下一个同级p
:打印视图结构q
:退出调试状态
LLDB
LLDB
是aliases/regexes
和Python
的脚本集合,可帮助开发者进行调试。详情可查看 官方文档
克隆
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 ------------------------- <UIWindow: 0x104b09930; frame = (0 0; 414 736); gestureRecognizers = <NSArray: 0x280b8db00>; layer = <UIWindowLayer: 0x2805c6780>> <UIButton: 0x104b0d9e0; frame = (192 350; 31 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x2805c7dc0>> <UIButtonLabel: 0x104b1efb0; frame = (0 6; 31 18); text = '保存'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2826f3750>> <UIButtonLabel: 0x104b0f800; frame = (0 6; 31 18); text = '继续'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2826f3700>> <UIDimmingView: 0x104b0a9c0; frame = (0 0; 0 0); opaque = NO; gestureRecognizers = <NSArray: 0x280bbd1d0>; layer = <CALayer: 0x2805c6dc0>> <UIButtonLabel: 0x104c11180; frame = (0 6; 31 18); text = '暂停'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2826e7890>> <UIButton: 0x104b0de20; frame = (302 350; 31 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x2805c7ea0>> <UIDropShadowView: 0x104b0e300; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x2805f4460>> <UIView: 0x104b07520; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x2805c7720>> <UIButton: 0x104b09c20; frame = (94 350; 31 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x2805c74a0>> <UIDimmingView: 0x104c07ed0; frame = (0 0; 0 0); opaque = NO; gestureRecognizers = <NSArray: 0x280bbd200>; layer = <CALayer: 0x2805da380>> <UITransitionView: 0x104b09730; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x2805f4d40>>
案例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.dylib`mach_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 CoreFoundation`CFRunLoopRunSpecific + 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
插件