前言
我们之前实验中通过切换cr3
以及将我们代码写入到GDT
中,通过调用公共区域内代码将notepad
中值进行了修改,在此次实验中我们将同样利用cr3
的进程切换来执行进程内私有的代码,而不需要将大量代码保存在GDT
中
实验过程
我们前面提到了每个进程存在有自己的私有空间,我们可以操作cr3
切换到其私有地址空间中,在此次实验我们构造两个相同基地址的程序,通过进程的切换来完成对全局变量的修改
主程序
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
| #include <stdio.h> #include <Windows.h>
DWORD g_cr3; DWORD g_signFlag = 0;
void __declspec (naked) IdtEntry() { __asm { mov eax, cr3 mov g_cr3, eax iretd; 提前返回
; addr 0x0401049 nop nop nop nop nop nop nop nop ; addr 0x401051 nop ; addr 0x0401052 mov g_signFlag, 0xAABBCCDD
; addr 0x040105C mov eax, ds: [0x8003f150] mov cr3, eax; 进程切换
; addr 0x0401065 } }
void crash() { __asm { int 0x20 } }
int main() { if ((DWORD)IdtEntry != 0x00401040) { printf("IdtEntry addr: %p\n", IdtEntry); return 0; } crash(); while (1) { printf("Cr3 addr : %p g_signFlag addr : %p\n", g_cr3, g_signFlag); Sleep(1000); }
system("pause"); return 0; }
|
副程序
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
| #include <stdio.h> #include <Windows.h>
DWORD g_cr3 ; DWORD g_signFlag;
void __declspec (naked) IdtEntry() { __asm { mov eax, cr3 mov ds : [0x8003f150] , eax; 将Cr3 丢入到 GDT 中
mov eax, 0x0AAC0300; 进程切换 mov cr3, eax
; addr 0x0401051->nop mov g_signFlag, 0x12345678 mov g_signFlag, 0x12345678 ; addr 0x401065 iretd } }
void crash() { __asm { int 0x20 } }
int main() {
if ((DWORD)IdtEntry != 0x00401040) { printf("IdtEntry addr: %p\n", IdtEntry); return 0; } crash(); system("pause"); return 0; }
|
我们可以看出来主程序下面直接使用iretd
进行了返回,因此下面的代码将不会被执行,但是其仍然写入到了内存空间中。主程序主要将cr3
以及signFlag
的值进行输出方便我们观察其值的变化
我们在副程序中写入其cr3
到GDT
的公共区域,为我们后面切换到主程序中后还可以切换回来做准备。因为两个程序的基地址是相同的,所以我们不用填充较多的代码来到达相应的地址空间中。因为我们切换cr3
后的代码地址仍然保持不变,仍然为顺序执行后续地址空间内的代码,利用这个特性,我们切换到主程序中相同的地址处便可以执行主程序同样地址中的对应代码
我们先执行主程序可以看到如下界面:
当我们执行副程序后可以看到g_signFlag
的值被修改为了0xAABBCCDD
其执行流程为:
副程序中的两个mov g_signFlag, 0x12345678
相当于填充字节数没有实际作用
总结
此次实验我们利用cr3
的特性对私有进程内代码对程序自身进行设置与修改,我们成功的修改了g_signFlag
的值。
实际上当我们拥有内核权限时,对于病毒等恶意程序可以利用上述操作来对抗静态反编译。通过对进程的切换并执行不同的代码而达到隐藏核心代码的效果,对于一些游戏保护程序也可以利用上述思路来实现对游戏的保护