avatar

内核中使用内存

内核中使用内存

c语言小知识:

https://zhuanlan.zhihu.com/p/87975119
https://zhuanlan.zhihu.com/p/86707793
https://zhuanlan.zhihu.com/p/55768830
https://zhuanlan.zhihu.com/p/55768545
https://zhuanlan.zhihu.com/p/38935280

程序的本质,就是内存里的一串串的数字,它们被 CPU 当作指令解析,才能够有意义

内存使用,无非就是申请、复制、设置、释放。在 C 语言里,它们对应的函数是:malloc、 memcpy、memset、free,在内核编程里,他们分别对应 ExAllocatePool、RtlMoveMemory、 RtlFillMemory、ExFreePool

PVOID ExAllocatePool(POOL_TYPE PoolType, SIZE_T NumberOfBytes);
VOID RtlMoveMemory(PVOID Destination, PVOID Source, SIZE_T Length);
VOID RtlFillMemory(PVOID Destination, SIZE_T Length, UCHAR Fill);
VOID ExFreePool(PVOID P);

RtlFillMemory 和 memset : 函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组,c:是赋给buffer的值,count:是buffer的长度 的原型不同、ExAllocatePool 和 malloc 的原型 : void *malloc(unsigned int size)也不同

ExAllocationPool多了一个参数,PoolType常用的只有 2 种:

PagedPool 和 NonPagedPool PagedPool 是分页内存,简单来说就是物理内存不够时,会把 这片内存移动到硬盘上,而 NonPagedPool 是无论物理内存如何紧缺,都绝对不把这片内存的内容移动到硬盘上

操作的内存,都是虚拟内存,和物理内存是两码事。但虚拟内存的数据是放在物理内存上的,两者存在映射关系,一 般来说,一片物理内存可以映射为多片虚拟内存,但一片虚拟内存必定只对应一片物理内存

假设虚拟内存是 0Xfffff80001234567 在物理内存的地址是 0x123456,当物理内存不够用 时,物理内存 0x123456 的原始内容就挪到硬盘上,然后把另外一片急需要用的内容移到物 理内存里。此时,当你再读取 0Xfffff80001234567 的内容时,就会引发缺页异常,系统就会 把在硬盘上的内容再次放到物理内存中(如果这个过程失败,一般就死机了)

一般来说,PagedPool 用来放数据(比如你用 ZwQuerySystemInformation 枚举内核模块,可以申请一大片 PagedPool 存放返回的数据), 而 NonPagedPool 用来放代码(你写内核 shellcode 并需要执行时,必须使用 NonPagedPool 存放 shellcode)。以我的经验来说,访问到切换出去的内存没事,但是执行到切换出去的内 存必然蓝屏

在用户态,内存是有属性的,有的内存片 只能读不能写 不 能 执行 ( PAGE_READ), 有的 内 存 片 可以读 可以写也可以执行 (PAGE_READ_WRITE_EXECUTE)。在内核里, PagedPool 和 NonPagedPool 都是可读可写可执 行的,而且没有类似 VirtualProtect 之类的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <ntddk.h>
#include <intrin.h >

void test()
{
PVOID ptr1 = ExAllocatePool(PagedPool, 0x100); //申请内存 相当于malloc
PVOID ptr2 = ExAllocatePool(NonPagedPool, 0x200);
RtlFillMemory(ptr2, 0x200, 0x90); //设置内存,相当于memset
RtlMoveMemory(ptr1, ptr2, 0x50);
ExFreePool(ptr1); //释放内存,相当于free
ExFreePool(ptr2);
}
//以下两个函数,在HOOK修改内存时使用
KIRQL WPOFFx64()
{
KIRQL irql = KeRaiseIrqlToDpcLevel();
UINT64 cr0 = __readcr0();
cr0 &= 0xfffffffffffeffff;
__writecr0(cr0);//将值Data写入 CR0 寄存器
_disable();//禁用中断
return irql;
}

void WPONx64(KIRQL irql)
{
UINT64 cr0 = __readcr0();
cr0 |= 0x10000;
_enable();
__writecr0(cr0);
KeLowerIrql(irql);//恢复当前的处理器到其原始值的IRQL
}

//此函数有点吹毛求疵,不推荐使用,因为效率太低了。
BOOLEAN SafeCopyMemory(PVOID pDestination, PVOID pSourceAddress, SIZE_T SizeOfCopy)
{
PMDL pMdl = NULL;
PVOID pSafeAddress = NULL;
if (!MmIsAddressValid(pDestination) || !MmIsAddressValid(pSourceAddress))
return FALSE;
pMdl = IoAllocateMdl(pDestination, (ULONG)SizeOfCopy, FALSE, FALSE, NULL);
if (!pMdl)
return FALSE;
__try
{
MmProbeAndLockPages(pMdl, KernelMode, IoReadAccess);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
IoFreeMdl(pMdl);
return FALSE;
}
pSafeAddress = MmGetSystemAddressForMdlSafe(pMdl, NormalPagePriority);
if (!pSafeAddress)
return FALSE;
__try
{
RtlMoveMemory(pSafeAddress, pSourceAddress, SizeOfCopy);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
;
}
MmUnlockPages(pMdl);
IoFreeMdl(pMdl);
return TRUE;
}

VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("[Memory]Unload...\n");
return;
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = DriverUnload;
DbgPrint("[Memory]Load...\n");
test();
return STATUS_SUCCESS;
}

测试结果:

img

Author: L0x1c
Link: https://l0x1c.github.io/2020/04/26/2020-2-23/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Donate
  • 微信
    微信
  • 支付寶
    支付寶