令Win32 应用程序跳入系统零层

发表于:2007-07-04来源:作者:点击数: 标签:
东南大学 卢威 周昊理 众所周知,在Windows95/98 的Win32 on Intel x86 体系中利用了处理器的三环保护模型中的零环(Ring0,最高权限级别)和三环(Ring3,最低权限级别)。一般应用程序都运行在Ring3 下,受到严格的 保护,只能规矩地使用Win32API。如果我们
东南大学 卢威 周昊理

  众所周知,在Windows95/98 的Win32 on Intel x86 体系中利用了处理器的三环保护模型中的零环(Ring0,最高权限级别)和三环(Ring3,最低权限级别)。一般应用程序都运行在Ring3 下,受到严格的" 保护",只能规矩地使用Win32API。如果我们想进行一些系统级的操作,例如在嵌入汇编中使用诸如"Mov EAX,CR0",或像在DOS 下那样调用一些必不可少的系统服务(如BIOS,DPMI 服务)而用"Int xx",都会导致" 非法操作"。但这种能力有时是必不可少的,一到这种时候Microsoft 就" 建议编写一个VxD"。VxD 大家早有所闻了,在VxD 里,不但可以执行CPU 的所有指令,而且可以调用VMM( 虚拟机管理器)和其他VxD 提供的上千个系统级服务。获得这一能力的最本质原因在于它运行在Ring0,与系统内核同一级别。但是它体系的复杂性、开发工具的不易获得、帮助文档的不完备,使Microsoft 排除了一大批程序员和竞争对手。而将在Windows2000(Windows98 也开始支持)中取代VxD 的WDM 对Win95 程序员也是个噩梦,它需要了解Windows NT 核心驱动模型。

  有没有简单一些的办法呢?我们可以令一个普通Win32 应用程序运行在Ring0 下,从而获得VxD 的能力吗?答案是肯定的。下面我们就简述一下这一技巧,有关Intel x86 保护模式的基础知识请大家看有关书籍。

  首先此技巧基于以下理论根据:

  一、SIDT 指令(将中断描述符表寄存器IDTR --64 位宽,16 ~47Bit 存有中断描述符表IDT 基地址--的内容存入指定地址单元)不是特权指令,就是说我们可以在Ring3 下执行该指令,获得IDT 的基地址,从而修改IDT,增加一个中断门安置我们的中断服务,一旦Ring3 程序中产生此中断,VMM 就会调用此中断服务程序,而此中断服务程序就运行在Ring0 下了。这一点与在DOS 下非常相似。

  二、Windows95 Win32 应用程序运行一个映射到全部4G 内存的段中,选择子为0137h,Ring0 中的VxD 运行在另一个映射到全部4G 内存的段中,选择子028h,这两个段除了选择子决定的访问权限不同外,没什么不同,各自段中相同的偏移量对应了相同的线性地址。所以我们放在Win32 应用程序中的中断服务程序可以以Ring3 的段偏移量被Ring0 中的VMM 寻址。

  下面我们以具体例子进一步说明,程序中有详细注释。

  这是一个Win32 Console Program(控制台应用程序),虽然运行中看起来很像DOS 筐中运行的实模式DOS 程序,但它是货真价实的运行在Ring3 下的Win32 程序。用Visual C ++5.0 AppWizard 创建一个Win32 Console Program 项目, 添加以下.CPP 文件, 编译即可。

#include<conio.h>
#include<iostream.h>
#include<windows.h>
#include<vmm.h>
 // 若无DDK 带下划线的可略去,
 这些语句演示了调用VMM/VXD 服务
DWORDLONG IDTR,SavedGate;
WORD OurGate[4]={0,0x0028,0xee00,0x0000};
// 中断门描述符格式如下:
http://www.ltesting.net/uploads/2007/07/1_200707042009045.jpg (10669 字节)
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<
运行结果:



  此方法的好处一是回避了奇特的VxD文件格式,不用使用汇编语言编程,二是应用程序不用带一个单独的VxD 文件,干净利索。
  值得一提的是,许多文章描述著名的CIH 病毒都说其利用了VxD 技术,是说它带一个单独的VxD文件吗?显然不可能,实际上CIH 病毒使用的就是以上技巧,进入Ring0后调用VMM 服务分配一块内存,把自身拷贝进去,然后用IFS VxD 的IFSMgr_InstallFileSystemApiHook 服务安装文件系统监视以感染其他文件,只不过CIH 病毒安装的是Int 3 中断,这跟在DOS 下没什么两样。我们也可以对此了解以更好地防范CIH 病毒。
  最后还要指出其缺陷,这个技巧仅能用在Windows95/97/98 下,在Windows NT 下行不通。不过听说CIH 病毒的作者已经开发了NT 版的CIH 病毒,说明NT 中也有类似的" 后门"。  

原文转自:http://www.ltesting.net