mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-13 20:58:36 -07:00
Merge branch 'fz-dev' into dev
This commit is contained in:
9
applications/external/picopass/picopass.c
vendored
9
applications/external/picopass/picopass.c
vendored
@@ -73,6 +73,12 @@ Picopass* picopass_alloc() {
|
|||||||
view_dispatcher_add_view(
|
view_dispatcher_add_view(
|
||||||
picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget));
|
picopass->view_dispatcher, PicopassViewWidget, widget_get_view(picopass->widget));
|
||||||
|
|
||||||
|
picopass->dict_attack = dict_attack_alloc();
|
||||||
|
view_dispatcher_add_view(
|
||||||
|
picopass->view_dispatcher,
|
||||||
|
PicopassViewDictAttack,
|
||||||
|
dict_attack_get_view(picopass->dict_attack));
|
||||||
|
|
||||||
return picopass;
|
return picopass;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +109,9 @@ void picopass_free(Picopass* picopass) {
|
|||||||
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget);
|
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewWidget);
|
||||||
widget_free(picopass->widget);
|
widget_free(picopass->widget);
|
||||||
|
|
||||||
|
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewDictAttack);
|
||||||
|
dict_attack_free(picopass->dict_attack);
|
||||||
|
|
||||||
// Worker
|
// Worker
|
||||||
picopass_worker_stop(picopass->worker);
|
picopass_worker_stop(picopass->worker);
|
||||||
picopass_worker_free(picopass->worker);
|
picopass_worker_free(picopass->worker);
|
||||||
|
|||||||
@@ -27,8 +27,16 @@
|
|||||||
#define PICOPASS_APP_EXTENSION ".picopass"
|
#define PICOPASS_APP_EXTENSION ".picopass"
|
||||||
#define PICOPASS_APP_SHADOW_EXTENSION ".pas"
|
#define PICOPASS_APP_SHADOW_EXTENSION ".pas"
|
||||||
|
|
||||||
|
#define PICOPASS_DICT_KEY_BATCH_SIZE 10
|
||||||
|
|
||||||
typedef void (*PicopassLoadingCallback)(void* context, bool state);
|
typedef void (*PicopassLoadingCallback)(void* context, bool state);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
IclassEliteDict* dict;
|
||||||
|
IclassEliteDictType type;
|
||||||
|
uint8_t current_sector;
|
||||||
|
} IclassEliteDictAttackData;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PicopassDeviceEncryptionUnknown = 0,
|
PicopassDeviceEncryptionUnknown = 0,
|
||||||
PicopassDeviceEncryptionNone = 0x14,
|
PicopassDeviceEncryptionNone = 0x14,
|
||||||
@@ -69,6 +77,7 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT];
|
PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT];
|
||||||
PicopassPacs pacs;
|
PicopassPacs pacs;
|
||||||
|
IclassEliteDictAttackData iclass_elite_dict_attack_data;
|
||||||
} PicopassDeviceData;
|
} PicopassDeviceData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
4
applications/external/picopass/picopass_i.h
vendored
4
applications/external/picopass/picopass_i.h
vendored
@@ -21,6 +21,7 @@
|
|||||||
#include <input/input.h>
|
#include <input/input.h>
|
||||||
|
|
||||||
#include "scenes/picopass_scene.h"
|
#include "scenes/picopass_scene.h"
|
||||||
|
#include "views/dict_attack.h"
|
||||||
|
|
||||||
#include <storage/storage.h>
|
#include <storage/storage.h>
|
||||||
#include <lib/toolbox/path.h>
|
#include <lib/toolbox/path.h>
|
||||||
@@ -36,6 +37,7 @@ enum PicopassCustomEvent {
|
|||||||
PicopassCustomEventWorkerExit,
|
PicopassCustomEventWorkerExit,
|
||||||
PicopassCustomEventByteInputDone,
|
PicopassCustomEventByteInputDone,
|
||||||
PicopassCustomEventTextInputDone,
|
PicopassCustomEventTextInputDone,
|
||||||
|
PicopassCustomEventDictAttackSkip,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -60,6 +62,7 @@ struct Picopass {
|
|||||||
Loading* loading;
|
Loading* loading;
|
||||||
TextInput* text_input;
|
TextInput* text_input;
|
||||||
Widget* widget;
|
Widget* widget;
|
||||||
|
DictAttack* dict_attack;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -68,6 +71,7 @@ typedef enum {
|
|||||||
PicopassViewLoading,
|
PicopassViewLoading,
|
||||||
PicopassViewTextInput,
|
PicopassViewTextInput,
|
||||||
PicopassViewWidget,
|
PicopassViewWidget,
|
||||||
|
PicopassViewDictAttack,
|
||||||
} PicopassView;
|
} PicopassView;
|
||||||
|
|
||||||
Picopass* picopass_alloc();
|
Picopass* picopass_alloc();
|
||||||
|
|||||||
141
applications/external/picopass/picopass_worker.c
vendored
141
applications/external/picopass/picopass_worker.c
vendored
@@ -23,7 +23,7 @@ PicopassWorker* picopass_worker_alloc() {
|
|||||||
|
|
||||||
// Worker thread attributes
|
// Worker thread attributes
|
||||||
picopass_worker->thread =
|
picopass_worker->thread =
|
||||||
furi_thread_alloc_ex("PicopassWorker", 8192, picopass_worker_task, picopass_worker);
|
furi_thread_alloc_ex("PicopassWorker", 8 * 1024, picopass_worker_task, picopass_worker);
|
||||||
|
|
||||||
picopass_worker->callback = NULL;
|
picopass_worker->callback = NULL;
|
||||||
picopass_worker->context = NULL;
|
picopass_worker->context = NULL;
|
||||||
@@ -66,15 +66,13 @@ void picopass_worker_start(
|
|||||||
|
|
||||||
void picopass_worker_stop(PicopassWorker* picopass_worker) {
|
void picopass_worker_stop(PicopassWorker* picopass_worker) {
|
||||||
furi_assert(picopass_worker);
|
furi_assert(picopass_worker);
|
||||||
if(picopass_worker->state == PicopassWorkerStateBroken ||
|
furi_assert(picopass_worker->thread);
|
||||||
picopass_worker->state == PicopassWorkerStateReady) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
picopass_worker_disable_field(ERR_NONE);
|
|
||||||
|
|
||||||
|
if(furi_thread_get_state(picopass_worker->thread) != FuriThreadStateStopped) {
|
||||||
picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
|
picopass_worker_change_state(picopass_worker, PicopassWorkerStateStop);
|
||||||
furi_thread_join(picopass_worker->thread);
|
furi_thread_join(picopass_worker->thread);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) {
|
void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state) {
|
||||||
picopass_worker->state = state;
|
picopass_worker->state = state;
|
||||||
@@ -460,6 +458,132 @@ ReturnCode picopass_write_block(PicopassBlock* AA1, uint8_t blockNo, uint8_t* ne
|
|||||||
return ERR_NONE;
|
return ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) {
|
||||||
|
furi_assert(picopass_worker);
|
||||||
|
furi_assert(picopass_worker->callback);
|
||||||
|
|
||||||
|
picopass_device_data_clear(picopass_worker->dev_data);
|
||||||
|
PicopassDeviceData* dev_data = picopass_worker->dev_data;
|
||||||
|
PicopassBlock* AA1 = dev_data->AA1;
|
||||||
|
PicopassPacs* pacs = &dev_data->pacs;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
|
||||||
|
memset(AA1[i].data, 0, sizeof(AA1[i].data));
|
||||||
|
}
|
||||||
|
memset(pacs, 0, sizeof(PicopassPacs));
|
||||||
|
|
||||||
|
IclassEliteDictAttackData* dict_attack_data =
|
||||||
|
&picopass_worker->dev_data->iclass_elite_dict_attack_data;
|
||||||
|
bool elite = (dict_attack_data->type != IclassStandardDictTypeFlipper);
|
||||||
|
|
||||||
|
rfalPicoPassReadCheckRes rcRes;
|
||||||
|
rfalPicoPassCheckRes chkRes;
|
||||||
|
|
||||||
|
ReturnCode err;
|
||||||
|
uint8_t mac[4] = {0};
|
||||||
|
uint8_t ccnr[12] = {0};
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
uint8_t key[PICOPASS_BLOCK_LEN] = {0};
|
||||||
|
|
||||||
|
// Load dictionary
|
||||||
|
IclassEliteDict* dict = dict_attack_data->dict;
|
||||||
|
if(!dict) {
|
||||||
|
FURI_LOG_E(TAG, "Dictionary not found");
|
||||||
|
picopass_worker->callback(PicopassWorkerEventNoDictFound, picopass_worker->context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
if(picopass_detect_card(1000) == ERR_NONE) {
|
||||||
|
picopass_worker->callback(PicopassWorkerEventCardDetected, picopass_worker->context);
|
||||||
|
|
||||||
|
// Process first found device
|
||||||
|
err = picopass_read_preauth(AA1);
|
||||||
|
if(err != ERR_NONE) {
|
||||||
|
FURI_LOG_E(TAG, "picopass_read_preauth error %d", err);
|
||||||
|
picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thank you proxmark!
|
||||||
|
pacs->legacy = picopass_is_memset(AA1[5].data, 0xFF, 8);
|
||||||
|
pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
|
||||||
|
if(pacs->se_enabled) {
|
||||||
|
FURI_LOG_D(TAG, "SE enabled");
|
||||||
|
picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
picopass_worker->callback(PicopassWorkerEventNoCardDetected, picopass_worker->context);
|
||||||
|
}
|
||||||
|
if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break;
|
||||||
|
|
||||||
|
furi_delay_ms(100);
|
||||||
|
} while(true);
|
||||||
|
|
||||||
|
FURI_LOG_D(
|
||||||
|
TAG, "Start Dictionary attack, Key Count %lu", iclass_elite_dict_get_total_keys(dict));
|
||||||
|
while(iclass_elite_dict_get_next_key(dict, key)) {
|
||||||
|
FURI_LOG_T(TAG, "Key %zu", index);
|
||||||
|
if(++index % PICOPASS_DICT_KEY_BATCH_SIZE == 0) {
|
||||||
|
picopass_worker->callback(
|
||||||
|
PicopassWorkerEventNewDictKeyBatch, picopass_worker->context);
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rfalPicoPassPollerReadCheck(&rcRes);
|
||||||
|
if(err != ERR_NONE) {
|
||||||
|
FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0
|
||||||
|
|
||||||
|
uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
|
||||||
|
uint8_t* div_key = AA1[PICOPASS_KD_BLOCK_INDEX].data;
|
||||||
|
|
||||||
|
loclass_iclass_calc_div_key(csn, key, div_key, elite);
|
||||||
|
loclass_opt_doReaderMAC(ccnr, div_key, mac);
|
||||||
|
|
||||||
|
err = rfalPicoPassPollerCheck(mac, &chkRes);
|
||||||
|
if(err == ERR_NONE) {
|
||||||
|
FURI_LOG_I(TAG, "Found key");
|
||||||
|
memcpy(pacs->key, key, PICOPASS_BLOCK_LEN);
|
||||||
|
err = picopass_read_card(AA1);
|
||||||
|
if(err != ERR_NONE) {
|
||||||
|
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
|
||||||
|
picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = picopass_device_parse_credential(AA1, pacs);
|
||||||
|
if(err != ERR_NONE) {
|
||||||
|
FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
|
||||||
|
picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
|
||||||
|
if(err != ERR_NONE) {
|
||||||
|
FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
|
||||||
|
picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(picopass_worker->state != PicopassWorkerStateEliteDictAttack) break;
|
||||||
|
}
|
||||||
|
FURI_LOG_D(TAG, "Dictionary complete");
|
||||||
|
if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) {
|
||||||
|
picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);
|
||||||
|
} else {
|
||||||
|
picopass_worker->callback(PicopassWorkerEventAborted, picopass_worker->context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int32_t picopass_worker_task(void* context) {
|
int32_t picopass_worker_task(void* context) {
|
||||||
PicopassWorker* picopass_worker = context;
|
PicopassWorker* picopass_worker = context;
|
||||||
|
|
||||||
@@ -470,9 +594,12 @@ int32_t picopass_worker_task(void* context) {
|
|||||||
picopass_worker_write(picopass_worker);
|
picopass_worker_write(picopass_worker);
|
||||||
} else if(picopass_worker->state == PicopassWorkerStateWriteKey) {
|
} else if(picopass_worker->state == PicopassWorkerStateWriteKey) {
|
||||||
picopass_worker_write_key(picopass_worker);
|
picopass_worker_write_key(picopass_worker);
|
||||||
|
} else if(picopass_worker->state == PicopassWorkerStateEliteDictAttack) {
|
||||||
|
picopass_worker_elite_dict_attack(picopass_worker);
|
||||||
|
} else {
|
||||||
|
FURI_LOG_W(TAG, "Unknown state %d", picopass_worker->state);
|
||||||
}
|
}
|
||||||
picopass_worker_disable_field(ERR_NONE);
|
picopass_worker_disable_field(ERR_NONE);
|
||||||
|
|
||||||
picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
|
picopass_worker_change_state(picopass_worker, PicopassWorkerStateReady);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ typedef enum {
|
|||||||
PicopassWorkerStateDetect,
|
PicopassWorkerStateDetect,
|
||||||
PicopassWorkerStateWrite,
|
PicopassWorkerStateWrite,
|
||||||
PicopassWorkerStateWriteKey,
|
PicopassWorkerStateWriteKey,
|
||||||
|
PicopassWorkerStateEliteDictAttack,
|
||||||
// Transition
|
// Transition
|
||||||
PicopassWorkerStateStop,
|
PicopassWorkerStateStop,
|
||||||
} PicopassWorkerState;
|
} PicopassWorkerState;
|
||||||
@@ -27,8 +28,10 @@ typedef enum {
|
|||||||
PicopassWorkerEventFail,
|
PicopassWorkerEventFail,
|
||||||
PicopassWorkerEventNoCardDetected,
|
PicopassWorkerEventNoCardDetected,
|
||||||
PicopassWorkerEventSeEnabled,
|
PicopassWorkerEventSeEnabled,
|
||||||
|
PicopassWorkerEventAborted,
|
||||||
PicopassWorkerEventStartReading,
|
PicopassWorkerEventCardDetected,
|
||||||
|
PicopassWorkerEventNewDictKeyBatch,
|
||||||
|
PicopassWorkerEventNoDictFound,
|
||||||
} PicopassWorkerEvent;
|
} PicopassWorkerEvent;
|
||||||
|
|
||||||
typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context);
|
typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context);
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ ADD_SCENE(picopass, write_card_success, WriteCardSuccess)
|
|||||||
ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess)
|
ADD_SCENE(picopass, read_factory_success, ReadFactorySuccess)
|
||||||
ADD_SCENE(picopass, write_key, WriteKey)
|
ADD_SCENE(picopass, write_key, WriteKey)
|
||||||
ADD_SCENE(picopass, key_menu, KeyMenu)
|
ADD_SCENE(picopass, key_menu, KeyMenu)
|
||||||
|
ADD_SCENE(picopass, elite_dict_attack, EliteDictAttack)
|
||||||
|
|||||||
170
applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c
vendored
Normal file
170
applications/external/picopass/scenes/picopass_scene_elite_dict_attack.c
vendored
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
#include "../picopass_i.h"
|
||||||
|
#include <dolphin/dolphin.h>
|
||||||
|
|
||||||
|
#define TAG "IclassEliteDictAttack"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DictAttackStateIdle,
|
||||||
|
DictAttackStateUserDictInProgress,
|
||||||
|
DictAttackStateFlipperDictInProgress,
|
||||||
|
DictAttackStateStandardDictInProgress,
|
||||||
|
} DictAttackState;
|
||||||
|
|
||||||
|
void picopass_dict_attack_worker_callback(PicopassWorkerEvent event, void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
Picopass* picopass = context;
|
||||||
|
view_dispatcher_send_custom_event(picopass->view_dispatcher, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void picopass_dict_attack_result_callback(void* context) {
|
||||||
|
furi_assert(context);
|
||||||
|
Picopass* picopass = context;
|
||||||
|
view_dispatcher_send_custom_event(
|
||||||
|
picopass->view_dispatcher, PicopassCustomEventDictAttackSkip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
picopass_scene_elite_dict_attack_prepare_view(Picopass* picopass, DictAttackState state) {
|
||||||
|
IclassEliteDictAttackData* dict_attack_data =
|
||||||
|
&picopass->dev->dev_data.iclass_elite_dict_attack_data;
|
||||||
|
PicopassWorkerState worker_state = PicopassWorkerStateReady;
|
||||||
|
IclassEliteDict* dict = NULL;
|
||||||
|
|
||||||
|
// Identify scene state
|
||||||
|
if(state == DictAttackStateIdle) {
|
||||||
|
if(iclass_elite_dict_check_presence(IclassEliteDictTypeUser)) {
|
||||||
|
FURI_LOG_D(TAG, "Starting with user dictionary");
|
||||||
|
state = DictAttackStateUserDictInProgress;
|
||||||
|
} else {
|
||||||
|
FURI_LOG_D(TAG, "Starting with standard dictionary");
|
||||||
|
state = DictAttackStateStandardDictInProgress;
|
||||||
|
}
|
||||||
|
} else if(state == DictAttackStateUserDictInProgress) {
|
||||||
|
FURI_LOG_D(TAG, "Moving from user dictionary to standard dictionary");
|
||||||
|
state = DictAttackStateStandardDictInProgress;
|
||||||
|
} else if(state == DictAttackStateStandardDictInProgress) {
|
||||||
|
FURI_LOG_D(TAG, "Moving from standard dictionary to elite dictionary");
|
||||||
|
state = DictAttackStateFlipperDictInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup view
|
||||||
|
if(state == DictAttackStateUserDictInProgress) {
|
||||||
|
worker_state = PicopassWorkerStateEliteDictAttack;
|
||||||
|
dict_attack_set_header(picopass->dict_attack, "Elite User Dictionary");
|
||||||
|
dict_attack_data->type = IclassEliteDictTypeUser;
|
||||||
|
dict = iclass_elite_dict_alloc(IclassEliteDictTypeUser);
|
||||||
|
|
||||||
|
// If failed to load user dictionary - try the system dictionary
|
||||||
|
if(!dict) {
|
||||||
|
FURI_LOG_E(TAG, "User dictionary not found");
|
||||||
|
state = DictAttackStateStandardDictInProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(state == DictAttackStateStandardDictInProgress) {
|
||||||
|
worker_state = PicopassWorkerStateEliteDictAttack;
|
||||||
|
dict_attack_set_header(picopass->dict_attack, "Standard System Dictionary");
|
||||||
|
dict_attack_data->type = IclassStandardDictTypeFlipper;
|
||||||
|
dict = iclass_elite_dict_alloc(IclassStandardDictTypeFlipper);
|
||||||
|
|
||||||
|
if(!dict) {
|
||||||
|
FURI_LOG_E(TAG, "Flipper standard dictionary not found");
|
||||||
|
state = DictAttackStateFlipperDictInProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(state == DictAttackStateFlipperDictInProgress) {
|
||||||
|
worker_state = PicopassWorkerStateEliteDictAttack;
|
||||||
|
dict_attack_set_header(picopass->dict_attack, "Elite System Dictionary");
|
||||||
|
dict_attack_data->type = IclassEliteDictTypeFlipper;
|
||||||
|
dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper);
|
||||||
|
if(!dict) {
|
||||||
|
FURI_LOG_E(TAG, "Flipper Elite dictionary not found");
|
||||||
|
// Pass through to let the worker handle the failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Free previous dictionary
|
||||||
|
if(dict_attack_data->dict) {
|
||||||
|
iclass_elite_dict_free(dict_attack_data->dict);
|
||||||
|
}
|
||||||
|
dict_attack_data->dict = dict;
|
||||||
|
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack, state);
|
||||||
|
dict_attack_set_callback(
|
||||||
|
picopass->dict_attack, picopass_dict_attack_result_callback, picopass);
|
||||||
|
dict_attack_set_current_sector(picopass->dict_attack, 0);
|
||||||
|
dict_attack_set_card_detected(picopass->dict_attack);
|
||||||
|
dict_attack_set_total_dict_keys(
|
||||||
|
picopass->dict_attack, dict ? iclass_elite_dict_get_total_keys(dict) : 0);
|
||||||
|
picopass_worker_start(
|
||||||
|
picopass->worker,
|
||||||
|
worker_state,
|
||||||
|
&picopass->dev->dev_data,
|
||||||
|
picopass_dict_attack_worker_callback,
|
||||||
|
picopass);
|
||||||
|
}
|
||||||
|
|
||||||
|
void picopass_scene_elite_dict_attack_on_enter(void* context) {
|
||||||
|
Picopass* picopass = context;
|
||||||
|
picopass_scene_elite_dict_attack_prepare_view(picopass, DictAttackStateIdle);
|
||||||
|
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack);
|
||||||
|
picopass_blink_start(picopass);
|
||||||
|
notification_message(picopass->notifications, &sequence_display_backlight_enforce_on);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent event) {
|
||||||
|
Picopass* picopass = context;
|
||||||
|
bool consumed = false;
|
||||||
|
|
||||||
|
uint32_t state =
|
||||||
|
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack);
|
||||||
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
|
if(event.event == PicopassWorkerEventSuccess ||
|
||||||
|
event.event == PicopassWorkerEventAborted) {
|
||||||
|
if(state == DictAttackStateUserDictInProgress ||
|
||||||
|
state == DictAttackStateStandardDictInProgress) {
|
||||||
|
picopass_worker_stop(picopass->worker);
|
||||||
|
picopass_scene_elite_dict_attack_prepare_view(picopass, state);
|
||||||
|
consumed = true;
|
||||||
|
} else {
|
||||||
|
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
} else if(event.event == PicopassWorkerEventCardDetected) {
|
||||||
|
dict_attack_set_card_detected(picopass->dict_attack);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == PicopassWorkerEventNoCardDetected) {
|
||||||
|
dict_attack_set_card_removed(picopass->dict_attack);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == PicopassWorkerEventNewDictKeyBatch) {
|
||||||
|
dict_attack_inc_current_dict_key(picopass->dict_attack, PICOPASS_DICT_KEY_BATCH_SIZE);
|
||||||
|
consumed = true;
|
||||||
|
} else if(event.event == PicopassCustomEventDictAttackSkip) {
|
||||||
|
if(state == DictAttackStateUserDictInProgress) {
|
||||||
|
picopass_worker_stop(picopass->worker);
|
||||||
|
consumed = true;
|
||||||
|
} else if(state == DictAttackStateFlipperDictInProgress) {
|
||||||
|
picopass_worker_stop(picopass->worker);
|
||||||
|
consumed = true;
|
||||||
|
} else if(state == DictAttackStateStandardDictInProgress) {
|
||||||
|
picopass_worker_stop(picopass->worker);
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(event.type == SceneManagerEventTypeBack) {
|
||||||
|
consumed = scene_manager_previous_scene(picopass->scene_manager);
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void picopass_scene_elite_dict_attack_on_exit(void* context) {
|
||||||
|
Picopass* picopass = context;
|
||||||
|
IclassEliteDictAttackData* dict_attack_data =
|
||||||
|
&picopass->dev->dev_data.iclass_elite_dict_attack_data;
|
||||||
|
// Stop worker
|
||||||
|
picopass_worker_stop(picopass->worker);
|
||||||
|
if(dict_attack_data->dict) {
|
||||||
|
iclass_elite_dict_free(dict_attack_data->dict);
|
||||||
|
dict_attack_data->dict = NULL;
|
||||||
|
}
|
||||||
|
dict_attack_reset(picopass->dict_attack);
|
||||||
|
picopass_blink_stop(picopass);
|
||||||
|
notification_message(picopass->notifications, &sequence_display_backlight_enforce_auto);
|
||||||
|
}
|
||||||
@@ -47,8 +47,21 @@ void picopass_scene_read_card_success_on_enter(void* context) {
|
|||||||
if(pacs->se_enabled) {
|
if(pacs->se_enabled) {
|
||||||
furi_string_cat_printf(credential_str, "SE enabled");
|
furi_string_cat_printf(credential_str, "SE enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
widget_add_button_element(
|
||||||
|
widget,
|
||||||
|
GuiButtonTypeCenter,
|
||||||
|
"Menu",
|
||||||
|
picopass_scene_read_card_success_widget_callback,
|
||||||
|
picopass);
|
||||||
} else if(empty) {
|
} else if(empty) {
|
||||||
furi_string_cat_printf(wiegand_str, "Empty");
|
furi_string_cat_printf(wiegand_str, "Empty");
|
||||||
|
widget_add_button_element(
|
||||||
|
widget,
|
||||||
|
GuiButtonTypeCenter,
|
||||||
|
"Menu",
|
||||||
|
picopass_scene_read_card_success_widget_callback,
|
||||||
|
picopass);
|
||||||
} else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
|
} else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) {
|
||||||
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff
|
// Neither of these are valid. Indicates the block was all 0x00 or all 0xff
|
||||||
furi_string_cat_printf(wiegand_str, "Invalid PACS");
|
furi_string_cat_printf(wiegand_str, "Invalid PACS");
|
||||||
@@ -56,6 +69,12 @@ void picopass_scene_read_card_success_on_enter(void* context) {
|
|||||||
if(pacs->se_enabled) {
|
if(pacs->se_enabled) {
|
||||||
furi_string_cat_printf(credential_str, "SE enabled");
|
furi_string_cat_printf(credential_str, "SE enabled");
|
||||||
}
|
}
|
||||||
|
widget_add_button_element(
|
||||||
|
widget,
|
||||||
|
GuiButtonTypeCenter,
|
||||||
|
"Menu",
|
||||||
|
picopass_scene_read_card_success_widget_callback,
|
||||||
|
picopass);
|
||||||
} else {
|
} else {
|
||||||
size_t bytesLength = 1 + pacs->record.bitLength / 8;
|
size_t bytesLength = 1 + pacs->record.bitLength / 8;
|
||||||
furi_string_set(credential_str, "");
|
furi_string_set(credential_str, "");
|
||||||
@@ -137,6 +156,9 @@ bool picopass_scene_read_card_success_on_event(void* context, SceneManagerEvent
|
|||||||
picopass_device_set_name(picopass->dev, "");
|
picopass_device_set_name(picopass->dev, "");
|
||||||
scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu);
|
scene_manager_next_scene(picopass->scene_manager, PicopassSceneCardMenu);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == GuiButtonTypeCenter) {
|
||||||
|
consumed = scene_manager_search_and_switch_to_another_scene(
|
||||||
|
picopass->scene_manager, PicopassSceneStart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return consumed;
|
return consumed;
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
#include "../picopass_i.h"
|
#include "../picopass_i.h"
|
||||||
enum SubmenuIndex {
|
enum SubmenuIndex {
|
||||||
SubmenuIndexRead,
|
SubmenuIndexRead,
|
||||||
SubmenuIndexRunScript,
|
SubmenuIndexEliteDictAttack,
|
||||||
SubmenuIndexSaved,
|
SubmenuIndexSaved,
|
||||||
SubmenuIndexAddManually,
|
|
||||||
SubmenuIndexDebug,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void picopass_scene_start_submenu_callback(void* context, uint32_t index) {
|
void picopass_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||||
@@ -17,6 +15,12 @@ void picopass_scene_start_on_enter(void* context) {
|
|||||||
Submenu* submenu = picopass->submenu;
|
Submenu* submenu = picopass->submenu;
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass);
|
submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass);
|
||||||
|
submenu_add_item(
|
||||||
|
submenu,
|
||||||
|
"Elite Dict. Attack",
|
||||||
|
SubmenuIndexEliteDictAttack,
|
||||||
|
picopass_scene_start_submenu_callback,
|
||||||
|
picopass);
|
||||||
submenu_add_item(
|
submenu_add_item(
|
||||||
submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass);
|
submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass);
|
||||||
|
|
||||||
@@ -43,6 +47,11 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) {
|
|||||||
picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved);
|
picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved);
|
||||||
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect);
|
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect);
|
||||||
consumed = true;
|
consumed = true;
|
||||||
|
} else if(event.event == SubmenuIndexEliteDictAttack) {
|
||||||
|
scene_manager_set_scene_state(
|
||||||
|
picopass->scene_manager, PicopassSceneStart, SubmenuIndexEliteDictAttack);
|
||||||
|
scene_manager_next_scene(picopass->scene_manager, PicopassSceneEliteDictAttack);
|
||||||
|
consumed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
281
applications/external/picopass/views/dict_attack.c
vendored
Normal file
281
applications/external/picopass/views/dict_attack.c
vendored
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
#include "dict_attack.h"
|
||||||
|
|
||||||
|
#include <gui/elements.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DictAttackStateRead,
|
||||||
|
DictAttackStateCardRemoved,
|
||||||
|
} DictAttackState;
|
||||||
|
|
||||||
|
struct DictAttack {
|
||||||
|
View* view;
|
||||||
|
DictAttackCallback callback;
|
||||||
|
void* context;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
DictAttackState state;
|
||||||
|
MfClassicType type;
|
||||||
|
FuriString* header;
|
||||||
|
uint8_t sectors_total;
|
||||||
|
uint8_t sectors_read;
|
||||||
|
uint8_t sector_current;
|
||||||
|
uint8_t keys_total;
|
||||||
|
uint8_t keys_found;
|
||||||
|
uint16_t dict_keys_total;
|
||||||
|
uint16_t dict_keys_current;
|
||||||
|
bool is_key_attack;
|
||||||
|
uint8_t key_attack_current_sector;
|
||||||
|
} DictAttackViewModel;
|
||||||
|
|
||||||
|
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
||||||
|
DictAttackViewModel* m = model;
|
||||||
|
if(m->state == DictAttackStateCardRemoved) {
|
||||||
|
canvas_set_font(canvas, FontPrimary);
|
||||||
|
canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Lost the tag!");
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
elements_multiline_text_aligned(
|
||||||
|
canvas, 64, 23, AlignCenter, AlignTop, "Make sure the tag is\npositioned correctly.");
|
||||||
|
} else if(m->state == DictAttackStateRead) {
|
||||||
|
char draw_str[32] = {};
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
canvas_draw_str_aligned(
|
||||||
|
canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
|
||||||
|
if(m->is_key_attack) {
|
||||||
|
snprintf(
|
||||||
|
draw_str,
|
||||||
|
sizeof(draw_str),
|
||||||
|
"Reuse key check for sector: %d",
|
||||||
|
m->key_attack_current_sector);
|
||||||
|
} else {
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->sector_current);
|
||||||
|
}
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
||||||
|
float dict_progress = m->dict_keys_total == 0 ?
|
||||||
|
0 :
|
||||||
|
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
|
||||||
|
float progress = m->sectors_total == 0 ? 0 :
|
||||||
|
((float)(m->sector_current) + dict_progress) /
|
||||||
|
(float)(m->sectors_total);
|
||||||
|
if(progress > 1.0) {
|
||||||
|
progress = 1.0;
|
||||||
|
}
|
||||||
|
if(m->dict_keys_current == 0) {
|
||||||
|
// Cause when people see 0 they think it's broken
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%d/%d", 1, m->dict_keys_total);
|
||||||
|
} else {
|
||||||
|
snprintf(
|
||||||
|
draw_str, sizeof(draw_str), "%d/%d", m->dict_keys_current, m->dict_keys_total);
|
||||||
|
}
|
||||||
|
elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);
|
||||||
|
canvas_set_font(canvas, FontSecondary);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "Keys found: %d/%d", m->keys_found, m->keys_total);
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 33, AlignLeft, AlignTop, draw_str);
|
||||||
|
snprintf(
|
||||||
|
draw_str, sizeof(draw_str), "Sectors Read: %d/%d", m->sectors_read, m->sectors_total);
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 43, AlignLeft, AlignTop, draw_str);
|
||||||
|
}
|
||||||
|
elements_button_center(canvas, "Skip");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool dict_attack_input_callback(InputEvent* event, void* context) {
|
||||||
|
DictAttack* dict_attack = context;
|
||||||
|
bool consumed = false;
|
||||||
|
if(event->type == InputTypeShort && event->key == InputKeyOk) {
|
||||||
|
if(dict_attack->callback) {
|
||||||
|
dict_attack->callback(dict_attack->context);
|
||||||
|
}
|
||||||
|
consumed = true;
|
||||||
|
}
|
||||||
|
return consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
DictAttack* dict_attack_alloc() {
|
||||||
|
DictAttack* dict_attack = malloc(sizeof(DictAttack));
|
||||||
|
dict_attack->view = view_alloc();
|
||||||
|
view_allocate_model(dict_attack->view, ViewModelTypeLocking, sizeof(DictAttackViewModel));
|
||||||
|
view_set_draw_callback(dict_attack->view, dict_attack_draw_callback);
|
||||||
|
view_set_input_callback(dict_attack->view, dict_attack_input_callback);
|
||||||
|
view_set_context(dict_attack->view, dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{ model->header = furi_string_alloc(); },
|
||||||
|
false);
|
||||||
|
return dict_attack;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_free(DictAttack* dict_attack) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{ furi_string_free(model->header); },
|
||||||
|
false);
|
||||||
|
view_free(dict_attack->view);
|
||||||
|
free(dict_attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_reset(DictAttack* dict_attack) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{
|
||||||
|
model->state = DictAttackStateRead;
|
||||||
|
model->type = MfClassicType1k;
|
||||||
|
model->sectors_total = 1;
|
||||||
|
model->sectors_read = 0;
|
||||||
|
model->sector_current = 0;
|
||||||
|
model->keys_total = 0;
|
||||||
|
model->keys_found = 0;
|
||||||
|
model->dict_keys_total = 0;
|
||||||
|
model->dict_keys_current = 0;
|
||||||
|
model->is_key_attack = false;
|
||||||
|
furi_string_reset(model->header);
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
View* dict_attack_get_view(DictAttack* dict_attack) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
return dict_attack->view;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
furi_assert(callback);
|
||||||
|
dict_attack->callback = callback;
|
||||||
|
dict_attack->context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_header(DictAttack* dict_attack, const char* header) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
furi_assert(header);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{ furi_string_set(model->header, header); },
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_card_detected(DictAttack* dict_attack) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{
|
||||||
|
model->state = DictAttackStateRead;
|
||||||
|
model->sectors_total = 1;
|
||||||
|
model->keys_total = model->sectors_total;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_card_removed(DictAttack* dict_attack) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{ model->state = DictAttackStateCardRemoved; },
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view, DictAttackViewModel * model, { model->sectors_read = sec_read; }, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view, DictAttackViewModel * model, { model->keys_found = keys_found; }, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{
|
||||||
|
model->sector_current = curr_sec;
|
||||||
|
model->dict_keys_current = 0;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_inc_current_sector(DictAttack* dict_attack) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{
|
||||||
|
if(model->sector_current < model->sectors_total) {
|
||||||
|
model->sector_current++;
|
||||||
|
model->dict_keys_current = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_inc_keys_found(DictAttack* dict_attack) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{
|
||||||
|
if(model->keys_found < model->keys_total) {
|
||||||
|
model->keys_found++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{ model->dict_keys_total = dict_keys_total; },
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{
|
||||||
|
if(model->dict_keys_current + keys_tried < model->dict_keys_total) {
|
||||||
|
model->dict_keys_current += keys_tried;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{
|
||||||
|
model->is_key_attack = is_key_attack;
|
||||||
|
model->key_attack_current_sector = sector;
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack) {
|
||||||
|
furi_assert(dict_attack);
|
||||||
|
with_view_model(
|
||||||
|
dict_attack->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{
|
||||||
|
if(model->key_attack_current_sector < model->sectors_total) {
|
||||||
|
model->key_attack_current_sector++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
44
applications/external/picopass/views/dict_attack.h
vendored
Normal file
44
applications/external/picopass/views/dict_attack.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <gui/view.h>
|
||||||
|
#include <gui/modules/widget.h>
|
||||||
|
|
||||||
|
#include <lib/nfc/protocols/mifare_classic.h>
|
||||||
|
|
||||||
|
typedef struct DictAttack DictAttack;
|
||||||
|
|
||||||
|
typedef void (*DictAttackCallback)(void* context);
|
||||||
|
|
||||||
|
DictAttack* dict_attack_alloc();
|
||||||
|
|
||||||
|
void dict_attack_free(DictAttack* dict_attack);
|
||||||
|
|
||||||
|
void dict_attack_reset(DictAttack* dict_attack);
|
||||||
|
|
||||||
|
View* dict_attack_get_view(DictAttack* dict_attack);
|
||||||
|
|
||||||
|
void dict_attack_set_callback(DictAttack* dict_attack, DictAttackCallback callback, void* context);
|
||||||
|
|
||||||
|
void dict_attack_set_header(DictAttack* dict_attack, const char* header);
|
||||||
|
|
||||||
|
void dict_attack_set_card_detected(DictAttack* dict_attack);
|
||||||
|
|
||||||
|
void dict_attack_set_card_removed(DictAttack* dict_attack);
|
||||||
|
|
||||||
|
void dict_attack_set_sector_read(DictAttack* dict_attack, uint8_t sec_read);
|
||||||
|
|
||||||
|
void dict_attack_set_keys_found(DictAttack* dict_attack, uint8_t keys_found);
|
||||||
|
|
||||||
|
void dict_attack_set_current_sector(DictAttack* dict_attack, uint8_t curr_sec);
|
||||||
|
|
||||||
|
void dict_attack_inc_current_sector(DictAttack* dict_attack);
|
||||||
|
|
||||||
|
void dict_attack_inc_keys_found(DictAttack* dict_attack);
|
||||||
|
|
||||||
|
void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total);
|
||||||
|
|
||||||
|
void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried);
|
||||||
|
|
||||||
|
void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector);
|
||||||
|
|
||||||
|
void dict_attack_inc_key_attack_current_sector(DictAttack* dict_attack);
|
||||||
@@ -37,9 +37,11 @@ char cli_getc(Cli* cli) {
|
|||||||
if(cli->session != NULL) {
|
if(cli->session != NULL) {
|
||||||
if(cli->session->rx((uint8_t*)&c, 1, FuriWaitForever) == 0) {
|
if(cli->session->rx((uint8_t*)&c, 1, FuriWaitForever) == 0) {
|
||||||
cli_reset(cli);
|
cli_reset(cli);
|
||||||
|
furi_delay_tick(10);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cli_reset(cli);
|
cli_reset(cli);
|
||||||
|
furi_delay_tick(10);
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ void view_allocate_model(View* view, ViewModelType type, size_t size) {
|
|||||||
view->model = malloc(size);
|
view->model = malloc(size);
|
||||||
} else if(view->model_type == ViewModelTypeLocking) {
|
} else if(view->model_type == ViewModelTypeLocking) {
|
||||||
ViewModelLocking* model = malloc(sizeof(ViewModelLocking));
|
ViewModelLocking* model = malloc(sizeof(ViewModelLocking));
|
||||||
model->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
model->mutex = furi_mutex_alloc(FuriMutexTypeRecursive);
|
||||||
furi_check(model->mutex);
|
furi_check(model->mutex);
|
||||||
model->data = malloc(size);
|
model->data = malloc(size);
|
||||||
view->model = model;
|
view->model = model;
|
||||||
|
|||||||
@@ -73,12 +73,11 @@ typedef enum {
|
|||||||
#define USB_SRV_ALL_EVENTS (UsbEventReset | UsbEventRequest | UsbEventMessage)
|
#define USB_SRV_ALL_EVENTS (UsbEventReset | UsbEventRequest | UsbEventMessage)
|
||||||
|
|
||||||
PLACE_IN_SECTION("MB_MEM2") static UsbSrv usb = {0};
|
PLACE_IN_SECTION("MB_MEM2") static UsbSrv usb = {0};
|
||||||
|
PLACE_IN_SECTION("MB_MEM2") static uint32_t ubuf[0x20];
|
||||||
|
PLACE_IN_SECTION("MB_MEM2") usbd_device udev;
|
||||||
|
|
||||||
static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
|
static const struct usb_string_descriptor dev_lang_desc = USB_ARRAY_DESC(USB_LANGID_ENG_US);
|
||||||
|
|
||||||
static uint32_t ubuf[0x20];
|
|
||||||
usbd_device udev;
|
|
||||||
|
|
||||||
static int32_t furi_hal_usb_thread(void* context);
|
static int32_t furi_hal_usb_thread(void* context);
|
||||||
static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length);
|
static usbd_respond usb_descriptor_get(usbd_ctlreq* req, void** address, uint16_t* length);
|
||||||
static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep);
|
static void reset_evt(usbd_device* dev, uint8_t event, uint8_t ep);
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ extern uint32_t SystemCoreClock;
|
|||||||
#define configTIMER_SERVICE_TASK_NAME "TimersSrv"
|
#define configTIMER_SERVICE_TASK_NAME "TimersSrv"
|
||||||
|
|
||||||
#define configIDLE_TASK_NAME "(-_-)"
|
#define configIDLE_TASK_NAME "(-_-)"
|
||||||
|
#define configIDLE_TASK_STACK_DEPTH 128
|
||||||
|
|
||||||
/* Set the following definitions to 1 to include the API function, or zero
|
/* Set the following definitions to 1 to include the API function, or zero
|
||||||
to exclude the API function. */
|
to exclude the API function. */
|
||||||
@@ -138,3 +139,7 @@ standard names. */
|
|||||||
#define traceTASK_SWITCHED_IN() \
|
#define traceTASK_SWITCHED_IN() \
|
||||||
extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \
|
extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \
|
||||||
furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack)
|
furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack)
|
||||||
|
|
||||||
|
#define portCLEAN_UP_TCB(pxTCB) \
|
||||||
|
extern void furi_thread_cleanup_tcb_event(TaskHandle_t task); \
|
||||||
|
furi_thread_cleanup_tcb_event(pxTCB)
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ struct FuriThreadStdout {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct FuriThread {
|
struct FuriThread {
|
||||||
bool is_service;
|
|
||||||
FuriThreadState state;
|
FuriThreadState state;
|
||||||
int32_t ret;
|
int32_t ret;
|
||||||
|
|
||||||
@@ -37,14 +36,19 @@ struct FuriThread {
|
|||||||
char* name;
|
char* name;
|
||||||
char* appid;
|
char* appid;
|
||||||
|
|
||||||
configSTACK_DEPTH_TYPE stack_size;
|
|
||||||
FuriThreadPriority priority;
|
FuriThreadPriority priority;
|
||||||
|
|
||||||
TaskHandle_t task_handle;
|
TaskHandle_t task_handle;
|
||||||
bool heap_trace_enabled;
|
|
||||||
size_t heap_size;
|
size_t heap_size;
|
||||||
|
|
||||||
FuriThreadStdout output;
|
FuriThreadStdout output;
|
||||||
|
|
||||||
|
// Keep all non-alignable byte types in one place,
|
||||||
|
// this ensures that the size of this structure is minimal
|
||||||
|
bool is_service;
|
||||||
|
bool heap_trace_enabled;
|
||||||
|
|
||||||
|
configSTACK_DEPTH_TYPE stack_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size);
|
static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size);
|
||||||
@@ -107,14 +111,8 @@ static void furi_thread_body(void* context) {
|
|||||||
// flush stdout
|
// flush stdout
|
||||||
__furi_thread_stdout_flush(thread);
|
__furi_thread_stdout_flush(thread);
|
||||||
|
|
||||||
// from here we can't use thread pointer
|
|
||||||
furi_thread_set_state(thread, FuriThreadStateStopped);
|
furi_thread_set_state(thread, FuriThreadStateStopped);
|
||||||
|
|
||||||
// clear thread local storage
|
|
||||||
furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) != NULL);
|
|
||||||
vTaskSetThreadLocalStoragePointer(NULL, 0, NULL);
|
|
||||||
|
|
||||||
thread->task_handle = NULL;
|
|
||||||
vTaskDelete(NULL);
|
vTaskDelete(NULL);
|
||||||
furi_thread_catch();
|
furi_thread_catch();
|
||||||
}
|
}
|
||||||
@@ -249,11 +247,11 @@ void furi_thread_start(FuriThread* thread) {
|
|||||||
furi_assert(thread);
|
furi_assert(thread);
|
||||||
furi_assert(thread->callback);
|
furi_assert(thread->callback);
|
||||||
furi_assert(thread->state == FuriThreadStateStopped);
|
furi_assert(thread->state == FuriThreadStateStopped);
|
||||||
furi_assert(thread->stack_size > 0 && thread->stack_size < 0xFFFF * 4);
|
furi_assert(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t)));
|
||||||
|
|
||||||
furi_thread_set_state(thread, FuriThreadStateStarting);
|
furi_thread_set_state(thread, FuriThreadStateStarting);
|
||||||
|
|
||||||
uint32_t stack = thread->stack_size / 4;
|
uint32_t stack = thread->stack_size / sizeof(StackType_t);
|
||||||
UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal;
|
UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal;
|
||||||
if(thread->is_service) {
|
if(thread->is_service) {
|
||||||
thread->task_handle = xTaskCreateStatic(
|
thread->task_handle = xTaskCreateStatic(
|
||||||
@@ -273,12 +271,25 @@ void furi_thread_start(FuriThread* thread) {
|
|||||||
furi_check(thread->task_handle);
|
furi_check(thread->task_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void furi_thread_cleanup_tcb_event(TaskHandle_t task) {
|
||||||
|
FuriThread* thread = pvTaskGetThreadLocalStoragePointer(task, 0);
|
||||||
|
if(thread) {
|
||||||
|
// clear thread local storage
|
||||||
|
vTaskSetThreadLocalStoragePointer(task, 0, NULL);
|
||||||
|
|
||||||
|
thread->task_handle = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool furi_thread_join(FuriThread* thread) {
|
bool furi_thread_join(FuriThread* thread) {
|
||||||
furi_assert(thread);
|
furi_assert(thread);
|
||||||
|
|
||||||
furi_check(furi_thread_get_current() != thread);
|
furi_check(furi_thread_get_current() != thread);
|
||||||
|
|
||||||
// Wait for thread to stop
|
// !!! IMPORTANT NOTICE !!!
|
||||||
|
//
|
||||||
|
// If your thread exited, but your app stuck here: some other thread uses
|
||||||
|
// all cpu time, which delays kernel from releasing task handle
|
||||||
while(thread->task_handle) {
|
while(thread->task_handle) {
|
||||||
furi_delay_ms(10);
|
furi_delay_ms(10);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ FuriThread* furi_thread_alloc_ex(
|
|||||||
void* context);
|
void* context);
|
||||||
|
|
||||||
/** Release FuriThread
|
/** Release FuriThread
|
||||||
|
*
|
||||||
|
* @warning see furi_thread_join
|
||||||
*
|
*
|
||||||
* @param thread FuriThread instance
|
* @param thread FuriThread instance
|
||||||
*/
|
*/
|
||||||
@@ -173,6 +175,9 @@ FuriThreadState furi_thread_get_state(FuriThread* thread);
|
|||||||
void furi_thread_start(FuriThread* thread);
|
void furi_thread_start(FuriThread* thread);
|
||||||
|
|
||||||
/** Join FuriThread
|
/** Join FuriThread
|
||||||
|
*
|
||||||
|
* @warning Use this method only when CPU is not busy(Idle task receives
|
||||||
|
* control), otherwise it will wait forever.
|
||||||
*
|
*
|
||||||
* @param thread FuriThread instance
|
* @param thread FuriThread instance
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ void vApplicationGetIdleTaskMemory(
|
|||||||
StackType_t** stack_ptr,
|
StackType_t** stack_ptr,
|
||||||
uint32_t* stack_size) {
|
uint32_t* stack_size) {
|
||||||
*tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t));
|
*tcb_ptr = memmgr_alloc_from_pool(sizeof(StaticTask_t));
|
||||||
*stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configMINIMAL_STACK_SIZE);
|
*stack_ptr = memmgr_alloc_from_pool(sizeof(StackType_t) * configIDLE_TASK_STACK_DEPTH);
|
||||||
*stack_size = configMINIMAL_STACK_SIZE;
|
*stack_size = configIDLE_TASK_STACK_DEPTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vApplicationGetTimerTaskMemory(
|
void vApplicationGetTimerTaskMemory(
|
||||||
|
|||||||
Reference in New Issue
Block a user