Данный опус является переводом статьи из
летнего, 59-ого выпуска журнала Phrack. Я вполне
допускаю, что в нее могли закрасться некие
неточности, поэтому в случае возникновения
вопросов обращайтесь к оригиналу: http://www.phrack.org/show.php?p=59&a=14.
Ну, понеслась.

Как работает драйвер клавиатуры

Согласись, для того что бы написать
грамотный keylogger нужно знать, каким образом
нажатие клавиши на клавиатуре
регистрируется в системе. Смотри схему:

handle_scancode -> put_queue -> tty_queue -> receive_buf ->
буфер tty_ldisc -> tty_read -> /dev/ttyX -> sys_read ->
процесс пользователя

Сначала, когда вы нажимаете клавишу на
клавиатуре, устройство посылает
соответствующий скан код клавиши
клавиатурному драйверу. Единичное нажатие
может произвести последовательность до
шести кодов. Функция handle_scancode() в
клавиатурном драйвере обрабатывает поток
кодов и конвертирует их (при помощи функции
перевода kbd_translate()) в серию событий типа "клавиша
нажата", "клавиша отпущена". 
Каждый баттон имеет свой уникальный код от 1
до 127. Нажатие клавиши генерирует этот код, а
отпускание - этот код + 128 (например когда вы
нажимаете 'a' то на гора выдается код 30, а
когда отпускаете - 158).

Дальше код клавиши в соответствии с
картой клавиш конвертится обратно в сам
символ (вообще говоря при этом учитываются
нажатые функциональные клавиши - Shift , AltGr,
Control, Alt, ShiftL, ShiftR, CtrlL и CtrlR, комбинации
активных "модификаторов" - CapsLock
например) и передается по цепочке буферов и
очередей к терминалу.

Драйвер клавиатуры может работать в
четырех режимах:

  • scancode (RAW MODE): приложенице получает
    сканкод из потока и дальше обрабатывает
    его собственным драйвером (пример тому - Х11).

  • keycode (MEDIUMRAW MODE): программа получает код
    клавиши, переведенный из скан кода
    драйвером в соответствии с собственной
    таблицей.

  • ASCII (XLATE MODE): код клавиши преобразуется в
    ASCII-код символа или некоторую
    последовательность ASCII-кодов символов в
    соответствии с таблицей раскладки
    клавиатуры, которая хранится в памяти/диске
    виде отдельного файла (можно поменять
    раскладку клавиатуры командой kbdconfig, она
    прописывает новое значение в файл /etc/sysconfig/keyboard
    и загружает указанную таблицу в
    оперативную память).

  • Unicode (UNICODE MODE): этот режим отличается от
    предыдущего другим форматом вывода
    символов - UTF8, а не ASCII.

Эти режимы в конце концов и определяют,
что приложение получит в результате
нажатия клавиши. (Кстати говоря: режим
работы драйвера клавиатуры можно узнать
или изменить с помощью kbd_mode(). Но
помни! Смена режима может вообще оставить
тебя без клавиатуры!)

Kernel based keylogger

В общем говоря, мы можем перехватить поток
кодов двумя способами: написать обработчик
прерывания клавиатуры или вмешаться в
передачу на одном из описанных выше этапов.

Обработчик прерывания

Гляди как реализуется первый вариант. В
архитектуре Intel для контроллера клавиатуры
выделено первое прерывание. Когда оно
возникает наш обработчик должен прочитать
сканкод и статус клавиатуры. События
клавиатуры можно получить на 0х60 порту (Keyboard
data register), а ее состояние - на 0х64 (Keyboard status
register).

/* below code is intel specific */
#define KEYBOARD_IRQ 1
#define KBD_STATUS_REG 0x64
#define KBD_CNTL_REG 0x64
#define KBD_DATA_REG 0x60
#define kbd_read_input() inb(KBD_DATA_REG)
#define kbd_read_status() inb(KBD_STATUS_REG)
#define kbd_write_output(val) outb(val, KBD_DATA_REG)
#define kbd_write_command(val) outb(val, KBD_CNTL_REG)
/* register our own IRQ handler */
request_irq(KEYBOARD_IRQ, my_keyboard_irq_handler, 0, "my keyboard",
NULL);

В my_keyboard_irq_handler():

scancode = kbd_read_input();
key_status = kbd_read_status();
log_scancode(scancode);

Этот обработчик канает только для
архитектуры Intel, так что в случае переноса
на другую платформу он работать
естественно не будет. Хотя, честно говоря,
где ты у нас видел другие платформы...

Замена функций

Это начальная функция драйвера
клавиатуры (keyboard.c), она обрабатывает скан
коды, получаемые от клавиатуры:

# /usr/src/linux/drives/char/keyboard.c
void handle_scancode(unsigned char scancode, int down);

Мы можем заменить настоящий handle_scancode()
собственной процедурой, которая помимо
всего прочего будет записывать коды клавиш.

(Продолжение следует)

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии