Skip to content

第一百六十七章:内核地址空间布局

渡劫期

涉及内核源码:

安全山脉的深处,林小源走进一片迷雾笼罩的高原。他按照记忆中的地址前行——0xffffffff81000000,那里应该是内核代码的起点。

但他什么都没找到。

脚下是空旷的荒原,内核根本不在那里。他试着再找,连续走了三次,每次踏上记忆中的地址,都是空无一物。

"你找不到了。"

迷雾中浮现一个模糊的身影,声音低沉而沉稳。那身影没有固定的形态,时而出现在左边,时而出现在右边。

"我是 KASLR,Kernel Address Space Layout Randomization。每次系统启动,我都会把内核在内存中的位置随机偏移。你以为你记住了地址?下次启动就变了。"

林小源皱眉:"随机化?"

"对,"KASLR 的声音从四面八方传来,"内核代码、内核数据、模块加载地址——我全都挪。攻击者就算知道内核的结构,也不知道它此刻藏在哪里。"

"能偏移多少?"

"256 种可能,每次启动随机选一个。听起来不多,但对攻击者来说,猜错一次就是内核崩溃——蓝屏。谁敢赌?"

c
/*
 * KASLR (Kernel Address Space Layout Randomization):
 *
 * 作用:
 *   随机化内核在内存中的位置
 *   每次启动地址不同
 *   攻击者无法预测目标地址
 *
 * 随机化的对象:
 *   内核代码 (.text)
 *   内核数据 (.data)
 *   模块加载地址
 *   内核栈地址
 *
 * 绕过方法:
 *   信息泄漏 — 泄漏内核地址
 *   侧信道 — 利用缓存时序
 *   暴力破解 — 多次尝试
 *
 * 防御:
 *   KASLR — 随机化地址
 *   SMEP — 禁止执行用户空间代码
 *   SMAP — 禁止访问用户空间数据
 *   PAN — 禁止直接访问用户空间
 */

/* 模拟内核地址空间布局 */
struct kernel_layout {
    unsigned long kernel_base;
    unsigned long text_start;
    unsigned long text_end;
    unsigned long data_start;
    unsigned long data_end;
    unsigned long module_start;
    unsigned long module_end;
};

/* 模拟 KASLR 偏移 */
unsigned long kaslr_offset(void) {
    return (rand() % 0x1000) * 0x100000;
}

void print_layout(struct kernel_layout *l) {
    printf("  内核基址:   0x%lx\n", l->kernel_base);
    printf("  .text:      0x%lx - 0x%lx\n", l->text_start, l->text_end);
    printf("  .data:      0x%lx - 0x%lx\n", l->data_start, l->data_end);
    printf("  模块:       0x%lx - 0x%lx\n", l->module_start, l->module_end);
}

printf("=== KASLR — 隐藏内核位置 ===\n\n");

srand(time(NULL));

/* 没有 KASLR 的布局 */
printf("--- 没有 KASLR ---\n");
struct kernel_layout no_kaslr = {
    .kernel_base = 0xffffffff81000000,
    .text_start = 0xffffffff81000000,
    .text_end = 0xffffffff81800000,
    .data_start = 0xffffffff81800000,
    .data_end = 0xffffffff81c00000,
    .module_start = 0xffffffff81c00000,
    .module_end = 0xffffffff82000000,
};
print_layout(&no_kaslr);
printf("  地址固定,攻击者知道目标\n\n");

/* 有 KASLR 的布局 */
printf("--- 有 KASLR ---\n");
for (int i = 0; i < 3; i++) {
    printf("启动 %d:\n", i + 1);
    unsigned long offset = kaslr_offset();
    struct kernel_layout kaslr = {
        .kernel_base = 0xffffffff81000000 + offset,
        .text_start = 0xffffffff81000000 + offset,
        .text_end = 0xffffffff81800000 + offset,
        .data_start = 0xffffffff81800000 + offset,
        .data_end = 0xffffffff81c00000 + offset,
        .module_start = 0xffffffff81c00000 + offset,
        .module_end = 0xffffffff82000000 + offset,
    };
    print_layout(&kaslr);
    printf("  偏移: 0x%lx\n\n", offset);
}

printf("--- 防御机制 ---\n");
printf("KASLR:\n");
printf("  随机化内核地址\n");
printf("  每次启动不同\n\n");
printf("SMEP:\n");
printf("  禁止执行用户空间代码\n");
printf("  防止跳转到用户空间\n\n");
printf("SMAP:\n");
printf("  禁止访问用户空间数据\n");
printf("  防止读写用户空间\n\n");

printf("--- 绕过方法 ---\n");
printf("1. 信息泄漏:\n");
printf("   泄漏内核地址\n");
printf("   /proc/kallsyms\n\n");
printf("2. 侧信道:\n");
printf("   缓存时序攻击\n\n");
printf("3. 暴力破解:\n");
printf("   多次尝试\n");
printf("   256 种可能\n");

林小源在迷雾中摸索前行,KASLR 的身影始终若隐若现。

"你的随机化……有没有弱点?"

KASLR 的声音沉默了一瞬,然后坦然道:"有。如果攻击者能泄漏一个内核地址——比如通过 /proc/kallsyms,或者某个漏洞泄出一个指针——就能反推出偏移量,然后算出所有地址。"

"那不是白费功夫?"

"不是,"KASLR 的语气变得严肃,"信息泄漏本身就是一个漏洞。我的存在,迫使攻击者必须先找到一个泄漏点,才能继续攻击。这多了一道门槛。而且我不是独自战斗——SMEP 禁止内核执行用户空间的代码,SMAP 禁止内核直接读写用户空间的数据。三道防线叠加,攻击者要同时绕过三个机制。"

迷雾中浮现出另外两个身影——一个刻着 "SMEP",一个刻着 "SMAP",静静矗立在 KASLR 身后。

"纵深防御,"林小源明白了。

高原的边缘,林小源俯瞰下方的内存全景。没有 KASLR 时,内核的位置一览无余——攻击者拿着一张精确的地图就能直达心脏。有了 KASLR,那张地图每次开机都要重画。

"但我听说,"林小源转身面对 KASLR,"有人用侧信道攻击绕过你?"

KASLR 的身影微微波动:"缓存时序攻击。攻击者测量内存访问时间,推断哪些地址被缓存过,从而缩小搜索范围。还有暴力破解——在虚拟机环境下,如果崩了就重启,多试几次总能猜中。"

"所以你不是万能的。"

"没有哪个防御是万能的,"KASLR 平静地说,"我的价值在于增加攻击成本。从'一眼看穿'变成'需要先泄漏再计算再验证'。每多一步,就有更多攻击者放弃。"

林小源望向远方。安全从来不是一堵不可逾越的高墙,而是一层又一层的迷雾——每一层都可能被穿透,但穿过后还有下一层。


道藏笔记

内核启示

没有 KASLR 的时候,内核代码、数据、模块加载地址每次都一样,攻击者拿着一张固定地图就能直达心脏。KASLR 每次启动把这些地址随机偏移,地图每次开机都要重画。

但 KASLR 不是万能的。如果攻击者能通过 /proc/kallsyms 或某个漏洞泄漏一个内核地址,就能反推偏移量算出所有地址。侧信道攻击也能缩小搜索范围。所以 KASLR 不是单独作战——SMEP 禁止内核执行用户空间代码,SMAP 禁止内核直接读写用户空间数据,三道防线叠加,攻击者得同时绕过才行。

KASLR 是屏障——隐藏位置,增加攻击难度。


破关试炼

KASLR 之试

内核地址空间布局随机化、让攻击者难以猜中内核地址的机制缩写是什么?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道