mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-10-23 17:23:23 +00:00
Merge pull request #3113 from kormax/mfdes-bruteaid-improvements
Improve `hf mfdes bruteaid`
This commit is contained in:
3 files changed
+550
-48
No files matched your search
@@ -3,6 +3,8 @@ All notable changes to this project will be documented in this file.
|
||||
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
|
||||
|
||||
## [unreleased][unreleased]
|
||||
- Added `dict`, `ascii`, `mad` presets for `hf mfdes bruteaid` (@kormax)
|
||||
- Added tag loss detection & recovery into `hf mfdes bruteaid` (@kormax)
|
||||
- Added --credit option for `hf iclass legrec` command to perform a credit key recovery. This is experimental and unfinished as it only partially works.(@antiklesys)
|
||||
- Added hardening for all host binaries. Exact level of hardening depends on the OS (@doegox)
|
||||
- Added `hf aliro read` command (@kormax)
|
||||
|
||||
+481
-16
@@ -18,8 +18,10 @@
|
||||
// Code heavily modified by B.Kerler :)
|
||||
|
||||
#include "cmdhfmfdes.h"
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "jansson.h"
|
||||
#include "commonutil.h" // ARRAYLEN
|
||||
#include "cmdparser.h" // command_t
|
||||
#include "comms.h"
|
||||
@@ -49,6 +51,10 @@
|
||||
|
||||
#define MAX_KEY_LEN 24
|
||||
#define MAX_KEYS_LIST_LEN 1024
|
||||
#define MFDES_BRUTEAID_RESELECT_ATTEMPTS 5
|
||||
#define MFDES_BRUTEAID_RESELECT_WAIT_MS 200
|
||||
#define MFDES_BRUTEAID_MAD_START 0xF0000FU
|
||||
#define MFDES_BRUTEAID_MAD_STEP 0x10U
|
||||
|
||||
#define status(x) ( ((uint16_t)(0x91 << 8)) + (uint16_t)x )
|
||||
/*
|
||||
@@ -173,6 +179,25 @@ static const mfdesCommonAID_t commonAids[] = {
|
||||
{ 0xF47300, "\xf4\x73\x00", "Inner Range card application" },
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MFDES_BRUTEAID_PRESET_FULL = 0,
|
||||
MFDES_BRUTEAID_PRESET_ASCII = 1,
|
||||
MFDES_BRUTEAID_PRESET_NUMBERS = 2,
|
||||
MFDES_BRUTEAID_PRESET_LETTERS = 3,
|
||||
MFDES_BRUTEAID_PRESET_DICTIONARY = 4,
|
||||
MFDES_BRUTEAID_PRESET_MAD = 5,
|
||||
} mfdes_bruteaid_preset_t;
|
||||
|
||||
static const CLIParserOption mfdesBruteAIDPresetOpts[] = {
|
||||
{MFDES_BRUTEAID_PRESET_FULL, "full"},
|
||||
{MFDES_BRUTEAID_PRESET_ASCII, "ascii"},
|
||||
{MFDES_BRUTEAID_PRESET_NUMBERS, "numbers"},
|
||||
{MFDES_BRUTEAID_PRESET_LETTERS, "letters"},
|
||||
{MFDES_BRUTEAID_PRESET_DICTIONARY, "dictionary"},
|
||||
{MFDES_BRUTEAID_PRESET_MAD, "mad"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
static int CLIGetUint32Hex(CLIParserContext *ctx, uint8_t paramnum, uint32_t defaultValue, uint32_t *value, bool *valuePresent, uint8_t nlen, const char *lengthErrorStr) {
|
||||
@@ -2174,20 +2199,384 @@ static int CmdHF14ADesSelectApp(const char *Cmd) {
|
||||
return res;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t current;
|
||||
uint32_t step;
|
||||
uint64_t total_count;
|
||||
uint64_t generated_count;
|
||||
} mfdes_bruteaid_generator_full_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t alphabet[100];
|
||||
size_t alphabet_len;
|
||||
size_t idx0;
|
||||
size_t idx1;
|
||||
size_t idx2;
|
||||
uint32_t id_start;
|
||||
uint32_t id_end;
|
||||
uint32_t step;
|
||||
uint64_t ordinal;
|
||||
uint64_t total_count;
|
||||
uint64_t generated_count;
|
||||
bool exhausted;
|
||||
} mfdes_bruteaid_generator_ascii_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t *aids;
|
||||
size_t aids_count;
|
||||
size_t idx;
|
||||
uint32_t step;
|
||||
uint64_t ordinal;
|
||||
uint64_t total_count;
|
||||
uint64_t generated_count;
|
||||
} mfdes_bruteaid_generator_dictionary_t;
|
||||
|
||||
typedef struct {
|
||||
mfdes_bruteaid_preset_t preset;
|
||||
uint64_t total_count;
|
||||
union {
|
||||
mfdes_bruteaid_generator_full_t full;
|
||||
mfdes_bruteaid_generator_ascii_t ascii;
|
||||
mfdes_bruteaid_generator_dictionary_t dictionary;
|
||||
} g;
|
||||
} mfdes_bruteaid_generator_t;
|
||||
|
||||
static void mfdesBruteAIDGeneratorFullInit(mfdes_bruteaid_generator_full_t *gen, uint32_t id_start, uint32_t id_end, uint32_t step) {
|
||||
memset(gen, 0, sizeof(*gen));
|
||||
gen->current = id_start;
|
||||
gen->step = step;
|
||||
gen->total_count = ((uint64_t)(id_end - id_start) / step) + 1;
|
||||
}
|
||||
|
||||
static bool mfdesBruteAIDGeneratorFullNext(mfdes_bruteaid_generator_full_t *gen, uint32_t *id, float *progress) {
|
||||
if (gen->generated_count >= gen->total_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*id = gen->current;
|
||||
*progress = (gen->total_count > 1) ? (100.0f * (float)gen->generated_count / (float)(gen->total_count - 1)) : 100.0f;
|
||||
|
||||
gen->generated_count++;
|
||||
if (gen->generated_count < gen->total_count) {
|
||||
gen->current += gen->step;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mfdesBruteAIDGeneratorAsciiInit(mfdes_bruteaid_generator_ascii_t *gen, uint32_t id_start, uint32_t id_end, uint32_t step) {
|
||||
memset(gen, 0, sizeof(*gen));
|
||||
gen->id_start = id_start;
|
||||
gen->id_end = id_end;
|
||||
gen->step = step;
|
||||
|
||||
for (uint8_t b = 0x09; b <= 0x0D; b++) {
|
||||
gen->alphabet[gen->alphabet_len++] = b;
|
||||
}
|
||||
for (uint16_t b = 0x20; b <= 0x7E; b++) {
|
||||
gen->alphabet[gen->alphabet_len++] = (uint8_t)b;
|
||||
}
|
||||
|
||||
uint64_t all_candidates = 0;
|
||||
for (size_t b2 = 0; b2 < gen->alphabet_len; b2++) {
|
||||
for (size_t b1 = 0; b1 < gen->alphabet_len; b1++) {
|
||||
for (size_t b0 = 0; b0 < gen->alphabet_len; b0++) {
|
||||
uint32_t id = ((uint32_t)gen->alphabet[b0]) | ((uint32_t)gen->alphabet[b1] << 8) | ((uint32_t)gen->alphabet[b2] << 16);
|
||||
if (id >= id_start && id <= id_end) {
|
||||
all_candidates++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen->total_count = (all_candidates + step - 1) / step;
|
||||
gen->exhausted = (gen->total_count == 0);
|
||||
}
|
||||
|
||||
static void mfdesBruteAIDGeneratorNumbersInit(mfdes_bruteaid_generator_ascii_t *gen, uint32_t id_start, uint32_t id_end, uint32_t step) {
|
||||
memset(gen, 0, sizeof(*gen));
|
||||
gen->id_start = id_start;
|
||||
gen->id_end = id_end;
|
||||
gen->step = step;
|
||||
|
||||
for (uint8_t b = 0x30; b <= 0x39; b++) {
|
||||
gen->alphabet[gen->alphabet_len++] = b;
|
||||
}
|
||||
|
||||
uint64_t all_candidates = 0;
|
||||
for (size_t b2 = 0; b2 < gen->alphabet_len; b2++) {
|
||||
for (size_t b1 = 0; b1 < gen->alphabet_len; b1++) {
|
||||
for (size_t b0 = 0; b0 < gen->alphabet_len; b0++) {
|
||||
uint32_t id = ((uint32_t)gen->alphabet[b0]) | ((uint32_t)gen->alphabet[b1] << 8) | ((uint32_t)gen->alphabet[b2] << 16);
|
||||
if (id >= id_start && id <= id_end) {
|
||||
all_candidates++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen->total_count = (all_candidates + step - 1) / step;
|
||||
gen->exhausted = (gen->total_count == 0);
|
||||
}
|
||||
|
||||
static void mfdesBruteAIDGeneratorLettersInit(mfdes_bruteaid_generator_ascii_t *gen, uint32_t id_start, uint32_t id_end, uint32_t step) {
|
||||
memset(gen, 0, sizeof(*gen));
|
||||
gen->id_start = id_start;
|
||||
gen->id_end = id_end;
|
||||
gen->step = step;
|
||||
|
||||
for (uint8_t b = 'A'; b <= 'Z'; b++) {
|
||||
gen->alphabet[gen->alphabet_len++] = b;
|
||||
}
|
||||
for (uint8_t b = 'a'; b <= 'z'; b++) {
|
||||
gen->alphabet[gen->alphabet_len++] = b;
|
||||
}
|
||||
|
||||
uint64_t all_candidates = 0;
|
||||
for (size_t b2 = 0; b2 < gen->alphabet_len; b2++) {
|
||||
for (size_t b1 = 0; b1 < gen->alphabet_len; b1++) {
|
||||
for (size_t b0 = 0; b0 < gen->alphabet_len; b0++) {
|
||||
uint32_t id = ((uint32_t)gen->alphabet[b0]) | ((uint32_t)gen->alphabet[b1] << 8) | ((uint32_t)gen->alphabet[b2] << 16);
|
||||
if (id >= id_start && id <= id_end) {
|
||||
all_candidates++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gen->total_count = (all_candidates + step - 1) / step;
|
||||
gen->exhausted = (gen->total_count == 0);
|
||||
}
|
||||
|
||||
static int mfdesBruteAIDGeneratorDictionaryInit(mfdes_bruteaid_generator_dictionary_t *gen, uint32_t id_start, uint32_t id_end, uint32_t step) {
|
||||
memset(gen, 0, sizeof(*gen));
|
||||
gen->step = step;
|
||||
|
||||
char *path = NULL;
|
||||
int res = searchFile(&path, RESOURCES_SUBDIR, "aid_desfire", ".json", true);
|
||||
if (res != PM3_SUCCESS) {
|
||||
PrintAndLogEx(ERR, "Cannot locate aid_desfire dictionary file");
|
||||
return PM3_EFILE;
|
||||
}
|
||||
|
||||
json_error_t error;
|
||||
json_t *root = json_load_file(path, 0, &error);
|
||||
free(path);
|
||||
if (root == NULL) {
|
||||
PrintAndLogEx(ERR, "Failed to load aid_desfire dictionary: line %d: %s", error.line, error.text);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
if (json_is_array(root) == false) {
|
||||
PrintAndLogEx(ERR, "Invalid aid_desfire dictionary format (root must be array)");
|
||||
json_decref(root);
|
||||
return PM3_ESOFT;
|
||||
}
|
||||
|
||||
size_t max_count = json_array_size(root);
|
||||
if (max_count == 0) {
|
||||
json_decref(root);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (max_count > (SIZE_MAX / (2 * sizeof(uint32_t)))) {
|
||||
json_decref(root);
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
|
||||
size_t alloc_count = max_count * 2;
|
||||
gen->aids = calloc(alloc_count, sizeof(uint32_t));
|
||||
if (gen->aids == NULL) {
|
||||
json_decref(root);
|
||||
return PM3_EMALLOC;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < max_count; i++) {
|
||||
json_t *entry = json_array_get(root, i);
|
||||
if (json_is_object(entry) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
json_t *aid_j = json_object_get(entry, "AID");
|
||||
if (json_is_string(aid_j) == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *aid_str = json_string_value(aid_j);
|
||||
if (aid_str == NULL || strlen(aid_str) != 6) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool is_hex = true;
|
||||
for (int c = 0; c < 6; c++) {
|
||||
if (isxdigit((uint8_t)aid_str[c]) == 0) {
|
||||
is_hex = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_hex == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t aid = 0;
|
||||
if (sscanf(aid_str, "%x", &aid) != 1) {
|
||||
continue;
|
||||
}
|
||||
aid &= 0xFFFFFF;
|
||||
|
||||
// People may fill in AID values in different byte orders
|
||||
// so we will try both big endian and little endian variants of the AID
|
||||
uint32_t aid_variants[] = {
|
||||
aid,
|
||||
((aid & 0x0000FFU) << 16) | (aid & 0x00FF00U) | ((aid & 0xFF0000U) >> 16)
|
||||
};
|
||||
|
||||
for (size_t v = 0; v < ARRAYLEN(aid_variants); v++) {
|
||||
uint32_t candidate = aid_variants[v];
|
||||
if (candidate < id_start || candidate > id_end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool exists = false;
|
||||
for (size_t j = 0; j < gen->aids_count; j++) {
|
||||
if (gen->aids[j] == candidate) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exists == false) {
|
||||
gen->aids[gen->aids_count++] = candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
json_decref(root);
|
||||
|
||||
gen->total_count = (gen->aids_count + step - 1) / step;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static bool mfdesBruteAIDGeneratorAsciiNext(mfdes_bruteaid_generator_ascii_t *gen, uint32_t *id, float *progress) {
|
||||
while (gen->exhausted == false) {
|
||||
uint32_t candidate = ((uint32_t)gen->alphabet[gen->idx0]) |
|
||||
((uint32_t)gen->alphabet[gen->idx1] << 8) |
|
||||
((uint32_t)gen->alphabet[gen->idx2] << 16);
|
||||
|
||||
gen->idx0++;
|
||||
if (gen->idx0 >= gen->alphabet_len) {
|
||||
gen->idx0 = 0;
|
||||
gen->idx1++;
|
||||
if (gen->idx1 >= gen->alphabet_len) {
|
||||
gen->idx1 = 0;
|
||||
gen->idx2++;
|
||||
if (gen->idx2 >= gen->alphabet_len) {
|
||||
gen->exhausted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate < gen->id_start || candidate > gen->id_end) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((gen->ordinal++ % gen->step) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*id = candidate;
|
||||
*progress = (gen->total_count > 1) ? (100.0f * (float)gen->generated_count / (float)(gen->total_count - 1)) : 100.0f;
|
||||
|
||||
gen->generated_count++;
|
||||
if (gen->generated_count >= gen->total_count) {
|
||||
gen->exhausted = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool mfdesBruteAIDGeneratorDictionaryNext(mfdes_bruteaid_generator_dictionary_t *gen, uint32_t *id, float *progress) {
|
||||
while (gen->idx < gen->aids_count) {
|
||||
uint32_t candidate = gen->aids[gen->idx++];
|
||||
if ((gen->ordinal++ % gen->step) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*id = candidate;
|
||||
*progress = (gen->total_count > 1) ? (100.0f * (float)gen->generated_count / (float)(gen->total_count - 1)) : 100.0f;
|
||||
|
||||
gen->generated_count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void mfdesBruteAIDGeneratorFree(mfdes_bruteaid_generator_t *gen) {
|
||||
if (gen->preset == MFDES_BRUTEAID_PRESET_DICTIONARY) {
|
||||
free(gen->g.dictionary.aids);
|
||||
gen->g.dictionary.aids = NULL;
|
||||
gen->g.dictionary.aids_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int mfdesBruteAIDGeneratorInit(mfdes_bruteaid_generator_t *gen, mfdes_bruteaid_preset_t preset, uint32_t id_start, uint32_t id_end, uint32_t step) {
|
||||
memset(gen, 0, sizeof(*gen));
|
||||
gen->preset = preset;
|
||||
|
||||
if (preset == MFDES_BRUTEAID_PRESET_ASCII) {
|
||||
mfdesBruteAIDGeneratorAsciiInit(&gen->g.ascii, id_start, id_end, step);
|
||||
gen->total_count = gen->g.ascii.total_count;
|
||||
} else if (preset == MFDES_BRUTEAID_PRESET_NUMBERS) {
|
||||
mfdesBruteAIDGeneratorNumbersInit(&gen->g.ascii, id_start, id_end, step);
|
||||
gen->total_count = gen->g.ascii.total_count;
|
||||
} else if (preset == MFDES_BRUTEAID_PRESET_LETTERS) {
|
||||
mfdesBruteAIDGeneratorLettersInit(&gen->g.ascii, id_start, id_end, step);
|
||||
gen->total_count = gen->g.ascii.total_count;
|
||||
} else if (preset == MFDES_BRUTEAID_PRESET_DICTIONARY) {
|
||||
int res = mfdesBruteAIDGeneratorDictionaryInit(&gen->g.dictionary, id_start, id_end, step);
|
||||
if (res != PM3_SUCCESS) {
|
||||
return res;
|
||||
}
|
||||
gen->total_count = gen->g.dictionary.total_count;
|
||||
} else if (preset == MFDES_BRUTEAID_PRESET_MAD) {
|
||||
mfdesBruteAIDGeneratorFullInit(&gen->g.full, id_start, id_end, MFDES_BRUTEAID_MAD_STEP);
|
||||
gen->total_count = gen->g.full.total_count;
|
||||
} else {
|
||||
mfdesBruteAIDGeneratorFullInit(&gen->g.full, id_start, id_end, step);
|
||||
gen->total_count = gen->g.full.total_count;
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static bool mfdesBruteAIDGeneratorNext(mfdes_bruteaid_generator_t *gen, uint32_t *id, float *progress) {
|
||||
if (gen->preset == MFDES_BRUTEAID_PRESET_ASCII ||
|
||||
gen->preset == MFDES_BRUTEAID_PRESET_NUMBERS ||
|
||||
gen->preset == MFDES_BRUTEAID_PRESET_LETTERS) {
|
||||
return mfdesBruteAIDGeneratorAsciiNext(&gen->g.ascii, id, progress);
|
||||
} else if (gen->preset == MFDES_BRUTEAID_PRESET_DICTIONARY) {
|
||||
return mfdesBruteAIDGeneratorDictionaryNext(&gen->g.dictionary, id, progress);
|
||||
}
|
||||
return mfdesBruteAIDGeneratorFullNext(&gen->g.full, id, progress);
|
||||
}
|
||||
|
||||
static int CmdHF14ADesBruteApps(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mfdes bruteaid",
|
||||
"Recover AIDs by bruteforce.\n"
|
||||
"WARNING: This command takes a loooong time",
|
||||
"hf mfdes bruteaid -> Search all apps\n"
|
||||
"hf mfdes bruteaid --start F0000F -i 16 -> Search MAD range manually");
|
||||
"hf mfdes bruteaid --preset mad -> Search MAD range preset (default start F0000F, step 16; can override start)\n"
|
||||
"hf mfdes bruteaid --preset ascii -> Search with ASCII printable + whitespace bytes only\n"
|
||||
"hf mfdes bruteaid --preset numbers -> Search with numeric bytes ('0'..'9') only\n"
|
||||
"hf mfdes bruteaid --preset letters -> Search with letter bytes ('A'..'Z','a'..'z') only\n"
|
||||
"hf mfdes bruteaid --preset dictionary -> Search AIDs from `aid_desfire` dictionary (direct + inverted byte order)");
|
||||
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0(NULL, "start", "<hex>", "Starting App ID as hex bytes (3 bytes, big endian)"),
|
||||
arg_str0(NULL, "end", "<hex>", "Last App ID as hex bytes (3 bytes, big endian)"),
|
||||
arg_int0("i", "step", "<dec>", "Increment step when bruteforcing"),
|
||||
arg_lit0("m", "mad", "Only bruteforce the MAD range"),
|
||||
arg_str0(NULL, "preset", "<full|ascii|numbers|letters|dictionary|mad>", "Bruteforce candidate preset (`full` default, `ascii` printable + whitespace, `numbers` = '0'..'9', `letters` = 'A'..'Z'+'a'..'z', `dictionary` = aid_desfire list with direct + inverted byte order, `mad` = step 16 with default start F0000F unless --start is provided)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
@@ -2206,11 +2595,22 @@ static int CmdHF14ADesBruteApps(const char *Cmd) {
|
||||
int endLen = 0;
|
||||
CLIGetHexWithReturn(ctx, 1, startAid, &startLen);
|
||||
CLIGetHexWithReturn(ctx, 2, endAid, &endLen);
|
||||
uint32_t idIncrement = arg_get_int_def(ctx, 3, 1);
|
||||
bool mad = arg_get_lit(ctx, 4);
|
||||
int idIncrementArg = arg_get_int_def(ctx, 3, 1);
|
||||
|
||||
int preset = MFDES_BRUTEAID_PRESET_FULL;
|
||||
if (CLIGetOptionList(arg_get_str(ctx, 4), mfdesBruteAIDPresetOpts, &preset)) {
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
if (idIncrementArg <= 0) {
|
||||
PrintAndLogEx(ERR, "Increment step should be greater than zero");
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
uint32_t idIncrement = (uint32_t)idIncrementArg;
|
||||
|
||||
// tru select PICC
|
||||
res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0);
|
||||
if (res != PM3_SUCCESS) {
|
||||
@@ -2219,38 +2619,102 @@ static int CmdHF14ADesBruteApps(const char *Cmd) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// TODO: We need to check the tag version, EV1 should stop after 26 apps are found
|
||||
if (mad) {
|
||||
idIncrement = 0x10;
|
||||
startAid[0] = 0xF0;
|
||||
startAid[1] = 0x00;
|
||||
startAid[2] = 0x0F;
|
||||
}
|
||||
|
||||
reverse_array(startAid, 3);
|
||||
reverse_array(endAid, 3);
|
||||
|
||||
uint32_t idStart = DesfireAIDByteToUint(startAid);
|
||||
uint32_t idEnd = DesfireAIDByteToUint(endAid);
|
||||
|
||||
// TODO: We need to check the tag version, EV1 should stop after 26 apps are found
|
||||
if (preset == MFDES_BRUTEAID_PRESET_MAD) {
|
||||
if (startLen == 0) {
|
||||
idStart = MFDES_BRUTEAID_MAD_START;
|
||||
}
|
||||
idIncrement = MFDES_BRUTEAID_MAD_STEP;
|
||||
}
|
||||
|
||||
if (idStart > idEnd) {
|
||||
PrintAndLogEx(ERR, "Start should be lower than end. start: %06x end: %06x", idStart, idEnd);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
PrintAndLogEx(INFO, "Bruteforce from " _YELLOW_("%06x") " to " _YELLOW_("%06x"), idStart, idEnd);
|
||||
PrintAndLogEx(INFO, "Enumerating through all AIDs manually, this will take a while!");
|
||||
const char *preset_name = CLIGetOptionListStr(mfdesBruteAIDPresetOpts, preset);
|
||||
if (preset_name == NULL) {
|
||||
preset_name = "unknown";
|
||||
}
|
||||
|
||||
for (uint32_t id = idStart; id <= idEnd && id >= idStart; id += idIncrement) {
|
||||
mfdes_bruteaid_generator_t generator = {0};
|
||||
res = mfdesBruteAIDGeneratorInit(&generator, (mfdes_bruteaid_preset_t)preset, idStart, idEnd, idIncrement);
|
||||
if (res != PM3_SUCCESS) {
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
|
||||
if (generator.total_count == 0) {
|
||||
PrintAndLogEx(WARNING, "No AID candidates in selected range/preset");
|
||||
mfdesBruteAIDGeneratorFree(&generator);
|
||||
DropField();
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
if (preset == MFDES_BRUTEAID_PRESET_ASCII ||
|
||||
preset == MFDES_BRUTEAID_PRESET_NUMBERS ||
|
||||
preset == MFDES_BRUTEAID_PRESET_LETTERS ||
|
||||
preset == MFDES_BRUTEAID_PRESET_DICTIONARY) {
|
||||
PrintAndLogEx(INFO, "Bruteforcing with preset " _YELLOW_("%s") " candidates " _YELLOW_("%llu"),
|
||||
preset_name, (unsigned long long)generator.total_count);
|
||||
} else {
|
||||
PrintAndLogEx(INFO, "Bruteforcing with preset " _YELLOW_("%s") " range " _YELLOW_("%06x") "-" _YELLOW_("%06x") " step " _YELLOW_("%u") " candidates " _YELLOW_("%llu"),
|
||||
preset_name, idStart, idEnd, idIncrement, (unsigned long long)generator.total_count);
|
||||
}
|
||||
if (preset == MFDES_BRUTEAID_PRESET_FULL) {
|
||||
PrintAndLogEx(INFO, "Enumerating through all AIDs manually, this will take a while!");
|
||||
}
|
||||
if (preset == MFDES_BRUTEAID_PRESET_DICTIONARY) {
|
||||
PrintAndLogEx(INFO, "Dictionary source: `aid_desfire` (direct + inverted byte order)");
|
||||
}
|
||||
|
||||
uint32_t id = 0;
|
||||
float progress = 0;
|
||||
while (mfdesBruteAIDGeneratorNext(&generator, &id, &progress)) {
|
||||
if (kbd_enter_pressed()) {
|
||||
break;
|
||||
}
|
||||
|
||||
float progress = 100.0 * (float)(id - idStart) / ((float)(idEnd - idStart));
|
||||
PrintAndLogEx(INPLACE, "Brute DESFire AID Progress " _YELLOW_("%0.1f") " %% current AID: %06X", progress, id);
|
||||
|
||||
res = DesfireSelectAIDHexNoFieldOn(&dctx, id);
|
||||
if (res == PM3_ECARDEXCHANGE || res == PM3_ETIMEOUT || res == PM3_ERFTRANS) {
|
||||
for (int attempt = 1; attempt <= MFDES_BRUTEAID_RESELECT_ATTEMPTS; attempt++) {
|
||||
printf("\33[2K\r"); // clear current inplace progress line before logging
|
||||
PrintAndLogEx(WARNING, "No card response while checking AID " _YELLOW_("%06X") ". Reselecting card (%d/%d)...",
|
||||
id, attempt, MFDES_BRUTEAID_RESELECT_ATTEMPTS);
|
||||
|
||||
msleep(MFDES_BRUTEAID_RESELECT_WAIT_MS);
|
||||
|
||||
res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0);
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (res == PM3_ECARDEXCHANGE || res == PM3_ETIMEOUT || res == PM3_ERFTRANS) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
res = DesfireSelectAIDHexNoFieldOn(&dctx, id);
|
||||
if (res == PM3_SUCCESS || res == PM3_EAPDU_FAIL ||
|
||||
(res != PM3_ECARDEXCHANGE && res != PM3_ETIMEOUT && res != PM3_ERFTRANS)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == PM3_ECARDEXCHANGE || res == PM3_ETIMEOUT || res == PM3_ERFTRANS) {
|
||||
PrintAndLogEx(FAILED, "Card is not responding after %d reselect attempts. Aborting at AID " _YELLOW_("%06X"),
|
||||
MFDES_BRUTEAID_RESELECT_ATTEMPTS, id);
|
||||
mfdesBruteAIDGeneratorFree(&generator);
|
||||
DropField();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == PM3_SUCCESS) {
|
||||
printf("\33[2K\r"); // clear current line before printing
|
||||
@@ -2260,6 +2724,7 @@ static int CmdHF14ADesBruteApps(const char *Cmd) {
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
PrintAndLogEx(SUCCESS, _GREEN_("Done!"));
|
||||
mfdesBruteAIDGeneratorFree(&generator);
|
||||
DropField();
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -517,8 +517,12 @@ static int DESFIRESendRaw(bool activate_field, uint8_t *data, size_t datalen, ui
|
||||
PrintAndLogEx(SUCCESS, "raw<< %s", sprint_hex(result, *result_len));
|
||||
}
|
||||
|
||||
if (*result_len < 1) {
|
||||
return PM3_SUCCESS;
|
||||
if (*result_len == 0) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
if (*result_len < (1 + 2)) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
*result_len -= (1 + 2);
|
||||
@@ -538,6 +542,7 @@ static int DESFIRESendRaw(bool activate_field, uint8_t *data, size_t datalen, ui
|
||||
if (GetAPDULogging()) {
|
||||
PrintAndLogEx(ERR, "Command (%02x) ERROR: 0x%02x", data[0], rcode);
|
||||
}
|
||||
|
||||
return PM3_EAPDU_FAIL;
|
||||
}
|
||||
return PM3_SUCCESS;
|
||||
@@ -591,6 +596,9 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin
|
||||
|
||||
res = DESFIRESendRaw(activate_field, &cdata[sendindx], sendlen, buf, DESFIRE_BUFFER_SIZE, &buflen, &rcode);
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (respcode != NULL) {
|
||||
*respcode = rcode;
|
||||
}
|
||||
uint16_t ssw = DESFIRE_GET_ISO_STATUS(rcode);
|
||||
PrintAndLogEx(DEBUG, "error DESFIRESendRaw %s", DesfireGetErrorString(res, &ssw));
|
||||
free(buf);
|
||||
@@ -638,8 +646,12 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext_t *ctx, uin
|
||||
|
||||
res = DESFIRESendRaw(false, cdata, 1, buf, DESFIRE_BUFFER_SIZE, &buflen, &rcode);
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (respcode != NULL) {
|
||||
*respcode = rcode;
|
||||
}
|
||||
uint16_t ssw = DESFIRE_GET_ISO_STATUS(rcode);
|
||||
PrintAndLogEx(DEBUG, "error DESFIRESendRaw %s", DesfireGetErrorString(res, &ssw));
|
||||
free(buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -716,6 +728,9 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext_t *ctx,
|
||||
|
||||
res = DESFIRESendApdu(activate_field, apdu, buf, DESFIRE_BUFFER_SIZE, &buflen, &sw);
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (respcode != NULL && ((sw & 0xFF00) == 0x9100)) {
|
||||
*respcode = sw & 0xFF;
|
||||
}
|
||||
PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", DesfireGetErrorString(res, &sw));
|
||||
free(buf);
|
||||
return res;
|
||||
@@ -768,6 +783,9 @@ static int DesfireExchangeISONative(bool activate_field, DesfireContext_t *ctx,
|
||||
|
||||
res = DESFIRESendApdu(false, apdu, buf, DESFIRE_BUFFER_SIZE, &buflen, &sw);
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (respcode != NULL && ((sw & 0xFF00) == 0x9100)) {
|
||||
*respcode = sw & 0xFF;
|
||||
}
|
||||
PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", DesfireGetErrorString(res, &sw));
|
||||
free(buf);
|
||||
return res;
|
||||
@@ -926,28 +944,36 @@ int DesfireSelectAID(DesfireContext_t *ctx, uint8_t *aid1, uint8_t *aid2) {
|
||||
|
||||
uint8_t resp[257] = {0};
|
||||
size_t resplen = 0;
|
||||
uint8_t respcode = 0;
|
||||
uint8_t respcode = 0xFF;
|
||||
|
||||
ctx->secureChannel = DACNone;
|
||||
int res = DesfireExchangeEx(true, ctx, MFDES_SELECT_APPLICATION, data, (aid2 == NULL) ? 3 : 6, &respcode, resp, &resplen, true, 0);
|
||||
if (res == PM3_SUCCESS) {
|
||||
if (resplen != 0) {
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (res == PM3_EAPDU_FAIL && respcode == 0xFF && resplen == 0) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
// select operation fail
|
||||
if (respcode != MFDES_S_OPERATION_OK) {
|
||||
return PM3_EAPDU_FAIL;
|
||||
}
|
||||
|
||||
DesfireClearSession(ctx);
|
||||
ctx->appSelected = (aid1[0] != 0x00 || aid1[1] != 0x00 || aid1[2] != 0x00);
|
||||
ctx->selectedAID = DesfireAIDByteToUint(aid1);
|
||||
|
||||
return PM3_SUCCESS;
|
||||
return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
if (resplen != 0) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
// no status byte received from card
|
||||
if (respcode == 0xFF) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
// select operation fail
|
||||
if (respcode != MFDES_S_OPERATION_OK) {
|
||||
return PM3_EAPDU_FAIL;
|
||||
}
|
||||
|
||||
DesfireClearSession(ctx);
|
||||
ctx->appSelected = (aid1[0] != 0x00 || aid1[1] != 0x00 || aid1[2] != 0x00);
|
||||
ctx->selectedAID = DesfireAIDByteToUint(aid1);
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
int DesfireSelectAIDHex(DesfireContext_t *ctx, uint32_t aid1, bool select_two, uint32_t aid2) {
|
||||
@@ -966,27 +992,36 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext_t *ctx, uint32_t aid) {
|
||||
|
||||
uint8_t resp[257] = {0};
|
||||
size_t resplen = 0;
|
||||
uint8_t respcode = 0;
|
||||
uint8_t respcode = 0xFF;
|
||||
|
||||
ctx->secureChannel = DACNone;
|
||||
int res = DesfireExchangeEx(false, ctx, MFDES_SELECT_APPLICATION, data, 3, &respcode, resp, &resplen, true, 0);
|
||||
if (res == PM3_SUCCESS) {
|
||||
if (resplen != 0) {
|
||||
if (res != PM3_SUCCESS) {
|
||||
if (res == PM3_EAPDU_FAIL && respcode == 0xFF && resplen == 0) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
// select operation fail
|
||||
if (respcode != MFDES_S_OPERATION_OK) {
|
||||
return PM3_EAPDU_FAIL;
|
||||
}
|
||||
|
||||
DesfireClearSession(ctx);
|
||||
ctx->appSelected = (aid != 0x000000);
|
||||
ctx->selectedAID = aid;
|
||||
|
||||
return PM3_SUCCESS;
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
|
||||
if (resplen != 0) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
// no status byte received from card
|
||||
if (respcode == 0xFF) {
|
||||
return PM3_ECARDEXCHANGE;
|
||||
}
|
||||
|
||||
// select operation fail
|
||||
if (respcode != MFDES_S_OPERATION_OK) {
|
||||
return PM3_EAPDU_FAIL;
|
||||
}
|
||||
|
||||
DesfireClearSession(ctx);
|
||||
ctx->appSelected = (aid != 0x000000);
|
||||
ctx->selectedAID = aid;
|
||||
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
void DesfirePrintMADAID(uint32_t appid, bool verbose) {
|
||||
|
||||
Reference in new issue
Block a user