获得进程ID
getpid函数
这个函数都用了很多次了,看一下定义和例子就行了
#include<sys/types.h>
#include <unistd.h>
pid_t getpid(void);
示例程序1
#include<cstdlib>
#include<malloc.h>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;
int main(){pid_t pid;if((pid=fork())==-1){printf("fork error!\n");exit(1);}if(pid==0)printf("getpid return %d\n",getpid());//子进程exit(0);
}
setuid和setgid
用setuid设置实际用户ID和有效用户ID.setgid用来设置实际组ID和有效组ID
实际用户和有效用户解释见 《Linux C编程实战》笔记:进程操作之创建进程-CSDN博客
#include <sys/types.h>
#include <unistd.h>
int setuid(uid_t uid);
int seteuid(uid_t uid);//使用seteuid只会设置有效用户,无论是什么权限
int setgid(gid_t gid);
int setegid(gid_t gid);
设置用户ID的setuid函数遵守以下规则(设置组ID的setgid函数与此类似)。
若进程具有root权限,则函数将实际用户ID、有效用户ID设置为参数uid。
若进程不具有root权限,则setuid只将有效用户ID设置为uid,不改变实际用户ID。
若以上两个条件都不能满足,则函数调用失败,返回-1,并设置errno为 EPERM。
从以上规则可以看出,只有超级用户进程才能更改实际用户ID,所以一个非特权用户进程是不能通过setuid或seteuid得到特权用户权限的。
内核时检查一个进程是否有权限访问某文件时,是使用进程的有效用户ID来检查的。
像su命令,普通用户运行时,su进程的权限是root权限
示例程序2
演示有效用户
#include<cstdlib>
#include<malloc.h>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;
int main(){int fd;printf("uid study:\n");printf("Process;s uid=%d,euid=%d\n",getuid(),geteuid());//seteuid(0);//printf("Process;s uid=%d,euid=%d",getuid(),geteuid());if((fd=open("test.c",O_RDWR))==-1){//想要以读写方式打开一个文件printf("Open failure,errno is %d:%s \n",errno,strerror(errno));exit(1);}else printf("Open successfully!\n");close (fd);exit(0);
}
首先,我们以root用户创建一个文件,可以看到其他用户只能读不能写
我们切换到一个普通用户运行程序,由于程序想要读写文件,是没有权限的
我们把注释取消看看这回能不能读写
首先要在root下位程序文件设置set_uid位,不然程序也是改变不了uid的
- 最前面的 "4" 代表了setuid位。
- "7" 代表了所有者的权限,包括读(4) + 写(2) + 执行(1)。
- "5" 代表了组的权限,包括读(4) + 执行(1)。
- "5" 代表了其他用户的权限,包括读(4) + 执行(1)。
不过在我的Linux下,即使设置了chmod,依然读不了文件。我也不知道书上是怎么成功的,这个例子我只能以失败告终。
顺便了解
strerror
是一个 C 语言标准库函数,用于将错误码(通常由全局变量errno
表示)转换为对应的错误消息字符串。函数原型通常定义在<string.h>
头文件中,其一般形式如下:
#include <string.h> char *strerror(int errnum);
参数
errnum
是一个整数,通常是全局变量errno
的值,表示发生的错误码。strerror
函数返回一个指向错误消息字符串的指针,该字符串描述了对应错误码的错误信息。
可以这样使用setuid函数:开始时某个程序需要root权限完成一些工作,但后续的工作不需要root权限。可以将该可执行程序文件设置set_uid位,并使得该文件的属主是root。这样普通用户执行这个程序时,进程就具有了root权限,当不再需要root权限时,调用setuid(getuid())恢复进程的实际用户ID和有效用户ID为执行该程序的普通用户的ID.对于一些提供网络服务的程序,这样做是非常有必要的,否则就可能被攻击者利用,使攻击者控制整个系统。
对于设置了set_uid 位的可执行程序也要注意,尤其是对那些属主是root 的更要注意。因为Linux 系统中 root用户拥有最高权力。黑客们往往喜欢寻找设置了set_uid位的可执行程序的漏洞,这样的程序如果存在缓冲区溢出漏洞,并且该程序是一个网络程序,那么黑客可以从远程的地方轻松地利用该漏洞获得运行该漏洞程序的主机的root权限。即使这样的程序不是网络程序,那么也可以使本机上的恶意普通用户提升为root用户。
改变进程优先级
nice函数
nice系统调用用来改变调用进程的优先级。
#include <unistd.h>
int nice (int increment);
在介绍nice系统调用的用法前,需要先了解两个重要的函数:getpriority和setpriority,它们声明如下:
#include <sys/resource.h>
int getpriority(int which, int who);
int setpriority(int which, int who,int prio);
getpriority函数:该函数返回一组进程的优先级。参数 which和 who组合确定返回哪一组进程的优先级。which的可能取值以及 who的意义如下。
PRIO_PROCESS:一个特定的进程,此时who的取值为进程ID。
PRIO_PGRP:一个进程组的所有进程,此时who的取值为进程组ID。
PRIO_USER:一个用户拥有的所有进程,此时参数who取值为实际用户ID。
getpriority函数如果调用成功返回指定进程的优先级,如果出错将返回-1,并设置errno的值。errno可能的取值如下。
ESRCH: which和 who的组合与现存的所有进程均不匹配。
EINVAL: which是个无效的值。
setpriority函数:该函数用来设置指定进程的优先级。进程指定的方法与getpriority 函数相同。如果调用成功,函数返回指定进程的优先级,出错则返回-1,并设置相应errno。除了产生与getpriority相同的两个错误外,还有可能产生以下错误。
EPERM:要设置优先级的进程与当前进程不属于同一个用户,并且当前进程没有CAP_SYS_NICE特许。
EACCES:该调用可能降低进程的优先级并且进程没有CAP_SYS_NICE特许。
nice是它们的组合形式,nice系统调用等于
int nice(int increment){int oldpro=getpriority(PRIO_PROCESS,getpid());return setpriority(PRIO_PROCESS,getpid(),oldpro+increment);
}
就相当于 原本的优先级加上increment参数,返回新的优先级
示例程序3
#include<cstdlib>
#include<malloc.h>
#include<cstring>
#include <cstdio>
#include<ctime>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/wait.h>
#include<sys/resource.h>
#include<fcntl.h>
#include<unistd.h>
#include<cerrno>
using namespace std;
int main(){pid_t pid;int stat_val=0;int oldpri,newpri;printf("nice study\n");pid=fork();switch (pid){case 0:printf("Child is running,CurPid is %d,ParentPid is %d\n",getpid(),getppid());oldpri=getpriority(PRIO_PROCESS,getpid());printf("Old priority=%d\n",oldpri);newpri=nice(2);//优先级提升2printf("New priority=%d\n",newpri);exit(0);case -1:perror("Process creation failed\n");break;default:printf("Parent is running,ChildPid is %d,ParentPid is %d\n",pid,getpid());break;}wait(&stat_val);exit(0);
}