From c0a7a7ae0690c07a6b4e5bb8629ccec349be5d96 Mon Sep 17 00:00:00 2001 From: YoungJules Date: Sat, 25 Jul 2026 12:42:19 +0200 Subject: [PATCH] hf legic: fix clone offsets and wrbl repeat --- client/src/cmdhflegic.c | 139 +++++++++++++++++----------------------- 1 file changed, 58 insertions(+), 81 deletions(-) diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 5e2458bcb..f5d67f194 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -65,12 +65,12 @@ static void legic_xor_with_crc(uint8_t *data, uint16_t cardsize, uint8_t crc) { } static bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, const uint8_t uid[4]) { - // Decoded segmented payload starts at byte 22. + // Segment headers are parsed from offset 22 everywhere else in LEGIC decoding. size_t start = 22; bool found_segment = false; while (start + 5 <= bytes_read) { - uint16_t seg_len = (((uint16_t)data[start + 1] & 0x07) << 8) | data[start]; + uint16_t seg_len = (((uint16_t)data[start + 1] & 0x0F) << 8) | data[start]; if (seg_len < 5 || start + seg_len > bytes_read) { break; } @@ -96,53 +96,14 @@ static bool legic_clone_update_segment_crcs(uint8_t *data, size_t bytes_read, co return true; } -static int legic_write_bytes_to_tag(uint16_t offset, const uint8_t *data, size_t dlen) { - uint32_t IV = 0x55; - legic_chk_iv(&IV); - - legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + dlen); - if (payload == NULL) { - PrintAndLogEx(WARNING, "Failed to allocate memory"); - return PM3_EMALLOC; - } - - payload->offset = (offset & 0xFFFF); - payload->iv = (IV & 0x7F); - payload->len = dlen; - memcpy(payload->data, data, dlen); - - PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_LEGIC_WRITER, (uint8_t *)payload, sizeof(legic_packet_t) + dlen); - free(payload); - - uint8_t timeout = 0; - while (WaitForResponseTimeout(CMD_HF_LEGIC_WRITER, &resp, 2000) == false) { - ++timeout; - PrintAndLogEx(NORMAL, "." NOLF); - if (timeout > 10) { - PrintAndLogEx(WARNING, "\ncommand execution time out"); - return PM3_ETIMEOUT; - } - } - PrintAndLogEx(NORMAL, ""); - - if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Failed writing tag"); - return PM3_ERFTRANS; - } - - return PM3_SUCCESS; -} - -static int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read) { - PrintAndLogEx(SUCCESS, "Restoring to card"); +static int legic_write_bytes_to_tag(uint16_t offset, uint8_t iv, const uint8_t *data, size_t bytes_read, const char *verb) { + PrintAndLogEx(SUCCESS, "%s", verb); // fast push mode g_conn.block_after_ACK = true; PacketResponseNG resp; - for (size_t i = 7; i < bytes_read; i += LEGIC_PACKET_SIZE) { + for (size_t i = offset; i < bytes_read; i += LEGIC_PACKET_SIZE) { size_t len = MIN((bytes_read - i), LEGIC_PACKET_SIZE); if (len == bytes_read - i) { g_conn.block_after_ACK = false; @@ -155,9 +116,9 @@ static int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read) { return PM3_EMALLOC; } payload->offset = i; - payload->iv = 0x55; + payload->iv = iv; payload->len = len; - memcpy(payload->data, dump + i, len); + memcpy(payload->data, data + i, len); clearCommandBuffer(); SendCommandNG(CMD_HF_LEGIC_WRITER, (uint8_t *)payload, sizeof(legic_packet_t) + len); @@ -187,6 +148,10 @@ static int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read) { return PM3_SUCCESS; } +static int legic_write_dump_to_tag(uint8_t *dump, size_t bytes_read) { + return legic_write_bytes_to_tag(7, 0x55, dump, bytes_read, "Restoring to card"); +} + static int CmdLegicMigrate(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic migrate", @@ -283,7 +248,9 @@ static int CmdLegicMigrate(const char *Cmd) { if (dcf_len > 0) { PrintAndLogEx(SUCCESS, "Applying explicit DCF update"); - int dcf_res = legic_write_bytes_to_tag(5, dcf, sizeof(dcf)); + uint8_t dcf_dump[7] = {0}; + memcpy(dcf_dump + 5, dcf, sizeof(dcf)); + int dcf_res = legic_write_bytes_to_tag(5, 0x55, dcf_dump, sizeof(dcf_dump), "Applying explicit DCF update"); if (dcf_res != PM3_SUCCESS) { free(dump); return dcf_res; @@ -850,12 +817,14 @@ static int CmdLegicWrbl(const char *Cmd) { CLIParserInit(&ctx, "hf legic wrbl", "Write data to a LEGIC Prime tag. It autodetects tagsize to ensure proper write", "hf legic wrbl -o 0 -d 11223344 -> Write 0x11223344 starting from offset 0)\n" - "hf legic wrbl -o 10 -d DEADBEEF -> Write 0xdeadbeef starting from offset 10"); + "hf legic wrbl -o 10 -d DEADBEEF -> Write 0xdeadbeef starting from offset 10\n" + "hf legic wrbl -o 35 --repeat 00 -> Repeat 0x00 from offset 35 to end"); void *argtable[] = { arg_param_begin, arg_int1("o", "offset", "", "offset in data array to start writing"), - arg_str1("d", "data", "", "data to write"), + arg_str0("d", "data", "", "data to write"), + arg_str0("r", "repeat", "", "repeat byte to write from offset to end of card"), arg_lit0(NULL, "danger", "Auto-confirm dangerous operations"), arg_param_end }; @@ -865,12 +834,39 @@ static int CmdLegicWrbl(const char *Cmd) { int dlen = 0; uint8_t data[LEGIC_PRIME_MIM1024] = {0}; - CLIGetHexWithReturn(ctx, 2, data, &dlen); + bool has_data = false; + if (arg_get_str(ctx, 2)->count > 0) { + CLIGetHexWithReturn(ctx, 2, data, &dlen); + has_data = (dlen > 0); + } - bool autoconfirm = arg_get_lit(ctx, 3); + int fill_len = 0; + uint8_t fill_buf[1] = {0}; + bool has_fill = false; + if (arg_get_str(ctx, 3)->count > 0) { + CLIParamHexToBuf(arg_get_str(ctx, 3), fill_buf, sizeof(fill_buf), &fill_len); + has_fill = (fill_len == 1); + if (!has_fill) { + PrintAndLogEx(WARNING, "Repeat byte must be exactly one byte"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + bool autoconfirm = arg_get_lit(ctx, 4); CLIParserFree(ctx); + if (has_data && has_fill) { + PrintAndLogEx(WARNING, "Use either --data or --repeat, not both"); + return PM3_EINVARG; + } + + if (!has_data && !has_fill) { + PrintAndLogEx(WARNING, "Either --data or --repeat is required"); + return PM3_EINVARG; + } + // OUT-OF-BOUNDS checks // UID 4+1 bytes can't be written to. if (offset < 5) { @@ -910,40 +906,21 @@ static int CmdLegicWrbl(const char *Cmd) { uint32_t IV = 0x55; legic_chk_iv(&IV); - PrintAndLogEx(SUCCESS, "Writing to tag to offset %i", offset); - - legic_packet_t *payload = calloc(1, sizeof(legic_packet_t) + dlen); - if (payload == NULL) { - PrintAndLogEx(WARNING, "Failed to allocate memory"); - return PM3_EMALLOC; - } - payload->offset = (offset & 0xFFFF); - payload->iv = (IV & 0x7F); - payload->len = dlen; - memcpy(payload->data, data, dlen); - - PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_LEGIC_WRITER, (uint8_t *)payload, sizeof(legic_packet_t) + dlen); - free(payload); - - uint8_t timeout = 0; - while (WaitForResponseTimeout(CMD_HF_LEGIC_WRITER, &resp, 2000) == false) { - ++timeout; - PrintAndLogEx(NORMAL, "." NOLF); - if (timeout > 10) { - PrintAndLogEx(WARNING, "\ncommand execution time out"); - return PM3_ETIMEOUT; + if (has_fill) { + if (offset >= card.cardsize) { + PrintAndLogEx(WARNING, "Out-of-bounds, Offset = %d, Cardsize = %d", offset, card.cardsize); + return PM3_EOUTOFBOUND; } - } - PrintAndLogEx(NORMAL, ""); - if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Failed writing tag"); - return PM3_ERFTRANS; + dlen = card.cardsize - offset; + memset(data + offset, fill_buf[0], dlen); + PrintAndLogEx(SUCCESS, "Filling from offset %d to end with %02X (%d bytes)", offset, fill_buf[0], dlen); + } else { + memmove(data + offset, data, dlen); } - return PM3_SUCCESS; + PrintAndLogEx(SUCCESS, "Writing to tag to offset %i", offset); + return legic_write_bytes_to_tag((uint16_t)offset, (uint8_t)(IV & 0x7F), data, (size_t)(offset + dlen), "Writing to card"); } static int CmdLegicCalcCrc(const char *Cmd) {