Red Hat的crash工具有个option是--kaslr,一般解析ramdump会用,工具定义:

–kaslr offset | auto
If x86, x86_64 or s390x kernel was configured with CONFIG_RANDOMIZE_BASE,
the offset value is equal to the difference between the symbol values
compiled into the vmlinux file and their relocated KASLR value. If
set to auto, the KASLR offset value will be automatically calculated.

ok, what is CONFIG_RANDOMIZE_BASE? 和arch有关,我们看下ARM64的定义,参考kernel 4.x。

config RANDOMIZE_BASE
bool "Randomize the address of the kernel image"
select ARM64_MODULE_PLTS if MODULES
select RELOCATABLE
help
Randomizes the virtual address at which the kernel image is
loaded, as a security feature that deters exploit attempts
relying on knowledge of the location of kernel internals.

It is the bootloader's job to provide entropy, by passing a
random u64 value in /chosen/kaslr-seed at kernel entry.

When booting via the UEFI stub, it will invoke the firmware's
EFI_RNG_PROTOCOL implementation (if available) to supply entropy
to the kernel proper. In addition, it will randomise the physical
location of the kernel Image as well.

If unsure, say N.

Kernel Address Space Layout Randomization (KASLR)

Since the location of kernel memory is almost always instrumental in
mounting a successful attack, making the location non-deterministic
raises the difficulty of an exploit. (Note that this in turn makes
the value of information exposures higher, since they may be used to
discover desired memory locations.)

ok, 大概明了是一个安全特性,地址随机,防止被攻击。bootloader提供/chosen/kaslr-seed:

QCOM LK:

162#Enable kaslr seed support
163ifeq ($(ENABLE_KASLRSEED),1)
164 DEFINES += ENABLE_KASLRSEED_SUPPORT=1
165else
166 DEFINES += ENABLE_KASLRSEED_SUPPORT=0
167endif
2139#if ENABLE_KASLRSEED_SUPPORT
2140 if (!scm_random((uintptr_t *)&kaslrseed, sizeof(kaslrseed))) {
2141 /* Adding Kaslr Seed to the chosen node */
2142 ret = fdt_appendprop_u64 (fdt, offset, (const char *)"kaslr-seed", (uint64_t)kaslrseed);
2143 if (ret)
2144 dprintf(CRITICAL, "ERROR: Cannot update chosen node [kaslr-seed] - 0x%x\n", ret);
2145 else
2146 dprintf(CRITICAL, "kaslr-Seed is added to chosen node\n");
2147 } else {
2148 dprintf(CRITICAL, "ERROR: Cannot generate Kaslr Seed\n");
2149 }
2150#endif

同样在UEFI下:

609  Status = GetKaslrSeed (&KaslrSeed);
610 if (Status == EFI_SUCCESS) {
611 /* Adding Kaslr Seed to the chosen node */
612 ret = fdt_appendprop_u64 (fdt, offset, (CONST char *)"kaslr-seed",
613 (UINT64)KaslrSeed);
614 if (ret) {
615 DEBUG ((EFI_D_INFO,
616 "ERROR: Cannot update chosen node [kaslr-seed] - 0x%x\n", ret));
617 } else {
618 DEBUG ((EFI_D_VERBOSE, "kaslr-Seed is added to chosen node\n"));
619 }
620 } else {
621 DEBUG ((EFI_D_INFO, "ERROR: Cannot generate Kaslr Seed - %r\n", Status));
622 }

一样一样的。

btw: arm貌似不支持CONFIG_RANDOMIZE_BASE。

内核有个参数可以直接disable:

   nokaslr         [KNL]
                   When CONFIG_RANDOMIZE_BASE is set, this disables
                   kernel and module base offset ASLR (Address Space
                   Layout Randomization).

kernel具体实现在arch/arm64/kernel/kaslr.c。

留意到高通的power code也有处理:

drivers/power/reset/msm-poweroff.c:261:#ifdef CONFIG_RANDOMIZE_BASE

我们来看下:

#ifdef CONFIG_RANDOMIZE_BASE
static void *kaslr_imem_addr;
static void store_kaslr_offset(void)
{
kaslr_imem_addr = map_prop_mem(KASLR_OFFSET_PROP);

if (kaslr_imem_addr) {
__raw_writel(0xdead4ead, kaslr_imem_addr);
__raw_writel(KASLR_OFFSET_BIT_MASK &
(kimage_vaddr - KIMAGE_VADDR), kaslr_imem_addr + 4);
__raw_writel(KASLR_OFFSET_BIT_MASK &
((kimage_vaddr - KIMAGE_VADDR) >> 32),
kaslr_imem_addr + 8);
iounmap(kaslr_imem_addr);
}
}

保存这个地址到imem上:

kaslr_imem_addr   --->| 0xdead4ead |
kaslr_imem_addr+4 --->| (kimage_vaddr - KIMAGE_VADDR).L32 |
kaslr_imem_addr+8 --->| (kimage_vaddr - KIMAGE_VADDR).H32 |
#define KASLR_OFFSET_PROP "qcom,msm-imem-kaslr_offset"

map_prop_mem():

static void *map_prop_mem(const char *propname)
{
struct device_node *np = of_find_compatible_node(NULL, NULL, propname);
void *addr;

if (!np) {
pr_err("Unable to find DT property: %s\n", propname);
return NULL;
}

addr = of_iomap(np, 0);
if (!addr)
pr_err("Unable to map memory for DT property: %s\n", propname);

return addr;
}

ok,找的设备树,好嘛,dt qcom已经放到私有目录了,就不贴了, kaslr_imem_addr地址来源设备树配置。

KIMAGE_VADDR:

/*
* PAGE_OFFSET - the virtual address of the start of the linear map (top
* (VA_BITS - 1))
* KIMAGE_VADDR - the virtual address of the start of the kernel image
* VA_BITS - the maximum number of bits for virtual addresses.
* VA_START - the first kernel virtual address.
*/
#define VA_BITS (CONFIG_ARM64_VA_BITS)
#define VA_START (UL(0xffffffffffffffff) - \
(UL(1) << VA_BITS) + 1)
#define PAGE_OFFSET (UL(0xffffffffffffffff) - \
(UL(1) << (VA_BITS - 1)) + 1)
#define KIMAGE_VADDR (MODULES_END)
#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)
#define MODULES_VADDR (VA_START + KASAN_SHADOW_SIZE)
#define MODULES_VSIZE (SZ_128M)

可以从这些定义中找出。

kimage_vaddr:

/* the virtual base of the kernel image (minus TEXT_OFFSET) */
extern u64 kimage_vaddr;
ENTRY(kimage_vaddr)
.quad _text - TEXT_OFFSET
#define KERNEL_START      _text
#define KERNEL_END _end

TEXT_OFFSET

Say Y here if you want the image load offset (AKA TEXT_OFFSET)
of the kernel to be randomized at build-time. When selected,

# The byte offset of the kernel image in RAM from the start of RAM.
ifeq ($(CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET), y)
TEXT_OFFSET := $(shell awk "BEGIN {srand(); printf \"0x%06x\n\", \
int(2 * 1024 * 1024 / (2 ^ $(CONFIG_ARM64_PAGE_SHIFT)) * \
rand()) * (2 ^ $(CONFIG_ARM64_PAGE_SHIFT))}")
else
TEXT_OFFSET := 0x00080000 //tj: here
endif

留意一个接口:

static inline unsigned long kaslr_offset(void)
{
return kimage_vaddr - KIMAGE_VADDR;
}

panic时会dump这个kaslr_offset():

/*
* Dump out kernel offset information on panic.
*/
static int dump_kernel_offset(struct notifier_block *self, unsigned long v,
void *p)
{
const unsigned long offset = kaslr_offset();

if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && offset > 0) {
pr_emerg("Kernel Offset: 0x%lx from 0x%lx\n",
offset, KIMAGE_VADDR);
} else {
pr_emerg("Kernel Offset: disabled\n");
}
return 0;
}

like:

505.654475:   <6> Kernel Offset: 0x2c28000000 from 0xffffff8008000000

这个值也存到了QCOM的imem上,QCOM的ramparser会解析,在ramdump.py里:

def determine_kaslr_offset(self):
self.kaslr_offset = 0
if self.kaslr_addr is None:
print_out_str('!!!! Kaslr addr is not provided.')
else:
kaslr_magic = self.read_u32(self.kaslr_addr, False)
if kaslr_magic != 0xdead4ead:
print_out_str('!!!! Kaslr magic does not match.')
else:
self.kaslr_offset = self.read_u64(self.kaslr_addr + 4, False)
print_out_str("The kaslr_offset extracted is: " + str(hex(self.kaslr_offset))) //tj:here

从地址self.kaslr_addr + 4读8个字节,可不就是kaslr_offset()嘛:]