mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-10-27 18:53:01 +00:00
Merge pull request #3493 from 0x6r1an0y/20260819-gdmfix
Improve `hf mf gdm*` commands
This commit is contained in:
1 file changed
+376
-151
+376
-151
@@ -9677,6 +9677,144 @@ static int gdm_resolve_wakeup(bool gdm, bool gen1a, bool wupa, int keylen,
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// A single wakeup/auth attempt: wakeup type, magic-auth command, and key.
|
||||
typedef struct {
|
||||
uint8_t wakeup_type;
|
||||
uint8_t auth_cmd;
|
||||
uint8_t key[6];
|
||||
} gdm_wakeup_t;
|
||||
|
||||
static const char *gdm_wakeup_name(const gdm_wakeup_t *w) {
|
||||
if (w->auth_cmd == MIFARE_MAGIC_GDM_AUTH_KEY) {
|
||||
return "WUPA + magic auth";
|
||||
}
|
||||
if (w->wakeup_type == MF_WAKE_GEN1A) {
|
||||
return "gen1a (40/43)";
|
||||
}
|
||||
return "gdm alt (20/23)";
|
||||
}
|
||||
|
||||
// Build the wakeup attempts to try. Explicit --gdm/--gen1a/--wupa (or a key) => 1 attempt;
|
||||
static int gdm_wakeup_attempts(bool gdm, bool gen1a, bool wupa, const uint8_t *key, int keylen,
|
||||
bool prefer_auth, gdm_wakeup_t attempts[3], int *count) {
|
||||
if (gdm || gen1a || wupa || keylen > 0) {
|
||||
uint8_t wt = 0, ac = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wt, &ac) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
attempts[0].wakeup_type = wt;
|
||||
attempts[0].auth_cmd = ac;
|
||||
memcpy(attempts[0].key, key, 6);
|
||||
*count = 1;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static const uint8_t types_blk[3] = { MF_WAKE_GEN1A, MF_WAKE_GDM_ALT, MF_WAKE_WUPA };
|
||||
static const uint8_t auths_blk[3] = { 0, 0, MIFARE_MAGIC_GDM_AUTH_KEY };
|
||||
static const uint8_t types_auth[3] = { MF_WAKE_WUPA, MF_WAKE_GEN1A, MF_WAKE_GDM_ALT };
|
||||
static const uint8_t auths_auth[3] = { MIFARE_MAGIC_GDM_AUTH_KEY, 0, 0 };
|
||||
const uint8_t *types = prefer_auth ? types_auth : types_blk;
|
||||
const uint8_t *auths = prefer_auth ? auths_auth : auths_blk;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
attempts[i].wakeup_type = types[i];
|
||||
attempts[i].auth_cmd = auths[i];
|
||||
memset(attempts[i].key, 0, 6);
|
||||
}
|
||||
*count = 3;
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Read a single block with one wakeup attempt. Quiet: returns status, prints nothing.
|
||||
static int gdm_try_read(uint8_t read_cmd, int block_no, const gdm_wakeup_t *w, uint8_t *out) {
|
||||
mf_readblock_ex_t payload = {
|
||||
.read_cmd = read_cmd,
|
||||
.block_no = block_no,
|
||||
.wakeup = w->wakeup_type,
|
||||
.auth_cmd = w->auth_cmd
|
||||
};
|
||||
memcpy(payload.key, w->key, sizeof(payload.key));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL_EX, (uint8_t *)&payload, sizeof(payload));
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL_EX, &resp, 1500) == false) {
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
if (resp.status != PM3_SUCCESS || resp.length != MFBLOCK_SIZE) {
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
memcpy(out, resp.data.asBytes, MFBLOCK_SIZE);
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Try each attempt against `probe_blk`; return the index of the first that reads
|
||||
// successfully (data in `out`), or -1 if all fail.
|
||||
static int gdm_pick_wakeup(uint8_t read_cmd, int probe_blk, const gdm_wakeup_t *attempts,
|
||||
int count, uint8_t *out) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (gdm_try_read(read_cmd, probe_blk, &attempts[i], out) == PM3_SUCCESS) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Probe with a matching READ to select the WRITE wakeup mode, avoiding partial writes.
|
||||
static int gdm_resolve_for_write(bool gdm, bool gen1a, bool wupa,
|
||||
const uint8_t *key, int keylen,
|
||||
uint8_t probe_read_cmd, int probe_blk,
|
||||
bool needs_backdoor, const char *cmd_name,
|
||||
gdm_wakeup_t *out_w,
|
||||
uint8_t *out_probe_data, bool *out_have_probe) {
|
||||
if (out_have_probe != NULL) {
|
||||
*out_have_probe = false;
|
||||
}
|
||||
|
||||
bool explicit_mode = (gdm || gen1a || wupa || keylen > 0);
|
||||
// config reads can use magic auth; data/hidden reads usually cannot, so try backdoors first
|
||||
bool prefer_auth = (probe_read_cmd == MIFARE_MAGIC_GDM_READ_CFG);
|
||||
|
||||
gdm_wakeup_t attempts[3];
|
||||
int n_attempts = 0;
|
||||
if (gdm_wakeup_attempts(gdm, gen1a, wupa, key, keylen, prefer_auth, attempts, &n_attempts) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
// Explicit wakeup: no probe, keep legacy single-attempt behaviour.
|
||||
if (explicit_mode) {
|
||||
*out_w = attempts[0];
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
uint8_t buf[MFBLOCK_SIZE] = {0};
|
||||
int idx = gdm_pick_wakeup(probe_read_cmd, probe_blk, attempts, n_attempts, buf);
|
||||
if (idx < 0) {
|
||||
if (needs_backdoor) {
|
||||
// WUPA magic auth still reads config
|
||||
gdm_wakeup_t w = { .wakeup_type = MF_WAKE_WUPA, .auth_cmd = MIFARE_MAGIC_GDM_AUTH_KEY };
|
||||
memset(w.key, 0, sizeof(w.key));
|
||||
if (gdm_try_read(MIFARE_MAGIC_GDM_READ_CFG, 0, &w, buf) == PM3_SUCCESS) {
|
||||
PrintAndLogEx(WARNING, "This card only responds to WUPA magic auth; " _YELLOW_("%s") " needs a gen1a/gdm backdoor", cmd_name);
|
||||
PrintAndLogEx(HINT, "Hint: enable one first: " _YELLOW_("hf mf gdmsetcfg --wakestyle gen1a --wupa -k <key>"));
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
}
|
||||
PrintAndLogEx(FAILED, "Could not find a working wakeup for " _YELLOW_("%s"), cmd_name);
|
||||
PrintAndLogEx(HINT, "Hint: tried gen1a/gdm/wupa; card may be sealed or use a non-default magic auth key");
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
|
||||
*out_w = attempts[idx];
|
||||
if (out_probe_data != NULL) {
|
||||
memcpy(out_probe_data, buf, MFBLOCK_SIZE);
|
||||
}
|
||||
if (out_have_probe != NULL) {
|
||||
*out_have_probe = true;
|
||||
}
|
||||
PrintAndLogEx(INFO, "Auto-detected wakeup: " _YELLOW_("%s"), gdm_wakeup_name(out_w));
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
// Read an <on|off> string option into a tri-state: 1 = on, 0 = off, -1 = not provided.
|
||||
static int gdm_arg_onoff(CLIParserContext *ctx, int idx) {
|
||||
struct arg_str *a = arg_get_str(ctx, idx);
|
||||
@@ -9694,7 +9832,8 @@ static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf gdmgetcfg",
|
||||
"Get configuration data from a GDM card.\n"
|
||||
"By default uses GDM alt wakeup (20/23). Use `--gen1a` or `--wupa` to override.",
|
||||
"With no wakeup flag it auto-detects the card's access mode (tries WUPA magic auth,\n"
|
||||
"then gen1a, then gdm alt). Use `--gdm`/`--gen1a`/`--wupa` to force one.",
|
||||
"hf mf gdmgetcfg\n"
|
||||
"hf mf gdmgetcfg --gen1a\n"
|
||||
"hf mf gdmgetcfg --wupa -k FFFFFFFFFFFF"
|
||||
@@ -9702,9 +9841,9 @@ static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) {
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("k", "key", "<hex>", "key 6 bytes (only for --wupa mode)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
@@ -9717,35 +9856,28 @@ static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) {
|
||||
bool wupa = arg_get_lit(ctx, 4);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
uint8_t wakeup_type = 0;
|
||||
uint8_t auth_cmd = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wakeup_type, &auth_cmd) != PM3_SUCCESS) {
|
||||
gdm_wakeup_t attempts[3];
|
||||
int n_attempts = 0;
|
||||
if (gdm_wakeup_attempts(gdm, gen1a, wupa, key, keylen, true, attempts, &n_attempts) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
mf_readblock_ex_t payload = {
|
||||
.read_cmd = MIFARE_MAGIC_GDM_READ_CFG,
|
||||
.block_no = 0,
|
||||
.wakeup = wakeup_type,
|
||||
.auth_cmd = auth_cmd
|
||||
};
|
||||
memcpy(payload.key, key, sizeof(payload.key));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL_EX, (uint8_t *)&payload, sizeof(payload));
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL_EX, &resp, 1500) == false) {
|
||||
PrintAndLogEx(WARNING, "command execution time out");
|
||||
return PM3_ETIMEOUT;
|
||||
uint8_t cfg[MFBLOCK_SIZE] = {0};
|
||||
int idx = gdm_pick_wakeup(MIFARE_MAGIC_GDM_READ_CFG, 0, attempts, n_attempts, cfg);
|
||||
if (idx < 0) {
|
||||
PrintAndLogEx(FAILED, "Failed to read config block");
|
||||
if (n_attempts > 1) {
|
||||
PrintAndLogEx(HINT, "Hint: tried WUPA/gen1a/gdm wakeups; card may be sealed or use a non-default magic auth key");
|
||||
}
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
if (n_attempts > 1) {
|
||||
PrintAndLogEx(INFO, "Auto-detected wakeup: " _YELLOW_("%s"), gdm_wakeup_name(&attempts[idx]));
|
||||
}
|
||||
|
||||
if (resp.status == PM3_SUCCESS && resp.length == MFBLOCK_SIZE) {
|
||||
parse_gdm_cfg(resp.data.asBytes);
|
||||
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
}
|
||||
|
||||
return resp.status;
|
||||
parse_gdm_cfg(cfg);
|
||||
PrintAndLogEx(NORMAL, "");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) {
|
||||
@@ -9753,25 +9885,28 @@ static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) {
|
||||
CLIParserInit(&ctx, "hf mf gdmsetcfg",
|
||||
"Set configuration data on a GDM card\n"
|
||||
"Can either write raw 16 bytes (-d) or configure specific flags.\n"
|
||||
"If flags are provided, it will read the config, update flags, and write it back.",
|
||||
"If flags are provided, it will read the config, update flags, and write it back.\n"
|
||||
"With no wakeup flag it auto-detects the card's access mode (WUPA magic auth, gen1a, or gdm alt).\n"
|
||||
"`access:` flags select how this command talks to the card; `config:` flags change what is stored.",
|
||||
"hf mf gdmsetcfg -d 850000000000000000005A5A00000008\n"
|
||||
"hf mf gdmsetcfg --cuid on --gen1 on\n"
|
||||
"hf mf gdmsetcfg --cuid on --wakestyle gen1a\n"
|
||||
"hf mf gdmsetcfg --wakestyle off\n"
|
||||
"hf mf gdmsetcfg --shadow off --sigsec off --wupa -k FFFFFFFFFFFF"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("d", "data", "<hex>", "raw bytes to write, 16 hex bytes"),
|
||||
arg_str0("k", "key", "<hex>", "key 6 bytes (only for regular wakeup)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_str0(NULL, "gen1", "<on|off>", "Gen1 mode (7A FF)"),
|
||||
arg_str0(NULL, "cuid", "<on|off>", "Gen2/CUID mode"),
|
||||
arg_str0(NULL, "cl2", "<on|off>", "CL2 mode (7-byte UID) - pair with `hf mf gdmsetuid -u <7-byte>` to set a valid UID"),
|
||||
arg_str0(NULL, "shadow", "<on|off>", "Shadow mode"),
|
||||
arg_str0(NULL, "magicauth", "<on|off>", "Magic auth"),
|
||||
arg_str0(NULL, "statenc", "<on|off>", "Static encrypted nonce"),
|
||||
arg_str0(NULL, "sigsec", "<on|off>", "Signature sector"),
|
||||
arg_lit0(NULL, "gdm", "access: force gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_str0(NULL, "cuid", "<on|off>", "config: Gen2/CUID mode"),
|
||||
arg_str0(NULL, "cl2", "<on|off>", "config: CL2 mode (7-byte UID) - pair with `hf mf gdmsetuid -u <7-byte>`"),
|
||||
arg_str0(NULL, "shadow", "<on|off>", "config: Shadow mode"),
|
||||
arg_str0(NULL, "magicauth", "<on|off>", "config: Magic auth (what --wupa uses)"),
|
||||
arg_str0(NULL, "statenc", "<on|off>", "config: Static encrypted nonce"),
|
||||
arg_str0(NULL, "sigsec", "<on|off>", "config: Signature sector"),
|
||||
arg_str0(NULL, "wakestyle", "<gdm|gen1a|off>", "config: backdoor - gdm (7AFF85), gen1a (7AFF00), or off (8500, seal)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
@@ -9789,13 +9924,29 @@ static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) {
|
||||
bool wupa = arg_get_lit(ctx, 5);
|
||||
|
||||
// read <on|off> flags into tri-states (1/0/-1) before freeing ctx
|
||||
int gen1_flag = gdm_arg_onoff(ctx, 6);
|
||||
int cuid_flag = gdm_arg_onoff(ctx, 7);
|
||||
int cl2_flag = gdm_arg_onoff(ctx, 8);
|
||||
int shadow_flag = gdm_arg_onoff(ctx, 9);
|
||||
int magicauth_flag = gdm_arg_onoff(ctx, 10);
|
||||
int statenc_flag = gdm_arg_onoff(ctx, 11);
|
||||
int sigsec_flag = gdm_arg_onoff(ctx, 12);
|
||||
int cuid_flag = gdm_arg_onoff(ctx, 6);
|
||||
int cl2_flag = gdm_arg_onoff(ctx, 7);
|
||||
int shadow_flag = gdm_arg_onoff(ctx, 8);
|
||||
int magicauth_flag = gdm_arg_onoff(ctx, 9);
|
||||
int statenc_flag = gdm_arg_onoff(ctx, 10);
|
||||
int sigsec_flag = gdm_arg_onoff(ctx, 11);
|
||||
|
||||
// Backdoor selector, the only control for it: gdm -> 7AFF85, gen1a -> 7AFF00, off -> 8500.
|
||||
enum { WS_NONE, WS_GDM, WS_GEN1A, WS_OFF } wakestyle = WS_NONE;
|
||||
struct arg_str *ws = arg_get_str(ctx, 12);
|
||||
if (ws->count > 0) {
|
||||
if (strcasecmp(ws->sval[0], "gdm") == 0) {
|
||||
wakestyle = WS_GDM;
|
||||
} else if (strcasecmp(ws->sval[0], "gen1a") == 0) {
|
||||
wakestyle = WS_GEN1A;
|
||||
} else if (strcasecmp(ws->sval[0], "off") == 0) {
|
||||
wakestyle = WS_OFF;
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "--wakestyle must be " _YELLOW_("gdm") ", " _YELLOW_("gen1a") " or " _YELLOW_("off") ", got `%s`", ws->sval[0]);
|
||||
CLIParserFree(ctx);
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
}
|
||||
|
||||
CLIParserFree(ctx);
|
||||
|
||||
@@ -9804,14 +9955,23 @@ static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t wakeup_type = 0;
|
||||
uint8_t auth_cmd = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wakeup_type, &auth_cmd) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
// Auto-detect wakeup by probing a config read (reachable via magic auth or either backdoor).
|
||||
gdm_wakeup_t gw;
|
||||
uint8_t probe_cfg[MFBLOCK_SIZE] = {0};
|
||||
bool have_probe = false;
|
||||
int wres = gdm_resolve_for_write(gdm, gen1a, wupa, key, keylen,
|
||||
MIFARE_MAGIC_GDM_READ_CFG, 0, false, "hf mf gdmsetcfg",
|
||||
&gw, probe_cfg, &have_probe);
|
||||
if (wres != PM3_SUCCESS) {
|
||||
return wres;
|
||||
}
|
||||
uint8_t wakeup_type = gw.wakeup_type;
|
||||
uint8_t auth_cmd = gw.auth_cmd;
|
||||
memcpy(key, gw.key, sizeof(key));
|
||||
|
||||
bool any_flag = (gen1_flag != -1 || cuid_flag != -1 || cl2_flag != -1 || shadow_flag != -1 ||
|
||||
magicauth_flag != -1 || statenc_flag != -1 || sigsec_flag != -1);
|
||||
bool any_flag = (cuid_flag != -1 || cl2_flag != -1 || shadow_flag != -1 ||
|
||||
magicauth_flag != -1 || statenc_flag != -1 || sigsec_flag != -1 ||
|
||||
wakestyle != WS_NONE);
|
||||
|
||||
// Logic: if -d is specified, write raw block.
|
||||
// Else, read config, update flags, write block.
|
||||
@@ -9852,9 +10012,11 @@ static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
// Step 1: Read config
|
||||
// Step 1: Read config - reuse the block already read while auto-probing the wakeup.
|
||||
uint8_t config[MFBLOCK_SIZE];
|
||||
if (gdm_read_config(wakeup_type, auth_cmd, key, config) != PM3_SUCCESS) {
|
||||
if (have_probe) {
|
||||
memcpy(config, probe_cfg, MFBLOCK_SIZE);
|
||||
} else if (gdm_read_config(wakeup_type, auth_cmd, key, config) != PM3_SUCCESS) {
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
bool changed = false;
|
||||
@@ -9869,21 +10031,28 @@ static int CmdHF14AGen4_GDM_SetCfg(const char *Cmd) {
|
||||
} \
|
||||
}
|
||||
|
||||
if (gen1_flag != -1) {
|
||||
if (gen1_flag == 1) {
|
||||
if (config[0] != 0x7A || config[1] != 0xFF) {
|
||||
config[0] = 0x7A;
|
||||
config[1] = 0xFF;
|
||||
changed = true;
|
||||
PrintAndLogEx(INFO, "Set Gen1 mode to on");
|
||||
}
|
||||
} else {
|
||||
if (config[0] != 0x85 || config[1] != 0x00) {
|
||||
config[0] = 0x85;
|
||||
config[1] = 0x00;
|
||||
changed = true;
|
||||
PrintAndLogEx(INFO, "Set Gen1 mode to off");
|
||||
}
|
||||
if (wakestyle == WS_OFF) {
|
||||
if (config[0] != 0x85 || config[1] != 0x00) {
|
||||
config[0] = 0x85;
|
||||
config[1] = 0x00;
|
||||
changed = true;
|
||||
PrintAndLogEx(INFO, "Set magic wakeup to off (backdoor sealed)");
|
||||
PrintAndLogEx(WARNING, "Both gdm and gen1a wakeups are now disabled; only WUPA magic auth remains");
|
||||
}
|
||||
} else if (wakestyle == WS_GDM || wakestyle == WS_GEN1A) {
|
||||
if (config[0] != 0x7A || config[1] != 0xFF) {
|
||||
config[0] = 0x7A;
|
||||
config[1] = 0xFF;
|
||||
changed = true;
|
||||
PrintAndLogEx(INFO, "Set magic wakeup to on");
|
||||
}
|
||||
uint8_t style_byte = (wakestyle == WS_GDM) ? 0x85 : 0x00;
|
||||
if (config[2] != style_byte) {
|
||||
config[2] = style_byte;
|
||||
changed = true;
|
||||
PrintAndLogEx(INFO, "Set wakeup style to %s", (wakestyle == WS_GDM) ? "gdm alt (20/23)" : "gen1a (40/43)");
|
||||
PrintAndLogEx(WARNING, "After this write the card answers " _YELLOW_("--%s") " only, not the other one",
|
||||
(wakestyle == WS_GDM) ? "gdm" : "gen1a");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9920,7 +10089,7 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf gdmsetblk",
|
||||
"Set public block data on a GDM card\n"
|
||||
"By default uses GDM alt wakeup (20/23). Use `--gen1a` or `--wupa` to override.\n"
|
||||
"With no wakeup flag it auto-detects the card's access mode (gen1a, gdm alt, or WUPA magic auth).\n"
|
||||
"`--force` param is used to override warnings like bad ACL writes.\n"
|
||||
" if not specified, it will exit if detected",
|
||||
"hf mf gdmsetblk --blk 1 -d 000102030405060708090a0b0c0d0e0f\n"
|
||||
@@ -9932,9 +10101,9 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) {
|
||||
arg_int1(NULL, "blk", "<dec>", "public block number"),
|
||||
arg_str1("d", "data", "<hex>", "bytes to write, 16 hex bytes"),
|
||||
arg_str0("k", "key", "<hex>", "key, 6 hex bytes (only for --wupa mode)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "gdm", "access: force gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "force", "override warnings"),
|
||||
arg_param_end
|
||||
};
|
||||
@@ -9966,11 +10135,17 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t wakeup_type = 0;
|
||||
uint8_t auth_cmd = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wakeup_type, &auth_cmd) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
// Auto-detect wakeup by probing a public-block read of the target block.
|
||||
gdm_wakeup_t gw;
|
||||
int wres = gdm_resolve_for_write(gdm, gen1a, wupa, key, keylen,
|
||||
ISO14443A_CMD_READBLOCK, b, false, "hf mf gdmsetblk",
|
||||
&gw, NULL, NULL);
|
||||
if (wres != PM3_SUCCESS) {
|
||||
return wres;
|
||||
}
|
||||
uint8_t wakeup_type = gw.wakeup_type;
|
||||
uint8_t auth_cmd = gw.auth_cmd;
|
||||
memcpy(key, gw.key, sizeof(key));
|
||||
|
||||
uint8_t blockno = (uint8_t)b;
|
||||
|
||||
@@ -10016,7 +10191,7 @@ static int CmdHF14AGen4_GDM_SetHidBlk(const char *Cmd) {
|
||||
CLIParserInit(&ctx, "hf mf gdmsethidblk",
|
||||
"Write hidden block data on a GDM card.\n"
|
||||
"Hidden blocks 0-7 map to the card's hidden sector area.\n"
|
||||
"By default uses GDM alt wakeup (20/23). Use `--gen1a` or `--wupa` to override.\n"
|
||||
"With no wakeup flag it auto-detects a backdoor (gen1a or gdm alt); a WUPA-only card is rejected.\n"
|
||||
"`--force` param is used to override warnings like bad BCC or ACL writes.\n"
|
||||
" if not specified, it will exit if detected",
|
||||
"hf mf gdmsethidblk --blk 0 -d 880494716904EA0C2394510800000000\n"
|
||||
@@ -10029,9 +10204,9 @@ static int CmdHF14AGen4_GDM_SetHidBlk(const char *Cmd) {
|
||||
arg_int1(NULL, "blk", "<dec>", "hidden block number (0-7)"),
|
||||
arg_str1("d", "data", "<hex>", "bytes to write, 16 hex bytes"),
|
||||
arg_str0("k", "key", "<hex>", "key 6 bytes (only for --wupa mode)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "gdm", "access: force gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "force", "override warnings"),
|
||||
arg_param_end
|
||||
};
|
||||
@@ -10066,11 +10241,17 @@ static int CmdHF14AGen4_GDM_SetHidBlk(const char *Cmd) {
|
||||
}
|
||||
|
||||
// --- validate key length + resolve wakeup mode ---
|
||||
uint8_t wakeup_type = 0;
|
||||
uint8_t auth_cmd = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wakeup_type, &auth_cmd) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
// Auto-detect wakeup by probing a hidden-block read; hidden writes need a gen1a/gdm backdoor.
|
||||
gdm_wakeup_t gw;
|
||||
int wres = gdm_resolve_for_write(gdm, gen1a, wupa, key, keylen,
|
||||
MIFARE_MAGIC_GDM_READBLOCK, b, true, "hf mf gdmsethidblk",
|
||||
&gw, NULL, NULL);
|
||||
if (wres != PM3_SUCCESS) {
|
||||
return wres;
|
||||
}
|
||||
uint8_t wakeup_type = gw.wakeup_type;
|
||||
uint8_t auth_cmd = gw.auth_cmd;
|
||||
memcpy(key, gw.key, sizeof(key));
|
||||
|
||||
uint8_t blockno = (uint8_t)b;
|
||||
|
||||
@@ -10171,40 +10352,34 @@ static int gdm_read_blocks(int blk, int sec, const uint8_t *key, int keylen,
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t wakeup_type = 0;
|
||||
uint8_t auth_cmd = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wakeup_type, &auth_cmd) != PM3_SUCCESS) {
|
||||
gdm_wakeup_t attempts[3];
|
||||
int n_attempts = 0;
|
||||
if (gdm_wakeup_attempts(gdm, gen1a, wupa, key, keylen, false, attempts, &n_attempts) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
int start_blk = (blk != -1) ? blk : mfFirstBlockOfSector(sec);
|
||||
int num_blks = (blk != -1) ? 1 : mfNumBlocksPerSector(sec);
|
||||
|
||||
for (int i = 0; i < num_blks; i++) {
|
||||
// probe the first block to pick a working wakeup, then reuse it for the rest
|
||||
uint8_t data[MFBLOCK_SIZE] = {0};
|
||||
int idx = gdm_pick_wakeup(read_cmd, start_blk, attempts, n_attempts, data);
|
||||
if (idx < 0) {
|
||||
PrintAndLogEx(FAILED, "Failed to read %s %d", label, start_blk);
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
if (n_attempts > 1) {
|
||||
PrintAndLogEx(INFO, "Auto-detected wakeup: " _YELLOW_("%s"), gdm_wakeup_name(&attempts[idx]));
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "%s %3d: %s", label, start_blk, sprint_hex_inrow(data, MFBLOCK_SIZE));
|
||||
|
||||
for (int i = 1; i < num_blks; i++) {
|
||||
int current_blk = start_blk + i;
|
||||
|
||||
mf_readblock_ex_t payload = {
|
||||
.read_cmd = read_cmd,
|
||||
.block_no = current_blk,
|
||||
.wakeup = wakeup_type,
|
||||
.auth_cmd = auth_cmd
|
||||
};
|
||||
memcpy(payload.key, key, sizeof(payload.key));
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_READBL_EX, (uint8_t *)&payload, sizeof(payload));
|
||||
PacketResponseNG resp;
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL_EX, &resp, 1500) == false) {
|
||||
PrintAndLogEx(FAILED, "command execution time out (block %d)", current_blk);
|
||||
return PM3_ETIMEOUT;
|
||||
}
|
||||
|
||||
if (resp.status == PM3_SUCCESS && resp.length == MFBLOCK_SIZE) {
|
||||
PrintAndLogEx(SUCCESS, "%s %3d: %s", label, current_blk, sprint_hex_inrow(resp.data.asBytes, MFBLOCK_SIZE));
|
||||
} else {
|
||||
PrintAndLogEx(FAILED, "Failed to read %s %d. Status: %d", label, current_blk, resp.status);
|
||||
return resp.status;
|
||||
if (gdm_try_read(read_cmd, current_blk, &attempts[idx], data) != PM3_SUCCESS) {
|
||||
PrintAndLogEx(FAILED, "Failed to read %s %d", label, current_blk);
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
PrintAndLogEx(SUCCESS, "%s %3d: %s", label, current_blk, sprint_hex_inrow(data, MFBLOCK_SIZE));
|
||||
}
|
||||
|
||||
return PM3_SUCCESS;
|
||||
@@ -10213,7 +10388,8 @@ static int gdm_read_blocks(int blk, int sec, const uint8_t *key, int keylen,
|
||||
static int CmdHF14AGen4_GDM_GetHidBlk(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf gdmgethidblk",
|
||||
"Read hidden block(s) from a GDM card",
|
||||
"Read hidden block(s) from a GDM card.\n"
|
||||
"With no wakeup flag it auto-detects the card's access mode (WUPA magic auth, gen1a, or gdm alt).",
|
||||
"hf mf gdmgethidblk --blk 0\n"
|
||||
"hf mf gdmgethidblk --sec 1 --gdm"
|
||||
);
|
||||
@@ -10222,9 +10398,9 @@ static int CmdHF14AGen4_GDM_GetHidBlk(const char *Cmd) {
|
||||
arg_int0(NULL, "blk", "<dec>", "block to read"),
|
||||
arg_int0(NULL, "sec", "<dec>", "sector to read"),
|
||||
arg_str0("k", "key", "<hex>", "key 6 bytes (only for --wupa mode)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
@@ -10245,7 +10421,8 @@ static int CmdHF14AGen4_GDM_GetHidBlk(const char *Cmd) {
|
||||
static int CmdHF14AGen4_GDM_GetBlk(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf gdmgetblk",
|
||||
"Read public block(s) from a GDM card",
|
||||
"Read public block(s) from a GDM card.\n"
|
||||
"With no wakeup flag it auto-detects the card's access mode (WUPA magic auth, gen1a, or gdm alt).",
|
||||
"hf mf gdmgetblk --blk 0\n"
|
||||
"hf mf gdmgetblk --sec 1 --gdm"
|
||||
);
|
||||
@@ -10254,9 +10431,9 @@ static int CmdHF14AGen4_GDM_GetBlk(const char *Cmd) {
|
||||
arg_int0(NULL, "blk", "<dec>", "block to read"),
|
||||
arg_int0(NULL, "sec", "<dec>", "sector to read"),
|
||||
arg_str0("k", "key", "<hex>", "key 6 bytes (only for --wupa mode)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
@@ -10277,7 +10454,8 @@ static int CmdHF14AGen4_GDM_GetBlk(const char *Cmd) {
|
||||
static int CmdHF14AGen4_GDM_SetUid(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf gdmsetuid",
|
||||
"Set UID on a GDM card",
|
||||
"Set UID on a GDM card.\n"
|
||||
"With no wakeup flag it auto-detects a backdoor (gen1a or gdm alt); a WUPA-only card is rejected.",
|
||||
"hf mf gdmsetuid -u 01020304\n"
|
||||
"hf mf gdmsetuid -u 01020304050607 --gen1a"
|
||||
);
|
||||
@@ -10285,9 +10463,9 @@ static int CmdHF14AGen4_GDM_SetUid(const char *Cmd) {
|
||||
arg_param_begin,
|
||||
arg_str1("u", "uid", "<hex>", "UID to set (4 or 7 hex bytes)"),
|
||||
arg_str0("k", "key", "<hex>", "key 6 bytes (only for --wupa mode)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "gdm", "access: force gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "f3d", "use F3 perso mode (writes to hidden block 1 instead of 0)"),
|
||||
arg_param_end
|
||||
};
|
||||
@@ -10312,11 +10490,17 @@ static int CmdHF14AGen4_GDM_SetUid(const char *Cmd) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t wakeup_type = 0;
|
||||
uint8_t auth_cmd = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wakeup_type, &auth_cmd) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
// Auto-detect wakeup by probing a hidden-block read; gdmsetuid needs a gen1a/gdm backdoor
|
||||
gdm_wakeup_t gw;
|
||||
int wres = gdm_resolve_for_write(gdm, gen1a, wupa, key, keylen,
|
||||
MIFARE_MAGIC_GDM_READBLOCK, 0, true, "hf mf gdmsetuid",
|
||||
&gw, NULL, NULL);
|
||||
if (wres != PM3_SUCCESS) {
|
||||
return wres;
|
||||
}
|
||||
uint8_t wakeup_type = gw.wakeup_type;
|
||||
uint8_t auth_cmd = gw.auth_cmd;
|
||||
memcpy(key, gw.key, sizeof(key));
|
||||
|
||||
// Step 1: Read Config
|
||||
uint8_t config[MFBLOCK_SIZE];
|
||||
@@ -10435,19 +10619,21 @@ static int CmdHF14AGen4_GDM_SetUid(const char *Cmd) {
|
||||
static int CmdHF14AGen4_GDM_Wipe(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf gdmwipe",
|
||||
"Wipe a GDM card to factory defaults",
|
||||
"Wipe a GDM card to factory defaults.\n"
|
||||
"With no wakeup flag it auto-detects a backdoor (gen1a or gdm alt); a WUPA-only card is rejected.",
|
||||
"hf mf gdmwipe --gdm"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str0("k", "key", "<hex>", "key 6 bytes (only for --wupa mode)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "gdm", "access: force gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0("a", "all", "wipe 4K card (40 sectors) instead of 1K card (16 sectors)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
// allow a bare `hf mf gdmwipe` (all args optional) so the no-flag auto-detect path works
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, true);
|
||||
|
||||
int keylen = 0;
|
||||
uint8_t key[6] = {0};
|
||||
@@ -10459,14 +10645,28 @@ static int CmdHF14AGen4_GDM_Wipe(const char *Cmd) {
|
||||
bool wipe_4k = arg_get_lit(ctx, 5);
|
||||
CLIParserFree(ctx);
|
||||
|
||||
uint8_t wakeup_type = 0;
|
||||
uint8_t auth_cmd = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wakeup_type, &auth_cmd) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
// Auto-detect wakeup by probing a hidden-block read; gdmwipe rewrites hidden blocks,
|
||||
// so it needs a gen1a/gdm backdoor (WUPA-only cards are rejected before any write).
|
||||
gdm_wakeup_t gw;
|
||||
int wres = gdm_resolve_for_write(gdm, gen1a, wupa, key, keylen,
|
||||
MIFARE_MAGIC_GDM_READBLOCK, 0, true, "hf mf gdmwipe",
|
||||
&gw, NULL, NULL);
|
||||
if (wres != PM3_SUCCESS) {
|
||||
return wres;
|
||||
}
|
||||
uint8_t wakeup_type = gw.wakeup_type;
|
||||
uint8_t auth_cmd = gw.auth_cmd;
|
||||
memcpy(key, gw.key, sizeof(key));
|
||||
|
||||
// Step 1: Write Config (gen1a + wupa + 4B UID)
|
||||
uint8_t standard_config[MFBLOCK_SIZE] = {0x7A, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x08};
|
||||
uint8_t cur_config[MFBLOCK_SIZE] = {0};
|
||||
if (gdm_read_config(wakeup_type, auth_cmd, key, cur_config) == PM3_SUCCESS) {
|
||||
standard_config[2] = cur_config[2];
|
||||
} else {
|
||||
PrintAndLogEx(WARNING, "Could not read current config, keeping the default wakeup style");
|
||||
}
|
||||
|
||||
// Step 1: Write Config
|
||||
uint8_t standard_config[MFBLOCK_SIZE] = {0x7A, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x08};
|
||||
mf_writeblock_ex_t config_payload = {
|
||||
.wakeup = wakeup_type,
|
||||
.auth_cmd = auth_cmd,
|
||||
@@ -10497,6 +10697,9 @@ static int CmdHF14AGen4_GDM_Wipe(const char *Cmd) {
|
||||
};
|
||||
memcpy(write_payload.key, key, 6);
|
||||
|
||||
int failed_blocks = 0;
|
||||
int total_blocks = 0;
|
||||
|
||||
for (int sec = 0; sec < max_sector; sec++) {
|
||||
int first_blk = mfFirstBlockOfSector(sec);
|
||||
int num_blks = mfNumBlocksPerSector(sec);
|
||||
@@ -10517,7 +10720,9 @@ static int CmdHF14AGen4_GDM_Wipe(const char *Cmd) {
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_WRITEBL_EX, (uint8_t *)&write_payload, sizeof(write_payload));
|
||||
WaitForResponseTimeout(CMD_HF_MIFARE_WRITEBL_EX, &resp, 1500);
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFARE_WRITEBL_EX, &resp, 1500) == false || resp.status != PM3_SUCCESS) {
|
||||
failed_blocks++;
|
||||
}
|
||||
|
||||
// Hidden block writes
|
||||
write_payload.write_cmd = MIFARE_MAGIC_GDM_WRITEBLOCK; // 0xA8
|
||||
@@ -10527,28 +10732,42 @@ static int CmdHF14AGen4_GDM_Wipe(const char *Cmd) {
|
||||
|
||||
clearCommandBuffer();
|
||||
SendCommandNG(CMD_HF_MIFARE_WRITEBL_EX, (uint8_t *)&write_payload, sizeof(write_payload));
|
||||
WaitForResponseTimeout(CMD_HF_MIFARE_WRITEBL_EX, &resp, 1500);
|
||||
if (WaitForResponseTimeout(CMD_HF_MIFARE_WRITEBL_EX, &resp, 1500) == false || resp.status != PM3_SUCCESS) {
|
||||
failed_blocks++;
|
||||
}
|
||||
total_blocks += 2;
|
||||
}
|
||||
PrintAndLogEx(INFO, "Wiping sector %d...", sec);
|
||||
}
|
||||
|
||||
PrintAndLogEx(SUCCESS, "Wipe completed!");
|
||||
return PM3_SUCCESS;
|
||||
// report write failures instead of always printing "Wipe completed!"
|
||||
if (failed_blocks == 0) {
|
||||
PrintAndLogEx(SUCCESS, "Wipe completed!");
|
||||
return PM3_SUCCESS;
|
||||
}
|
||||
|
||||
PrintAndLogEx(FAILED, "Wipe incomplete - " _RED_("%d") " of %d block writes failed", failed_blocks, total_blocks);
|
||||
if (failed_blocks == total_blocks) {
|
||||
PrintAndLogEx(HINT, "Hint: the card did not accept any data write. Check that the wakeup still matches");
|
||||
PrintAndLogEx(HINT, " the card's config (`" _YELLOW_("hf mf gdmgetcfg") "`), then retry");
|
||||
}
|
||||
return PM3_EFAILED;
|
||||
}
|
||||
|
||||
static int CmdHF14AGen4_GDM_SetSig(const char *Cmd) {
|
||||
CLIParserContext *ctx;
|
||||
CLIParserInit(&ctx, "hf mf gdmsetsig",
|
||||
"Set MFC EV1 signature on a GDM card",
|
||||
"Set MFC EV1 signature on a GDM card.\n"
|
||||
"With no wakeup flag it auto-detects a backdoor (gen1a or gdm alt); a WUPA-only card is rejected.",
|
||||
"hf mf gdmsetsig -s <64 hex chars>"
|
||||
);
|
||||
void *argtable[] = {
|
||||
arg_param_begin,
|
||||
arg_str1("s", "sig", "<hex>", "32-byte signature (64 hex characters)"),
|
||||
arg_str0("k", "key", "<hex>", "key 6 bytes (only for --wupa mode)"),
|
||||
arg_lit0(NULL, "gdm", "use gdm alt (20/23) magic wakeup (default)"),
|
||||
arg_lit0(NULL, "gen1a", "use gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "use regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_lit0(NULL, "gdm", "access: force gdm alt (20/23) magic wakeup"),
|
||||
arg_lit0(NULL, "gen1a", "access: force gen1a (40/43) magic wakeup"),
|
||||
arg_lit0(NULL, "wupa", "access: force regular wakeup (WUPA + magic auth with key)"),
|
||||
arg_param_end
|
||||
};
|
||||
CLIExecWithReturn(ctx, Cmd, argtable, false);
|
||||
@@ -10571,11 +10790,17 @@ static int CmdHF14AGen4_GDM_SetSig(const char *Cmd) {
|
||||
return PM3_EINVARG;
|
||||
}
|
||||
|
||||
uint8_t wakeup_type = 0;
|
||||
uint8_t auth_cmd = 0;
|
||||
if (gdm_resolve_wakeup(gdm, gen1a, wupa, keylen, &wakeup_type, &auth_cmd) != PM3_SUCCESS) {
|
||||
return PM3_EINVARG;
|
||||
// Auto-detect wakeup by probing a hidden-block read (block 5); gdmsetsig needs a gen1a/gdm backdoor.
|
||||
gdm_wakeup_t gw;
|
||||
int wres = gdm_resolve_for_write(gdm, gen1a, wupa, key, keylen,
|
||||
MIFARE_MAGIC_GDM_READBLOCK, 5, true, "hf mf gdmsetsig",
|
||||
&gw, NULL, NULL);
|
||||
if (wres != PM3_SUCCESS) {
|
||||
return wres;
|
||||
}
|
||||
uint8_t wakeup_type = gw.wakeup_type;
|
||||
uint8_t auth_cmd = gw.auth_cmd;
|
||||
memcpy(key, gw.key, sizeof(key));
|
||||
|
||||
// Step 1: Write signature part 1 (block 5)
|
||||
mf_writeblock_ex_t write_payload = {
|
||||
|
||||
Reference in new issue
Block a user