Skip to content

Commit 727a009

Browse files
committed
process bits before emulating them, implement them with bitwise operations
1 parent fd112e3 commit 727a009

File tree

7 files changed

+323
-0
lines changed

7 files changed

+323
-0
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,17 @@ External RX options:
4242
- Some read-head directly connected to GPIO, ADC'd, and parsed all on the Flipper. Likely the most compact and cheapest module option, but also would require the most work.
4343
- USB HID input likely infeasible; seems the FZ cannot act as an HID host.
4444

45+
## arha-bitwise branch todo & notes
46+
Attempting to exploit flipper hardware to some extent
47+
48+
- [X] Preprocess all MSR data into bitwise arrays, including manchester encoding.
49+
- [ ] Feed bits from timers
50+
- [ ] Sync to the lfrfid timer and experiment representing a field flip with a few cycles of a high frequency carrier. Perhaps mag readerfrontends will lowpass such signals, and keep only the low frequency component, in an attempt to drown out nearby noise
51+
- [ ] Can the CC1101 radio be used in any way? Driving it from GD0 can be done in 50us, or about 10khz. Probably more with sync/packet mode
52+
- [ ] Can the 5V pin act as a coil driver? I've read reports it can drive 0.4A, other reports it can drive 2A. It boils down to bq25896 being fast enough. Ref: bq25896_enable_otg, which will probably need bypassing kernel libs and calling furi_hal_i2c_tx/furi_hal_i2c_tx whatever calls from Cube libs.
53+
- [ ] Investigate transparent mode on 3916
54+
- [ ] Can the piezo be used at its resonant frequency? I've seen LF signals being emulated with nothing but headphones at a subharmonic; and the wheel brake on some carts seems to also work with audiofreq signals (or the RF emission from driving a speaker)
55+
4556
----
4657
## Credits
4758
This project interpolates work from [Samy Kamkar's original MagSpoof project](https://github.com/samyk/magspoof), [dunaevai135 & AlexYaro's Flipper hackathon project](https://github.com/dunaevai135/flipperzero-firmware), and the Flipper team's [LF RFID](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/main/lfrfid) and [SubGhz](https://github.com/flipperdevices/flipperzero-firmware/tree/dev/applications/main/subghz) apps.

assets/samy-javier-branch.mag

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Filetype: Flipper Mag device
2+
Version: 1
3+
# Mag device track data
4+
# found in samy magspoof branch f150bb783237051fba7e4e6ed96a722e542a9663, using as test data, card is long expired
5+
Track 1: %B493173000682759^URISTA HDZ-IVAN JAVIER ^150220100234000000?
6+
Track 2: ;493173000682759=15022100000234?
7+
Track 3:

assets/samy.mag

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Filetype: Flipper Mag device
2+
Version: 1
3+
# Mag device track data
4+
# Track 1: %B123456781234567^LASTNAME/FIRST^YYMMSSSDDDDDDDDDDDDDDDDDDDDDDDDD?
5+
Track 1: %B426684131234567^LASTNAME/FIRST^YYMMSSSDDDDDDDDDDDDDDDDDDDDDDDDD?
6+
# Track 2: ;123456781234567=YYMMSSSDDDDDDDDDDDDDD?
7+
Track 2: ;426684131234567=230188855555555555555?
8+
Track 3:

helpers/mag_helpers.c

+277
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,21 @@ const uint8_t bitlen[] = {7, 5, 5};
1717
const int sublen[] = {32, 48, 48};
1818
uint8_t bit_dir = 0;
1919

20+
void bitbang_raw(bool value, MagSetting* setting)
21+
{
22+
switch(setting->tx) {
23+
case MagTxStateRFID:
24+
furi_hal_gpio_write(RFID_PIN_OUT, value);
25+
break;
26+
case MagTxStateGPIOA6A7:
27+
furi_hal_gpio_write(GPIO_PIN_A, value);
28+
furi_hal_gpio_write(GPIO_PIN_B, !value);
29+
break;
30+
default:
31+
break;
32+
}
33+
}
34+
2035
void play_bit_rfid(uint8_t send_bit, MagSetting* setting) {
2136
// internal TX over RFID coil
2237
bit_dir ^= 1;
@@ -214,6 +229,139 @@ void track_to_bits(uint8_t* bit_array, const char* track_data, uint8_t track_ind
214229
//furi_string_free(tmp_str);
215230
}
216231

232+
void mag_spoof_bitwise(Mag* mag) {
233+
MagSetting* setting = mag->setting;
234+
235+
236+
FuriString* ft1 = mag->mag_dev->dev_data.track[0].str;
237+
FuriString* ft2 = mag->mag_dev->dev_data.track[1].str;
238+
239+
char* data1; char* data2;
240+
data1 = malloc(furi_string_size(ft1)+1);
241+
data2 = malloc(furi_string_size(ft2)+1);
242+
strncpy(data1, furi_string_get_cstr(ft1), furi_string_size(ft1));
243+
strncpy(data2, furi_string_get_cstr(ft2), furi_string_size(ft2));
244+
245+
if(furi_log_get_level() >= FuriLogLevelDebug) {
246+
debug_msr_string(data1, BITS_TRACK1, OFFSET_TRACK1);
247+
debug_msr_string(data2, BITS_TRACK2, OFFSET_TRACK2);
248+
}
249+
250+
251+
uint8_t bits_t1_raw[64] = {0x55}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits
252+
uint8_t bits_t1_manchester[128] = {0x55}; // twice the above
253+
uint16_t bits_t1_count = msr_encode(data1, (uint8_t*) bits_t1_manchester, (uint8_t*) bits_t1_raw, BITS_TRACK1, OFFSET_TRACK1);
254+
uint8_t bits_t2_raw[64] = {0x55}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits
255+
uint8_t bits_t2_manchester[128] = {0x55}; // twice the above
256+
uint16_t bits_t2_count = msr_encode(data2, (uint8_t*) bits_t2_manchester, (uint8_t*) bits_t2_raw, BITS_TRACK2, OFFSET_TRACK2);
257+
258+
if(furi_log_get_level() >= FuriLogLevelDebug) {
259+
printf("Manchester bitcount: T1: %d, T2: %d\r\n", bits_t1_count, bits_t2_count);
260+
261+
printf("T1 raw: ");
262+
for (int i = 0; i < bits_t1_count / 16; i++) printf("%02x ", bits_t1_raw[i]);
263+
printf("\r\n");
264+
265+
printf("T1 manchester: ");
266+
for (int i = 0; i < bits_t1_count / 8; i++) printf("%02x ", bits_t1_manchester[i]);
267+
printf("\r\n");
268+
269+
printf("T2 raw: ");
270+
for (int i = 0; i < bits_t2_count / 16; i++) printf("%02x ", bits_t2_raw[i]);
271+
printf("\r\n");
272+
273+
printf("T2 manchester: ");
274+
for (int i = 0; i < bits_t2_count / 8; i++) printf("%02x ", bits_t2_manchester[i]);
275+
printf("\r\n");
276+
277+
printf("Bitwise emulation done\r\n\r\n");
278+
}
279+
280+
if(!tx_init(setting)) return;
281+
282+
FURI_CRITICAL_ENTER();
283+
bool bit = false;
284+
285+
286+
if((setting->track == MagTrackStateAll))
287+
for(uint16_t i = 0; i < ZERO_PREFIX; i++)
288+
{
289+
bit ^= 0xFF;
290+
bitbang_raw(bit, setting);
291+
furi_delay_us(setting->us_clock*2);
292+
}
293+
294+
if((setting->track == MagTrackStateAll) || (setting->track == MagTrackStateOne))
295+
for(uint16_t i = 0; i < bits_t1_count; i++)
296+
{
297+
uint8_t byte = i / 8;
298+
uint8_t bitmask = 1 << (7-(i % 8));
299+
/* this comment is mostly for zw's convenience:
300+
*
301+
* bits are stored in their arrays like on a card (LSB first). This is not how usually bits are stored in a
302+
* byte, with the MSB first. the var bitmask creates the pattern to iterate through each bit, LSB first, like so
303+
* 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x80... masking bits one by one from the current byte
304+
*
305+
* i've chosen this LSB approach since bits and bytes are hard enough to visualize with the 5/8 and 7/8 encoding
306+
* MSR uses. It's a biiit more complicated to process, but visualizing it with printf or a debugger is
307+
* infinitely easier
308+
*
309+
* Encoding the following pairs of 5 bits as 5/8: A1234 B1234 C1234 D1234
310+
* using this LSB format looks like: A1234B12 34C1234D 12340000
311+
* using the MSB format, looks like: 21B4321A D4321C43 00004321
312+
* this means reading each byte backwards when printing/debugging, and the jumping 16 bits ahead, reading 8 more
313+
* bits backward, jumping 16 more bits ahead.
314+
*
315+
* I find this much more convenient for debugging, with the tiny incovenience of reading the bits in reverse
316+
* order. THus, the reason for the bitmask above
317+
*/
318+
319+
bit = !!(bits_t1_manchester[byte] & bitmask);
320+
321+
// TODO: reimplement timing delays. Replace fixed furi_hal_cortex_delay_us to wait instead to a specific value
322+
// for DWT->CYCCNT. Note timer is aliased to 64us as per
323+
// #define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) | furi_hal_cortex.c
324+
325+
bitbang_raw(bit, setting);
326+
furi_delay_us(setting->us_clock);
327+
// if (i % 2 == 1) furi_delay_us(setting->us_interpacket);
328+
}
329+
330+
if((setting->track == MagTrackStateAll))
331+
for(uint16_t i = 0; i < ZERO_BETWEEN; i++)
332+
{
333+
bit ^= 0xFF;
334+
bitbang_raw(bit, setting);
335+
furi_delay_us(setting->us_clock*2);
336+
}
337+
338+
if((setting->track == MagTrackStateAll) || (setting->track == MagTrackStateTwo))
339+
for(uint16_t i = 0; i < bits_t2_count; i++)
340+
{
341+
uint16_t j = bits_t2_count - i - 1;
342+
uint8_t byte = j / 8;
343+
uint8_t bitmask = 1 << (7-(j % 8));
344+
bool bit = !!(bits_t2_manchester[byte] & bitmask);
345+
bitbang_raw(bit, setting);
346+
furi_delay_us(setting->us_clock);
347+
// if (i % 2 == 1) furi_delay_us(setting->us_interpacket);
348+
}
349+
350+
if((setting->track == MagTrackStateAll))
351+
for(uint16_t i = 0; i < ZERO_SUFFIX; i++)
352+
{
353+
bit ^= 0xFF;
354+
bitbang_raw(bit, setting);
355+
furi_delay_us(setting->us_clock*2);
356+
}
357+
358+
FURI_CRITICAL_EXIT();
359+
free(data1);
360+
free(data2);
361+
tx_reset(setting);
362+
363+
}
364+
217365
void mag_spoof(Mag* mag) {
218366
MagSetting* setting = mag->setting;
219367

@@ -316,3 +464,132 @@ bool get_bit(uint8_t* b, uint32_t blen, uint32_t bitpos) {
316464
}
317465
return bitpos;
318466
}*/
467+
468+
469+
470+
471+
uint16_t add_bit(bool value, uint8_t* out, uint16_t count)
472+
{
473+
uint8_t bit = count % 8;
474+
uint8_t byte = count / 8;
475+
if (value)
476+
{
477+
out[byte] |= 0x01;
478+
}
479+
if (bit < 7) out[byte] <<= 1;
480+
return count+1;
481+
}
482+
483+
uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count)
484+
{
485+
static bool toggle = 0;
486+
toggle ^= 0x01;
487+
count = add_bit(toggle, out, count);
488+
if (value) toggle ^= 0x01;
489+
count = add_bit(toggle, out, count);
490+
return count;
491+
}
492+
493+
494+
uint16_t msr_encode(char* data, uint8_t* out_manchester, uint8_t* out_raw, uint8_t track_bits, uint8_t track_ascii_offset)
495+
{
496+
/*
497+
* track_bits - the number of raw (data) bits on the track. on ISO cards, that's 7 for track 5, or 4 for 2/3 - this is samy's bitlen
498+
* - this count includes the parity bit
499+
* track_ascii_offset - how much the ascii values are offset. track 1 makes space (ascii 32) become data 0x00,
500+
* - tracks 2/3 make ascii "0" become data 0x00 - this is samy's sublen
501+
*
502+
*/
503+
504+
uint16_t raw_bits_count = 0;
505+
uint16_t output_count = 0;
506+
int tmp, crc, lrc = 0;
507+
508+
for (int i = 0; i < PREFIX_NUM_ZEROES; i++)
509+
{
510+
output_count = add_bit_manchester(0, out_manchester, output_count);
511+
raw_bits_count = add_bit(0, out_raw, raw_bits_count);
512+
}
513+
514+
515+
for (int i = 0; *(data+i) != 0; i++)
516+
{
517+
crc = 1;
518+
tmp = *(data+i) - track_ascii_offset;
519+
520+
for (int j = 0; j < track_bits-1; j++)
521+
{
522+
crc ^= tmp & 1;
523+
lrc ^= (tmp & 1) << j;
524+
raw_bits_count = add_bit(tmp & 0x01, out_raw, raw_bits_count);
525+
output_count = add_bit_manchester(tmp & 0x01, out_manchester, output_count);
526+
tmp >>= 1;
527+
}
528+
raw_bits_count = add_bit(crc, out_raw, raw_bits_count);
529+
output_count = add_bit_manchester(crc, out_manchester, output_count);
530+
}
531+
532+
533+
// LRC byte
534+
tmp = lrc;
535+
crc = 1;
536+
for (int j = 0; j < track_bits-1; j++)
537+
{
538+
crc ^= tmp & 0x01;
539+
raw_bits_count = add_bit(tmp & 0x01, out_raw, raw_bits_count);
540+
output_count = add_bit_manchester(tmp & 0x01, out_manchester, output_count);
541+
tmp >>= 1;
542+
}
543+
raw_bits_count = add_bit(crc, out_raw, raw_bits_count);
544+
output_count = add_bit_manchester(crc, out_manchester, output_count);
545+
546+
return output_count;
547+
}
548+
549+
void debug_msr_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset)
550+
{
551+
uint8_t bits_raw[64] = {0}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits
552+
uint8_t bits_manchester[128] = {0}; // twice the above
553+
int numbits = 0;
554+
555+
printf("Encoding [%s] with %d bits\r\n", data, track_bits);
556+
numbits = msr_encode(data, (uint8_t*)bits_manchester, (uint8_t*)bits_raw, track_bits, track_ascii_offset);
557+
printf("Got %d bits\r\n", numbits);
558+
printf("Raw byte stream: ");
559+
for(int i = 0; i < numbits / 8 / 2; i++)
560+
{
561+
printf("%02x", bits_raw[i]);
562+
if (i%4==3) printf(" ");
563+
}
564+
565+
printf("\r\n");
566+
567+
printf("Bits ");
568+
int space_counter = 0;
569+
for(int i = 0; i < numbits / 2; i++)
570+
{
571+
if (i < PREFIX_NUM_ZEROES)
572+
{
573+
printf("X");
574+
continue;
575+
}
576+
else if (i == PREFIX_NUM_ZEROES)
577+
{
578+
printf (" ");
579+
space_counter = 0;
580+
}
581+
printf("%01x", (bits_raw[i/8] & (1 << (7-(i%8)))) != 0);
582+
if ((space_counter) % track_bits == track_bits-1) printf(" ");
583+
space_counter++;
584+
}
585+
586+
printf("\r\n");
587+
588+
printf("Manchester encoded, byte stream: ");
589+
for(int i = 0; i < numbits / 8; i++)
590+
{
591+
printf("%02x", bits_manchester[i]);
592+
if (i%4==3) printf(" ");
593+
}
594+
printf("\r\n\r\n");
595+
}

helpers/mag_helpers.h

+12
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,15 @@ void track_to_bits(uint8_t* bit_array, const char* track_data, uint8_t track_ind
1515
void mag_spoof(Mag* mag);
1616
void set_bit(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val);
1717
bool get_bit(uint8_t* b, uint32_t blen, uint32_t bitpos);
18+
19+
20+
#define PREFIX_NUM_ZEROES 25
21+
#define BITS_TRACK1 7
22+
#define OFFSET_TRACK1 32
23+
#define BITS_TRACK2 5
24+
#define OFFSET_TRACK2 48
25+
uint16_t add_bit(bool value, uint8_t* out, uint16_t count);
26+
uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count);
27+
uint16_t msr_encode(char* data, uint8_t* out_manchester, uint8_t* out_raw, uint8_t track_bits, uint8_t track_ascii_offset);
28+
void debug_msr_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset);
29+
void mag_spoof_bitwise(Mag* mag);

mag_i.h

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <furi.h>
99
#include <furi_hal.h>
10+
#include <furi/core/log.h>
1011

1112
#include <gui/gui.h>
1213
#include <gui/view.h>

scenes/mag_scene_emulate.c

+7
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ void mag_scene_emulate_on_enter(void* context) {
3939

4040
widget_add_button_element(widget, GuiButtonTypeLeft, "Config", mag_widget_callback, mag);
4141
widget_add_button_element(widget, GuiButtonTypeRight, "Send", mag_widget_callback, mag);
42+
widget_add_button_element(widget, GuiButtonTypeCenter, "Bitwise", mag_widget_callback, mag);
4243

4344
view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget);
4445
furi_string_free(tmp_str);
@@ -61,6 +62,12 @@ bool mag_scene_emulate_on_event(void* context, SceneManagerEvent event) {
6162
mag_spoof(mag);
6263
notification_message(mag->notifications, &sequence_blink_stop);
6364
break;
65+
case GuiButtonTypeCenter:
66+
consumed = true;
67+
notification_message(mag->notifications, &sequence_blink_start_cyan);
68+
mag_spoof_bitwise(mag);
69+
notification_message(mag->notifications, &sequence_blink_stop);
70+
break;
6471
}
6572
}
6673

0 commit comments

Comments
 (0)