在计算机组成当中,每一个硬件模块都有专属于自己的寄存器,这些寄存器用于短时间内保存自己的数据,接受命令用于下一步执行,或者保存硬件的状态。而这些寄存器可以通过相应的端口进行访问这些寄存器。
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath ) { LARGE_INTEGER timeout; theDriverObject->DriverUnload = OnUnload; gTimer = (PKTIMER)ExAllocatePool(NonPagedPool,sizeof(KTIMER)); gDPCP = (PKDPC)ExAllocatePool(NonPagedPool,sizeof(KDPC)); timeout.QuadPart = -10; KeInitializeTimer( gTimer ); KeInitializeDpc( gDPCP, timerDPC, NULL ); if(TRUE == KeSetTimerEx( gTimer, timeout, 300, gDPCP)) // 300 ms timer { DbgPrint("Timer was already queued.."); } return STATUS_SUCCESS; }
首先在驱动入口程序设置一个定时器以及相应的定时器处理函数,定时器每隔300ms进行一次异步的定时处理程序进行一次处理。而与DPC相关联的异步处理函数式timerDPC,下一步进入到timerDPC的分析。
VOID timerDPC( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID sys1, IN PVOID sys2) { SetLEDS( g_key_bits++ ); if(g_key_bits > 0x07) g_key_bits = 0; }
在timerDPC当中对键盘LED进行设置,然后在键盘按键计数大于7的时候,对键盘计数进行清0。
void SetLEDS( UCHAR theLEDS ) { if(FALSE == SendKeyboardCommand( 0xED )) { DbgPrint("SetLEDS::error sending keyboard command\n"); } // send the flags for the LEDS if(FALSE == SendKeyboardCommand( theLEDS )) { DbgPrint("SetLEDS::error sending keyboard command\n"); } }
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; }
函数首先等待输入缓冲标志为满,然后对缓冲进行排空,然后利用0X60端口写入命令。
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; }
在等待函数当中,首先读取端口数据,然后建等待,再看看其中的INPUTBUFFER标志是否被标上。
void DrainOutputBuffer() { char _t[255]; int i = 100; 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--); }