mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-07-07 05:31:17 -07:00
Add command and code for bidirectional LF emulation of Hitag2. Should be extended for other types of tags
This commit is contained in:
parent
802a36162a
commit
0fa9ca5b53
6 changed files with 215 additions and 2 deletions
|
@ -14,6 +14,7 @@ THUMBSRC = start.c \
|
||||||
iso15693.c \
|
iso15693.c \
|
||||||
util.c \
|
util.c \
|
||||||
version.c \
|
version.c \
|
||||||
|
hitag2.c \
|
||||||
usb.c
|
usb.c
|
||||||
|
|
||||||
# These are to be compiled in ARM mode
|
# These are to be compiled in ARM mode
|
||||||
|
|
|
@ -645,6 +645,9 @@ void UsbPacketReceived(BYTE *packet, int len)
|
||||||
case CMD_VERSION:
|
case CMD_VERSION:
|
||||||
SendVersion();
|
SendVersion();
|
||||||
break;
|
break;
|
||||||
|
case CMD_LF_SIMULATE_BIDIR:
|
||||||
|
SimulateTagLowFrequencyBidir(c->ext1, c->ext2);
|
||||||
|
break;
|
||||||
#ifdef WITH_LCD
|
#ifdef WITH_LCD
|
||||||
case CMD_LCD_RESET:
|
case CMD_LCD_RESET:
|
||||||
LCDReset();
|
LCDReset();
|
||||||
|
|
|
@ -74,6 +74,7 @@ void AcquireRawBitsTI(void);
|
||||||
void SimulateTagLowFrequency(int period, int ledcontrol);
|
void SimulateTagLowFrequency(int period, int ledcontrol);
|
||||||
void CmdHIDsimTAG(int hi, int lo, int ledcontrol);
|
void CmdHIDsimTAG(int hi, int lo, int ledcontrol);
|
||||||
void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol);
|
void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol);
|
||||||
|
void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen);
|
||||||
|
|
||||||
/// iso14443.h
|
/// iso14443.h
|
||||||
void SimulateIso14443Tag(void);
|
void SimulateIso14443Tag(void);
|
||||||
|
|
201
armsrc/lfops.c
201
armsrc/lfops.c
|
@ -6,6 +6,7 @@
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
#include <proxmark3.h>
|
#include <proxmark3.h>
|
||||||
#include "apps.h"
|
#include "apps.h"
|
||||||
|
#include "hitag2.h"
|
||||||
#include "../common/crc16.c"
|
#include "../common/crc16.c"
|
||||||
|
|
||||||
void AcquireRawAdcSamples125k(BOOL at134khz)
|
void AcquireRawAdcSamples125k(BOOL at134khz)
|
||||||
|
@ -61,6 +62,10 @@ void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,BYT
|
||||||
{
|
{
|
||||||
BOOL at134khz;
|
BOOL at134khz;
|
||||||
|
|
||||||
|
/* Make sure the tag is reset */
|
||||||
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||||
|
SpinDelay(2500);
|
||||||
|
|
||||||
// see if 'h' was specified
|
// see if 'h' was specified
|
||||||
if(command[strlen((char *) command) - 1] == 'h')
|
if(command[strlen((char *) command) - 1] == 'h')
|
||||||
at134khz= TRUE;
|
at134khz= TRUE;
|
||||||
|
@ -77,6 +82,8 @@ void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,BYT
|
||||||
|
|
||||||
// Give it a bit of time for the resonant antenna to settle.
|
// Give it a bit of time for the resonant antenna to settle.
|
||||||
SpinDelay(50);
|
SpinDelay(50);
|
||||||
|
// And a little more time for the tag to fully power up
|
||||||
|
SpinDelay(2000);
|
||||||
|
|
||||||
// Now set up the SSC to get the ADC samples that are now streaming at us.
|
// Now set up the SSC to get the ADC samples that are now streaming at us.
|
||||||
FpgaSetupSsc();
|
FpgaSetupSsc();
|
||||||
|
@ -95,11 +102,12 @@ void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,BYT
|
||||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER);
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER);
|
||||||
}
|
}
|
||||||
LED_D_ON();
|
LED_D_ON();
|
||||||
if(*(command++) == '0')
|
if(*(command++) == '0') {
|
||||||
SpinDelayUs(period_0);
|
SpinDelayUs(period_0);
|
||||||
else
|
} else {
|
||||||
SpinDelayUs(period_1);
|
SpinDelayUs(period_1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||||
LED_D_OFF();
|
LED_D_OFF();
|
||||||
SpinDelayUs(delay_off);
|
SpinDelayUs(delay_off);
|
||||||
|
@ -478,6 +486,195 @@ void SimulateTagLowFrequency(int period, int ledcontrol)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Provides a framework for bidirectional LF tag communication
|
||||||
|
* Encoding is currently Hitag2, but the general idea can probably
|
||||||
|
* be transferred to other encodings.
|
||||||
|
*
|
||||||
|
* The new FPGA code will, for the LF simulator mode, give on SSC_FRAME
|
||||||
|
* (PA15) a thresholded version of the signal from the ADC. Setting the
|
||||||
|
* ADC path to the low frequency peak detection signal, will enable a
|
||||||
|
* somewhat reasonable receiver for modulation on the carrier signal
|
||||||
|
* that is generated by the reader. The signal is low when the reader
|
||||||
|
* field is switched off, and high when the reader field is active. Due
|
||||||
|
* to the way that the signal looks like, mostly only the rising edge is
|
||||||
|
* useful, your mileage may vary.
|
||||||
|
*
|
||||||
|
* Neat perk: PA15 can not only be used as a bit-banging GPIO, but is also
|
||||||
|
* TIOA1, which can be used as the capture input for timer 1. This should
|
||||||
|
* make it possible to measure the exact edge-to-edge time, without processor
|
||||||
|
* intervention.
|
||||||
|
*
|
||||||
|
* Arguments: divisor is the divisor to be sent to the FPGA (e.g. 95 for 125kHz)
|
||||||
|
* t0 is the carrier frequency cycle duration in terms of MCK (384 for 125kHz)
|
||||||
|
*
|
||||||
|
* The following defines are in carrier periods:
|
||||||
|
*/
|
||||||
|
#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */
|
||||||
|
#define HITAG_T_1_MIN 24 /* T[1] should be 26..30 */
|
||||||
|
#define HITAG_T_EOF 40 /* T_EOF should be > 36 */
|
||||||
|
#define HITAG_T_WRESP 208 /* T_wresp should be 204..212 */
|
||||||
|
|
||||||
|
static void hitag_handle_frame(int t0, int frame_len, char *frame);
|
||||||
|
//#define DEBUG_RA_VALUES 1
|
||||||
|
#define DEBUG_FRAME_CONTENTS 1
|
||||||
|
void SimulateTagLowFrequencyBidir(int divisor, int t0)
|
||||||
|
{
|
||||||
|
#if DEBUG_RA_VALUES || DEBUG_FRAME_CONTENTS
|
||||||
|
int i = 0;
|
||||||
|
#endif
|
||||||
|
char frame[10];
|
||||||
|
int frame_pos=0;
|
||||||
|
|
||||||
|
DbpString("Starting Hitag2 emulator, press button to end");
|
||||||
|
hitag2_init();
|
||||||
|
|
||||||
|
/* Set up simulator mode, frequency divisor which will drive the FPGA
|
||||||
|
* and analog mux selection.
|
||||||
|
*/
|
||||||
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_SIMULATOR);
|
||||||
|
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor);
|
||||||
|
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
|
||||||
|
RELAY_OFF();
|
||||||
|
|
||||||
|
/* Set up Timer 1:
|
||||||
|
* Capture mode, timer source MCK/2 (TIMER_CLOCK1), TIOA is external trigger,
|
||||||
|
* external trigger rising edge, load RA on rising edge of TIOA, load RB on rising
|
||||||
|
* edge of TIOA. Assign PA15 to TIOA1 (peripheral B)
|
||||||
|
*/
|
||||||
|
|
||||||
|
PMC_PERIPHERAL_CLK_ENABLE = (1 << PERIPH_TC1);
|
||||||
|
PIO_PERIPHERAL_B_SEL = (1 << GPIO_SSC_FRAME);
|
||||||
|
TC1_CCR = TC_CCR_CLKDIS;
|
||||||
|
TC1_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 | TC_CMR_ETRGEDG_RISING | TC_CMR_ABETRG |
|
||||||
|
TC_CMR_LDRA_RISING | TC_CMR_LDRB_RISING;
|
||||||
|
TC1_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
|
||||||
|
|
||||||
|
/* calculate the new value for the carrier period in terms of TC1 values */
|
||||||
|
t0 = t0/2;
|
||||||
|
|
||||||
|
int overflow = 0;
|
||||||
|
while(!BUTTON_PRESS()) {
|
||||||
|
WDT_HIT();
|
||||||
|
if(TC1_SR & TC_SR_LDRAS) {
|
||||||
|
int ra = TC1_RA;
|
||||||
|
if((ra > t0*HITAG_T_EOF) | overflow) ra = t0*HITAG_T_EOF+1;
|
||||||
|
#if DEBUG_RA_VALUES
|
||||||
|
if(ra > 255 || overflow) ra = 255;
|
||||||
|
((char*)BigBuf)[i] = ra;
|
||||||
|
i = (i+1) % 8000;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(overflow || (ra > t0*HITAG_T_EOF) || (ra < t0*HITAG_T_0_MIN)) {
|
||||||
|
/* Ignore */
|
||||||
|
} else if(ra >= t0*HITAG_T_1_MIN ) {
|
||||||
|
/* '1' bit */
|
||||||
|
if(frame_pos < 8*sizeof(frame)) {
|
||||||
|
frame[frame_pos / 8] |= 1<<( 7-(frame_pos%8) );
|
||||||
|
frame_pos++;
|
||||||
|
}
|
||||||
|
} else if(ra >= t0*HITAG_T_0_MIN) {
|
||||||
|
/* '0' bit */
|
||||||
|
if(frame_pos < 8*sizeof(frame)) {
|
||||||
|
frame[frame_pos / 8] |= 0<<( 7-(frame_pos%8) );
|
||||||
|
frame_pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
overflow = 0;
|
||||||
|
LED_D_ON();
|
||||||
|
} else {
|
||||||
|
if(TC1_CV > t0*HITAG_T_EOF) {
|
||||||
|
/* Minor nuisance: In Capture mode, the timer can not be
|
||||||
|
* stopped by a Compare C. There's no way to stop the clock
|
||||||
|
* in software, so we'll just have to note the fact that an
|
||||||
|
* overflow happened and the next loaded timer value might
|
||||||
|
* have wrapped. Also, this marks the end of frame, and the
|
||||||
|
* still running counter can be used to determine the correct
|
||||||
|
* time for the start of the reply.
|
||||||
|
*/
|
||||||
|
overflow = 1;
|
||||||
|
|
||||||
|
if(frame_pos > 0) {
|
||||||
|
/* Have a frame, do something with it */
|
||||||
|
#if DEBUG_FRAME_CONTENTS
|
||||||
|
((char*)BigBuf)[i++] = frame_pos;
|
||||||
|
memcpy( ((char*)BigBuf)+i, frame, 7);
|
||||||
|
i+=7;
|
||||||
|
i = i % sizeof(BigBuf);
|
||||||
|
#endif
|
||||||
|
hitag_handle_frame(t0, frame_pos, frame);
|
||||||
|
memset(frame, 0, sizeof(frame));
|
||||||
|
}
|
||||||
|
frame_pos = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
LED_D_OFF();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DbpString("All done");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hitag_send_bit(int t0, int bit) {
|
||||||
|
if(bit == 1) {
|
||||||
|
/* Manchester: Loaded, then unloaded */
|
||||||
|
LED_A_ON();
|
||||||
|
SHORT_COIL();
|
||||||
|
while(TC1_CV < t0*15);
|
||||||
|
OPEN_COIL();
|
||||||
|
while(TC1_CV < t0*31);
|
||||||
|
LED_A_OFF();
|
||||||
|
} else if(bit == 0) {
|
||||||
|
/* Manchester: Unloaded, then loaded */
|
||||||
|
LED_B_ON();
|
||||||
|
OPEN_COIL();
|
||||||
|
while(TC1_CV < t0*15);
|
||||||
|
SHORT_COIL();
|
||||||
|
while(TC1_CV < t0*31);
|
||||||
|
LED_B_OFF();
|
||||||
|
}
|
||||||
|
TC1_CCR = TC_CCR_SWTRG; /* Reset clock for the next bit */
|
||||||
|
|
||||||
|
}
|
||||||
|
static void hitag_send_frame(int t0, int frame_len, const char const * frame, int fdt)
|
||||||
|
{
|
||||||
|
OPEN_COIL();
|
||||||
|
PIO_OUTPUT_ENABLE = (1 << GPIO_SSC_DOUT);
|
||||||
|
|
||||||
|
/* Wait for HITAG_T_WRESP carrier periods after the last reader bit,
|
||||||
|
* not that since the clock counts since the rising edge, but T_wresp is
|
||||||
|
* with respect to the falling edge, we need to wait actually (T_wresp - T_g)
|
||||||
|
* periods. The gap time T_g varies (4..10).
|
||||||
|
*/
|
||||||
|
while(TC1_CV < t0*(fdt-8));
|
||||||
|
|
||||||
|
int saved_cmr = TC1_CMR;
|
||||||
|
TC1_CMR &= ~TC_CMR_ETRGEDG; /* Disable external trigger for the clock */
|
||||||
|
TC1_CCR = TC_CCR_SWTRG; /* Reset the clock and use it for response timing */
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i=0; i<5; i++)
|
||||||
|
hitag_send_bit(t0, 1); /* Start of frame */
|
||||||
|
|
||||||
|
for(i=0; i<frame_len; i++) {
|
||||||
|
hitag_send_bit(t0, !!(frame[i/ 8] & (1<<( 7-(i%8) ))) );
|
||||||
|
}
|
||||||
|
|
||||||
|
OPEN_COIL();
|
||||||
|
TC1_CMR = saved_cmr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback structure to cleanly separate tag emulation code from the radio layer. */
|
||||||
|
static int hitag_cb(const char* response_data, const int response_length, const int fdt, void *cb_cookie)
|
||||||
|
{
|
||||||
|
hitag_send_frame(*(int*)cb_cookie, response_length, response_data, fdt);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Frame length in bits, frame contents in MSBit first format */
|
||||||
|
static void hitag_handle_frame(int t0, int frame_len, char *frame)
|
||||||
|
{
|
||||||
|
hitag2_handle_command(frame, frame_len, hitag_cb, &t0);
|
||||||
|
}
|
||||||
|
|
||||||
// compose fc/8 fc/10 waveform
|
// compose fc/8 fc/10 waveform
|
||||||
static void fc(int c, int *n) {
|
static void fc(int c, int *n) {
|
||||||
BYTE *dest = (BYTE *)BigBuf;
|
BYTE *dest = (BYTE *)BigBuf;
|
||||||
|
|
|
@ -50,6 +50,7 @@ typedef struct {
|
||||||
#define CMD_HID_DEMOD_FSK 0x020B
|
#define CMD_HID_DEMOD_FSK 0x020B
|
||||||
#define CMD_HID_SIM_TAG 0x020C
|
#define CMD_HID_SIM_TAG 0x020C
|
||||||
#define CMD_SET_LF_DIVISOR 0x020D
|
#define CMD_SET_LF_DIVISOR 0x020D
|
||||||
|
#define CMD_LF_SIMULATE_BIDIR 0x020E
|
||||||
|
|
||||||
// For the 13.56 MHz tags
|
// For the 13.56 MHz tags
|
||||||
#define CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693 0x0300
|
#define CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693 0x0300
|
||||||
|
|
|
@ -663,6 +663,15 @@ static void CmdLosim(char *str)
|
||||||
SendCommand(&c, FALSE);
|
SendCommand(&c, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void CmdLosimBidir(char *str)
|
||||||
|
{
|
||||||
|
UsbCommand c;
|
||||||
|
c.cmd = CMD_LF_SIMULATE_BIDIR;
|
||||||
|
c.ext1 = 47; /* Set ADC to twice the carrier for a slight supersampling */
|
||||||
|
c.ext2 = 384;
|
||||||
|
SendCommand(&c, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
static void CmdLoread(char *str)
|
static void CmdLoread(char *str)
|
||||||
{
|
{
|
||||||
UsbCommand c;
|
UsbCommand c;
|
||||||
|
@ -2834,6 +2843,7 @@ static struct {
|
||||||
{"loread", CmdLoread, 0, "['h'] -- Read 125/134 kHz LF ID-only tag (option 'h' for 134)"},
|
{"loread", CmdLoread, 0, "['h'] -- Read 125/134 kHz LF ID-only tag (option 'h' for 134)"},
|
||||||
{"losamples", CmdLosamples, 0, "[128 - 16000] -- Get raw samples for LF tag"},
|
{"losamples", CmdLosamples, 0, "[128 - 16000] -- Get raw samples for LF tag"},
|
||||||
{"losim", CmdLosim, 0, "Simulate LF tag"},
|
{"losim", CmdLosim, 0, "Simulate LF tag"},
|
||||||
|
{"losimbidir", CmdLosimBidir, 0, "Simulate LF tag (with bidirectional data transmission between reader and tag)"},
|
||||||
{"ltrim", CmdLtrim, 1, "<samples> -- Trim samples from left of trace"},
|
{"ltrim", CmdLtrim, 1, "<samples> -- Trim samples from left of trace"},
|
||||||
{"mandemod", Cmdmanchesterdemod, 1, "[i] [clock rate] -- Manchester demodulate binary stream (option 'i' to invert output)"},
|
{"mandemod", Cmdmanchesterdemod, 1, "[i] [clock rate] -- Manchester demodulate binary stream (option 'i' to invert output)"},
|
||||||
{"manmod", Cmdmanchestermod, 1, "[clock rate] -- Manchester modulate a binary stream"},
|
{"manmod", Cmdmanchestermod, 1, "[clock rate] -- Manchester modulate a binary stream"},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue