zoukankan      html  css  js  c++  java
  • IDT HOOK思路整理

    IDT(中断描述符表)分为IRQ(真正的硬件中断)和软件中断(又叫异常)。

    HOOK的思路为,替换键盘中断处理的函数地址为自己的函数地址。这样在键盘驱动和过滤驱动之前就可以截获键盘输入。

    思路确定之后,可以写代码了

    首先获取到IDT,这个需要使用汇编指令sidt来获取,这个指令读取了IDTR寄存器的内容,返回结构的格式为:

    typedef struct P2C_IDTR_ {
        P2C_U16 limit;        // 范围
        P2C_U32 base;        // 基地址(就是开始地址)
    } P2C_IDTR, *PP2C_IDTR;

    获取的方法为:

    void *p2cGetIdt()
    {
        P2C_IDTR idtr;
        // 一句汇编读取到IDT的位置。
        _asm sidt idtr
        return (void *)idtr.base;
    }

    注:IDT中断描述符表中最多有256个中断或异常向量

    在保护模式下,中断描述符表的表项由8个字节组成,结构如下

    typedef struct P2C_IDT_ENTRY_ {
            P2C_U16 offset_low;
            P2C_U16 selector;
            P2C_U8 reserved;
            P2C_U8 type:4;
            P2C_U8 always0:1;
            P2C_U8 dpl:2;
            P2C_U8 present:1;
            P2C_U16 offset_high;
    } P2C_IDTENTRY, *PP2C_IDTENTRY;

    上述结构中offset_low和offset_high构成一个32位的虚拟地址,代表中断处理跳转地址,windows下PS/2键盘的中断号一般是0x93,而中断号代表了某项在中断表中的索引,我们的目标是将该处地址替换成我们的函数地址,达到HOOK的目的。操作如下

        PP2C_IDTENTRY idt_item=(PP2C_IDTENTRY)p2cGetIdt();
        //将指针指向PS/2中断项
        idt_item += nIndex;

    定义一个裸函数

    __declspec(naked) p2cInterruptProc()
    {
        __asm
        {
            pushad                    // 保存所有的通用寄存器
            pushfd                    // 保存标志寄存器
            call p2cUserFilter    // 调一个我们自己的函数。 这个函数将实现
                                        // 一些我们自己的功能
            popfd                    // 恢复标志寄存器
            popad                    // 恢复通用寄存器
            jmp    g_old_addr        // 跳到原来的中断服务程序
        }
    }

    该函数的作用只是跳到自定义的函数,执行完之后,跳回之前的地址继续执行。

    准备工作完成之后,就可以进行HOOK了

    VOID HOOK_IDT(ULONG nIndex,BOOLEAN b)
    {
        PP2C_IDTENTRY idt_item=(PP2C_IDTENTRY)p2cGetIdt();
        //将指针指向PS/2中断项
        
        idt_item += nIndex;
        
        if(b)
        {
            //保存原来的地址
            g_old_addr = (void *)P2C_MAKELONG(idt_item->offset_low,idt_item->offset_high);
            //替换成自己的函数
            idt_item->offset_low = P2C_LOW16_OF_32(p2cInterruptProc);
            idt_item->offset_high = P2C_HIGH16_OF_32(p2cInterruptProc);
            DbgPrint("源地址为%x 替换后的地址%x
    ",g_old_addr,p2cInterruptProc);
            
        }
        else
        {
            idt_item->offset_low = P2C_LOW16_OF_32(g_old_addr);
            idt_item->offset_high = P2C_HIGH16_OF_32(g_old_addr);
            DbgPrint("替换为原来的地址");
        }
    }
    nIndex=0x93
    完整代码如下:
      1 #include <ntddk.h>
      2 
      3 // 这一句存在,则本程序编译为替换INT0x93的做法。如果
      4 // 不存在,则为IOAPIC重定位做法。
      5 // #define BUILD_FOR_IDT_HOOK
      6 
      7 // 由于这里我们必须明确一个域是多少位,所以我们预先定义几个明
      8 // 确知道多少位长度的变量,以避免不同环境下编译的麻烦.
      9 typedef unsigned char P2C_U8;
     10 typedef unsigned short P2C_U16;
     11 typedef unsigned long P2C_U32;
     12 
     13 #define P2C_MAKELONG(low, high) 
     14 ((P2C_U32)(((P2C_U16)((P2C_U32)(low) & 0xffff)) | ((P2C_U32)((P2C_U16)((P2C_U32)(high) & 0xffff))) << 16))
     15 
     16 #define P2C_LOW16_OF_32(data) 
     17 ((P2C_U16)(((P2C_U32)data) & 0xffff))
     18 
     19 #define P2C_HIGH16_OF_32(data) 
     20 ((P2C_U16)(((P2C_U32)data) >> 16))
     21 
     22 // 从sidt指令获得一个如下的结构。从这里可以得到IDT的开始地址
     23 #pragma pack(push,1)
     24 typedef struct P2C_IDTR_ {
     25     P2C_U16 limit;        // 范围
     26     P2C_U32 base;        // 基地址(就是开始地址)
     27 } P2C_IDTR, *PP2C_IDTR;
     28 #pragma pack(pop)
     29 
     30 // 下面这个函数用sidt指令读出一个P2C_IDTR结构,并返回IDT的地址。
     31 void *p2cGetIdt()
     32 {
     33     P2C_IDTR idtr;
     34     // 一句汇编读取到IDT的位置。
     35     _asm sidt idtr
     36     return (void *)idtr.base;
     37 }
     38 
     39 #pragma pack(push,1)
     40 typedef struct P2C_IDT_ENTRY_ {
     41         P2C_U16 offset_low;
     42         P2C_U16 selector;
     43         P2C_U8 reserved;
     44         P2C_U8 type:4;
     45         P2C_U8 always0:1;
     46         P2C_U8 dpl:2;
     47         P2C_U8 present:1;
     48         P2C_U16 offset_high;
     49 } P2C_IDTENTRY, *PP2C_IDTENTRY;
     50 #pragma pack(pop)
     51 
     52 
     53 VOID *g_old_addr=NULL;
     54 // 首先读端口获得按键扫描码打印出来。然后将这个扫
     55 // 描码写回端口,以便别的应用程序能正确接收到按键。
     56 // 如果不想让别的程序截获按键,可以写回一个任意的
     57 // 数据。
     58 #define OBUFFER_FULL 0x02
     59 #define IBUFFER_FULL 0x01
     60 
     61 ULONG p2cWaitForKbRead()
     62 {
     63     int i = 100;
     64     P2C_U8 mychar;    
     65     do
     66     {
     67         _asm in al,0x64
     68         _asm mov mychar,al
     69         KeStallExecutionProcessor(50);
     70         if(!(mychar & OBUFFER_FULL)) break;
     71     } while (i--);
     72     if(i) return TRUE;
     73     return FALSE;
     74 }
     75 
     76 ULONG p2cWaitForKbWrite()
     77 {
     78     int i = 100;
     79     P2C_U8 mychar;
     80     do
     81     {
     82         _asm in al,0x64
     83         _asm mov mychar,al
     84         KeStallExecutionProcessor(50);
     85         if(!(mychar & IBUFFER_FULL)) break;
     86     } while (i--);
     87     if(i) return TRUE;
     88     return FALSE;
     89 }
     90 
     91 void p2cUserFilter()
     92 {
     93     
     94     static P2C_U8 sch_pre = 0;
     95     P2C_U8    sch;
     96     DbgPrint("p2cUserFilter
    ");
     97     p2cWaitForKbRead();
     98     _asm in al,0x60
     99     _asm mov sch,al
    100     KdPrint(("p2c: scan code = %2x
    ",sch));
    101    //  把数据写回端口,以便让别的程序可以正确读取。
    102     if(sch_pre != sch)
    103     {
    104         sch_pre = sch;
    105         _asm mov al,0xd2
    106         _asm out 0x64,al
    107         p2cWaitForKbWrite();
    108         _asm mov al,sch
    109         _asm out 0x60,al
    110     }
    111 }
    112 
    113 __declspec(naked) p2cInterruptProc()
    114 {
    115     __asm
    116     {
    117         pushad                    // 保存所有的通用寄存器
    118         pushfd                    // 保存标志寄存器
    119         call p2cUserFilter    // 调一个我们自己的函数。 这个函数将实现
    120                                     // 一些我们自己的功能
    121         popfd                    // 恢复标志寄存器
    122         popad                    // 恢复通用寄存器
    123         jmp    g_old_addr        // 跳到原来的中断服务程序
    124     }
    125 }
    126 
    127 VOID HOOK_IDT(ULONG nIndex,BOOLEAN b)
    128 {
    129     PP2C_IDTENTRY idt_item=(PP2C_IDTENTRY)p2cGetIdt();
    130     //将指针指向PS/2中断项
    131     
    132     idt_item += nIndex;
    133     
    134     if(b)
    135     {
    136         //保存原来的地址
    137         g_old_addr = (void *)P2C_MAKELONG(idt_item->offset_low,idt_item->offset_high);
    138         //替换成自己的函数
    139         idt_item->offset_low = P2C_LOW16_OF_32(p2cInterruptProc);
    140         idt_item->offset_high = P2C_HIGH16_OF_32(p2cInterruptProc);
    141         DbgPrint("源地址为%x 替换后的地址%x
    ",g_old_addr,p2cInterruptProc);
    142         
    143     }
    144     else
    145     {
    146         idt_item->offset_low = P2C_LOW16_OF_32(g_old_addr);
    147         idt_item->offset_high = P2C_HIGH16_OF_32(g_old_addr);
    148         DbgPrint("替换为原来的地址");
    149     }
    150 }
    151 #define  DELAY_ONE_MICROSECOND  (-10)
    152 #define  DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
    153 #define  DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)
    154 //驱动卸载函数
    155 VOID  IDT_Unload(IN PDRIVER_OBJECT DriverObject) 
    156 {
    157     LARGE_INTEGER interval;
    158     HOOK_IDT(0x93,FALSE);
    159     KdPrint (("p2c: unloading
    ")); 
    160     // 睡眠5秒。等待所有irp处理结束
    161     interval.QuadPart = (5*1000 * DELAY_ONE_MILLISECOND);        
    162     KeDelayExecutionThread(KernelMode,FALSE,&interval);
    163 }
    164 //驱动程序入口
    165 NTSTATUS DriverEntry( 
    166                      IN PDRIVER_OBJECT DriverObject, 
    167                      IN PUNICODE_STRING RegistryPath 
    168                      ) 
    169 {
    170 
    171     
    172     // 卸载函数。
    173     
    174     
    175     HOOK_IDT(0x93,TRUE);
    176     DriverObject->DriverUnload = IDT_Unload;
    177     return STATUS_SUCCESS;
    178 }
     
  • 相关阅读:
    Iframe和Frame中实现cookie跨域的方法(转载)
    android中拷贝assets下的资源文件到SD卡中(可以超过1M)
    OpenSL ES 查询设备支持的SL Profiles
    NDK开发中的一个HTTP下载实例附带下载进度
    android中配置文件property的用途以及使用<转>
    Eclipse 工程使用相对路径导入Jar包设置
    Android 解压zip文件(支持中文)
    c++实现一个比较两个string类型的版本号的小demo
    linux c++下载http文件并显示进度<转>
    Linux下类似windows下_beginthread和_endthread 的多线程开发
  • 原文地址:https://www.cnblogs.com/zwt1234/p/4463614.html
Copyright © 2011-2022 走看看