12下一页
返回列表 发新帖

为 Linux 编写一个简单的 rootkit(1)

[复制链接]

34

主题

772

回帖

3001

积分

大学生

Rank: 5Rank: 5

金币
101
好评
44
信誉
153

MT论坛新人考神MT论坛帅哥

QQ
发表于 2022-7-26 16:34:08 | 显示全部楼层 | 阅读模式  来自 河南
本帖最后由 趣探坊 于 2022-7-26 16:40 编辑

在本文中,我将介绍如何为 Linux 编写一个简单的 rootkit。但是,要理解本文,您必须知道如何编写
linux内核模块。如果你不知道,你可以阅读Kernel modules — The Linux Kernel documentation (linux-kernel-labs.github.io)


什么是 Rootkit?当你闯入sb的系统时,你可能希望能够在一段时间后“回来”。当您在该系统中安装
Rootkit 时,您将能够随时获得管理员权限。好的 Rootkit 可以隐藏在受感染的系统中,
因此管理员无法找到它们。有很多方法可以隐藏在系统中。我不打算描述所有这些




什么是 Rootkit?当你闯入sb的系统时,你可能希望能够在一段时间后“回来”。当您在该系统中安装
Rootkit 时,您将能够随时获得管理员权限。好的 Rootkit 可以隐藏在受感染的系统中,
因此管理员无法找到它们。有很多方法可以隐藏在系统中。我不打算描述所有这些
在本文中,我们只讨论 Linux Rootkit。
Linux有一些主要类型的rootkit。
例如,有些rootkit将systems(ls,ps,netstat等)中一些最重要的程序替换为它们的修改版本,
这些版本不会让管理员看到有问题的地方。虽然,这样的rootkit很容易检测到。
其他 Rootkit 用作 Linux 内核模块。它们在内核模式下工作,因此它们可以执行任何操作。他们可以隐藏自己,文件,
进程等。在本教程中,我们将讨论这种类型的rootkit。
请注意,它不是“真正的”rootkit。要使用其功能(例如获取root权限),您必须具有
对已安装rootkit的系统的本地访问权限。它可以是“普通”用户帐户,但您必须能够登录到该帐户。此外,当安装了 rootkit
的系统重新启动时,我们的 rootkit 将被“卸载”,因为它不会在引导时加载。但是本文并不意味着要为脚本小子提供
他们能够使用的真正rootkit。本文只需要教你编程rootkit的基础知识。
首先,我将大致描述这个rootkit是如何工作的,然后我将展示它的代码,最后我将详细地编写它是如何工作的。
所以,让我们开始吧:
  • 我将首先描述它将具有哪些功能。
    • 当用户向 rootkit“发送”正确的命令时,他将获得 root 权限。
    • 另一个命令将允许用户隐藏进程
    • 为了能够安全地卸载rootkit(没有任何糟糕或错误),它将具有使rootkit可见等功能。我很快就会描述它们。
    • 另一个功能将让用户“取消隐藏”最后隐藏的进程。
  • 让我们看看在加载 rootkit 期间将调用哪些函数:
    • module_remember_info() - 此函数保存有关 rootkit 的一些信息,以便以后可以卸载它。
    • proc_init() - 这是非常重要的函数,可以将命令“发送”到rootkit。
    • module_hide() - 在此函数中,我们隐藏了 rootkit
    • tidy() - 在这个函数中,我们做了一些清理。如果我们不这样做,在卸载rootkit时会出现一些错误。
    • rootkit_protect() - 这是一个非常简单的函数,即使它是可见的,也无法通过“rmmod rootkit”命令
      卸载rootkit。但是,如果内核 wa 是在支持
      强制卸载模块的情况下编译的,则仍可以通过 “rmmod -f rootkit” 卸载。

  • 现在,我将详细描述这些函数:
  • proc_init():
    • 如前所述,此函数可以将命令发送到 rootkit。首先,我想在proc中创建一个条目,
      然后将其隐藏,以便无法通过“readdir”系统调用找到它。但这不是一个好主意。仍然可以通过在proc中浏览条目列表从内核模式中找到rootkit
      。那么,我做了什么呢?rootkit 查找现有条目(例如 /proc/version),
      并将其现有函数(如 read_proc 和 write_proc)替换为其他函数。命令通过写入或读取
      “受感染”条目发送到 rootkit。你可以问:“那么通过阅读还是写作?还是两者兼而有之?这取决于哪些功能具有受感染的条目。
      如果它只有写入功能,我们替换它。为什么不创建阅读功能?因为如果条目
      突然获得写作功能,那将是可疑的。我们必须避免它 - 管理员无法检测到我们!如果条目只有读取功能,我们替换它。
      如果它同时具有读取和写入功能,则我们仅替换写入功能。
      那么,如何将命令传递给该条目呢?当写入函数被替换时,您只需写入该条目的正确命令即可。您可以使用 echo 或类似程序执行此操作
      。但是,如果你想获得root权限,你必须编写自己的程序来写入该条目,然后
      使用execve syscall run shell。
      如果读取功能被替换,则必须编写特殊程序。它有什么作用?它必须使用读取系统调用从该条目读取。
      此函数的参数之一是指向必须写入数据的缓冲区的指针。要将命令传递给我们的条目,您必须将该命令
      保存在缓冲区中。然后,将指向该缓冲区的指针作为读取系统调用的参数。
      稍后,我将展示示例程序的代码,该代码可用于将命令传递给rootkit。
      让我们转到下一个函数。

  • rootkit_hide():
    • 在此函数中,我们隐藏了 rootkit。第一个问题是 rootkit 由 “lsmod” 命令显示,并且在 /proc/modules 文件中可见。
      要解决此问题,我们可以从模块的主列表中删除模块。每个模块都由模块结构表示。让我们看一下这个结构的定义:
      struct module
      {
              enum module_state state;

              /* Member of list of modules */
              struct list_head list;

              /* Unique handle for this module */
              char name[MODULE_NAME_LEN];

              /* Sysfs stuff. */
              struct module_kobject mkobj;
              struct module_attribute *modinfo_attrs;
              const char *version;
              const char *srcversion;
              struct kobject *holders_dir;

              /* Exported symbols */
              const struct kernel_symbol *syms;
              const unsigned long *crcs;
              unsigned int num_syms;

              /* Kernel parameters. */
              struct kernel_param *kp;
              unsigned int num_kp;

              /* GPL-only exported symbols. */
              unsigned int num_gpl_syms;
              const struct kernel_symbol *gpl_syms;
              const unsigned long *gpl_crcs;

      #ifdef CONFIG_UNUSED_SYMBOLS
              /* unused exported symbols. */
              const struct kernel_symbol *unused_syms;
              const unsigned long *unused_crcs;
              unsigned int num_unused_syms;

              /* GPL-only, unused exported symbols. */
              unsigned int num_unused_gpl_syms;
              const struct kernel_symbol *unused_gpl_syms;
              const unsigned long *unused_gpl_crcs;
      #endif

              /* symbols that will be GPL-only in the near future. */
              const struct kernel_symbol *gpl_future_syms;
              const unsigned long *gpl_future_crcs;
              unsigned int num_gpl_future_syms;

              /* Exception table */
              unsigned int num_exentries;
              struct exception_table_entry *extable;

              /* Startup function. */
              int (*init)(void);

              /* If this is non-NULL, vfree after init() returns */
              void *module_init;

              /* Here is the actual code + data, vfree'd on unload. */
              void *module_core;

              /* Here are the sizes of the init and core sections */
              unsigned int init_size, core_size;

              /* The size of the executable code in each section.  */
              unsigned int init_text_size, core_text_size;

              /* Arch-specific module values */
              struct mod_arch_specific arch;

              unsigned int taints;    /* same bits as kernel:tainted */

      #ifdef CONFIG_GENERIC_BUG
              /* Support for BUG */
              unsigned num_bugs;
              struct list_head bug_list;
              struct bug_entry *bug_table;
      #endif

      #ifdef CONFIG_KALLSYMS
              /* We keep the symbol and string tables for kallsyms. */
              Elf_Sym *symtab;
              unsigned int num_symtab;
              char *strtab;

              /* Section attributes */
              struct module_sect_attrs *sect_attrs;

              /* Notes attributes */
              struct module_notes_attrs *notes_attrs;
      #endif

              /* Per-cpu data. */
              void *percpu;

              /* The command line arguments (may be mangled).  People like
                 keeping pointers to this stuff */
              char *args;
      #ifdef CONFIG_MARKERS
              struct marker *markers;
              unsigned int num_markers;
      #endif
      #ifdef CONFIG_TRACEPOINTS
              struct tracepoint *tracepoints;
              unsigned int num_tracepoints;
      #endif

      #ifdef CONFIG_TRACING
              const char **trace_bprintk_fmt_start;
              unsigned int num_trace_bprintk_fmt;
      #endif

      #ifdef CONFIG_MODULE_UNLOAD
              /* What modules depend on me? */
              struct list_head modules_which_use_me;

              /* Who is waiting for us to be unloaded */
              struct task_struct *waiter;

              /* Destruction function. */
              void (*exit)(void);

      #ifdef CONFIG_SMP
              char *refptr;
      #else
              local_t ref;
      #endif
      #endif

      结构list_head列表 - 这是模块的主要列表。我们必须从此列表中删除我们的模块。
      当我们这样做时,rootkit 将不再被“lsmod”和“/proc/modules”看到。但是我们的 rootkit 在 /sys/module/ 目录中仍然可见。/sys 也是特殊的文件系统(如 /proc)。
      /sys 中的每个条目都由 kobject 结构表示。每个模块都有自己的 kobject。在结构模块的定义中,我们看到:
      结构module_kobject mkobj
      让我们看看module_kobject结构的定义:
      1. [backcolor=white][color=#000000]struct module_kobject
      2. {
      3.         struct kobject kobj;
      4.         struct module *mod;
      5.         struct kobject *drivers_dir;
      6.         struct module_param_attrs *mp;
      7. };[/color][/backcolor]
      复制代码
      这是 kobject 的列表。首先,我们必须通过kobject_del()函数从/sys/modules中删除我们的模块,然后我们必须从“entry”列表中删除我们的kobject。我们来谈谈下一个函数
      tidy():
      • 当您分析内核在卸载模块期间执行的操作时,您将看到它删除了该模块的 /sys/module 中的条目。
        但是有一个问题 - 我们删除了该条目。因此,当我们卸载模块时,内核将尝试删除不存在的条目。这将导致
        糟糕,并且可能系统将崩溃。我们必须避免它。但是你可以看到,当我们将一些指针设置为 NULL 时,内核不会尝试
        删除该条目。如果你想真正理解这个函数,你必须自己浏览Linux内核的源代码。关于加载和卸载模块的过程的写作
        可能比您目前正在阅读的7篇这样的文章还要大

      rootkit_protect():
      • 非常简单的功能。它只是调用try_module_get函数,将指向当前模块的指针作为参数。
        try_module_get会增加对模块的引用计数器。因此,无法通过正常的“rmmod”命令卸载模块。
        但是,如前所述,如果内核编译时支持强制卸载模块,则仍可以通过“rmmod -f”命令卸载
        模块。
      • 若要从用户模式列出正在运行的进程,程序将列出 /proc 的内容。每个进程都有自己的目录。该目录
        的名称是此进程的 PID。请注意,proc_dir_entry具有指向file_operations结构的指针。
        此结构定义对文件的操作。在这种情况下,在 /proc 中输入。让我们看一下这个结构的定义:
        struct file_operations {
                struct module *owner;
                loff_t (*llseek) (struct file *, loff_t, int);
                ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
                ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
                ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
                ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
                int (*readdir) (struct file *, void *, filldir_t);
                unsigned int (*poll) (struct file *, struct poll_table_struct *);
                int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
                long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
                long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
                int (*mmap) (struct file *, struct vm_area_struct *);
                int (*open) (struct inode *, struct file *);
                int (*flush) (struct file *, fl_owner_t id);
                int (*release) (struct inode *, struct file *);
                int (*fsync) (struct file *, struct dentry *, int datasync);
                int (*aio_fsync) (struct kiocb *, int datasync);
                int (*fasync) (int, struct file *, int);
                int (*lock) (struct file *, int, struct file_lock *);
                ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
                unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
                int (*check_flags)(int);
                int (*flock) (struct file *, int, struct file_lock *);
                ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
                ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
                int (*setlease)(struct file *, long, struct file_lock **);

        对我们来说,重要的字段是:读取,写入和读取。
        • readdir 此函数用于列出目录的内容。我们如何隐藏流程?我们将隐藏进程的 pid 存储在“pid”缓冲区中。
          我们发现 /proc proc_dir_entry。然后,我们将file_operations中的readdir函数替换为我们自己的函数。此函数通常列出 /proc 的内容,但省略表示隐藏进程的目录。读像功能如何工作?它只是遍历目录中的元素,但有一件有趣的事情。它不会直接写入与目录内容相关的数据,而是使用filldir函数(作为参数给出)来执行此操作。filldir_t filldir - 这是指向 filldir 函数的指针,该函数必须由 readdir 函数使用。让我们看一下原型:
          static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
                             u64 ino, unsigned int d_type)

          例如:
          • /proc 目录的 readdir 函数列出其内容。它贯穿所有元素。对于每个元素,它调用 filldir 作为“name”参数,给出当前元素的名称。因此,如果程序列出 /proc 的内容以查看系统中运行的进程,并且使用file_operations结构中的 readdir 函数来列出目录的内容,我们可以修改 /proc 的 readdir,以便它不会显示我们想要隐藏的进程!我们只是在 /proc 的file_operations结构中设置了 “readdir” 指针到我们的 readdir 版本。我们的readdir只是调用原始readdir,但因为它的“filldir”参数为我们的filldir函数提供了ointer。我们的填充物是做什么的?它检查“name”参数是否等于其中一个隐藏进程的pid。如果是,它只是不显示它。否则,它将调用原始的 filldir 函数。我必须解释的另一件事是与替换读取和写入功能有关。在 /proc 中“定义”输入的读取和写入函数有两种可能性。您可以在proc_read/proc_write字段中提供指向函数的指针,也可以在条目的file_operations结构的读/写字段中提供指向函数的指针。当我们感染条目时,我们设置proc_read/proc_write指向我们函数的指针,如果它最初是设置的,我们设置file_operations的读/写字段(如果设置)。如何将用户的权限更改为root权限?我们必须将当前进程的 uid、euid、gid 和 egid 更改为 0。每个过程都由task_struct结构表示。这是一个非常复杂的结构,我不会在这里展示它的定义。uid,gid和其他类似的“事物”存储在信誉结构中,这是task_struct的元素。要更改此字段的值,我们必须调用prepare_creds()函数,该函数返回指向结构信誉的指针,其中uid,gid等设置为等于当前进程信誉结构中的uid,gid等的值。然后,我们可以修改此结构的所有字段。最后,我们调用commit_creds() 函数,将指向结构 cred 的指针作为参数。我们如何找到必须被感染的条目?/proc 中的条目以列表的形式组织 - proc_dir_entry具有字段“next”,该字段指向当前目录中的下一个条目。/proc 中的每个目录都有“subdir”字段,该字段是指向该目录中第一个条目的指针。那么我们如何找到我们想要感染的条目呢?首先,我们将指针设置为 /proc 目录。让我们将此指针命名为“ptr”。然后我们将其设置为 ptr->subdir。之后,我们将ptr指向的条目名称与我们要感染的条目名称进行比较。如果相等,我们找到了我们的条目。否则,我们转到ptr->next并将其名称与感染等条目进行比较。所有命令和其他重要内容都在rootkit_conf.conf.h 配置文件中配置。



回复

使用道具 举报

84

主题

3695

回帖

9401

积分

禁止发言

金币
1951
好评
111
信誉
70

考神MT论坛新人MT论坛帅哥MT论坛活跃会员MT论坛侠客

发表于 2022-7-26 16:38:06 来自手机  | 显示全部楼层  来自 山西
感谢分享
回复

使用道具 举报

18

主题

9045

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
4987
好评
3
信誉
202

考神

发表于 2022-7-26 16:43:54 来自手机  | 显示全部楼层  来自 广东
感谢分享
回复

使用道具 举报

175

主题

8836

回帖

2万

积分

博士后

Rank: 8Rank: 8

金币
7378
好评
29
信誉
165

MT论坛新人MT论坛帅哥考神MT论坛最佳新人MT论坛灌水老大MT论坛活跃会员

发表于 2022-7-26 17:40:37 来自手机  | 显示全部楼层  来自 河南
感谢大佬分享
回复

使用道具 举报

16

主题

4250

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
749
好评
5
信誉
150

MT论坛帅哥考神

发表于 2022-7-26 17:44:27 来自手机  | 显示全部楼层  来自 重庆
感谢分享
回复

使用道具 举报

5

主题

4429

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
8186
好评
10
信誉
195

MT论坛最佳新人考神MT论坛新人

QQ
发表于 2022-7-26 19:44:50 来自手机  | 显示全部楼层  来自 江西
感谢分享
回复

使用道具 举报

0

主题

11

回帖

39

积分

小学生

Rank: 2

金币
25
好评
0
信誉
100
发表于 2022-7-26 20:21:55 | 显示全部楼层  来自 安徽
谢谢谢谢谢谢谢谢
回复

使用道具 举报

37

主题

5737

回帖

1万

积分

博士生

欣冉吖

Rank: 7Rank: 7Rank: 7

金币
5302
好评
25
信誉
407
发表于 2022-7-26 21:29:38 来自手机  | 显示全部楼层  来自 河北
感谢分享
回复

使用道具 举报

16

主题

3792

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
4890
好评
1
信誉
100
发表于 2022-7-26 22:12:52 来自手机  | 显示全部楼层  来自 广东
学习学习
回复

使用道具 举报

0

主题

115

回帖

391

积分

初中生

Rank: 3Rank: 3

金币
333
好评
0
信誉
100
发表于 2022-7-29 13:47:50 | 显示全部楼层  来自 台湾

学习学习
回复

使用道具 举报

1

主题

1188

回帖

3714

积分

大学生

Rank: 5Rank: 5

金币
362
好评
1
信誉
100
发表于 2022-7-29 16:13:25 来自手机  | 显示全部楼层  来自 江西
感谢分享
回复

使用道具 举报

45

主题

2648

回帖

7864

积分

硕士生

Rank: 6Rank: 6

金币
1521
好评
1
信誉
101
发表于 2022-7-30 11:18:50 | 显示全部楼层  来自 江西
学习学习
回复

使用道具 举报

40

主题

5888

回帖

1万

积分

博士生

Rank: 7Rank: 7Rank: 7

金币
5106
好评
35
信誉
112

MT论坛帅哥考神MT论坛最佳新人

发表于 2022-7-30 12:59:48 来自手机  | 显示全部楼层  来自 湖南
学习学习
回复

使用道具 举报

34

主题

1515

回帖

4948

积分

大学生

Rank: 5Rank: 5

金币
171
好评
2
信誉
125

考神MT论坛新人

发表于 2022-7-30 19:31:36 来自手机  | 显示全部楼层  来自 贵州
看看隐藏
回复

使用道具 举报

7

主题

737

回帖

2605

积分

大学生

Thirteen Team创建者

Rank: 5Rank: 5

金币
1456
好评
17
信誉
101

考神

发表于 2022-7-31 12:04:32 来自手机  | 显示全部楼层  来自 山西
快看快看
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表