freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Linux进程隐藏:初级篇
2020-09-23 15:52:06

前言

Linux下查看进程信息的途径通常有以下几种方式

途径说明
top、ps等命令通过ps及top命令查看进程信息时,只能查到相对路径,查不到的进程的详细信息,如绝对路径等。常用命令如下
ps -A 查看所有进程名称;
ps -ef 接显示进程绝对路径及参数;
ps -aux显示所有用户进程状态,最后一列显示进程绝对路径及参数;
top命令 参考http://c.biancheng.net/view/1065.html
/proc/pid/文件夹Linux在启动一个进程时,系统会在/proc下创建一个以pid命名的文件夹,在该文件夹下会有我们的进程的信息,其中包括一个名为exe的文件即记录了绝对路径,通过ll或ls –l命令即可查看。exe实际运行程序的符号链接;
cmdline 一个只读文件,包含进程的完整命令行信息;
comm 包含进程的命令名;
cwd 进程当前工作目录的符号链接;
status 进程状态信息,包含的信息多于stat;
stat 进程状态信息;
cwd 进程当前工作目录的符号链接;
latency 显示哪些代码造成的延时比较大;
environ记录了进程运行时的环境变量;
fd目录下是进程打开或使用的文件的符号连接。

初级隐藏篇介绍以下两种方式修改隐藏进程名

1. 通过修改进程argv[0]修改进程名
2. 通过Linux prctl修改进程名

一、通过修改进程argv[0]修改进程名

优缺点:

优点是ps -ef 、ps -aux看不到进程名及参数了

缺点是这种方法仅仅是修改了/prco/pid/cmdline 的值,使用ps -A 或者top 命令还是可以看到进程名称

我们知道在一个程序中,参数的个数保存在int型argc中,参数保存在数组argv[]中,数组的第一个元素argv[0]保存的就是进程名,第二个元素argv[1]保存的是第一个参数,依次类推。通过修改进程argv[0]修改进程名,这一方法实现比较简单,我们只要在进程启动mian函数中修改掉argv数组所指向的内存空间的内容即可,这里需要注意的是 linux中main()还有一个隐藏参数就是环境变量信息,存放了运行时所需要的环境变量,

  1. 如果新名称比argv[0]的长度小,我们可以直接修改,并把多余的部分请0

  2. 如果新名称比argv[0]长我们需要两步

​ 1) 申请新内存保存环境变量信息和argv[1…argc-1]参数信息

​ 2) 修改argv[0],将新名称往后到environ的最后一项清0

以下示例代码仅仅是将argv[]清空

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {


    printf("========== Before the modification ============\n");
    printf("ProcessName: %s\n", argv[0]);
    for(int i = 1; i < argc; i ++){
        printf("Argv%d: %s\n", i, argv[i]);
    }


    /* Start the modification */
    int new_argc = argc;
    char ** new_argv = malloc((argc+1) * sizeof(*new_argv));
    for(int j = 0; j < argc; j++)
    {
        size_t length = strlen(argv[j]) + 1;
        new_argv[j] = malloc(length);
        memcpy(new_argv[j], argv[j], length);
        memset(argv[j], '\0', length);
    }


    printf("========== After the modification ============\n");
    printf("ProcessName: %s\n", argv[0]);
    for(int i = 1; i < argc; i ++){
        printf("Argv%d: %s\n", i, argv[i]);
    }


    printf("========== Copy data ============\n");
    printf("ProcessName: %s\n", new_argv[0]);
    for(int k = 1; k < new_argc; k ++){
        printf("Argv%d: %s\n", k, new_argv[k]);
    }


    sleep(1000);
    return 0;
}

二、通过Linux prctl修改进程名

优缺点:

优点是修改了/prco/pid/stat及/prco/pid/status中的进程名称,使用ps -A 或者top 命令看不到原来的进程名称

缺点是未修改/prco/pid/cmdline 的值,使用ps -ef 、ps -aux可以看到进程名称及参数

使用prctl修改进程名实现也比较简单,

看下面代码

/*
gcc changetitle.c -o changetitle
*/
#include <stdio.h>
#include <sys/prctl.h>

int main(int argc, char *argv[], char *envp[])
{
    char *new_name = "1234567890abcdefg";
    
    getchar();
    prctl(PR_SET_NAME, new_name);
    getchar();




    return 0;
}

但是prctl修改的进程名,只能是16个字节(包括’\0’),当新名称长度大于16时就会截断,上面的新名字截断后是1234567890abcde

ubuntu18@ubuntu:~/Desktop/change_processname$ ps -A | grep chang

ubuntu18@ubuntu:~/Desktop/change_processname$ ps -A | grep 1234

10764 pts/8    00:00:00 1234567890abcde

ubuntu18@ubuntu:~/Desktop/change_processname$ cat /proc/10764/stat

10764 (1234567890abcde) S 10709 10764 10709 34824 10764 4194304 69 0 0 0 0 0 0 0 20 0 1 0 14090125 4612096 197 18446744073709551615 94579895803904 94579895806128 140721599190352 0 0 0 0 0 0 1 0 0 17 0 0 0 0 0 0 94579897904560 94579897905168 94579902476288 140721599193924 140721599193938 140721599193938 140721599197162 0

三、两者方法相结合

我们可以发现,使用以上两种方法相结合,可以使得 ps -ef 、ps -aux 、ps -A 、top、/proc/pid/status、/proc/pid/cmdline 均看不到真实的进程信息;

看下面代码:

/*
gcc changetitle.c -o changetitle
*/
#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';
    }
    //修改argv[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
    prctl(PR_SET_NAME,buf);
}


int main(int argc, char *argv[])
{
    char argv_buf[MAXLINE] = {0}; // save argv paramters
    int i;


    for( i = 1; i < argc; i++)
    {
        strcat(argv_buf, argv[i]);
        strcat(argv_buf, " ");
    }
    
    //修改argv[0]所指向的内存空间的内容
    setproctitle_init(argc, argv, environ);
    
    //调用prctl修改进程名
    setproctitle("%s@%s %s", "12345678", "ip", argv_buf);


    for (i = 0; environ[i] != NULL; i++)
        free(environ[i]);
    getchar();


    return 0;
}

但是这样还是有一定的局限性,比如说,ps、top等命令还是能看见真实的pid信息,proc文件夹下还是会生成相应的pid文件夹;最理想的情况应该是让我们的进程信息彻底消失,Linux进程隐藏-中级隐藏篇将会进一步介绍更加高级的进程隐藏技术。

Relevant Link:

https://www.linuxprobe.com/linux-proc-pid.html

https://www.cnblogs.com/LittleHann/p/4991600.html

http://blog.chinaunix.net/uid-29482215-id-4120748.html

# 系统安全 # 木马 # linux安全
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者