首页
社区
课程
招聘
2k进ring0
发表于: 2004-9-18 21:35 6711

2k进ring0

2004-9-18 21:35
6711
1> Windows陷阱机制简介

陷阱(Trap)是Windows系统中一种不可缺少的系统机制。当系统中发生中断(硬件中断或软件中断),异常时,处理器会捕捉这个动作,并将系统的控制转移到一个固定的处理程序处,进行相应的操作处理。在处理器开始处理发生的中断或异常前,必须保存一些处理器环境参数到堆栈中以备系统还原时使用。系统是通过一种称为陷阱帧(Trap Frame)的方式来实现的,它将系统中全部线程的环境数据保存到内核堆栈(Kernel Stack)中,在执行完后通过堆栈的出栈机制来恢复系统控制流程中的执行点。内核中的陷阱机制分为中断和异常。中断是系统中随即发生的异步事件,与当前系统的处理器状态无关。同时系统中的中断可分为可屏蔽中断和不可屏蔽中断。而异常则是一种同步事件,在特定情况下异常可以重现,而中断不可以。中断又可以分为硬件中断和软件中断。很明显硬件中断是与硬件相关的,比如I/O设备执行的某些操作,处理器时钟或硬件端口上的处理等。软件中断则是通过中断指令int xx引入的,它往往是应用程序在用户模式执行后进入操作系统的代码,这时系统为用户提供了各种各样的系统服务。比如我们上次提到的系统服务调用(System Service Call),在Windows NT/2000下就是通过软件中断int 0x2e(System Service Interrupt)来实现的,虽然在Windows XP/2003下微软使用了一种称为“快速系统调用接口”来为用户提供系统服务,不过大量的中断服务仍然存在与系统之中的。

2> 中断处理及其相关流程

此处我们讨论的是与特定处理器相关的数据结构,所以会有一些移植方面的问题,本文仅针对Intel的x86 Family处理器,并且本文附带的程序也只支持在Intel x86处理器上正常执行。何为IDT?IDT(Interrupt Descriptor Table)称为中断描述符表。它是可容纳8192个单元的数组,数组中的每个成员是称之为“门”的长度为8字节的段描述符。在IDT中门可分为三种:中断门(Interrrupt Gate),陷阱门(Trap Gate)和任务门(Task Gate),但主要的是中断门和陷阱门。而它们两者之间也只有少许差别,我们在此只关心IDT中的中断门,如果您对这方面比较感兴趣,请查阅Intel处理器的相关文档《Intel Architecture Software Developer's Manual,Volume 3》。同时,在系统中存在一个中断描述符表寄存器(IDTR),它包含了系统中断描述符表的基地址和IDT的限制信息,它于一条汇编指令sidt息息相关。在下文中我们将看到它是我们实现各种中断描述符表扩展的基础和关键!还有一点是需要注意的,在Windows系统中引入了分页,分段和虚拟存储机制后,就存在这一种调度机制,将需要执行的代码和数据调入内存,将不需要的数据调到外存(辅助存储器,如硬盘等)。如果我们在执行某些代码时发现了我们需要的数据不在内存中时,就会发出一个“缺页中断”,这时系统就会在IDT中搜寻这个中断的ISR(Interrupt Service Routine,中断服务例程),执行相应的调入工作。大家可以想象如果我们的中断描述符表被调出到外存后会是什么样的结果?那时系统将无法定位“缺页中断”的服务例程,至此系统将会崩溃掉!

在中断描述符表中,我们刚才提到了一个感兴趣的寄存器IDTR,当然我们更关心对我们来说更直接的数据:IDT中的代码段选择器(Code Segment Selector),中断执行代码的偏移量(Offset)和中断描述符的权限等级(Descriptor Privilege Level)参数。下面我们看看中断指令的执行流程,我们应该知道应用程序执行在用户模式(Ring 3)下,而中断描述符表则是存在于内核模式(Ring 0)才可以访问的系统地址空间内的。在软件中断发生后,也就是应用程序调用了某条软件中断指令后,处理器首先在IDT中检索传入的中断号参数,找到响应的入口单元后就检查中断门的权限等级参数,看是否允许Ring 3下的应用程序调用,这样操作系统就为我们保留了对软件中断调用控制的权力,然而硬件中断和异常是不会关注权限方面的信息。如果当前权限等级(Current Privilge Level,CPL)数值大于中断门描述符需要的权限(Descriptor Privilege Level),也就是权限不够时会引发一个通用保护故障(General Protection Fault),反之则进行处理器的切换从用户堆栈到内核堆栈。现在是保存线程环境的时候了,处理器将在用户模式下的堆栈指针(SS:ESP)和标准的中断帧(EFLAGS和CS:EIP)压入堆栈。之后处理器进入我们的中断服务例程,执行相关的代码处理后通过汇编指令iretd返回到调用的应用程序。在指令iretd执行时,系统将存储在堆栈中的线程环境数据出栈还原,待系统恢复中断指令执行前的环境后就接着执行应用程序的后续代码。

3> 中断相关数据结构

首先我们介绍一下前面我们提到的一条关键汇编指令sidt的相关数据结构。在执行指令sidt后,系统将会把中断描述符表的基地址和限制(总共长六字节)保存在指令中指向的变量指针中,这就是我们进行IDT操作的入门口。

typedef struct _idtr
{
//定义中断描述符表的限制,长度两字节;
short IDTLimit;
//定义中断描述服表的基址,长度四字节;
unsigned int IDTBase;
}IDTR,*PIDTR;

当我们获得了IDT的入口后,就会在中断描述符表中检索我们需要处理的中断号对应的IDT单元,单元中包含了很多我们需要注意的数据结构,其中我们最为关心的是代码段选择器,中断代码执行的偏移量和特权等级等,那好我们先给出它的定义,在下文中我们将详细讨论它们的具体应用。

typedef struct _idtentry
{
//中断执行代码偏移量的底16位;
unsigned short OffsetLow;
//选择器,也就是寄存器;
unsigned short Selector;
//保留位,始终为零;
unsigned char Reserved;
//IDT中的门的类型:包括中断门,陷阱门和任务门;
unsigned char Type:4;
//段标识位;
unsigned char SegmentFlag:1;
//中断门的权限等级,0表示内核级,3表示用户级;
unsigned char DPL:2;
//呈现标志位;
unsigned char Present:1;
//中断执行代码偏移量的高16位;
unsigned short OffsetHigh;
}IDTENTRY,*PIDTENTRY;

4> 创建软件中断钩子的作用

作为普通的Windows程序员,或许您需要的是熟悉对系统基本功能的操作,以及对通用程序开发的熟练掌握。但对于一个有想法的Windows内核级分析开发人员来说,对系统底层的深入了解是非常必要的,同时也是非常重要的。Hook为我们创造了一个绝好的机会,它使我们了解系统内部运行机制的想法成为了一种可能。同时,书写一个系统相关的监视程序可以自动的对系统内部操作进行记录与分析。当然我们不能局限于对系统的了解,我们更渴望实施对系统的修改与扩展,改变系统原有的操作特性,注入我们需要的功能组件,让系统做更适合我们自己,也是我们最希望看到的操作。前面我们曾经谈到了创建系统服务调用的钩子来截获系统服务调用,同样在Windows2000下,系统服务是通过系统服务中断(System Service Interrupt,int 0x2e)来实现的,通过截获软件中断同样可以达到监视并修改系统服务调用的功能。在此我们主要讨论的是为软件中断创建钩子,不过对于硬件中断和异常也同样不例外,我们同样可以将本文提到的方法应用于硬件中断和异常。比如我们也可以通过截获键盘驱动的中断调用来书写内核级的键盘记录器,它可以直接对每次击键和释放进行操作,效果是非常的明显,不过这还需要使用到一些微软为我们提供的与硬件中断钩子相关的函数。

5> 如何创建软件中断钩子?

其实创建软件中断钩子的过程应该是比较明显了,下面我们将先简要介绍一下创建Hook的过程,然后以实际代码进行具体的讲解。首先我们通过汇编指令sidt(sidt: Store Interrupt Descriptor Table Register;lidt: Load Interrupt Descriptor Table Register)来获取IDT的基地址IDTBase,然后我们在中断描述符表中搜寻我们需要HOOK的中断号HOOKINTID,它应该是在0-255内的一个整数,虽然最新的Intel处理器声称支持8192个中断描述符单元,但由于某些限制原因,仍然只能处理前256个中断描述门。在找到我们需要Hook的中断描述门后,将它原本的中断执行代码偏移量(32位)保存到一个全局变量OldISR中,以备我们在执行中断处理或恢复IDT时使用。这样新的IDT中对应中断号的执行代码偏移量就指向了我们自己的处理代码了。在我们的处理代码NewISR中,注意先要保存一些线程环境,在处理完我们额外添加的执行程序(Monitor,监视注册表相关的16个系统服务调用)后,恢复现场并执行中断门以前指向的程序代码。这样,对外就看不出我们对中断门做了什么额外的处理,感觉和以前没什么两样!如果我们只是处理了我们添加的代码而没有继续执行中断门对应的以前的程序代码,那么系统必将混乱甚至崩溃!同样在我们卸载我们的软件中断钩子时,就是进行了一个逆向工作。先获取IDT的基地址,然后将保存在全局变量中的旧的执行代码地址偏移量赋给对应中断号的偏移量单元(OffsetLow/OffsetHigh)。大概过程讲得差不多了,相关程序为T-HookInt,我们再看看代码吧!

VOID
HookInt(VOID)
{
//保存IDT入口的基地址和限制信息的数据结构;
IDTR idtr;
//记录IDT数组的指针,通过它可以查找到我们需要Hook中断号对应的中断门;
PIDTENTRY IdtEntry;

//汇编指令sidt,获取IDT入口信息;
__asm sidt idtr;

//赋予IDT基地址值;
IdtEntry = (PIDTENTRY)idtr.IDTBase;

//保存中断号HOOKINTID对应中断门所指向的执行代码偏移量,以备执行中断处理或恢复时使用;
OldISR = ((unsigned int)IdtEntry[HOOKINTID].OffsetHigh << 16) | (IdtEntry[HOOKINTID].OffsetLow);

//关中断
__asm cli
//更新执行代码偏移量的底16位;
IdtEntry[HOOKINTID].OffsetLow = (unsigned short)NewISR;
//更新执行代码偏移量的高16位;
IdtEntry[HOOKINTID].OffsetHigh = (unsigned short)((unsigned int)NewISR >> 16);
//开中断
__asm sti;
}

VOID
UnhookInt(VOID)
{
IDTR idtr;
PIDTENTRY IdtEntry;

__asm sidt idtr;
IdtEntry = (PIDTENTRY)idtr.IDTBase;

__asm cli
//恢复中断号HOOKINTID对应中断门执行代码偏移量的底16位;
IdtEntry[HOOKINTID].OffsetLow = (unsigned short)OldISR;
//恢复中断号HOOKINTID对应中断门执行代码偏移量的高16位;
IdtEntry[HOOKINTID].OffsetHigh = (unsigned short)((unsigned int)OldISR >> 16);
__asm sti;

}

VOID
__fastcall
Monitor()
{
……
//由于我们处理的中断号为0x2e,
//对应于系统服务中断(System Service Interrupt),
//通过获取eax寄存器中的数值来区分系统服务调用;
__asm mov dwServiceId,eax;

//执行内核函数获取当前进程的ID号;
dwProcessId = (unsigned int)PsGetCurrentProcessId();

//提升当前IRQL,防止被中断;
KeRaiseIrql(HIGH_LEVEL,&OldIrql);

switch(dwServiceId)
{
//如果eax对应的数值为0x23,
//则对应于Windows2000的ZwCreateKey系统服务调用;
case 0x23:
DbgPrint("ProcessId: %d ZwCreateKey\n",dwProcessId);
break;
……
default:
break;
}

//恢复原始IRQL;
KeLowerIrql(OldIrql);
}

6> 添加软件中断的作用与原理

通过添加软件中断,我们可以扩展系统的功能,改变系统的很多操作行为。在前面我们介绍过为系统添加新的系统服务调用来扩展系统,通过添加新的软件中断同样可以到达添加系统服务调用的目的,并且我们可以在新添的中断处理程序中执行Ring 0级别的任意代码,那是何等的让人欣慰!

其实在IDT中,256个中断门单元并不是被完全利用的,还剩下一些流给将来扩展使用的中断门,我们可以自己给这些未使用的中断门添加一些机制为我所用。其实添加软件中断的过程和前面我们详细讲解的添加软件中断钩子有很多相似的地方,所以在此我就不做很详细的介绍了。同样是,首先获得IDT的基地址,然后在中断描述符表中查找我们将要添加的中断号对应的中断门描述符,之后给相关的参数赋值,使其成为名副其实的软件中断门。这时我们就可以在应用程序中使用中断指令int xx来调用我们自己中断门中的服务程序了。

7> 添加软件中断的实现过程

相关程序为T-ADDIG(Add Interrupt Gate),我们来看看代码哈~

NTSTATUS
InstallIG()
{
……

//判断我们想要添加的中断是否已被占用;
if(IdtEntry[ADDINTID].OffsetLow != 0
|| IdtEntry[ADDINTID].OffsetHigh != 0)
{
return STATUS_UNSUCCESSFUL;
}

//复制原始的中断门描述信息;
RtlCopyMemory(&OldIdtEntry,&IdtEntry[ADDINTID],sizeof(OldIdtEntry));

//关中断
__asm cli

//更新执行代码偏移量的底16位;
IdtEntry[ADDINTID].OffsetLow = (unsigned short)InterruptServiceRoutine;
//目的代码段的段选择器,CS为8;
IdtEntry[ADDINTID].Selector = 8;
//保留位,始终为零;
IdtEntry[ADDINTID].Reserved = 0;
//门类型,0xe代表中断门;
IdtEntry[ADDINTID].Type = 0xe;
//SegmentFlag设置0代码为段;
IdtEntry[ADDINTID].SegmentFlag = 0;
//描述符权限等级为3,允许用户模式程序调用本中断;
IdtEntry[ADDINTID].DPL = 3;
//呈现标志位,设置为一;
IdtEntry[ADDINTID].Present = 1;
//更新执行代码偏移量的高16位;
IdtEntry[ADDINTID].OffsetHigh = (unsigned short)((unsigned int)InterruptServiceRoutine >> 16);

//开中断
__asm sti

return STATUS_SUCCESS;
}

VOID
RemoveIG()
{
……
__asm cli
//恢复我们修改过的中断门描述符;
RtlCopyMemory(&IdtEntry[ADDINTID],&OldIdtEntry,sizeof(OldIdtEntry));
__asm sti
}

extern
void
_cdecl
InterruptServiceRoutine(VOID)
{
unsigned int Command;
//获取eax寄存器中的数值,接受从用户模式传入的命令参数;
__asm mov Command,eax;
//执行内核代码,获取操作系统版本号;
DbgPrint("NtBuildNumber == %d\n",(unsigned short)NtBuildNumber);
//中断返回;
__asm iretd;
}

后记

写到这儿,我们只是介绍了扩展IDT的一些基本方法,当然还有很多更深入的,更值得我们研究的课题需要大家努力去探索。比如我们可以将T-HookInt扩展,不仅仅是监视系统注册表操作相关的系统服务调用,不过在Windows XP/2003上由于其内在机制的一些变更,所以通过Hook int 0x2e来截获系统服务调用就不这么现实了。当然还有基于IDT的内核级后门,可以通过添加新的软件中断为任意用户提供SYSTEM权限级别的Command等。总之,探究Windows内核奥秘的旅行还未结束,或许这只能算是一次起航罢了。

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (8)
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
2
支持.
就是风水不在,要不然他会给出演示程序的
2004-9-19 02:04
0
雪    币: 241
活跃值: (145)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
非常不错.nbw在哪里可以搞到演示程序.
2004-9-19 10:36
0
雪    币: 339
活跃值: (1510)
能力值: ( LV13,RANK:970 )
在线值:
发帖
回帖
粉丝
4
这是wowo写的.他开头说的那些文章我也没见到过.
他们现在早就不研究如何进入ring 0 了.按照vxk的说法:"几年前就不在 ring 3混了"

                      总结进入RING0的方法
                        by wowocock1/CVC.GB

关于进入RING0层的方法,大家一定听说过不少,我在复习保护模式编程中将一些进RING0
的方法;总结了一下,包括调用门,任务门,中断门,陷阱门等,这些方法都是直接利用
IA32的方法,所以和操作系统应该没有多大关系,当然由于NT内核对GDT,IDT,的保护所
以我们不能用这些方法,不过如果一旦突破了NT的保护,那么所有的方法就都可以使用了,
其他的还有SEH等方法,我在前面的文章中也有介绍。

-----------------Code---

;========================================
;      WOWOCOCK  编写      ;
;======================================== 
   .586p
   .model flat, stdcall
   option casemap :none  ; case sensitive
   include \masm32\include\windows.inc
   include \masm32\include\kernel32.inc
   include \masm32\include\user32.inc
   includelib \masm32\lib\kernel32.lib
   includelib \masm32\lib\user32.lib
;;--------------
TSS       STRUC
TRLink     dw   0   ;链接字段
        dw   0   ;不使用,置为0
TRESP0     dd   0   ;0级堆栈指针
TRSS0      dw   0   ;0级堆栈段寄存器
        dw   0   ;不使用,置为0
TRESP1     dd   0   ;1级堆栈指针
TRSS1      dw   0   ;1级堆栈段寄存器
        dw   0   ;不使用,置为0
TRESP2     dd   0   ;2级堆栈指针
TRSS2      dw   0   ;2级堆栈段寄存器
        dw   0   ;不使用,置为0
TRCR3      dd   0   ;CR3
TREIP      dd   0   ;EIP
TREFlag     dd   0   ;EFLAGS
TREAX      dd   0   ;eax
TRECX      dd   0   ;ecx
TREDX      dd   0   ;edx
TREBX      dd   0   ;ebx
TRESP      dd   0   ;esp
TREBP      dd   0   ;ebp
TRESI      dd   0   ;esi
TREDI      dd   0   ;edi
TRES      dw   0   ;ES
        dw   0   ;不使用,置为0
TRCS      dw   0   ;CS
        dw   0   ;不使用,置为0
TRSS      dw   0   ;ss
        dw   0   ;不使用,置为0
TRDS      dw   0   ;DS
        dw   0   ;不使用,置为0
TRFS      dw   0   ;FS
        dw   0   ;不使用,置为0
TRGS      dw   0   ;GS
        dw   0   ;不使用,置为0
TRLDTR     dw   0   ;LDTR
        dw   0   ;不使用,置为0
TRTrip     dw   0   ;调试陷阱标志(只用位0)
TRIOMap     dw   $+2  ;指向I/O许可位图区的段内偏移
TSS       ENDS
  .data
sztit   db "Gate Test",0
CTEXTCall db "call gate to Ring0!继续?",0
CTEXTInt  db "int gate to Ring0 By int 5 !继续?",0
CTEXTIntx db "int gate to Ring0 By int X !继续?",0
CTEXTTrap db "Trap gate to Ring0 By int 1!继续?",0
CTEXTFault db "Fault gate to Ring0!继续?",0
CTEXTTask db "Task gate to Ring0!继续?",0
temp1 db "Cr3的内容是:%8X",0
temp2 db 100 dup(?)
Freq db 08h    ;发声频率
gdtR df 0
idtR df 0
ldtR dw 0     
trR  dw 0     ;the contents of GDTR,IDTR,LDTR,TR

ldtDes dw 0    
    dw 0    ;LDT Limit  
    dd 0    ;LDT Base
Callgt dq 0    ;call gate's sel:off

TrDes dw 0    
    dw 0    ;TR Limit  
    dd 0    ;TR Base

Tss1Sel dw ?   ;TSS

Call32  dd 0
Tss1Gate dw ?   ;任务门

TSS1 TSS <>
Tss1Limit equ $-TSS1

TSS2 TSS <>
TestCR3 dd 4

MyCall MACRO Selector,Offsetv
 db 09ah
 dd Offsetv
 dw Selector
ENDM
;;-----------------------------------------
  .code
__Start:
  sgdt  fword ptr gdtR
  sidt  fword ptr idtR
  sldt  word ptr ldtR   
  str   word ptr trR  ;save them for later use

    ;-----------------------
    ; get the ldt mes
    ;-----------------------
    movzx esi,ldtR
    add  esi,dword ptr [gdtR+2] ;esi->ldt descriptor

    mov  ax,word ptr [esi]
    mov  word ptr [ldtDes],ax
    mov  ax,word ptr [esi+6]
    and  ax,0fh
    mov  word ptr [ldtDes+2],ax     ;get ldt Limit
    
    mov  eax,[esi+2]
    and  eax,0ffffffh
    mov  ebx,[esi+4]
    and  ebx,0ff000000h
    or  eax,ebx
    mov  dword ptr [ldtDes+4],eax    ;get ldt Base
    ;-----------------------
    ; get the tr mes
    ;-----------------------    
    movzx esi,trR
    add  esi,dword ptr [gdtR+2]

    mov  ax,word ptr [esi]
    mov  word ptr [TrDes],ax
    mov  ax,word ptr [esi+6]
    and  ax,0fh
    mov  word ptr [TrDes+2],ax     ;get tr Limit
    
    mov  eax,[esi+2]
    and  eax,0ffffffh
    mov  ebx,[esi+4]
    and  ebx,0ff000000h
    or  eax,ebx
    mov  dword ptr [TrDes+4],eax;get tr Base
    ;-------------------------------------
    ; 这里演示在GDT中寻找空白表项来制造调用门
    ;-------------------------------------
    mov  esi,dword ptr [gdtR+2] ;esi->gdt base
    movzx eax,word ptr [gdtR]  ;eax=gdt limit
    call  Search_XDT
                        ;esi==gdt Base
    mov  esi,dword ptr [gdtR+2]
    push  offset myring0_prc_callgt    ;set callgate in gdt
    pop  word ptr [esi+eax+0]    
    pop  word ptr [esi+eax+6]      ;offset

    mov  word ptr [esi+eax+2],28h
    mov  word ptr [esi+eax+4],0EC00h 
        ;sel=28h,dpl=3,and attribute ->386 call gate!

    and  dword ptr Callgt,0
    or   al,3h
    mov  word ptr [Callgt+4],ax
    call  fword ptr [Callgt]      ;use callgate to Ring0!

    ;--------------------------------------------
    ; 这里演示在Ldt中制造调用门
    ;--------------------------------------------
    invoke  MessageBoxA,0, addr CTEXTCall,addr sztit,MB_YESNO
    cmp  eax,IDNO
    jz   @xit000           ;继续演示?

    mov  esi,dword ptr [ldtDes+4]   ;esi->ldt base
    mov  eax,dword ptr [ldtDes]    ;eax=ldt limit

    call  Search_XDT          ;eax返回找到的空白选择子
    mov  esi,dword ptr [ldtDes+4]

    push  offset myring0_prc_callgt  ;set callgate in ldt
    pop  word ptr [esi+eax+0]    
    pop  word ptr [esi+eax+6]    ;offset

    mov  word ptr [esi+eax+2],28h
    mov  word ptr [esi+eax+4],0EC00h 
        ;sel=28h,dpl=3,and attribute ->386 call gate!

    and  dword ptr Callgt,0
    or   al,7h            ;所以选择子一定要指向LDT
    mov  word ptr [Callgt+4],ax
    call  fword ptr [Callgt]      ;use callgate to Ring0! 

; *通过中断门进入ring0,像在Dos下一样,我们只要替换中断向量表的地址以指向我们
; *自己的程序就可以了,不过在win下中断向量表变为IDT(中断描述符表),其第0~1保存
; *中断处理程序偏移的低16位,6~7字节保存偏移的高16位,我们必须使用描述符具有DPL=3
; *的中断门以便在ring3下转入中断程序,而int 03h,04h,05h,10h,13h,30h等本来就是
; *DPL=3,我们可以方便地利用之,注意中断处理程序返回用iretd
    ;---------------------------
    ; 下面利用int 5进入ring0
    ;---------------------------
    invoke  MessageBoxA,0,addr CTEXTInt,addr sztit,MB_YESNO
    cmp  eax,IDNO
    jz   @xit000           ;继续演示?

    mov  esi,dword ptr [idtR+2]    ;esi->idt base
    push  dword ptr [esi+8*5+0]
    push  dword ptr [esi+8*5+4]    ;保存INT 5,中断描述符

    push  offset myring0_prc_Intgt   ;替换原来INT5的入口地址
    pop  word ptr [esi+8*5]
    pop  word ptr [esi+8*5+6]
    int  5              ;进入ring0!
    ;int  3              ;//可选择利用int 3
    ;db  0CCh            ;//则保存和恢复就改为8*3
    ;为了增强反跟踪效果
    ;当然也可以利用int 1,方法一致不过可能在某些处理器上冲突                    
    pop  dword ptr [esi+8*5+4]    ;恢复,int 5,中断描述符
    pop  dword ptr [esi+8*5+0]

; *当然,上面使用的全部是DPL=3的int如1,3,5等,如果我们准备使用任意int 来达到
; *目的该怎么办?这就需要自己改int descriptor 的属性值,使DPL=3,sel=28h
; *如下面使用int 255
; *__________________________________________
    invoke  MessageBoxA,0,addr CTEXTIntx,addr sztit,MB_YESNO
    cmp  eax,IDNO
    jz   @xit000           ;继续演示?

    movzx ebx,word ptr [idtR] ;ebx=idt limit
    sub ebx,7

    push dword ptr [esi+ebx+0] ; save IDT entry
    push dword ptr [esi+ebx+4]

    push offset myring0_prc_Intgt
    pop word ptr [esi+ebx+0]
    mov word ptr [esi+ebx+2],28h   ;ring0 Sel
    mov word ptr [esi+ebx+4],0EE00h ;P=1,386中断门,DPL=3
    pop word ptr [esi+ebx+6]
    
    ;mov eax,ebx
    ;shl eax,5
    ;add eax,90C300CDh

    ;push eax
    ;call ss:esp ; 在堆栈中形成指令 int 5Fh ret直接转入执行!
    int 5fh
    ;pop eax         ; int调用,酷吧!
    
    pop dword ptr [esi+ebx+4]; 恢复
    pop dword ptr [esi+ebx+0]
; *
; *还有其他的方法进入ring0,如陷阱门,与中断门基本一致,只不过是让硬件自己产生中断
; *我们则自己置TF=1引发之,注意在中断处理中关闭TF,否则会造成死循环,不断单步,还有故,
; *障门产生故障之后注意cs:eip已经压入堆栈,如果不改指令的话,就得自己修改eip指向安全
; *地址故障门的好处在于不必自己置sel为28h,也不必担心DPL=0,操作系统为我们准备好了一
; *切我们只要替换int处理地址就行了,以下是简单例子
; *__________________________________________
    invoke  MessageBoxA,0,addr CTEXTTrap,addr sztit,MB_YESNO
    cmp  eax,IDNO
    jz   @xit001           ;继续演示?
    ;---------------------------------
    ; int1 单步陷阱或者int4 除法溢出陷阱
    ; 这里演示int 1,int 4类似
    ; 这个和上面的有不同吗,有!就是int 1
    ; 是由CPU而不是我们显式用int 1指令引发
    ;---------------------------------
    push dword ptr [esi+(8*1)+0]   ; 保存原int 1
    push dword ptr [esi+(8*1)+4] 

    push offset myring0_prc_Trapgt
    pop word ptr [esi+(8*1)+0]
    pop word ptr [esi+(8*1)+6]

    pushfd
    pop eax 
    or ah,1 
    push eax
    popfd     ; set TF=1

    nop      ; ring0!

    pop dword ptr [esi+(8*1)+4]; restore IDT entry 
    pop dword ptr [esi+(8*1)+0]
    ;--------------------------------------------
    ; 这里演示故障门,除法错误
    ;--------------------------------------------
    @xit001:invoke  MessageBoxA,0,addr CTEXTFault,addr sztit,MB_YESNO
    cmp  eax,IDNO
    jz   @xit000           ;继续演示?

    push dword ptr [esi+(8*0)+0] ;              
    push dword ptr [esi+(8*0)+4]                       
                                           
    push offset Ring0Code_div                         
    pop word ptr [esi+(8*0)+0]                        
    pop word ptr [esi+(8*0)+6]       
                                           
    xor eax,eax                                
    div eax            ; 除法错误,进入故障门ring0!
    ;-----------------------------------------
    invoke  MessageBoxA,0,addr CTEXTTask,addr sztit,MB_YESNO
    cmp  eax,IDNO
    jz   @xit000
    ;-------------------------------------
    ; 这里演示在GDT中寻找空白表项来制造TSS
    ;-------------------------------------
    mov  esi,dword ptr [gdtR+2]
    movzx eax,word ptr [gdtR]
    call  Search_XDT
    and  ax,0fff8h
    mov  Tss1Sel,ax   ;save Tss1 selector ,esi==gdt Base
    mov  esi,dword ptr [gdtR+2]
    mov  ebx,offset TSS1
    mov  word ptr [esi+eax+0],Tss1Limit    
    mov  dword ptr [esi+eax+2],ebx    ;offset

    mov  word ptr [esi+eax+5],89h
    shr  ebx,24
    mov  byte ptr [esi+eax+7],bl ;set mytss

    ;--------------------------------------------
    ; 这里演示在Ldt中制造任务门
    ;--------------------------------------------
    mov  esi,dword ptr [ldtDes+4]
    mov  eax,dword ptr [ldtDes]

    call  Search_XDT          ;eax返回找到的空白选择子
    push  eax
    or   ax,7h
    mov  Tss1Gate,ax
    pop  eax
    mov  esi,dword ptr [ldtDes+4]

    mov  word ptr [esi+eax+0],0    
    mov  word ptr [esi+eax+6],0    ;offset
    push  word ptr Tss1Sel
    pop  word ptr [esi+eax+2]
    mov  word ptr [esi+eax+4],0E500h ;Tss Gate

    mov esi,dword ptr [TrDes+4]
    assume esi:ptr TSS
    push word ptr ldtR
    pop word ptr[esi].TRLDTR ;设置LDT SELECTOR(WINDOWS98的TSS中LDT 为0???)

    lea edi,TSS1
    assume edi:ptr TSS
    push word ptr trR
    pop word ptr [edi].TRLink  ;返回TSS选择子,设置联接字

    push dword ptr[esi].TRESP0  ;设置SP0
    pop dword ptr[edi].TRESP0
  
    push word ptr[esi].TRSS0   ;设置SS0
    pop word ptr[edi].TRSS0

    push dword ptr[esi].TRCR3  
       ;设置CR3寄存器,即设置好转换以后所有的段及页转换相关寄存器
    pop dword ptr[edi].TRCR3

    push offset Ring0
    pop dword ptr[edi].TREIP

    mov word ptr[edi].TRCS,28h  ;CS=28
    mov word ptr[edi].TRSS,30h  ;ss=30

    push word ptr ldtR      ;设置LDTR
    pop word ptr[edi].TRLDTR

    push ds
    pop dword ptr[edi].TRDS
    mov word ptr[edi+54h+2],0

    call fword ptr Call32
    mov ebx,dword ptr [TestCR3]
   @xit000:
    invoke wsprintf,addr temp2,addr temp1,TestCR3
    invoke MessageBoxA,0,addr temp2,addr sztit,0
    mov eax,dword ptr [ldtDes+4];恢复GDT,LDT中的空选择子。
    movzx esi,Tss1Gate
    and  esi,0fffffff8h
    add eax,esi
    mov dword ptr [eax],0
    mov dword ptr [eax+4],0
    mov eax,dword ptr [gdtR+2]
    movzx esi,Tss1Sel
    add eax,esi
    mov dword ptr [eax],0
    mov dword ptr [eax+4],0
    invoke  ExitProcess,0
    ;-----------------------------------------
Ring0Code_div proc far                            
                                       
    pushad                                  
    mov  ecx,10     ;EIP
   ambalance002:         ;cs
    push  ecx      ;EFLAGS
    call  Beeps
    pop  ecx
    loop  ambalance002                                
    popad                                   
                                       
    add dword ptr [esp],2 ; 修改Eip,略过除错指令(div eax)2个字节长,继续执行             
                                       
    iretd                                   
                                       
Ring0Code_div endp                              

myring0_prc_Trapgt  proc  far
    pushad         ;注意压栈结构为
    mov  ecx,10      ;esp->EIP
   ambalance002:       ;   cs
    push  ecx       ;   EFLAGS
    call  Beeps
    pop  ecx
    loop  ambalance002
    popad
    and  byte ptr [esp+9],0FEh ;一定要置TF=0,终止
    iretd         ;注意iretd,不是iret(w)
myring0_prc_Trapgt  endp

myring0_prc_Intgt  proc   far
    pushad
    mov  ecx,10
   ambalance001:
    push  ecx
    call  Beeps
    pop  ecx
    loop  ambalance001
    popad
    iretd        
myring0_prc_Intgt  endp

myring0_prc_callgt  proc far
    pushad
    pushfd
    pop eax 
    or eax,3000h 
    push eax
    popfd
    mov  ecx,10
   ambalance:
    push  ecx
    call  Beeps
    pop  ecx
    loop  ambalance
    popad
    retf
myring0_prc_callgt  endp
;-----------------------------------------
Search_XDT proc near   ;entry esi==Base of Ldt or GDT
             ;eax==Limit
    pushad   
    mov ebx,eax    ;ebx=limit
    mov eax,8     ; skipping null selector
   @@1:    
    cmp dword ptr [esi+eax+0],0  
    jnz @@2 
    cmp dword ptr [esi+eax+4],0  
    jz @@3 
   @@2:    
    add eax,8    
    cmp eax,ebx    
    jb @@1   ;if we haven't found any free GDT entry,
          ;lets use the last two entries    
    mov eax,ebx   
    sub eax,7     
   @@3:   
    mov [esp+4*7],eax   ; return off in eax
    popad         ; eax=free GDT entry selector
    ret    
Search_XDT endp
;-----------------------------------------
Beeps proc near        ;经典的发声子程序,学dos的时候应该
  pushad         ;没少用吧...
  mov al,0B6h      
  out 43h,al 
  mov al,Freq     ;接口要求,不要多问

  out 42h,al 
  out 42h,al 

  xor byte ptr Freq,0Ch   ; 换频率
                ; 以便下次发出不同的音高
  in al,61h
  or al,3 
  out 61h,al 
  mov ecx,1000000h    ;延时
  loop $

  and al,0FCh       ;关声音
  out 61h,al 
  popad
  ret
Beeps endp

  Ring0:
  mov ebx,cr3
  mov TestCR3,ebx
  iretd
END  __Start
;______________________________Over...___________
2004-9-19 12:20
0
雪    币: 2384
活跃值: (766)
能力值: (RANK:410 )
在线值:
发帖
回帖
粉丝
5
顶,好贴!!!;)
2004-9-19 12:24
0
雪    币: 204
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
顶,收藏~
2004-9-19 14:08
0
雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
7
不错,是一篇好文・
nbw兄,还给出来演示程序・
不错・多谢・
2004-9-19 15:42
0
雪    币: 241
活跃值: (160)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
98跟XP能不能进啊?
2004-9-19 19:40
0
雪    币: 140
活跃值: (70)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
存档再说
2004-9-20 13:39
0
游客
登录 | 注册 方可回帖
返回
//