Pārlūkot izejas kodu

Merge pull request #1 from arha/arha-bitwise

Zachary Weiss 3 gadi atpakaļ
vecāks
revīzija
c347fc529d

+ 11 - 0
README.md

@@ -42,6 +42,17 @@ External RX options:
 - 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.
 - USB HID input likely infeasible; seems the FZ cannot act as an HID host.
 
+## arha-bitwise branch todo & notes
+Attempting to exploit flipper hardware to some extent
+
+- [X] Preprocess all MSR data into bitwise arrays, including manchester encoding. 
+- [ ] Feed bits from timers
+- [ ] Sync to the lfrfid timer and experiment representing a field flip with a few cycles of a high frequency carrier, like the 125khz lfrfid one. Perhaps mag readers' frontends will lowpass such signals, and keep only the low frequency component, in an attempt to drown out nearby noise?
+- [X] Can the CC1101 radio be used in any way? Driving it from GD0 can achieve 50us, or about 10khz. Probably more with sync/packet mode. **Currently under testing**. The signal is extra noisy with a very wide bandwidth, but, in theory, it can work
+- [ ] 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.
+- [ ] Investigate transparent mode on 3916
+- [ ] Can the piezo be used at its resonant frequency? I've seen LF signals being emulated with [nothing but headphones](https://github.com/smre/DCF77/blob/master/DCF77.py#L124) running a subharmonic; the wheel brake on some carts seems to react to audiofreq signals (or the RF emission from driving a speaker)
+
 ----
 ## Credits
 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.  

+ 7 - 0
assets/samy-javier-branch.mag

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

+ 8 - 0
assets/samy.mag

@@ -0,0 +1,8 @@
+Filetype: Flipper Mag device
+Version: 1
+# Mag device track data
+# Track 1: %B123456781234567^LASTNAME/FIRST^YYMMSSSDDDDDDDDDDDDDDDDDDDDDDDDD?
+Track 1: %B426684131234567^LASTNAME/FIRST^YYMMSSSDDDDDDDDDDDDDDDDDDDDDDDDD?
+# Track 2: ;123456781234567=YYMMSSSDDDDDDDDDDDDDD?
+Track 2: ;426684131234567=230188855555555555555?
+Track 3: 

+ 342 - 0
helpers/mag_helpers.c

@@ -16,6 +16,50 @@ 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)
+{
+    switch(setting->tx) {
+        case MagTxStateRFID:
+            furi_hal_gpio_write(RFID_PIN_OUT, value);
+            break;
+        case MagTxStateGPIOA6A7:
+            furi_hal_gpio_write(GPIO_PIN_A, value);
+            furi_hal_gpio_write(GPIO_PIN_B, !value);
+            break;
+        case MagTxCC1101_434:
+        case MagTxCC1101_868:
+            if (last_value == 2 || value != (bool)last_value)
+            {
+                furi_hal_gpio_write(&gpio_cc1101_g0, true);
+                furi_delay_us(64);
+                furi_hal_gpio_write(&gpio_cc1101_g0, false);
+            }
+            break;
+        default:
+            break;
+    }
+
+    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
@@ -58,6 +102,10 @@ bool play_bit(uint8_t send_bit, MagSetting* setting) {
     case MagTxStateGPIOA6A7:
         play_bit_gpio(send_bit, setting);
         break;
+    case MagTxCC1101_434:
+    case MagTxCC1101_868:
+        play_bit_rf(send_bit & 0x01, setting);
+        break;
     default:
         return false;
     }
@@ -120,6 +168,28 @@ void tx_reset_gpio() {
     furi_hal_power_disable_otg();
 }
 
+void tx_init_rf(int hz)
+{
+    // presets and frequency will need some experimenting
+    furi_hal_subghz_reset();
+    furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
+    // furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync);
+    // furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync);
+    // furi_hal_subghz_load_preset(FuriHalSubGhzPreset2FSKDev238Async);
+    // furi_hal_subghz_load_preset(FuriHalSubGhzPreset2FSKDev476Async);
+    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+    furi_hal_subghz_set_frequency_and_path(hz);
+    furi_hal_subghz_tx();
+    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();
+}
+
 bool tx_init(MagSetting* setting) {
     // Initialize configured TX method
     switch(setting->tx) {
@@ -129,6 +199,12 @@ bool tx_init(MagSetting* setting) {
     case MagTxStateGPIOA6A7:
         tx_init_gpio();
         break;
+    case MagTxCC1101_434:
+        tx_init_rf(434000000);
+        break;
+    case MagTxCC1101_868:
+        tx_init_rf(868000000);
+        break;
     default:
         return false;
     }
@@ -145,6 +221,10 @@ bool tx_reset(MagSetting* setting) {
     case MagTxStateGPIOA6A7:
         tx_reset_gpio();
         break;
+    case MagTxCC1101_434:
+    case MagTxCC1101_868:
+        tx_deinit_rf();
+        break;
     default:
         return false;
     }
@@ -214,6 +294,139 @@ void track_to_bits(uint8_t* bit_array, const char* track_data, uint8_t track_ind
     //furi_string_free(tmp_str);
 }
 
+void mag_spoof_bitwise(Mag* mag) {
+    MagSetting* setting = mag->setting;
+
+
+    FuriString* ft1 = mag->mag_dev->dev_data.track[0].str;
+    FuriString* ft2 = mag->mag_dev->dev_data.track[1].str;
+
+    char* data1; char* data2;
+    data1 = malloc(furi_string_size(ft1)+1);
+    data2 = malloc(furi_string_size(ft2)+1);
+    strncpy(data1, furi_string_get_cstr(ft1), furi_string_size(ft1));
+    strncpy(data2, furi_string_get_cstr(ft2), furi_string_size(ft2));
+
+    if(furi_log_get_level() >= FuriLogLevelDebug) {
+        debug_msr_string(data1, BITS_TRACK1, OFFSET_TRACK1);
+        debug_msr_string(data2, BITS_TRACK2, OFFSET_TRACK2);
+    }
+
+
+    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);
+    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);
+
+    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: ");
+        for (int i = 0; i < bits_t1_count / 8; i++) printf("%02x ", bits_t1_manchester[i]);
+        printf("\r\n");
+
+        printf("T2 raw: ");
+        for (int i = 0; i < bits_t2_count / 16; i++) printf("%02x ", bits_t2_raw[i]);
+        printf("\r\n");
+
+        printf("T2 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");
+    }
+
+    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((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));
+        /* this comment is mostly for zw's convenience:
+         *
+         * 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);
+    }
+
+    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 == 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);
+        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);
+    }
+
+    FURI_CRITICAL_EXIT();
+    free(data1);
+    free(data2);
+    tx_reset(setting);
+
+}
+
 void mag_spoof(Mag* mag) {
     MagSetting* setting = mag->setting;
 
@@ -316,3 +529,132 @@ bool get_bit(uint8_t* b, uint32_t blen, uint32_t bitpos) {
     }
     return bitpos;
 }*/
+
+
+
+
+uint16_t add_bit(bool value, uint8_t* out, uint16_t count)
+{
+    uint8_t bit = count % 8;
+    uint8_t byte = count / 8;
+    if (value)
+    {
+        out[byte] |= 0x01;
+    }
+    if (bit < 7) out[byte] <<= 1;
+    return count+1;
+}
+
+uint16_t add_bit_manchester(bool value, uint8_t* out, uint16_t count)
+{
+    static bool toggle = 0;
+    toggle ^= 0x01;
+    count = add_bit(toggle, out, count);
+    if (value) toggle ^= 0x01;
+    count = add_bit(toggle, out, count);
+    return count;
+}
+
+
+uint16_t msr_encode(char* data, uint8_t* out_manchester, uint8_t* out_raw, uint8_t track_bits, uint8_t track_ascii_offset)
+{
+    /*
+     * 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
+     *            - this count includes the parity bit
+     * track_ascii_offset - how much the ascii values are offset. track 1 makes space (ascii 32) become data 0x00,
+     *                    - tracks 2/3 make ascii "0" become data 0x00 - this is samy's sublen
+     *
+     */
+
+    uint16_t raw_bits_count = 0;
+    uint16_t output_count = 0;
+    int tmp, crc, lrc = 0;
+
+    for (int i = 0; i < PREFIX_NUM_ZEROES; 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;
+        tmp = *(data+i) - track_ascii_offset;
+
+        for (int j = 0; j < track_bits-1; j++)
+        {
+            crc ^= tmp & 1;
+            lrc ^= (tmp & 1) << j;
+            raw_bits_count = add_bit(tmp & 0x01, out_raw, raw_bits_count);
+            output_count = add_bit_manchester(tmp & 0x01, out_manchester, output_count);
+            tmp >>= 1;
+        }
+        raw_bits_count = add_bit(crc, out_raw, raw_bits_count);
+        output_count = add_bit_manchester(crc, out_manchester, output_count);
+    }
+
+
+    // LRC byte
+    tmp = lrc;
+    crc = 1;
+    for (int j = 0; j < track_bits-1; j++)
+    {
+        crc ^= tmp & 0x01;
+        raw_bits_count = add_bit(tmp & 0x01, out_raw, raw_bits_count);
+        output_count = add_bit_manchester(tmp & 0x01, out_manchester, output_count);
+        tmp >>= 1;
+    }
+    raw_bits_count = add_bit(crc, out_raw, raw_bits_count);
+    output_count = add_bit_manchester(crc, out_manchester, output_count);
+
+    return output_count;
+}
+
+void debug_msr_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(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:     ");
+    for(int i = 0; i < numbits / 8 / 2; i++)
+    {
+        printf("%02x", bits_raw[i]);
+        if (i%4==3) printf(" ");
+    }
+
+    printf("\r\n");
+
+    printf("Bits                 ");
+    int space_counter = 0;
+    for(int i = 0; i < numbits / 2; i++)
+    {
+        if (i < PREFIX_NUM_ZEROES)
+        {
+            printf("X");
+            continue;
+        }
+        else if (i == PREFIX_NUM_ZEROES)
+        {
+            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++;
+    }
+
+    printf("\r\n");
+
+    printf("Manchester encoded, byte stream: ");
+    for(int i = 0; i < numbits / 8; i++)
+    {
+        printf("%02x", bits_manchester[i]);
+        if (i%4==3) printf(" ");
+    }
+    printf("\r\n\r\n");
+}

+ 14 - 0
helpers/mag_helpers.h

@@ -15,3 +15,17 @@ void track_to_bits(uint8_t* bit_array, const char* track_data, uint8_t track_ind
 void mag_spoof(Mag* mag);
 void set_bit(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val);
 bool get_bit(uint8_t* b, uint32_t blen, uint32_t bitpos);
+
+
+#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(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);

+ 2 - 0
helpers/mag_types.h

@@ -29,4 +29,6 @@ typedef enum {
 typedef enum {
     MagTxStateRFID,
     MagTxStateGPIOA6A7,
+    MagTxCC1101_434,
+    MagTxCC1101_868,
 } MagTxState;

+ 3 - 0
mag_i.h

@@ -7,6 +7,9 @@
 
 #include <furi.h>
 #include <furi_hal.h>
+#include <furi/core/log.h>
+#include <furi_hal_gpio.h>
+#include <furi_hal_resources.h>
 
 #include <gui/gui.h>
 #include <gui/view.h>

+ 7 - 0
scenes/mag_scene_emulate.c

@@ -39,6 +39,7 @@ 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);
@@ -61,6 +62,12 @@ 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;
         }
     }
 

+ 6 - 1
scenes/mag_scene_emulate_config.c

@@ -10,14 +10,19 @@ enum MagSettingIndex {
     MagSettingIndexInterpacket,
 };
 
-#define TX_COUNT 2
+#define TX_COUNT 4
 const char* const tx_text[TX_COUNT] = {
     "RFID",
     "A6/A7",
+    "434MHz",
+    "868MHz",
 };
 const uint32_t tx_value[TX_COUNT] = {
     MagTxStateRFID,
     MagTxStateGPIOA6A7,
+    MagTxCC1101_434,
+    MagTxCC1101_868,
+
 };
 
 #define TRACK_COUNT 3