在计算机组成当中,每一个硬件模块都有专属于自己的寄存器,这些寄存器用于短时间内保存自己的数据,接受命令用于下一步执行,或者保存硬件的状态。而这些寄存器可以通过相应的端口进行访问这些寄存器。
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--);
}