1. H/W 제어
- IRQ( Interrupt request ) : 15개의 인터럽트를 갖는다.
- CPU는 8259 칩 2개를 사용하여 입력포트를 늘린다.(7개(1개는 다른 8259칩이랑 연결) + 8개 )
http://dblab.co.kr/entry/1016화-이론-2-Hook
- 60, 64번포트를 사용하여 읽어오고 내보낸다.
- out, in 특정 포트를 사용하는 기본적인 명령어 이다.(인텔명령어)
- 호환성을 높이기 위한 MS는 매크로를 만들어 놨다.
=> WRITE_PORT_CHAR(), READ_PORT_CHAR() // HAL( hardware abstract layer )
- CPU는 8259 칩 2개를 사용하여 입력포트를 늘린다.(7개(1개는 다른 8259칩이랑 연결) + 8개 )
http://dblab.co.kr/entry/1016화-이론-2-Hook
- 60, 64번포트를 사용하여 읽어오고 내보낸다.
- out, in 특정 포트를 사용하는 기본적인 명령어 이다.(인텔명령어)
- 호환성을 높이기 위한 MS는 매크로를 만들어 놨다.
=> WRITE_PORT_CHAR(), READ_PORT_CHAR() // HAL( hardware abstract layer )
=> 키보드에 달려있는 3개의 LED를 깜박인다. (-_-..안되는 것도 있다. )
#include "ntddk.h"
#include <stdio.h>
VOID rootkit_command_thread(PVOID context);
HANDLE gWorkerThread;
PKTIMER gTimer;
PKDPC gDPCP;
UCHAR g_key_bits = 0;
// 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)
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 SetLEDS( UCHAR theLEDS )
{
// setup for setting LEDS
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");
}
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
DbgPrint("ROOTKIT: OnUnload called\n");
KeCancelTimer( gTimer );
ExFreePool( gTimer );
ExFreePool( gDPCP );
}
// called periodically
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;
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
LARGE_INTEGER timeout;
theDriverObject->DriverUnload = OnUnload;
// these objects must be non paged
gTimer = ExAllocatePool(NonPagedPool,sizeof(KTIMER));
gDPCP = 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;
}
#include <stdio.h>
VOID rootkit_command_thread(PVOID context);
HANDLE gWorkerThread;
PKTIMER gTimer;
PKDPC gDPCP;
UCHAR g_key_bits = 0;
// 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)
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 SetLEDS( UCHAR theLEDS )
{
// setup for setting LEDS
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");
}
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
DbgPrint("ROOTKIT: OnUnload called\n");
KeCancelTimer( gTimer );
ExFreePool( gTimer );
ExFreePool( gDPCP );
}
// called periodically
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;
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
LARGE_INTEGER timeout;
theDriverObject->DriverUnload = OnUnload;
// these objects must be non paged
gTimer = ExAllocatePool(NonPagedPool,sizeof(KTIMER));
gDPCP = 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;
}
Tag | WDM