WDM 실습 - 간단한 키보드 제어

from Study/WDM 2007/11/30 19:54 조회 : 4743
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 )

=> 키보드에 달려있는 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;
}
이올린에 북마크하기(0) 이올린에 추천하기(0)
Tag |

Trackback Address :: http://dblab.co.kr/trackback/316

댓글을 달아 주세요

  1. 아이오아이오 2008/01/12 05:12  address  modify / delete  reply

    먼저 제게 너무나 소중한 정보를 알려주셔서 고개 숙여 감사드립니다.
    주요 코드만 뽑아서 제 프로그램에서 동작시키니 잘 동작하고 있습니다.
    (부족한 지식에 인터넷을 돌아다니며 protected mode에서 포트를 access하는 방법은 알아 낼 수 있었습니다)
    제가 지금 port 0x60과 0x64를 이용하여 키보드와 마우스 입력을 emulate(물리적인 키보드 스트록이나 마우스 버튼 클릭 같은 입력신호가 없이 유저 프로그램으로 입력신호를 생성)하는 내용을 알아보고 있습니다.
    염치불구하고 몇가지 여쭙겠습니다.

    1. 위의 두 포트를 이용하여 키보드, 마우스 입력을 emulate하는 것이 가능한가요?
    2. 예제 코드에서 키보드 LED를 점멸시키는 주요 명령이 port 0x60에 보내지는 0xED와 0x00~0x07로 이해했습니다. 이렇게 키보드와 마우스를 입출력을 제어하는 명령이나 이와 관련된 정보를 얻을 수 있겠습니까? 아니면 어디서 구할 수 있을까요?
    3. 제가 알고 싶은 emulation을 구현하기 위해 필요한 정보가 또 어떤게 있을까요?

    그럼 다시 찾아뵙겠습니다.
    계획하신 모든 일들을 이루시길 바랍니다. (__)

    • serahero 2008/01/12 15:38  address  modify / delete

      -_-. 도움 드리지 못해서 죄송합니다. 저도 '루트킷' 책보면서 공부하고 있는 입장이라. 확실히 대답 해드리지 못하겠네요.
      음. 드라이버온라인 사이트에 가시면 도움 좀 되실듯 합니다.

  2. 아이오아이오 2008/01/12 19:30  address  modify / delete  reply

    네 감사합니다.