ls的功能是列出目录中的内容,通过ls命令隐藏后门是最简单基本的rootkit。

磁盘上的文件系统由文件和目录组成,文件的内容可以是任意的数据,而目录的内容只能是文件名的列表和子目录名的列表,目录中的内容是该目录下的文件和子目录它们的名字的列表。

先来看一看关于目录的结构体

/usr/include/bits/dirent.h

struct dirent {
  ino_t          d_ino;         /* inode number */
  off_t          d_off;         /* offset to the next dirent */
  unsigned short d_reclen;      /* length of this record */
  unsigned char  d_type;        /* type of file; not supportedby all file system types */
  char           d_name[256];   /* filename */
 };

d_ino是目录中的文件的i-node节点号,通过d_ino可以找到该文件的i-node节点,而i-node节点中保存了文件的属性等信息。通过i-node节点就可以访问到文件的内容。

关于目录访问的一些函数

/usr/include/dirent.h

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
struct dirent *readdir(DIR *dirp);
  int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
  int closedir(DIR *dirp);
  void rewinddir(DIR *dirp);

关于目录中文件的属性等信息使用struct stat结构体来描述的,其结构的定义如下

/usr/include/bits/stat.h

struct stat
{
  __dev_t st_dev;                 /* Device. */
  unsigned short int __pad1;      /* 用于填充对齐 */
  __ino_t   st_ino;               /* 32bit file serial number. */
  __mode_t  st_mode;              /* File mode. */
  __nlink_t st_nlink;             /* Link count. */
  __uid_t   st_uid;               /* User ID of the file's owner. */
  __gid_t   st_gid;               /* Group ID of the file's group.*/
  __dev_t   st_rdev;              /* Device number, if device. */
  unsigned short int __pad2;      /* 用于填充对齐 */
  __off_t     st_size;            /* Size of file, in bytes. */
  __blksize_t st_blksize;         /* Optimal block size for I/O. */
  __blkcnt_t  st_blocks;          /* Number 512-byte blocks allocated. */
  struct timespec st_atim;        /* Time of last access. */
  struct timespec st_mtim;        /* Time of last modification. */
  struct timespec st_ctim;        /* Time of last status change. */
 # define st_atime st_atim.tv_sec    /* Backward compatibility. */
 # define st_mtime st_mtim.tv_sec
 # define st_ctime st_ctim.tv_sec
  __ino64_t st_ino;               /* File serial number. */
};

该结构体提供了关于文件(或者设备)的如下重要信息:

st_mode文件类型和许可权限
st_nlink文件链接数
st_uid文件属主id
st_gid文件属主所在组的id
st_size文件的字节数
st_blocks文件所占的块数

“ACM”三个时间:

st_atime文件最后访问时间access time
st_mtime文件最后修改时间modification time
st_ctime文件属性/状态最后改变的时间change status time

另外在/usr/include/sys/stat.h中提供了下列关于文件信息的宏定义

/usr/include/sys/stat.h

typedef __dev_t     dev_t;
typedef __gid_t     gid_t;
typedef __ino_t     ino_t;
typedef __mode_t    mode_t;
typedef __nlink_t   nlink_t;
typedef __off_t     off_t;
typedef __uid_t     uid_t;
typedef __blkcnt_t  blkcnt_t;
typedef __blksize_t blksize_t;

/* Test macros for file types. */
#define __S_ISTYPE(mode, mask) (((mode) & __S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), __S_IFDIR)   // directory 目录文件类型
#define S_ISCHR(mode) __S_ISTYPE((mode), __S_IFCHR)   // char 字文件类型
#define S_ISBLK(mode) __S_ISTYPE((mode), __S_IFBLK)   // block 块文件类型
#define S_ISREG(mode) __S_ISTYPE((mode), __S_IFREG)   // regular 普通文件类型
#define S_ISFIFO(mode) __S_ISTYPE((mode), __S_IFIFO)  // FIFO 管道文件类型
#define S_ISLNK(mode) __S_ISTYPE((mode), __S_IFLNK)   // link 链接文件类型
#define S_ISSOCK(mode) __S_ISTYPE((mode), __S_IFSOCK) // sock 网络套接字文件类型

/* Protection bits. */
#define S_ISUID __S_ISUID     /* Set user ID on execution. */
#define S_ISGID __S_ISGID     /* Set group ID on execution. */
#define S_IRUSR __S_IREAD     /* Read by owner. */
#define S_IWUSR __S_IWRITE    /* Write by owner. */
#define S_IXUSR __S_IEXEC     /* Execute by owner. */

/* Read, write, and execute by owner. */
#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC)
#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */

/* Read, write, and execute by group. */
#define S_IRWXG (S_IRWXU >> 3)
#define S_IROTH (S_IRGRP >> 3) /* Read by others. */
#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */
#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */

/* Read, write, and execute by others. */
#define S_IRWXO (S_IRWXG >> 3)
# define S_BLKSIZE 512         /* Block size for `st_blocks'. */

有了上面的基础,实现代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>

void do_ls(char[]);
void do_stat(char*);
void show_file_info(char*, struct stat*);
void mode_str(int, char[]);
char *uid_str(uid_t);
char *gid_str(gid_t);

int main(int argc, char *argv[])
{
  if(argc == 1)
    do_ls(".");
  else
    while(--argc){
      printf("%s:\n", *++argv);
      do_ls(*argv);
    }
  return 0;
}

void do_ls(char dirname[])
{
  DIR *dir_ptr;           /* the directory */
  struct dirent *direntp; /* each entry */
  char full_path[256];
  if((dir_ptr = opendir(dirname)) == NULL){
    fprintf(stderr, "ls2: cannot open %s\n", dirname);
  }else{
    while((direntp = readdir(dir_ptr)) != NULL){
      strcpy(full_path, dirname);
      int dir_len = strlen(dirname);
      if(dirname[dir_len - 1] != '/'){  /* 处理目录字符串最后没有‘/’的情况 */
        full_path[dir_len] = '/';
        strcpy(full_path + dir_len + 1, direntp->d_name);
      }else
        strcpy(full_path + dir_len, direntp->d_name);
      do_stat(full_path);
    }
  closedir(dir_ptr);
  }
}

void do_stat(char *filename) /* 获得目录中文件的相关信息 */
{
	struct stat info;
/* 如果filename最后没有‘/’的话,stat调用失败 */
  if(stat(filename, &info) == -1){ /* cannot stat */
  	perror("stat"); /* say why */
  	printf("filename:%s\n", filename);
  }else{
  	char *pname = strrchr(filename, '/');
  	show_file_info(pname + 1, &info);
  }
}

void show_file_info(char *filename, struct stat *info_p) /* 打印文件的相关信息 */
{
	char modestr[11];
	mode_str(info_p->st_mode, modestr);
	printf("%s", modestr);
	printf("%3d ", (int)info_p->st_nlink);
	printf("%-8s", uid_str(info_p->st_uid));
	printf("%-8s", gid_str(info_p->st_gid));
	printf("%4ld ", (long)info_p->st_size);
	printf("%.12s ", 4 + ctime(&info_p->st_mtime));
	printf("%s\n", filename);
}

void mode_str(int mode, char str[]) /* 将文件的相关信息转化成 “crw-rw---"的形式 */
{
	strcpy(str, "----------"); /* default = no perms */
	if(S_ISDIR(mode)) str[0] = 'd'; /* directory */
	if(S_ISCHR(mode)) str[0] = 'c'; /* char device */
	if(S_ISBLK(mode)) str[0] = 'b'; /* block device */
  if(S_ISLNK(mode)) str[0] = 'l';
  if(mode & S_IRUSR) str[1] = 'r'; /* 3 bits for user */
  if(mode & S_IWUSR) str[2] = 'w';
  if(mode & S_IXUSR) str[3] = 'x';
  if(mode & S_IRGRP) str[4] = 'r'; /* 3 bits for group */
  if(mode & S_IWGRP) str[5] = 'w';
  if(mode & S_IXGRP) str[6] = 'x';
  if(mode & S_IROTH) str[7] = 'r'; /* 3 bits for other */
  if(mode & S_IWOTH) str[8] = 'w';
  if(mode & S_IXOTH) str[9] = 'x';
}

char *uid_str(uid_t uid) /* 将uid转化成username */
{
  static char numstr[10];
  struct passwd *pw_ptr;
  if((pw_ptr = getpwuid(uid)) == NULL){
  	sprintf(numstr, "%d", uid);
  	return numstr;
  }else
  return pw_ptr->pw_name;
}

char *gid_str(gid_t gid) /* 将gid转化成groupname */
{
  static char numstr[10];
  struct group *grp_ptr;
  if((grp_ptr = getgrgid(gid)) == NULL){
  	sprintf(numstr, "% d", gid);
    return numstr;
  }else
  return grp_ptr->gr_name;
}