PS/2 keyboard sniffer

I’m not sure I’m posting in the right place, but I know you’ll tell me if I’m wrong :slight_smile:

So, it’s taken a few days of experimenting, but I’m able to write a simple sniffer that watches PS/2 traffic from the keyboard to the PC. This works on the basis that each packet is 11 bytes long - I simply read the data line whenever the clock line falls, and on the 11th (last) time I output the value to my PC.

The problem is when the PC sends data to the keyboard (like an LED command); since the PC has to change the clock line in order to get into the request-to-send state, obviously my simple clock-line watching logic isn’t going to work anymore.

Obviously I need to somehow monitor the clock line to know when it’s going into the request-to-send state, but I’m not sure how I’d do that? Since (according to the docs) I can’t use millis() inside the ISR, how do I tell how long the clock line has been low for ?

int asynchclock = 3; // ps2 pin 5 - black wire - interrupt 1
int synchdata = 4; // ps2 pin 1 - white wire - no interrupt

volatile int kbd_data;
volatile byte kbd_ptr;
byte scan;

void setup()
{
Serial.begin(57600);
pinMode(ledPin, OUTPUT);
pinMode(asynchclock,INPUT);
pinMode(synchdata,INPUT);
attachInterrupt(1,fallingclock,FALLING);
}

void loop()
{

kbd_ptr = 0;
kbd_data = 0;
for (;:wink: // ever
{
/* if we’ve captured a complete keystroke then send it to the host
*/
if (11 == kbd_ptr)
{
scan = (kbd_data >> 1) & 0xFF;
Serial.println(scan,HEX);
kbd_ptr = 0;
kbd_data = 0;
}
}
}

void fallingclock()
{
// clock line falling, so kbd->host data
kbd_data = kbd_data | (digitalRead(synchdata) << kbd_ptr);
kbd_ptr ++ ;
}

Interesting problem. Your code is very simple, but in order to handle bi-directional PS/2 protocol, it’s going to get more complex.

A couple of approaches to try:

  • instead of using FALLING interrupt state, use the CHANGE mode. That way you get an interrupt on both falling and rising clocks. Measure the time between the rise and fall and you know how long the clock line was held. Depending on which way the data is flowing, you can then read bits on either low or high side of the clock.
  • pulseIn might be a way to measure how long the clock interval is. Of course then you are not using interrupts and will have to be careful about when to read the data line.
  • you could use a ‘man in the middle’ setup, where the clock lines from the host and device are wired to separate pins on the arduino. The software would have to echo the clock state to the other pin, but you would be able to detect whether it was the device or host yanking the clock line. But then the keyboard wouldn’t work if the sniffer was off.

Note that on host-to-device packets the device sends and ACK bit. I’d also add code to detect invalid packets (bad start/stop/parity bit) and to timeout if the whole packet isn’t sent.
If you haven’t seen it already, this web page has really good info on PS/2 protocol:
http://www.computer-engineering.org/ps2protocol/
And SparkFun has a similar device to count keystrokes, you might get some hints from it:

HTH

  • instead of using FALLING interrupt state, use the CHANGE mode. That way you get an interrupt on both falling and rising clocks.

Yes, I think I came to that same conclusion as well!
Today’s theory is that I can look for the data line to go low - since the data always starts with a start bit of 0, I can tell which way the data is going based on the clock line, and I’ll just count 11 clock changes to get the data.
Capturing device->host data is pretty easy, it’s host->device stuff (like LED commands) which is causing me issues at the moment.

  • you could use a ‘man in the middle’ setup, where the clock lines from the host and device are wired to separate pins on the arduino. The software would have to echo the clock state to the other pin, but you would be able to detect whether it was the device or host yanking the clock line. But then the keyboard wouldn’t work if the sniffer was off.

There’s already commercial devices out there that do that, and they don’t work that well - I have one collecting dust in my desk drawer. I know it’s possible to do this by just ‘sniffing’ the lines because I have one that works, the problem is that the manufacturer isn’t around anymore.

Just as an update for anyone else who ever does anything with a PS/2 port, there are machines out there which have non-standard keyboard controllers.

The main differences I’ve found so far:

  • the clock line is driven from host (not the keyboard)
  • the clock line runs constantly (even when the keyboard is idle)
  • the data line does not necessarily return to high when idle (most important when trying to detect that the host has entered ‘request-to-send’ state)

The annoying thing is that the keyboards attached to these controllers still work fine, so you have no way of knowing whether you have one or not until you hook a scope up to it.