由 Linux 内核在 2001 年引入的一项内核补丁技术,旨在为防止堆栈指针泄漏引发的重定向攻击。通过 ASLR(Address Space Layout Randomization)随机化内存布局,使得指针偏移难以预测,从而防范攻击。
在最新的 Android 版本中,ASLR 已经做为系统安全的一部分被强制开启。并且不可关闭。各版本 ASLR 对比:
ASLR 的实现在安卓中共分为两部分,分别是内核态与用户态。
ASLR 的开启与关闭由进程的 personality 属性或全局变量 randomize_va_space 控制。当一个进程 execve(init) 启动时,在内核中经由以下链条:
sys_execve -> do_execve -> do_execveat_common -> bprm_execve -> exec_binpr -> search_binary_handler -> load_binary -> load_elf_binary,在 load_elf_binary 装载 ELF 的过程中会设置 PF_RANDOMIZE 标志。
一旦 PF_RANDOMIZE 标志设置成功,后续的二进制程序、共享库、堆、栈内存都将开启随机化。对已经建立完成的内存布局,后续再修改标志也不会重排现有布局。
用户态的 ASLR 是在 Linker 中强制开启的,这是安卓独有的特性。当共享库(so)被 Linker 加载时,在 find_libraries 阶段会将自身以及依赖的库加载顺序随机打乱。
同样在 ReserveAddressSpace 阶段也加入了 ASLR。
在安卓中要做到完全关闭 ASLR,需要将内核态与用户态两层限制都给绕过。
在常规的系统中我们可以直接将 /proc/sys/kernel/randomize_va_space 设置为 0 即可关闭内核态的 ASLR。但是因为安卓 APP 的启动都是由 Zygote 进程孵化而来, 而 Zygote 进程是由 Init 在系统启动较早期启动。通过上面的内核实现分析,我们已经得知进程的 PF_RANDOMIZE 标志一旦被设置,ASLR 将不可关闭。因此,我们只能在 Zygote 启动之前修改,才能实现内核态 ASLR 的关闭。
如此,我们可以借助 Apatch 提供的 post-fs-data 时机来运行脚本,完成对 randomize_va_space 的修改。
不过实测之后,这段脚本并没有生效。经过一番排查,在 Init 进程的启动过程中会将 randomize_va_space 重置为 2。 
因此,我们必须在 Init 进程启动的过程中将 randomize_va_space 改回来。通过上面的内核分析,我们知道 PF_RANDOMIZE 标志是在 load_elf_binary 函数中设置的。这样就为我们提供了一个完美的 HOOK 时机,我们可以在 load_elf_binary 函数进入之前将 randomize_va_space 改为 0 。
将这段编译为 KPM 模块,刷进 boot.img。再次启动时将会发现内存段的高位地址已经被固定。
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 6小时前
被lidongyooo编辑
,原因: