前面的文章介绍了很多种隐藏进程,隐藏TCP连接,隐藏内核模块的方法,总结起来和大多数网上介绍Rootkit的文章种介绍的方法不同点在于:

  • 网上大多数文章均是hook procfs来达到隐藏对象的目的。
  • 我的方法则是直接将对象从链表等数据结构中摘除。

无疑,我的方法更简单,因为很显然,hook procfs需要修改大量关于VFS API调用相关的代码。

但是,跑了和尚跑不了庙,虽然进程,TCP连接,内核模块等摘了链表,它还是在slab中啊!我们只需要dump特定的slab对象,就能找到它们:

  • dump出名字为TCP的kmem cache所有slab对象,就能找到隐藏的TCP连接。
  • dump出名字为task_struct的kmem cache所有slab对象,就能找到隐藏的进程。

然而,内核并没有提供dump所有slab对象的方法。

我们知道,/proc/slabinfo里面的信息完全不足够,这里面仅仅是伴随着slab对象的分配和释放,Linux内核记录的一些统计信息,那么,我们必须自己实现所有slab的dump操作。

来吧,让我实现它。

slab位于buddy系统的上层,从buddy系统拿page,所以我们需要从page上做文章。

让人恼火的是,作为slab的默认实现的slub kmem_cache,竟然没有字段保存自己保有了那些page! 一旦对象被分配了,它就和kmem_cache的管理脱钩了,直到释放时,它才重新回到kmem_cache的freelist中。

然而,不幸中总有万幸,虽然kmem_cache没有所有page的引用,但是page中有反向引用啊:

  • page的slab_cache字段记录着自己所属的kmem_cache。

方案就是,扫描所有的page,按照其kmem_cache字段归类汇总即可。代码如下:

#include <linux/module.h>
#include <linux/kallsyms.h>
#include <net/inet_sock.h>struct list_head *_slab_caches;
struct list_head __slab_caches;struct kmemcache_stat {struct list_head list;struct kmem_cache *slab;unsigned long cnt;void (*print_obj)(struct kmem_cache *, void *);void *priv;
};// 遍历连续的n个包含连续obj的pages
#define for_each_object(__p, __s, __addr, __objects) \for (__p = (__addr); __p < (__addr) + (__objects) * (__s)->size;\__p += (__s)->size)// task_struct的打印回调函数
void print_task(struct kmem_cache *s, void *p)
{struct task_struct *p1 = (struct task_struct *)p;printk("##### %s   %d   \n", p1->comm, p1->pid);
}// tcp socket的打印回调函数
void print_tcp(struct kmem_cache *s, void *p)
{struct inet_sock *sk = (struct inet_sock *)p;printk("##### %08X->%08X %d %d  \n",sk->inet_daddr,sk->inet_rcv_saddr,ntohs(sk->inet_dport),sk->inet_num);
}// mm_struct的打印回调函数
void print_mm(struct kmem_cache *s, void *p)
{struct mm_struct *mm = (struct mm_struct *)p;printk("##### owner:[%s] %d   PGD:%lx  \n",mm->owner?mm->owner->comm:"[null]",mm->owner?mm->owner->pid:-1,(unsigned long)mm->pgd);
}// vm_area_struct的打印回调函数
void print_vm(struct kmem_cache *s, void *p)
{struct vm_area_struct *vma = (struct vm_area_struct *)p;if (vma->vm_mm && vma->vm_mm->owner) {printk("##### VMA owner:[%s] %d   PGD:%lx  \n",vma->vm_mm->owner->comm,vma->vm_mm->owner->pid,(unsigned long)vma->vm_mm->pgd);}
}void print_object(struct kmemcache_stat *ment, void *p)
{ment->cnt ++;if (ment->print_obj) {ment->print_obj(ment->slab, p);}
}unsigned long show_objects(struct page *page)
{struct kmem_cache *s = page->slab_cache;struct kmemcache_stat *entry;void *p, *addr;int found = 0;list_for_each_entry (entry, &__slab_caches, list) {if (entry->slab == s) {found = 1;break;}}if (!found) {return 1;}addr = page_address(page);for_each_object (p, s, addr, page->objects) {print_object(entry, p);}// 越过n个pagesreturn (PAGE_ALIGN((unsigned long)p) - (unsigned long)addr)/PAGE_SIZE;}static void slab_scan(void)
{int i = 0;for_each_online_node(i) {unsigned long spfn, epfn, pfn;spfn= node_start_pfn(i);epfn = node_end_pfn(i);for (pfn = spfn; pfn < epfn;) {struct page *page = pfn_to_page(pfn);if(!PageSlab(page)) {pfn ++;continue;}pfn += show_objects(page);}}
}static int __init dump_slab_obj_init(void)
{struct kmem_cache *entry;struct kmemcache_stat *ment, *tmp;unsigned long total = 0;_slab_caches = (struct list_head *)kallsyms_lookup_name("slab_caches");if (!_slab_caches) {return -1;}INIT_LIST_HEAD(&__slab_caches);list_for_each_entry (entry, _slab_caches, list) {struct kmemcache_stat *stat;stat = kmalloc(sizeof(struct kmemcache_stat), GFP_KERNEL);stat->cnt = 0;stat->print_obj = NULL;INIT_LIST_HEAD(&stat->list);list_add(&stat->list, &__slab_caches);stat->slab = entry;if (!strcmp(entry->name, "task_struct")) {stat->print_obj = print_task;}if (!strcmp(entry->name, "TCP")) {stat->print_obj = print_tcp;}if (!strcmp(entry->name, "mm_struct")) {stat->print_obj = print_mm;}if (!strcmp(entry->name, "vm_area_struct")) {stat->print_obj = print_vm;}}slab_scan();list_for_each_entry_safe (ment, tmp, &__slab_caches, list) {printk("[%s]  %lu\n", ment->slab->name, ment->cnt);total += ment->cnt;list_del(&ment->list);kfree(ment);}printk("total objs:  %lu\n", total);return -1;
}module_init(dump_slab_obj_init);
MODULE_LICENSE("GPL");

代码非常简单,我来给出一个输出:

 ##### owner:[sshd] 1841   PGD:ffff88003be3d000##### owner:[[null]] -1   PGD:ffff88003c110000##### owner:[bash] 1843   PGD:ffff880000098000##### owner:[sshd] 1745   PGD:ffff880035aac000##### owner:[agetty] 664   PGD:ffff88003c321000##### owner:[dhclient] 1793   PGD:ffff88003c3fd000##### owner:[[null]] -1   PGD:ffff880035aa2000##### owner:[[null]] -1   PGD:ffff88003bd06000
...##### gmain   2260##### tuned   1248##### VMA owner:[firewalld] 658   PGD:ffff88003b7f6000##### VMA owner:[firewalld] 658   PGD:ffff88003b7f6000##### VMA owner:[firewalld] 658   PGD:ffff88003b7f6000##### VMA owner:[firewalld] 658   PGD:ffff88003b7f6000
... ##### 0138A8C0->6E38A8C0 62006 22##### 0138A8C0->6E38A8C0 62011 22
...total objs:  2367564

是的,你没有看错,我是故意展示mm_struct,vm_area_struct以及TCP的:

  • 你隐藏了task_struct,甚至不在task的kmem_cache里分配task_struct,但mm_struct/vm_area_struct出卖了你的task。
  • 你隐藏了TCP,从ehash中摘除了它,然而它还在TCP socket的slab中。

Linux内核的所有数据结构编织成了一张大网,这张网彼此关联着。 只要你揪住一个角,其它的东西就全部出来了。

每个task,对于现代操作系统Linux,无论是内核线程还是普通用户进程必然有它的PGD,找到了PGD,就能dump出这个task的地址空间数据。而我们知道,PGD位于task的mm_struct字段mm中,而mm_struct的owner则直接指向了task本身,如果我们在slab中找到了mm_struct,就可以定位到task了,无论它藏在哪里!

如果你害怕有人将fork过程的copy_process给hook了,详见:
https://blog.csdn.net/dog250/article/details/105939822
也就是说mm_struct对象以及task_struct对象本身均不在slab中分配,那么好办,随便找个它的vm_area_struct对象呗,然后从它的vm_mm字段定位mm_struct,进而再定位到task。我们知道,vm_area_struct的分配是随着task的运行自动进行的,很难hook它的分配过程,所以,从vm_area_struct来按图索骥揪出隐藏的task,绝对是一个屡试不爽的好办法。

进水了,进水了。

只要你使用了slab,那么一定可以被按图索骥顺藤摸瓜,获得便利和高效的代价就是被系统管控。 即便我在前面的文章中使用kmalloc的匿名slab分配了task_struct,那依然是个slab对象啊。

如果你想让你的Rootkit藏得更深,那势必要想办法彻底脱离这些内核数据结构的管理,如何做呢?直接调用alloc_pages吗?非也! alloc_pages依然是受控的,获取的页面依然会被加入各种list被管控。到底要怎样呢??很简单!

偷页面!!

这是下文的内容。


浙江温州皮鞋湿,下雨进水不会胖。

dump出Linux内核所有的slab对象缉拿内核Rootkit相关推荐

  1. 【Linux】Linux内核空间的slab分配模式

    内核在运行时,经常需要在内核空间3G~3G+high_memory这个内存空间申请动态内存,以存放一些结构类型的数据.例如,在创建一个程序时,它就要为该程序控制块task_struct申请一段内存空间 ...

  2. Linux内核中的slab/slob/slub-- 在搞晕前先记下来

    很久很久以前:一个叫做Mark Hemment的哥儿们写了Slab.在接下来的一些年里,其他人对Slab进行了完善.一年半以前,SLOB问世了.SLOB的目标是针对嵌入式系统的,主要是适用于那些内存非 ...

  3. Linux 2.6.19.x 内核编译配置选项简介(内核裁剪)

    Linux 2.6.19.x 内核编译配置选项简介 Code maturity level options 代码成熟度选项 Prompt for development and/or incomple ...

  4. Linux内核剖析-----IO复用函数epoll内核源码剖析

    本文参考董浩博客 http://donghao.org/uii/   epoll内核实现 (1)内核为epoll做准备工作 这个模块在内核初始化时(操作系统启动)注册了一个新的文件系统,叫" ...

  5. Linux内存管理之slab机制(创建slab)

    Linux内核中创建slab主要由函数cache_grow()实现,从slab的创建中我们可以完整地看到slab与对象.页面的组织方式. /* * Grow (by 1) the number of ...

  6. linux 进程slab,Linux内存管理之slab slab是什么

    一:准备知识: 前面我们分析过了大内存分配的实现机制,事实上,若为小块内存而请求整个页面,这样对于内存来说是一种极度的浪费.因此linux采用了slab来管理小块内存的分配与释放.Slab最早是由su ...

  7. linux 哪个进程在使用串口_Linux内核的整体架构

    转载自蜗窝科技 Linux内核的整体架构​www.wowotech.net 作者:wowo发布于:2014-2-21 13:23 分类:Linux内核分析 0. 转载者说在前面的话 转载目的: 能够让 ...

  8. Linux内存管理之SLAB分配器

    注:本文讲述的SLAB相关代码是基于Linux内核v4.7,代码网址. 1.SLAB分配器的由来 在讲SLAB分配器之前先说两个概念: 内部碎片和外部碎片. 外部碎片指的是还没有被分配出去(不属于任何 ...

  9. Linux内存管理之slab 1:slab原理(+buddy伙伴系统)

    Linux内存管理之slab 1:slab原理(+buddy伙伴系统) 1. 为什么有了Buddy(伙伴系统)还需要slab? 1.1 什么是伙伴系统? 1.1.1 伙伴系统思想 1.2 伙伴系统例子 ...

最新文章

  1. php 前端控制器,前端控制器模式
  2. CSV在制药企业中的应用
  3. ajax实现异步校验
  4. 用户研究,你还在“凭感觉”吗?
  5. 基于Git rebase修改历史提交信息
  6. VTK:可视化算法之FlyingHeadSlice
  7. [BUUCTF-pwn]——[极客大挑战 2019]Not Bad(ORW)(内涵peak小知识)
  8. [渝粤教育] 厦门理工学院 机械设计 参考 资料
  9. [剑指offer]面试题第[36]题[JAVA][二叉搜索树与双向链表][递归]
  10. php double 类型 浮点数相减
  11. jmeter录制脚本_jmeter(二)Badboy录制脚本
  12. html 中添加提示,如何interpretHTML UIB-提示
  13. 对比找出两张Excel表的不同数据
  14. 2014.10.6模拟赛【魔兽争霸】
  15. 连接腾讯云云服务器的mysql
  16. 尽管凭借主持人的身份成名,张绍刚先生在内心深处却对这一角色认可度很低
  17. 《数据结构(C语言版)》严蔚敏代码实现———顺序表
  18. 2019QS世界大学学科排名重磅发布!
  19. ATUO CAD 如何延伸线段?
  20. Apache2.4的安装、配置与常见的问题(Windows)

热门文章

  1. Android中使用ALSA声卡
  2. PMBOK 第六版十大知识领域
  3. prerender-spa-plugin 预渲染
  4. 第2章 规划和安装VMware ESXi
  5. CpuLimit脚本进程CPU使用率限制
  6. python模拟哔哩哔哩滑块登入验证
  7. JVM调优之JConsole和JVisualVM工具使用
  8. Python之Selenium的基本使用(1)
  9. wifi678响应超时服务器无应答,宽带连接时错误678如何办
  10. PPT中制作倒影艺术字