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:;
P2C_U8 always0:;
P2C_U8 dpl:;
P2C_U8 present:;
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\n",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
完整代码如下:
 #include <ntddk.h>

 // 这一句存在,则本程序编译为替换INT0x93的做法。如果
// 不存在,则为IOAPIC重定位做法。
// #define BUILD_FOR_IDT_HOOK // 由于这里我们必须明确一个域是多少位,所以我们预先定义几个明
// 确知道多少位长度的变量,以避免不同环境下编译的麻烦.
typedef unsigned char P2C_U8;
typedef unsigned short P2C_U16;
typedef unsigned long P2C_U32; #define P2C_MAKELONG(low, high) \
((P2C_U32)(((P2C_U16)((P2C_U32)(low) & 0xffff)) | ((P2C_U32)((P2C_U16)((P2C_U32)(high) & 0xffff))) << )) #define P2C_LOW16_OF_32(data) \
((P2C_U16)(((P2C_U32)data) & 0xffff)) #define P2C_HIGH16_OF_32(data) \
((P2C_U16)(((P2C_U32)data) >> )) // 从sidt指令获得一个如下的结构。从这里可以得到IDT的开始地址
#pragma pack(push,1)
typedef struct P2C_IDTR_ {
P2C_U16 limit; // 范围
P2C_U32 base; // 基地址(就是开始地址)
} P2C_IDTR, *PP2C_IDTR;
#pragma pack(pop) // 下面这个函数用sidt指令读出一个P2C_IDTR结构,并返回IDT的地址。
void *p2cGetIdt()
{
P2C_IDTR idtr;
// 一句汇编读取到IDT的位置。
_asm sidt idtr
return (void *)idtr.base;
} #pragma pack(push,1)
typedef struct P2C_IDT_ENTRY_ {
P2C_U16 offset_low;
P2C_U16 selector;
P2C_U8 reserved;
P2C_U8 type:;
P2C_U8 always0:;
P2C_U8 dpl:;
P2C_U8 present:;
P2C_U16 offset_high;
} P2C_IDTENTRY, *PP2C_IDTENTRY;
#pragma pack(pop) VOID *g_old_addr=NULL;
// 首先读端口获得按键扫描码打印出来。然后将这个扫
// 描码写回端口,以便别的应用程序能正确接收到按键。
// 如果不想让别的程序截获按键,可以写回一个任意的
// 数据。
#define OBUFFER_FULL 0x02
#define IBUFFER_FULL 0x01 ULONG p2cWaitForKbRead()
{
int i = ;
P2C_U8 mychar;
do
{
_asm in al,0x64
_asm mov mychar,al
KeStallExecutionProcessor();
if(!(mychar & OBUFFER_FULL)) break;
} while (i--);
if(i) return TRUE;
return FALSE;
} ULONG p2cWaitForKbWrite()
{
int i = ;
P2C_U8 mychar;
do
{
_asm in al,0x64
_asm mov mychar,al
KeStallExecutionProcessor();
if(!(mychar & IBUFFER_FULL)) break;
} while (i--);
if(i) return TRUE;
return FALSE;
} void p2cUserFilter()
{ static P2C_U8 sch_pre = ;
P2C_U8 sch;
DbgPrint("p2cUserFilter\n");
p2cWaitForKbRead();
_asm in al,0x60
_asm mov sch,al
KdPrint(("p2c: scan code = %2x\r\n",sch));
// 把数据写回端口,以便让别的程序可以正确读取。
if(sch_pre != sch)
{
sch_pre = sch;
_asm mov al,0xd2
_asm out 0x64,al
p2cWaitForKbWrite();
_asm mov al,sch
_asm out 0x60,al
}
} __declspec(naked) p2cInterruptProc()
{
__asm
{
pushad // 保存所有的通用寄存器
pushfd // 保存标志寄存器
call p2cUserFilter // 调一个我们自己的函数。 这个函数将实现
// 一些我们自己的功能
popfd // 恢复标志寄存器
popad // 恢复通用寄存器
jmp g_old_addr // 跳到原来的中断服务程序
}
} 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\n",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("替换为原来的地址");
}
}
#define DELAY_ONE_MICROSECOND (-10)
#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
#define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)
//驱动卸载函数
VOID IDT_Unload(IN PDRIVER_OBJECT DriverObject)
{
LARGE_INTEGER interval;
HOOK_IDT(0x93,FALSE);
KdPrint (("p2c: unloading\n"));
// 睡眠5秒。等待所有irp处理结束
interval.QuadPart = (* * DELAY_ONE_MILLISECOND);
KeDelayExecutionThread(KernelMode,FALSE,&interval);
}
//驱动程序入口
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{ // 卸载函数。 HOOK_IDT(0x93,TRUE);
DriverObject->DriverUnload = IDT_Unload;
return STATUS_SUCCESS;
}
 

IDT HOOK思路整理的更多相关文章

  1. Windows xp下IDT Hook和GDT的学习

    一.前言   对于IDT第一次的认知是int 2e ,在系统调用的时候原来R3进入R0的方式就是通过int 2e自陷进入内核,然后进入KiSystemService函数,在根据系统服务调用号调用系统服 ...

  2. Angular2发布思路(整理官网Deployment页面)

    本文是按着ng2官网的高级内容“Deployment”的思路整理得出的,原文虽然在angular2的中文站下挂着,截止目前却还是英文版未翻译,笔者就在这里结合自己的理解给出原文的一点点整理.这是原文地 ...

  3. GTP+SDI工程播出部分思路整理(3)

    GTP+SDI工程播出部分思路整理(3) 1.本文的目的主要分析video_out_to_sdi模块中输入信号 tx_usrclk, rst, tx_mode, tx_level_b的使用 Tx_us ...

  4. GTP+SDI工程播出部分思路整理(2)

    GTP+SDI工程播出部分思路整理(2) 以同样的方法来分析tx_video_a_c_in信号: SDI核中tx_video_a_c_in信号连接情况如下所示 .tx_video_a_c_in     ...

  5. GTP+SDI工程播出部分思路整理

    GTP+SDI工程播出部分思路整理 1.video_out_to_sdi模块 关于video_out_to_sdi模块的输出信号: tx_video_a_y[9:0] 这是要输入SDI IP核内的 t ...

  6. 一些JavaSE学习过程中的思路整理(主观性强,持续更新中...)

    目录 一些JavaSE学习过程中的思路整理(主观性强,持续更新中...) Java书写规范 IDEA的一些常用快捷键 Java类中作为成员变量的类 Java源文件中只能有一个public类 Java中 ...

  7. iOS 工程自动化 - 思路整理

    4 月份参加 2017@Swift 大会的时候有幸听到了 @zesming 大佬关于美团组件化的 Topic,有一张图印象特别深刻. 来自 @zesming 大佬 后来跟 @zesming 大佬沟通怎 ...

  8. iOS内置图片瘦身思路整理

    一.前言 前段时间注意到我们APP的包大小超过100MB了,所以随口跟老板说了下能否采用字体文件(.ttf)替代PNG图片,老板对应用瘦身很感兴趣因此让我做下技术调研.这篇文章主要是将我们的各个技术方 ...

  9. 解决Android加固多进程ptrace反调试的思路整理

    本文博客链接:http://blog.csdn.net/qq1084283172/article/details/53613481 一.Android多进程反调试的原理代码 当ptrace附加目标进程 ...

随机推荐

  1. 数据库SQL语句优化小结

    网上查找的总结: 1.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如: select id from t where num is null ...

  2. spring加载过程,源码带你理解从初始化到bean注入

    spring在容器启动时,容器正式初始化入口refresh()如下图 ①包括初始化FactoryBean.解析XML注册所有BeanDefinition信息  ②包括注册scope管理类  ③初始化单 ...

  3. 同一行多个div宽度自适应布局

    主要运用到的是:布局神器display:table-cell 元素两端对齐 第一个案例是让两个元素分别向左和向右对齐,如果是过去,我一定会用float来实现,但其实用table可以这么做: 自动平均划 ...

  4. javase--反射

    //书写规则 package cn.reflex; public interface PCI { public void open(); public void close(); } //调用方法 p ...

  5. postgresql pgsql最新版安装指南及数据存储路径更改及主从配置

    postgresql pgsql最新版安装指南及数据存储路径更改及主从配置 安装指南 首先在apt的list添加你当前系统版本对应的apt列表 目前官网有16.04,14.04,12.04 分别对应下 ...

  6. 基于Codeigniter框架实现的APNS批量推送—叮咚,查水表

    最近兼职公司已经众筹成功的无线门铃的消息推送出现了问题,导致有些用户接收不到推送的消息,真是吓死宝宝了,毕竟自己一手包办的后台服务,影响公司信誉是多么的尴尬,容我简单介绍一下我们的需求:公司开发的是一 ...

  7. Dw CS 破解

    据说,CS5的破解也可以用CS6的破解方法,不过可能本人太菜,有所失误,总是不成功,安装成功后,打开总是提示 : 我们无法开始您的Adobe Dreamweaver cs5 subscription ...

  8. spring spring data jpa save操作事务

    整合spring spring data jpa的时候,在save方法上加了@Transactional注解.此时调用springdatajpa save方法并不会真的把数据提交给数据库,而是缓存起来 ...

  9. VS2012 还原默认设置

    恢复默认设置的2种方法 如果VS出现问题或设置变乱,可以通过恢复默认设置使之回到安装成功时的状态,从而解决出现的问题.VS恢复默认设置的方法有2种,分别是:通过"导入和导出设置"实 ...

  10. Git 版本管理的简单理解

    来源:百度知道 现在使用Git版本管理代码的项目非常多.但是Git本身是一条复杂的系统.我从几个简单的点来说明Git的基本功能.希望能帮助初学者快速入门. 工具/原料   Git code dot j ...