Friday, August 9, 2013

Playing with the HackRF - Keyfobs

* Go here for Part 2, Processing the signal with GNU Radio Companion.

The HackRF is on Kickstarter, so here's a little write-up of some of the awesome stuff you can do with it, and why you should go get one.

I've been wanting to play around with my car keyfobs for a while; mostly out of idle curiosity.  They're something everyone has, and tend to put out a nice clean signal.  For my testing I used a 2006 Subaru keyfob, and my friend's Kia keyfob - they use different encodings, so this is a fairly interesting test.

Beyond academic interest, keyfobs may not be that useful to play with - they (should) all use rolling codes so that a captured unlock sequence can't be used more than once.  I wouldn't assume they've all implemented this properly, of course, as some conferences and speakers are pointing out.  It's definitely an area where more research is needed.

To start with, I did some searching to find out what frequency they operate at.  It turns out Kia runs at 315MHz, while Toyota and Subaru run at 433.847MHz (for many models, at least).

So, now we know where to look - what do we do with that?  First off, I fired up GQRX to get an idea of what was going on; pressing the keyfob returned data (sorry, don't have a screenshot handy of that specifically, but here's an example of gqrx capturing OOK data):

So we know something is going on; now we want to record it.  The quickest way to do this is hackrf_transfer:

$ hackrf_transfer -r -f 312000000 -s 8000000

Notice we're capturing slightly off-center from 315MHz.  Since we're capturing 8MHz wide, we'll get our signal, but by offsetting from center we avoid the DC offset which causes a spike at the center of the HackRF tuning band.  Recent firmware has made this significantly smaller, but it's still easy to avoid entirely, so lets do that.

Leave hackrf_transfer running for a while, while pressing the lock (or unlock) button on your key fob a few times.  After a few seconds, hit control-c to stop capturing.

I suggest naming your files carefully - the capture data itself is raw IQ samples, and doesn't contain any indication of how wide the samples are (8M), what the center frequency is (312MHz), or how it's encoded (for HackRF, all the captures are 8bit).  Better to name it properly to begin with.

Now we've got a big file of capture data; what can we do with it?  One good tool is baudline - ...  Unfortunately the licensing terms for baudline prevent its inclusion in distributions, so you'll need to download it independently.

There are a few quirks with baudline - one of them is that it doesn't handle large files (over about 50MB) very well, another is that it runs "file" (the file autotype detection) on any file you provide for it.  This is slow, so a quick fix for that is to alter your $PATH variable before launching baudline.  My baudline launcher script looks like:

export PATH=~/bin/disable:$PATH

and ~/bin/disable/ contains a "file" script which contains simply:

exit 0

So we need to make a smaller file for baudline - fortunately, "dd" handles this very well.  To get the first 50MB of the sample file,

$ dd bs=1M count=50

To get subsequent blocks of the file,

$ dd bs=1M count=50 skip=50

The tool "split" can also be used to break the file up into parts.

So we've got a smaller chunk of our file, let's open it up in baudline.  Use right-click, "input->open file..." then select the file and set the format to "raw" instead of "auto detect".  That will get you:

The magic settings here are:

"custom" sample rate of 8M (since we captured at 8MHz / 8M samples wide).  If you captured at another rate, like 20MHz, put 20000000 here.

"channels" are 2, since hackrf logs I and Q data.  Since we're logging IQ, turn on the "quadrature" checkbox, and since HackRF logs differently than baudline expects, turn on "flip complex".

Finally, since HackRF logs unsigned 8bit samples, click "8 bit linear (unsigned)".

Now we get a fancy display, like:

And we get a nice display of our signal; in this screenshot it's a Subaru keyfob using OOK (on-off-keying) encoding, basically morse code (transmit or not transmit)

Using the Kia keyfob, we get a FSK (frequency-shift-keying) signal, zoomed in we see:

What do we see here?  After the transmitter turns on, it sends a pattern of 'off' and 'on' signals, basically '101010101010'.  Look back at the Subaru OOK capture - it also sends a consistent '1010101010' pattern at the beginning.  This preamble tells the receiver that real packet data is coming, and helps us see how long the duty cycle is for 'on' and 'off'.

Scrolling further down the Kia FSK signal, we see it begin to send real data:

Which is the actual FSK encoded bits being sent to the car to unlock.

Finally, in baudline, it's possible to select a range and save it to a file:

by selecting the range, and right clicking to "output->export selection as..."

Beware, baudline changes the format of the file when you export it.  This will affect loading the file in the future, and any gnuradio code you develop to process the file in the future.  A less destructive option is to use 'dd' to trim the file (just make some guesses at the size) and load it into baudline.

To re-open a file you've saved from baudline, use the following settings:

Specifically, the file changes from 8bit unsigned to 16bit, and no longer needs the "flip complex" checkbox set.

Next post, I'll load a HackRF sample file into Gnuradio-Companion and try to extract the ASK bitstream directly.

* Go here for Part 2, Processing the signal with GNU Radio Companion.


  1. Nice use of baudline. Just curious, what is the baudrate of the Kia FSK signal?

    Here are some tips that might fix some of the baudline issues you experienced:

    * Baudline has a maximum file size limit of 64M samples when using a 2048 point FFT. You can double that max file size if you use a 4096 point FFT, quadruple it if you use a 8192 point FFT, ...

    * Baudline's Raw Parameters window does use the "file -zbL $name" command for magic hints but I've never experienced it to be slow. It's runtime speed is almost instantaneous so I suspect either your Linux distribution is doing something bad or a major performance bug has been introduced into the file.c codebase. BTW your /bin/disable/file solution is pretty clever.

    * Baudline saves files in a 16-bit .wav format which will take double the space of your original 8-bit data. Gzip is fast and it can compress out that extra zero bit space. Baudline can also automatically load .gz files. It's not a perfect solution but it is a way around the file inflation.

    * The text layout in the Raw Parameters window looks squished. Baudline needs some fonts that your Linux distribution doesn't install by default. The baudline FAQ has some font installation instructions that may help:

    * Instead of loading the 8-bit file into baudline, an alternate method is to use stdin and stream it in. Something like:

    cat | baudline -stdin -quadrature -channels 2 -flipcomplex -format u8 -samplerate 8000000 -overlap 100 -memory 256

    Then just switch baudline into record mode and watch the RF data stream in. You can also stream live real-time data into baudline with the -stdin command option. Modify your hackrf_transfer program to save sample data to stdout or do something like:

    nc rfcat_server 1900 | baudline -stdin -samplerate 8000000 -format u8

  2. The entire sample including preamble is about 270mS, the preamble is about 160mS of that.

    Re: gzip - yeah, not a bad idea, I've actually had good luck with xz and raw samples. Fortunately, once trimmed, the signals are pretty small. Unfortunately, afaik, gnuradio won't read compressed files automatically, and that's stage 2.

    Re: Streaming data, we could also use named pipes to accomplish that, but the amount of data the hackrf can generate (8 to 20MSamps) will swamp it pretty quickly.

  3. Unix fifos work well too and are very fast. I don't think swamping baudline will be a problem. HackRF's 20 Msample/sec output is a lot less than this 1.5 Gsample/sec stdin capture benchmark:

    Imagine what a newer and faster computer could do?

  4. I was referring more to overflowing the amount baudline likes to load before it blows up; I'm still hitting the problem where more than 50-60 meg in a file and baudline doesn't enjoy loading it.

    While not decked out as a gaming system, I'm running a very modern laptop - system resources aren't in general an issue.

  5. The maximum file size that baudline can load is a function of FFT size. A 2048 point FFT imposes a max 64Msample file size. Try using a larger FFT size.

    Did you know that options on the command line will be inserted automatically in the Raw Parameters window? Something like:

    baudline -quadrature -channels 2 -flipcomplex -format u8 -samplerate 8000000

    This will save you from having to manually configure those settings everytime. You could run this from a script.