Windows内核实验 ——— 内存延迟分配

前言

在Windows中对虚拟内存分配存在有一个机制,当我们分配的虚拟内存没有使用之前是不会映射物理内存的,此次实验中我们将构造一个新的节表,将一个变量写到此段表开辟的页头部,通过赋值来观察其是否映射到了物理内存中

实验内容

与之前相同,我们写一个简单的中断提取后构造一个节表,在节表中定义一个变量,将变量赋值给我们的全局变量,通过打印全局变量来观察Windows的内存是否映射到物理内存中

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
#include <stdio.h>
#include <Windows.h>

DWORD res = 0;
#pragma section(".ttt", read, write)
__declspec(allocate(".ttt")) DWORD a = 0x12345678;

void __declspec(naked) IdtEntry() {
__asm {
mov eax, a
mov res, eax
iretd
}
}

void crash() {
__asm {
int 0x20
}
}

int main() {
if ((DWORD)IdtEntry != 0x0401040) {
printf("addr : %p", IdtEntry);
return 0;
}
crash();
printf("res = %x\n", res);
system("pause");
return 0;
}

我们通过代码观察会显而易见的认为res的值将会被设置为0x12345678,但是我们实际运行一下你会发现res的值为 0

出现上述原因主要是我们在新的区段里定义了变量 a,此时其还没有映射到物理内存中,当我们需要真正访问对应的内存时CPU才会将其进行映射到物理内存中去。上述代码因为我们没有对将变量 a其映射到物理内存中,在第一次访问时会产生相应的缺页异常,然后异常处理程序发现对应的页未被挂上,挂上对应页后恢复异常继续执行。但是我们此处缺失相应的环境因此仍然导致其未能正确的赋值

对于上述问题我们可以通过访问变量 a的内存空间,当我们访问时其便已经挂在到物理内存中去了,我们可以构造一个新的程序对其进行验证

无论是外部访问还是自身代码访问都可以将其进行挂载到物理页面

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
#include <stdio.h>
#include <Windows.h>

DWORD res = 0;
#pragma section(".ttt", read, write)
__declspec(allocate(".ttt")) DWORD a = 0x12345678;

void __declspec(naked) IdtEntry() {
__asm {
mov eax, a
mov res, eax
iretd
}
}

void crash() {
__asm {
int 0x20
}
}

int main() {
if ((DWORD)IdtEntry != 0x0401040) {
printf("addr : %p", IdtEntry);
return 0;
}
system("pause");
crash();
printf("res = %x\n", res);
system("pause");
return 0;
}

上面代码我们只是简单的在main中添加了一条语句,我们需要做的是在第一个pause停下时通过CE来访问变量 a的地址,之后再继续打印一次res的值

先调试拿一下变量 a的地址

之后通过CE进行访问对应内存空间

之后我们继续程序

可以成功的看到res已经被正确的赋值了

总结

对于上述内容我们相当于有了一个分支结构,当我们的内存被读写时其便会挂载在物理页,我们可以利用其来实现一个代码保护或者是反调试的功能。

当我们的反编译器读取到此处内存时会导致一些赋值的操作产生,进而通过变量值的变化我们可以进行监测,出现不正常的赋值时我们便可以认定处于调试状态下,进而实现反调试的操作


Windows内核实验 ——— 内存延迟分配
https://equinox-shame.github.io/2023/06/11/Windows内核实验 — 内存延迟分配/
作者
梓曰
发布于
2023年6月11日
许可协议