kaslr全称为kernel address space layout randomization,是linux内核的一个非常重要的安全机制,该机制可以让kernel image映射的执行地址相对于链接地址有个偏移,使内核符号地址变得随机,提升内核的安全性和防攻击能力。

内核源码实现
KASLR的实现原理比较简单,在内核启动阶段,获取一个kernel image的偏移值,偏移值可以通过dtb或者bios传递,或者随机源。

Linux内核支持arm、x86_64、PowerPC等多种不同的架构,不同的架构下,kaslr的实现方式各不相同,但核心思想均在于增加随机偏移。在内核启动阶段,通过获取一个随机值,并对内核加载地址进行相应的随机偏移。该偏移值既可以通过dtb传递,也可以基于随机源生成,在完成内核数据随机映射之后,还需要对符号地址进行重定位,校正内核代码的符号寻址,以此确保内核代码的正常执行。以arm64 5.10内核为例,kaslr在实现时主要通过改变原先固定的内存布局来提升内核安全性,因此在代码实现过程中,kaslr与内存功能存在比较强的耦合关系,Linux内存的虚拟地址空间布局与内核编译配置有关,不同的配置会产生不同的地址空间模型,以4KB pages + 4 levels (48-bit)为例,其虚拟地址空间模型如下:(详细的内存布局信息可参考内核文档:Documentation/arm64/memory.rst)

如图所示,Arm64的虚拟地址空间整体可划分为两个部分:内核地址空间和用户地址空间,通常内核地址空间由内核代码分配使用,所有进程共享内核地址空间,而用户地址空间则为用户态进程独占。内核启动阶段,内核镜像的加载、解压和运行均在内核地址空间完成,kaslr也主要在这里对内核内存布局进行随机调整,arm64的内存布局随机主要分为三部分:内核镜像随机、线性映射区随机以及module随机,在随机过程中需要提供随机种子,随机种子生成如下:

rm64的随机种子通过dtb文件获取,因此会为dtb区域建立映射,从dtb文件解析kaslr-seed的属性配置,同时会获取dtb里面的command line配置,

开启kaslr后,head.S中调用 kaslr_early_init -> get_cmdline

#ifdef CONFIG_RANDOMIZE_BASE
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
#endif
(arch\arm64\kernel\kaslr.c)
>>u64 __init kaslr_early_init(u64 dt_phys)

>>get_cmdline(void *fdt){
>>static __initconst const u8 default_cmdline[] =CONFIG_CMDLINE; 
}

判断是否存在nokaslr配置参数,当不存在随机种子或者配置了nokaslr参数时,kaslr功能会被关闭,并修改相应的kaslr_status状态变量为KASLR_DISABLED_CMDLINE或者KASLR_DISABLED_NO_SEED。  由dtb文件获取到kaslr-seed配置后,会对种子进行处理,基于不同的处理方式,分别用于镜像、线性区和module区域的随机,如下图:

offset为镜像随机偏移值,内核需要保证偏移地址2M对齐,同时通过(VA_BITS_MIN - 2)限制内核随机范围在vmalloc区域中间的一半,避免使用头部和尾部的1/4区域,以避免跟其他的内存分配特性冲突。memstart_offset_seed为线性区的随机种子,内核使用seed的高16位作为线性区域的随机值。  Arm64在内存初始化时,内核会将物理内存通过线性映射的方式完整映射到虚拟地址空间的线性映射区域,如下图:

随机范围是线性区减去物理内存的大小,同时限制偏移粒度ARM64_MEMSTART_ALIGN(256MB),线性区的使用主要涉及virt_to_phys和phys_to_virt两个地址转换接口,其代码实现如下:

以virt_to_phys为例,通过virt_to_phys的接口实现,可以看出memstart_addr为物理地址的起始值,PAGE_OFFSET为虚拟地址中内核地址空间的起始位置,物理地址与虚拟地址之间存在以下转换关系:物理地址 = 虚拟地址 – PAGE_OFFSET + memstart_addr 因此内核可以通过调整memstart_addr的值来进行线性区的映射关系的随机偏移。  Kernel系统启动初期,__primary_switched函数调用kaslr_early_init函数初始化随机偏移值,并保存在x23寄存器当中,实现如下图:

后续进行kernel image虚拟地址映射,会将内核映射的虚拟地址加上kaslr随机偏移(x23寄存器),如下图:

完成kernel image虚拟地址映射以后,需要调用__relocate_kernel进行重定位,函数实现如下:

__relocate_kernel主要对重定位段进行符号重定位,重定位段包含了内核执行过程中需要用到的变量符号,比如_stext、_etext,这些符号对应的地址在链接时确定的,使能kaslr之后,kernel image经过偏移映射,运行时的虚拟地址与编译确定的原始虚拟地址不同,如果不进行重定位操作,则内核无法正常执行。

kaslr特性使能和调试
​使能条件​

kaslr功能通过CONFIG_RANDOMIZE_BASE进行控制。
cmdline中不能存在nokaslr参数,否则kaslr不被使能。

​随机种子​

通过dts指定随机种子,如下:

​验证测试​

通过内核符号地址信息,可以观察内核符号加载状态,以此判断kaslr特性是否生效,命令如下:
echo 0 > /proc/sys/kernel/kptr_restrict
head /proc/kallsyms

 kaslr开源社区参考资料

openEuler Kernel SIG

openEuler kernel 源代码仓库:https://gitee.com/openeuler/kernel 欢迎大家多多 star、fork,多多参与社区开发,多多贡献补丁。关于贡献补丁请参考:如何参与 openEuler 内核开发

以上是基础dt实现的,下面看一个基于BIOS的流程

arm64 linux从4.1x阶段默认配置都打开了CONFIG_EFI_STUB, 默认选择UEFI的启动方式。为什么ARM选择UEFI替换DTB启动方式,可以参考linaro的这篇文章(http://www.linaro.org/blog/when-will-uefi-and-acpi-be-ready-on-arm/)

本篇也基于UEFI启动方式分析下内核KASLR的实现,当前主要通过bios实现 EFI_RNG_PROTOCOL协议来传递硬件熵。

从linux内核的启动流程开始分析,head.S(arch/arm64/kernel/head.S)是vmlinux的入口。

_head:
    /*
     * DO NOT MODIFY. Image header expected by Linux boot-loaders.
     */
#ifdef CONFIG_EFI                       ------ EFI启动配置
    /*
     * This add instruction has no meaningful effect except that
     * its opcode forms the magic "MZ" signature required by UEFI.
     */
    add    x13, x18, #0x16            ----------- 把自己伪装成一个UEFI image,kernel需要符合PE格式
    b    stext                               ----------stext的功能是获取处理器类型和机器类型信息,并创建临时的页表,然后开启MMU功能, 是内核启动的分支
#else
    b    stext                // branch to kernel start, magic    
    .long    0                // reserved
#endif
    le64sym    _kernel_offset_le        // --------- kernel在RAM中加载的偏移,如果等于0,表示加载到RAM的0地址的位置上
    le64sym    _kernel_size_le        // --------- kernel的大小
    le64sym    _kernel_flags_le        // --------- kernel的一些属性
    .quad    0                // reserved
    .quad    0                // reserved
    .quad    0                // reserved
    .ascii    "ARM\x64"            // Magic number
#ifdef CONFIG_EFI
    .long    pe_header - _head        // Offset to the PE header.

pe_header:
    __EFI_PE_HEADER

__EFI_PE_HEADER定义在efi-head.S文件中(arch/arm64/kernel/efi-head.S)

.macro    __EFI_PE_HEADER
    .long    PE_MAGIC
coff_header:
    .short    IMAGE_FILE_MACHINE_ARM64        // --------- 表示machine type是AArch64 
    .short    section_count                // ------------- 该PE文件有多少个section
    .long    0                     // --------------- 该文件的创建时间
    .long    0                    // -------------- 符号表信息 
    .long    0                    // -------------- 符号表中的符号的数目 
    .short    section_table - optional_header        // ---------- optional header的长度 
    .short    IMAGE_FILE_DEBUG_STRIPPED | \
        IMAGE_FILE_EXECUTABLE_IMAGE | \
        IMAGE_FILE_LINE_NUMS_STRIPPED        // ----------- Characteristics,具体的含义请查看PE规格书

optional_header:
    .short    PE_OPT_MAGIC_PE32PLUS            // PE32+ format
    .byte    0x02                    // MajorLinkerVersion
    .byte    0x14                    // MinorLinkerVersion
    .long    __initdata_begin - efi_header_end    // ----------- 正文段的大小
    .long    __pecoff_data_size            // --------- data段的大小
    .long    0                    // ------- bss段的大小
    .long    __efistub_entry - _head            // 加载到memory后入口函数
    .long    efi_header_end - _head            // ----------- 代码段在image file中的偏移

可以看出,加载到memory后的入口函数是__efistub_entry, 它是在哪里定义的呢?
查看Makefile(arch/arm64/kernel/Makefile)可以发现

OBJCOPYFLAGS := --prefix-symbols=__efistub_
$(obj)/%.stub.o: $(obj)/%.o FORCE
    $(call if_changed,objcopy)

编译的对象会有一个预加载的符号__efistub_, 主要作用是为了防止命名冲突,所以真正的入口函数是
entry, 定义在efi-entry.S文件中(arch/arm64/kernel/efi-entry.S)

ENTRY(entry)                ------------ entry的入口函数
    /*
     * Create a stack frame to save FP/LR with extra space
     * for image_addr variable passed to efi_entry().
     */
    stp    x29, x30, [sp, #-32]!
    mov    x29, sp

/*
     * Call efi_entry to do the real work.
     * x0 and x1 are already set up by firmware. Current runtime
     * address of image is calculated and passed via *image_addr.
     *
     * unsigned long efi_entry(void *handle,
     *                         efi_system_table_t *sys_table,
     *                         unsigned long *image_addr) ;
     */
    adr_l    x8, _text
    add    x2, sp, 16
    str    x8, [x2]
    bl    efi_entry                 ----------真正的入口函数是efi_entry
    cmn    x0, #1
    b.eq    efi_load_fail

上面代码我们主要关注的是bl efi-entry,现在我们找到了内核中的入口函数的实现

efi_entry(void *handle,  efi_system_table_t *sys_table, unsigned long *image_addr) ;

efi_entry定义在/drivers/firmware/efi/libstub/arm-stub.c中,

unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
                   unsigned long *image_addr)
{
     ....
     
     * Get the command line from EFI, using the LOADED_IMAGE
     * protocol. We are going to copy the command line into the
     * device tree, so this can be allocated anywhere.
     */
    cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size);  --- (1)
    if (!cmdline_ptr) {
        pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n");
        goto fail;
    }

status = handle_kernel_image(sys_table, image_addr, &image_size,
                     &reserve_addr,
                     &reserve_size,
                     dram_base, image);          ------ (2)
    if (status != EFI_SUCCESS) {
        pr_efi_err(sys_table, "Failed to relocate kernel\n");
        goto fail_free_cmdline;
    }

if (fdt_addr) {   ---- (3)
        pr_efi(sys_table, "Using DTB from command line\n");
    } else {
        /* Look for a device tree configuration table entry. */
        fdt_addr = (uintptr_t)get_fdt(sys_table, &fdt_size);
        if (fdt_addr)
            pr_efi(sys_table, "Using DTB from configuration table\n");
    }

if (!fdt_addr)
        pr_efi(sys_table, "Generating empty DTB\n");

.... 
    
    new_fdt_addr = fdt_addr;
    status = allocate_new_fdt_and_exit_boot(sys_table, handle,
                &new_fdt_addr, efi_get_max_fdt_addr(dram_base), ----- (4)
                initrd_addr, initrd_size, cmdline_ptr,
                fdt_addr, fdt_size);

... 
}

(1) efi_entry 通过efi_convert_cmdline从uefi中拿到cmdline, 然后将cmdline从utf16转成utf8返回。

(2) efi_entry中会调用handle_kernel_image, 重定位内核。

handle_kernel_image(/drivers/firmware/efi/libstub/arm64-stub.c):
efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg,
                 unsigned long *image_addr,
                 unsigned long *image_size,
                 unsigned long *reserve_addr,
                 unsigned long *reserve_size,
                 unsigned long dram_base,
                 efi_loaded_image_t *image)
{
    efi_status_t status;
    unsigned long kernel_size, kernel_memsize = 0;
    void *old_image_addr = (void *)*image_addr;
    unsigned long preferred_offset;
    u64 phys_seed = 0; // kaslr-seed, 默认为0
     
     //内核使能CONFIG_RANDOMIZE_BASE
    if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
        //确保command line没有传递nokaslr参数,如果传递nokaslr则关闭KASLR
        if (!nokaslr()) { 
            // 通过EFI_RNG_PROTOCOL获取BIOS传递过来的随机值
            status = efi_get_random_bytes(sys_table_arg,
                              sizeof(phys_seed),
                              (u8 *)&phys_seed);
            if (status == EFI_NOT_FOUND) {
                pr_efi(sys_table_arg, "EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
            } else if (status != EFI_SUCCESS) {
                pr_efi_err(sys_table_arg, "efi_get_random_bytes() failed\n");
                return status;
            }
        } else {
            pr_efi(sys_table_arg, "KASLR disabled on kernel command line\n");
        }
    }
 
     // 保证kernel位于VMALLOC区域大小的范围 
    preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET;
    if (preferred_offset < dram_base)
        preferred_offset += MIN_KIMG_ALIGN;
 
    kernel_size = _edata - _text;
    kernel_memsize = kernel_size + (_end - _edata);
     
     // 如果随机值不为0并且CONFIG_RANDOMIZE_BASE配置打开, 所以BIOS在产生随机值时需要做一个判断
    if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) {
        //如果未设置CONFIG_DEBUG_ALIGN_RODATA,则在区间[0,MIN_KIMG_ALIGN]中生成一个不违反此内核的事实对齐约束的位移。
        
        u32 mask = (MIN_KIMG_ALIGN - 1) & ~(EFI_KIMG_ALIGN - 1);
        u32 offset = !IS_ENABLED(CONFIG_DEBUG_ALIGN_RODATA) ?
                 (phys_seed >> 32) & mask : TEXT_OFFSET;
 
        //保证传递的偏移地址2M地址对齐
        offset |= TEXT_OFFSET % EFI_KIMG_ALIGN;
 
         // 在一个随机的物理地址加载内核
        *reserve_size = kernel_memsize + offset;
        status = efi_random_alloc(sys_table_arg, *reserve_size,
                      MIN_KIMG_ALIGN, reserve_addr,
                      (u32)phys_seed);
 
        *image_addr = *reserve_addr + offset;
    } else {
        if (*image_addr == preferred_offset)
            return EFI_SUCCESS;
         
        *image_addr = *reserve_addr = preferred_offset;
        *reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN);
         
        status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS,
                    EFI_LOADER_DATA,
                    *reserve_size / EFI_PAGE_SIZE,
                    (efi_physical_addr_t *)reserve_addr);
    }
 
    if (status != EFI_SUCCESS) {
        *reserve_size = kernel_memsize + TEXT_OFFSET;
        status = efi_low_alloc(sys_table_arg, *reserve_size,
                       MIN_KIMG_ALIGN, reserve_addr);
 
        if (status != EFI_SUCCESS) {
            pr_efi_err(sys_table_arg, "Failed to relocate kernel\n");
            *reserve_size = 0;
            return status;
        }
        *image_addr = *reserve_addr + TEXT_OFFSET;
    }
    memcpy((void *)*image_addr, old_image_addr, kernel_size);
 
    return EFI_SUCCESS;
}

handle_kernel_image的主要作用是在一个随机的物理地址中加载内核。

(3) 如果是acpi启动,没有fdt的情况下会生成一个fdt

(4) 在allocate_new_fdt_and_exit_boot -> update_fdt中将之前获取的内容(如cmdline ptr, seed)copy到chosen中

现在我们获取了一个保存关键信息的fdt.
现在我们重新回到head.S的流程
如果使能了KASLR的内核配置(CONFIG_RANDOMIZE_BASE)

__primary_switch:
#ifdef CONFIG_RANDOMIZE_BASE
    mov    x19, x0                // preserve new SCTLR_EL1 value
    mrs    x20, sctlr_el1            // preserve old SCTLR_EL1 value
#endif

bl    __enable_mmu
#ifdef CONFIG_RELOCATABLE
    bl    __relocate_kernel
#ifdef CONFIG_RANDOMIZE_BASE
    ldr    x8, =__primary_switched                   // ----- 跳转到primary_switched
    adrp    x0, __PHYS_OFFSET
    blr    x8

//-------- 在x23 寄存器中有一个KASLR位移,我们需要通过丢弃当前的内核映射并创建一个新的映射。
    pre_disable_mmu_workaround           
    msr    sctlr_el1, x20            //  ------------------ 关闭MMU
    isb
    bl    __create_page_tables        // ------------------ 创建页表映射

tlbi    vmalle1                // -------- 删除TBL
    dsb    nsh

msr    sctlr_el1, x19            // -------- 打开MMU
    isb
    ic    iallu                // 获取刷新指令
    dsb    nsh
    isb

bl    __relocate_kernel    // ------------ relocate kernel
#endif
#endif
    ldr    x8, =__primary_switched
    adrp    x0, __PHYS_OFFSET
    br    x8
ENDPROC(__primary_switch)

现在看下primary_switched中的处理

__primary_switched:
    adrp    x4, init_thread_union
    add    sp, x4, #THREAD_SIZE
    adr_l    x5, init_task
    msr    sp_el0, x5            // Save thread_info

adr_l    x8, vectors            // load VBAR_EL1 with virtual
    msr    vbar_el1, x8            // vector table address
    isb

stp    xzr, x30, [sp, #-16]!
    mov    x29, sp

str_l    x21, __fdt_pointer, x5        // Save FDT pointer

ldr_l    x4, kimage_vaddr        // Save the offset between
    sub    x4, x4, x0            // the kernel virtual and
    str_l    x4, kimage_voffset, x5        // physical mappings

// Clear BSS
    adr_l    x0, __bss_start
    mov    x1, xzr
    adr_l    x2, __bss_stop
    sub    x2, x2, x0
    bl    __pi_memset
    dsb    ishst                // Make zero page visible to PTW

#ifdef CONFIG_KASAN
    bl    kasan_early_init
#endif
#ifdef CONFIG_RANDOMIZE_BASE
    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()
0:
#endif
    add    sp, sp, #16
    mov    x29, #0
    mov    x30, #0
    b    start_kernel
ENDPROC(__primary_switched)

在配置CONFIG_RANDOMIZE_BASE后,会进入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;

/*
     * Set a reasonable default for module_alloc_base in case
     * we end up running with module randomization disabled.
     */
    module_alloc_base = (u64)_etext - MODULES_VSIZE;

/*
     * Try to map the FDT early. If this fails, we simply bail,
     * and proceed with KASLR disabled. We will make another
     * attempt at mapping the FDT in setup_machine()
     */
    early_fixmap_init();
    fdt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL);  
    if (!fdt)
        return 0;

/*
     * Retrieve (and wipe) the seed from the FDT
     */
    seed = get_kaslr_seed(fdt);                            ------------- (1)
    if (!seed)
        return 0;

/*
     * Check if 'nokaslr' appears on the command line, and
     * return 0 if that is the case.
     */
    cmdline = get_cmdline(fdt);
    str = strstr(cmdline, "nokaslr");                -------------- (2)
    if (str == cmdline || (str > cmdline && *(str - 1) == ' '))
        return 0;

mask = ((1UL << (VA_BITS - 2)) - 1) & ~(SZ_2M - 1);         ----------(3)
    offset = BIT(VA_BITS - 3) + (seed & mask);

/* use the top 16 bits to randomize the linear region */
    memstart_offset_seed = seed >> 48;          ----- (4)

if (IS_ENABLED(CONFIG_RANDOMIZE_MODULE_REGION_FULL)) {
        /*
         * Randomize the module region over a 4 GB window covering the
         * kernel. This reduces the risk of modules leaking information
         * about the address of the kernel itself, but results in
         * branches between modules and the core kernel that are
         * resolved via PLTs. (Branches between modules will be
         * resolved normally.)
         */
        module_range = SZ_4G - (u64)(_end - _stext);
        module_alloc_base = max((u64)_end + offset - SZ_4G,
                    (u64)MODULES_VADDR);
    } else {
        /*
         * Randomize the module region by setting module_alloc_base to
         * a PAGE_SIZE multiple in the range [_etext - MODULES_VSIZE,
         * _stext) . This guarantees that the resulting region still
         * covers [_stext, _etext], and that all relative branches can
         * be resolved without veneers.
         */
        module_range = MODULES_VSIZE - (u64)(_etext - _stext);
        module_alloc_base = (u64)_etext + offset - MODULES_VSIZE;
    }

/* use the lower 21 bits to randomize the base of the module region */
    module_alloc_base += (module_range * (seed & ((1 << 21) - 1))) >> 21; 
    module_alloc_base &= PAGE_MASK;

return offset;
}

(1)kaslr_early_init会根据之前生成的fdt种获取kaslr-seed

(2) 解析cmdline, 确保没有传递nokaslr参数

(3) 保证传递的偏移地址2M地址对齐,并且保证kernel位于VMALLOC区域大小的一半地址空间以下 (VA_BITS - 2)。当VA_BITS=48时,mask=0x0000_3fff_ffe0_0000。

(4) 随机化线性映射区地址

回到上面流程,kaslr_early_init获取的偏移地址offset保存在x23寄存器中。然后重新创建kernel image的映射。

创建映射的函数是__create_page_tables。
函数也定义在head.S文件中,主要是为了映射内核在vmalloc域的随机地址空间. 此处还有一个__relocate_kernel的跳转,有什么用呢?例如链接脚本中常见的几个变量_text、_etext、_end。这几个你应该很熟悉,他们是一个地址并且他们的值是链接的时候确定下来,那么现在使能kaslr的情况下,代码中再访问_text的值就很明显不是运行时的虚拟地址,而是链接时候的值。因此,__relocate_kernel函数可以负责重定位这些变量。保证访问这些变量的值依然是正确的值。

功能验证
因为我这边没有一个实现了EFI_RNG_PROTOCAL的BIOS,所以我对内核代码进行了修改,主要验证下KASLR的整个流程是否ok.

上面已经说过,获取kaslr-seed主要通过efi_get_random_bytes(drivers/firmware/efi/libstub/random.c)

efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg,
                  unsigned long size, u8 *out)
{
    efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID;
    efi_status_t status;
    struct efi_rng_protocol *rng;

// *out即使返回的随机值,可以在这里手动赋予一个值,每次启动都重新赋一个
    *out = 0x12345678;
    return EFI_SUCCESS;
    
    status = efi_call_early(locate_protocol, &rng_proto, NULL,
                (void **)&rng);
    if (status != EFI_SUCCESS)
        return status;

return rng->get_rng(rng, NULL, size, out);
}

增加红色代码,修改完成后,多更改几次*out的返回值,查看函数的偏移地址是否每次都不一样即可。
使用如下命令即可:
cat /proc/kallsyms | grep do_fork

总结
如果内核想要使用KASLR的功能,需要保证配置CONFIG_RADOMIZE_BASE和CONFIG_RANDOMIZE_TEXT_OFFSET打开,并且启动参数cmdline中不要添加nokaslr.

kaslr的主要流程可以分为以下几步:
1.通过handle_kernel_image在一个随机的物理地址加载内核
2. 通过kaslr_early_init获取内核映射偏移地址,然后映射内核在vmalloc域的一个随机虚拟地址
3. 映射一些变量以及符号表,偏移地址和image一样

如果需要验证KASLR,可以反复启动内核,查看函数的偏移地址是否每次都不一样即可。

KASLR-内核地址空间布局随机化相关推荐

  1. linux内存布局和地址空间布局随机化(ASLR)下的可分配地址空间

    https://zsummer.github.io/2019/11/04/2019-11-04-aslr/ 地址空间布局随机化(ASLR) <mmap的随机化> <Meltdown( ...

  2. 内存安全 - 地址空间布局随机化(ASLR)

    说明 学过编译原理可知,C语言程序中所有变量的内存地址编译后都是确定了的,但是在linux平台上实际使用时可以发现变量的内存地址并不是固定的,如下: * 示例代码 #include <stdio ...

  3. 关于Windows上地址空间布局随机化防御机制的分析(下)

    将32位程序重新编译为64位程序,以使地址空间布局随机化更有效 尽管Windows的64位版本已经成为主流十多年了,但32位版本的使用者仍然很多.有些程序确实需要保持与第三方插件的兼容性,比如web浏 ...

  4. 关闭aslr oracle,地址空间布局随机化 (Address Space Layout Randomization, ASLR)

    地址空间布局随机化 (Address Space Layout Randomization, ASLR) ASLR 是 Oracle Solaris 系统的一种功能,利用此功能可以随机生成进程地址空间 ...

  5. Linux内核地址空间布局详解

    1. 简介 虽然x86_64的物理地址范围为64bit,但是因为地址空间太大目前不可能完全用完,当前支持57bit和48bit两种虚拟地址模式. 地址模式 单个空间 用户地址空间 内核地址空间 32位 ...

  6. Linux mem 2.2 内核地址空间布局详解

    文章目录 1. 简介 2. 内核页表初始化 2.0 decompress阶段 2.1 `head_64.S`和`head64.c` 2.2 start_kernel() 2.2.1 物理内存(e820 ...

  7. linux 内核地址随机化,GNU/Linux内核的地址随机化

    地址空间布局随机化(ASLR)是一项增加安全性的技术,***者发现漏洞之后开始编写exploit时如果要考虑绕过ASLR这会增加编写exploit的难度,最早是2001年Grsecurity社区(强悍 ...

  8. arm64_linux head.S的执行流程(3.18)- 12.msm8996 地址空间布局

    1.前言 本文基于高通8996平台,kernel版本为3.18.31. 2. 8996内存地址空间布局 通过启动时的打印信息可以知道kernel image的虚拟地址空间布局如下: 结合bootloa ...

  9. 【转载】64 位 Windows 内核虚拟地址空间布局(基于 X64 CPU)

    原文链接:http://shayi1983.blog.51cto.com/4681835/1734822 本文为原创翻译,原文出处为 http://www.codemachine.com/articl ...

  10. 原创翻译: 64 位 Windows 内核虚拟地址空间布局(基于 X64 CPU)

    本文为原创翻译,原文出处为 http://www.codemachine.com/article_x64kvas.html 对于原文中,较难理解或者论述过于简单的部分,则添加了译注:译注来自于内核调试 ...

最新文章

  1. 汽车abs matlab仿真模糊pid控制
  2. MySQL的TRUNCATE关键字
  3. python 删除非空文件夹
  4. 是人是谁_谁是白鹤滩最可爱的人
  5. beta:scrum8
  6. 前台js获取url传递参数(后台Request.QueryString接收)
  7. 【Computer Organization笔记17】大实验讨论:各组数据通路展示
  8. 交换两个数不是三个数。
  9. 让解析器可以快速处理词法单元之间的空格
  10. 22.Linux-块设备驱动之框架详细分析(详解)
  11. matlab二极管伏安特性,基于Matlab对Spice二极管特性受温度影响的研究
  12. 如何下载python模块_python安装模块与包
  13. 推流是什么,直播为什么要推流
  14. 详解电脑死机没反应怎么解决
  15. 计算理论导引第三版答案
  16. Composer私有仓库建设与开发入门
  17. 颈椎病及腰椎间盘突出病因病理(程序员)
  18. Python学习笔记——绘图设置(三)箱线图
  19. 对自动变速器的控制器建模
  20. Rust能力养成之(12)用Cargo进行项目管理:实操项目imgtool

热门文章

  1. 复杂网络社区划分方法综述
  2. solidworks 之迈迪齿轮设计方法 粗浅解释
  3. 服务器2003系统无故重启,WINDOWS 2003 服务器自动定时重启的方法
  4. 计算机网络实训室建设设备,网络综合布线实训室建设方案
  5. 联想计算机进系统不认键盘鼠标,联想台式电脑鼠标和键盘没反应怎么办
  6. Cesium最全源码集合
  7. Ubuntu文本编辑器
  8. Python语法糖系列
  9. opencv 4.5.2 使用比色卡进行颜色校正
  10. 计算机完成双系统无法启动怎么办,电脑双系统引导修复教程