diff --git a/applications/flipfrid/LICENSE.md b/applications/flipfrid/LICENSE.md new file mode 100644 index 000000000..a856581c9 --- /dev/null +++ b/applications/flipfrid/LICENSE.md @@ -0,0 +1,8 @@ +/* + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * @G4N4P4T1 wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. + * ---------------------------------------------------------------------------- + */ \ No newline at end of file diff --git a/applications/flipfrid/README.md b/applications/flipfrid/README.md new file mode 100644 index 000000000..d5f79bf5d --- /dev/null +++ b/applications/flipfrid/README.md @@ -0,0 +1,4 @@ +# FlipFrid + +A simple implementation of ZigFrid on Flipper zero (+bonus) +(https://z4ziggy.wordpress.com/2017/07/21/zigfrid-a-passive-rfid-fuzzer/) \ No newline at end of file diff --git a/applications/flipfrid/application.fam b/applications/flipfrid/application.fam new file mode 100644 index 000000000..87b38b6d2 --- /dev/null +++ b/applications/flipfrid/application.fam @@ -0,0 +1,11 @@ +App( + appid="flipfrid", + name="Rfid fuzzer", + apptype=FlipperAppType.PLUGIN, + entry_point="flipfrid_start", + cdefines=["APP_FLIP_FRID"], + requires=["gui"], + stack_size=1 * 1024, + icon="A_125khz_14", + order=185, +) diff --git a/applications/flipfrid/flipfrid.c b/applications/flipfrid/flipfrid.c new file mode 100644 index 000000000..5f38ff458 --- /dev/null +++ b/applications/flipfrid/flipfrid.c @@ -0,0 +1,317 @@ +#include +#include +#include +#include + +#include +#include "raw_em4100.h" +#include + +#include "flipfrid.h" + +#define NUMBER_OF_ATTACKS 4 +#define TIME_BETWEEN_CARDS \ + 5 // Emulate 2 cards per second : (5 * (configTICK_RATE_HZ_RAW/10)) == (5*(1000/10)) == (5*100) == (500)ms +#define TAG "FLIPFRID" + +uint8_t id_list[16][5] = { + {0x00, 0x00, 0x00, 0x00, 0x00}, // Null bytes + {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // Only FF + {0x11, 0x11, 0x11, 0x11, 0x11}, // Only 11 + {0x22, 0x22, 0x22, 0x22, 0x22}, // Only 22 + {0x33, 0x33, 0x33, 0x33, 0x33}, // Only 33 + {0x44, 0x44, 0x44, 0x44, 0x44}, // Only 44 + {0x55, 0x55, 0x55, 0x55, 0x55}, // Only 55 + {0x66, 0x66, 0x66, 0x66, 0x66}, // Only 66 + {0x77, 0x77, 0x77, 0x77, 0x77}, // Only 77 + {0x88, 0x88, 0x88, 0x88, 0x88}, // Only 88 + {0x99, 0x99, 0x99, 0x99, 0x99}, // Only 99 + {0x12, 0x34, 0x56, 0x78, 0x9A}, // Incremental UID + {0x04, 0xd0, 0x9b, 0x0d, 0x6a}, // From arha + {0x34, 0x00, 0x29, 0x3d, 0x9e}, // From arha + {0x04, 0xdf, 0x00, 0x00, 0x01}, // From arha + {0xCA, 0xCA, 0xCA, 0xCA, 0xCA}, // From arha +}; + +typedef enum { + DefaultKeys, + BruteForceCustomerId, + BadCrc, +} AttackType; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType evt_type; + InputKey key; + InputType input_type; +} FlipFridEvent; + +// STRUCTS +typedef struct { + bool emitting; + AttackType current_attack_type; + uint8_t* current_uid; + uint8_t meta_data; + NotificationApp* notify; +} FlipFridState; + +static void flipfrid_draw_callback(Canvas* const canvas, void* ctx) { + const FlipFridState* flipfrid_state = (FlipFridState*)acquire_mutex((ValueMutex*)ctx, 100); + + if(flipfrid_state == NULL) { + return; + } + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + // Frame + canvas_draw_frame(canvas, 0, 0, 128, 64); + + // Title + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignTop, "Flip/Frid"); + + // Badge Type + char uid[16]; + char badge_type[21]; + switch(flipfrid_state->current_attack_type) { + case DefaultKeys: + strcpy(badge_type, " Default Values >"); + break; + case BruteForceCustomerId: + strcpy(badge_type, "< BF Customer ID >"); + break; + case BadCrc: + strcpy(badge_type, "< Bad CRC "); + break; + default: + break; + } + + if(flipfrid_state->current_attack_type == BruteForceCustomerId) { + snprintf(uid, sizeof(uid), " ID : %2X ", flipfrid_state->current_uid[0]); + } else if (flipfrid_state->current_attack_type == BadCrc) { + snprintf(uid, sizeof(uid), "Sending packets"); + } else { + snprintf( + uid, + sizeof(uid), + "%2X:%2X:%2X:%2X:%2X", + flipfrid_state->current_uid[0], + flipfrid_state->current_uid[1], + flipfrid_state->current_uid[2], + flipfrid_state->current_uid[3], + flipfrid_state->current_uid[4]); + } + + // Badge infos + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, badge_type); + + if(flipfrid_state->emitting) { + canvas_draw_str_aligned(canvas, 64, 42, AlignCenter, AlignCenter, uid); + } else { + canvas_draw_str_aligned( + canvas, 64, 42, AlignCenter, AlignCenter, "Press OK to start/stop"); + } + + release_mutex((ValueMutex*)ctx, flipfrid_state); +} + +void flipfrid_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + FlipFridEvent event = { + .evt_type = EventTypeKey, .key = input_event->key, .input_type = input_event->type}; + furi_message_queue_put(event_queue, &event, 25); +} + +static void flipfrid_timer_callback(FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + FlipFridEvent event = { + .evt_type = EventTypeTick, .key = InputKeyUp, .input_type = InputTypeRelease}; + furi_message_queue_put(event_queue, &event, 25); +} + +// ENTRYPOINT +int32_t flipfrid_start(void* p) { + UNUSED(p); + + // Input + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlipFridEvent)); + FlipFridState* flipfrid_state = (FlipFridState*)malloc(sizeof(FlipFridState)); + ValueMutex flipfrid_state_mutex; + + // Mutex + if(!init_mutex(&flipfrid_state_mutex, flipfrid_state, sizeof(FlipFridState))) { + furi_message_queue_free(event_queue); + free(flipfrid_state); + } + + // Configure view port + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, flipfrid_draw_callback, &flipfrid_state_mutex); + view_port_input_callback_set(view_port, flipfrid_input_callback, event_queue); + + // Configure timer + FuriTimer* timer = + furi_timer_alloc(flipfrid_timer_callback, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, furi_kernel_get_tick_frequency() / 10); // configTICK_RATE_HZ_RAW 1000 + + // Register view port in GUI + Gui* gui = (Gui*)furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + // Init values + FlipFridEvent event; + flipfrid_state->emitting = false; + flipfrid_state->current_uid = id_list[0]; + flipfrid_state->current_attack_type = DefaultKeys; + flipfrid_state->meta_data = 0; + flipfrid_state->notify = furi_record_open(RECORD_NOTIFICATION); + + // RFID Configuration + size_t data_size = 5; // Default EM4100 data size is 5 (1 customer id + 4 uid) + LFRFIDWorker* worker; + const ProtocolBase* lfrfid_protocol[] = {&protocol_raw_em4100, &protocol_raw_wrong_crc_em4100}; + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocol, 2); + worker = lfrfid_worker_alloc(dict); + uint8_t selectedProtocol = CLEAN; + + // Application state + int menu_selected_item_index = 0; // Menu current item index + uint8_t counter = 0; // Used to count the step and wqaiting time between each test + uint8_t attack_state = 0; // Used to store the current attack state + uint8_t candidate[5]; // uid candidate + bool running = true; // Used to stop the application + + // Main loop + while(running) { + // Get next event + FuriStatus event_status = furi_message_queue_get(event_queue, &event, 25); + if(event_status == FuriStatusOk) { + if(event.evt_type == EventTypeKey) { + if(event.input_type == InputTypeShort) { + counter = 0; + switch(event.key) { + case InputKeyUp: + case InputKeyDown: + // OSEF + break; + case InputKeyRight: + // Next badge type + flipfrid_state->emitting = false; + if(menu_selected_item_index < (NUMBER_OF_ATTACKS - 1)) { + menu_selected_item_index++; + flipfrid_state->current_attack_type = + (AttackType)menu_selected_item_index; + } + break; + case InputKeyLeft: + // Previous badge type + flipfrid_state->emitting = false; + if(menu_selected_item_index > 0) { + menu_selected_item_index--; + flipfrid_state->current_attack_type = + (AttackType)menu_selected_item_index; + } + break; + case InputKeyOk: + if(flipfrid_state->emitting) { + flipfrid_state->emitting = false; + attack_state = 0; + // TODO FIX BLINK + notification_message(flipfrid_state->notify, &sequence_blink_stop); + } else { + flipfrid_state->emitting = true; + attack_state = 0; + // TODO FIX BLINK + notification_message(flipfrid_state->notify, &sequence_blink_start_magenta); + } + break; + case InputKeyBack: + flipfrid_state->emitting = false; + running = false; + break; + } + } + } else if(event.evt_type == EventTypeTick) { + // Emulate card + if(flipfrid_state->emitting) { + if(1 == counter) { + protocol_dict_set_data( + dict, (LFRFIDProtocol)0, flipfrid_state->current_uid, data_size); + worker = lfrfid_worker_alloc(dict); + lfrfid_worker_start_thread(worker); + lfrfid_worker_emulate_start(worker, (LFRFIDProtocol)selectedProtocol); + } else if(0 == counter) { + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + // set next value + switch(flipfrid_state->current_attack_type) { + case DefaultKeys: { + selectedProtocol = CLEAN; + data_size = 5; + flipfrid_state->current_uid = id_list[attack_state]; + attack_state = attack_state + 1; + if(attack_state >= sizeof(id_list) / sizeof(id_list[0])) { + attack_state = 0; + } + break; + } + case BruteForceCustomerId: { + data_size = 5; + selectedProtocol = CLEAN; + candidate[0] = attack_state; + flipfrid_state->current_uid = candidate; + attack_state = attack_state + 1; + if((attack_state + 1) == 256) { + attack_state = 0; + } + break; + } + case BadCrc: { + selectedProtocol = BAD_CRC; + data_size = 5; + candidate[0] = 0xFF; + candidate[1] = 0xDE; + candidate[2] = 0xAD; + candidate[3] = 0xBE; + candidate[4] = 0xEF; + flipfrid_state->current_uid = candidate; + break; + } + } + } + if(counter > TIME_BETWEEN_CARDS) { + counter = 0; + } else { + counter++; + } + } + view_port_update(view_port); + } + } + } + + // Cleanup + notification_message(flipfrid_state->notify, &sequence_blink_stop); + lfrfid_worker_stop(worker); + lfrfid_worker_stop_thread(worker); + lfrfid_worker_free(worker); + protocol_dict_free(dict); + furi_timer_stop(timer); + furi_timer_free(timer); + free(flipfrid_state); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_message_queue_free(event_queue); + furi_record_close(RECORD_GUI); + return 0; +} \ No newline at end of file diff --git a/applications/flipfrid/flipfrid.h b/applications/flipfrid/flipfrid.h new file mode 100644 index 000000000..1b2369a4c --- /dev/null +++ b/applications/flipfrid/flipfrid.h @@ -0,0 +1,7 @@ +#pragma once +#include +#include +#include +#include "raw_em4100.h" + +int32_t flipfrid_start(void* p); \ No newline at end of file diff --git a/applications/flipfrid/raw_em4100.c b/applications/flipfrid/raw_em4100.c new file mode 100644 index 000000000..4cc477ee7 --- /dev/null +++ b/applications/flipfrid/raw_em4100.c @@ -0,0 +1,106 @@ +#include "raw_em4100.h" +#include + +bool protocol_em4100_raw_encoder_start(ProtocolEM4100* proto) { + FURI_LOG_D("RAW_EM4100", "encoder_start : CLEAN"); + // header + proto->encoded_data = 0b111111111; + + // data + for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) { + em4100_write_nibble(false, proto->data[i], &proto->encoded_data); + em4100_write_nibble(true, proto->data[i], &proto->encoded_data); + } + + // column parity and stop bit + uint8_t parity_sum; + + for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { + parity_sum = 0; + for(uint8_t i = 1; i <= EM_ROW_COUNT; i++) { + uint8_t parity_bit = (proto->encoded_data >> (i * EM_BITS_PER_ROW_COUNT - 1)) & 1; + parity_sum += parity_bit; + } + proto->encoded_data = (proto->encoded_data << 1) | ((parity_sum % 2) & 1); + } + + // stop bit + proto->encoded_data = (proto->encoded_data << 1) | 0; + + proto->encoded_data_index = 0; + proto->encoded_polarity = true; + + return true; +}; + +bool protocol_em4100_wrong_crc_encoder_start(ProtocolEM4100* proto) { + FURI_LOG_D("RAW_EM4100", "encoder_start : WRONG CRC"); + // header + proto->encoded_data = 0b111111111; + + // data + for(uint8_t i = 0; i < EM4100_DECODED_DATA_SIZE; i++) { + em4100_write_nibble(false, proto->data[i], &proto->encoded_data); + em4100_write_nibble(true, proto->data[i], &proto->encoded_data); + } + + for(uint8_t c = 0; c < EM_COLUMN_COUNT; c++) { + proto->encoded_data = (proto->encoded_data << 1) | ((0 % 2) & 1); + } + + // stop bit + proto->encoded_data = (proto->encoded_data << 1) | 1; + + proto->encoded_data_index = 0; + proto->encoded_polarity = true; + + return true; +}; + +const ProtocolBase protocol_raw_em4100 = { + .name = "RawEM4100", + .manufacturer = "EM-Micro", + .data_size = EM4100_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_em4100_alloc, + .free = (ProtocolFree)protocol_em4100_free, + .get_data = (ProtocolGetData)protocol_em4100_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_em4100_raw_encoder_start, + .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_em4100_render_data, + .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, + .write_data = (ProtocolWriteData)protocol_em4100_write_data, +}; + +const ProtocolBase protocol_raw_wrong_crc_em4100 = { + .name = "RawEM4100", + .manufacturer = "EM-Micro", + .data_size = EM4100_DECODED_DATA_SIZE, + .features = LFRFIDFeatureASK | LFRFIDFeaturePSK, + .validate_count = 3, + .alloc = (ProtocolAlloc)protocol_em4100_alloc, + .free = (ProtocolFree)protocol_em4100_free, + .get_data = (ProtocolGetData)protocol_em4100_get_data, + .decoder = + { + .start = (ProtocolDecoderStart)protocol_em4100_decoder_start, + .feed = (ProtocolDecoderFeed)protocol_em4100_decoder_feed, + }, + .encoder = + { + .start = (ProtocolEncoderStart)protocol_em4100_wrong_crc_encoder_start, + .yield = (ProtocolEncoderYield)protocol_em4100_encoder_yield, + }, + .render_data = (ProtocolRenderData)protocol_em4100_render_data, + .render_brief_data = (ProtocolRenderData)protocol_em4100_render_data, + .write_data = (ProtocolWriteData)protocol_em4100_write_data, +}; \ No newline at end of file diff --git a/applications/flipfrid/raw_em4100.h b/applications/flipfrid/raw_em4100.h new file mode 100644 index 000000000..faa2ccd9e --- /dev/null +++ b/applications/flipfrid/raw_em4100.h @@ -0,0 +1,11 @@ +#pragma once +#include + + +enum FlipFridProtocol { + CLEAN, + BAD_CRC, +}; + +extern const ProtocolBase protocol_raw_em4100; +extern const ProtocolBase protocol_raw_wrong_crc_em4100; \ No newline at end of file diff --git a/applications/meta/application.fam b/applications/meta/application.fam index 2859d144d..0d654eb9c 100644 --- a/applications/meta/application.fam +++ b/applications/meta/application.fam @@ -36,6 +36,7 @@ App( provides=[ "music_player", "bt_hid", + "flipfrid", ], )