Pull to refresh

Создаем хардварный логгер клавиатуры

Reading time 13 min
Views 50K
У тебя наверняка не раз возникала ситуация, когда программные логгеры клавы не могли решить поставленных задач. Например, отловить пароль от биоса с помощью программного кейлоггера, загружаемого системой, невозможно. Лично я столкнулся с подобной проблемой, когда мне нужно было узнать админский пароль в локальной сети одной фирмы. Тогда я и подумал, что было бы очень круто сделать «железный» логгер, который бы подключался между клавиатурой и компьютером и ловил все нажатые клавиши, начиная с включения компьютера. В предлагаемой статье изложены принципы работы PS/2 интерфейса, и перехват данных, передаваемых по нему.

image
Парочка аппаратных логгеров клавиатуры

Принципы


Для того чтобы сконструировать подобное устройство, сначала нужно разобраться с тем, как же работает клавиатура. Есть два основных типа клавиатур: АТ (старый стандарт) и PS/2. Отличаются они только разъемами: АТ имеет DIN, а PS/2 — miniDIN. Первый — большой круглый разъемчик с пятью штырьками, второй — маленький, как у мышки, с шестью пинами. По протоколу обмена они полностью совместимы. Наверняка, ты видел переходники с широких старых разъемов на новые маленькие. Этот стандарт появился еще в 1984 году вместе с первым персональным компьютером IBM PC и используется по сей день, практически не претерпев никаких изменений.
Клавиатура представляет собой матрицу кнопок (около восьми строк на 16 колонок), которые снимаются контроллером клавиатуры и при нажатии передаются в виде скан-кодов в компьютер (не путать с ASCII-кодами). Между клавиатурой и компьютером установлен двухсторонний обмен. Контроллер клавиатуры передает скан-коды нажатых клавиш: скан-код отпускания клавиши вместе со скан-кодом самой клавиши. Контроллер клавиатуры, находящийся на материнской плате компьютера, определяет функции клавиатуры (была ли, например, нажата клавиша ) и выдает их уже в систему. Он может управлять контроллером, встроенным в клавиатуру, к примеру, моргать светодиодами на клавиатуре, устанавливать скорость автоповтора клавиш и т.д. Проще говоря, в клавиатуре есть микросхема, которая все нажатые клавиши тупо кидает в провод, далее на материнской плате другая микросхема декодирует эти скан-коды и управляет клавиатурой. Следовательно, если мы подсоединимся к проводу клавиатуры, будем слушать, что там происходит, и сохранять это в некоторую память, мы вполне сможем восстановить то, что набиралось на клавиатуре в этот момент.

Протокол работы с клавиатурой


Общие принципы ясны, теперь выясним, по какому протоколу осуществляется обмен данными между клавиатурой и материнской платой. Надо сразу отметить, что клавиатура правит балом и является более приоритетным устройством на своем интерфейсе, нежели материнская плата. От разъема на материнской плате к клавиатуре идет четыре провода: общий провод (GND), +5 вольт, данные (Data) и тактовый сигнал (Clock).

image
Распиновка разъёмов клавиатуры (старый и новый)

Протокол работы клавиатуры последовательный, то есть байт передается всего по одному проводу, синхронизируясь тактовым сигналом. В режиме покоя сигнал Clock находится в высоком уровне (логическая единица). Когда мы нажимаем какую-либо клавишу, клавиатура начинает передачу данных.

image
Последовательный инферфейс клавиатуры.

Тактовый сигнал принимает состояние логического нуля (переходит с высокого уровня на низкий), и на шине данных выставляется нулевой бит. Этот бит называется Start bit, он дает контроллеру на мамке понять, что мы начинаем передачу данных. Далее тактовый сигнал вновь принимает значение логической единицы, и по новому спаду в логический ноль передается нулевой бит. Таким образом передаются все 8 бит кода, начиная с нулевого и заканчивая седьмым битом. Завершает передачу один бит четности (Parity Bit), подтверждающий верность принятых данных, и стоп-бит (Stop bit), показывающий, что передача окончена и может передаваться следующий байт. Получается, что одна посылка занимает 11 бит, включая вспомогательные биты. Более наглядно это представлено на картинках и на видео ниже.

image
Осциллограмма передаваемых данных с клавиатуры. Сверху сигнал Clock, снизу Data

Из этого следует, что нам в первую очередь нужно слушать сигнал Clock и по спаду его фронтов читать, что же у нас происходит на шине данных.

Клавиатурные данные


Итак, с протоколом работы клавиатуры вроде бы все ясно. Теперь посмотрим, какие же данные курсируют от клавиатуры к компьютеру и обратно. Эти данные можно разделить на три группы.
1. Управляющие команды, посылаемые компьютером в клавиатуру.
Например: FFh — — сброс клавиатуры. Эти команды могут зажигать светодиоды на клавиатуре, управлять ее работой и т.д. Интересная группа команд, позволяет весьма гибко переконфигурировать клавиатуру и даже разогнать или замедлить ее (привет оверклокерам :) ). Всего этих команд восемь, они имеют зарезервированные коды EDh, EEh, F0h, F3h, F4h, F5h, FEh и FFh. Каждый код – это отдельная команда, назначение которой ты можешь узнать в документации.
2. Команды, посылаемые клавиатурой к компьютеру.
Эта группа команд показывает нам статус клавиатуры или какие-то проблемы с обработкой. Например: 00h – ошибка или переполнение буфера клавиатуры. Этих команд всего семь штук, они имеют коды FAh, AAh, EEh, FEh, F0h 00h и FF. Нам интересны две команды: AAh и F0h. Команда AAh показывает, что самотестирование при подаче питания прошло успешно. Теперь мы можем быть на 100% уверены, что клавиатура включена. С момента прохода этой команды по проводам мы можем смело запускать наш логгер и следить за набираемыми клавишами на клавиатуре. Кстати, когда при загрузке биос ругается на отсутствие клавиатуры, он сообщает нам, что не получил ту самую команду AAh — самотестирование клавы не прошло успешно. F0h же показывает, что была отжата нажатая клавиша.
3. И, наконец, самая главная группа данных, посылаемых клавиатурой компьютеру, – это скан-коды нажатых клавиш. Как я уже говорил, скан-коды большинства клавиш занимают 1 байт. К примеру, у клавиши F1 скан-код будет 05h. Но есть и расширенные клавиши, скан-код которых занимает 2 и более байт. Например: у правого Alt скан-код будет выглядеть как E0 11h, а у клавиши Pause — E1 14 77 E1 F0 14 F0 77h, целых 8 байт! Отсюда интересный вывод, что количество и функциональность клавиш можно расширять бесконечно и ты можешь сам сделать собственную клавиатуру с любым возможным числом клавиш, назначив им любые функции: от управления Винампом до пуска ядерных боеголовок. Для примеру покажу, какие команды пойдут в компьютер при наборе на клавиатуре моего ника dlinyj:

23 F0 23 4B F0 4B 43 F0 43 31 F0 31 35 F0 35 3B F0 3Bh

Тут все логично, скан-коды клавиш будут такие: d=23h, l=4Bh, i=43h, n=31h, y=35h, j=3Bh, а F0h является командой, которая показывает, какая клавиша в какой момент была отжата. То есть сначала посылается код нажатой клавиши, затем при отжатии – команда о том, что клавиша была отжата, и код отжатой клавиши. Ты можешь в любом порядке зажать несколько клавиш и в другом порядке их отжать, компьютер это воспримет.

image
image
Таблица сканкодов клавиш


Передача сканкодов клавиш к компьютеру.


То как это выглядит на осциллографе.

Постановка задачи


Обычно разработка устройства начинается с железной части, а потом уже к железу пишется программа. Но мы все делали одновременно: и паяли железо, и писали ПО. Однако принципы построения аппаратной части были заложены еще на этапе идеи.
Для начала надо очертить круг задач, которые мы поставили, изготавливая этот логгер. Задача перед нами стояла следующая: создать устройство, вставляемое в разрыв провода клавиатуры и отслеживающие нажатие клавиш. Дальше это устройство, в зависимости от конфигурации программы, должно было быть способно сохранять нажатые клавиши в энергонезависимую память. Затем надо было реализовать возможность снятия этого лога через компьютерный интерфейс, например USB. Важно было учесть и различные варианты развития этого устройства: подключение радиопередатчика, сохранение больших массивов данных, перевод в ASCII-код на ходу, сопряжение с множеством компьютерных интерфейсов. Забегая вперед, скажу, что в нашем устройстве реализованы все возможные способы расширения его функциональности.
Мы выбрали процессоры семейства AVR. Они идеально подходят для поставленной задачи: они недорогие, весьма простые в освоении, у них обильная периферия, есть оперативная и, что самое главное, энергонезависимая память – EPROM. Из семейства мы отобрали наиболее простой и дешевый ATtiny2313. Этот камушек стоит всего $1,5-2 имеет на своем борту 2 Кб программной памяти, 128 байт оперативки и столько же энергонезависимой памяти. Также у него есть важные для нас последовательные интерфейсы SPI и UART. По SPI мы можем подключать внешнюю память (например, MMC-карточку на 4 Гб) или с помощью программатора, прямо не отключая наш девайс от клавиатуры, сливать содержимое энергонезависимой памяти. UART – это асинхронный приемопередатчик, расширенная версия протокола RS-232 (см. мою статью на хабре). Он послужит нам для связи контроллера с компьютером через согласующие схемы. Но главное, что наш процессор может обрабатывать внешнее прерывание. Есть ножка контроллера, которая может реагировать на смену сигнала. Например, если у нас сигнал в логической единице, то будет обрабатываться прерывание. Нам необходимо настроить устройство так, чтобы прерывание срабатывало по спаду (то есть переходу с логической единицы на ноль) сигнала CLOCK в клавиатуре. Проводок Data мы подсоединим к ножке INT1. Прерывание он обрабатывать не будет. Это сделано для того, чтобы, не сильно изменяя код, можно было легко перенести его под другой процессор – INT0 и INT1 всегда находятся на одних и тех же пинах порта D на любом процессоре семейства AVR: PD2 и PD3.
Сливать данные из EPROM можно двумя способами. Первый способ – сливать неперекодированные данные с помощью программатора, а затем перекодировать своей программой. Такой способ хорош тем, что не нужно морочиться с всевозможными интерфейсами и т.п. Просто несколько проводов на LPT-порт и все. Второй вариант – скачивать уже подготовленный текст в ASCII-кодах по порту RS-232. Для этого у процессора предусмотрены ножки RX и TX, как у COM-порта.
Но не торопись сразу подключать процессор напрямую к компьютеру – ты его просто спалишь. Дело в том, что уровень сигналов СОМ-порта отличен от такового у процессора. Логический ноль У СОМ-порта кодируется как -15 вольт, а единица — как +15 вольт. А у процессора — от нуля до +5 вольт. Существуют схемы согласования, множество которых ты найдешь в интернете и радиожурналах. Самый простой путь – это использовать шнурок для мобильника, который на рынке стоит всего 30 рублей. Он на USB, но на конце его есть заветные контакты RX и TX, которые должны подсоединяться к мобильнику. Если ты подключишь его к USB и поставишь все необходимые драйверы, то у тебя в системе появится еще один СОМ-порт. Распиновки таких шнурков можешь найти у нас на диске. Не забудь только, что сигнал RX – означает приём, а TX – передачу. По сему следует сигнал RX микросхемы цеплять на TX ножку процессора и наоборот TX микрушки на RX МК

Процесс разработки


Любую микропроцессорную разработку надо начинать с проверенных временем решений. Для начала данную разработку стоит отладить на готовых решениях, например, с дисплеем, с сопряжением с компьютером и т.д. Поэтому мы взяли макетную плату с уже встроенным дисплеем и с полностью отлаженной процедурой работы с ним. Это необходимо было для того, чтобы убедиться, что клавиатуру мы видим и отслеживаем.
Первым делом мы подали на клавиатуру питание и посмотрели на осциллографе процесс передачи данных от нее. Это важный момент, поскольку на этом этапе можно выявить некоторые недокументированные возможности протокола, которые потом создадут большие грабли. К примеру, у разных типов клавиатур может отсутствовать или неверно отображаться бит четности. Но в нашем случае было все в порядке.

image
Отладочная плата с USB-шнурком

Дальше мы подключили клавиатуру к процессору и попробовали просто ловить прерывание, не преобразуя в скан-код. Поначалу были некоторые проблемы: то не ловилось, то появлялся FFh. Оказалось, что при запайке мы поменяли местами сигналы Clock и Data. После их перестановки все встало на свои места. Нажатия клавиш сразу отображались на дисплее. Первые две цифры – сколько байт было принято с момента подачи питания. Вторая пара цифр — последний принятый байт, третья и четвертая — предпоследний и последний байт соответственно.
Эта задача, весьма простая на первый взгляд, решалась целых три дня. Сначала лезли непонятные глюки — как оказалось, фирменный программатор просто портил код. Когда решили эту проблему, начались траблы с компилятором. В общем, разработка — медленное и нудное занятие, но оно того стоит!
Дальше была прикручена давно написанная библиотека работы с UARTом для сопряжения с компьютером. Для подключения процессора к компьютеру использовался DATA-кабель от мобильного телефона, и от него питалась вся наша схема. Для этого потребовалось разобрать сам кабелек, найти документацию на распиновку кабелей для мобильников и все грамотно распаять. Когда все было сделано и программный код заработал вместе с аппаратной частью, мы увидели нажимаемые клавиши в терминальной программе.

image
Аппаратные исходники

Последим этапом стала работа с встроенной в процессор энергонезависимой памятью. В нашем распоряжении было всего 128 байт памяти, а если учесть, что каждая клавиша занимает минимум 3 байта (нажатие, код отжатия и код отжатой клавиши), то ее должно было хватить ровно на пароль от операционки, не более. У ATmega8 512 байт EPROM, что в 4 раза больше, чем у ATtiny2313, и, быть может, ты успеешь захватить какие-то моменты из асечной переписки. В эту память процедура записи изначально была циклическая: мы пишем в память, доходим до конца и снова пишем сначала. Получалось, что исходный текст затирается. Последним штрихом была дописанная часть программы, которая определяла конец памяти и больше не продолжала запись.
Дальше мы стали приводить отладочный вариант в божеский вид. Сначала была сделана плата, в которой после запайки обнаружились ошибки, и она была забракована – пришлось делать все заново. Потом мы сняли лишние отладочные функции, отточили код и исправили ошибки. Напоследок код был перенесен на более крутой и дорогой процессор ATmega8 для увеличения EPROM. Сделано это было отчасти еще из-за того, что в свое время я уже делал подобную разработку, но не довел ее до ума, а плата осталась. И чтобы добру не пропадать, я заморочился и перенес код, благо это было несложно.
На момент выхода статьи можно сливать данные через UART, но только в виде скан-кодов, и дальше по бумажке их перекодировать (абсолютно хакерский подход — выучить все скан-коды клавиатуры и делать всю процедуру в уме), или использовать нашу программу на паскале. Надеюсь, в конечном продукте будут реализованы все возможные функции. Сейчас данные сливаются посылкой любого символа в порт, к которому подключен контроллер. Для этого удобно пользоваться специальной терминальной программой, которая лежит на нашем диске.

Программная реализация


Программный код очень объемный, поэтому в статью он не влез и ты можешь посмотреть его в приложении — там все подробно расписано, есть комментарии. Но общий подход я тебе изложу.
Программный код можно разбить на характерные функциональные блоки, которые выполняют конкретные поставленные задачи. Первый блок запускается после включения компьютера. В нем инициализируются прерывания (прерывание от UART и прерывание по спаду сигнала CLOCK), сам асинхронный приемопередатчик аkа UART. Очищаются и инициализируются объемы оперативной и энергонезависимой памяти. Определяется стек. Ну в общем, проводится полная инициализация и настойка всего оборудования. Второй основной блок – это обработчик прерывания от клавиатуры. О нем я расскажу подробнее.

Int0Handler:
….
inc KbdRxState
mov Tmp1, KbdRxState
cpi Tmp1,1
brne KbdState1_8


Это и есть обработчик прерывания от клавиатуры. В случае падения фронта на пине INT0, к которому у нас подключен сигнал CLOCK, у нас вызывается это прерывание. Первая команда у нас увеличивает счетчик принятых бит. Вторая и третья проверяют значение счетчика, сравнивая его с единицей. Третья: если счетчик равен единице, то это означает, что был принят старт-бит, и это и есть начало передачи.

sbis KbdPort, KbdDat
rjmp Int0LEx
clr KbdRxState
rjmp Int0LEx


Здесь мы проверяем стартовый бит. При приходе стартового бита на шине данных должен быть логический ноль. Если у нас на пине, куда приходит сигнал Data, не ноль, это означает, что был дребезг контактов и нас эти данные не интересуют — тогда все сбрасывается и начинается снова.


KbdState1_8:
cpi Tmp1, 10
brge KbdStatePar


Тут мы опять смотрим количество принятых бит, но здесь уже определяется, не достигли ли мы конца. Значение должно лежать в диапазоне от 1 до 8. Если оно больше, то, значит, это стоп-бит или бит четности. И мы переходим на недописанную процедуру проверки четности. Если у тебя есть желание, можешь ее дописать, но, как показала практика, в ней нет необходимости. Вероятность ошибки слишком мала.

clc
sbic KbdPort, KbdDat
sec
ror KbdData
rjmp Int0LEx


Очевидная часть программы. Сначала мы очищаем флаг переноса. Далее смотрим состояние порта данных, идущих от клавиатуры. Если у нас на входе стоит единица, то мы устанавливаем флаг переноса командой sec, если же ноль, то пропускаем команду установки флага. Затем мы задвигаем принятый бит из флага переноса в регистр KbdData.

KbdStatePar:
cpi Tmp1, 10
brne KbdStateAsk
rjmp Int0LEx

KbdStateAsk:
….
KbdEndState:
…..
rcall SendEvent
clr KbdRxState
rjmp Int0LEx

Int0LEx:
….
reti


В оставшейся части мы проверяем количество последних принятых бит. В процедуре KbdStateAsk должна быть проверка на четность. rcall SendEvent — вызов процедуры, которая кладет полученные данные в EPROM. После этого мы очищаем счетчик принятых бит и выходим из процедуры обработки прерывания.
В следующем блоке у нас идет процедура записи в EPROM – энергонезависимую память, куда помещается собранное содержимое регистра KbdData. Оно туда записывается с начала памяти и до тех пор, пока мы не исчерпаем ее объем.

image
Содержимое EEPROM (нижнее окно)после снятия лога.

Последний блок – обработка прерывания от UART. Если у нас происходит прерывание, то мы сливаем в разном типе данных, в зависимости от принятого символа. Но пока доступен только слив в бинарном виде, и для этого используется латинская буква b.
Если ты внимательно просмотришь код на диске, то ты можешь встретить несколько вспомогательных процедур. Это остатки предыдущих программ, которые нужны были нам для отладки, но коду они совершенно не мешают. Там есть процедура моргания светодиодом и процедуры работы с сегментными ЖК-дисплеями. К слову сказать, данный код являет собой многозадачную операционную систему реального времени для AVR-микроконтроллеров, которую товарищ Dihalt довёл до ума.

Сопряжение с компьютером


Для связи с компьютером у контроллера есть интерфейс UART. Его можно подключить к COM-порту компьютера через такую согласующую схему.

image
Схема сопряжения на МАХ232

Это самая распространенная и уже отмирающая схема. Отмирает она вместе с СОМ-портами. Например, в моем ноуте уже нет СОМ-порта. И что же делать? Есть аналогичные микросхемы для связи. К примеру, самая распространенная FT232BM или ее более совершенная модификация с меньшим обвязом – FT232R. Эта микросхема, после успешной запайки и установки всех необходимых драйверов, видится в системе как СОМ-порт. Кстати, это важный момент для тех, у кого есть ноутбук и древние устройства, но нет старого доброго порта. Совместив обе схемы, можно получить полноценный СОМ-порт, к которому возможно подключить модем или даже старую мышку.

image
Плата с FT232BM

На мой взгляд, самый удобный и дешевый вариант – использовать шнурок от мобильного телефона. Функции он выполняет те же, что и микросхема FT232. Там тоже надо поставить драйверы и чуток погеморроиться с распиновкой разъема. Но выигрыш в цене будет очевиден – это в 10 раз дешевле. Мы попробовали все три варианта и пришли к выводу, что шнурок для мелких поделок – идеальное решение.

image
Шнурок для мобильника

Итог


В этой статье мы подробно рассмотрели процесс разработки и изготовления радиоэлектронного устройства. Подобные подходы используются при проектировании материнских плат, мобильных телефонов, микроволновок, да и всей электроники в общем. Самое главное в нашей разработке то, что ей есть, куда расти. Ты сам, поковыряв код и разобравшись в нем, можешь дописать необходимые процедуры. А если и не дописать, то найти готовые в интернете. Главное – сообразить, как их пристыковать. Например, можно найти радио-приемопередатчик на UART. Таких устройств множество, и стоят они недорого — около $50. Немного видоизменив код, ты получишь передающий логгер, лог которого можно принимать на ноутбуке во дворе. Можно повесить внешнюю флешку на гиг, чтобы раз в месяц только ходить и собирать логи. Короче говоря, применений тьма тьмущая.

image
Готовый логгер

Могу сказать, что устройство пока достаточно сырое. Необходимо для него дописать клиентскую программу, которая может декодировать нажатые клавиши. Так же стоит написать интерфейс внешней флешки, т.к. внутренней EEPROM очень мало для экспериментов, и её ресурс так мал, что на этапе написания данной статьи я его исчерпал. Но почва для экспериментов громадная.

З.Ы. Эта статья является адаптированной версией моей статьи в «Хакер» за сентябрь 2007 года, которая называлась: «Шпионим за тётей Клавой».

З.З.Ы. Разумеется это устройство демонстрирует принцип работы клавиатуры. И использование его в качестве шпионажа может повлечь уголовную ответственность. Автор не несёт ответственности за использование данной информации.

Список литературы и ссылки.

1.Исходники и техническое описание собранно в один архив http://narod.ru/disk/1027682001/%D0%A1%D0%BE%D1%80%D1%86%D1%8B%20%D0%B8%20%D0%B4%D0%BE%D0%BA%D0%B8.rar.html
2. Операционная система реального времени, используемая в устройстве (смотрим снизу в верх, изначально она на ассемблере, потом Ди переписал на си) http://easyelectronics.ru/tag/rtos
3. Терминальные программы http://easyelectronics.ru/terminalnye-programmy.html
Tags:
Hubs:
+194
Comments 84
Comments Comments 84

Articles