在编写网络服务器程序时,为了响应客户端的请求,我们经常需要新建进程来处理业务流程;而且又是为了关闭某个非法请求或者关闭长连接的客户端,这时就需要杀死进程 killall proc_name。 但是在新建进程时,子进程名与父进程名相同。因此需要由进程名及参数来区分客户端连接。
在linux中prctl可以满足这个要求,下满是man手册:
PR_SET_NAME (since Linux 2.6.9)
Set the process name for the calling process, using the value in
the location pointed to by (char *) arg2.
The name can be up to
16 bytes long, and should be null terminated if it contains
fewer bytes.
但是prctl修改的进程名,只能是16个字节(包括'\0')。下面是修改的代码(changetitle.c):
-
#include <stdio.h>
-
#include <sys/prctl.h>
-
-
int main(int argc, char *argv[], char *envp[])
-
{
-
char *new_name = "abcdefghijklmnopqrstuvwxyz";
-
-
getchar();
-
prctl(PR_SET_NAME, new_name);
-
getchar();
-
-
return 0;
-
}
当新名称长度大于16时就会截断,上面的新名字截断后是
abcdefghijklmno。这对于我们来说是有缺陷的。而且通过ps -aux 查看,进程名称并没有改变,改变的只是/prco/$(PID)/stat和
/prco/$(PID)/status的值,而/prco/$(PID)/cmdline并没有改变。这种方式使用起来也是不方便的。
下面介绍另一种方式,可以与上面的方式互补。
首先看一下main函数的原型:int main(int argc, char *argv[]);
argv[0]存放的是终端执行的程序名称也就是进程名。argv[1...argc-1]存放的是命令行参数。
linux中main()还有一个隐藏参数就是环境变量信息,存放了运行时所需要的环境变量。
我们可以通过以下来访问这个变量
argv与environ是连续存放在栈区的。下面代码可以查看参数信息:
-
#include <stdio.h>
-
#include <string.h>
-
-
extern char **environ;
-
int main(int argc , char *argv[])
-
{
-
int i;
-
-
printf("argc:%d\n" , argc);
-
-
for (i = 0; i < argc; ++i)
-
{
-
printf("argv[%d](0x%x):%s\n" , i , (unsigned int)argv[i], argv[i]);
-
}
-
-
printf("evriron=0x%x\n" , (unsigned int)environ[0]);
-
-
return 0;
-
}
通过上面可以看出,我们只需要修改argv[0]所指向的内存空间的内容,就可以修改进程名。但是如果新名称比argv[0]的长度小,我们可以直接修改,并把多余的部分请0,如果新名称
比argv[0]长我们需要两步:
1、申请新内存保存环境变量信息和argv[1...argc-1]参数信息
2、修改argv[0],将新名称往后到environ的最后一项清0
以下是参考代码:
-
#include <unistd.h>
-
#include <stdio.h>
-
#include <stdarg.h>
-
#include <string.h>
-
#include <stdlib.h>
-
#include <sys/prctl.h>
-
-
# define MAXLINE 2048
-
-
extern char **environ;
-
-
static char **g_main_Argv = NULL; /* pointer to argument vector */
-
static char *g_main_LastArgv = NULL; /* end of argv */
-
-
void setproctitle_init(int argc, char **argv, char **envp)
-
{
-
int i;
-
-
for (i = 0; envp[i] != NULL; i++) // calc envp num
-
continue;
-
environ = (char **) malloc(sizeof (char *) * (i + 1)); // malloc envp pointer
-
-
for (i = 0; envp[i] != NULL; i++)
-
{
-
environ[i] = malloc(sizeof(char) * strlen(envp[i]));
-
strcpy(environ[i], envp[i]);
-
}
-
environ[i] = NULL;
-
-
g_main_Argv = argv;
-
if (i > 0)
-
g_main_LastArgv = envp[i - 1] + strlen(envp[i - 1]);
-
else
-
g_main_LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
-
}
-
-
void setproctitle(const char *fmt, ...)
-
{
-
char *p;
-
int i;
-
char buf[MAXLINE];
-
-
extern char **g_main_Argv;
-
extern char *g_main_LastArgv;
-
va_list ap;
-
p = buf;
-
-
va_start(ap, fmt);
-
vsprintf(p, fmt, ap);
-
va_end(ap);
-
-
i = strlen(buf);
-
-
if (i > g_main_LastArgv - g_main_Argv[0] - 2)
-
{
-
i = g_main_LastArgv - g_main_Argv[0] - 2;
-
buf[i] = '\0';
-
}
-
(void) strcpy(g_main_Argv[0], buf);
-
-
p = &g_main_Argv[0][i];
-
while (p < g_main_LastArgv)
-
*p++ = '\0';
-
g_main_Argv[1] = NULL;
-
-
prctl(PR_SET_NAME,buf);
-
}
-
-
int main(int argc, char *argv[])
-
{
-
char argv_buf[MAXLINE] = {0}; // save argv paramters
-
-
for(int i = 1; i < argc; i++)
-
{
-
strcat(argv_buf, argv[i]);
-
strcat(argv_buf, " ");
-
}
-
-
setproctitle_init(argc, argv, environ);
-
-
setproctitle("%s@%s %s", "new_name", "ip", argv_buf);
-
-
for (int i = 0; environ[i] != NULL; i++)
-
free(environ[i]);
-
getchar();
-
-
return 0;
-
}
上面的代码使用了prctl和修改argv[0]两种修改方法的结合,通过ps -a 、 ps -ef 、ps -aux、 top 等等命令都只能查询到新进程名,/proc/$PID/ 下的文件也显示了新进程名的信息。
应用场景:
1、标识父子进程名称,防止被误杀
2、构造假的进程名及参数,引导非法进入人员到蜜罐系统,取证
阅读(21017) | 评论(0) | 转发(0) |