IoConnectInterrupt . 1

IoConnectInterrupt的内部实现, 看他是如何把ServiceRoutine( ServiceContext ) 设置成IDT中断处理函数的

先看看_KINTERRUPT的结构
kd> dt _KINTERRUPT 817687d8
nt!_KINTERRUPT
+0x000 Type             : 22
+0x002 Size             : 484
+0x004 InterruptListEntry : _LIST_ENTRY 
+0x00c ServiceRoutine   : 0xf980467e             // 偏移 +0x0c = ServiceRoutine
+0x010 ServiceContext   : 0x817c8030             // 偏移 +0x10 = ServiceContext
+0x014 SpinLock         : 0
+0x018 TickCount        : 0xffffffff
+0x01c ActualLock       : 0x81768a3c -> 0
+0x020 DispatchAddress : 0x80546660    
+0x024 Vector           : 0x162                   // 偏移 +0x24 = Vector     
+0x028 Irql             : 0x5 ''
+0x029 SynchronizeIrql : 0x5 ''                   
+0x02a FloatingSave     : 0 ''
+0x02b Connected        : 0x1 ''
+0x02c Number           : 0 ''
+0x02d ShareVector      : 0 ''
+0x030 Mode             : 1 ( Latched ) 中断模式
+0x034 ServiceCount     : 0
+0x038 DispatchCount    : 0xffffffff
+0x03c DispatchCode     : [106] 0x56535554

系统是通过一个模板化的中断处理函数,在其内部去调用这个ServiceRoutine来完成中断处理的,模板结构如下

        public  _KiInterruptTemplate
_KiInterruptTemplate    label   byte

        public  _KiInterruptTemplate2ndDispatch
_KiInterruptTemplate2ndDispatch equ     this dword
        mov      edi,0                                     // 不同的中断, 内部修改成不同的 mov edi,PKINTERRUPT

        public  _KiInterruptTemplateObject
_KiInterruptTemplateObject      equ     this dword

        jmp _KeSynchronizeExecution                        // 这里会被改为, jmp KiInterruptDispatch

        public  _KiInterruptTemplateDispatch
_KiInterruptTemplateDispatch    equ     this dword

//
// 下面是 KiInterruptDispatch 函数, 对照上面的`_KINTERRUPT`结构, 看看偏移都是什么值
//
      int __stdcall KiInterruptDispatch()
.text:004048CA _KiInterruptDispatch@0 proc near        ; DATA XREF: KiGetVectorInfo(x,x)+45o

.text:004048CA                 inc     large dword ptr fs:5C4h
.text:004048D1                 mov     ebp, esp
.text:004048D3                 mov     eax, [edi+24h]            // 偏移 +0x24 = Vector
.text:004048D6                 mov     ecx, [edi+29h]            // 偏移 +0x29 = SynchronizeIrql
.text:004048D9                 push    eax
.text:004048DA                 sub     esp, 4
.text:004048DD                 push    esp
.text:004048DE                 push    eax
.text:004048DF                 push    ecx
.text:004048E0                 call    ds:__imp__HalBeginSystemInterrupt@12 ; HalBeginSystemInterrupt(x,x,x)
.text:004048E6                 or      eax, eax
.text:004048E8                 jz      short loc_40492A
.text:004048EA                 sub     esp, 0Ch
.text:004048ED                 cmp     _PPerfGlobalGroupMask, 0
.text:004048F4                 mov     [ebp+var_C], 0
.text:004048FB                 jnz     short loc_40493E
.text:004048FD
.text:004048FD loc_4048FD:                             ; CODE XREF: KiInterruptDispatch()+6Ej
.text:004048FD                                         ; KiInterruptDispatch()+7Dj ...
.text:004048FD                 mov     esi, [edi+1Ch]
.text:00404900                 lock bts dword ptr [esi], 0
.text:00404905                 jb      short loc_404932
.text:00404907                 mov     eax, [edi+10h]                 // 偏移 +0x10 = ServiceContext
.text:0040490A                 push    eax
.text:0040490B                 push    edi
.text:0040490C                 call    dword ptr [edi+0Ch]            // 偏移 +0x0c = ServiceRoutine, 真正的中断处理函数
...

由此我们得知中断处理函数为这个样子

mov edi,PKINTERRUPT
jmp KiInterruptDispatch

函数IoConnectInterrupt内部调用的两个主要函数为KeInitializeInterruptKeConnectInterrupt, 首先在KeInitializeInterrupt内部

    NormalDispatchCode = &(Interrupt->DispatchCode[0]);

    pl = NormalDispatchCode;

    for (Index = 0; Index < NORMAL_DISPATCH_LENGTH; Index += 1) {
        *NormalDispatchCode++ = KiInterruptTemplate[Index];                     // 把上面提到的模板原样拷贝到 Interrupt->DispatchCode
    }

    //
    // The following two instructions set the address of current interrupt
    // object the the NORMAL dispatching code.
    //

    pl = (PULONG)((PUCHAR)pl + ((PUCHAR)&KiInterruptTemplateObject -            // KiInterruptTemplateObject 和 KiInterruptTemplate 中间就是
                                (PUCHAR)KiInterruptTemplate) -4);               // mov edi, 0 这条指令, 再 -4,指向的就是edi后面的这个值,
                                                                                // mov edi, 0 的汇编指令为 “BF 00 00 00 00”

    *pl = (ULONG)Interrupt;                                                     // 赋值后变为了 mov edi, PKINTERRUPT
      
    ...

函数KeConnectInterrupt的内部,通过调用KiConnectVectorAndInterruptObject来完成

    ...

    KiGetVectorInfo (                          // 内部得到 InterruptDispatch函数地址
        Interrupt->Vector,                     // DispatchInfo->InterruptDispatch = KiInterruptDispatch;
        &DispatchInfo                          // DispatchInfo->FloatingDispatch  = KiFloatingDispatch;
        );                                     // DispatchInfo->ChainedDispatch   = KiChainedDispatch;
                                               // DispatchInfo->NoDispatch        = (PKINTERRUPT_ROUTINE) (((ULONG) &KiStartUnexpectedRange) +
                                               //                                   (IDTEntry - PRIMARY_VECTOR_BASE) * KiUnexpectedEntrySize);
            
    // 根据不同的Type,最后选择不同的 DispatchAddress             
    if (Type == NoConnect) {
        DispatchAddress = DispatchInfo.NoDispatch;
    } else {
        DispatchAddress = DispatchInfo.ChainedDispatch;
        if (Type == NormalConnect) {
            DispatchAddress = DispatchInfo.InterruptDispatch;       
            if (Interrupt->FloatingSave) {
                DispatchAddress = DispatchInfo.FloatingDispatch;
            }
        }

        Interrupt->DispatchAddress = DispatchAddress;

        //
        // Set interrupt objects dispatch code to kernel dispatcher
        //

        pl = &(Interrupt->DispatchCode[0]);                               //
        pl = (PULONG)((PUCHAR)pl +                                        // KiInterruptTemplateDispatch 和 KiInterruptTemplate 中间就是 
                    ((PUCHAR)&KiInterruptTemplateDispatch -               // mov edi,0 和 jmp _KeSynchronizeExecution 两条指令
                     (PUCHAR)KiInterruptTemplate) -4);                    // 和处理mov edi,0时一样,    jmp 的汇编指令为 E9 00 00 00 00                                                                    
                                                                            
                                                                          // 目的地址         = (jmp的下面一条指令地址)  + (地址偏移)
                                                                          // DispatchAddress  =  ( p1 + 4 )     +  (地址偏移)
                                                                          // 我们只要修改成 jmp (地址偏移) ,就能够跳到目的地址
        *pl = (ULONG)DispatchAddress-(ULONG)((PUCHAR)pl+4);               // 给(地址偏移)赋值, 这样就完成了 jmp KiInterruptDispatch
        
        ...
        
        // 把模板都修改完成了,下面把地址设置到IDT里
        //
        DispatchAddress = (PKINTERRUPT_ROUTINE) &Interrupt->DispatchCode; // 指向我们修改好的模板了

        KiSetHandlerAddressToIDT (Interrupt->Vector, DispatchAddress);    // #define KiSetHandlerAddressToIDT(Vector, HandlerAddress) {\
                                                                          //      UCHAR IDTEntry = HalVectorToIDTEntry(Vector); \
                                                                          //      ULONG Ha = (ULONG)HandlerAddress; \
                                                                          //      KeGetPcr()->IDT[IDTEntry].ExtendedOffset = HIGHWORD(Ha); \
                                                                          //      KeGetPcr()->IDT[IDTEntry].Offset = LOWWORD(Ha); \

      ...                                                                

花1个小时看代码,最少也得再花1个小时才能把他们写出来,越来越佩服那些文章内容写的好,而且排版还漂亮的人了。

...

posted @ 2020-11-06 14:28  一条小鳄鱼  阅读(307)  评论(0)    收藏  举报