给一个Linux内核栈被破坏的示例

Linux 内核栈破坏示例

以下是一个通过内核模块故意破坏内核栈的示例。该模块通过缓冲区溢出覆盖栈上的关键数据(如返回地址),导致内核崩溃(Kernel Panic)。

代码示例 (stack_corrupt.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>

// 故意破坏栈的函数
static void corrupt_stack(void) {
char small_buffer[16]; // 小缓冲区(易溢出)

printk(KERN_INFO "准备破坏内核栈...\n");

// 故意越界写入(破坏栈帧和返回地址)
memset(small_buffer, 0xAA, 256); // 写入256字节(远超缓冲区大小)

// 此行代码通常不会执行
printk(KERN_INFO "栈破坏完成!\n");
}

// 模块初始化
static int __init stack_corrupt_init(void) {
printk(KERN_INFO "加载栈破坏模块\n");
corrupt_stack(); // 调用破坏函数
return 0;
}

// 模块退出
static void __exit stack_corrupt_exit(void) {
printk(KERN_INFO "卸载模块\n");
}

module_init(stack_corrupt_init);
module_exit(stack_corrupt_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("内核栈破坏示例");

关键点解析

  1. 缓冲区溢出:

    • small_buffer 只分配了 16 字节。
    • memset(small_buffer, 0xAA, 256) 写入 256 字节,覆盖栈上的:
      • 函数返回地址
      • 栈帧指针
      • 其他局部变量
  2. 后果:

    • corrupt_stack() 返回时,CPU 尝试跳转到无效地址 (0xAAAAAAAA)。
    • 触发 **内核恐慌 (Kernel Panic)**,系统崩溃。
    • 日志中会出现错误(如 "general protection fault""invalid opcode")。

如何测试

  1. 编译模块:

    1
    make -C /lib/modules/$(uname -r)/build M=$PWD modules
  2. 加载模块(导致崩溃):

    1
    sudo insmod stack_corrupt.ko
  3. 查看内核日志(崩溃前):

    1
    dmesg | tail

    输出示例:

    1
    2
    3
    [ 1234.567890] 加载栈破坏模块
    [ 1234.567891] 准备破坏内核栈...
    [ 1234.567892] 内核崩溃信息...

常见栈破坏原因

  1. 内核缓冲区溢出:

    • 未检查用户空间复制操作(如 copy_from_user())。
    • 错误的数组索引或指针运算。
  2. 递归过深:

    • 无限递归耗尽内核栈(通常 8-16 KB)。
  3. 野指针操作:

    • 访问已释放的内存或无效指针。

调试建议

  1. KASAN (Kernel Address Sanitizer):

    • 检测内存越界访问(需开启内核编译选项)。
  2. Oops 分析:

    • 通过 dmesg 查看崩溃时的寄存器状态和调用栈。
  3. 栈保护机制:

    • CONFIG_STACKPROTECTOR:内核栈 Canary 值保护。
    • CONFIG_FRAME_POINTER:保留帧指针用于回溯。

警告:此示例会立即导致系统崩溃!仅在测试环境使用。