长亭百川云 - 文章详情

XNU进程地址随机化解读

kernsec

70

2024-07-13

1.1 进程栈、代码段地址随机化

这里指的是进程的用户态栈,记住一个进程实际拥有两个栈, 一个用于跑用户态的代码,一个用于请求系统调用时在内核中使用的栈空间。在前面分析BSD进程随机化时,我们注意到bsd并没有给进程的用户态栈加入地址随机化, XNU虽然继承了BSD进程模型,但作为一个商业操作系统没有栈的随机化功能显然是说不过去的, 自然给其进程加入了随机化功能。

bsd/kern/mach_loader.c

除了mmap,进程的栈、代码段以及共享库的地址随机化都是在内核从磁盘加载二进制文件时完成的。[1]处vm_map_get_max_aslr_slide_section函数选取了随机化的范围,

void

aslr_section_offset表示的随机几个页面大小,aslr_section_size表示每个页面的大小,对于不同的CPU体系架构拥有不同的值,在arm64下 aslr_section_offset设定为3,页面大小设置为0x0000000000200000ULL,其他架构下aslr_section_offset设为1, 页面大小设为0。[2]处使用random函数生成了一个临时随机范围aslr_section_offset。[3]处的aslr_page_offset表示栈和代码段使用的随机范围,没错xnu的栈和text代码使用的是同一个随机范围, 而linux使用的都是不同的。Xnu这样做提升了一点性能,但安全性也会降低一些。vm_map_get_max_aslr_slide_pages函数选取了要随机多少个页面大小,在arm64下为1<<12,在其他架构的64位下为1<<16,32位为1<<8。

uint64_t

最后aslr_page_offset在左移12位,那么在arm64下其随机化的范围就为0-16MB,其实随机化的范围并不大,而且都是以页面对齐的,所以只需暴力才解4096次就能猜到offset。即使加上[5]处的临时offset,也提高不了多少安全等级。

[4] 处的vm_map_get_max_loader_aslr_slide_pages计算的是共享库的地址随机化范围,与上述类似,最终随机范围为0-4MB。

Win10进程栈在64位下可以做到上TB的随机化范围, 笔者也给linux扩展了栈的随机化范围,通过打入AKSP补丁,栈的随机化也可以做到上TB的范围。如此来看,XNU进程的栈地址随机化未免有点小家子气了。

对栈基地址的设置是在load_unixthread里设置的:

static

load_threadstack选取了栈基地址,然后减去slide。Slide为上述的aslr_page_offset,但是它的使用还有个前提条件:

static

Slide初始化为0,只有当二进制为PIE编译或者为动态连接器才会被设置为aslr_offset,这样对于普通的二进制程序栈并没有地址随机化能力!

1.2 mmap地址随机化

XNU提供了posix标准的mmap函数,对于匿名映射的内存地址随机化是在mach层的vm_map_enter函数来设置的。

osfmk/vm/vm_map.c

对于主动提供了VM_FLAGS_RANDOM_ADDR标志或者在CONFIG_EMBEDDED下开启了jit code条件下都会使用[3]处的vm_map_random_address_for_size函数选取一块包含了随机化范围的起始地址。

#define MAX_TRIES_TO_GET_RANDOM_ADDRESS 1000

这个函数比较奇葩, 尝试循环1000次找到带有随机化范围的vm_map_entry,[1]处首先计算当前进程还剩的虚拟内存空间大小, [2]处使用random函数产生了一个页面对齐的随机数,然后与addr_space_size取模,在64位下,addr_space_size的取值可能非常大, 所以xnu尝试最多1000次循环来找到一个合适的地址空间。使用这样的算法,offset的可控性很差, 还有可能因为随机数的问题导致整个mmap动作失败,我觉得后续xnu的内核工程师应该会改进这个算法。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2