基于msm-5.4
一、简介
1. 什么是KASLR
KASLR是 kernel address space layout randomization 的缩写,直译过来就是内核地址空间布局随机化。KASLR技术允许将kernel image映射到 vmalloc 区域的任何位置(待确认哦)。
2. 引入KASLR的原因
没有KASLR的时候,kernel image都会映射到一个固定的链接地址,安全性得不到保证。KASLR技术可以让kernel image映射的地址相对于链接地址有个偏移。这个偏移可以通过dts设置,如果bootloader支持每次开机随机生成偏移数值,那么可以做到每次开机kernel image映射的虚拟地址都不一样。因此在安全性上有一定的提升。
3. 主要实现文件
arch/arm64/kernel/kaslr.c
arch/arm64/kernel/head.S
二、实现逻辑
1. 偏移对内核镜像映射的影响
//arch/arm64/kernel/head.S
__primary_switched: mov x0, x21 //pass FDT address in x0 bl kaslr_early_init //parse FDT for KASLR options cbz x0, 0f //KASLR disabled? just proceed 若返回0则跳转到下面标签0:处运行,若返回的offset非0则继续执行 orr x23, x23, x0 //record KASLR offset x23或上返回的offsets ret // to __primary_switch() 返回到上一级函数中 0: b start_kernel //若返回offset非0就不会从这里启动内核了 ENDPROC(__primary_switched)
这里调用 kaslr_early_init(), 将设备树做为参数,返回值保存在x0中,然后位或给x23, 由于x23初始值是0,因此x23=x0=返回值offset.
若返回值offset不为0,要在内核虚拟基地址上加上这个offset,重新映射一遍内核镜像:
__primary_switch: adrp x1, init_pg_dir bl __enable_mmu //这里就使能了MMU #ifdef CONFIG_RELOCATABLE bl __relocate_kernel #ifdef CONFIG_RANDOMIZE_BASE ldr x8, =__primary_switched adrp x0, __PHYS_OFFSET blr x8 //若kaslr_early_init()返回的offset非0,才会返回到这里继续往下执行 /* 若执行到这里,在 x23 中有一个 KASLR 偏移,需要丢弃当前内核映射并创建一个新的内核映射来考虑到它 */ pre_disable_mmu_workaround bl __create_page_tables //recreate kernel mapping 重新创建内核映射页表 #endif #endif ldr x8, =__primary_switched //在这里执行 start_kernel() adrp x0, __PHYS_OFFSET br x8 ENDPROC(__primary_switch)
返回offset!=0会返回到生成函数,导致 blr x8 被执行,之后disable掉之前的映射,重新创建内核镜像的页表。
__create_page_tables: /* Map the kernel image (starting with PHYS_OFFSET). */ adrp x0, init_pg_dir mov_q x5, KIMAGE_VADDR + TEXT_OFFSET // compile time __va(_text) add x5, x5, x23 //add KASLR displacement 在内核镜像虚拟地址上再加上保存在x23中的kaslr的offset mov x4, PTRS_PER_PGD adrp x6, _end // runtime __pa(_end) adrp x3, _text // runtime __pa(_text) sub x6, x6, x3 // _end - _text add x6, x6, x5 // runtime __va(_end) map_memory x0, x1, x5, x6, x7, x3, x4, x10, x11, x12, x13, x14 ENDPROC(__create_page_tables) .ltorg
再次创建内核镜像页表,要映射到的虚拟地址为 KIMAGE_VADDR + TEXT_OFFSET + offset.
2. 随机偏移获取方法
u64 __init kaslr_early_init(u64 dt_phys) //kaslr.c { void *fdt; u64 seed, offset, mask, module_range; const u8 *cmdline, *str; int size; /* 为 module_alloc_base 设置一个合理的默认值,以防我们最终在禁用模块随机化的情况下运行 */ module_alloc_base = (u64)_etext - MODULES_VSIZE; /* * 尝试尽早映射 FDT。如果失败,我们只需放弃,然后禁用 KASLR。我们将在 setup_machine() 中 * 再次尝试映射 FDT. msm-5.4上没有这个函数,msm-4.14在 /external/u-boot/common/board_f.c 中 * 此时MMU还远没有开启。 */ early_fixmap_init(); fdt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL); if (!fdt) return 0; /* 解析设备树 "/chosen" 节点的 "kaslr-seed" 属性的值,得到随机种子,若没有配置则直接返回0 */ seed = get_kaslr_seed(fdt); if (!seed) return 0 //实验:替换为 seed = 0x800000;后,内核起不来 /* 若是在“/chosen”的"bootargs"属性或cmdline中指定了“ nokaslr” 标签,则返回0,表示kaslr不生效。*/ cmdline = kaslr_get_cmdline(fdt); str = strstr(cmdline, "nokaslr"); if (str == cmdline || (str > cmdline && *(str - 1) == ' ')) return 0; /* * 好的,我们继续启用 KASLR。计算与种子相距合适的内核映像偏移量。让我们将 * 内核放置在 VMALLOC 区域的中间一半(VA_BITS_MIN - 2),并避开下四分之一 * 和上四分之一以避免与其他分配发生冲突。 * 即使我们可以在 16k 和 64k 页面的页面粒度上随机化,我们也要始终四舍五入 * 到 2 MB,这样我们就不会干扰使用连续 PTE 进行映射的能力。 * * 补充: 启动阶段内核镜像映射粒度是2M,4k宵页映射一个PMD页表也映射2M. */ /* mask = (1<<37 - 1) & ~(2M-1) = 0x1fffe00000 */ mask = ((1UL << (VA_BITS_MIN - 2)) - 1) & ~(SZ_2M - 1); /* 偏移是2M对齐,且要落在虚拟地址空间的[1/4, 3/8] 之间 */ offset = BIT(VA_BITS_MIN - 3) + (seed & mask); /* 随机种子是64bit的,这里保留高16bit的值,来随机化线性映射区的地址 */ memstart_offset_seed = seed >> 48; if (IS_ENABLED(CONFIG_KASAN)) //默认关闭 /* * KASAN 不希望模块区域与 vmalloc 区域相交,因为影子内存是在加载时为每个模块分配的, * 而 vmalloc 区域则被 KASAN 零页所遮蔽。因此,如果启用了 KASAN,请将模块置于 vmalloc * 区域之外,并将内核置于模块区域的 4 GB 以内。 */ return offset % SZ_2G; if (IS_ENABLED(CONFIG_RANDOMIZE_MODULE_REGION_FULL)) { //默认使能 /* * 在覆盖内核的 2 GB 窗口上随机化模块区域(启动汇编中内核段映射最大支持2GB)。 * 这降低了模块泄露有关内核本身地址信息的风险,但会导致模块和核心内核之间的分支通过 PLT 解析。 */ module_range = SZ_2G - (u64)(_end - _stext); //delta=0x3707000 /* _end + offset - SZ_2G = 0xffffffbf937be000 + offset 与 MODULES_VADDR=0xffffffc008000000 取较大值 */ module_alloc_base = max((u64)_end + offset - SZ_2G, (u64)MODULES_VADDR); } else { module_range = MODULES_VSIZE - (u64)(_etext - _stext); module_alloc_base = (u64)_etext + offset - MODULES_VSIZE; } /* 使用低21位随机化模块区域的基地址 */ module_alloc_base += (module_range * (seed & ((1 << 21) - 1))) >> 21; module_alloc_base &= PAGE_MASK; //4k对齐 pr_info("HAM: %s: offset=0x%lx", offset); //调用过早,这里无法打印出来 return offset; }
可以看到,这个随机化除了内核镜像虚拟基地址随机化外,还对线性映射区的虚拟地址、module_alloc区的虚拟基地址进行了随机化。返回的offset用于随机内核镜像的虚拟基地址,返回的offset偏移是2M对齐,且要落在虚拟地址空间的[1/4, 3/8]之间。
KASLR使能开关:CONFIG_RANDOMIZE_BASE,它选中编译 kaslr.c, 从head.S中看它还依赖 CONFIG_RELOCATABLE, 所以这两个都需要使能。
使能后生效的条件(不是返回0)是在/chosen节点中配置了kaslr-seed随机种子,这个随机种子虽然看似在设备树中静态配置的,但是可以由bootloader进行填充,并且可以做到每次开机都不一样。其次是在 /chosen 节点的 bootargs 和 default_cmdline 中没有没有指定 “ nokaslr” 标签。
/ { chosen { kaslr-seed = <0x10000000>; }; };
三、实验
将 seed = 0x800000; 后内核起不来,若要使能,还需要进一步调试。
四、总结
1. KASLR就是在映射内核镜像的时候,在虚拟基地址上加上一个偏移。利用随机种子,可以让每次启动偏移都不一样。
2. 通过 CONFIG_RANDOMIZE_BASE 和 CONFIG_RELOCATABLE 使能此功能,若是想生效,还需要在 /chosen 节点中配置 kaslr-seed 随机种子,并且不能在 bootargs 和 cmdline 中指定 “ nokaslr”标签。
3. 算KASLR加的这个偏移,对内核镜像的映射一共有两个加偏移的位置了。另一个是 _text 要映射的虚拟基地址和Makefile中指定的 TEXT_OFFSET 决定的。
#define __PHYS_OFFSET (KERNEL_START - TEXT_OFFSET) //_text - TEXT_OFFSET ENTRY(stext) adrp x23, __PHYS_OFFSET ENDPROC(stext)
参考:
KASLR: http://www.wowotech.net/memory_management/441.html //蜗蜗
标签:__,映射,22,KASLR,kaslr,内存,offset,内核 From: https://www.cnblogs.com/hellokitty2/p/18320872