首页
论坛
课程
招聘

[原创]rootkit ring3进ring0之门系列[二] -- 中断门

2008-4-1 16:31 37777

[原创]rootkit ring3进ring0之门系列[二] -- 中断门

2008-4-1 16:31
37777
接上篇续,我们继续谈谈386保护模式中的门,今天一起学习下中断门。

386实模式下的中断和异常的转移方法与8086相同。这里介绍的中断和异常的转移方法是指 80386在保护模式下响应中断和处理异常时所采用的转移方法。
1.中断描述符表IDT
    象全局描述符表GDT一样,在整个系统中,中断描述符表IDT只有一个。中断描述符表寄存器IDTR指示IDT在内存中的位置。由于80386只识别256个中断向量号,所以IDT最大长度是2K。
    中断描述符表IDT所含的描述符只能是中断门、陷阱门和任务门。也就是说,在保护模式下,80386只有通过中断门、陷阱门或任务门才能转移到对应的中断或异常处理程序。

我们直观的看看IDT的内存位置,在本机 IDT: 8003f400
lkd> !pcr
KPCR for Processor 0 at ffdff000:
    Major 1 Minor 1
        NtTib.ExceptionList: b2c3fc7c
            NtTib.StackBase: b2c3fdf0
           NtTib.StackLimit: b2c3c000
         NtTib.SubSystemTib: 00000000
              NtTib.Version: 00000000
          NtTib.UserPointer: 00000000
              NtTib.SelfTib: 7ffde000

                    SelfPcr: ffdff000
                       Prcb: ffdff120
                       Irql: 00000000
                        IRR: 00000000
                        IDR: ffffffff
              InterruptMode: 00000000
                        IDT: 8003f400
                        GDT: 8003f000
                        TSS: 80042000

              CurrentThread: 873dd1e8
                 NextThread: 00000000
                 IdleThread: 80552d20
前面我们讲过80386只识别256个中断向量号,每个中断向量号占8个字节,对应着一个描述符。因此,中断描述表IDT共占 256 * 8 = 2048字节。
下面是中断描述符的结构。


在IDT中,并不是这256个中断向量都被系统占用了,系统紧紧占用了一小部分,大部分是空闲的,因此用户可以在其中添加自己的中断描述符,即中断门。

2.中断响应和异常处理的步骤
    由硬件自动实现的中断响应和异常处理的步骤如下:
    首先,判断中断向量号要索引的门描述符是否超出IDT的界限。若超出界限,就引起通用保护故障,出错码为中断向量号乘8再加2。
    其次,从IDT中取得对应的门描述符,分解出选择子、偏移量和描述符属性类型,并进 行有关检查。描述符只能是任务门、286中断门、286陷阱门、386中断门或386陷阱门,否则就引起通用保护故障,出错码是中断向量号乘8再加2。如果是由INT n指令或INTO指令引起转移,还要检查中断门、陷阱门或任务门描述符中的DPL是否满足CPL<=DPL(对于其它的异常或中断,门中的DPL被 忽略)。这种检查可以避免应用程序执行INT n指令时,使用分配给各种设备用的中断向量号。如果检查不通过,就引起通用保护故障,出错码是中断向量号乘8再加2。门描述符中的P位必须是1,表示门描述符是一个有效项,否则就引起段不存在故障,出错码是中断向量号乘8再加2。
    最后,根据门描述符类型,分情况转入中断或异常处理程序。 如下图:

   
3.通过中断门的转移
     如果中断向量号所指示的门描述符是386中断门,那么控制转移到当前任务的一个处理程序过程,并且可以变换特权级。与其它调用门的CALL指令一样,从中断门中获取指向处理程序的48位全指针。其中16位 选择子是对应处理程序或代码段的选择子,它指示全局描述符表GDT或局部描述符表LDT中的代码段描述符;32位偏移指示处理程序入口点在代码段内的偏移量。
    通过中断门的转移过程如下所示,该过程由硬件自动进行,简单了解下。
    (1)若选择子为空,则产生通用保护故障;
    (2)取对应的描述符;
    (3)若非存储段描述符,则产生通用保护故障;
    (4)若非一致代码段且DPL且段存在,则切换到内层堆栈;
    (5)调整RPL=0;
    (6)把描述符装入CS;
    (7)若入口偏移越界,则产生通用保护故障;
    (8)EFLAGS压入堆栈;
    (9)CS压入堆栈;
    (10)EIP压入堆栈;
    (11)使TF=0,NT=0;
    (12)若为中断门,则使IF=0;
    (13)若有出错码,则把出错码压入堆栈;
    (14)转入处理程序。
    由上述转移过程可见,中断门中指示处理程序的选择子必须指向描述一个可执行的代码段的描述符。如果选择子为空,就引起通用保护故障,出错码是0。如果描述符不是代码段描述符,就引起通用保护故障,出错码含选择子。
    中断或异常可以转移到同一特权级或内层特权级。上述指定处理程序代码段的描述符中的类型及DPL字段,决定了这种同一任务内的转移是否要发生特权级变换。如果是一个非一致代码段,并且DPL<CPL则产生通用保护异常。
    上述转移过程中的第六步,也就是“把描述符装入CS”,是指把上述指定处理 程序段的描述符装入CS的高速缓冲寄存器中,在这一步骤中要对描述符进行类似通过调用门进行转移的其它检查,包括是否代码段描述符和代码段描述符是否存在 等,因此可能再发生异常。在对该描述符进行检查时,通过调整门中选择子的RPL=0(在处理器内部调整,而不影响存储器中的选择子的RPL字段)的方法, 实现只考虑代码段的DPL,而不考虑门中选择子的RPL。把描述符装入CS之后,还要检查门描述符中给出的表示处理程序代码段入口的偏移是否越界,即是否 超出段界限。如果越界,就引起出错码为0的通用保护故障。
    把TF置成0,表示不允许处理程序单步执行。把NT置成0,表示处理程序在利用中断返回指令IRET返回时,返回到同一任务而不是一个嵌套任务。
    通过中断门的转移和通过陷阱门的转移之间的差别只是对IF标志的处理。对于中断门,在转移过程中把IF置为0,使得在处理程序执行期间屏蔽掉INTR中断。

4.中断或异常处理后的返回
     中断返回指令IRET用于从中断处理程序的返回。该指令的执行根据任务嵌套标志NT位是否为1分为两种情形。 该过程由硬件自动进行,简单了解下。
    NT位为1,表示是嵌套任务的返回。
    NT位为0,表示当前任务内的返回。

后面跟上我们的代码:
.386
.model flat, stdcall
option casemap:none

include myIntGate.inc
.const
CCOUNTED_UNICODE_STRING "\\Device\\MyIntGate",g_usDeviceName,4
CCOUNTED_UNICODE_STRING "\\??\\MyIntGate",g_usSymbolicLinkName,4
IDT_LIMIT = 256 * 8
GATE_TYPE = 0eeh

.data?
g_myInt dd ?
g_myIntoffset dd ?
g_IsAddMyGate dd ?

.code
;中断执行函数
MyIntGateFunction proc
   invoke DbgPrint,$CTA0("MyIntGate calling...\n")
   iretd
MyIntGateFunction endp

;在IDT中添加中断门,返回中断号
AddMyIntGate proc FuncAddr:DWORD
    push ebx
        push ecx
        sidt [esp-2]
        pop ecx
       
        mov eax,0
        .while eax < IDT_LIMIT
             lea edx,[ecx+eax]
             assume edx:ptr GATE
             test [edx].GTYPE,80h
            .if ZERO?
               cli
               mov ebx,FuncAddr
               mov [edx].OFFSETL,bx
               mov [edx].SELECTOR,08h
               mov [edx].DCOUNT,0
               mov [edx].GTYPE,GATE_TYPE
               shr ebx,16
               mov [edx].OFFSETH,bx   
               sti  
          .break
           .endif   
            assume edx:nothing
           add eax,8
        .endw
        pop ebx
        mov g_myInt,eax
        ret
AddMyIntGate endp

;去掉添加的中断门
RemoveMyIntGate proc
        push ebx
        push ecx
        sidt [esp-2]
        pop ecx
        mov eax,g_myIntoffset
        lea edx,[ecx+eax]
        assume edx:ptr GATE
        cli
        mov [edx].OFFSETL,0
        mov [edx].SELECTOR,08h
        mov [edx].DCOUNT,0
        mov [edx].GTYPE,0
        mov [edx].OFFSETH,0
        sti
        assume edx:nothing
        pop ebx
        ret

RemoveMyIntGate endp

DispatchControl proc uses esi edi pDeviceObject:PDEVICE_OBJECT,pIrp:PIRP
        mov esi,pIrp
        assume esi:ptr _IRP
        mov [esi].IoStatus.Status,STATUS_UNSUCCESSFUL
        and [esi].IoStatus.Information,0
       
        IoGetCurrentIrpStackLocation esi
        mov edi,eax
        assume edi:ptr IO_STACK_LOCATION
        .if [edi].Parameters.DeviceIoControl.IoControlCode == IOCTL_MYINTGATE && !g_IsAddMyGate
                invoke AddMyIntGate,offset MyIntGateFunction
                mov g_myIntoffset,eax
                xor edx,edx
                cdq
                mov ecx,8
                div ecx
                mov g_myInt,eax
                mov edx,[esi].AssociatedIrp.SystemBuffer
                mov [edx],eax
               
                mov [esi].IoStatus.Status, STATUS_SUCCESS
                mov [esi].IoStatus.Information,4
                mov g_IsAddMyGate,1
        .else
                mov [esi].IoStatus.Status,STATUS_INVALID_DEVICE_REQUEST
        .endif
       
        push [esi].IoStatus.Status
        assume edi:nothing
        assume esi:nothing
        invoke IoCompleteRequest,esi,IO_NO_INCREMENT
        pop eax
       
        ret

DispatchControl endp
DispatchCreateClose proc pDeviceObject:PDEVICE_OBJECT,pIrp:PIRP
   mov eax,pIrp
   assume eax:ptr _IRP
   mov [eax].IoStatus.Status,STATUS_SUCCESS
   and [eax].IoStatus.Information,0
   fastcall IofCompleteRequest,pIrp,IO_NO_INCREMENT
   assume eax:nothing
   mov eax,STATUS_SUCCESS
   ret
DispatchCreateClose endp
DriverUnload proc pDriverObject:PDRIVER_OBJECT
    .if g_IsAddMyGate
           invoke RemoveMyIntGate
        .endif
        invoke IoDeleteSymbolicLink,addr g_usSymbolicLinkName
        mov eax,pDriverObject
        invoke IoDeleteDevice,(DRIVER_OBJECT PTR[eax]).DeviceObject
        ret

DriverUnload endp

DriverEntry proc pDriverObject:PDRIVER_OBJECT,pusRegistryPath:PUNICODE_STRING
    LOCAL pDeviceObject:PDEVICE_OBJECT
    LOCAL status:NTSTATUS
   
    mov status,STATUS_DEVICE_CONFIGURATION_ERROR
    invoke IoCreateDevice,pDriverObject,0,addr g_usDeviceName,FILE_DEVICE_UNKNOWN,0,FALSE,addr pDeviceObject
    .if eax == STATUS_SUCCESS
        invoke IoCreateSymbolicLink,addr g_usSymbolicLinkName,addr g_usDeviceName
        .if eax == STATUS_SUCCESS
            mov eax,pDriverObject
            assume eax:ptr DRIVER_OBJECT
            mov [eax].MajorFunction[IRP_MJ_CREATE * (sizeof PVOID)],offset DispatchCreateClose
            mov [eax].MajorFunction[IRP_MJ_CLOSE * (sizeof PVOID)],offset DispatchCreateClose
            mov [eax].MajorFunction[IRP_MJ_DEVICE_CONTROL *(sizeof PVOID)],offset DispatchControl
            mov [eax].DriverUnload,offset DriverUnload
            assume eax:nothing
            mov g_IsAddMyGate,0
            mov status,STATUS_SUCCESS
        .else
                invoke IoDeleteDevice,pDeviceObject
        .endif
    .endif
    mov eax,status
    ret

DriverEntry endp

end DriverEntry

最后友情提示:
在我的本机返回添加的中断向量是0x20, 调用方法是:
         __asm int 0x20;
               
当然,如果你有参数传递的话,可以使用寄存器进行传递,在MyIntGateFunction中断响应函数中可以得到这个参数。例如:
        _asm
        {
              mov eax, 100
              int 0x20
        }

[推荐]看雪企服平台,提供项目众包、渗透测试、安全分析、定制项目开发、APP等级保护等安全服务!

上传的附件:
最新回复 (22)
小子贼野 10 2008-4-1 16:58
2
0
沙发

收藏了

基本看不懂
lOOp 2008-4-1 18:26
3
0
沙发让小子给抢了。。。。
继续支持楼主大虾
小子贼野 10 2008-4-1 18:41
4
0
希望楼主能赶快把所有题目都写完,然后我们就有CHM收藏+学习了
zhuwg 11 2008-4-1 18:52
5
0
跟着膜拜+学习
sislcb 7 2008-4-2 11:27
6
0
支持啊,支持
dem 2008-4-2 19:23
7
0
呵呵,全然不懂,还是要支持下论坛里的这些大牛们,没有你们的无私,我们无法成长
orichi 2008-4-24 10:55
8
0
在pediy中学到了不少
orichi 2008-4-24 11:50
9
0
其实我想知道老大们你们的这些资料是从哪里获得,能给我网址吗!
combojiang 26 2008-4-24 12:10
10
0
呵呵,到intel的网站上下载intel cpu相关资料。
zmmkele 2008-8-12 11:07
11
0
对着Undocument..... 看
jadesoft 1 2008-8-14 14:21
12
0
牛X,请问我用这个技术能做什么
linhanshi 2008-9-7 11:23
13
0
support.
凯文米特 2008-9-18 21:43
14
0
学到了不少
better 2 2008-12-5 22:48
15
0
例子中MyIntGateFunction 写的有问题,改成
MyIntGateFunction proc
   assume  fs:nothing
   push fs
   mov r_fs,fs
   pushad
   cli
   mov ebx,30h
   mov fs,bx        ;调用内核函数前一定要设置fs,害我不知吃了多少苦!!
   invoke DbgPrint,$CTA0("MyIntGate calling...\nBefore Call DbgPrint FS=%08X"),r_fs
   sti
   popad
   mov r_fs,0
   pop fs
   iretd
MyIntGateFunction endp
否则返回ring3时FS寄存器是错误的指向(为0),下面的函数没法执行了……
twoseconds 2008-12-13 16:59
16
0
顶一下,但是看不懂!!收藏。。。
安摧 2 2009-2-5 14:17
17
0
例子中的cli和sti的使用有问题!

本来目的是用来实现互斥的,但是实际上还是没有实现。
安摧 2 2009-2-5 14:29
18
0
我的电脑运行没有问题。

帮你顶一个,谁能解释一下,
recnad 2009-4-6 13:45
19
0
最近正研究门, mark, 感谢楼主!
panderfly 2010-9-25 00:54
20
0
学习了
wenboly 2010-11-16 23:52
21
0
我想知道,这个门类型0xEE是不是:8E+60得来的?
以及调用门中的0xEC是不是:8C+60??
Speeday 8 2013-8-26 13:02
22
0
楼主有很多好文章 ,学习。。
hhxlovety 2014-3-17 15:58
23
0
哥 你能告诉我怎么编译嘛?
游客
登录 | 注册 方可回帖
返回