目录

Kernel总结

一些好用的结构体以及衍生出来的攻击方法

user_key_payload

限制是只能分配最多200个最长20000字节

分配

分配一: size >= 0x18(GFP_kernel)

__x64_sys_add_key->kmalloc

plen可以控制,同时_payload为用户传进来的变量

但是需要注意的是后面会将其free掉,所以和send_msg类似

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
	SYSCALL_DEFINE5(add_key, const char __user *, _type,
75			const char __user *, _description,
76			const void __user *, _payload,
77			size_t, plen,
78			key_serial_t, ringid)
...
...
111		/* pull the payload in if one was supplied */
112		payload = NULL;
113	
114		if (plen) {
115			ret = -ENOMEM;
116			payload = kvmalloc(plen, GFP_KERNEL);
117			if (!payload)
118				goto error2;
119	
120			ret = -EFAULT;
121			if (copy_from_user(payload, _payload, plen) != 0)	

释放

poll_list

msg_msg

pt_regs

setxattr

shm_file_data

pipe_buffer

dirty_pipe

pipe_prime

dirty_cred

skb_buffer (>= 512)

pgv 与页级内存页分配

USMA

Cross cache attack

seq_operations (kmalloc-32 | GFP_KERNEL_ACCOUNT)

SLUB分配器

为什么需要内核分配器

操作系统虚拟内存中用于内存管理的最小数据单位是页面,大多数体系结构的传统大小为 4KB(0x1000)。由于每次进程请求某些东西时分配 4KB 内存是不切实际的,即使小到几十个字节,也需要一种中间机制来微管理页面的内容。

虽然在用户空间中这是由 malloc 系列执行的,但内核需要一个不同的系统,与系统本身更紧密地集成,可以在中断上下文中工作,符合 DMA 限制.

为此,内核实现了一个管理页面分配、碎片和重新分配的分配器。它基本上作为零售供应商工作:它获取大库存(4KB 页),然后在模块需要时将它们分成小块进行交易。这个分配器的基本版本称为 SLAB。

SLAB

当内核子系统请求(或释放)对象的数据时,主要开销在于初始化(或销毁)它,而不是为其分配内存。如果有一组频繁分配和释放的公共内核对象,将它们放在一个快速可达的地方可以使整个过程更有效率。

这就是 SLAB 的原理:分配器会跟踪这些称为缓存的块,以便在收到为某种类型的数据对象分配内存的请求时,它可以立即用已经分配的槽满足请求。在此上下文中,slab 是内存中包含预分配内存块的一个或多个连续页面。

Slab 可能存在于以下状态之一:

  • empty:slab 中的所有对象都是空闲的
  • partial: slab 由自由对象和被占用对象组成
  • full:slab中的所有对象都为占用状态

当然,分配器的目的是尽可能快地处理请求,因此跟踪部分 slab 是至关重要的。这是通过缓存完成的,每个对象类型有一个缓存。

SLUB

SLUB 是我们感兴趣的 SLAB 变体,旨在实现更好的调试、更少的碎片和更好的性能。它继续采用基于 slab 的基本模型,但修复了 SLAB 设计中的几个缺陷,特别是围绕具有大量处理器的系统。自 2008 年 2.6.23 以来,它已在主线 Linux 内核中被默认采用。

让我们看看 SLUB 的实现细节,通过常见用例场景的示例。

首先,slab 中的对象在内存中是连续存储的,但它们通过链表在逻辑上相互连接:这样分配器总能找到下一个空闲对象,而无需关心已经使用的数据。

Fresh slab

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209174053937.png

Partial slab

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209174109789.png

在 SLUB 中,与 SLAB 不同,指向下一个空闲对象的指针直接存储在对象本身内部,不需要额外的元数据空间并实现 100% 的 slab 利用率。

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209174228006.png

其中 objsize 是对象本身的大小,offset 是下一个指针之前的空间量,size 是总大小。

所有这些信息以及更多信息都存储在 off-slab 中,存储在跟踪此类元数据的数据结构中:kmem_cache。

每种对象类型只有一个 kmem_cache,并且该对象的所有 slab 都由同一个 kmem_cache 管理。这些结构通过双链表相互链接,可以通过导出的 slab_caches 变量从内核中的任何地方访问。

在 kmem_cache 内部,存储了两种指针以跟踪 slab:一个 kmem_cache_node 数组和一个指向 kmem_cache_cpu 的指针。后者管理 active slab:它只有一个,并且与当前 cpu 相关(SMD 处理器可以有不同的 active 缓存)。下一次分配的结果将始终从active slab 返回,由 freelist 字段指向。另一方面,kmem_cache_node 跟踪不活动的部分和完整的 slab:它们在free的情况下被访问,或者当partial slab 被填满并且另一个部分需要取代它的位置时。

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209174339469.png

例子

Standard allocation

分配器从 kmem_cache 导航到 kmem_cache_cpu 并访问空闲列表以找到第一个空闲对象,该对象被返回(红色)。相应地更新指针以将其从链接列表中删除,并且空闲列表指向下一个空闲对象。

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209174616262.png

Allocation that fills up the active slab, with an available partial

返回活动 slab 中的最后一个对象后,已填满的页面将在完整列表中移动,而部分列表中的另一个 slab 将成为活动 slab。

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209174702128.png

Allocation that fills up the active slab, with no available partial

返回active slab中的最后一个对象后,填满的页面在full list中移动,然后系统分配一个新的slab成为active slab。

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209174754407.png

Standard free

当内核释放属于部分 slab(或活动的 slab)的对象时,SLUB 只是将其标记为空闲并更新指针。

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209175222460.png

Free from empty

当内核释放属于部分 slab 的最后一个对象时,该 slab 被释放并交给内存管理单元。

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209175338042.png

Free from full

当内核释放一个属于完整 slab 的对象时,该 slab 不再是完整的并被移动到部分列表。

https://tuchuang-1304629987.cos.ap-chengdu.myqcloud.com//image/image-20230209175413104.png