第一百六十七章:内核地址空间布局
渡劫期涉及内核源码:
一
安全山脉的深处,林小源走进一片迷雾笼罩的高原。他按照记忆中的地址前行——0xffffffff81000000,那里应该是内核代码的起点。
但他什么都没找到。
脚下是空旷的荒原,内核根本不在那里。他试着再找,连续走了三次,每次踏上记忆中的地址,都是空无一物。
"你找不到了。"
迷雾中浮现一个模糊的身影,声音低沉而沉稳。那身影没有固定的形态,时而出现在左边,时而出现在右边。
"我是 KASLR,Kernel Address Space Layout Randomization。每次系统启动,我都会把内核在内存中的位置随机偏移。你以为你记住了地址?下次启动就变了。"
林小源皱眉:"随机化?"
"对,"KASLR 的声音从四面八方传来,"内核代码、内核数据、模块加载地址——我全都挪。攻击者就算知道内核的结构,也不知道它此刻藏在哪里。"
"能偏移多少?"
"256 种可能,每次启动随机选一个。听起来不多,但对攻击者来说,猜错一次就是内核崩溃——蓝屏。谁敢赌?"
/*
* 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");#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/*
* 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);
}
int main() {
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");
return 0;
}二
林小源在迷雾中摸索前行,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 之试
内核地址空间布局随机化、让攻击者难以猜中内核地址的机制缩写是什么?