• <div id="0yoao"><tr id="0yoao"></tr></div>
    <dl id="0yoao"></dl>
  • <sup id="0yoao"></sup>
    <div id="0yoao"><tr id="0yoao"></tr></div>
  • <div id="0yoao"><tr id="0yoao"></tr></div>
  • ?

    KASLR

    作者:smcdef 发布于:2018-5-6 10:00 分类:内存管理

    KASLR

    引言

    什么是KASLR?KASLR是kernel address space layout randomization的缩写,直译过来就是内核地址空间布局随机化。KASLR技术允许kernel image加载到VMALLOC区域的任何位置。当KASLR关闭的时候,kernel image都会映射到一个固定的链接地址。对于黑客来说是透明的,因此安全性得不到保证。KASLR技术可以让kernel image映射的地址相对于链接地址有个偏移。偏移地址可以通过dts设置。如果bootloader支持每次开机随机生成偏移数值,那么可以做到每次开机kernel image映射的虚拟地址都不一样。因此,对于开启KASLR的kernel来说,不同的产品的kernel image映射的地址几乎都不一样。因此在安全性上有一定的提升。

    注?#20309;?#31456;代码分析基于linux-4.15,架构基于aarch64(ARM64)。

    如何使用

    打开KASLR功能非常简单,在支持KASLR的内核配置选项添加选项CONFIG_RANDOMIZE_BASE=y。同时还需要告知kernel映射的偏移地址,通过dts传递。在chosen节点下添加kaslr-seed属性。属性值就是偏移地址。另外command line不要带nokaslr,否则KASLR还是关闭。dts信息举例如下。顺便说一下,在dts中<>符号中是一个32 bit的值。但是在ARM64平台,这里的kaslr-seed属性是一个特例,他就是一个64 bit的值。
    / {
        chosen {
        	kaslr-seed = <0x10000000>;
        };
    }; 

    如何获取偏移

    kaslr-seed属性的解析在kaslr_early_init函数完成。该函数根据输入参数dtb首地址(物理地址)解析dtb,找到偏移地址,最后返回。kaslr_early_init实现如下。
    u64 __init kaslr_early_init(u64 dt_phys)
    {
    	void *fdt;
    	u64 seed, offset, mask, module_range;
    	const u8 *cmdline, *str;
    	int size;
    
    	early_fixmap_init();                                         /* 1 */
    	fdt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL);       /* 1 */
    
    	seed = get_kaslr_seed(fdt);                                  /* 2 */
    	if (!seed)
    		return 0;
    
    	cmdline = get_cmdline(fdt);
    	str = strstr(cmdline, "nokaslr");                            /* 3 */
    	if (str == cmdline || (str > cmdline && *(str - 1) == ' '))
    		return 0;
    
    	mask = ((1UL << (VA_BITS - 2)) - 1) & ~(SZ_2M - 1);          /* 4 */
    	offset = seed & mask;
    
    	/* use the top 16 bits to randomize the linear region */
    	memstart_offset_seed = seed >> 48;                           /* 5 */
    
    	if ((((u64)_text + offset) >> SWAPPER_TABLE_SHIFT) !=
    	    (((u64)_end + offset) >> SWAPPER_TABLE_SHIFT))
    		offset = round_down(offset, 1 << SWAPPER_TABLE_SHIFT);   /* 6 */
    
    	return offset;
    } 
    1. 由于dtb的地址是物理地址,因?#35828;?#19968;步?#20219;猟tb区域建立映射。
    2. 从dtb文件获取kaslr-seed属性的值。
    3. 确保command line没?#20889;?#36882;nokaslr参数,如果传递nokaslr则关闭KASLR。
    4. 保证传递的偏移地址2M地址对齐,并且保证kernel位于VMALLOC区域大小的一半地址空间以下 (VA_BITS - 2)。当VA_BITS=48时,mask=0x0000_3fff_ffe0_0000。
    5. 线性映射区地址也会随机化。
    6. kernel启动初期只有一个PUD页表,因此希望kernel映射在1G(1 << SWAPPER_TABLE_SHIFT)大小范围内,这样就不用两个PUD页表。如果kernel加上偏移offset后不满足这点,自然要重新对齐。

    如何创建映射

    kernel启动初期在汇编阶段创建映射关系。代码位于head.S文件。在__primary_switched函数中会调用kaslr_early_init得到偏移地址。保存在x23寄存器中。然后重新创建kernel image的映射。
    __primary_switched:
    	tst	x23, ~(MIN_KIMG_ALIGN - 1)	// already running randomized?
    	b.ne	0f
    	mov	x0, x21				        // pass FDT address in x0
    	bl	kaslr_early_init		    // parse FDT for KASLR options
    	cbz	x0, 0f				        // KASLR disabled? just proceed
    	orr	x23, x23, x0			    // record KASLR offset
    	ldp	x29, x30, [sp], #16		    // we must enable KASLR, return
    	ret					            // to __primary_switch() 

    创建映射的函数是__create_page_tables。

     
    
    __create_page_tables:
        /*
         * Map the kernel image.
         */
        adrp	x0, swapper_pg_dir
        mov_q	x5, KIMAGE_VADDR + TEXT_OFFSET  // compile time __va(_text)
        add	x5, x5, x23                         // add KASLR displacement
        create_pgd_entry x0, x5, x3, x6
        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)
        create_block_map x0, x7, x3, x5, x6

    这段代码在我的另一篇文章《ARM64 Kernel Image Mapping的变化》已经有分析过,这里就略过了。注意第7行,kernel image映射的虚拟地址加上了一个偏移地址x23。还有一点需要说明,就?#23884;?#37325;定位段进行重定位。这部分代码在arch/arm64/kernel/head.S文件__relocate_kernel函数实现。大概说下__relocate_kernel有什么用呢!例如链接脚?#23616;?#24120;见的几个变量_text_etext_end。这几个你应该很熟悉,他们是一个地址并且他们的值是链接的时候确定下来,那么现在使能kaslr的情况下,代码?#24615;?#35775;问_text的值就很明显不是运行时的虚拟地址,而是链接时候的值。因此,__relocate_kernel函数可以负责重定位这些变量。保证访问这些变量的值依然是正确的值。这部分涉及编译和链接,有兴趣?#30446;?#20197;研究一下《程序员的自我修养》这本书(我不太熟悉)。

    addr2line怎么办

    KASLR在技术上增加了OS安全性,但?#23884;?#20110;调试或许增加了些难?#21462;:我?#35265;得呢?首先,我们知道编译kernel的时候链接地址和最终运行地址是不一样的,因此如果发生oops,栈的回溯信息中的函数地址其实都是运行地址。如果你使用过addr2line工具的话,应该不会陌生addr2line -e vmlinux 0xffffff8000080000这条命令获取某个地址在代码中的哪一?#23567;?#37027;么现在问题是oops中的地址和链接地址有一个偏差,并且这个偏差随着bootloader传递的值而变化。现在摆在我们眼前的是addr2line工具还怎么用?下面就为你答疑解惑。kernel开机log中会打印Virtual kernel memory layout。举例如下。
     
    
    Virtual kernel memory layout:
      modules : 0xffffff8000000000 - 0xffffff8008000000   (   128 MB)
      vmalloc : 0xffffff8008000000 - 0xffffffbebfff0000   (   250 GB)
        .text : 0xffffff80ae280000 - 0xffffff80af2e0000   ( 16768 KB)
      .rodata : 0xffffff80af300000 - 0xffffff80afb60000   (  8576 KB)
        .init : 0xffffff80afb60000 - 0xffffff80b01e0000   (  6656 KB)
        .data : 0xffffff80b01e0000 - 0xffffff80b044f200   (  2493 KB)
         .bss : 0xffffff80b044f200 - 0xffffff80b0e18538   ( 10021 KB)
      fixed   : 0xffffffbefe7fb000 - 0xffffffbefec00000   (  4116 KB)
      PCI I/O : 0xffffffbefee00000 - 0xffffffbeffe00000   (    16 MB)
      vmemmap : 0xffffffbf00000000 - 0xffffffc000000000   (     4 GB maximum)
                0xffffffbf00000000 - 0xffffffbf03000000   (    48 MB actual)
      memory  : 0xffffffc000000000 - 0xffffffc0c0000000   (  3072 MB)

    注意看以上.text区域(kernel代码段)起始地址和结束地址是不是位于VMALLOC区域。如果发生oops,log中函数的地址必然是一个位于.text段的地址,假设是addr_run,但是链接地址应该是KIMAGE_VADDR + TEXT_OFFSET,这两个宏定义参考这篇文章《ARM64 Kernel Image Mapping的变化》。在这个例子中,KIMAGE_VADDR = 0xffffff8008000000,TEXT_OFFSET = 0x80000。addr2line工具使用的必须是链接地址,因此需要将addr_run转换成链接地址。公式很容易推导出来,addr_link = addr_run - .text_start + vmalloc_start + TEXT_OFFSET。在这个例子中就是addr_link = addr_run - 0xffffff80ae280000 + 0xffffff8008000000 + 0x80000。计算的addr_link就可?#20801;?#29992;addr2line工具解析了。Have fun!

     

    标签: kaslr

    评论:

    yiyeguzhou100
    2019-02-19 19:20
    ?#34892;?#20316;者写了这么好的文档, 我在们的产品上用的arm64架构,  我发现CONFIG_RANDOMIZE_BASE配置了(CONFIG_RANDOMIZE_BASE=y),CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET没有配置(is not set), 我编译个若干次内核进行测试, 发现kernel image会被load到固定的位置(0xffff000010080000~0xffff000010c95000), 其中vmalloc区域的地址是0xffff000010000000~0xffff7bffbfff0000。
    这样看来即使配置了CONFIG_RANDOMIZE_BASE也没有实现kernel image会被随机copy到不同的位置。 反倒时我觉得配置了CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET才能实现kernel image被随机copy到不同的位置。 因CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET会决定TEXT_OFFSET的值。
    smcdef
    2019-02-21 11:58
    @yiyeguzhou100:当你配置CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET后,你就会发?#32622;?#27425;开机后,kernel image 映射开始地址是固定不变的。你看到现象虽然变了,但是这并不是kaslr ,kaslr 的目的是每次开机都不一样。实际上这个选项是配置编译时期使TEXT_OFFSET随机值。不配置该选项时,默认TEXT_OFFSET = 0x00080000。所以这个选项其实和kaslr 无关。该配置选项实际上还是链接地址等于最终的运行地址。但是kaslr 却不想?#21462;?
    你配置CONFIG_RANDOMIZE_BASE后,kaslr 没有起效。可能原因文章提到的dts kaslr-seed没有。
    zhangge0409
    2018-11-03 21:52
    请教下博主,我想查看一个程序在运行时的符号表信息,比如程序为app1,
    使用nm app1查看的符号表和app1在运行时的符号表地址信息不一致(应该是链接脚?#23616;?#23450;位地址)
    如何才能看到程序的符号表和运行时一致呢??
    天津网站建设
    2018-09-06 22:09
    ?#34892;?#21338;主,写得真好
    litao6169
    2018-08-06 16:15
    addr_link = addr_run - 0xffffff80ae280000 + 0xffffff8008000000 + 0x80000 等同于:
    addr_link = addr_run - (0xffffff80ae280000 - 0xffffff8008000000 - 0x80000)
    而(0xffffff80ae280000 - 0xffffff8008000000 - 0x80000)实际就是karsl-seed 偏移地址。
    hzm
    2018-05-18 11:28
    请问下,开了KASLR后, kernel image 加载到VMALLOC是随机变化的, 加载到物理地址还是固定的吗?
    因为如果不是固定,bootloader那边加载image也需要偏移的信息

    谢谢。
    smcdef
    2018-05-18 19:25
    @hzm?#20309;?#29702;地址还是以前一样,改变的只是虚拟地址而已,物理地址在哪其?#24471;?#20160;么影响,创建的是虚拟地址到物理地址的映射关系
    hzm
    2018-05-19 11:24
    @smcdef:多谢解答。。另外这个过程可以理解成静态地?#20998;?#23450;位?
    还有的是不大理解 为什么要0x80000这个偏移,没KASLR的版本是为了预留一段空间用来fix mapping吗? 那打开了KASLR似乎不需要这个偏移?
    smcdef
    2018-05-19 16:15
    @hzm:针对ARM32架构,0x80000偏移之前的物理地址是特殊用途,例如页表。但是针对ARM64架构,好像没什么作用。你说的fix mapping,可以参考另一篇fix mapping的文章。
    hzm
    2018-05-20 23:08
    @smcdef:TEXT_OFFSET放的是哪个页表?看了窝窝内存初始化【上?#31354;?#31687;,说swapper进程放在bss?#25105;?#21518;
    smcdef
    2018-05-24 22:27
    @hzm:注意区分ARM32和ARM64的区别

    发表评论:

    Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog
    连码三全中是什么
  • <div id="0yoao"><tr id="0yoao"></tr></div>
    <dl id="0yoao"></dl>
  • <sup id="0yoao"></sup>
    <div id="0yoao"><tr id="0yoao"></tr></div>
  • <div id="0yoao"><tr id="0yoao"></tr></div>
  • <div id="0yoao"><tr id="0yoao"></tr></div>
    <dl id="0yoao"></dl>
  • <sup id="0yoao"></sup>
    <div id="0yoao"><tr id="0yoao"></tr></div>
  • <div id="0yoao"><tr id="0yoao"></tr></div>