text & style

This commit is contained in:
iceman1001 2025-06-08 10:02:00 +02:00
parent 911d4f9df2
commit 2d610b8dc0
11 changed files with 124 additions and 99 deletions

View file

@ -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 the `PM3ULTIMATE` platform in the build / docs. *untested* (@iceman1001)
- Added fpga compilation for PM3ULTIMATE device (@n-hutton)
- Updated the ATR list (@iceman1001)
- Fixed fpga binary images to use fixed seed 2 (@n-hutton)
- Changed `hf iclass sim -t 7` - implemented simulation that glitches key block responses (@antiklesys)

View file

@ -14,6 +14,9 @@ PLATFORM=PM3RDV4
#PLATFORM=PM3ICOPYX
#PLATFORM_EXTRAS=FLASH
# For PM3 Ultimate:
# uncomment the line below
#PLATFORM=PM3ULTIMATE
# If you want more than one PLATFORM_EXTRAS option, separate them by spaces:
#PLATFORM_EXTRAS=BTADDON

View file

@ -667,7 +667,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) {
if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) {
if(glitch_key_read){
if (glitch_key_read) {
goto send;
}
@ -792,7 +792,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) {
cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd);
if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) {
memcpy(emulator + (current_page * page_size) + (8 * 3), diversified_kd, 8);
if(simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY){
if (simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) {
glitch_key_read = true;
goto send;
}

View file

@ -2250,7 +2250,8 @@ write_dump:
return PM3_SUCCESS;
}
static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool replay, bool verbose, bool use_secure_pagemode, bool shallow_mod) {
static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata, uint8_t *KEY, bool use_credit_key,
bool elite, bool rawkey, bool replay, bool verbose, bool use_secure_pagemode, bool shallow_mod) {
iclass_writeblock_req_t payload = {
.req.use_raw = rawkey,
@ -5067,56 +5068,49 @@ static int CmdHFiClassLookUp(const char *Cmd) {
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
bool use_vb6kdf = arg_get_lit(ctx, 7);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
bool use_elite = arg_get_lit(ctx, 5);
bool use_raw = arg_get_lit(ctx, 6);
if (use_vb6kdf) {
use_elite = true;
} else {
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
}
int csn_len = 0;
uint8_t csn[8] = {0};
CLIGetHexWithReturn(ctx, 2, csn, &csn_len);
if (csn_len > 0) {
if (csn_len != 8) {
PrintAndLogEx(ERR, "CSN is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
int epurse_len = 0;
uint8_t epurse[8] = {0};
CLIGetHexWithReturn(ctx, 3, epurse, &epurse_len);
if (epurse_len > 0) {
if (epurse_len != 8) {
PrintAndLogEx(ERR, "ePurse is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
}
int macs_len = 0;
uint8_t macs[8] = {0};
CLIGetHexWithReturn(ctx, 4, macs, &macs_len);
if (macs_len > 0) {
if (macs_len != 8) {
PrintAndLogEx(ERR, "MAC is incorrect length");
CLIParserFree(ctx);
return PM3_EINVARG;
}
bool use_elite = arg_get_lit(ctx, 5);
bool use_raw = arg_get_lit(ctx, 6);
bool use_vb6kdf = arg_get_lit(ctx, 7);
if (use_vb6kdf == false) {
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
}
CLIParserFree(ctx);
// santity checks
if (csn_len > 0 && csn_len != 8) {
PrintAndLogEx(ERR, "CSN is incorrect length");
return PM3_EINVARG;
}
if (epurse_len > 0 && epurse_len != 8) {
PrintAndLogEx(ERR, "ePurse is incorrect length");
return PM3_EINVARG;
}
if (macs_len > 0 && macs_len != 8) {
PrintAndLogEx(ERR, "MAC is incorrect length");
return PM3_EINVARG;
}
uint8_t CCNR[12];
uint8_t MAC_TAG[4] = { 0, 0, 0, 0 };
@ -5161,7 +5155,8 @@ static int CmdHFiClassLookUp(const char *Cmd) {
// Iclass_prekey_t
iclass_prekey_t *prekey = calloc(keycount, sizeof(iclass_prekey_t));
if (!prekey) {
if (prekey == NULL) {
PrintAndLogEx(WARNING, "Failed to allocate memory");
free(keyBlock);
return PM3_EMALLOC;
}
@ -5182,13 +5177,14 @@ static int CmdHFiClassLookUp(const char *Cmd) {
// Sort mac list
qsort(prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32);
PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT");
PrintAndLogEx(SUCCESS, "Searching for %s key...", _YELLOW_("DEBIT"));
iclass_prekey_t *item;
iclass_prekey_t lookup;
memcpy(lookup.mac, MAC_TAG, 4);
// Binsearch
item = (iclass_prekey_t *) bsearch(&lookup, prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32);
if (item != NULL) {
PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex(item->key, 8));
add_key(item->key);

View file

@ -48,21 +48,15 @@
#include "mifare/mifarehost.h"
#include "crypto/originality.h"
// Defines for Saflok parsing
#define SAFLOK_YEAR_OFFSET 1980
#define SAFLOK_BASIC_ACCESS_BYTE_NUM 17
#define SAFLOK_KEY_LENGTH 6
#define SAFLOK_UID_LENGTH 4 // Matches Mifare 4-byte UID
#define SAFLOK_MAGIC_TABLE_SIZE 192
#define SAFLOK_CHECK_SECTOR 1
typedef struct {
uint64_t a;
uint64_t b;
} MfClassicKeyPair;
// Structure for Saflok key levels
typedef struct {
uint8_t level_num;
@ -114,8 +108,8 @@ static const uint8_t saflok_c_aDecode[256] = {
// Function to decrypt Saflok card data
static void DecryptSaflokCardData(
const uint8_t strCard[SAFLOK_BASIC_ACCESS_BYTE_NUM],
// int length, // length is always SAFLOK_BASIC_ACCESS_BYTE_NUM
uint8_t decryptedCard[SAFLOK_BASIC_ACCESS_BYTE_NUM]) {
uint8_t decryptedCard[SAFLOK_BASIC_ACCESS_BYTE_NUM]
) {
int i;
int num;
int num2;
@ -163,8 +157,8 @@ static uint8_t CalculateCheckSum(uint8_t data[SAFLOK_BASIC_ACCESS_BYTE_NUM]) {
static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t *sector1_info) {
(void)sector1_info; // Not directly used for payload parsing currently
if (!sector0_info) {
PrintAndLogEx(WARNING, "Saflok: Sector 0 information not available for parsing.");
if (sector0_info == NULL) {
PrintAndLogEx(WARNING, "Saflok: Sector 0 information not available for parsing");
return;
}
@ -177,16 +171,16 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t
num_to_bytes(sector0_info->Key[MF_KEY_A], MIFARE_KEY_SIZE, key_bytes_for_s0);
key_type_for_s0 = MF_KEY_A; // MF_KEY_A is typically #define'd as 0x60
s0_key_found = true;
PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key A for reading blocks.");
PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key A for reading blocks");
} else if (sector0_info->foundKey[MF_KEY_B]) { // Fallback to Key B for Sector 0
num_to_bytes(sector0_info->Key[MF_KEY_B], MIFARE_KEY_SIZE, key_bytes_for_s0);
key_type_for_s0 = MF_KEY_B; // MF_KEY_B is typically #define'd as 0x61
s0_key_found = true;
PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key B for reading blocks.");
PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key B for reading blocks");
}
if (!s0_key_found) {
PrintAndLogEx(WARNING, "Saflok: No known keys for Sector 0. Cannot read blocks 1 & 2 for parsing.");
if (s0_key_found == false) {
PrintAndLogEx(WARNING, "Saflok: No known keys for Sector 0. Cannot read blocks 1 & 2 for parsing");
return;
}
@ -195,23 +189,23 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t
// Read absolute block 1 (data block within sector 0)
if (mf_read_block(1, key_type_for_s0, key_bytes_for_s0, block1_content) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 1 using Sector 0 %s key.", (key_type_for_s0 == MF_KEY_A) ? "A" : "B");
PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 1 using Sector 0 %s key", (key_type_for_s0 == MF_KEY_A) ? "A" : "B");
return;
}
PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 1.");
PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 1");
// Read absolute block 2 (data block within sector 0)
if (mf_read_block(2, key_type_for_s0, key_bytes_for_s0, block2_content) != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 2 using Sector 0 %s key.", (key_type_for_s0 == MF_KEY_A) ? "A" : "B");
PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 2 using Sector 0 %s key", (key_type_for_s0 == MF_KEY_A) ? "A" : "B");
return;
}
PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 2.");
PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 2");
uint8_t basicAccess[SAFLOK_BASIC_ACCESS_BYTE_NUM];
uint8_t decodedBA[SAFLOK_BASIC_ACCESS_BYTE_NUM];
memcpy(basicAccess, block1_content, 16); // 16 bytes from Block 1
memcpy(basicAccess + 16, block2_content, 1); // 1 byte from Block 2
memcpy(basicAccess, block1_content, MFBLOCK_SIZE); // 16 bytes from Block 1
memcpy(basicAccess + MFBLOCK_SIZE, block2_content, 1); // 1 byte from Block 2
DecryptSaflokCardData(basicAccess, decodedBA);
@ -298,18 +292,25 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t
// Handle day rollover for expiration
static const uint8_t days_in_month_lookup[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 1-indexed month
if (expire_month > 0 && expire_month <= 12) {
while (true) {
uint8_t max_days = days_in_month_lookup[expire_month];
if (expire_month == 2 && (expire_year % 4 == 0 && (expire_year % 100 != 0 || expire_year % 400 == 0))) {
if (expire_month == 2 &&
(expire_year % 4 == 0 &&
(expire_year % 100 != 0 || expire_year % 400 == 0))) {
max_days = 29; // Leap year
}
if (expire_day <= max_days) {
break;
}
if (max_days == 0) { // Should not happen with valid month
PrintAndLogEx(WARNING, "Saflok: Invalid day/month for expiration rollover calculation.");
PrintAndLogEx(WARNING, "Saflok: Invalid day/month for expiration rollover calculation");
break;
}
expire_day -= max_days;
expire_month++;
if (expire_month > 12) {
@ -317,8 +318,9 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t
expire_year++;
}
}
} else if (expire_month != 0) { // Allow 0 if it signifies no expiration or error
PrintAndLogEx(WARNING, "Saflok: Invalid expiration month (%u) before day rollover.", expire_month);
PrintAndLogEx(WARNING, "Saflok: Invalid expiration month (%u) before day rollover", expire_month);
}
uint8_t checksum = decodedBA[16];
@ -326,19 +328,19 @@ static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t
bool checksum_valid = (checksum_calculated == checksum);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Saflok Details"));
PrintAndLogEx(SUCCESS, "Key Level: %u (%s)", saflok_key_levels[key_level].level_num, saflok_key_levels[key_level].level_name);
PrintAndLogEx(SUCCESS, "LED Warning: %s", led_warning ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Key ID: %u (0x%02X)", key_id, key_id);
PrintAndLogEx(SUCCESS, "Key Record: %u (0x%04X)", key_record, key_record);
PrintAndLogEx(SUCCESS, "Opening Key: %s", opening_key ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Sequence Number & Combination: %u (0x%02X)", sequence_combination_number, sequence_combination_number);
PrintAndLogEx(SUCCESS, "Override Deadbolt: %s", override_deadbolt ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Restricted Weekdays: %s", restricted_weekday_string);
PrintAndLogEx(SUCCESS, "Property ID: %u (0x%04X)", property_id, property_id);
PrintAndLogEx(SUCCESS, "Creation Date: %04u-%02u-%02u %02u:%02u", creation_year, creation_month, creation_day, creation_hour, creation_minute);
PrintAndLogEx(SUCCESS, "Expiration Date: %04u-%02u-%02u %02u:%02u", expire_year, expire_month, expire_day, expiry_hour, expiry_minute);
PrintAndLogEx(SUCCESS, "Checksum Valid: %s", checksum_valid ? "Yes" : "No");
PrintAndLogEx(INFO, "--- " _CYAN_("SAFLOK details"));
PrintAndLogEx(SUCCESS, "Key Level............. %u (%s)", saflok_key_levels[key_level].level_num, saflok_key_levels[key_level].level_name);
PrintAndLogEx(SUCCESS, "LED Warning........... %s", led_warning ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Key ID................ %u (0x%02X)", key_id, key_id);
PrintAndLogEx(SUCCESS, "Key Record............ %u (0x%04X)", key_record, key_record);
PrintAndLogEx(SUCCESS, "Opening Key........... %s", opening_key ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Sequence & Combination: %u (0x%02X)", sequence_combination_number, sequence_combination_number);
PrintAndLogEx(SUCCESS, "Override Deadbolt..... %s", override_deadbolt ? "Yes" : "No");
PrintAndLogEx(SUCCESS, "Restricted Weekdays... %s", restricted_weekday_string);
PrintAndLogEx(SUCCESS, "Property ID........... %u (0x%04X)", property_id, property_id);
PrintAndLogEx(SUCCESS, "Creation Date......... %04u-%02u-%02u %02u:%02u", creation_year, creation_month, creation_day, creation_hour, creation_minute);
PrintAndLogEx(SUCCESS, "Expiration Date....... %04u-%02u-%02u %02u:%02u", expire_year, expire_month, expire_day, expiry_hour, expiry_minute);
PrintAndLogEx(SUCCESS, "Checksum Valid........ ( %s )", checksum_valid ? _GREEN_("ok") : _RED_("fail"));
}
@ -434,20 +436,19 @@ static int initSectorTable(sector_t **src, size_t items) {
static void decode_print_st(uint16_t blockno, uint8_t *data) {
if (mfIsSectorTrailer(blockno)) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Sector trailer decoder") " --------------------------");
PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6));
PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3));
PrintAndLogEx(INFO, "user / gpb... " _GREEN_("%02x"), data[9]);
PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6));
PrintAndLogEx(INFO, "------------------------ " _CYAN_("Sector trailer decoder") " ------------------------");
PrintAndLogEx(INFO, " Key A........ " _BRIGHT_GREEN_("%s"), sprint_hex_inrow(data, 6));
PrintAndLogEx(INFO, " ACR.......... " _MAGENTA_("%s"), sprint_hex_inrow(data + 6, 3));
PrintAndLogEx(INFO, " User / gpb... %02x", data[9]);
PrintAndLogEx(INFO, " Key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6));
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, " # | access rights");
PrintAndLogEx(INFO, "----+-----------------------------------------------------------------------");
PrintAndLogEx(INFO, "----+-------------------------------------------------------------------");
if (mfValidateAccessConditions(&data[6]) == false) {
PrintAndLogEx(WARNING, _RED_("Invalid Access Conditions"));
PrintAndLogEx(WARNING, _RED_("Invalid access conditions"));
}
int bln = mfFirstBlockOfSector(mfSectorNum(blockno));
int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1;
for (int i = 0; i < 4; i++) {
@ -458,13 +459,13 @@ static void decode_print_st(uint16_t blockno, uint8_t *data) {
uint8_t cond = mf_get_accesscondition(i, &data[6]);
if (cond == 0 || cond == 1 || cond == 2) {
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "OBS! Key B is readable, it SHALL NOT be able to authenticate on original MFC");
PrintAndLogEx(INFO, "OBS!");
PrintAndLogEx(INFO, "Key B is readable, it SHALL NOT be able to authenticate on original MFC");
}
}
}
PrintAndLogEx(INFO, "----------------------------------------------------------------------------");
PrintAndLogEx(INFO, "------------------------------------------------------------------------");
PrintAndLogEx(NORMAL, "");
}
}
@ -541,7 +542,7 @@ void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose) {
char ascii[24] = {0};
ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1);
PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"),
PrintAndLogEx(INFO, "%3d | " _BRIGHT_GREEN_("%s") _MAGENTA_("%s") "%02X " _GREEN_("%s") "| " _YELLOW_("%s"),
blockno,
keya,
acl,
@ -1428,7 +1429,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) {
}
int keylen = 0;
uint8_t key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
uint8_t key[MIFARE_KEY_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
CLIGetHexWithReturn(ctx, 4, key, &keylen);
int s = arg_get_int_def(ctx, 5, 0);

View file

@ -29,15 +29,17 @@ define KNOWN_PLATFORM_DEFINITIONS
Known definitions:
+============================================+
+==================================================+
| PLATFORM | DESCRIPTION |
+============================================+
+==================================================+
| PM3RDV4 (def) | Proxmark3 RDV4 |
+--------------------------------------------+
+--------------------------------------------------+
| PM3GENERIC | Proxmark3 generic target |
+--------------------------------------------+
+--------------------------------------------------+
| PM3ICOPYX | iCopy-X with XC3S100E |
+--------------------------------------------+
+--------------------------------------------------+
| PM3ULTIMATE | Proxmark3 Ultimate with XC2S50 |
+--------------------------------------------------+
+============================================+
| PLATFORM_EXTRAS | DESCRIPTION |
@ -153,7 +155,21 @@ else ifeq ($(PLATFORM),PM3ICOPYX)
PLATFORM_DEFS = -DWITH_FLASH -DICOPYX -DXC3
PLTNAME = iCopy-X with XC3S100E
PLATFORM_FPGA = xc3s100e
else ifeq ($(PLATFORM),PM3ULTIMATE)
# FPGA bitstream files, the order doesn't matter anymore - only hf has a bitstream
FPGA_BITSTREAMS = fpga_pm3_ult_hf.bit
ifneq ($(SKIP_LF),1)
FPGA_BITSTREAMS += fpga_pm3_ult_lf.bit
endif
ifneq ($(SKIP_FELICA),1)
FPGA_BITSTREAMS += fpga_pm3_ult_felica.bit
endif
ifneq ($(SKIP_ISO15693),1)
FPGA_BITSTREAMS += fpga_pm3_ult_hf_15.bit
endif
PLATFORM_DEFS = -DWITH_FLASH -DXC2S50
PLTNAME = Proxmark3 Ultimate with XC2S50
PLATFORM_FPGA = xc2s50
else
$(error Invalid or empty PLATFORM: $(PLATFORM). $(KNOWN_DEFINITIONS))
endif

View file

@ -25,6 +25,9 @@
#if defined XC3
#define FPGA_TYPE "3s100evq100"
#define FPGA_CONFIG_SIZE 72864L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE
#elif defined XC2S50
#define FPGA_TYPE "2s50vq144"
#define FPGA_CONFIG_SIZE 69984L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE
#else
#define FPGA_TYPE "2s30vq100"
#define FPGA_CONFIG_SIZE 42336L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE

View file

@ -3695,7 +3695,8 @@
"hf iclass sim -t 2 -> execute loclass attack online part",
"hf iclass sim -t 3 -> simulate full iCLASS 2k tag",
"hf iclass sim -t 4 -> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key",
"hf iclass sim -t 6 -> simulate full iCLASS 2k tag that doesn't respond to r/w requests to the last SIO block"
"hf iclass sim -t 6 -> simulate full iCLASS 2k tag that doesn't respond to r/w requests to the last SIO block",
"hf iclass sim -t 7 -> simulate full iCLASS 2k tag that doesn't XOR or respond to r/w requests on block 3"
],
"offline": false,
"options": [
@ -13375,6 +13376,6 @@
"metadata": {
"commands_extracted": 768,
"extracted_by": "PM3Help2JSON v1.00",
"extracted_on": "2025-06-07T09:11:06"
"extracted_on": "2025-06-08T07:56:09"
}
}

View file

@ -112,7 +112,7 @@ At the moment both are maintained because they don't perfectly overlap yet.
| Feature | Makefile | Remarks |
|-----|---|---|
| Platform choice | `PLATFORM=` | values: `PM3RDV4`, `PM3GENERIC`, `PM3ICOPYX` |
| Platform choice | `PLATFORM=` | values: `PM3RDV4`, `PM3GENERIC`, `PM3ICOPYX`, `PM3ULTIMATE` |
| Platform size | `PLATFORM_SIZE=` | values: `256`, `512` |
| Platform extras | `PLATFORM_EXTRAS=` | values: `BTADDON`, `FPC_USART_DEV` |
| Skip LF/HF techs in the firmware | `SKIP_`*`=1` | see `common_arm/Makefile.hal` for a list |

View file

@ -61,11 +61,12 @@ For an up-to-date exhaustive list of options, you can run `make PLATFORM=`.
Here are the supported values you can assign to `PLATFORM` in `Makefile.platform`:
| PLATFORM | DESCRIPTION |
|-----------------|--------------------------|
| PM3RDV4 (def) | Proxmark3 RDV4 |
| PM3GENERIC | Proxmark3 generic target |
| PM3ICOPYX | iCopy-X with XC3S100E |
| PLATFORM | DESCRIPTION |
|-----------------|-------------------------------|
| PM3RDV4 (def) | Proxmark3 RDV4 |
| PM3GENERIC | Proxmark3 generic target |
| PM3ICOPYX | iCopy-X with XC3S100E |
| PM3ULTIMATE | Proxmar3 Ultimate with XC2S50 |
By default `PLATFORM=PM3RDV4`.

View file

@ -28,6 +28,8 @@
//`define PM3GENERIC
// iCopy-X with XC3S100E
//`define PM3ICOPYX
// Proxmark3 Ultimate with XC2S50
//`define PM3ULTIMATE
// Pass desired defines to compiler to enable required modules
// WITH_LF enables Low Frequency mode when defined else HF is enabled