diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index b21277e6f..c59cfc144 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -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 ")); + 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 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", "", "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", "", "raw bytes to write, 16 hex bytes"), arg_str0("k", "key", "", "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", "", "Gen1 mode (7A FF)"), - arg_str0(NULL, "cuid", "", "Gen2/CUID mode"), - arg_str0(NULL, "cl2", "", "CL2 mode (7-byte UID) - pair with `hf mf gdmsetuid -u <7-byte>` to set a valid UID"), - arg_str0(NULL, "shadow", "", "Shadow mode"), - arg_str0(NULL, "magicauth", "", "Magic auth"), - arg_str0(NULL, "statenc", "", "Static encrypted nonce"), - arg_str0(NULL, "sigsec", "", "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", "", "config: Gen2/CUID mode"), + arg_str0(NULL, "cl2", "", "config: CL2 mode (7-byte UID) - pair with `hf mf gdmsetuid -u <7-byte>`"), + arg_str0(NULL, "shadow", "", "config: Shadow mode"), + arg_str0(NULL, "magicauth", "", "config: Magic auth (what --wupa uses)"), + arg_str0(NULL, "statenc", "", "config: Static encrypted nonce"), + arg_str0(NULL, "sigsec", "", "config: Signature sector"), + arg_str0(NULL, "wakestyle", "", "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 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", "", "public block number"), arg_str1("d", "data", "", "bytes to write, 16 hex bytes"), arg_str0("k", "key", "", "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", "", "hidden block number (0-7)"), arg_str1("d", "data", "", "bytes to write, 16 hex bytes"), arg_str0("k", "key", "", "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", "", "block to read"), arg_int0(NULL, "sec", "", "sector to read"), arg_str0("k", "key", "", "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", "", "block to read"), arg_int0(NULL, "sec", "", "sector to read"), arg_str0("k", "key", "", "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", "", "UID to set (4 or 7 hex bytes)"), arg_str0("k", "key", "", "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", "", "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", "", "32-byte signature (64 hex characters)"), arg_str0("k", "key", "", "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 = {