RFID 仿真/模拟/监控/拦截/检测/嗅探器

本文详细介绍了各种RFID技术工具,包括基于声音卡的RFID监听器/模拟器、PicNic HF RFID模拟器和PicNic的多个模块。探讨了使用AVR作为RFID标签的方法,强调了简化设计的重要性。此外,还涵盖了RFID的指令时序、状态机、硬件组件和安全影响。内容还包括不同类型的RFID欺骗器、模拟器和相关项目的源代码,以及关于ProxClone和蓝牙适配器等项目的介绍。该文对于理解RFID工作原理、安全挑战及构建相关项目非常有用。
摘要由CSDN通过智能技术生成

Sound card based RFID sniffer/emulator 

(Too tired after recon.cx to do draw the schematics better than that :) Stay tuned for the next version including Tag emulation.)

PicNic - yet another emulator/spyware for HF RFID

Abstract

Rather than a final construction, this is just an experimental kit addressed to anybody, who wants to play with e.g. MIFARE chip emulation etc. It has two main parts – HW module which is a simple microcontroller driven HF RFID transceiver and a particular SW module which does the particular job (fake UID sending, terminal spying, MIFARE hacking, etc.). The modules currently available in public are listed bellow. Of course, it is definitely up to you to build your own HW/SW modules basing on some of those already existing. Remember – this is a kit... it all started as a demo project for my students, then I decided to put it here - just for an inspiration. 

The name PicNic itself explain the main design paradigm and also tells why I did construct „yet another emulator/spyware“. The word “Pic” comes from the name of the microcontroller family being used, while “Nic” comes from the Czech word for “Nothing”. So, PicNic = PIC and „Nothing else around it“. Thus, in opposite to the other designs available on the internet which often try to be as robust as possible, I searched for a minimal design possible. I do not say I have reached the absolute minimum, but I feel I am close enough. 

The whole design is addressed to people with a moderate knowledge of radio electronics, PIC programming, and RFID technology. 

Documentation

Rosa, T.: PicNic for HF RFIDSanta’s Crypto Get-together 2008, Rump Session presentation, Prague 

Klima, V. and Rosa, T.: PicNic pro RFID-KV, Sdelovaci technika, 1/2009

(this article is in Czech, I hope I will find a time to translate it; nevertheless, the scheme and short notes  presented bellow together with source code comments in assembler files should be enough to understand how it works and how to build it up)

HW design

 

Note on the PIC clock source

The microcontroller oscillator is driven by X-tal with parallel resonant frequency at 13.56 MHz. We should use a parallel cut with a load capacitance approx. 30 pF. Using the same frequency as of the basic carrier, we can get easily synchronized with the RFID terminal on the very basic level of the instruction flow. 

Note on the magnetic antenna design

The analog part uses magnetic antenna which is dictated by the fact that HF RFID operates in the near field range where the inductive coupling is reasonable. Rather than a particular antenna type, a general design rules are presented here. The antenna is composed of the main coil, the serial resistor, and the resonant capacitor. From the electromagnetic field viewpoint, the coil should be constructed as 4 to 15 turns in a plane around a surface similar to a general ISO card. From the circuit viewpoint, the inductance should be kept at moderate level (tents of uH at maximum) as we need to set the resonant capacitor accordingly. The well known Thomson formula can be used as an estimate here:

f = 1/[2*Pi*sqrt(LC)]

Furthermore, we must care about the quality (the Q factor) of the antenna as it limits the transceiver bandwidth. Let us say we need 1.7 MHz bandwidth (as the subcarrier for ISO 14443A is 847.5 kHz), then we can use the following estimation for the serial resistance:

R > 3.4*Pi*10^6*L

We keep the value of R_ANT as low as possible otherwise, as we do not want to mute the antenna too much.

A classical diode AM detector is coupled to the antenna directly.

Its output is then fed to the analog input RA1.

An internal comparator is working there which output is fed to TMR0 and CCP modules of PIC.

TMR0 is used to “de-modify” the modified Miller encoding used by ISO 14443A for terminal data transmission.

The CCP module is used as a capture register for TMR1 which is then used to synchronize the time frame for the emulator response

(cf. ISO 14443-3A for the detailed requirements).

Another part attached to the antenna circuit is the load modulator.

It is composed of a Graetz bridge which load impedance is driven by an induced N-channel MOSFET.

Note that using this technology over a classical bipolar transistor turned out to be necessary

due to a poor switching characteristic of the bipolar one when driven by an asymmetric input source at 847.5 kHz.

On the other hand, the gate saturation voltage affects the minimum power requirements of the whole PicNic.

If we want to go bellow 4.5 V, a different part with a lower saturation voltage should be considered.

The whole modulation impedance is mainly resistive and the resistance can be adjusted by R13 (we start at the value of 100 Ohms typically).

The MOSFET itself is driven by RB5 digital output. 

Note on the external communication

The design expects an USART connection to the outside world as well. We shall not forget to do a proper signal conversion, of course.

To connect a RS232 of PC, we can use, for instance, a well known MAX232 convertor of Maxim.

We can also use the FTDI chips if we want a straight forward connection via USB.

I also have a very good experience with the Bluetooth serial adaptor OEMSPA311i of ConnectBlue.

The particular communication speed etc. is up to the particular SW module.

As the clock frequency is the same as of the basic carrier,

we can derive the (13.56*10^6/128) bps (approx. 106 kbps) or (13.56*10^6/64) bps (approx. 212 kbps) easily.

The question is, of course, what we can set on the receiver’s part.

It is worth noting that FTDI controllers are equipped with so-called rational (or sub-integer) divider

which allows quite precise setup for wide range of communication speeds. This is another reason why using the FTDI chips is quite reasonable. 

The signals used for the communication are Rx, Tx, and DTR.

There is a circuit (cf. the scheme and the source code initialization) that performs automatic hard reset of the PIC on each DTR signal change.

The simple scheme works mainly because of the PORTA pins going to the high impedance state with the falling edge

on MCLR and the possibility to ex-or the comparator output with a user defined bit value.

The serial line is planned mainly for sniffing and monitoring purpose.

Details on how the particular firmware employs the serial line can be found in the particular source code comments… 

Crypto1 enhancement

To support MIFARE Classic attacks on schemes relying both on UID and crypto memory access,

the version with Crypto1 coprocessor support is currently under construction.

The coprocessor itself is a (V)HDL design (many thanks to Jiri Bucek <jiri_dot_bucek (at) fit_dot_cvut_dot_cz> for his vital help and patience)

targeted for a simple CPLD devices. In particular, the (a bit obsolete)Digilent’s Digilab XCRP circuit board featuring the Xilinx XCR3064XL is used, now. 

Current status of the project:

-          VHDL design: OK

-          VHDL testbench: OK

-          PicNic interconnection: OK

-          Key load procedure: OK

-          UID and n_T load procedure: OK

-          MIFARE Classic login procedure: OK

-          MIFARE Classic simple read procedure: OK

-          Access control reader protocol: sometimes fails – to be further tested 

Some pictures of the whole prototype design can be found here: 

device detailinner sideouter box.

ALPHA codes snapshot - just to get an idea (interconnection with CPLD can be derived from the sources).

http://crypto.hyperlink.cz/files/FakeUIDCrypto_1_0_alpha.zip 

(Ab)Using the NFC-WI/S2C interface (SAM-PicNic)

This interface is supported by several NFC front ends of NXP, mainly by the PN511 and PN512.

Note that these chips are also embedded in the PN531, PN532, and PN533 controllers,

since those devices are in fact composed of PN511 (or PN512) plus certain 80C51-based microcontroller core.

The NFC-WI/S2C interface main purpose is to allow using the core analog Tx/Rx path for attaching the so-called Secure Module (SAM).

The Secure Module can be, for instance, a somehow stripped MIFARE Classic chip (only its digital part is used). 

The interface provides mainly the following five signals: power source/referenceGNDSIGOUTSIGIN, and SIC_CLK

The SIGOUT is an output providing modified Miller encoded data (the particular analog signal appearance depends on the particular registry configuration of the controller). The SIGIN is an input expecting Manchester coded signal with the subcarrier of 847.5 kHz (modulation signal of the transponder). The SIC_CLK provides recovered basic carrier signal of 13.56 MHz. Some other signals can be added to this interface, provided these are useful for the specific purpose. For instance, there is the CLAD signal allowing the SAM to inform the controller about start/end of the SAM RF-activity. Details can be found in the technical documentation of NXP. It can be obtained from NXP under NDA. In time of writing this notice, however, a copy of it appeared in the doc lib at www.proxmark.org

The idea of attaching the PicNic is that simple:

-          use SIGOUT in place of the antenna/demodulator output

-          use SIGIN in place of MOSFET load modulator input

-          use SIC_CLK as a clock source for the PIC16F648A (as it supports external clock signal allowing even the DC idle state!); yes(!)

– the choice of designing the PicNic firmware as for the PIC having the clock signal the same as the RF basic carrier finally pays off here. 

The main benefits of this setup should be:

-          much better timing synchronization of the PicNic Rx and Tx (mainly the Tx-after-RX frame delay)

-         possibility to simply “implant” PicNic into existing NFC devices; this could allow using existing antenna and low-level analog design as well as hiding the PicNic into, for instance, an NFC equipped mobile phone etc. 

Current status of the project:

-          prepare a demo board for SAM-PicNic experiments: OK

  It is based on SCL3710 dongle. The interesting signals of PN531 have been drawn outside the original PCB layout. Cf. overall demo board design or SCL3710 interconnection details. The SCL3710 is driven by the libnfc library via USB channel.

-          research the interconnection details: to be done (basing on the demo board measurements)

-          redesign PicNic fw (minor changes are expected): to be done

-          verify the real PicNic implantation: to be done 

SW modules

FakeUID 1.3.2 – emulator of ISO14443A anticollison handshake for 4 bytes long UID. Certain support for data gathering for MIFARE Classic cryptanalysis is also added (see comments in the source code). 

FakeUID 1.4-beta – supports ISO14443A UID of all lengths (4/7/10 bytes) and presents a redesigned Tx synchronization to allow easier application protocol extension (see comments in the source code). 

FakeUID 1.4 – final version, minor changes done and some bugs corrected. 

FakeUID 1.5-beta – adds a better support for the DESFire emulation. Allows capturing of terminal response for the Authenticate command. This enables offline key guessing attacks. Furthermore, it adds support for the R(NAK) – R(ACK) ping-pong which is used by some readers (e.g. CardMan 5321) to check the presence of a transponder in the terminal field. The ATS support is added as well. 

FakeUID 1.5-beta OpenCard Special Edition –

the exact experimental version employed for OpenCard partial emulation in applications relaying only on static transponder data (mainly UID).

Surprisingly, such applications were already found!

This firmware was used to show that building security of RFID applications on UID-only approach is really very, very bad idea.

The whole story (in Czech) is captured here. Some technical details were discussed in this lecture (CZ). 

Recommended reading

[1] Finkenzeller K.: RFID Handbook, John Wiley & Sons Ltd., 2nd edition, 2004

[2] Lee, Y.: Antenna Circuit Design for RFID Applications, Microchip Tech. Inc., 2003

[3] PIC16F627A/628A/648A Data Sheet, Microchip Tech. Inc., 2007 

 

USING AN AVR AS AN RFID TAG

 

Experiments in RFID, continued…

Last time, I posted an ultra-simple “from scratch” RFID reader, which uses no application-specific components: just a Propeller microcontroller and a few passive components. This time, I tried the opposite: building an RFID tag using no application-specific parts.

Well, my solution is full of dirty tricks, but the results aren’t half bad. I used an Atmel AVR microcontroller (the ATtiny85) and a coil. That’s it. You can optionally add a couple of capacitors to improve performance with some types of coils, but with this method it’s possible to build a working RFID tag just by soldering a small inductor to an AVR chip:

The above prototype emulates an EM4102-style tag- a very popular style of low-frequency RFID tag which stores a 40-bit unique ID. I can read my bogus ID value (0x12345678AB) using Parallax’s RFID reader. Below is another prototype, with a larger coil and a couple of capacitors for added range and stability. It is programmed to emulate a HID prox card, a simple FSK-modulated tag with a 44-bit payload. I can read this card successfully with my garage door opener. This one is a little large to conveniently carry around, but a smaller AVR package should help.

So, the shiny electrical tape is beautiful, but how does this thing even work? The power pins on the microcontroller aren’t even connected!

As I said, this makes use of several dirty tricks:

  • The coil actually powers the AVR through two of its I/O pins. Nearly every chip out there has clamping diodes on its I/O pins, which prevent voltages on that pin from rising above the chip’s supply voltage or sinking below ground. These diodes are useful for arresting static discharge.When you first hold the RFID tag up to a reader, the chip has no power- the supply voltage is zero. When the coil starts to pick up power from the RFID reader, these two I/O pins are presented with a sine wave, a few volts in amplitude. Anywhere that sine wave exceeds the supply voltage, some energy is diverted from the coil to the chip’s supply rails, via the clamping diode. The end result is that the chip is powered, and the coil’s sine wave is truncated. The top and bottom of the sine have been chopped off, and it looks a lot more like a square wave now.
  • Power filtering using the AVR’s die capacitance. In the smaller prototype, there is no power filtering capacitor at all. In fact, the power is filtered by the internal capacitance of the power planes in the AVR’s silicon die. This isn’t much, but it makes the power supply stable enough that we can execute code even though the supply is pulsing at 125 kHz.
  • Very low voltage operation. This particular ATtiny85 chip is specified for operation at voltages as low as 2.5v. The extended voltage range version (I didn’t have any of these handy) is specified down to 1.8v. But I’m running these AVRs at barely over 1 volt. At these voltages, the normal AVR clock oscillators don’t work- but I can get away with this because of the next hack…
  • The coil is the AVR’s clock source. The inductor isn’t just hooked up to any I/O pin: it’s actually connected to the AVR’s clock input. Remember the square-ish wave we’re left with after the clamping diodes suck away some power? That waveform is now our clock input. The microcontroller is executing code at 125 kHz, in lockstep with the RFID reader’s carrier wave.
  • Firmware? What firmware? At such low speeds, the chip’s firmware looks less like a program, and more like a sequence of I/O operations to perform in sync with each carrier clock cycle. There aren’t a lot of cycles to spare. In the EM4102 protocol, you could potentially do some useful work with the 32 clock cycles you have between each bit. With the HID protocol, though, you need to output an FSK edge as often as once every 4 clock cycles. As a result, the firmware on the RFID tag is extremely dumb. The “source code” is really just a set of fancy assembler macros which convert an RFID tag code into a long sequence of I/O instructions.

The fact that this thing works at all is quite a testament to the robust design of the AVR. The latest AVRFID source is in Subversion, as usual.

/*
 * Software-only implementation of a passive low-frequency RFID tag,
 * using an AVR microcontroller.
 *
 * Version 1.1, 2010-06-15
 *
 * Copyright (c) 2008-2010 Micah Dowty <micah@navi.cx>
 * See end of file for license terms. (BSD style)
 * Improved HID modulation contributed by Luke Koops <luke.koops@gmail.com>
 * HID parity bit support contributed by Cesar Fernandez <cex123@gmail.com>
 *
 * Supports EM4102-style tags, and the HID 125 kHz prox card protocol.
 * The card format and ID number are set below, with #defines.
 *
 * Basic schematic: * * ATtiny85 * +--------------+ * --| RST Vcc |-- * +- L1 ----| B3/CLKI SCK |-- * +---------| B4 MISO |-- * --| GND MOSI |-- * +--------------+ * * L1 is about 1 mH. It and the AVR are the only components. * All other pins should be unconnected. * * AVR notes: * * - Low-voltage parts are better, but I've had success using * this with the non-extended voltage range parts as well. * * - Program the fuses for an external clock with no divider. * On the ATtiny85, this means setting lfuse to 0xC0. * Note that after you set this fuse, your programmer will * need to supply a clock on pin 2 for subsequent programming * operations. * * Optional parts: * * - Power decoupling capacitor, between 0.1 and 10uF. * Bigger is generally better, as it will increase the * usable range- but if you use this tag with readers that * have a pulsed carrier wave, bigger caps may take too long * to charge. * * - A load capacitor, in parallel with L1. This will depend * on your coil. For physically small coils, the internal * capacitance of the AVR seems to be enough. For larger coils, * it may be necessary to use a cap here. Experiment to find the * best value. * * - A header, for in-circuit programming. You'll need to expose nearly * every pin on the chip, since the AVR will also need an external * clock. * * - If you want to make an active (powered) tag, you could hook a 3V * battery up to the Vcc and GND pins on the AVR. To decrease the power * usage when idle, you may want to hook a large (a couple megohm) * pull-down resistor to the clock input, to be sure CLKI doesn't float * when there is no RF field present. * * Theory of operation: * * Like all passive RFID tags, this circuit is powered by the 125 kHz * carrier wave emitted by the RFID reader. In our case, the coil is * just connected to two AVR I/O pins. We're actually powering the AVR * through its protective clamping diodes, and the power is retained by * the AVR die's internal capacitance. * * This is a very weak power source, and the AVR typically gets little * over a volt of Vcc. As a result, most of the AVR's oscillators won't * start. We can, however, use the carrier wave itself as a clock as well. * This also makes the software easy, since the instruction clock is also * the RF clock. We're already clamping the coil voltage into something * resembles a square wave, so this makes a good external clock source. * * To send data back to the reader, passive RFID tags can selectively * attenuate the reader's carrier wave. Most RFID tags do that with a * transistor which shorts their coil. We accomplish this by driving the * coil I/O pins to ground, by toggling the DDRB register. Since the I/O * drivers on the AVR are weaker than the RF signal, we still get enough * of a pulse to provide the CLKI input. * * And that's about all there is to it. The software is quite simple- we * are mostly just using assembler macros to convert the desired RFID tag * code into sequences of subroutine calls which output bits. We can't * get too fancy with the software, since it's only executing at 125 kHz. * */ /************ Configuration *****************************************/ // Uncomment exactly one format: #define FORMAT_IS_EM4102 //#define FORMAT_IS_HID // For the EM4102: An 8-bit manufacturer ID and 32-bit unique ID. #define EM4102_MFR_ID 0x12 #define EM4102_UNIQUE_ID 0x3456789A /* * For the HID card: * A 20-bit manufacturer code, 8-bit site code, and 16-bit unique ID, 1-bit odd parity. * * Manufacturer code is fixed. If modified, HID readers do not recognise the tag. * (This may also be a kind of fixed header.) Tested on HID readers with 26-bit wiegand output. */ #define HID_MFG_CODE 0x01002 // Do not modify #define HID_SITE_CODE 0x9F #define HID_UNIQUE_ID 1326 // May be written on the back of the card /************ Common ************************************************/ #ifndef __ASSEMBLER__ #define __ASSEMBLER__ #endif #include <avr/io.h> .global main #define OUT_PINS _BV(PINB3) | _BV(PINB4) .macro delay cycles .if \cycles > 1 rjmp .+0 delay (\cycles - 2) .elseif \cycles > 0 nop delay (\cycles - 1) .endif .endm .macro manchester bit, count=1 .if \count manchester (\bit >> 1), (\count - 1) .if \bit & 1 baseband_1 baseband_0 .else baseband_0 baseband_1 .endif .endif .endm .macro stop_bit baseband_0 baseband_1_last .endm /************ EM4102 Implementation *********************************/ /* * The common EM4102 cards use Manchester encoding, at a fixed rate of * 64 RF clocks per bit. This means 32 clock cycles per half-bit (baseband * code). There are a total of 64 manchester-encoded bits per packet. 40 * of these are payload, 9 bits are header (all ones) and one bit is a stop * bit (zero). All other bits are parity, with one row parity bit every * 4 bits, and four column parity bits at the end of the packet. */ #ifdef FORMAT_IS_EM4102 #define ROW_PARITY(n) ( (((n) & 0xF) << 1) | \ (((n) ^ ((n) >> 1) ^ ((n) >> 2) ^ ((n) >> 3)) & 1) ) #define COLUMN_PARITY ( (EM4102_MFR_ID >> 4) ^ \ (EM4102_MFR_ID) ^ \ (EM4102_UNIQUE_ID >> 28) ^ \ (EM4102_UNIQUE_ID >> 24) ^ \ (EM4102_UNIQUE_ID >> 20) ^ \ (EM4102_UNIQUE_ID >> 16) ^ \ (EM4102_UNIQUE_ID >> 12) ^ \ (EM4102_UNIQUE_ID >> 8) ^ \ (EM4102_UNIQUE_ID >> 4) ^ \ (EM4102_UNIQUE_ID) ) main: .macro baseband_0 rcall baseband30_0 rjmp .+0 .endm .macro baseband_1 rcall baseband30_1 rjmp .+0 .endm .macro baseband_1_last rcall baseband30_1 rjmp main .endm .macro header manchester 0x1FF, 9 .endm header manchester ROW_PARITY(EM4102_MFR_ID >> 4), 5 manchester ROW_PARITY(EM4102_MFR_ID >> 0), 5 manchester ROW_PARITY(EM4102_UNIQUE_ID >> 28), 5 manchester ROW_PARITY(EM4102_UNIQUE_ID >> 24), 5 manchester ROW_PARITY(EM4102_UNIQUE_ID >> 20), 5 manchester ROW_PARITY(EM4102_UNIQUE_ID >> 16), 5 manchester ROW_PARITY(EM4102_UNIQUE_ID >> 12), 5 manchester ROW_PARITY(EM4102_UNIQUE_ID >> 8), 5 manchester ROW_PARITY(EM4102_UNIQUE_ID >> 4), 5 manchester ROW_PARITY(EM4102_UNIQUE_ID >> 0), 5 manchester COLUMN_PARITY, 4 stop_bit /* * Emit a 0 at the baseband layer. * Takes a total of 30 clock cycles, including call overhead. */ baseband30_0: ldi r16, OUT_PINS // 1 rjmp baseband30 // 2 /* * Emit a 1 at the baseband layer. * Takes a total of 30 clock cycles, including call overhead. */ baseband30_1: ldi r16, 0 // 1 rjmp baseband30 // 2 /* * Internal routine for baseband32_0 and _1. Must use * a total of 24 clock cycles. (32 - 1 ldi - 2 rjmp - 3 rcall) */ baseband30: out _SFR_IO_ADDR(DDRB), r16 // 1 delay 19 // 19 ret // 4 #endif /* FORMAT_IS_EM4102 */ /************ HID Implementation *********************************/ /* * This works with the HID 125 kHz prox cards I've tested it with, * but there are undoubtedly other formats used by HID. My cards are * marked with the model number "HID 0004H". * * These cards use both manchester encoding and FSK modulation. The FSK * modulation represents zeroes and ones using 4 and 5 full RF cycles, respectively. * An entire baseband bit lasts 50 RF cycles. * * Each packet begins with a header consisting of the baseband bit pattern "000111". * After that, we have 45 manchester-encoded bits before the packet repeats. The * last bit appears to be a stop bit, always zero. The previous 20 bits encode the * 6-digit unique ID, which is printed on the back of the card. The other 24 bits * have an unknown use. They could be a site code or manufacturing code. In the cards * I've examined, these bits are constant. */ #ifdef FORMAT_IS_HID #define ODD_PARITY(n) ((( ((n) >> 0 ) ^ ((n) >> 1 ) ^ ((n) >> 2 ) ^ ((n) >> 3 ) ^ \ ((n) >> 4 ) ^ ((n) >> 5 ) ^ ((n) >> 6 ) ^ ((n) >> 7 ) ^ \ ((n) >> 8 ) ^ ((n) >> 9 ) ^ ((n) >> 10) ^ ((n) >> 11) ^ \ ((n) >> 12) ^ ((n) >> 13) ^ ((n) >> 14) ^ ((n) >> 15) ^ \ ((n) >> 16) ^ ((n) >> 17) ^ ((n) >> 18) ^ ((n) >> 19) ^ \ ((n) >> 20) ^ ((n) >> 21) ^ ((n) >> 22) ^ ((n) >> 23) ^ \ ((n) >> 24) ^ ((n) >> 25) ^ ((n) >> 26) ^ ((n) >> 27) ^ \ ((n) >> 28) ^ ((n) >> 29) ^ ((n) >> 30) ^ ((n) >> 31) ) & 1) ^ 1) main: eor r16, r16 ldi r17, OUT_PINS loop: /* * Toggle the output modulation, in the specified number * of total clock cycles. */ .macro toggle clocks delay (\clocks - 2) eor r16, r17 out _SFR_IO_ADDR(DDRB), r16 .endm /* * Emit a 0 at the baseband layer. (Toggle every 4 cycles, for 50 cycles) * There was an rjmp that got us to the beginning of the loop, so drop * 2 cycles from the delay if this is the first bit. That will give the * appropriate delay before the toggle. * * From observing the HID card, each 0 bit is either 48 or 52 cycles. * The length alternates to keep the average at 50. This keeps the * waveform smooth, and keeps each bit in its 50 cycle time slot. * * We don't have time for a function call, so we just chew * up lots of flash... */ .macro baseband_0 .if startloop toggle 2 // 4 .equ startloop, 0 .else toggle 4 // 4 .endif toggle 4 // 8 toggle 4 // 12 toggle 4 // 16 toggle 4 // 20 toggle 4 // 24 toggle 4 // 28 toggle 4 // 32 toggle 4 // 36 toggle 4 // 40 toggle 4 // 44 toggle 4 // 48 .if evenzero .equ evenzero, 0 .else toggle 4 // 52 .equ evenzero, 1 .endif .endm /* * Emit a 1 at the baseband layer. (Toggle every 5 cycles, for 50 cycles) */ .macro baseband_1 .if startloop toggle 3 // 4 .equ startloop, 0 .else toggle 5 // 4 .endif toggle 5 // 10 toggle 5 // 15 toggle 5 // 20 toggle 5 // 25 toggle 5 // 30 toggle 5 // 35 toggle 5 // 40 toggle 5 // 45 toggle 5 // 50 .endm .macro header .equ evenzero, 0 .equ startloop, 1 baseband_0 baseband_0 baseband_0 baseband_1 baseband_1 baseband_1 .endm /* * This should add up to 45 bits. * * Some cards may use different 45-bit codes: For example, * a Wiegand code, or something more site-specific. But the * cards that I've seen use a 20-bit manufacturer code, * 8-bit site code, 16-bit unique ID, and a single parity bit. * * If your card uses ad ifferent coding scheme, you can add, * remove, and modify these 'manchester' macros. Just make sure * the result adds up to the right number of bits. */ header manchester HID_MFG_CODE, 20 manchester HID_SITE_CODE, 8 manchester HID_UNIQUE_ID, 16 manchester ODD_PARITY(HID_MFG_CODE ^ HID_SITE_CODE ^ HID_UNIQUE_ID), 1 rjmp loop #endif /* FORMAT_IS_HID */ /*****************************************************************/ /* * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ 

AVR RFID, OPTIMIZED AND PORTED TO C

Way back in 2008, I posted a writeup about using an AVR microcontroller as an RFID tag. Since then, it’s been great to see many people pick up this code and build their own DIY RFID tags.

In my original project, I was just interested in using an AVR as a way of emulating any tag protocol I wanted, even proprietary protocols like the HID cards that are so common for door entry. But a general purpose microcontroller really lends itself to making even more interesting tags. For example, imagine an action figure that has different poses which trigger microswitches that can be read by the AVR. It could report a different RFID code depending on which pose the action figure is in. This kind of very low-power physical computing is really interesting to me.

Trammell Hudson recently took a big step in this direction, in the name of creating a “multipass” card which could stay in his pocket and pretend to be any number of other cards. His original idea didn’t quite work out, due to limitations in the HID readers. But along the way, he created an optimized version of the AVRFID firmware which uses much less flash memory, and he ported it to C so that it can be more easily extended and modified.

He made this posible by very carefully choosing the instructions in the inner loops, creating a state machine that just barely fits within the available clock cycles:

One issue with programming HID Prox compatible cards is that the AVR’s RCALL and RET instructions are quite slow — 3 and 4 clocks respectively — so making a function call and returning from it requires seven clocks and would cause errors in the RF waveform. To get around this, Beth expanded all of the code inline to produce a single function that bit-bangs the coil loading with NOP‘s between each cycle. The 20-bit manufacturer ID (0x01002), 8-bit faciity code and 16-bit unique ID, all Manchester encoded, required 80 instructions per bit for a total of 3700 instructions out of the Tiny85′s maximum of 4096. Supporting 34-bit cards would not be possible with this design, much less multiple card IDs!

While RCALL/RET are out of the question, I noticed that IJMP is only 2 clocks. This means that the CPU can do an indirect jump to the value in the 16-bit Z register in enough time to be ready for the next FSK cycle. If we know where to go, that is… The LPM instruction takes three cycles to read a byte from flash into a register, which just barely fits during the idle time during a FSK baseband one. Loading the Z register for LPM takes at least two clocks (since it is really the two 8-bit registers r31:r30), which means the pgm_read_word() macro in avr/progmem.h won’t work. While the rest of the firmware is in mostly normal C, I resorted to writing assembly to interleave the coil toggling with the operations to determine the next output state and make the appropriate jump. If you want to follow along, the source for the RFID firmware is available in rfid/avrfid2.c.

His post covers a lot of ground, including how to connect an off-the-shelf HID card reader to a computer, and how to repeatedly program the AVR using a Bus Pirate.

Go check out the full article already!

/** \file
 * AVR RFID card.
 *
 * Based on avrfrid.S by Beth at Scanlime.
 * http://scanlime.org/2008/09/using-an-avr-as-an-rfid-tag/
 *
 * Normal C code doesn't really work here since we are limited to
 * very small number of cycles per bit.  The HID Prox cards are
 * FSK modulated with only four or five RF cycles per baseband cycle.
 * Since the AVR RCALL and RET instructions take four clocks each
 * we would miss all of our timing constaints if we tried to make those calls.
 *
 * However, the IJMP only takes 2 clock cycles, so we can build a state
 * machine and use it to make "function calls".  LPM also takes three
 * clocks, so we can't load a full address and jump to it within the
 * timing constraint, but we can split these operations across the
 * ten 5-cycle transitions during sending a baseband 1. 
 *
 * Each of these transitions takes 2 cycles for the XOR and OUT to
 * set the state, which leaves three cycles for our work.
 *
 * when programming with buspirate (wire/clip):
 *
 *    White/white   Reset 1  v  8 Vcc   Red
 *    Blue/Blue     Xtal1 2     7 SCK   Purple/green
 *                  Xtal2 3     6 MISO  Black/Black
 *    Black         Gnd   4     5 MOSI  Gray/Yellow
 *
 */
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sfr_defs.h>

static void manchester_0(void);
static void manchester_1(void);
static void hid_header(void); static void hid_reset(void); //int main(void); #define HID_MFG_CODE 0x01002 // Do not modify #define HID_SITE_CODE 42 #define HID_UNIQUE_ID 23946 // May be written on the back of the card #define HID_HEADER "2" #define HID_RESET "3" static const char hid_bits[] PROGMEM __attribute__((__used__)) = { #if 1 HID_HEADER "0000" "0001" "0000" "0000" "0010" // HID Manufacturer code 0x01002 for n1002 cards? "00101010" // Site code 42 "01011101" "10001010" // ID 23946 "0" // parity #else HID_HEADER "0001" "0000" "0000" "0000" "0010" // HID Manufacturer code, trying 10002 for 34-bit? "00000000" // fc-12 "00001100" "00111000" // id 14371 "00100011" "0" #endif HID_RESET }; typedef void (*state_function)(void); static const state_function state_handlers[] PROGMEM __attribute__((__used__)) = { manchester_0, manchester_1, hid_header, hid_reset, }; #if 0 // HID manufacturer code (20 bits) == 0x01002 _0, _0, _0, _0, _0, _0, _0, _1, _0, _0, _0, _0, _0, _0, _0, _0, _0, _0, _1, _0, // Facility code (8 bits) == 42 _0, _0, _1, _0, _1, _0, _1, _0, // ID (16 bits) == 23946 _0, _1, _0, _1, _1, _1, _0, _1, _1, _0, _0, _0, _1, _0, _1, _0, // Parity _0, // And return to the header when we're done hid_header }; #endif /** Use r16 and r17 to track the state of the pins. * * These are hard coded in toggle_raw(). */ volatile register uint8_t r16 __asm__("r16"); volatile register uint8_t r17 __asm__("r17"); /** r15 tracks which bit are we currently sending. * * This is hard coded in hid_header(). */ volatile register uint8_t bit_num __asm__("r15"); /** Jump to what ever has been stored into Z (r31:r30) * * PC <- Z * 2 clocks */ static inline void __attribute__((__noreturn__)) ijmp(void) { __asm__ __volatile__("ijmp"); while(1); // make gcc happy } /** * Delay a specific number of clock cycles. * * rjmp is 2 clocks, nop is 1. * * So do one nop if the delay is an odd value and then rjmp's for n/2 * to maximize code density. Doesn't matter for the state machine version, * but otherwise the straight-code version would overflow the 8 KB space. */ static inline void __attribute__((__always_inline__)) delay( const uint8_t n ) { switch (n/2) { case 8: asm("rjmp .+0"); case 7: asm("rjmp .+0"); case 6: asm("rjmp .+0"); case 5: asm("rjmp .+0"); case 4: asm("rjmp .+0"); case 3: asm("rjmp .+0"); case 2: asm("rjmp .+0"); case 1: asm("rjmp .+0"); case 0: break; } if (n % 2 == 1) asm("nop"); } /** Toggle the output pins to change the coil state. * * The DDRB pins are used to short the coil, which causes * an increase in current draw at the reader. * * 2 clocks. */ asm( ".macro toggle\n" "eor r16, r17\n" "out 0x17, r16\n" // _SFR_IO_ADDR(DDRB) ".endm\n" ); static void __attribute__((__always_inline__)) toggle_raw(void) { __asm__ __volatile__("toggle"); } /** Toggle the state of the output pins and delay for some clocks. * * The toggle_raw() takes 2 clocks, so we delay for the remainder. */ static void __attribute__((__always_inline__)) toggle( const uint8_t n ) { toggle_raw(); if (n > 2) delay(n-2); } #define ZERO_FREQ 4 #define ONE_FREQ 5 /** Send a 0 at the baseband layer. * * If delay_slot is set, the delays after the last FSK slot will not be * done, instead allowing the caller to make use of three extra clock * cycles for their own usage. */ static void __attribute__((__always_inline__)) baseband_0( uint8_t delay_slot ) { toggle(ZERO_FREQ); // 4 toggle(ZERO_FREQ); // 8 toggle(ZERO_FREQ); // 12 toggle(ZERO_FREQ); // 16 toggle(ZERO_FREQ); // 20 toggle(ZERO_FREQ); // 24 toggle(ZERO_FREQ); // 28 toggle(ZERO_FREQ); // 32 toggle(ZERO_FREQ); // 36 toggle(ZERO_FREQ); // 40 toggle(ZERO_FREQ); // 44 toggle(delay_slot ? ZERO_FREQ : 0); // 48 } /** Send a 1 at the baseband layer. * * This is only used by the header during setup since it must send * several 1 bits in a row. Only the last one computes the next state. * There are no delay slots following this function. */ static void __attribute__((__always_inline__)) baseband_1(void) { toggle(ONE_FREQ); // 5 toggle(ONE_FREQ); // 10 toggle(ONE_FREQ); // 15 toggle(ONE_FREQ); // 20 toggle(ONE_FREQ); // 25 toggle(ONE_FREQ); // 30 toggle(ONE_FREQ); // 35 toggle(ONE_FREQ); // 40 toggle(ONE_FREQ); // 45 toggle(ONE_FREQ); // 50 } /** Send a 1 at the baseband layer. * * Interleaved with the FSK are the operations to load the next * function pointer. Once the function "returns", the Z register * will contain the address of the next function in the state machine. * * This was too difficult to write in C and have gcc output the correct * stream of instructions. Instead it is in inline assembly. * The rough translation into C: * * toggle 5 * z = &hid_bits[bit_num]; * toggle 10 * next_state = lpm(z); * toggle 15 * next_state = (next_state - '0') * 2 * toggle 20 * z = &state_handlers[next_state]; * toggle 25 * next_func_lo = lpm(z++); * toggle 30 * next_func_hi = lpm(z++); * toggle 35 * z = next_func_hi << 8 | next_func_lo; * toggle 40 * bit_num++; * toggle 45 * delay * toggle 50 * No delay (leave these free for caller) */ static void __attribute__((__always_inline__)) baseband_1_load(void) { __asm__ __volatile__( "toggle /* 5 */\n" "ldi r30, lo8(hid_bits)\n" "ldi r31, hi8(hid_bits)\n" "add r30, %0\n" "toggle /* 10 */\n" "lpm r24, Z\n" "toggle /* 15 */\n" "ldi r30, lo8(state_handlers)\n" "ldi r31, hi8(state_handlers)\n" "nop\n" "toggle /* 20 */\n" "subi r24, '0'\n" "lsl r24\n" "add r30, r24\n" "toggle /* 25 */\n" "lpm r24, Z+\n" "toggle /* 30 */\n" "lpm r31, Z\n" "toggle /* 35 */\n" "mov r30, r24\n" "rjmp .+0\n" "toggle /* 40 */\n" "inc %0\n" "rjmp .+0\n" "toggle /* 45 */\n" "nop\n" "rjmp .+0\n" "toggle /* 50 */\n" "/* Leave slot free */\n" : "=r" (bit_num) // 0  ); } /** Send the HID header start bits. * * The HID header is an illegal state in the Manchester encoding * used to indicate the start of the packet. * * The last baseband 1 will load the first state machine function * pointer and jump into the statemachine. */ static void hid_header(void) { baseband_0(1); baseband_0(1); baseband_0(1); baseband_1(); baseband_1(); baseband_1_load(); delay(1); ijmp(); } /** Output a manchester 0. * * Output a baseband 0, followed by a baseband 1. * During the baseband 1 the Z register will be updated * to contain the pointer to the next function in the state machine. * * After the 1, with one delay slot since ijmp() takes two clocks, * we jump to the next state. */ static void manchester_0(void) { baseband_0(1); baseband_1_load(); delay(1); ijmp(); } /** Output a manchester 1. * * Output a baseband 1, followed by a baseband 0. * During the baseband 1 the Z register will be updated * to contain the pointer to the next function in the state machine. * * After the 0, with no delay slots since ijmp() takes two clocks, * we jump to the next state. */ static void manchester_1(void) { baseband_1_load(); delay(3); // 3 delays slots remain baseband_0(0); ijmp(); } /** Restart the state machine at state 0. * * This must be the last state in the machine and is the first one * called from main to kick things off. */ static void hid_reset(void) { // We will start in state 0, so the next to read is 1 // gcc keeps optimizign writes to r15 out for some reason  __asm__ __volatile__( "eor %0, %0\n" "inc %0\n" : "=r"(bit_num) ); __asm__ __volatile__("rjmp hid_header"); } /** Entry point at 0x0. * * Since we linking with -nostdlib, main needs to be at 0x0. * The easiest way to force that with the default linker script * is to put it in the .vectors text section. */ int __attribute__((section(".vectors"))) main(void) { r16 = 0; r17 = _BV(PINB3) | _BV(PINB4); hid_reset(); /* Never returns */ }

AVR RFID Multipass

I was inspired by 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值