前言
在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已经被正确的赋值了
总结
对于上述内容我们相当于有了一个分支结构,当我们的内存被读写时其便会挂载在物理页,我们可以利用其来实现一个代码保护或者是反调试的功能。
当我们的反编译器读取到此处内存时会导致一些赋值的操作产生,进而通过变量值的变化我们可以进行监测,出现不正常的赋值时我们便可以认定处于调试状态下,进而实现反调试的操作