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
相关产品推荐
相关产品推荐

