众所周知,在Windows95/98的Win32onIntelx86体系中利用了处理器的三环保护模型中的零环(Ring0,最高权限级别)和三环(Ring3,最低权限级别)。
一般应用程序都运行在Ring3下,受到严格的”保护”,只能规矩地使用Win32API。如果我们想进行一些系统级的操作,例如在嵌入汇编中使用诸如”MovEAX,CR0″,或像在DOS下那样调用一些必不可少的系统服务(如BIOS,DPMI服务)而用”Intxx”,都会导致”非法操作”。但这种能力有时是必不可少的,一到这种时候Microsoft就”建议编写一个VxD”。
VxD大家早有所闻了,在VxD里,不但可以执行CPU的所有指令,而且可以调用VMM(虚拟机管理器)和其他VxD提供的上千个系统级服务。
获得这一能力的最本质原因在于它运行在Ring0,与系统内核同一级别。但是它体系的复杂性、开发工具的不易获得、帮助文档的不完备,使Microsoft排除了一大批程序员和竞争对手。
而将在Windows2000(Windows98也开始支持)中取代VxD的WDM对Win95程序员也是个噩梦,它需要了解WindowsNT核心驱动模型。
有没有简单一些的办法呢?我们可以令一个普通Win32应用程序运行在Ring0下,从而获得VxD的能力吗?答案是肯定的。下面我们就简述一下这一技巧,有关Intelx86保护模式的基础知识请大家看有关书籍。
首先此技巧基于以下理论根据:
一、SIDT指令(将中断描述符表寄存器IDTR--64位宽,16~47Bit存有中断描述符表IDT基地址--的内容存入指定地址单元)不是特权指令,就是说我们可以在Ring3下执行该指令,获得IDT的基地址,从而修改IDT,增加一个中断门安置我们的中断服务,一旦Ring3程序中产生此中断,VMM就会调用此中断服务程序,而此中断服务程序就运行在Ring0下了。这一点与在DOS下非常相似。
二、Windows95Win32应用程序运行一个映射到全部4G内存的段中,选择子为0137h,Ring0中的VxD运行在另一个映射到全部4G内存的段中,选择子028h,这两个段除了选择子决定的访问权限不同外,没什么不同,各自段中相同的偏移量对应了相同的线性地址。所以我们放在Win32应用程序中的中断服务程序可以以Ring3的段偏移量被Ring0中的VMM寻址。
下面我们以具体例子进一步说明,程序中有详细注释。
这是一个Win32ConsoleProgram(控制台应用程序),虽然运行中看起来很像DOS筐中运行的实模式DOS程序,但它是货真价实的运行在Ring3下的Win32程序。用VisualC++5.0AppWizard创建一个Win32ConsoleProgram项目,添加以下.CPP文件,编译即可。
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 81 82 83 |
// 若 无DDK 带 下 划 线 的 可 略 去, 这 些 语 句 演 示 了 调 用VMM/VXD 服 务 DWORDLONG IDTR,SavedGate; WORD OurGate[4]={0,0x0028,0xee00,0x0000}; // 中 断 门 描 述 符 格 式 如 下: DWORD _eax,_ecx,_cr0; WORD vmmver; HVM sysvm; void nothing() { //Used to test call in Ring0 sysvm=Get_Sys_VM_Handle(); } void __declspec( naked ) Ring0Proc(void) // 中 断 例 程, 运 行 在Ring0 { _asm{ mov _eax,eax // mov _ecx,ecx // mov eax, CR0 // 测 试Ring3 中 不 能 执 行 的 特 权 指 令 mov _cr0,eax // } VMMCall(Get_VMM_Version); // 调 用VMM 服 务 _asm{ mov vmmver,ax } nothing(); // 测 试 在 运 行 于Ring0 的 中 断 例 程 中 调 用 子 _asm iretd // 中 断 返 回, 与 在 实 模 式 编 程 无 本 质 区 别 } void main() // 主 程 序 { _asm{ mov eax, offset Ring0Proc mov [OurGate], ax // 将 中 断 函 数 的 地 址 shr eax, 16 // 填 入 新 造 的 中 断 门 mov [OurGate +6], ax // 描 述 符 sidt fword ptr IDTR // 将 中 断 描 述 符 表 寄 存 器(IDTR) 的 内 容 取 出 mov ebx, dword ptr [IDTR +2] // 取 出 中 断 描 述 符 表(IDT) 基 地 址 add ebx, 8 *9 // 计 算Int 9 的 描 述 符 应 放 置 的 地 址 选 用 Int9 是 因 为 它 在Win32 保 护 模 式 下 未 占 用 mov edi, offset SavedGate mov esi, ebx movsd // 保 存 原 来 的Int 9 描 述 符 到 movsd //SavedGate 以 便 恢 复 mov edi, ebx mov esi, offset OurGate movsd // 替 换 原 来 的 中 断 门 描 述 符 movsd // 以 安 装 中 断 服 务 例 程 mov eax,0x6200 // 用 以 测 试 放 在EAX 中 的 数 据 能 否 正 确 传 到Ring0 中 断 mov ecx,0 // 用 以 测 试 放 在ECX 中 的 数 据 能 否 正 确 传 到Ring0 中 断 mov ecx,0 // 用 以 测 试 放 在ECX 中 的 数 据 能 否 正 确 传 到Ring0 中 断 // 因 为 很 多VxD 服 务 都 用 此 二 寄 存 器 传 递 参 数 int 9h // 人 为 触 发 中 断, 平 时 会 出 现 保 护 错 误 蓝 屏 或 非 法 操 // 作 对 话 框, 现 在 安 装 了 // 中 断 服 务 例 程 后, 就 会 通 过 //VMM 在Ring0 调 用 中 断 服 务 例 程 - -Ring0Proc mov edi, ebx mov esi, offset SavedGate movsd // 恢 复 原 来 的 中 断 门 描 述 符 movsd } cout<<"CR0="<<_cr0< } _getch(); if(0="=_getch())" while(_kbhit()="=0);" do{} continue.?< |