From 3330b8f963977d7a1a6a37c3f551fdd3db196da1 Mon Sep 17 00:00:00 2001 From: Zachary Weiss Date: Fri, 3 Feb 2023 15:50:13 -0500 Subject: [PATCH] Numerous updates - Ver bump - Readme updates - Removal of old precompute & play approach - Disabling of WIP scenes (Add Manually and Edit) - Track three and reverse track toggle - Config menu visual bug fixed - Set available (non-empty) track(s) as default during saved_menu_on_enter - Slightly cleaner emulate scene UI code - Misc refactoring and cleanup - Bitwise bugfix: zero prefix was both being added to encodings and played by itself - Bitwise bugfix: zero playback was stretching clock rather than num of half-bits - Bitwise bugfix: zero prefix and suffix unwrapped from if statements --- README.md | 22 +- application.fam | 2 +- helpers/mag_helpers.c | 467 ++++++++---------------------- helpers/mag_helpers.h | 30 +- helpers/mag_types.h | 7 +- mag.c | 2 +- scenes/mag_scene_emulate.c | 55 ++-- scenes/mag_scene_emulate_config.c | 26 +- scenes/mag_scene_saved_info.c | 4 +- scenes/mag_scene_saved_menu.c | 28 +- scenes/mag_scene_start.c | 14 +- 11 files changed, 228 insertions(+), 429 deletions(-) diff --git a/README.md b/README.md index 173b9e1ee..a5c6e25b9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # magspoof_flipper -WIP of MagSpoof for the Flipper Zero. Basic TX of saved files confirmed working against an MSR90 with an external H-bridge module mirroring Samy Kamkar's design. RFID coil output weaker; able to be picked up/detected by more compact mag readers such as Square, but yet to have success with it being decoded/parsed properly. Additional WIP investigation into alternate internal TX options (CC1101, ST25R3916, piezo) underway, courtesy of arha. Sample files with test data are included in `assets` for anyone wishing to experiment. +WIP of MagSpoof for the Flipper Zero. Basic TX of saved files confirmed working against an MSR90 with an external H-bridge module mirroring Samy Kamkar's design. RFID coil output weaker; able to be picked up/detected by more compact mag readers such as Square, but yet to have success with it being decoded/parsed properly. Additional investigation into alternate internal TX options (CC1101, ST25R3916, piezo) underway; tentatively, RFID coil + speaker (`LF + P` config setting) results in the strongest internal TX tested to date but still weaker than a dedicated external module or an actual card swipe (and sounds like a dial-up modem from hell). Sample files with test data are included in `assets` for anyone wishing to experiment. -Disclaimer: use responsibly, and at your own risk. While in my testing, I've seen no reason to believe this could damage the RFID hardware, this is inherently driving the coil in ways it was not designed or intended for; I take no responsibility for fried/bricked Flippers. Similarly, please only use this with magstripe cards and mag readers you own — this is solely meant as a proof of concept for educational purposes. I neither condone nor am sympathetic to malicious uses of my code. +Disclaimer: use responsibly, and at your own risk. While in my testing, I've seen no reason to believe this could damage the RFID (or other) hardware, this is inherently driving the coil in ways it was not designed or intended for; I take no responsibility for fried/bricked Flippers. Similarly, please only use this with magstripe cards and mag readers you own — this is solely meant as a proof of concept for educational purposes. I neither condone nor am sympathetic to malicious uses of my code. ## Optional GPIO TX Module For those desiring better TX than the internal RFID coil can offer, one can build the module below, consisting of an H-bridge, a capacitor, and a coil. @@ -13,21 +13,23 @@ For those desiring better TX than the internal RFID coil can offer, one can buil Known bugs: - [X] File format issues when Track 2 data exists but Track 1 is left empty; doesn't seem to be setting the Track 2 field with anything (doesn't overwrite existing data). However, `flipper_format_read_string()` doesn't seem to return `false`. Is the bug in my code, or with `flipper_format`? - [X] Review how it's done in [unirfremix (Sub-GHz Remote)](https://github.com/DarkFlippers/unleashed-firmware/blob/dev/applications/main/unirfremix/unirfremix_app.c), as IIRC that can handle empty keys, despite using the `flipper_format` lib for parsing. -- [ ] Attempting to play a track that doesn't have data results in a crash (as one might expect). Need to lock out users from selecting empty tracks in the config menu or do better error handling +- [X] Attempting to play a track that doesn't have data results in a crash (as one might expect). Need to lock out users from selecting empty tracks in the config menu or do better error handling (*Doesn't crash now, but still should probably prevent users from being able to select*) - [ ] Custom text input scene with expanded characterset (Add Manually) has odd behavior when navigating the keys near the numpad Emulation: -- [ ] Validate arha's bitmap changes, transition over to it fully +- [X] Validate arha's bitmap changes, transition over to it fully - [X] Test piezo TX (prelim tests promising) - [ ] General code cleanup -- [ ] Reverse track precompute & replay (should be simple with new bitmap approach; just iterate through bytes backwards, bits forwards?) +- [X] Reverse track precompute & replay - [ ] Parameter tuning, find best defaults, troubleshoot improperly parsed TX - [ ] Pursue skunkworks TX improvement ideas listed below +- [ ] Remove or reimplement interpacket + - [ ] Verify `furi_delay_us` aliasing to `64us` Scenes: -- [ ] Finish emulation config scene (reverse track functionality; possibly expand settings list to include prefix/between/suffix options) -- [ ] "Edit" scene (generalize "Add manually") -- [ ] "Rename" scene (generalize input_name) +- [X] Finish emulation config scene (reverse track functionality; possibly expand settings list to include prefix/between/suffix options) +- [ ] "Edit" scene (generalize `input_value`) +- [ ] "Rename" scene (generalize `input_name`) File management: - [ ] Update Add Manually flow to reflect new file format (currently only sets Track 2) @@ -39,6 +41,10 @@ Internal TX improvements: - [ ] Attempt downstream modulation techniques in addition to upstream, like the LF RFID worker does when writing. - [ ] Implement using the timer system, rather than direct-writing to pins - [ ] Use the NFC (HF RFID) coil instead of or in addition to the LF coil (likely unfruitful from initial tests; we can enable/disable the oscillating field, but even with transparent mode to the ST25R3916, it seems we don't get low-enough-level control to pull it high/low correctly) +- [ ] Add "subcarriers" to each half-bit transmitted (wiggle the pin high and low rapidly) + - [ ] Piezo subcarrier tests + - [ ] LF subcarrier tests + - [ ] Retry NFC oscillating field? External RX options: 1. [TTL / PS/2 mag reader connected to UART](https://www.alibaba.com/product-detail/Mini-portable-12-3-tracks-usb_60679900708.html) (bulky, harder to source, but likely easiest to read over GPIO, and means one can read all tracks) diff --git a/application.fam b/application.fam index fd4278993..47bc1446e 100644 --- a/application.fam +++ b/application.fam @@ -16,7 +16,7 @@ App( fap_icon="icons/mag_10px.png", fap_category="Tools", fap_icon_assets="icons", - fap_version=(0, 1), # major, minor + fap_version=(0, 4), # major, minor fap_description="WIP MagSpoof port using the RFID subsystem", fap_author="Zachary Weiss", fap_weburl="https://github.com/zacharyweiss/magspoof_flipper", diff --git a/helpers/mag_helpers.c b/helpers/mag_helpers.c index 7fd6777b3..7e386d6fe 100644 --- a/helpers/mag_helpers.c +++ b/helpers/mag_helpers.c @@ -15,10 +15,10 @@ const uint8_t bitlen[] = {7, 5, 5}; // char offset by track const int sublen[] = {32, 48, 48}; -uint8_t bit_dir = 0; + uint8_t last_value = 2; -void bitbang_raw(bool value, MagSetting* setting) { +void play_halfbit(bool value, MagSetting* setting) { switch(setting->tx) { case MagTxStateRFID: furi_hal_gpio_write(RFID_PIN_OUT, value); @@ -49,114 +49,45 @@ void bitbang_raw(bool value, MagSetting* setting) { last_value = value; } -void play_bit_rf(bool bit, MagSetting* setting) { - bit_dir ^= 1; - furi_hal_gpio_write(&gpio_cc1101_g0, true); - furi_delay_us(64); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_delay_us(setting->us_clock); - - if(bit) { - furi_hal_gpio_write(&gpio_cc1101_g0, true); - furi_delay_us(64); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - } - furi_delay_us(setting->us_clock); - furi_delay_us(setting->us_interpacket); -} - -void play_bit_rfid(uint8_t send_bit, MagSetting* setting) { - // internal TX over RFID coil - bit_dir ^= 1; - furi_hal_gpio_write(RFID_PIN_OUT, bit_dir); - furi_delay_us(setting->us_clock); - - if(send_bit) { - bit_dir ^= 1; - furi_hal_gpio_write(RFID_PIN_OUT, bit_dir); - } - furi_delay_us(setting->us_clock); - - furi_delay_us(setting->us_interpacket); -} - -void play_bit_gpio(uint8_t send_bit, MagSetting* setting) { - // external TX over motor driver wired to PIN_A and PIN_B - bit_dir ^= 1; - furi_hal_gpio_write(GPIO_PIN_A, bit_dir); - furi_hal_gpio_write(GPIO_PIN_B, !bit_dir); - furi_delay_us(setting->us_clock); - - if(send_bit) { - bit_dir ^= 1; - furi_hal_gpio_write(GPIO_PIN_A, bit_dir); - furi_hal_gpio_write(GPIO_PIN_B, !bit_dir); +void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting, bool reverse) { + for(uint16_t i = 0; i < n_bits; i++) { + uint16_t j = (reverse) ? (n_bits - i - 1) : i; + uint8_t byte = j / 8; + uint8_t bitmask = 1 << (7 - (j % 8)); + /* Bits are stored in their arrays like on a card (LSB first). This is not how usually bits are stored in a + * byte, with the MSB first. the var bitmask creates the pattern to iterate through each bit, LSB first, like so + * 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x80... masking bits one by one from the current byte + * + * I've chosen this LSB approach since bits and bytes are hard enough to visualize with the 5/8 and 7/8 encoding + * MSR uses. It's a biiit more complicated to process, but visualizing it with printf or a debugger is + * infinitely easier + * + * Encoding the following pairs of 5 bits as 5/8: A1234 B1234 C1234 D1234 + * using this LSB format looks like: A1234B12 34C1234D 12340000 + * using the MSB format, looks like: 21B4321A D4321C43 00004321 + * this means reading each byte backwards when printing/debugging, and the jumping 16 bits ahead, reading 8 more + * bits backward, jumping 16 more bits ahead. + * + * I find this much more convenient for debugging, with the tiny incovenience of reading the bits in reverse + * order. Thus, the reason for the bitmask above + */ + + bool bit = !!(bits_manchester[byte] & bitmask); + + // TODO: reimplement timing delays. Replace fixed furi_hal_cortex_delay_us to wait instead to a specific value + // for DWT->CYCCNT. Note timer is aliased to 64us as per + // #define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) | furi_hal_cortex.c + + play_halfbit(bit, setting); + furi_delay_us(setting->us_clock); + // if (i % 2 == 1) furi_delay_us(setting->us_interpacket); } - furi_delay_us(setting->us_clock); - - furi_delay_us(setting->us_interpacket); -} - -void play_bit_piezo(uint8_t send_bit, MagSetting* setting) { - // TX testing with parasitic EMF from buzzer - bit_dir ^= 1; - furi_hal_gpio_write(&gpio_speaker, bit_dir); - furi_delay_us(setting->us_clock); - - if(send_bit) { - bit_dir ^= 1; - furi_hal_gpio_write(&gpio_speaker, bit_dir); - } - furi_delay_us(setting->us_clock); - - furi_delay_us(setting->us_interpacket); -} - -void play_bit_lf_p(uint8_t send_bit, MagSetting* setting) { - // TX testing with parasitic EMF from buzzer - bit_dir ^= 1; - furi_hal_gpio_write(&gpio_speaker, bit_dir); - furi_hal_gpio_write(RFID_PIN_OUT, bit_dir); - furi_delay_us(setting->us_clock); - - if(send_bit) { - bit_dir ^= 1; - furi_hal_gpio_write(&gpio_speaker, bit_dir); - furi_hal_gpio_write(RFID_PIN_OUT, bit_dir); - } - furi_delay_us(setting->us_clock); - - furi_delay_us(setting->us_interpacket); -} - -bool play_bit(uint8_t send_bit, MagSetting* setting) { - // Initialize configured TX method - switch(setting->tx) { - case MagTxStateRFID: - play_bit_rfid(send_bit, setting); - break; - case MagTxStateGPIO: - play_bit_gpio(send_bit, setting); - break; - case MagTxStatePiezo: - play_bit_piezo(send_bit, setting); - break; - case MagTxStateLF_P: - play_bit_lf_p(send_bit, setting); - break; - case MagTxCC1101_434: - case MagTxCC1101_868: - play_bit_rf(send_bit & 0x01, setting); - break; - default: - return false; - } - - return true; } void tx_init_rfid() { // initialize RFID system for TX + + // OTG needed for RFID? Or just legacy from GPIO? furi_hal_power_enable_otg(); furi_hal_ibutton_start_drive(); @@ -187,29 +118,6 @@ void tx_deinit_rfid() { furi_hal_power_disable_otg(); } -void tx_init_gpio() { - furi_hal_power_enable_otg(); - // gpio_item_configure_all_pins(GpioModeOutputPushPull); - furi_hal_gpio_init(GPIO_PIN_A, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(GPIO_PIN_B, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(GPIO_PIN_ENABLE, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - - furi_hal_gpio_write(GPIO_PIN_ENABLE, 1); - - // had some issues with ~300; bumped higher temporarily - furi_delay_ms(500); -} - -void tx_deinit_gpio() { - furi_hal_gpio_write(GPIO_PIN_A, 0); - furi_hal_gpio_write(GPIO_PIN_B, 0); - furi_hal_gpio_write(GPIO_PIN_ENABLE, 0); - - // set back to analog output mode? - //gpio_item_configure_all_pins(GpioModeAnalog); - furi_hal_power_disable_otg(); -} - void tx_init_rf(int hz) { // presets and frequency will need some experimenting furi_hal_subghz_reset(); @@ -224,18 +132,9 @@ void tx_init_rf(int hz) { furi_hal_gpio_write(&gpio_cc1101_g0, false); } -void tx_deinit_rf() { - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); -} - void tx_init_piezo() { // TODO: some special mutex acquire procedure? c.f. furi_hal_speaker.c furi_hal_gpio_init(&gpio_speaker, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - - // tossing in this delay arbitrarily to make it easier to see light blinking during TX - //furi_delay_ms(100); } void tx_deinit_piezo() { @@ -250,7 +149,16 @@ bool tx_init(MagSetting* setting) { tx_init_rfid(); break; case MagTxStateGPIO: - tx_init_gpio(); + furi_hal_power_enable_otg(); + // gpio_item_configure_all_pins(GpioModeOutputPushPull); + furi_hal_gpio_init(GPIO_PIN_A, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(GPIO_PIN_B, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(GPIO_PIN_ENABLE, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + + furi_hal_gpio_write(GPIO_PIN_ENABLE, 1); + + // had some issues with ~300; bumped higher temporarily + furi_delay_ms(500); break; case MagTxStatePiezo: tx_init_piezo(); @@ -279,7 +187,13 @@ bool tx_deinit(MagSetting* setting) { tx_deinit_rfid(); break; case MagTxStateGPIO: - tx_deinit_gpio(); + furi_hal_gpio_write(GPIO_PIN_A, 0); + furi_hal_gpio_write(GPIO_PIN_B, 0); + furi_hal_gpio_write(GPIO_PIN_ENABLE, 0); + + // set back to analog output mode? + //gpio_item_configure_all_pins(GpioModeAnalog); + furi_hal_power_disable_otg(); break; case MagTxStatePiezo: tx_deinit_piezo(); @@ -290,7 +204,9 @@ bool tx_deinit(MagSetting* setting) { break; case MagTxCC1101_434: case MagTxCC1101_868: - tx_deinit_rf(); + furi_hal_gpio_write(&gpio_cc1101_g0, false); + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); break; default: return false; @@ -299,248 +215,104 @@ bool tx_deinit(MagSetting* setting) { return true; } -// due for deprecation -void track_to_bits(uint8_t* bit_array, const char* track_data, uint8_t track_index) { - // convert individual track to bits - - int tmp, crc, lrc = 0; - int i = 0; - - // convert track data to bits - for(uint8_t j = 0; track_data[j] != '\0'; j++) { - crc = 1; - tmp = track_data[j] - sublen[track_index]; - - for(uint8_t k = 0; k < bitlen[track_index] - 1; k++) { - crc ^= tmp & 1; - lrc ^= (tmp & 1) << k; - bit_array[i] = tmp & 1; - i++; - tmp >>= 1; - } - bit_array[i] = crc; - i++; - } - - FURI_LOG_D(TAG, "LRC"); - // finish calculating final "byte" (LRC) - tmp = lrc; - crc = 1; - for(uint8_t j = 0; j < bitlen[track_index] - 1; j++) { - crc ^= tmp & 1; - bit_array[i] = tmp & 1; - i++; - tmp >>= 1; - } - bit_array[i] = crc; - i++; - - // My makeshift end sentinel. All other values 0/1 - bit_array[i] = 2; - i++; - - // Log the output (messy but works) - //char output[500] = {0x0}; - /*FuriString* tmp_str; - tmp_str = furi_string_alloc(); - for(uint8_t j = 0; bit_array[j] != 2; j++) { - furi_string_cat_printf(tmp_str, "%d", (bit_array[j] & 1)); - //strcat(output, furi_string_get_cstr(tmp_str)); - } - FURI_LOG_D(TAG, "Track %d: %s", (track_index + 1), track_data); - FURI_LOG_D(TAG, "Track %d: %s", (track_index + 1), furi_string_get_cstr(tmp_str));*/ - //furi_string_free(tmp_str); -} - -void mag_spoof_bitwise(Mag* mag) { +void mag_spoof(Mag* mag) { MagSetting* setting = mag->setting; + // TODO: cleanup this section. Possibly move precompute + tx_init to emulate_on_enter? FuriString* ft1 = mag->mag_dev->dev_data.track[0].str; FuriString* ft2 = mag->mag_dev->dev_data.track[1].str; + FuriString* ft3 = mag->mag_dev->dev_data.track[2].str; - char* data1; - char* data2; + char *data1, *data2, *data3; data1 = malloc(furi_string_size(ft1) + 1); data2 = malloc(furi_string_size(ft2) + 1); + data3 = malloc(furi_string_size(ft3) + 1); strncpy(data1, furi_string_get_cstr(ft1), furi_string_size(ft1)); strncpy(data2, furi_string_get_cstr(ft2), furi_string_size(ft2)); + strncpy(data3, furi_string_get_cstr(ft3), furi_string_size(ft3)); if(furi_log_get_level() >= FuriLogLevelDebug) { - debug_msr_string(data1, BITS_TRACK1, OFFSET_TRACK1); - debug_msr_string(data2, BITS_TRACK2, OFFSET_TRACK2); + debug_mag_string(data1, bitlen[0], sublen[0]); + debug_mag_string(data2, bitlen[1], sublen[1]); + debug_mag_string(data3, bitlen[2], sublen[2]); } uint8_t bits_t1_raw[64] = {0x00}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits uint8_t bits_t1_manchester[128] = {0x00}; // twice the above - uint16_t bits_t1_count = msr_encode( - data1, (uint8_t*)bits_t1_manchester, (uint8_t*)bits_t1_raw, BITS_TRACK1, OFFSET_TRACK1); + uint16_t bits_t1_count = mag_encode( + data1, (uint8_t*)bits_t1_manchester, (uint8_t*)bits_t1_raw, bitlen[0], sublen[0]); uint8_t bits_t2_raw[64] = {0x00}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits uint8_t bits_t2_manchester[128] = {0x00}; // twice the above - uint16_t bits_t2_count = msr_encode( - data2, (uint8_t*)bits_t2_manchester, (uint8_t*)bits_t2_raw, BITS_TRACK2, OFFSET_TRACK2); + uint16_t bits_t2_count = mag_encode( + data2, (uint8_t*)bits_t2_manchester, (uint8_t*)bits_t2_raw, bitlen[1], sublen[1]); + uint8_t bits_t3_raw[64] = {0x00}; + uint8_t bits_t3_manchester[128] = {0x00}; + uint16_t bits_t3_count = mag_encode( + data3, (uint8_t*)bits_t3_manchester, (uint8_t*)bits_t3_raw, bitlen[2], sublen[2]); if(furi_log_get_level() >= FuriLogLevelDebug) { printf("Manchester bitcount: T1: %d, T2: %d\r\n", bits_t1_count, bits_t2_count); - printf("T1 raw: "); for(int i = 0; i < bits_t1_count / 16; i++) printf("%02x ", bits_t1_raw[i]); - printf("\r\n"); - - printf("T1 manchester: "); + printf("\r\nT1 manchester: "); for(int i = 0; i < bits_t1_count / 8; i++) printf("%02x ", bits_t1_manchester[i]); - printf("\r\n"); - - printf("T2 raw: "); + printf("\r\nT2 raw: "); for(int i = 0; i < bits_t2_count / 16; i++) printf("%02x ", bits_t2_raw[i]); - printf("\r\n"); - - printf("T2 manchester: "); + printf("\r\nT2 manchester: "); for(int i = 0; i < bits_t2_count / 8; i++) printf("%02x ", bits_t2_manchester[i]); - printf("\r\n"); - - printf("Bitwise emulation done\r\n\r\n"); + printf("\r\nT3 raw: "); + for(int i = 0; i < bits_t3_count / 16; i++) printf("%02x ", bits_t3_raw[i]); + printf("\r\nT3 manchester: "); + for(int i = 0; i < bits_t3_count / 8; i++) printf("%02x ", bits_t3_manchester[i]); + printf("\r\nBitwise emulation done\r\n\r\n"); } - if(!tx_init(setting)) return; last_value = 2; - FURI_CRITICAL_ENTER(); bool bit = false; - if((setting->track == MagTrackStateAll)) - for(uint16_t i = 0; i < ZERO_PREFIX; i++) { - bit ^= 0xFF; - bitbang_raw(bit, setting); - furi_delay_us(setting->us_clock * 2); - } + if(!tx_init(setting)) return; - if((setting->track == MagTrackStateAll) || (setting->track == MagTrackStateOne)) - for(uint16_t i = 0; i < bits_t1_count; i++) { - uint8_t byte = i / 8; - uint8_t bitmask = 1 << (7 - (i % 8)); - /* Bits are stored in their arrays like on a card (LSB first). This is not how usually bits are stored in a - * byte, with the MSB first. the var bitmask creates the pattern to iterate through each bit, LSB first, like so - * 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x80... masking bits one by one from the current byte - * - * I've chosen this LSB approach since bits and bytes are hard enough to visualize with the 5/8 and 7/8 encoding - * MSR uses. It's a biiit more complicated to process, but visualizing it with printf or a debugger is - * infinitely easier - * - * Encoding the following pairs of 5 bits as 5/8: A1234 B1234 C1234 D1234 - * using this LSB format looks like: A1234B12 34C1234D 12340000 - * using the MSB format, looks like: 21B4321A D4321C43 00004321 - * this means reading each byte backwards when printing/debugging, and the jumping 16 bits ahead, reading 8 more - * bits backward, jumping 16 more bits ahead. - * - * I find this much more convenient for debugging, with the tiny incovenience of reading the bits in reverse - * order. Thus, the reason for the bitmask above - */ - - bit = !!(bits_t1_manchester[byte] & bitmask); - - // TODO: reimplement timing delays. Replace fixed furi_hal_cortex_delay_us to wait instead to a specific value - // for DWT->CYCCNT. Note timer is aliased to 64us as per - // #define FURI_HAL_CORTEX_INSTRUCTIONS_PER_MICROSECOND (SystemCoreClock / 1000000) | furi_hal_cortex.c - - bitbang_raw(bit, setting); - furi_delay_us(setting->us_clock); - // if (i % 2 == 1) furi_delay_us(setting->us_interpacket); - } + FURI_CRITICAL_ENTER(); + for(uint16_t i = 0; i < (ZERO_PREFIX * 2); i++) { + // is this right? + bit ^= 0xFF; + play_halfbit(bit, setting); + furi_delay_us(setting->us_clock); + } - if((setting->track == MagTrackStateAll)) - for(uint16_t i = 0; i < ZERO_BETWEEN; i++) { - bit ^= 0xFF; - bitbang_raw(bit, setting); - furi_delay_us(setting->us_clock * 2); - } + if((setting->track == MagTrackStateOneAndTwo) || (setting->track == MagTrackStateOne)) + play_track((uint8_t*)bits_t1_manchester, bits_t1_count, setting, false); - if((setting->track == MagTrackStateAll) || (setting->track == MagTrackStateTwo)) - for(uint16_t i = 0; i < bits_t2_count; i++) { - uint16_t j = bits_t2_count - i - 1; - uint8_t byte = j / 8; - uint8_t bitmask = 1 << (7 - (j % 8)); - bool bit = !!(bits_t2_manchester[byte] & bitmask); - bitbang_raw(bit, setting); + if((setting->track == MagTrackStateOneAndTwo)) + for(uint16_t i = 0; i < (ZERO_BETWEEN * 2); i++) { + bit ^= 0xFF; + play_halfbit(bit, setting); furi_delay_us(setting->us_clock); - // if (i % 2 == 1) furi_delay_us(setting->us_interpacket); } - if((setting->track == MagTrackStateAll)) - for(uint16_t i = 0; i < ZERO_SUFFIX; i++) { - bit ^= 0xFF; - bitbang_raw(bit, setting); - furi_delay_us(setting->us_clock * 2); - } + if((setting->track == MagTrackStateOneAndTwo) || (setting->track == MagTrackStateTwo)) + play_track( + (uint8_t*)bits_t2_manchester, + bits_t2_count, + setting, + (setting->reverse == MagReverseStateOn)); + + if((setting->track == MagTrackStateThree)) + play_track((uint8_t*)bits_t3_manchester, bits_t3_count, setting, false); + for(uint16_t i = 0; i < (ZERO_SUFFIX * 2); i++) { + bit ^= 0xFF; + play_halfbit(bit, setting); + furi_delay_us(setting->us_clock); + } FURI_CRITICAL_EXIT(); + free(data1); free(data2); + free(data3); tx_deinit(setting); } -// due for deprecation -void mag_spoof(Mag* mag) { - MagSetting* setting = mag->setting; - - // precompute tracks (WIP; ignores reverse and 3rd track) - // likely will be reworked to antirez's bitmap method anyway... - const char* data1 = furi_string_get_cstr(mag->mag_dev->dev_data.track[0].str); - const char* data2 = furi_string_get_cstr(mag->mag_dev->dev_data.track[1].str); - uint8_t bit_array1[2 * (strlen(data1) * bitlen[0]) + 1]; - uint8_t bit_array2[2 * (strlen(data2) * bitlen[1]) + 1]; - track_to_bits(bit_array1, data1, 0); - track_to_bits(bit_array2, data2, 1); - - bool spoofed = false; - do { - // Initialize configured TX method - if(!tx_init(setting)) break; - - // Critical timing section (need to eliminate ifs? does this impact timing?) - FURI_CRITICAL_ENTER(); - // Prefix of zeros - for(uint16_t i = 0; i < ZERO_PREFIX; i++) { - if(!play_bit(0, setting)) break; - } - - // Track 1 - if((setting->track == MagTrackStateAll) || (setting->track == MagTrackStateOne)) { - for(uint16_t i = 0; bit_array1[i] != 2; i++) { - if(!play_bit((bit_array1[i] & 1), setting)) break; - } - } - - // Zeros between tracks - if(setting->track == MagTrackStateAll) { - for(uint16_t i = 0; i < ZERO_BETWEEN; i++) { - if(!play_bit(0, setting)) break; - } - } - - // Track 2 (TODO: Reverse track) - if((setting->track == MagTrackStateAll) || (setting->track == MagTrackStateTwo)) { - for(uint16_t i = 0; bit_array2[i] != 2; i++) { - if(!play_bit((bit_array2[i] & 1), setting)) break; - } - } - - // Suffix of zeros - for(uint16_t i = 0; i < ZERO_SUFFIX; i++) { - if(!play_bit(0, setting)) break; - } - FURI_CRITICAL_EXIT(); - - // Reset configured TX method - if(!tx_deinit(setting)) break; - spoofed = true; - } while(0); - - UNUSED(spoofed); - /*if(!spoofed) { - // error handling? - // cleanup? - }*/ -} - uint16_t add_bit(bool value, uint8_t* out, uint16_t count) { uint8_t bit = count % 8; uint8_t byte = count / 8; @@ -560,7 +332,7 @@ uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count) { return count; } -uint16_t msr_encode( +uint16_t mag_encode( char* data, uint8_t* out_manchester, uint8_t* out_raw, @@ -578,10 +350,11 @@ uint16_t msr_encode( uint16_t output_count = 0; int tmp, crc, lrc = 0; - for(int i = 0; i < PREFIX_NUM_ZEROES; i++) { + /* // why are we adding zeros to the encoded string if we're also doing it while playing? + for(int i = 0; i < ZERO_PREFIX; i++) { output_count = add_bit_manchester(0, out_manchester, output_count); raw_bits_count = add_bit(0, out_raw, raw_bits_count); - } + }*/ for(int i = 0; *(data + i) != 0; i++) { crc = 1; @@ -613,13 +386,13 @@ uint16_t msr_encode( return output_count; } -void debug_msr_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset) { +void debug_mag_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset) { uint8_t bits_raw[64] = {0}; // 68 chars max track 1 + 1 char crc * 7 approx =~ 483 bits uint8_t bits_manchester[128] = {0}; // twice the above int numbits = 0; printf("Encoding [%s] with %d bits\r\n", data, track_bits); - numbits = msr_encode( + numbits = mag_encode( data, (uint8_t*)bits_manchester, (uint8_t*)bits_raw, track_bits, track_ascii_offset); printf("Got %d bits\r\n", numbits); printf("Raw byte stream: "); @@ -633,13 +406,13 @@ void debug_msr_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset printf("Bits "); int space_counter = 0; for(int i = 0; i < numbits / 2; i++) { - if(i < PREFIX_NUM_ZEROES) { + /*if(i < ZERO_PREFIX) { printf("X"); continue; - } else if(i == PREFIX_NUM_ZEROES) { + } else if(i == ZERO_PREFIX) { printf(" "); space_counter = 0; - } + }*/ printf("%01x", (bits_raw[i / 8] & (1 << (7 - (i % 8)))) != 0); if((space_counter) % track_bits == track_bits - 1) printf(" "); space_counter++; diff --git a/helpers/mag_helpers.h b/helpers/mag_helpers.h index 24eb6afd5..a61f143b8 100644 --- a/helpers/mag_helpers.h +++ b/helpers/mag_helpers.h @@ -2,36 +2,24 @@ #include #include -void play_bit_rfid(uint8_t send_bit, MagSetting* setting); -void play_bit_gpio(uint8_t send_bit, MagSetting* setting); -void play_bit_piezo(uint8_t send_bit, MagSetting* setting); -void play_bit_lf_p(uint8_t send_bit, MagSetting* setting); -bool play_bit(uint8_t send_bit, MagSetting* setting); +void play_halfbit(bool value, MagSetting* setting); +void play_track(uint8_t* bits_manchester, uint16_t n_bits, MagSetting* setting, bool reverse); + +void tx_init_rf(int hz); void tx_init_rfid(); -void tx_init_gpio(); -void tx_deinit_rfid(); -void tx_deinit_gpio(); +void tx_init_piezo(); bool tx_init(MagSetting* setting); +void tx_deinit_piezo(); +void tx_deinit_rfid(); bool tx_deinit(MagSetting* setting); -#define PREFIX_NUM_ZEROES 25 -#define BITS_TRACK1 7 -#define OFFSET_TRACK1 32 -#define BITS_TRACK2 5 -#define OFFSET_TRACK2 48 uint16_t add_bit(bool value, uint8_t* out, uint16_t count); uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count); -uint16_t msr_encode( +uint16_t mag_encode( char* data, uint8_t* out_manchester, uint8_t* out_raw, uint8_t track_bits, uint8_t track_ascii_offset); -void debug_msr_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset); -void mag_spoof_bitwise(Mag* mag); -void tx_deinit_rf(); -void tx_init_rf(int hz); - -// due for deprecation -void track_to_bits(uint8_t* bit_array, const char* track_data, uint8_t track_index); +void debug_mag_string(char* data, uint8_t track_bits, uint8_t track_ascii_offset); void mag_spoof(Mag* mag); diff --git a/helpers/mag_types.h b/helpers/mag_types.h index d7743b9c4..286d6d882 100644 --- a/helpers/mag_types.h +++ b/helpers/mag_types.h @@ -1,6 +1,6 @@ #pragma once -#define MAG_VERSION_APP "0.03" +#define MAG_VERSION_APP "0.04" #define MAG_DEVELOPER "Zachary Weiss" #define MAG_GITHUB "github.com/zacharyweiss/magspoof_flipper" @@ -16,14 +16,15 @@ typedef enum { } MagView; typedef enum { - MagReverseStateOn, MagReverseStateOff, + MagReverseStateOn, } MagReverseState; typedef enum { - MagTrackStateAll, + MagTrackStateOneAndTwo, MagTrackStateOne, MagTrackStateTwo, + MagTrackStateThree, } MagTrackState; typedef enum { diff --git a/mag.c b/mag.c index 853e00e0f..ce4a46be8 100644 --- a/mag.c +++ b/mag.c @@ -3,7 +3,7 @@ #define TAG "Mag" #define SETTING_DEFAULT_REVERSE MagReverseStateOff -#define SETTING_DEFAULT_TRACK MagTrackStateAll +#define SETTING_DEFAULT_TRACK MagTrackStateOneAndTwo #define SETTING_DEFAULT_TX_RFID MagTxStateGPIO #define SETTING_DEFAULT_US_CLOCK 240 #define SETTING_DEFAULT_US_INTERPACKET 10 diff --git a/scenes/mag_scene_emulate.c b/scenes/mag_scene_emulate.c index aac8ddd78..e7e8737eb 100644 --- a/scenes/mag_scene_emulate.c +++ b/scenes/mag_scene_emulate.c @@ -1,6 +1,18 @@ #include "../mag_i.h" #include "../helpers/mag_helpers.h" +#define TAG "MagSceneEmulate" + +void cat_trackstr(FuriString* str, uint8_t calls, uint8_t i, FuriString* trackstr) { + furi_string_cat_printf( + str, + "%sTrack %d:%s%s\n", + (calls == 0) ? "" : "\n", // if first line, don't prepend a "\n" + (i + 1), + furi_string_empty(trackstr) ? " " : "\n", + furi_string_empty(trackstr) ? "< empty >" : furi_string_get_cstr(trackstr)); +} + void mag_scene_emulate_on_enter(void* context) { Mag* mag = context; Widget* widget = mag->widget; @@ -13,26 +25,33 @@ void mag_scene_emulate_on_enter(void* context) { // TODO: Display other relevant config settings (namely RFID vs GPIO)? - widget_add_icon_element(widget, 2, 1, &I_mag_file_10px); + widget_add_icon_element(widget, 1, 1, &I_mag_file_10px); widget_add_string_element( widget, 13, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str)); furi_string_reset(tmp_str); + FURI_LOG_D(TAG, "%d", mag->setting->reverse); + + // print relevant data + uint8_t cat_count = 0; for(uint8_t i = 0; i < MAG_DEV_TRACKS; i++) { FuriString* trackstr = mag->mag_dev->dev_data.track[i].str; - // there's definitely a better way to do this... - bool is_active_one = (mag->setting->track == MagTrackStateOne) & (i == 0); - bool is_active_two = (mag->setting->track == MagTrackStateTwo) & (i == 1); - bool is_active_both = (mag->setting->track == MagTrackStateAll); - - if(is_active_one | is_active_two | is_active_both) { - furi_string_cat_printf( - tmp_str, - "Track %d:%s%s%s", - (i + 1), - furi_string_empty(trackstr) ? " " : "\n", - furi_string_empty(trackstr) ? "< empty >" : furi_string_get_cstr(trackstr), - (i + 1 == MAG_DEV_TRACKS) ? "\n" : "\n\n"); + + // still messy / dumb way to do this, but slightly cleaner than before. + // will clean up more later + switch(mag->setting->track) { + case MagTrackStateOne: + if(i == 0) cat_trackstr(tmp_str, cat_count++, i, trackstr); + break; + case MagTrackStateTwo: + if(i == 1) cat_trackstr(tmp_str, cat_count++, i, trackstr); + break; + case MagTrackStateThree: + if(i == 2) cat_trackstr(tmp_str, cat_count++, i, trackstr); + break; + case MagTrackStateOneAndTwo: + if((i == 0) | (i == 1)) cat_trackstr(tmp_str, cat_count++, i, trackstr); + break; } } @@ -40,8 +59,6 @@ void mag_scene_emulate_on_enter(void* context) { widget_add_button_element(widget, GuiButtonTypeLeft, "Config", mag_widget_callback, mag); widget_add_button_element(widget, GuiButtonTypeRight, "Send", mag_widget_callback, mag); - widget_add_button_element(widget, GuiButtonTypeCenter, "Bitwise", mag_widget_callback, mag); - view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); furi_string_free(tmp_str); } @@ -63,12 +80,6 @@ bool mag_scene_emulate_on_event(void* context, SceneManagerEvent event) { mag_spoof(mag); notification_message(mag->notifications, &sequence_blink_stop); break; - case GuiButtonTypeCenter: - consumed = true; - notification_message(mag->notifications, &sequence_blink_start_cyan); - mag_spoof_bitwise(mag); - notification_message(mag->notifications, &sequence_blink_stop); - break; } } diff --git a/scenes/mag_scene_emulate_config.c b/scenes/mag_scene_emulate_config.c index c0e83f9ef..87b3fb72b 100644 --- a/scenes/mag_scene_emulate_config.c +++ b/scenes/mag_scene_emulate_config.c @@ -15,7 +15,7 @@ const char* const tx_text[TX_COUNT] = { "RFID", "GPIO", "Piezo", - "LF+P", + "LF + P", "434MHz", "868MHz", }; @@ -26,19 +26,20 @@ const uint32_t tx_value[TX_COUNT] = { MagTxStateLF_P, MagTxCC1101_434, MagTxCC1101_868, - }; -#define TRACK_COUNT 3 +#define TRACK_COUNT 4 const char* const track_text[TRACK_COUNT] = { - "ALL", + "1 + 2", "1", "2", + "3", }; const uint32_t track_value[TRACK_COUNT] = { - MagTrackStateAll, + MagTrackStateOneAndTwo, MagTrackStateOne, MagTrackStateTwo, + MagTrackStateThree, }; #define REVERSE_COUNT 2 @@ -137,7 +138,7 @@ static void mag_scene_emulate_config_set_track(VariableItem* item) { mag->setting->track = track_value[index]; } else if(mag->setting->reverse == MagReverseStateOn) { variable_item_set_current_value_index( - item, value_index_uint32(MagTrackStateAll, track_value, TRACK_COUNT)); + item, value_index_uint32(MagTrackStateOneAndTwo, track_value, TRACK_COUNT)); } // TODO: Check there is data in selected track? @@ -148,10 +149,12 @@ static void mag_scene_emulate_config_set_reverse(VariableItem* item) { Mag* mag = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - if(mag->setting->track == MagTrackStateAll) { - // only allow reverse track to be set when playing all + if(mag->setting->track == MagTrackStateOneAndTwo) { + // only allow reverse track to be set when playing both 1 and 2 variable_item_set_current_value_text(item, reverse_text[index]); mag->setting->reverse = reverse_value[index]; + //FURI_LOG_D(TAG, "%s", reverse_text[index]); + //FURI_LOG_D(TAG, "%d", mag->setting->reverse); } else { variable_item_set_current_value_index( item, value_index_uint32(MagReverseStateOff, reverse_value, REVERSE_COUNT)); @@ -200,9 +203,10 @@ void mag_scene_emulate_config_on_enter(void* context) { variable_item_set_current_value_text(item, track_text[value_index]); // Reverse + //FURI_LOG_D(TAG, "%d", mag->setting->reverse); item = variable_item_list_add( mag->variable_item_list, - "Reverse (WIP):", + "Reverse:", REVERSE_COUNT, mag_scene_emulate_config_set_reverse, mag); @@ -220,6 +224,7 @@ void mag_scene_emulate_config_on_enter(void* context) { variable_item_set_current_value_text(item, clock_text[value_index]); // Interpacket + /* item = variable_item_list_add( mag->variable_item_list, "Interpacket:", @@ -230,7 +235,8 @@ void mag_scene_emulate_config_on_enter(void* context) { value_index_uint32(mag->setting->us_interpacket, interpacket_value, INTERPACKET_COUNT); scene_manager_set_scene_state(mag->scene_manager, MagSceneEmulateConfig, (uint32_t)item); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, interpacket_text[value_index]); + variable_item_set_current_value_text(item, interpacket_text[value_index]);*/ + UNUSED(mag_scene_emulate_config_set_interpacket); view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewVariableItemList); } diff --git a/scenes/mag_scene_saved_info.c b/scenes/mag_scene_saved_info.c index 62ba967b3..fd4e808b5 100644 --- a/scenes/mag_scene_saved_info.c +++ b/scenes/mag_scene_saved_info.c @@ -10,7 +10,7 @@ void mag_scene_saved_info_on_enter(void* context) { // Use strlcpy instead perhaps, to truncate to screen width, then add ellipses if needed? furi_string_printf(tmp_str, "%s\r\n", mag->mag_dev->dev_name); - widget_add_icon_element(widget, 2, 1, &I_mag_file_10px); + widget_add_icon_element(widget, 1, 1, &I_mag_file_10px); widget_add_string_element( widget, 13, 2, AlignLeft, AlignTop, FontPrimary, furi_string_get_cstr(tmp_str)); furi_string_reset(tmp_str); @@ -29,8 +29,6 @@ void mag_scene_saved_info_on_enter(void* context) { widget_add_text_scroll_element(widget, 0, 15, 128, 49, furi_string_get_cstr(tmp_str)); - //widget_add_button_element(widget, GuiButtonTypeLeft, "Back", mag_widget_callback, mag); - view_dispatcher_switch_to_view(mag->view_dispatcher, MagViewWidget); furi_string_free(tmp_str); } diff --git a/scenes/mag_scene_saved_menu.c b/scenes/mag_scene_saved_menu.c index 5672e3697..f2b66de41 100644 --- a/scenes/mag_scene_saved_menu.c +++ b/scenes/mag_scene_saved_menu.c @@ -2,7 +2,7 @@ enum SubmenuIndex { SubmenuIndexEmulate, - SubmenuIndexEdit, + //SubmenuIndexEdit, SubmenuIndexDelete, SubmenuIndexInfo, }; @@ -17,10 +17,26 @@ void mag_scene_saved_menu_on_enter(void* context) { Mag* mag = context; Submenu* submenu = mag->submenu; + // messy code to quickly check which tracks are available for emulation/display + // there's likely a better spot to do this, but the MagDevice functions don't have access to the full mag struct... + bool is_empty_t1 = furi_string_empty(mag->mag_dev->dev_data.track[0].str); + bool is_empty_t2 = furi_string_empty(mag->mag_dev->dev_data.track[1].str); + bool is_empty_t3 = furi_string_empty(mag->mag_dev->dev_data.track[2].str); + + if(!is_empty_t1 && !is_empty_t2) { + mag->setting->track = MagTrackStateOneAndTwo; + } else if(!is_empty_t1) { + mag->setting->track = MagTrackStateOne; + } else if(!is_empty_t2) { + mag->setting->track = MagTrackStateTwo; + } else if(!is_empty_t3) { + mag->setting->track = MagTrackStateThree; + } // TODO: what happens if no track data present? + submenu_add_item( submenu, "Emulate (WIP)", SubmenuIndexEmulate, mag_scene_saved_menu_submenu_callback, mag); - submenu_add_item( - submenu, "Edit (WIP)", SubmenuIndexEdit, mag_scene_saved_menu_submenu_callback, mag); + //submenu_add_item( + // submenu, "Edit (WIP)", SubmenuIndexEdit, mag_scene_saved_menu_submenu_callback, mag); submenu_add_item( submenu, "Delete", SubmenuIndexDelete, mag_scene_saved_menu_submenu_callback, mag); submenu_add_item( @@ -43,9 +59,9 @@ bool mag_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexEmulate) { scene_manager_next_scene(mag->scene_manager, MagSceneEmulate); consumed = true; - } else if(event.event == SubmenuIndexEdit) { - scene_manager_next_scene(mag->scene_manager, MagSceneUnderConstruction); - consumed = true; + //} else if(event.event == SubmenuIndexEdit) { + // scene_manager_next_scene(mag->scene_manager, MagSceneUnderConstruction); + // consumed = true; } else if(event.event == SubmenuIndexDelete) { scene_manager_next_scene(mag->scene_manager, MagSceneDeleteConfirm); consumed = true; diff --git a/scenes/mag_scene_start.c b/scenes/mag_scene_start.c index 1ad044fda..3a1cd2f90 100644 --- a/scenes/mag_scene_start.c +++ b/scenes/mag_scene_start.c @@ -2,7 +2,7 @@ typedef enum { SubmenuIndexSaved, - SubmenuIndexAddManually, + //SubmenuIndexAddManually, SubmenuIndexAbout, } SubmenuIndex; @@ -17,8 +17,8 @@ void mag_scene_start_on_enter(void* context) { Submenu* submenu = mag->submenu; submenu_add_item(submenu, "Saved", SubmenuIndexSaved, mag_scene_start_submenu_callback, mag); - submenu_add_item( - submenu, "Add Manually", SubmenuIndexAddManually, mag_scene_start_submenu_callback, mag); + //submenu_add_item( + // submenu, "Add Manually", SubmenuIndexAddManually, mag_scene_start_submenu_callback, mag); submenu_add_item(submenu, "About", SubmenuIndexAbout, mag_scene_start_submenu_callback, mag); submenu_set_selected_item( @@ -41,10 +41,10 @@ bool mag_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(mag->scene_manager, MagSceneFileSelect); consumed = true; break; - case SubmenuIndexAddManually: - scene_manager_next_scene(mag->scene_manager, MagSceneInputValue); - consumed = true; - break; + //case SubmenuIndexAddManually: + // scene_manager_next_scene(mag->scene_manager, MagSceneInputValue); + // consumed = true; + // break; case SubmenuIndexAbout: scene_manager_next_scene(mag->scene_manager, MagSceneAbout); consumed = true;