危险漫步博客
新鲜的“黑客思维”就是从全新的角度看待黑客技术,从更高的层面去思考;专注于黑客精神及技术交流分享的独立博客。
文章2289 浏览18787728

RING3下通过修改中断描述表来监控键盘输入

何为中断门?众所周知,Windows有R0和R3之分,要想从R3进入R0有很多方法,其中之一就是”穿过”名叫中断门的这扇”门”,人们把由中断引起的用户模式与内核模式的切换,形象的称之为穿过中断门。要说如何穿过中断门,这就离不开中断描述表IDT,或者称之为中断索引表要更为之贴切。

IDT者,Interrupt Descriptor Table也,名字很吓人,究其实质就是一块存放特殊数据的内存。

每当按下键盘上的键子就会产生一次键盘中断,每当点击鼠标就会产生一次鼠标中断,可见中断无处不在。操作系统每次收到中断就会访问这块名叫idt所代表的内存,然后根据中断索引来找到相应的处理函数,进而来处理此次中断。试想一下,如果我们把键盘的中断处理函数hook掉,是不是就能达到监控键盘的目的呢?答案是肯定的。

中断分为硬件中断,和软件中断。硬件中断又被称之为IRQ。一个IRQ是来自于哪个硬件,一般来讲都是固定的,比如IRQ1就一定是PS/2键盘的中断。实际上每个IRQ存在一个“中断索引”(中断号)与之对应,比如,在排除IOAPIC重定位的情况下,IRQI对应的就是int 93h,但是说了半天,这“中断索引”又是神马?且看int 93h这条指令,其中的93就是索引。但是又是怎样应用这个索引的呢?各位看官,您莫要着急,让我徐徐道来。

其实idt这块内存是由许多的上面的结构体“垒砌”而成的,中断的处理函数的地址就写在其中。在32位的情况下,中断处理函数的地址是32位的,但是很蛋疼的是,不知道什么原因,他被分成了两部分,offset_low中是低16位,offset_high是高16位。type是中断的类别。其他别的与本文无关就不费口舌了,但是值得注意的是,结构体中类似BYTE type:4;这样的声明,它的意思是type这个元素只占4位。以此类推always0只占1位,dpl只占2位,present只占1位,这就体现了c语言的强大之处,在这里借鉴一些前辈的经验。这些经验都可以去百度上搜索的,危险漫步就不一一列举了,实在找不到的朋友们可以找我要的。

GET_IDT_ADDRESS是把高低16位地址转换成32位地址,SPLJT_IDT_ LOW16是取32位地址的低16位,SPLIT_IDT_HIGH16是取32位地址的高16位。

下面来看看具体的索引过程,当执行int 93这条指令的时候,操作系统找到IDT表中第0x93个IDT_ELEMENT结构,然后得到里面的处理函数,进而执行处理函数。一切就是如此简单,只需要改变offsetlow和offset_high就能达到我们hook的目地。说的这么热火朝天,还没介绍怎么获得IDT表的地址呢,其实很简单,这里就不一一列举了。有需要的可以私聊找我要的。

上面的函数就是获得IDT的过程,sidt idtr意为把IDT的地址存放在idtr中,获得IDT后,又要怎样Hook呢。代码就不贴出来了。还是老规矩,需要的可以私聊找我要的。naked是告诉编译器不要生成,,杂七杂八的东西,直接生成我们的代码,这样才能堆栈平衡,才不会出问题,钩子是挂上了,但是怎么读取键盘的输入呢?许多前辈指出可以用读取硬件端口的形势。先介绍一下有关于键盘的端口吧。要用到的端口一共有2个,一个是0x60,一个是0x64。这两个端口的配合很猥琐,0x60是数据端口,用指令in al,0x60即可获得从键盘输入的数据。而0x64是状态端口,因为0x60端口不是每时每刻都是可读或者可写的,所以0x64标志了0x60端口的到底是可读还是可写。代码需要两段由于篇幅原因就不贴出来了。

上面的两段代码是判断端口是否可读可写,下面的代码就是真刀实枪的读取键盘输入了,再看代码之前,我要交代一件事情,首先我读取0x60端口中的内容,读取后,0x60端口中就没有内容了,如果不做处理直接传到原始的处理函数的话,操作系统就会一点反应木有的,因为木有数据可处理,所以在向下传之前,还要把数据再写回0x60端口去,然后再向下到原始的处理函数,此时问题又出现了,虽然我们把内容传了下去,但是在写端口的同时,又会引发一次键盘中断,又会执行一次我们的新的处理函数,这是一个死循环。要解决这个问题,可以预先定义一个全局变量,把第一次读取的内容保存到这个全局变量里,在第二次读取端口之后,比较新读取的内容和第一次读取的内容,如果相同则不回写0x60端口。

到此,该说的都已经说了,对了还要说一句--,BYTE,WORD,DWORD在ddk中是木有定义的,所以需要自己声明一下。

相关推荐