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

sys_execve()中替换可执行文件失效问题的原因排查与修复咨询

问题描述

我修改了内核sys_execve()系统调用,试图将/usr/bin/echo替换为/usr/bin/ls,日志显示替换逻辑已触发,但实际执行的仍是echo而非ls。附修改后的内核代码、QEMU启动命令及测试输出:

修改后的内核代码

SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    const char *kernel_filename;
    kernel_filename = strndup_user(filename, PATH_MAX);
    if (IS_ERR(kernel_filename)) {
        return PTR_ERR(kernel_filename);
    }

    if (strcmp(kernel_filename, "/usr/bin/echo") == 0) {
        printk(KERN_INFO "Modifying from %s to /usr/bin/ls\n", kernel_filename);
        const char * const* k_argv;
        const char * const* k_envp;
        int argc = 0;
        int envc = 0;
        copy_exec_args(argv, envp, &k_argv, &k_envp, &argc, &envc);
        int res  = kernel_execve("/usr/bin/ls", k_argv, k_envp);
        kfree(kernel_filename);
        if (k_argv) {
            for (int i = 0; i < argc; i++)
                kfree(k_argv[i]);
            kfree(k_argv);
        }
        if (k_envp) {
            for (int i = 0; i < envc; i++)
                kfree(k_envp[i]);
            kfree(k_envp);
        }
        return res;
    }
    kfree(kernel_filename);

    return do_execve(getname(filename), argv, envp);
}

QEMU启动命令

qemu-system-x86_64 \
    -kernel ./linux/arch/x86_64/boot/bzImage \
    -initrd ./ram.img \
    -drive file=vm-disk.qcow2,format=qcow2 \
    -m 2G \
    -append "root=/dev/sda rw console=ttyS0" \
    -nographic

测试输出

(initramfs) /usr/bin/echo hello
[  195.914196] Modifying from /usr/bin/echo to /usr/bin/ls
[  195.915116] kernel_execve: /usr/bin/ls
[  195.915931] /usr/bin/ls
[  195.916546] /usr/bin/ls
hello
失效原因

你直接调用kernel_execve后返回,原sys_execve的用户态调用上下文(栈、指令指针等)并未被完全覆盖。kernel_execve内部完成了新程序的加载,但执行完毕后进程会回到原echo进程的用户态代码继续运行,所以最终输出了hello而非ls的结果。

另外,手动调用copy_exec_args拷贝参数的方式存在风险:该函数并非内核公开稳定API,且你在调用kernel_execve后重复释放参数内存,可能引发内存错误。

修复方法

放弃直接调用kernel_execve,改为修改传入do_execve的文件名参数,复用原sys_execve的完整流程,确保进程用户态地址空间被完全替换:

修改后的代码示例

SYSCALL_DEFINE3(execve,
        const char __user *, filename,
        const char __user *const __user *, argv,
        const char __user *const __user *, envp)
{
    struct filename *name;
    const char *kernel_filename;
    kernel_filename = strndup_user(filename, PATH_MAX);
    if (IS_ERR(kernel_filename)) {
        return PTR_ERR(kernel_filename);
    }

    if (strcmp(kernel_filename, "/usr/bin/echo") == 0) {
        printk(KERN_INFO "Modifying from %s to /usr/bin/ls\n", kernel_filename);
        // 构造内核态的文件名结构
        name = getname_kernel("/usr/bin/ls");
        kfree(kernel_filename);
        if (IS_ERR(name)) {
            return PTR_ERR(name);
        }
        return do_execve(name, argv, envp);
    }

    kfree(kernel_filename);
    name = getname(filename);
    if (IS_ERR(name)) {
        return PTR_ERR(name);
    }
    return do_execve(name, argv, envp);
}

关键修改点

  • 复用原do_execve流程,确保进程完成完整的地址空间替换,彻底覆盖原echo的用户态上下文
  • 使用getname_kernel构造内核态文件名,避免用户态地址的权限问题
  • 无需手动拷贝argv和envp,让do_execve自行处理参数的安全拷贝与验证

修改后重新编译内核并启动QEMU,执行/usr/bin/echo hello时,会实际运行ls命令,输出当前目录的文件列表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 22:46:13