C语言中如何获取父进程的uid/euid/gid?能否不依赖/proc或ps获取父进程UID?
嘿,我来帮你梳理这两个C语言相关的进程凭证获取问题:
1. 在C语言中获取父进程的UID、EUID以及GID
首先,要拿到父进程的这些身份ID,第一步肯定是用getppid()获取父进程的PID对吧?接下来的关键就是怎么从PID拿到对应的凭证信息:
要是你的系统支持
/proc文件系统(绝大多数Linux发行版都支持),这绝对是最省事的办法。你可以直接读取/proc/[pid]/status文件,里面的Uid和Gid字段就包含了你要的信息——Uid那一行的四个数值分别是real UID、effective UID、saved set UID和filesystem UID,Gid同理。写个简单的C程序就能解析这些内容:打开文件,逐行扫描,匹配到Uid:或Gid:后提取后面的数字就行。但如果你不能碰
/proc(对应第二个问题的要求),那事情就麻烦多了。POSIX标准里根本没有提供跨平台的系统调用,能直接获取另一个进程的凭证信息。不同的UNIX-like系统有各自的“偏方”:- Linux:可以用
ptrace()系统调用,先附加到目标进程,然后读取内核里task_struct结构体里的凭证数据。但这方法有俩大坑:一是需要权限——普通用户只能附加自己的进程,root才能碰任意进程;二是内核版本一变,task_struct的布局可能就改了,代码直接失效,完全不具备可移植性。 - BSD系系统:可以用
kvm接口,通过读取内核虚拟内存来获取进程信息,但同样需要特殊权限,而且得链接特定的库,也是系统专属的。
- Linux:可以用
2. 无需/proc或ps,获取父进程/任意进程的UID
先给你个明确结论:在标准POSIX环境下,根本没有类似getppid()这种直接的系统调用,能拿到父进程或者任意进程的UID。getppid()是POSIX标准里的,但它只返回PID,没给配套的获取身份凭证的接口。
要是必须躲开/proc和ps这种shell工具,那只能靠特定系统的非标准手段:
- Linux下的ptrace方案:简单说就是用
ptrace(PTRACE_ATTACH, pid, NULL, NULL)把目标进程挂起,然后通过ptrace(PTRACE_PEEKDATA)读取进程内存里的凭证信息,最后再用PTRACE_DETACH恢复进程运行。但这方法问题不少:- 权限卡得严:普通用户只能操作自己的进程,root才能为所欲为。
- 会打断目标进程:附加之后进程会暂停,虽然能恢复,但可能影响人家的正常运行。
- 内核版本绑定:进程的UID存在内核的
task_struct里,不同内核版本的结构体偏移不一样,你得对着内核源码找正确的偏移,代码换个内核版本就废了。
给你写个极简的示例(仅适用于特定旧版Linux,别直接拿去用):
#include <sys/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdio.h> #include <errno.h> int get_process_uid(pid_t pid) { if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) { perror("Failed to attach to process"); return -1; } // 等待目标进程进入停止状态 int status; if (waitpid(pid, &status, 0) == -1) { perror("waitpid failed"); ptrace(PTRACE_DETACH, pid, NULL, NULL); return -1; } // 注意:这里的内存偏移是示例,实际要对应你的内核版本 // 你得去查对应内核的task_struct结构体里uid字段的偏移 long uid_val = ptrace(PTRACE_PEEKDATA, pid, (void*)0xc0000000 + 0x10, NULL); if (uid_val == -1 && errno != 0) { perror("Failed to read UID"); ptrace(PTRACE_DETACH, pid, NULL, NULL); return -1; } // 解除附加,让进程继续运行 if (ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1) { perror("Failed to detach from process"); return -1; } return (int)uid_val; } int main() { pid_t parent_pid = getppid(); int parent_uid = get_process_uid(parent_pid); if (parent_uid != -1) { printf("Parent process UID: %d\n", parent_uid); } return 0; }
再次强调,这个示例只是演示思路,实际用的时候得自己查内核源码找正确的偏移,实用性非常有限。
- BSD系统的kvm方案:比如FreeBSD里,你可以用
kvm_openfiles()打开内核文件,然后用kvm_getproc2()获取进程的kinfo_proc结构体,里面就有UID信息。但这需要链接-lkvm库,而且得有读取内核内存的权限,同样是系统专属的。
最后总结一下:
- 要是能用
/proc,那这是Linux下最可靠、最容易实现的方法,没啥坑。 - 要是必须禁用
/proc,那只能用特定系统的非标准接口,不仅权限要求高,还存在兼容性问题,根本没有通用的POSIX级解决方案。
内容的提问来源于stack exchange,提问作者Zibri
相关产品推荐
相关产品推荐

