Linux下C语言实现以root权限重试加载内核模块的架构方案
权限提升与内核模块加载程序实现方案
一、权限检查与自动提权流程
- 程序启动时,通过
cap_get_proc()检查是否具备CAP_SYS_MODULE权限:- 若具备,直接调用
init_module()或finit_module()执行内核模块加载逻辑。 - 若不具备,检查当前UID是否为0:
- 是则直接返回错误(已为root但无权限属于异常场景)。
- 否则,尝试通过
sudo或pkexec重新启动自身,同时通过环境变量MY_MODULE_LOADER_ELEVATED=1标记提权后的执行模式。
- 若具备,直接调用
- 提权后的进程启动时,先检查环境变量
MY_MODULE_LOADER_ELEVATED:- 若存在,跳过提权流程,直接执行加载逻辑并将结果反馈给父进程。
- 若不存在,进入正常权限检查流程。
二、父子进程可靠IPC实现(Unix域套接字)
针对文件描述符传递依赖执行链的问题,采用Unix域套接字实现稳定的父子进程通信,流程如下:
- 父进程(非root)创建临时Unix域套接字,路径设为
/tmp/.module_loader_sock_<PID>(PID为父进程ID,避免路径冲突)。 - 父进程监听该套接字,等待子进程连接。
- 父进程通过
sudo/pkexec启动自身时,将套接字路径通过环境变量MY_MODULE_LOADER_SOCK传递给子进程。 - 提权后的子进程读取环境变量中的套接字路径,连接该套接字。
- 子进程执行加载逻辑后,将结果(成功/失败状态、错误码等)通过套接字发送给父进程。
- 父进程接收结果后,关闭并删除套接字,根据结果处理错误后退出。
核心代码片段
父进程创建套接字并启动提权进程:
#include <sys/socket.h> #include <sys/un.h> #include <stdlib.h> #include <unistd.h> int main() { // 创建Unix域套接字 int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), "/tmp/.module_loader_sock_%d", getpid()); unlink(addr.sun_path); // 确保路径无残留 bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); listen(sockfd, 1); // 设置环境变量传递套接字路径 setenv("MY_MODULE_LOADER_SOCK", addr.sun_path, 1); setenv("MY_MODULE_LOADER_ELEVATED", "1", 1); // 优先尝试sudo,失败则用pkexec execlp("sudo", "sudo", "./module_loader", NULL); execlp("pkexec", "pkexec", "./module_loader", NULL); // 执行到此处说明提权工具调用失败 perror("Failed to invoke sudo/pkexec"); unlink(addr.sun_path); close(sockfd); return 1; }
子进程发送执行结果:
#include <sys/socket.h> #include <sys/un.h> #include <stdlib.h> #include <unistd.h> int load_kernel_module() { // 实际加载逻辑,返回0表示成功,非0为错误码 return 0; } int main() { if (getenv("MY_MODULE_LOADER_ELEVATED")) { const char* sock_path = getenv("MY_MODULE_LOADER_SOCK"); if (!sock_path) exit(1); int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path)-1); if (connect(sockfd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { perror("Failed to connect to parent socket"); exit(1); } int result = load_kernel_module(); write(sockfd, &result, sizeof(result)); close(sockfd); exit(result); } // 非提权模式下的权限检查逻辑... return 0; }
三、内核模块与用户态私有IPC方案(Netlink)
若要避免在/dev下创建设备文件,Netlink是最优的私有IPC选择——它基于套接字机制,无需额外设备文件,且支持双向通信,仅需约定私有协议号即可实现专属通信通道:
- 内核模块注册Netlink私有协议(可选用12-31范围内的预留私有协议号,或动态分配)。
- 用户态程序创建Netlink套接字,与内核模块的协议号绑定。
- 双方通过
nlmsg_*系列函数构造、解析消息,完成命令交互与结果传递。
内核模块核心代码
#include <linux/netlink.h> #include <linux/socket.h> #include <linux/module.h> #define NETLINK_MY_MODULE 31 // 私有协议号 static struct sock *nl_sock; static void handle_user_msg(struct sk_buff *skb) { struct nlmsghdr *nlh = nlmsg_hdr(skb); // 处理用户态发送的消息,执行对应逻辑 // 构造并发送回复消息... } static int __init my_module_init(void) { nl_sock = netlink_kernel_create(&init_net, NETLINK_MY_MODULE, 0, handle_user_msg, NULL, THIS_MODULE); if (!nl_sock) return -ENOMEM; return 0; } static void __exit my_module_exit(void) { netlink_kernel_release(nl_sock); } module_init(my_module_init); module_exit(my_module_exit); MODULE_LICENSE("GPL");
用户态核心代码
#include <linux/netlink.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #define NETLINK_MY_MODULE 31 #define MAX_PAYLOAD 1024 int main() { int sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_MY_MODULE); struct sockaddr_nl addr; memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_pid = getpid(); bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); // 构造发送到内核的消息 struct nlmsghdr *nlh = (struct nlmsghdr*)malloc(NLMSG_SPACE(MAX_PAYLOAD)); memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD)); nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD); nlh->nlmsg_pid = getpid(); nlh->nlmsg_flags = 0; struct iovec iov; iov.iov_base = (void*)nlh; iov.iov_len = nlh->nlmsg_len; struct msghdr msg; memset(&msg, 0, sizeof(msg)); msg.msg_name = (void*)&addr; msg.msg_namelen = sizeof(addr); msg.msg_iov = &iov; msg.msg_iovlen = 1; sendmsg(sockfd, &msg, 0); // 接收内核回复消息 recvmsg(sockfd, &msg, 0); // 处理回复内容... free(nlh); close(sockfd); return 0; }
内容的提问来源于stack exchange,提问作者Programmer_2147483647
相关产品推荐
相关产品推荐

