Merge branch 'master' into master

Signed-off-by: Jarek Barwinski <116510448+jareckib@users.noreply.github.com>
This commit is contained in:
Jarek Barwinski 2025-03-17 23:00:28 +00:00 committed by GitHub
commit 06ff7934f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
124 changed files with 3736 additions and 2282 deletions

View file

@ -45,6 +45,13 @@ jobs:
- name: Install dependencies
run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.4-dev liblua5.4-0 lua5.4 sed libssl-dev
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install setuptools
python3 -m pip install ansicolors sslcrypto
if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi
- name: Checkout repository
uses: actions/checkout@v4

View file

@ -3,6 +3,23 @@ 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]
- Improved `pcf7931` generic readability of the code. Unified datatypes and added documentation/explainations (@tinooo)
- Improved `lf pcf7931` read code - fixed some checks for more stability (@tinooo)
- Changed `trace list -t seos` - improved annotation (@iceman1001)
- Added `make commands` to regenerate commands documentation files and autocompletion data independently of `make style` (@doegox)
- Added texecom identification, thanks @en4rab ! (@iceman1001)
- Added `hf 15 slixprotectpage` command
- Fixed `hf mf gload` - missing parameter (@iceman1001)
- Changed `hf mf gload` - now handles 1k ev1 sized dumps (@iceman1001)
- Changed wiegand format unpack functions to clear struct later (@iceman1001)
- Changed `wiegand decode` - now accepts new padding format (@iceman1001)
- Changed `mem spiffs tree` - ID is now shown in decimal (@iceman1001)
- Added sample wiegand format 56bit (@iceman1001)
- Changed Wiegand formats to include number of bits (@iceman1001)
- Fix compilation warning in hitagS (@iceman1001)
- Added new wiegand format H800002 (@jmichelp)
- Changed `Makefile.platform.sample` file - now have clear instructions for generating images for other proxmark3 hardware (@iceman1001)
- Changed `doc/magic_cards_notes.md` - now contains documentation for iKey LLC's MF4 tag (@team-orangeBlue)
- Changed `hf mf cload` - now accepts MFC Ev1 sized dumps (@iceman1001)
- Changed `hf mfu info` - now properly identify ULEv1 AES 50pF (@iceman1001)
- Changed `hf mf info` - now differentiates between full USCUID and cut down ZUID chips (@nvx)

View file

@ -204,6 +204,7 @@ help:
@echo "+ fpga_compress - Make tools/fpga_compress"
@echo
@echo "+ style - Apply some automated source code formatting rules"
@echo "+ commands - Regenerate commands documentation files and autocompletion data
@echo "+ check - Run offline tests. Set CHECKARGS to pass arguments to the test script"
@echo "+ .../check - Run offline tests against specific target. See above."
@echo "+ miscchecks - Detect various encoding issues in source code"
@ -303,7 +304,7 @@ endif
# easy printing of MAKE VARIABLES
print-%: ; @echo $* = $($*)
style:
style: commands
# Make sure astyle is installed
@command -v astyle >/dev/null || ( echo "Please install 'astyle' package first" ; exit 1 )
# Remove spaces & tabs at EOL, add LF at EOF if needed on *.c, *.h, *.cpp. *.lua, *.py, *.pl, Makefile, *.v, pm3
@ -317,13 +318,14 @@ style:
--keep-one-line-blocks --max-continuation-indent=60 \
--style=google --pad-oper --unpad-paren --pad-header \
--align-pointer=name {} \;
commands:
# Update commands.md
[ -x client/proxmark3 ] && client/proxmark3 -m | tr -d '\r' > doc/commands.md
# Make sure python3 is installed
@command -v python3 >/dev/null || ( echo "Please install 'python3' package first" ; exit 1 )
# Update commands.json, patch port in case it was run under Windows
[ -x client/proxmark3 ] && client/proxmark3 --fulltext | sed 's#com[0-9]#/dev/ttyACM0#'|python3 client/pyscripts/pm3_help2json.py - - | tr -d '\r' > doc/commands.json
# Update the readline autocomplete autogenerated code
[ -x client/proxmark3 ] && client/proxmark3 --fulltext | python3 client/pyscripts/pm3_help2list.py - - | tr -d '\r' > client/src/pm3line_vocabulary.h

View file

@ -1,8 +1,20 @@
# If you want to use it, copy this file as Makefile.platform and adjust it to your needs
# Run 'make PLATFORM=' to get an exhaustive list of possible parameters for this file.
# By default PM3 RDV4 image is generated.
# Comment the line below and uncomment further down according to which device you have
PLATFORM=PM3RDV4
# For PM3 Easy:
# uncomment the line below
#PLATFORM=PM3GENERIC
# For ICOPY-X and PM3 Max:
# uncomment the two lines below
#PLATFORM=PM3ICOPY3
#PLATFORM_EXTRAS=FLASH
# If you want more than one PLATFORM_EXTRAS option, separate them by spaces:
#PLATFORM_EXTRAS=BTADDON
#PLATFORM_EXTRAS=FLASH
@ -10,6 +22,7 @@ PLATFORM=PM3RDV4
#PLATFORM_EXTRAS=BTADDON FLASH
#STANDALONE=LF_SAMYRUN
# Uncomment the line below to set the correct LED order on board Proxmark3 Easy
# Only available with PLATFORM=PM3GENERIC
#LED_ORDER=PM3EASY

View file

@ -321,9 +321,10 @@ bool RAMFUNC LogTraceBits(const uint8_t *btBytes, uint16_t bitLen, uint32_t time
// Emulator memory
int emlSet(const uint8_t *data, uint32_t offset, uint32_t length) {
uint8_t *mem = BigBuf_get_EM_addr();
if (!mem) {
if (mem == NULL) {
return PM3_EMALLOC;
}
if (offset + length <= CARD_MEMORY_SIZE) {
memcpy(mem + offset, data, length);
return PM3_SUCCESS;
@ -335,9 +336,10 @@ int emlSet(const uint8_t *data, uint32_t offset, uint32_t length) {
int emlGet(uint8_t *out, uint32_t offset, uint32_t length) {
uint8_t *mem = BigBuf_get_EM_addr();
if (!mem) {
if (mem == NULL) {
return PM3_EMALLOC;
}
if (offset + length <= CARD_MEMORY_SIZE) {
memcpy(out, mem + offset, length);
return PM3_SUCCESS;

View file

@ -311,7 +311,7 @@ void WriteTagToFlash(uint32_t uid, size_t size) {
uint32_t len = size;
uint8_t data[(size * (16 * 64)) / 1024];
emlGetMem(data, 0, (size * 64) / 1024);
emlGetMem_xt(data, 0, (size * 64) / 1024, MIFARE_BLOCK_SIZE);
char dest[SPIFFS_OBJ_NAME_LEN];
uint8_t buid[4];
@ -646,7 +646,7 @@ failtag:
emlClearMem();
uint8_t mblock[16];
for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) {
emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1);
emlGetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, MIFARE_BLOCK_SIZE);
for (uint8_t t = 0; t < 2; t++) {
memcpy(mblock + t * 10, foundKey[t][sectorNo], 6);
}
@ -807,7 +807,7 @@ int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) {
emlSetMem_xt(dataoutbuf, FirstBlockOfSector(s) + blockNo, 1, 16);
} else {
// sector trailer, keep the keys, set only the AC
emlGetMem(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1);
emlGetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, MIFARE_BLOCK_SIZE);
memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4);
emlSetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, 16);
}
@ -878,7 +878,7 @@ void saMifareMakeTag(void) {
int flags = 0;
for (int blockNum = 0; blockNum < 16 * 4; blockNum++) {
uint8_t mblock[16];
emlGetMem(mblock, blockNum, 1);
emlGetMem_xt(mblock, blockNum, 1, MIFARE_BLOCK_SIZE);
// switch on field and send magic sequence
if (blockNum == 0)
flags = 0x08 + 0x02;

View file

@ -500,7 +500,7 @@ void RunMod(void) {
uint8_t mblock[MIFARE_BLOCK_SIZE];
for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; ++sectorNo) {
if (validKey[0][sectorNo] || validKey[1][sectorNo]) {
emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1);
emlGetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, MIFARE_BLOCK_SIZE);
for (uint8_t keyType = 0; keyType < 2; ++keyType) {
if (validKey[keyType][sectorNo]) {
memcpy(mblock + keyType * 10, foundKey[keyType][sectorNo], 6);

View file

@ -445,7 +445,7 @@ void RunMod(void) {
// received a RATS request
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) {
prevCmd = 0;
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
} else {
if (g_dbglevel == DBG_DEBUG) {

View file

@ -338,7 +338,7 @@ void RunMod() {
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1)
p_response = &responses[RESP_INDEX_SAKC1];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
resp = 1;
} else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension
DbpString(_YELLOW_("!!") " Reader accepted time extension!");

View file

@ -247,7 +247,7 @@ void RunMod(void) {
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2)
p_response = &responses[RESP_INDEX_SAKC2];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
} else if (receivedCmd[0] == ISO14443A_CMD_PPS) {
p_response = &responses[RESP_INDEX_PPS];
} else {
@ -425,7 +425,7 @@ void RunMod(void) {
} else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2)
p_response = &responses[RESP_INDEX_SAKC2];
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
} else if (receivedCmd[0] == ISO14443A_CMD_PPS) {
p_response = &responses[RESP_INDEX_PPS];
} else {

View file

@ -257,7 +257,7 @@ void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low
// Calculate new high and low base value from card number and facility code, without parity
new_low = (fc << 21) | (cardnum << 1);
new_high = 0x28 | ((fc >> 11) & 1); // 0x28 is 101000
new_high = (fc >> 11) & 1;
int n_ones;
uint32_t i;
@ -319,6 +319,7 @@ void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low
new_high = new_high | 0x4;
// Setting new calculated values
add_HID_preamble(0, &new_high, &new_low, 35);
*low = new_low;
*high = new_high;
}

View file

@ -176,8 +176,7 @@ void hid_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t card
newlow |= oddparity32((newlow >> 1) & 0xFFF);
newlow |= (evenparity32((newlow >> 13) & 0xFFF)) << 25;
newhigh |= 0x20; // Bit 37; standard header
newlow |= 1U << 26; // leading 1: start bit
add_HID_preamble(NULL, &newhigh, &newlow, 26);
*low = newlow;
*high = newhigh;

View file

@ -16,8 +16,8 @@
//-----------------------------------------------------------------------------
// LF HID ProxII Brutforce v2 by lnv42 - based on Proxbrute by Brad antoniewicz
//
// Following code is a trivial brute forcer for when you know the facility
// code and want to find valid(s) card number(s). It will try all card
// Following code is a trivial brute forcer (H10301 26-bit) when you know the
// facility code and want to find valid(s) card number(s). It will try all card
// fnumbers rom CARDNUM_START to CARDNUM_END one by one (max. ~65k tries).
// This brute force will be a lot faster than Proxbrute that will try all
// possibles values for LF low, even those with bad checksum (~4g tries).
@ -46,8 +46,7 @@ void RunMod(void) {
StandAloneMode();
Dbprintf(">> LF HID proxII bruteforce v2 a.k.a Prox2Brute Started <<");
FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
const uint32_t high = 0x20; // LF high value is always 0x20 here
uint32_t high = 0;
uint32_t fac = FACILITY_CODE, cardnum = 0;
@ -82,6 +81,7 @@ void RunMod(void) {
uint32_t low = (cardnum << 1) | (fac << 17);
low |= oddparity32((low >> 1) & 0xFFF);
low |= evenparity32((low >> 13) & 0xFFF) << 25;
add_HID_preamble(NULL, &high, &low, 26);
Dbprintf("[=] trying Facility = %08x, Card = %08x, raw = %08x%08x",
fac, cardnum, high, low);

View file

@ -1370,7 +1370,11 @@ static void PacketReceived(PacketCommandNG *packet) {
// involved in dealing with emulator memory. But if it is called later, it might
// destroy the Emulator Memory.
//-----------------------------------------------------------------------------
EmlClearIso15693();
// Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent
// an inconvenient reset in the future by Iso15693InitTag
FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15);
BigBuf_Clear_EM();
reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0);
break;
}
case CMD_HF_ISO15693_EML_SETMEM: {
@ -1402,7 +1406,7 @@ static void PacketReceived(PacketCommandNG *packet) {
return;
}
uint8_t *buf = BigBuf_malloc(payload->length);
uint8_t *buf = BigBuf_calloc(payload->length);
emlGet(buf, payload->offset, payload->length);
LED_B_ON();
reply_ng(CMD_HF_ISO15693_EML_GETMEM, PM3_SUCCESS, buf, payload->length);
@ -1463,6 +1467,17 @@ static void PacketReceived(PacketCommandNG *packet) {
WritePasswordSlixIso15693(payload->old_pwd, payload->new_pwd, payload->pwd_id);
break;
}
case CMD_HF_ISO15693_SLIX_PROTECT_PAGE: {
struct p {
uint8_t read_pwd[4];
uint8_t write_pwd[4];
uint8_t divide_ptr;
uint8_t prot_status;
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
ProtectPageSlixIso15693(payload->read_pwd, payload->write_pwd, payload->divide_ptr, payload->prot_status);
break;
}
case CMD_HF_ISO15693_SLIX_DISABLE_PRIVACY: {
struct p {
uint8_t pwd[4];
@ -1696,20 +1711,21 @@ static void PacketReceived(PacketCommandNG *packet) {
uint8_t tagtype;
uint16_t flags;
uint8_t uid[10];
uint8_t rats[20];
uint8_t ats[20];
uint8_t aid[30];
uint8_t response[100];
uint8_t apdu[100];
int aid_len;
int respond_len;
int apdu_len;
bool enumerate;
uint8_t selectaid_response[100];
uint8_t getdata_response[100];
uint32_t ats_len;
uint32_t aid_len;
uint32_t selectaid_response_len;
uint32_t getdata_response_len;
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
// ## Simulate iso14443a tag - pass tag type, UID, ATS, AID, responses
SimulateIso14443aTagAID(payload->tagtype, payload->flags, payload->uid,
payload->rats, sizeof(payload->rats), payload->aid, payload->response,
payload->apdu, payload->aid_len, payload->respond_len,
payload->apdu_len, payload->enumerate); // ## Simulate iso14443a tag - pass tag type, UID, rats, aid, resp, apdu
payload->ats, payload->ats_len, payload->aid, payload->aid_len,
payload->selectaid_response, payload->selectaid_response_len,
payload->getdata_response, payload->getdata_response_len);
break;
}
case CMD_HF_ISO14443A_ANTIFUZZ: {
@ -1889,36 +1905,65 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_MIFARE_EML_MEMCLR: {
MifareEMemClr();
reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0);
//-----------------------------------------------------------------------------
// Work with emulator memory
//
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not
// involved in dealing with emulator memory. But if it is called later, it might
// destroy the Emulator Memory.
//-----------------------------------------------------------------------------
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
// Not only clears the emulator memory,
// also sets default MIFARE values for sector trailers.
emlClearMem();
reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0);
break;
}
case CMD_HF_MIFARE_EML_MEMSET: {
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
struct p {
uint8_t blockno;
uint16_t blockno;
uint8_t blockcnt;
uint8_t blockwidth;
uint8_t data[];
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
// backwards compat... default bytewidth
if (payload->blockwidth == 0)
payload->blockwidth = 16;
if (payload->blockwidth == 0) {
payload->blockwidth = MIFARE_BLOCK_SIZE;
}
emlSetMem_xt(payload->data, payload->blockno, payload->blockcnt, payload->blockwidth);
break;
}
case CMD_HF_MIFARE_EML_MEMGET: {
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
struct p {
uint8_t blockno;
uint16_t blockno;
uint8_t blockcnt;
uint8_t blockwidth;
} PACKED;
struct p *payload = (struct p *) packet->data.asBytes;
MifareEMemGet(payload->blockno, payload->blockcnt);
//
size_t size = payload->blockno * payload->blockwidth;
if (size > PM3_CMD_DATA_SIZE) {
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0);
return;
}
uint8_t *buf = BigBuf_calloc(size);
emlGetMem_xt(buf, payload->blockno, payload->blockcnt, payload->blockwidth); // data, block num, blocks count (max 4)
LED_B_ON();
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size);
LED_B_OFF();
BigBuf_free_keep_EM();
break;
}
case CMD_HF_MIFARE_EML_LOAD: {

View file

@ -133,7 +133,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, ui
}
void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]) {
if (length % 8) return;
if (length % 8) {
return;
}
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
@ -143,7 +145,9 @@ void aes128_nxp_receive(const void *in, void *out, size_t length, const void *ke
}
void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]) {
if (length % 8) return;
if (length % 8) {
return;
}
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
@ -152,12 +156,15 @@ void aes128_nxp_send(const void *in, void *out, size_t length, const void *key,
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, length, iv, tin, tout);
}
void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key) {
uint8_t data[8];
memcpy(data, value, 8);
for (int n = 0; n < 8; n++) {
void Desfire_des_key_new(const uint8_t *value, desfirekey_t key) {
uint8_t data[8] = {0};
memcpy(data, value, sizeof(data));
for (size_t n = 0; n < sizeof(data); n++) {
data[n] &= 0xFE;
}
Desfire_des_key_new_with_version(data, key);
}
@ -246,22 +253,24 @@ void Desfire_key_set_version(desfirekey_t key, uint8_t version) {
void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfirekey_t authkey, desfirekey_t key) {
uint8_t buffer[24];
uint8_t buffer[24] = {0};
switch (authkey->type) {
case T_DES:
case T_DES: {
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
Desfire_des_key_new_with_version(buffer, key);
break;
case T_3DES:
}
case T_3DES: {
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
memcpy(buffer + 8, rnda + 4, 4);
memcpy(buffer + 12, rndb + 4, 4);
Desfire_3des_key_new_with_version(buffer, key);
break;
case T_3K3DES:
}
case T_3K3DES: {
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
memcpy(buffer + 8, rnda + 6, 4);
@ -270,22 +279,15 @@ void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfire
memcpy(buffer + 20, rndb + 12, 4);
Desfire_3k3des_key_new(buffer, key);
break;
case T_AES:
}
case T_AES: {
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
memcpy(buffer + 8, rnda + 12, 4);
memcpy(buffer + 12, rndb + 12, 4);
Desfire_aes_key_new(buffer, key);
break;
}
}
static size_t key_macing_length(desfirekey_t key);
// iceman, see memxor inside string.c, dest/src swapped..
static void xor(const uint8_t *ivect, uint8_t *data, const size_t len) {
for (size_t i = 0; i < len; i++) {
data[i] ^= ivect[i];
}
}
}
@ -306,7 +308,7 @@ void cmac_generate_subkeys(desfirekey_t key) {
// Used to compute CMAC on complete blocks
memcpy(key->cmac_sk1, l, kbs);
txor = l[0] & 0x80;
txor = (l[0] & 0x80);
lsl(key->cmac_sk1, kbs);
@ -317,7 +319,7 @@ void cmac_generate_subkeys(desfirekey_t key) {
// Used to compute CMAC on the last block if non-complete
memcpy(key->cmac_sk2, key->cmac_sk1, kbs);
txor = key->cmac_sk1[0] & 0x80;
txor = (key->cmac_sk1[0] & 0x80);
lsl(key->cmac_sk2, kbs);
@ -341,15 +343,14 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le
while (len % kbs) {
buffer[len++] = 0x00;
}
xor(key->cmac_sk2, buffer + len - kbs, kbs);
xor(buffer + len - kbs, key->cmac_sk2, kbs);
} else {
xor(key->cmac_sk1, buffer + len - kbs, kbs);
xor(buffer + len - kbs, key->cmac_sk1, kbs);
}
mifare_cypher_blocks_chained(NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER);
memcpy(cmac, ivect, kbs);
//free(buffer);
}
size_t key_block_size(const desfirekey_t key) {
@ -374,7 +375,7 @@ size_t key_block_size(const desfirekey_t key) {
/*
* Size of MACing produced with the key.
*/
static size_t key_macing_length(const desfirekey_t key) {
size_t key_macing_length(const desfirekey_t key) {
size_t mac_length = DESFIRE_MAC_LENGTH;
switch (key->type) {
case T_DES:
@ -393,10 +394,11 @@ static size_t key_macing_length(const desfirekey_t key) {
* Size required to store nbytes of data in a buffer of size n*block_size.
*/
size_t padded_data_length(const size_t nbytes, const size_t block_size) {
if ((!nbytes) || (nbytes % block_size))
if ((!nbytes) || (nbytes % block_size)) {
return ((nbytes / block_size) + 1) * block_size;
else
} else {
return nbytes;
}
}
/*
@ -412,12 +414,14 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c
size_t crc_length = 0;
if (!(communication_settings & NO_CRC)) {
switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY:
case AS_LEGACY: {
crc_length = 2;
break;
case AS_NEW:
}
case AS_NEW: {
crc_length = 4;
break;
}
}
}
@ -428,18 +432,20 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c
void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, size_t offset, int communication_settings) {
uint8_t *res = data;
uint8_t mac[4];
uint8_t mac[4] = {0};
size_t edl;
bool append_mac = true;
desfirekey_t key = DESFIRE(tag)->session_key;
if (!key)
desfirekey_t key = DESFIRE(tag)->session_key;
if (!key) {
return data;
}
switch (communication_settings & MDCM_MASK) {
case MDCM_PLAIN:
if (AS_LEGACY == DESFIRE(tag)->authentication_scheme)
case MDCM_PLAIN: {
if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) {
break;
}
/*
* When using new authentication methods, PLAIN data transmission from
@ -452,13 +458,16 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
*/
append_mac = false;
}
/* pass through */
case MDCM_MACED:
case MDCM_MACED: {
switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY:
if (!(communication_settings & MAC_COMMAND))
case AS_LEGACY: {
if (!(communication_settings & MAC_COMMAND)) {
break;
}
/* pass through */
edl = padded_data_length(*nbytes - offset, key_block_size(DESFIRE(tag)->session_key)) + offset;
@ -475,8 +484,9 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
// Copy again provided data (was overwritten by mifare_cypher_blocks_chained)
memcpy(res, data, *nbytes);
if (!(communication_settings & MAC_COMMAND))
if (!(communication_settings & MAC_COMMAND)) {
break;
}
// Append MAC
size_t bla = maced_data_length(DESFIRE(tag)->session_key, *nbytes - offset) + offset;
(void)bla++;
@ -485,9 +495,12 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
*nbytes += 4;
break;
case AS_NEW:
if (!(communication_settings & CMAC_COMMAND))
}
case AS_NEW: {
if (!(communication_settings & CMAC_COMMAND)) {
break;
}
cmac(key, DESFIRE(tag)->ivect, res, *nbytes, DESFIRE(tag)->cmac);
if (append_mac) {
@ -498,10 +511,11 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
*nbytes += DESFIRE_CMAC_LENGTH;
}
break;
}
}
break;
case MDCM_ENCIPHERED:
}
case MDCM_ENCIPHERED: {
/* |<-------------- data -------------->|
* |<--- offset -->| |
* +---------------+--------------------+-----+---------+
@ -517,8 +531,10 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
* encypher()/decypher()
*/
if (!(communication_settings & ENC_COMMAND))
if (!(communication_settings & ENC_COMMAND)) {
break;
}
edl = enciphered_data_length(tag, *nbytes - offset, communication_settings) + offset;
// Fill in the crypto buffer with data ...
@ -526,14 +542,16 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
if (!(communication_settings & NO_CRC)) {
// ... CRC ...
switch (DESFIRE(tag)->authentication_scheme) {
case AS_LEGACY:
case AS_LEGACY: {
AddCrc14A(res + offset, *nbytes - offset);
*nbytes += 2;
break;
case AS_NEW:
}
case AS_NEW: {
crc32_append(res, *nbytes);
*nbytes += 4;
break;
}
}
}
// ... and padding
@ -543,32 +561,34 @@ void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes,
mifare_cypher_blocks_chained(tag, NULL, NULL, res + offset, *nbytes - offset, MCD_SEND, (AS_NEW == DESFIRE(tag)->authentication_scheme) ? MCO_ENCYPHER : MCO_DECYPHER);
break;
default:
}
default: {
*nbytes = -1;
res = NULL;
break;
}
}
return res;
}
void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings) {
void *res = data;
uint8_t first_cmac_byte = 0x00;
desfirekey_t key = DESFIRE(tag)->session_key;
if (!key) {
return data;
}
// Return directly if we just have a status code.
if (1 == *nbytes) {
return res;
}
desfirekey_t key = DESFIRE(tag)->session_key;
if (!key) {
return data;
}
uint8_t first_cmac_byte = 0x00;
switch (communication_settings & MDCM_MASK) {
case MDCM_PLAIN: {
@ -659,11 +679,12 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
break;
}
case MDCM_ENCIPHERED: {
(*nbytes)--;
bool verified = false;
int crc_pos = 0x00;
int end_crc_pos = 0x00;
uint8_t x;
/*
* AS_LEGACY:
@ -742,8 +763,9 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
verified = true;
for (int n = end_crc_pos; n < *nbytes - 1; n++) {
uint8_t byte = ((uint8_t *)res)[n];
if (!((0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos))))
if (!((0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)))) {
verified = false;
}
}
}
@ -768,7 +790,7 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
break;
}
case AS_NEW: {
x = ((uint8_t *)res)[crc_pos - 1];
uint8_t x = ((uint8_t *)res)[crc_pos - 1];
((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos];
((uint8_t *)res)[crc_pos] = x;
break;
@ -802,9 +824,10 @@ void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes
void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size) {
uint8_t ovect[DESFIRE_MAX_CRYPTO_BLOCK_SIZE];
if (direction == MCD_SEND) {
xor(ivect, data, block_size);
xor(data, ivect, block_size);
} else {
memcpy(ovect, data, block_size);
}
@ -812,70 +835,80 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect,
uint8_t edata[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
switch (key->type) {
case T_DES:
case T_DES: {
switch (operation) {
case MCO_ENCYPHER:
case MCO_ENCYPHER: {
//DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT);
des_encrypt(edata, data, key->data);
break;
case MCO_DECYPHER:
}
case MCO_DECYPHER: {
//DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
des_decrypt(edata, data, key->data);
break;
}
}
break;
case T_3DES:
}
case T_3DES: {
switch (operation) {
case MCO_ENCYPHER:
case MCO_ENCYPHER: {
mbedtls_des3_set2key_enc(&ctx3, key->data);
mbedtls_des3_crypt_ecb(&ctx3, data, edata);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT);
// DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT);
break;
case MCO_DECYPHER:
}
case MCO_DECYPHER: {
mbedtls_des3_set2key_dec(&ctx3, key->data);
mbedtls_des3_crypt_ecb(&ctx3, data, edata);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
// DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
break;
}
}
break;
case T_3K3DES:
}
case T_3K3DES: {
switch (operation) {
case MCO_ENCYPHER:
case MCO_ENCYPHER: {
mbedtls_des3_set3key_enc(&ctx3, key->data);
mbedtls_des3_crypt_ecb(&ctx3, data, edata);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT);
// DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_ENCRYPT);
break;
case MCO_DECYPHER:
}
case MCO_DECYPHER: {
mbedtls_des3_set3key_dec(&ctx3, key->data);
mbedtls_des3_crypt_ecb(&ctx3, data, edata);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_DECRYPT);
// DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT);
// DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT);
break;
}
}
break;
case T_AES:
}
case T_AES: {
switch (operation) {
case MCO_ENCYPHER: {
mbedtls_aes_init(&actx);
mbedtls_aes_setkey_enc(&actx, key->data, 128);
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_ENCRYPT, sizeof(edata), ivect, data, edata);
mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_ENCRYPT, data, edata);
break;
}
case MCO_DECYPHER: {
mbedtls_aes_init(&actx);
mbedtls_aes_setkey_dec(&actx, key->data, 128);
mbedtls_aes_crypt_cbc(&actx, MBEDTLS_AES_DECRYPT, sizeof(edata), ivect, edata, data);
mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_DECRYPT, data, edata);
break;
}
}
break;
}
}
memcpy(data, edata, block_size);
@ -883,7 +916,7 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect,
if (direction == MCD_SEND) {
memcpy(ivect, data, block_size);
} else {
xor(ivect, data, block_size);
xor(data, ivect, block_size);
memcpy(ivect, ovect, block_size);
}
}

View file

@ -183,7 +183,7 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, ui
void aes128_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]);
void aes128_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[16]);
void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key);
void Desfire_des_key_new(const uint8_t *value, desfirekey_t key);
void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key);
void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key);
void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key);
@ -207,4 +207,6 @@ size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int c
void cmac_generate_subkeys(desfirekey_t key);
void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac);
size_t key_macing_length(desfirekey_t key);
#endif

View file

@ -125,7 +125,7 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece
Dbprintf("");
// use annotate to give some hints about the command
annotate(&receivedCmd[1], receivedCmd_len-1);
annotate(&receivedCmd[1], receivedCmd_len - 1);
// This is a common request from the reader which we can just immediately respond to since we know we can't
// handle it.
@ -141,7 +141,7 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece
currentState = GENERATE_AC;
memcpy(receivedCmd, (unsigned char[]){ 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6);
memcpy(receivedCmd, (unsigned char[]) { 0x03, 0x80, 0xae, 0x80, 0x00, 0x1d }, 6);
for (int i = 0; i < 29; i++) {
receivedCmd[6 + i] = receivedCmd[12 + i];
@ -240,7 +240,8 @@ void ExecuteEMVSim(uint8_t *receivedCmd, uint16_t receivedCmd_len, uint8_t *rece
0x20, 0x00, 0x9f, 0x26, 0x08, 0x56, 0xcb, 0x4e, 0xe1, 0xa4,
0xef, 0xac, 0x74, 0x9f, 0x27, 0x01, 0x80, 0x9f, 0x36, 0x02,
0x00, 0x07, 0x9f, 0x6c, 0x02, 0x3e, 0x00, 0x9f, 0x6e, 0x04,
0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff};
0x20, 0x70, 0x00, 0x00, 0x90, 0x00, 0xff, 0xff
};
// do the replacement
template[0] = responseToReader[0]; // class bit 0

View file

@ -821,12 +821,17 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *
// stage 1, got UID
if (bCrypto == false) {
uint64_t ui64key = key[0] |
((uint64_t)key[1]) << 8 |
((uint64_t)key[2]) << 16 |
((uint64_t)key[3]) << 24 |
((uint64_t)key[4]) << 32 |
((uint64_t)key[5]) << 40;
uint32_t ui32uid = MemLeToUint4byte(rx);
DBG Dbprintf("hitag2_crypto: key array ");
DBG Dbhexdump(6, key, false);
uint64_t ui64key = key[0] | ((uint64_t)key[1]) << 8 | ((uint64_t)key[2]) << 16 | ((uint64_t)key[3]) << 24 | ((uint64_t)key[4]) << 32 | ((uint64_t)key[5]) << 40;
uint32_t ui32uid = rx[0] | ((uint32_t)rx[1]) << 8 | ((uint32_t)rx[2]) << 16 | ((uint32_t)rx[3]) << 24;
DBG Dbprintf("hitag2_crypto: key=0x%x%x uid=0x%x"
, (uint32_t)((REV64(ui64key)) >> 32)
, (uint32_t)((REV64(ui64key)) & 0xffffffff)

View file

@ -74,7 +74,7 @@ static uint8_t pwdh0, pwdl0, pwdl1; // password bytes
static uint8_t rnd[] = {0x85, 0x44, 0x12, 0x74}; // random number
static uint16_t timestamp_high = 0; // Timer Counter 2 overflow count, ~47min
#define TIMESTAMP (AT91C_BASE_TC2->TC_SR &AT91C_TC_COVFS ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0)
#define TIMESTAMP ( (AT91C_BASE_TC2->TC_SR & AT91C_TC_COVFS) ? timestamp_high += 1 : 0, ((timestamp_high << 16) + AT91C_BASE_TC2->TC_CV) / T0)
//#define SENDBIT_TEST
@ -538,7 +538,8 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen,
rotate_uid++;
*txlen = 32;
// init crypt engine
state = ht2_hitag2_init(REV64(tag.data.s.key), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rx));
uint32_t le_rx = MemLeToUint4byte(rx);
state = ht2_hitag2_init(REV64(tag.data.s.key), REV32(tag.data.s.uid_le), REV32(le_rx));
DBG Dbhexdump(8, tx, false);
for (int i = 0; i < 4; i++) {
@ -776,7 +777,9 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) {
if (ledcontrol) LED_B_ON();
// Capture reader cmd start timestamp
if (start_time == 0) start_time = TIMESTAMP - HITAG_T_LOW;
if (start_time == 0) {
start_time = TIMESTAMP - HITAG_T_LOW;
}
// Capture reader frame
if (rb >= HITAG_T_STOP) {
@ -864,9 +867,6 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3
uint32_t rb_i = 0, h2 = 0, h3 = 0, h4 = 0;
uint8_t edges[160] = {0};
// Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RB:%i TIMESTAMP:%u", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV,
// AT91C_BASE_TC1->TC_RB, TIMESTAMP);
// Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods
while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) {
@ -1034,8 +1034,9 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t
DbpString("htS: response_bit:");
Dbhexdump(*rxlen, response_bit, false);
Dbprintf("htS: skipping %d bit SOF", sof_bits);
if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) {
DbpString("htS: Warning, not all bits of SOF are 1");
DBG DbpString("htS: Warning, not all bits of SOF are 1");
}
}
@ -1128,12 +1129,10 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
// if the tag is in authentication mode try the key or challenge
if (packet->cmd == HTSF_KEY) {
DBG DbpString("Authenticating using key:");
DBG Dbhexdump(6, packet->key, false);
key_le = MemLeToUint6byte(packet->key);
key_le = *(uint64_t *)packet->key;
uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rnd));
uint32_t le_val = MemLeToUint4byte(rnd);
uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(le_val));
uint8_t auth_ks[4];
for (int i = 0; i < 4; i++) {
@ -1144,6 +1143,8 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
txlen = concatbits(tx, txlen, rnd, 0, 32);
txlen = concatbits(tx, txlen, auth_ks, 0, 32);
DBG DbpString("Authenticating using key:");
DBG Dbhexdump(6, packet->key, false);
DBG Dbprintf("%02X %02X %02X %02X %02X %02X %02X %02X", tx[0], tx[1], tx[2], tx[3], tx[4], tx[5], tx[6], tx[7]);
} else if (packet->cmd == HTSF_CHALLENGE) {
@ -1225,7 +1226,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz
pwdl1 = 0;
if (packet->cmd == HTSF_KEY) {
uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(*(uint32_t *)rnd));
uint32_t le_val = MemLeToUint4byte(rnd);
uint64_t state = ht2_hitag2_init(REV64(key_le), REV32(tag.data.s.uid_le), REV32(le_val));
for (int i = 0; i < 4; i++) {
ht2_hitag2_byte(&state);
}

View file

@ -690,8 +690,6 @@ static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) {
if (Demod.bitCount) { // there are some remaining data bits
Demod.shiftReg <<= (8 - Demod.bitCount); // left align the decoded bits
Demod.output[Demod.len++] = Demod.shiftReg & 0xFF; // and add them to the output
// Dbprintf("A | len... %u - %u == 0x%02x", Demod.len, Demod.bitCount, Demod.output[0]);
return true;
}
@ -1077,6 +1075,7 @@ bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffe
if (ts->max > max_buffer_size) {
Dbprintf("ToSend buffer, Out-of-bound, when modulating bits for tag answer:");
Dbhexdump(response_info->response_n, response_info->response, false);
Dbprintf("Need %i, got %i", ts->max, max_buffer_size);
return false;
}
@ -1108,7 +1107,7 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_
}
bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t *iRATs, size_t irats_len, tag_response_info_t **responses,
uint8_t *ats, size_t ats_len, tag_response_info_t **responses,
uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) {
uint8_t sak = 0;
// The first response contains the ATQA (note: bytes are transmitted in reverse order).
@ -1130,9 +1129,9 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
// TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1
// TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us)
// TC(1) = 0x02: CID supported, NAD not supported
// static uint8_t rRATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 };
static uint8_t rRATS[40] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 };
uint8_t rRATS_len = 8;
// static uint8_t rATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 };
static uint8_t rATS[40] = { 0x06, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 };
uint8_t rATS_len = 8;
// GET_VERSION response for EV1/NTAG
static uint8_t rVERSION[10] = { 0x00 };
@ -1184,8 +1183,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
rATQA[0] = 0x44;
rATQA[1] = 0x03;
sak = 0x20;
memcpy(rRATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8);
rRATS_len = 8;
memcpy(rATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8);
rATS_len = 8; // including CRC
break;
}
case 4: { // ISO/IEC 14443-4 - javacard (JCOP)
@ -1254,8 +1253,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
}
case 11: { // ISO/IEC 14443-4 - javacard (JCOP) / EMV
memcpy(rRATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00", 19);
rRATS_len = 19;
memcpy(rATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00\x00\x00", 21);
rATS_len = 21; // including CRC
rATQA[0] = 0x04;
sak = 0x20;
break;
@ -1271,19 +1270,21 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
}
}
// copy the iRATs if supplied.
// iRATs is a pointer to 20 byte array
// rRATS is a 40 byte array
if ((flags & FLAG_RATS_IN_DATA) == FLAG_RATS_IN_DATA) {
memcpy(rRATS, iRATs, irats_len);
// rats len is dictated by the first char of the string, add 2 crc bytes
rRATS_len = (iRATs[0] + 2);
// Since its Varible length we can send value > 40 and overflow our array.
// Even if RATS protocol defined as max 40 bytes doesn't mean people try stuff
if (rRATS_len > sizeof(rRATS)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: iRATS overflow. Max %zu, got %zu", sizeof(rRATS), rRATS_len);
// copy the ats if supplied.
// ats is a pointer to 20 byte array
// rATS is a 40 byte array
if ((flags & FLAG_ATS_IN_DATA) == FLAG_ATS_IN_DATA) {
// Even if RATS protocol defined as max 40 bytes doesn't mean people try stuff. Check for overflow before copy
if (ats_len + 2 > sizeof(rATS)) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: ATS overflow. Max %zu, got %zu", sizeof(rATS) - 2, ats_len);
return false;
}
memcpy(rATS, ats, ats_len);
rATS_len = ats_len + 2;
// ATS length (without CRC) is supposed to match its first byte TL
if (ats_len != ats[0]) {
if (g_dbglevel >= DBG_INFO) Dbprintf("[-] WARNING: actual ATS length (%zu) differs from its TL value (%u).", ats_len, ats[0]);
}
}
// if uid not supplied then get from emulator memory
@ -1379,7 +1380,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
return false;
}
AddCrc14A(rRATS, rRATS_len - 2);
AddCrc14A(rATS, rATS_len - 2);
AddCrc14A(rPPS, sizeof(rPPS) - 2);
@ -1407,7 +1408,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
{ .response = rSAKc1, .response_n = sizeof(rSAKc1) }, // Acknowledge select - cascade 1
{ .response = rSAKc2, .response_n = sizeof(rSAKc2) }, // Acknowledge select - cascade 2
{ .response = rSAKc3, .response_n = sizeof(rSAKc3) }, // Acknowledge select - cascade 3
{ .response = rRATS, .response_n = sizeof(rRATS) }, // dummy ATS (pseudo-ATR), answer to RATS
{ .response = rATS, .response_n = sizeof(rATS) }, // dummy ATS (pseudo-ATR), answer to RATS
{ .response = rVERSION, .response_n = sizeof(rVERSION) }, // EV1/NTAG GET_VERSION response
{ .response = rSIGN, .response_n = sizeof(rSIGN) }, // EV1/NTAG READ_SIG response
{ .response = rPPS, .response_n = sizeof(rPPS) }, // PPS response
@ -1415,7 +1416,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
};
// since rats len is variable now.
responses_init[RESP_INDEX_RATS].response_n = rRATS_len;
responses_init[RESP_INDEX_ATS].response_n = rATS_len;
// "precompiled" responses.
// These exist for speed reasons. There are no time in the anti collision phase to calculate responses.
@ -1428,7 +1429,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
// 85 bytes normally (rats = 8 bytes)
// 77 bytes + ratslen,
#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rRATS_len) * 8) + 77 + rRATS_len + 12 + 12 + 12)
#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rATS_len) * 8) + 77 + rATS_len + 12 + 12 + 12)
uint8_t *free_buffer = BigBuf_calloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE);
// modulation buffer pointer and current buffer free space size
@ -1454,8 +1455,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
// response to send, and send it.
// 'hf 14a sim'
//-----------------------------------------------------------------------------
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads,
uint8_t *iRATs, size_t irats_len) {
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads,
uint8_t *ats, size_t ats_len) {
#define ATTACK_KEY_COUNT 16
@ -1489,7 +1490,17 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
#define DYNAMIC_MODULATION_BUFFER_SIZE 512
uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE);
if (dynamic_response_buffer == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
uint8_t *dynamic_modulation_buffer = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER_SIZE);
if (dynamic_modulation_buffer == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
tag_response_info_t dynamic_response_info = {
.response = dynamic_response_buffer,
.response_n = 0,
@ -1497,7 +1508,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
.modulation_n = 0
};
if (SimulateIso14443aInit(tagType, flags, data, iRATs, irats_len, &responses, &cuid, counters, tearings, &pages) == false) {
if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len, &responses, &cuid, counters, tearings, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
return;
@ -1671,10 +1682,10 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
EmSend4bit(CARD_NACK_IV);
} else {
// first blocks of emu are header
uint16_t start = block * 4 + MFU_DUMP_PREFIX_LENGTH;
uint8_t emdata[MAX_MIFARE_FRAME_SIZE];
emlGet(emdata, start, 16);
AddCrc14A(emdata, 16);
uint16_t start = (block * 4) + MFU_DUMP_PREFIX_LENGTH;
uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0};
emlGet(emdata, start, MIFARE_BLOCK_SIZE);
AddCrc14A(emdata, MIFARE_BLOCK_SIZE);
EmSendCmd(emdata, sizeof(emdata));
numReads++; // Increment number of times reader requested a block
@ -1692,8 +1703,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
p_response = &responses[RESP_INDEX_UIDC1];
} else { // all other tags (16 byte block tags)
uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0};
emlGet(emdata, block, 16);
AddCrc14A(emdata, 16);
emlGet(emdata, block, MIFARE_BLOCK_SIZE);
AddCrc14A(emdata, MIFARE_BLOCK_SIZE);
EmSendCmd(emdata, sizeof(emdata));
// We already responded, do not send anything with the EmSendCmd14443aRaw() that is called below
p_response = NULL;
@ -1717,13 +1728,14 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
} else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE
// cmd + block + 4 bytes data + 2 bytes crc
if (CheckCrc14A(receivedCmd, len)) {
uint8_t block = receivedCmd[1];
if (block > pages) {
// send NACK 0x0 == invalid argument
EmSend4bit(CARD_NACK_IV);
} else {
// first blocks of emu are header
emlSetMem_xt(&receivedCmd[2], block + MFU_DUMP_PREFIX_LENGTH / 4, 1, 4);
emlSetMem_xt(&receivedCmd[2], block + (MFU_DUMP_PREFIX_LENGTH / 4), 1, 4);
// send ACK
EmSend4bit(CARD_ACK);
}
@ -1820,7 +1832,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
EmSend4bit(CARD_NACK_NA);
p_response = NULL;
} else {
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
}
} else if (receivedCmd[0] == MIFARE_ULC_AUTH_1) { // ULC authentication, or Desfire Authentication
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
@ -1836,7 +1848,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_
}
if (memcmp(pwd, "\x00\x00\x00\x00", 4) == 0) {
Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(data));
Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(useruid));
if (g_dbglevel >= DBG_DEBUG) Dbprintf("Calc pwd... %02X %02X %02X %02X", pwd[0], pwd[1], pwd[2], pwd[3]);
}
@ -3941,9 +3953,10 @@ It can also continue after the AID has been selected, and respond to other reque
This was forked from the original function to allow for more flexibility in the future, and to increase the processing speed of the original function.
/// */
void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t *iRATs, size_t irats_len, uint8_t *aid, uint8_t *resp,
uint8_t *apdu, int aidLen, int respondLen, int apduLen, bool enumerate) {
void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid,
uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len,
uint8_t *selectaid_response, size_t selectaid_response_len,
uint8_t *getdata_response, size_t getdata_response_len) {
tag_response_info_t *responses;
uint32_t cuid = 0;
uint32_t counters[3] = { 0x00, 0x00, 0x00 };
@ -3954,6 +3967,12 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 };
uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 };
// Buffers must be provided by the caller, even if lengths are 0
// Copy the AID, AID Response, and the GetData APDU response into our variables
if ((aid == NULL) || (selectaid_response == NULL) || (getdata_response == NULL)) {
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINVARG, NULL, 0);
}
// free eventually allocated BigBuf memory but keep Emulator Memory
BigBuf_free_keep_EM();
@ -3962,7 +3981,17 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
#define DYNAMIC_MODULATION_BUFFER2_SIZE 1536
uint8_t *dynamic_response_buffer2 = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER2_SIZE);
if (dynamic_response_buffer2 == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
uint8_t *dynamic_modulation_buffer2 = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER2_SIZE);
if (dynamic_modulation_buffer2 == NULL) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0);
return;
}
tag_response_info_t dynamic_response_info = {
.response = dynamic_response_buffer2,
.response_n = 0,
@ -3970,7 +3999,7 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
.modulation_n = 0
};
if (SimulateIso14443aInit(tagType, flags, data, iRATs, irats_len, &responses, &cuid, counters, tearings, &pages) == false) {
if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, counters, tearings, &pages) == false) {
BigBuf_free_keep_EM();
reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0);
return;
@ -3990,25 +4019,9 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
set_tracing(true);
LED_A_ON();
// Filters for when this comes through
static uint8_t aidFilter[30] = { 0x00 }; // Default AID Value
static uint8_t aidResponse[100] = { 0x00 }; // Default AID Response
static uint8_t apduCommand [100] = { 0x00 }; // Default APDU GetData Response
// Copy the AID, AID Response, and the GetData APDU response into our variables
if (aid != 0) {
memcpy(aidFilter, aid, aidLen);
}
if (resp != 0) {
memcpy(aidResponse, resp, respondLen);
}
if (apdu != 0) {
memcpy(apduCommand, apdu, apduLen);
}
// main loop
bool finished = false;
bool got_rats = false;
while (finished == false) {
// BUTTON_PRESS check done in GetIso14443aCommandFromReader
WDT_HIT();
@ -4046,24 +4059,32 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
} else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
p_response = NULL;
finished = true;
if (got_rats) {
finished = true;
}
} else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request
p_response = &responses[RESP_INDEX_RATS];
p_response = &responses[RESP_INDEX_ATS];
got_rats = true;
} else {
// clear old dynamic responses
dynamic_response_info.response_n = 0;
dynamic_response_info.modulation_n = 0;
// Check for ISO 14443A-4 compliant commands, look at left nibble
// Check for ISO 14443A-4 compliant commands, look at left byte (PCB)
uint8_t offset = 0;
switch (receivedCmd[0]) {
case 0x0B:
case 0x0A: { // IBlock (command CID)
case 0x0B: // IBlock with CID
case 0x0A:
offset = 1;
case 0x02: // IBlock without CID
case 0x03: {
dynamic_response_info.response[0] = receivedCmd[0];
dynamic_response_info.response[1] = 0x00;
switch (receivedCmd[3]) { // APDU Class Byte
// receivedCmd in this case is expecting to structured with a CID, then the APDU command for SelectFile
// | IBlock (CID) | CID | APDU Command | CRC |
switch (receivedCmd[2 + offset]) { // APDU Class Byte
// receivedCmd in this case is expecting to structured with possibly a CID, then the APDU command for SelectFile
// | IBlock (CID) | CID | APDU Command | CRC |
// or | IBlock (noCID) | APDU Command | CRC |
case 0xA4: { // SELECT FILE
// Select File AID uses the following format for GlobalPlatform
@ -4072,40 +4093,40 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
// xx in this case is len of the AID value in hex
// aid len is found as a hex value in receivedCmd[6] (Index Starts at 0)
int aid_len = receivedCmd[6];
uint8_t *received_aid = &receivedCmd[7];
int received_aid_len = receivedCmd[5 + offset];
uint8_t *received_aid = &receivedCmd[6 + offset];
// aid enumeration flag
if (enumerate == true) {
Dbprintf("Received AID (%d):", aid_len);
Dbhexdump(aid_len, received_aid, false);
if ((flags & FLAG_ENUMERATE_AID) == FLAG_ENUMERATE_AID) {
Dbprintf("Received AID (%d):", received_aid_len);
Dbhexdump(received_aid_len, received_aid, false);
}
if (memcmp(aidFilter, received_aid, aid_len) == 0) { // Evaluate the AID sent by the Reader to the AID supplied
if ((received_aid_len == aid_len) && (memcmp(aid, received_aid, aid_len) == 0)) { // Evaluate the AID sent by the Reader to the AID supplied
// AID Response will be parsed here
memcpy(dynamic_response_info.response + 2, aidResponse, respondLen + 2);
dynamic_response_info.response_n = respondLen + 2;
memcpy(dynamic_response_info.response + 1 + offset, selectaid_response, selectaid_response_len + 1 + offset);
dynamic_response_info.response_n = selectaid_response_len + 2;
} else { // Any other SELECT FILE command will return with a Not Found
dynamic_response_info.response[2] = 0x6A;
dynamic_response_info.response[3] = 0x82;
dynamic_response_info.response_n = 4;
dynamic_response_info.response[1 + offset] = 0x6A;
dynamic_response_info.response[2 + offset] = 0x82;
dynamic_response_info.response_n = 3 + offset;
}
}
break;
case 0xDA: { // PUT DATA
// Just send them a 90 00 response
dynamic_response_info.response[2] = 0x90;
dynamic_response_info.response[3] = 0x00;
dynamic_response_info.response_n = 4;
dynamic_response_info.response[1 + offset] = 0x90;
dynamic_response_info.response[2 + offset] = 0x00;
dynamic_response_info.response_n = 3 + offset;
}
break;
case 0xCA: { // GET DATA
if (sentCount == 0) {
// APDU Command will just be parsed here
memcpy(dynamic_response_info.response + 2, apduCommand, apduLen + 2);
dynamic_response_info.response_n = respondLen + 2;
memcpy(dynamic_response_info.response + 1 + offset, getdata_response, getdata_response_len + 2);
dynamic_response_info.response_n = selectaid_response_len + 1 + offset;
} else {
finished = true;
break;
@ -4116,18 +4137,18 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
default : {
// Any other non-listed command
// Respond Not Found
dynamic_response_info.response[2] = 0x6A;
dynamic_response_info.response[3] = 0x82;
dynamic_response_info.response_n = 4;
dynamic_response_info.response[1 + offset] = 0x6A;
dynamic_response_info.response[2 + offset] = 0x82;
dynamic_response_info.response_n = 3 + offset;
}
}
break;
}
break;
case 0xCA:
case 0xC2: { // Readers sends deselect command
dynamic_response_info.response[0] = 0xCA;
case 0xCA: // S-Block Deselect with CID
case 0xC2: { // S-Block Deselect without CID
dynamic_response_info.response[0] = receivedCmd[0];
dynamic_response_info.response[1] = 0x00;
dynamic_response_info.response_n = 2;
finished = true;
@ -4135,12 +4156,15 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
break;
default: {
// Never seen this command before
// Never seen this PCB before
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
if (g_dbglevel >= DBG_DEBUG) {
Dbprintf("Received unknown command (len=%d):", len);
Dbhexdump(len, receivedCmd, false);
}
if ((receivedCmd[0] & 0x10) == 0x10) {
Dbprintf("Warning, reader sent a chained command but we lack support for it. Ignoring command.");
}
// Do not respond
dynamic_response_info.response_n = 0;
}
@ -4149,13 +4173,15 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
if (dynamic_response_info.response_n > 0) {
// Copy the CID from the reader query
dynamic_response_info.response[1] = receivedCmd[1];
if (offset > 0) {
dynamic_response_info.response[1] = receivedCmd[1];
}
// Add CRC bytes, always used in ISO 14443A-4 compliant cards
AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n);
dynamic_response_info.response_n += 2;
if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) {
if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER2_SIZE) == false) {
if (g_dbglevel >= DBG_DEBUG) DbpString("Error preparing tag response");
LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true);
break;

View file

@ -105,7 +105,7 @@ typedef enum {
RESP_INDEX_SAKC1,
RESP_INDEX_SAKC2,
RESP_INDEX_SAKC3,
RESP_INDEX_RATS,
RESP_INDEX_ATS,
RESP_INDEX_VERSION,
RESP_INDEX_SIGNATURE,
RESP_INDEX_PPS,
@ -142,12 +142,13 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time);
RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time);
void RAMFUNC SniffIso14443a(uint8_t param);
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t exitAfterNReads,
void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads,
uint8_t *iRATs, size_t irats_len);
void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t *iRATs, size_t irats_len, uint8_t *aid, uint8_t *resp,
uint8_t *apdu, int aid_len, int respond_len, int apdu_len, bool enumerate);
void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid,
uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len,
uint8_t *selectaid_response, size_t selectaid_response_len,
uint8_t *getdata_response, size_t getdata_response_len);
bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data,
uint8_t *iRATs, size_t irats_len, tag_response_info_t **responses,

View file

@ -2117,14 +2117,6 @@ void Iso15693InitTag(void) {
StartCountSspClk();
}
void EmlClearIso15693(void) {
// Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent
// an inconvenient reset in the future by Iso15693InitTag
FpgaDownloadAndGo(FPGA_BITSTREAM_HF_15);
BigBuf_Clear_EM();
reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0);
}
// Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands
// all demodulation performed in arm rather than host. - greg
void SimTagIso15693(const uint8_t *uid, uint8_t block_size) {
@ -2775,10 +2767,10 @@ void LockPassSlixIso15693(uint32_t pass_id, uint32_t password) {
LED_A_ON();
uint8_t cmd_inventory[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1, 0x01, 0x00, 0x00, 0x00 };
uint8_t cmd_get_rnd[] = {ISO15693_REQ_DATARATE_HIGH, 0xB2, 0x04, 0x00, 0x00 };
uint8_t cmd_set_pass[] = {ISO15693_REQ_DATARATE_HIGH, 0xB3, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
//uint8_t cmd_write_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, 0xB4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t cmd_lock_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, 0xB5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 };
uint8_t cmd_get_rnd[] = {ISO15693_REQ_DATARATE_HIGH, ISO15693_GET_RANDOM_NUMBER, 0x04, 0x00, 0x00 };
uint8_t cmd_set_pass[] = {ISO15693_REQ_DATARATE_HIGH, ISO15693_SET_PASSWORD, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
//uint8_t cmd_write_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, ISO15693_WRITE_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t cmd_lock_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, ISO15693_LOCK_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 };
uint16_t crc;
uint16_t recvlen = 0;
uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH];
@ -3028,14 +3020,7 @@ static uint32_t disable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_ti
return PM3_SUCCESS;
}
static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid) {
uint8_t rnd[2];
if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) {
return PM3_ETIMEOUT;
}
static uint32_t set_pass_15693_SlixRnd(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid, uint8_t *rnd) {
// 0x04, == NXP from manufacture id list.
uint8_t c[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_SET_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, pass_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
@ -3055,6 +3040,18 @@ static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uin
return PM3_SUCCESS;
}
static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, const uint8_t *uid) {
uint8_t rnd[2];
if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) {
return PM3_ETIMEOUT;
}
return set_pass_15693_SlixRnd(start_time, eof_time, pass_id, password, uid, rnd);
}
static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, const uint8_t *password) {
uint8_t rnd[2];
if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) {
@ -3062,7 +3059,7 @@ static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time,
}
// 0x04, == NXP from manufacture id list.
uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xBA, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_ENABLE_PRIVACY, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
init_password_15693_Slix(&c[3], password, rnd);
AddCrc15(c, 7);
@ -3096,7 +3093,7 @@ static uint32_t disable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time,
}
// 0x04, == NXP from manufacture id list.
uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xA3, 0x04, 0x00, 0x00};
uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_RESET_EAS, 0x04, 0x00, 0x00};
AddCrc15(c, 3);
start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
@ -3127,7 +3124,7 @@ static uint32_t enable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, c
}
}
// 0x04, == NXP from manufacture id list.
uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, 0xA2, 0x04, 0x00, 0x00};
uint8_t c[] = { ISO15_REQ_DATARATE_HIGH, ISO15693_SET_EAS, 0x04, 0x00, 0x00};
//init_password_15693_Slix(&c[3], password, rnd);
AddCrc15(c, 3);
@ -3162,6 +3159,26 @@ static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_tim
return PM3_SUCCESS;
}
static uint32_t protect_page_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t divide_ptr, uint8_t prot_status, const uint8_t *uid) {
uint8_t protect_cmd[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_PROTECT_PAGE, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, divide_ptr, prot_status, 0x00, 0x00};
memcpy(&protect_cmd[3], uid, 8);
AddCrc15(protect_cmd, 13);
start_time = *eof_time + DELAY_ISO15693_VICC_TO_VCD_READER;
uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH];
uint16_t recvlen = 0;
int res_wrp = SendDataTag(protect_cmd, sizeof(protect_cmd), false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, eof_time, &recvlen);
if (res_wrp != PM3_SUCCESS && recvlen != 3) {
return PM3_EWRONGANSWER;
}
return PM3_SUCCESS;
}
static uint32_t pass_protect_EASAFI_15693_Slix(uint32_t start_time, uint32_t *eof_time, bool set_option_flag, const uint8_t *password) {
uint8_t flags;
@ -3262,6 +3279,37 @@ void WritePasswordSlixIso15693(const uint8_t *old_password, const uint8_t *new_p
}
void ProtectPageSlixIso15693(const uint8_t *read_password, const uint8_t *write_password, uint8_t divide_ptr, uint8_t prot_status) {
LED_D_ON();
Iso15693InitReader();
StartCountSspClk();
uint32_t start_time = 0, eof_time = 0;
int res = PM3_SUCCESS;
uint8_t uid[8], rnd[2];
get_uid_slix(start_time, &eof_time, uid);
if (get_rnd_15693_Slix(start_time, &eof_time, rnd) == false) {
reply_ng(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, PM3_ETIMEOUT, NULL, 0);
switch_off();
return;
}
if (read_password)
res = set_pass_15693_SlixRnd(start_time, &eof_time, 0x01, read_password, uid, rnd);
if (res == PM3_SUCCESS && write_password)
res = set_pass_15693_SlixRnd(start_time, &eof_time, 0x02, write_password, uid, rnd);
if (res == PM3_SUCCESS)
res = protect_page_15693_Slix(start_time, &eof_time, divide_ptr, prot_status, uid);
reply_ng(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, res, NULL, 0);
switch_off();
}
void DisablePrivacySlixIso15693(const uint8_t *password) {
LED_D_ON();
Iso15693InitReader();

View file

@ -46,7 +46,6 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo
//void RecordRawAdcSamplesIso15693(void);
void AcquireRawAdcSamplesIso15693(void);
void ReaderIso15693(iso15_card_select_t *p_card); // ISO15693 reader
void EmlClearIso15693(void);
void SimTagIso15693(const uint8_t *uid, uint8_t block_size); // simulate an ISO15693 tag
void BruteforceIso15693Afi(uint32_t flags); // find an AFI of a tag
void SendRawCommand15693(iso15_raw_cmd_t *packet); // send arbitrary commands from CLI
@ -69,4 +68,5 @@ void EnableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd);
void PassProtextEASSlixIso15693(const uint8_t *password);
void PassProtectAFISlixIso15693(const uint8_t *password);
void WriteAFIIso15693(const uint8_t *password, bool use_pwd, uint8_t *uid, bool use_uid, uint8_t afi);
void ProtectPageSlixIso15693(const uint8_t *read_password, const uint8_t *write_password, uint8_t divide_ptr, uint8_t prot_status);
#endif

View file

@ -944,6 +944,33 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, int16_t *remainder) {
}
}
bool add_HID_preamble(uint32_t *hi2, uint32_t *hi, uint32_t *lo, uint8_t length) {
// Invalid value
if (length > 84 || length == 0)
return false;
if (length == 48) {
*hi |= 1U << (length - 32); // Example leading 1: start bit
return true;
}
if (length >= 64) {
*hi2 |= 0x09e00000; // Extended-length header
*hi2 |= 1U << (length - 64); // leading 1: start bit
} else if (length > 37) {
*hi2 |= 0x09e00000; // Extended-length header
*hi |= 1U << (length - 32); // leading 1: start bit
} else if (length == 37) {
// No header bits added to 37-bit cards
} else if (length >= 32) {
*hi |= 0x20; // Bit 37; standard header
*hi |= 1U << (length - 32); // leading 1: start bit
} else {
*hi |= 0x20; // Bit 37; standard header
*lo |= 1U << length; // leading 1: start bit
}
return true;
}
// prepare a waveform pattern in the buffer based on the ID given then
// simulate a HID tag until the button is pressed
void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol, int numcycles) {
@ -968,13 +995,7 @@ void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, boo
uint16_t n = 8;
if (longFMT) {
// Ensure no more than 84 bits supplied
if (hi2 > 0xFFFFF) {
DbpString("Tags can only have 84 bits.");
return;
}
bitlen = 8 + 8 * 2 + 84 * 2;
hi2 |= 0x9E00000; // 9E: long format identifier
manchesterEncodeUint32(hi2, 16 + 12, bits, &n);
manchesterEncodeUint32(hi, 32, bits, &n);
manchesterEncodeUint32(lo, 32, bits, &n);
@ -1316,10 +1337,6 @@ int lf_hid_watch(int findone, uint32_t *high, uint32_t *low, bool ledcontrol) {
cardnum = (lo >> 1) & 0xFFFF;
fac = (lo >> 17) & 0xFF;
}
if (bitlen == 37) {
cardnum = (lo >> 1) & 0x7FFFF;
fac = ((hi & 0xF) << 12) | (lo >> 20);
}
if (bitlen == 34) {
cardnum = (lo >> 1) & 0xFFFF;
fac = ((hi & 1) << 15) | (lo >> 17);
@ -2274,15 +2291,10 @@ void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, boo
uint8_t last_block = 0;
if (longFMT) {
// Ensure no more than 84 bits supplied
if (hi2 > 0xFFFFF) {
DbpString("Tags can only have 84 bits");
return;
}
// Build the 6 data blocks for supplied 84bit ID
last_block = 6;
// load preamble (1D) & long format identifier (9E manchester encoded)
data[1] = 0x1D96A900 | (manchesterEncode2Bytes((hi2 >> 16) & 0xF) & 0xFF);
// load preamble (1D)
data[1] = 0x1D000000 | (manchesterEncode2Bytes((hi2 >> 16) & 0xFFFF) & 0xFFFFFF);
// load raw id from hi2, hi, lo to data blocks (manchester encoded)
data[2] = manchesterEncode2Bytes(hi2 & 0xFFFF);
data[3] = manchesterEncode2Bytes(hi >> 16);

View file

@ -34,6 +34,7 @@ void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycl
void SimulateTagLowFrequency(int period, int gap, bool ledcontrol);
void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen);
bool add_HID_preamble(uint32_t *hi2, uint32_t *hi, uint32_t *lo, uint8_t length);
void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol, int numcycles);
void CmdHIDsimTAG(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol);

View file

@ -2223,7 +2223,7 @@ OUT:
blockno = (32 * 4 + (i - 32) * 16) ^ 0xF;
}
// get ST
emlGetMem(block, blockno, 1);
emlGetMem_xt(block, blockno, 1, MIFARE_BLOCK_SIZE);
memcpy(block, k_sector[i].keyA, 6);
memcpy(block + 10, k_sector[i].keyB, 6);
@ -2376,8 +2376,8 @@ void MifareChkKeys_file(uint8_t *fn) {
void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) {
uint16_t isOK = PM3_EUNDEF;
uint8_t uid[10];
uint32_t cuid;
uint8_t uid[10] = { 0 };
uint32_t cuid = 0;
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
@ -2388,8 +2388,12 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) {
LED_A_ON();
uint8_t rec_answer[MAX_MIFARE_FRAME_SIZE] = {0};
uint8_t rec_answer_par[MAX_MIFARE_PARITY_SIZE] = {0};
while (true) {
if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) {
if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card");
break;
}
@ -2400,11 +2404,9 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) {
break;
}
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE];
uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE];
int len = mifare_sendcmd_short(pcs, true, MIFARE_EV1_PERSONAL_UID, perso_option, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL);
if (len != 1 || receivedAnswer[0] != CARD_ACK) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]);
int len = mifare_sendcmd_short(pcs, true, MIFARE_EV1_PERSONAL_UID, perso_option, rec_answer, sizeof(rec_answer), rec_answer_par, NULL);
if (len != 1 || rec_answer[0] != CARD_ACK) {
if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", rec_answer[0]);
break;
}
@ -2427,39 +2429,6 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) {
}
//-----------------------------------------------------------------------------
// Work with emulator memory
//
// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not
// involved in dealing with emulator memory. But if it is called later, it might
// destroy the Emulator Memory.
//-----------------------------------------------------------------------------
void MifareEMemClr(void) {
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
emlClearMem();
}
void MifareEMemGet(uint8_t blockno, uint8_t blockcnt) {
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
//
size_t size = blockcnt * 16;
if (size > PM3_CMD_DATA_SIZE) {
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0);
return;
}
uint8_t *buf = BigBuf_malloc(size);
emlGetMem(buf, blockno, blockcnt); // data, block num, blocks count (max 4)
LED_B_ON();
reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size);
LED_B_OFF();
BigBuf_free_keep_EM();
}
//-----------------------------------------------------------------------------
// Load a card into the emulator memory
//
@ -2471,12 +2440,15 @@ int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) {
}
int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) {
if ((keytype > MF_KEY_B) && (key == NULL)) {
if (g_dbglevel >= DBG_ERROR) {
Dbprintf("Error, missing key");
}
return PM3_EINVARG;
}
LED_A_ON();
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
@ -2510,10 +2482,10 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) {
// MFC 1K EV1, skip sector 16 since its lockdown
if (s == 16) {
// unknown sector trailer, keep the keys, set only the AC
uint8_t st[16] = {0x00};
emlGetMem(st, FirstBlockOfSector(s) + 3, 1);
uint8_t st[MIFARE_BLOCK_SIZE] = {0x00};
emlGetMem_xt(st, FirstBlockOfSector(s) + 3, 1, MIFARE_BLOCK_SIZE);
memcpy(st + 6, "\x70\xF0\xF8\x69", 4);
emlSetMem_xt(st, FirstBlockOfSector(s) + 3, 1, 16);
emlSetMem_xt(st, FirstBlockOfSector(s) + 3, 1, MIFARE_BLOCK_SIZE);
continue;
}
@ -2556,7 +2528,8 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) {
}
have_uid = true;
} else { // no need for anticollision. We can directly select the card
if (!bd_authenticated) { // no need to select if bd_authenticated with backdoor
if (bd_authenticated == false) { // no need to select if bd_authenticated with backdoor
if (iso14443a_fast_select_card(uid, cascade_levels) == 0) {
continue;
}
@ -2565,7 +2538,7 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) {
// Auth
if (keytype > MF_KEY_B) {
if (! bd_authenticated) {
if (bd_authenticated == false) {
ui64Key = bytes_to_num(key, 6);
if (mifare_classic_auth(pcs, cuid, 0, keytype, ui64Key, AUTH_FIRST)) {
retval = PM3_EFAILED;
@ -2592,7 +2565,7 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) {
#define MAX_RETRIES 2
uint8_t data[16] = {0x00};
uint8_t data[MIFARE_BLOCK_SIZE] = {0x00};
for (uint8_t b = 0; b < NumBlocksPerSector(s); b++) {
memset(data, 0x00, sizeof(data));
@ -2614,18 +2587,18 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key) {
}
// No need to copy empty
if (memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0) {
if (memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", sizeof(data)) == 0) {
break;
}
if (IsSectorTrailer(b)) {
// sector trailer, keep the keys, set only the AC
uint8_t st[16] = {0x00};
emlGetMem(st, tb, 1);
uint8_t st[MIFARE_BLOCK_SIZE] = {0x00};
emlGetMem_xt(st, tb, 1, MIFARE_BLOCK_SIZE);
memcpy(st + 6, data + 6, 4);
emlSetMem_xt(st, tb, 1, 16);
emlSetMem_xt(st, tb, 1, MIFARE_BLOCK_SIZE);
} else {
emlSetMem_xt(data, tb, 1, 16);
emlSetMem_xt(data, tb, 1, MIFARE_BLOCK_SIZE);
}
break;
}
@ -2927,7 +2900,6 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) {
// variables
uint8_t rec[1] = {0x00};
uint8_t recpar[1] = {0x00};
uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73};
uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f};
uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8};
uint8_t gen4gdmAuth[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92};
@ -2940,6 +2912,8 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) {
uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE);
uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE);
uint8_t *uid = BigBuf_calloc(10);
iso14a_card_select_t *card = (iso14a_card_select_t *) BigBuf_calloc(sizeof(iso14a_card_select_t));
uint16_t flag = MAGIC_FLAG_NONE;
uint32_t cuid = 0;
int res = 0;
@ -2991,144 +2965,141 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) {
// reset card
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, false);
res = iso14443a_select_card(uid, card, &cuid, true, 0, false);
if (res) {
if (cuid == 0xAA55C396) {
flag |= MAGIC_FLAG_GEN_UNFUSED;
}
ReaderTransmit(rats, sizeof(rats), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (memcmp(card->ats, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10", 9) == 0) {
// test for some MFC gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(card->ats, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) {
// test for some MFC 7b gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(card->ats, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) {
// test for Ultralight magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(card->ats, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) {
// test for Ultralight EV1 magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(card->ats, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) {
// test for some other Ultralight EV1 magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(card->ats, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) {
// test for some other Ultralight magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(card->ats, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) {
// test for NTAG213 magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
}
if (res) {
if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10", 9) == 0) {
// test for some MFC gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) {
// test for some MFC 7b gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) {
// test for Ultralight magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) {
// test for Ultralight EV1 magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) {
// test for some other Ultralight EV1 magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) {
// test for some other Ultralight magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
} else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) {
// test for NTAG213 magic gen2
isGen2 = true;
flag |= MAGIC_FLAG_GEN_2;
// test for super card
ReaderTransmit(superGen1, sizeof(superGen1), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 22) {
uint8_t isGen = MAGIC_FLAG_SUPER_GEN1;
// check for super card gen2
// not available after RATS, reset card before executing
mf_reset_card();
iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
ReaderTransmit(rdbl00, sizeof(rdbl00), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 18) {
isGen = MAGIC_FLAG_SUPER_GEN2;
}
// test for super card
ReaderTransmit(superGen1, sizeof(superGen1), NULL);
flag |= isGen;
}
}
if (is_mfc == false) {
// magic ntag test
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res == 2) {
ReaderTransmit(rdblf0, sizeof(rdblf0), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 22) {
uint8_t isGen = MAGIC_FLAG_SUPER_GEN1;
// check for super card gen2
// not available after RATS, reset card before executing
mf_reset_card();
iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
ReaderTransmit(rdbl00, sizeof(rdbl00), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 18) {
isGen = MAGIC_FLAG_SUPER_GEN2;
}
flag |= isGen;
if (res == 18) {
flag |= MAGIC_FLAG_NTAG21X;
}
}
if (is_mfc == false) {
// magic ntag test
} else {
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
// CUID (with default sector 0 B key) test
// regular cards will NAK the WRITEBLOCK(0) command, while DirectWrite will ACK it
// if we do get an ACK, we immediately abort to ensure nothing is ever actually written
// only perform test if we haven't already identified Gen2. No need test if we have a positive identification already
if (isGen2 == false) {
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res == 2) {
ReaderTransmit(rdblf0, sizeof(rdblf0), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 18) {
flag |= MAGIC_FLAG_NTAG21X;
}
}
} else {
if (res) {
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs;
pcs = &mpcs;
uint64_t tmpkey = bytes_to_num(key, 6);
if (mifare_classic_authex(pcs, cuid, 0, keytype, tmpkey, AUTH_FIRST, NULL, NULL) == 0) {
// CUID (with default sector 0 B key) test
// regular cards will NAK the WRITEBLOCK(0) command, while DirectWrite will ACK it
// if we do get an ACK, we immediately abort to ensure nothing is ever actually written
// only perform test if we haven't already identified Gen2. No need test if we have a positive identification already
if (isGen2 == false) {
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res) {
uint64_t tmpkey = bytes_to_num(key, 6);
if (mifare_classic_authex(pcs, cuid, 0, keytype, tmpkey, AUTH_FIRST, NULL, NULL) == 0) {
if ((mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, 0, buf, PM3_CMD_DATA_SIZE, par, NULL) == 1) && (buf[0] == 0x0A)) {
flag |= MAGIC_FLAG_GEN_2;
// turn off immediately to ensure nothing ever accidentally writes to the block
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
}
if ((mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, 0, buf, PM3_CMD_DATA_SIZE, par, NULL) == 1) && (buf[0] == 0x0A)) {
flag |= MAGIC_FLAG_GEN_2;
// turn off immediately to ensure nothing ever accidentally writes to the block
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
}
crypto1_deinit(pcs);
}
}
// magic MFC Gen3 test 1
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res) {
ReaderTransmit(rdbl00, sizeof(rdbl00), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 18) {
flag |= MAGIC_FLAG_GEN_3;
}
}
// magic MFC Gen4 GDM magic auth test
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res) {
ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 4) {
flag |= MAGIC_FLAG_GDM_AUTH;
}
}
// QL88 test
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res) {
if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) {
flag |= MAGIC_FLAG_QL88;
}
crypto1_deinit(pcs);
}
}
};
// magic MFC Gen3 test 1
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res) {
ReaderTransmit(rdbl00, sizeof(rdbl00), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 18) {
flag |= MAGIC_FLAG_GEN_3;
}
}
// magic MFC Gen4 GDM magic auth test
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res) {
ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL);
res = ReaderReceive(buf, PM3_CMD_DATA_SIZE, par);
if (res == 4) {
flag |= MAGIC_FLAG_GDM_AUTH;
}
}
// QL88 test
mf_reset_card();
res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true);
if (res) {
if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) {
flag |= MAGIC_FLAG_QL88;
}
crypto1_deinit(pcs);
}
}
// GDM alt magic wakeup (20)
ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL);

View file

@ -43,8 +43,6 @@ void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem);
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
void MifareChkKeys_file(uint8_t *fn);
void MifareEMemClr(void);
void MifareEMemGet(uint8_t blockno, uint8_t blockcnt);
int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype, uint8_t *key);
int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype, uint8_t *key);

View file

@ -48,8 +48,8 @@
#include "parity.h"
static bool IsKeyBReadable(uint8_t blockNo) {
uint8_t sector_trailer[16];
emlGetMem(sector_trailer, SectorTrailer(blockNo), 1);
uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0};
emlGetMem_xt(sector_trailer, SectorTrailer(blockNo), 1, MIFARE_BLOCK_SIZE);
uint8_t AC = ((sector_trailer[7] >> 5) & 0x04)
| ((sector_trailer[8] >> 2) & 0x02)
| ((sector_trailer[8] >> 7) & 0x01);
@ -57,55 +57,64 @@ static bool IsKeyBReadable(uint8_t blockNo) {
}
static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) {
uint8_t sector_trailer[16];
emlGetMem(sector_trailer, blockNo, 1);
uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0};
emlGetMem_xt(sector_trailer, blockNo, 1, MIFARE_BLOCK_SIZE);
uint8_t AC = ((sector_trailer[7] >> 5) & 0x04)
| ((sector_trailer[8] >> 2) & 0x02)
| ((sector_trailer[8] >> 7) & 0x01);
switch (action) {
case AC_KEYA_READ: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsTrailerAccessAllowed: AC_KEYA_READ");
}
return false;
}
case AC_KEYA_WRITE: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsTrailerAccessAllowed: AC_KEYA_WRITE");
}
return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01))
|| (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03)));
}
case AC_KEYB_READ: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsTrailerAccessAllowed: AC_KEYB_READ");
}
return (keytype == AUTHKEYA && (AC == 0x00 || AC == 0x02 || AC == 0x01));
}
case AC_KEYB_WRITE: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsTrailerAccessAllowed: AC_KEYB_WRITE");
}
return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01))
|| (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03)));
}
case AC_AC_READ: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsTrailerAccessAllowed: AC_AC_READ");
}
return ((keytype == AUTHKEYA)
|| (keytype == AUTHKEYB && !(AC == 0x00 || AC == 0x02 || AC == 0x01)));
}
case AC_AC_WRITE: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsTrailerAccessAllowed: AC_AC_WRITE");
}
return ((keytype == AUTHKEYA && (AC == 0x01))
|| (keytype == AUTHKEYB && (AC == 0x03 || AC == 0x05)));
}
default:
default: {
return false;
}
}
}
static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) {
uint8_t sector_trailer[16];
emlGetMem(sector_trailer, SectorTrailer(blockNo), 1);
uint8_t sector_trailer[MIFARE_BLOCK_SIZE] = {0};
emlGetMem_xt(sector_trailer, SectorTrailer(blockNo), 1, MIFARE_BLOCK_SIZE);
uint8_t sector_block;
if (blockNo <= MIFARE_2K_MAXBLOCK) {
@ -120,54 +129,62 @@ static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action
AC = ((sector_trailer[7] >> 2) & 0x04)
| ((sector_trailer[8] << 1) & 0x02)
| ((sector_trailer[8] >> 4) & 0x01);
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsDataAccessAllowed: case 0x00 - %02x", AC);
}
break;
}
case 0x01: {
AC = ((sector_trailer[7] >> 3) & 0x04)
| ((sector_trailer[8] >> 0) & 0x02)
| ((sector_trailer[8] >> 5) & 0x01);
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsDataAccessAllowed: case 0x01 - %02x", AC);
}
break;
}
case 0x02: {
AC = ((sector_trailer[7] >> 4) & 0x04)
| ((sector_trailer[8] >> 1) & 0x02)
| ((sector_trailer[8] >> 6) & 0x01);
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsDataAccessAllowed: case 0x02 - %02x", AC);
}
break;
}
default:
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsDataAccessAllowed: Error");
}
return false;
}
switch (action) {
case AC_DATA_READ: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsDataAccessAllowed - AC_DATA_READ: OK");
}
return ((keytype == AUTHKEYA && !(AC == 0x03 || AC == 0x05 || AC == 0x07))
|| (keytype == AUTHKEYB && !(AC == 0x07)));
}
case AC_DATA_WRITE: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsDataAccessAllowed - AC_DATA_WRITE: OK");
}
return ((keytype == AUTHKEYA && (AC == 0x00))
|| (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03)));
}
case AC_DATA_INC: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("IsDataAccessAllowed - AC_DATA_INC: OK");
}
return ((keytype == AUTHKEYA && (AC == 0x00))
|| (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06)));
}
case AC_DATA_DEC_TRANS_REST: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("AC_DATA_DEC_TRANS_REST: OK");
}
return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x06 || AC == 0x01))
|| (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06 || AC == 0x01)));
}
@ -252,29 +269,33 @@ bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag
// Length: 4,7,or 10 bytes
if (IS_FLAG_UID_IN_EMUL(flags)) {
if (uid == NULL) {
uid = uid_tmp;
}
// Get UID, SAK, ATQA from EMUL
uint8_t block0[16];
emlGet(block0, 0, 16);
uint8_t block0[MIFARE_BLOCK_SIZE];
emlGet(block0, 0, MIFARE_BLOCK_SIZE);
// Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA
if ((block0[0] ^ block0[1] ^ block0[2] ^ block0[3]) == block0[4] && (block0[6] & 0xc0) == 0) {
FLAG_SET_UID_IN_DATA(flags, 4);
memcpy(uid, block0, 4);
rSAK[0] = block0[5];
memcpy(rATQA, &block0[6], sizeof(rATQA));
}
// Check for 7 bytes UID: double size uid bits in ATQA
else if ((block0[8] & 0xc0) == 0x40) {
} else if ((block0[8] & 0xc0) == 0x40) {
// Check for 7 bytes UID: double size uid bits in ATQA
FLAG_SET_UID_IN_DATA(flags, 7);
memcpy(uid, block0, 7);
rSAK[0] = block0[7];
memcpy(rATQA, &block0[8], sizeof(rATQA));
} else {
Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found"));
return false;
}
} else {
if (uid == NULL) {
Dbprintf("ERROR: " _RED_("Missing UID"));
@ -288,16 +309,19 @@ bool MifareSimInit(uint16_t flags, uint8_t *uid, uint16_t atqa, uint8_t sak, tag
memcpy(rATQA, rATQA_Mini, sizeof(rATQA));
rSAK[0] = rSAK_Mini;
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare Mini ATQA/SAK");
} else if (IS_FLAG_MF_SIZE(flags, MIFARE_1K_MAX_BYTES)) {
memcpy(rATQA, rATQA_1k, sizeof(rATQA));
rSAK[0] = rSAK_1k;
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 1K ATQA/SAK");
} else if (IS_FLAG_MF_SIZE(flags, MIFARE_2K_MAX_BYTES)) {
memcpy(rATQA, rATQA_2k, sizeof(rATQA));
rSAK[0] = rSAK_2k;
*rats = rRATS;
*rats_len = sizeof(rRATS);
if (g_dbglevel > DBG_NONE) Dbprintf("Enforcing Mifare 2K ATQA/SAK with RATS support");
} else if (IS_FLAG_MF_SIZE(flags, MIFARE_4K_MAX_BYTES)) {
memcpy(rATQA, rATQA_4k, sizeof(rATQA));
rSAK[0] = rSAK_4k;
@ -825,8 +849,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
// if key not known and FLAG_NESTED_AUTH_ATTACK and we have nt/nt_enc/parity, send recorded nt_enc and parity
if ((flags & FLAG_NESTED_AUTH_ATTACK) == FLAG_NESTED_AUTH_ATTACK) {
if (emlGetKey(cardAUTHSC, cardAUTHKEY) == 0) {
uint8_t buf[16] = {0};
emlGetMem(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + cardAUTHSC, 1);
uint8_t buf[MIFARE_BLOCK_SIZE] = {0};
emlGetMem_xt(buf, (CARD_MEMORY_RF08S_OFFSET / MIFARE_BLOCK_SIZE) + cardAUTHSC, 1, MIFARE_BLOCK_SIZE);
if (buf[(cardAUTHKEY * 8) + 3] == 0xAA) { // extra check to tell we have nt/nt_enc/par_err
running_nested_auth_attack = true;
// nt
@ -955,7 +979,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
// first block
if (blockNo == 4) {
p_em += blockNo * 16;
p_em += (blockNo * MIFARE_BLOCK_SIZE);
// TLV in NDEF, flip length between
// 4 | 03 21 D1 02 1C 53 70 91 01 09 54 02 65 6E 4C 69
// 0xFF means long length
@ -970,7 +994,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
}
}
emlGetMem(response, blockNo, 1);
emlGetMem_xt(response, blockNo, 1, MIFARE_BLOCK_SIZE);
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("[MFEMUL_WORK - ISO14443A_CMD_READBLOCK] Data Block[%d]: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", blockNo,
@ -1010,11 +1034,11 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
}
} else {
if (IsAccessAllowed(blockNo, cardAUTHKEY, AC_DATA_READ) == false) {
memset(response, 0x00, 16); // datablock cannot be read
memset(response, 0x00, MIFARE_BLOCK_SIZE); // datablock cannot be read
if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK - IsAccessAllowed] Data block %d (0x%02x) cannot be read", blockNo, blockNo);
}
}
AddCrc14A(response, 16);
AddCrc14A(response, MIFARE_BLOCK_SIZE);
mf_crypto1_encrypt(pcs, response, MAX_MIFARE_FRAME_SIZE, response_par);
EmSendCmdPar(response, MAX_MIFARE_FRAME_SIZE, response_par);
FpgaDisableTracing();
@ -1109,7 +1133,9 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
// case MFEMUL_WORK => CMD RATS
if (receivedCmd_len == 4 && receivedCmd_dec[0] == ISO14443A_CMD_RATS && (receivedCmd_dec[1] & 0xF0) <= 0x80 && (receivedCmd_dec[1] & 0x0F) <= 0x0e) {
if (rats && rats_len) {
if (encrypted_data) {
memcpy(response, rats, rats_len);
mf_crypto1_encrypt(pcs, response, rats_len, response_par);
@ -1117,46 +1143,58 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
} else {
EmSendCmd(rats, rats_len);
}
FpgaDisableTracing();
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("[MFEMUL_WORK] RCV RATS => ACK");
}
} else {
EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA);
FpgaDisableTracing();
cardSTATE_TO_IDLE();
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("[MFEMUL_WORK] RCV RATS => NACK");
}
}
break;
}
// case MFEMUL_WORK => ISO14443A_CMD_NXP_DESELECT
if (receivedCmd_len == 3 && receivedCmd_dec[0] == ISO14443A_CMD_NXP_DESELECT) {
if (rats && rats_len) {
// response back NXP_DESELECT
if (encrypted_data) {
memcpy(response, receivedCmd_dec, receivedCmd_len);
mf_crypto1_encrypt(pcs, response, receivedCmd_len, response_par);
EmSendCmdPar(response, receivedCmd_len, response_par);
} else
} else {
EmSendCmd(receivedCmd_dec, receivedCmd_len);
}
FpgaDisableTracing();
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => ACK");
}
} else {
EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA);
FpgaDisableTracing();
cardSTATE_TO_IDLE();
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => NACK");
}
}
break;
}
// case MFEMUL_WORK => command not allowed
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("Received command not allowed, nacking");
}
EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA);
FpgaDisableTracing();
break;
@ -1164,14 +1202,16 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
// AUTH1
case MFEMUL_AUTH1: {
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("[MFEMUL_AUTH1] Enter case");
}
if (receivedCmd_len != 8) {
cardSTATE_TO_IDLE();
LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true);
if (g_dbglevel >= DBG_EXTENDED)
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("MFEMUL_AUTH1: receivedCmd_len != 8 (%d) => cardSTATE_TO_IDLE())", receivedCmd_len);
}
break;
}
@ -1191,6 +1231,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
ar_nr_resp[0].state = NESTED;
finished = true;
}
if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) {
for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) {
@ -1267,22 +1308,29 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t
// WRITE BL2
case MFEMUL_WRITEBL2: {
if (receivedCmd_len == MAX_MIFARE_FRAME_SIZE) {
mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec);
if (CheckCrc14A(receivedCmd_dec, receivedCmd_len)) {
if (IsSectorTrailer(cardWRBL)) {
emlGetMem(response, cardWRBL, 1);
if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE)) {
emlGetMem_xt(response, cardWRBL, 1, MIFARE_BLOCK_SIZE);
if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE) == false) {
memcpy(receivedCmd_dec, response, 6); // don't change KeyA
}
if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE)) {
if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE) == false) {
memcpy(receivedCmd_dec + 10, response + 10, 6); // don't change KeyA
}
if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE)) {
if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE) == false) {
memcpy(receivedCmd_dec + 6, response + 6, 4); // don't change AC bits
}
} else {
if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE)) {
if (IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE) == false) {
memcpy(receivedCmd_dec, response, 16); // don't change anything
}
}

View file

@ -756,14 +756,16 @@ uint8_t FirstBlockOfSector(uint8_t sectorNo) {
}
// work with emulator memory
void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int block_width) {
void emlSetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width) {
uint32_t offset = blockNum * block_width;
uint32_t len = blocksCount * block_width;
emlSet(data, offset, len);
}
void emlGetMem(uint8_t *data, int blockNum, int blocksCount) {
emlGet(data, (blockNum * 16), (blocksCount * 16));
void emlGetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width) {
uint32_t offset = blockNum * block_width;
uint32_t len = blocksCount * block_width;
emlGet(data, offset, len);
}
bool emlCheckValBl(int blockNum) {
@ -817,10 +819,11 @@ uint64_t emlGetKey(int sectorNum, int keyType) {
}
void emlClearMem(void) {
BigBuf_Clear_EM();
const uint8_t trailer[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0x69, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const uint8_t uid[] = {0xe6, 0x84, 0x87, 0xf3, 0x16, 0x88, 0x04, 0x00, 0x46, 0x8e, 0x45, 0x55, 0x4d, 0x70, 0x41, 0x04};
uint8_t *mem = BigBuf_get_EM_addr();
memset(mem, 0, CARD_MEMORY_SIZE);
// fill sectors trailer data
for (uint16_t b = 3; b < MIFARE_4K_MAXBLOCK; ((b < MIFARE_2K_MAXBLOCK - 4) ? (b += 4) : (b += 16))) {

View file

@ -131,8 +131,9 @@ uint8_t SectorTrailer(uint8_t blockNo);
// emulator functions
void emlClearMem(void);
void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int block_width);
void emlGetMem(uint8_t *data, int blockNum, int blocksCount);
void emlSetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width);
void emlGetMem_xt(uint8_t *data, uint16_t blockNum, uint8_t blocksCount, uint8_t block_width);
uint64_t emlGetKey(int sectorNum, int keyType);
int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum);
void emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum);

View file

@ -28,105 +28,151 @@
#define T0_PCF 8 //period for the pcf7931 in us
#define ALLOC 16
size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
// IIR filter consts
#define IIR_CONST1 0.1f
#define IIR_CONST2 0.9f
// 2021 iceman, memor
// used to decimate samples. this allows DoAcquisition to sample for a longer duration.
// Decimation of 4 makes sure that all blocks can be sampled at once!
#define DECIMATION 4
#define CLOCK (64/DECIMATION) // this actually is 64, but since samples are decimated by 2, CLOCK is also /2
#define TOLERANCE (CLOCK / 8)
#define _16T0 (CLOCK/4)
#define _32T0 (CLOCK/2)
#define _64T0 (CLOCK)
// calculating the two possible pmc lengths, based on the clock. -4 at the end is to make sure not to increment too far
#define PMC_16T0_LEN ((128 + 127 + 16 + 32 + 33 + 16) * CLOCK/64);
#define PMC_32T0_LEN ((128 + 127 + 16 + 32 + 33 ) * CLOCK/64);
// theshold for recognition of positive/negative slope
#define THRESHOLD 80
size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
uint8_t bits[256] = {0x00};
uint8_t blocks[8][16];
uint8_t *dest = BigBuf_get_addr();
uint16_t g_GraphTraceLen = BigBuf_max_traceLen();
// limit g_GraphTraceLen to a little more than 2 data frames.
// To make sure a complete dataframe is in the dataset.
// 1 Frame is 16 Byte -> 128byte. at a T0 of 64 -> 8129 Samples per frame.
// + PMC -> 384T0 --> 8576 samples required for one block
// to make sure that one complete block is definitely being sampled, we need 2 times that
// which is ~17.xxx samples. round up. and clamp to this value.
int g_GraphTraceLen = BigBuf_max_traceLen();
if (g_GraphTraceLen > 18000) {
g_GraphTraceLen = 18000;
}
int i = 2, j, lastval, bitidx, half_switch;
int clock = 64;
int tolerance = clock / 8;
int pmc, block_done;
int lc, warnings = 0;
size_t num_blocks = 0;
int lmin = 64, lmax = 192;
uint8_t dir;
// TODO: Doublecheck why this is being limited? - seems not to be needed.
// g_GraphTraceLen = (g_GraphTraceLen > 18000) ? 18000 : g_GraphTraceLen;
BigBuf_Clear_keep_EM();
LFSetupFPGAForADC(LF_DIVISOR_125, true);
DoAcquisition_default(0, true, ledcontrol);
DoAcquisition(DECIMATION, 8, 0, 0, false, 0, 0, 0, ledcontrol);
/* Find first local max/min */
if (dest[1] > dest[0]) {
while (i < g_GraphTraceLen) {
if (!(dest[i] > dest[i - 1]) && dest[i] > lmax) {
break;
}
i++;
}
dir = 0;
} else {
while (i < g_GraphTraceLen) {
if (!(dest[i] < dest[i - 1]) && dest[i] < lmin) {
break;
}
i++;
}
dir = 1;
}
uint8_t j;
uint8_t half_switch;
uint8_t bitPos;
uint32_t sample; // to keep track of the current sample that is being analyzed
uint32_t samplePosLastEdge;
uint32_t samplePosCurrentEdge;
uint8_t lastClockDuration; // used to store the duration of the last "clock", for decoding. clock may not be the correct term, maybe bit is better. The duration between two edges is meant
uint8_t beforeLastClockDuration; // store the clock duration of the cycle before the last Clock duration. Basically clockduration -2
uint8_t block_done;
size_t num_blocks = 0;
EdgeType expectedNextEdge = FALLING; // direction in which the next edge is expected should go.
lastval = i++;
half_switch = 0;
pmc = 0;
samplePosLastEdge = 0;
block_done = 0;
bitPos = 0;
lastClockDuration=0;
for (bitidx = 0; i < g_GraphTraceLen; i++) {
for (sample = 1 ; sample < g_GraphTraceLen-4; sample++) {
// condition is searching for the next edge, in the expected diretion.
//todo: without flouz
dest[sample] = (uint8_t)(dest[sample-1] * IIR_CONST1 + dest[sample] * IIR_CONST2); // apply IIR filter
if ((dest[i - 1] > dest[i] && dir == 1 && dest[i] > lmax) || (dest[i - 1] < dest[i] && dir == 0 && dest[i] < lmin)) {
lc = i - lastval;
lastval = i;
if ( ((dest[sample] + THRESHOLD) < dest[sample-1] && expectedNextEdge == FALLING ) ||
((dest[sample] - THRESHOLD) > dest[sample-1] && expectedNextEdge == RISING )) {
//okay, next falling/rising edge found
// Switch depending on lc length:
// Tolerance is 1/8 of clock rate (arbitrary)
if (ABS(lc - clock / 4) < tolerance) {
// 16T0
if ((i - pmc) == lc) { // 16T0 was previous one
expectedNextEdge = (expectedNextEdge == FALLING) ? RISING : FALLING; //toggle the next expected edge
samplePosCurrentEdge = sample;
beforeLastClockDuration = lastClockDuration; // save the previous clock duration for PMC recognition
lastClockDuration = samplePosCurrentEdge - samplePosLastEdge;
samplePosLastEdge = sample;
// Dbprintf("%d, %d, edge found, len: %d, nextEdge: %d", sample, dest[sample], lastClockDuration*DECIMATION, expectedNextEdge);
// Switch depending on lastClockDuration length:
// 16T0
if (ABS(lastClockDuration - _16T0) < TOLERANCE) {
// if the clock before also was 16T0, it is a PMC!
if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) {
// It's a PMC
i += (128 + 127 + 16 + 32 + 33 + 16) - 1;
lastval = i;
pmc = 0;
Dbprintf(_GREEN_("PMC 16T0 FOUND:") " bitPos: %d, sample: %d", bitPos, sample);
sample += PMC_16T0_LEN; // move to the sample after PMC
expectedNextEdge = FALLING;
samplePosLastEdge = sample;
block_done = 1;
} else {
pmc = i;
}
} else if (ABS(lc - clock / 2) < tolerance) {
// 32TO
if ((i - pmc) == lc) { // 16T0 was previous one
// 32TO
} else if (ABS(lastClockDuration - _32T0) < TOLERANCE) {
// if the clock before also was 16T0, it is a PMC!
if (ABS(beforeLastClockDuration - _16T0) < TOLERANCE) {
// It's a PMC !
i += (128 + 127 + 16 + 32 + 33) - 1;
lastval = i;
pmc = 0;
Dbprintf(_GREEN_("PMC 32T0 FOUND:") " bitPos: %d, sample: %d", bitPos, sample);
sample += PMC_32T0_LEN; // move to the sample after PMC
expectedNextEdge = FALLING;
samplePosLastEdge = sample;
block_done = 1;
// if no pmc, then its a normal bit.
// Check if its the second time, the edge changed if yes, then the bit is 0
} else if (half_switch == 1) {
bits[bitidx++] = 0;
bits[bitPos] = 0;
// reset the edge counter to 0
half_switch = 0;
bitPos++;
// if it is the first time the edge changed. No bit value will be set here, bit if the
// edge changes again, it will be. see case above.
} else
half_switch++;
} else if (ABS(lc - clock) < tolerance) {
// 64TO
bits[bitidx++] = 1;
// 64T0
} else if (ABS(lastClockDuration - _64T0) < TOLERANCE) {
// this means, bit here is 1
bits[bitPos] = 1;
bitPos++;
// Error
} else {
// Error
if (++warnings > 10) {
// some Error. maybe check tolerances.
// likeley to happen in the first block.
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("Error: too many detection errors, aborting");
}
// In an Ideal world, this can be enabled. However, if only bad antenna field, this print will flood the output
// and one might miss some "good" frames.
//Dbprintf(_RED_("ERROR in demodulation") " Length last clock: %d - check threshold/tolerance/signal. Toss block", lastClockDuration*DECIMATION);
return 0;
}
// Toss this block.
block_done = 1;
}
if (block_done == 1) {
if (bitidx == 128) {
// Dbprintf(_YELLOW_("Block Done") " bitPos: %d, sample: %d", bitPos, sample);
// check if it is a complete block. If bitpos <128, it means that we did not receive
// a complete block. E.g. at the first start of a transmission.
// only save if a complete block is being received.
if (bitPos == 128) {
for (j = 0; j < 16; ++j) {
blocks[num_blocks][j] =
128 * bits[j * 8 + 7] +
@ -141,24 +187,25 @@ size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol) {
}
num_blocks++;
}
bitidx = 0;
// now start over for the next block / first complete block.
bitPos = 0;
block_done = 0;
half_switch = 0;
}
if (i < g_GraphTraceLen) {
dir = (dest[i - 1] > dest[i]) ? 0 : 1;
}
}else {
// Dbprintf("%d, %d", sample, dest[sample]);
}
if (bitidx == 255) {
bitidx = 0;
// one block only holds 16byte (=128 bit) and then comes the PMC. so if more bit are found than 129, there must be an issue and PMC has not been identfied...
// TODO: not sure what to do in such case...
if (bitPos >= 129) {
Dbprintf(_RED_("PMC should have been found...") " bitPos: %d, sample: %d", bitPos, sample);
bitPos = 0;
}
if (num_blocks == 4) {
break;
}
}
memcpy(outBlocks, blocks, 16 * num_blocks);
return num_blocks;
}
@ -204,25 +251,32 @@ bool IsBlock1PCF7931(const uint8_t *block) {
}
void ReadPCF7931(bool ledcontrol) {
uint8_t maxBlocks = 8; // readable blocks
int found_blocks = 0; // successfully read blocks
int max_blocks = 8; // readable blocks
uint8_t memory_blocks[8][17]; // PCF content
uint8_t single_blocks[8][17]; // PFC blocks with unknown position
// TODO: Why 17 byte len? 16 should be good.
uint8_t memory_blocks[maxBlocks][17]; // PCF content
uint8_t single_blocks[maxBlocks][17]; // PFC blocks with unknown position
uint8_t tmp_blocks[4][16]; // temporary read buffer
int single_blocks_cnt = 0;
size_t n; // transmitted blocks
uint8_t tmp_blocks[4][16]; // temporary read buffer
uint8_t found_0_1 = 0; // flag: blocks 0 and 1 were found
//uint8_t found_0_1 = 0; // flag: blocks 0 and 1 were found
int errors = 0; // error counter
int tries = 0; // tries counter
// reuse lenghts and consts to properly clear
memset(memory_blocks, 0, 8 * 17 * sizeof(uint8_t));
memset(single_blocks, 0, 8 * 17 * sizeof(uint8_t));
int i = 0, j = 0;
int i = 0;
//j = 0;
do {
Dbprintf("ReadPCF7931() -- Reading Loop ==========");
i = 0;
memset(tmp_blocks, 0, 4 * 16 * sizeof(uint8_t));
@ -232,15 +286,13 @@ void ReadPCF7931(bool ledcontrol) {
// exit if no block is received
if (errors >= 10 && found_blocks == 0 && single_blocks_cnt == 0) {
if (g_dbglevel >= DBG_INFO)
Dbprintf("[!!] Error, no tag or bad tag");
Dbprintf("[!!] Error, no tag or bad tag");
return;
}
// exit if too many errors during reading
if (tries > 50 && (2 * errors > tries)) {
// exit if too many tries without finding the first block
if (tries > 10) {
Dbprintf("End after 10 tries");
if (g_dbglevel >= DBG_INFO) {
Dbprintf("[!!] Error reading the tag, only partial content");
}
@ -248,93 +300,98 @@ void ReadPCF7931(bool ledcontrol) {
goto end;
}
// our logic breaks if we don't get at least two blocks
if (n < 2) {
// skip if all 0s block or no blocks
if (n == 0 || !memcmp(tmp_blocks[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))
continue;
// This part was not working properly.
// So currently the blocks are not being sorted, but at least printed.
// add block to single blocks list
if (single_blocks_cnt < max_blocks) {
for (i = 0; i < single_blocks_cnt; ++i) {
if (!memcmp(single_blocks[i], tmp_blocks[0], 16)) {
j = 1;
break;
}
}
if (j != 1) {
memcpy(single_blocks[single_blocks_cnt], tmp_blocks[0], 16);
print_result("got single block", single_blocks[single_blocks_cnt], 16);
single_blocks_cnt++;
}
j = 0;
}
++tries;
continue;
}
// // our logic breaks if we don't get at least two blocks
// if (n < 2) {
// // skip if all 0s block or no blocks
// if (n == 0 || !memcmp(tmp_blocks[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16))
// continue;
if (g_dbglevel >= DBG_EXTENDED)
Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (max_blocks == 0 ? found_blocks : max_blocks), tries, errors);
// // add block to single blocks list
// if (single_blocks_cnt < maxBlocks) {
// for (i = 0; i < single_blocks_cnt; ++i) {
// if (!memcmp(single_blocks[i], tmp_blocks[0], 16)) {
// j = 1;
// break;
// }
// }
// if (j != 1) {
// memcpy(single_blocks[single_blocks_cnt], tmp_blocks[0], 16);
// print_result("got single block", single_blocks[single_blocks_cnt], 16);
// single_blocks_cnt++;
// }
// j = 0;
// }
// ++tries;
// continue;
// }
// Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (maxBlocks == 0 ? found_blocks : maxBlocks), tries, errors);
// if (g_dbglevel >= DBG_EXTENDED)
// Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (maxBlocks == 0 ? found_blocks : maxBlocks), tries, errors);
// print blocks that have been found
for (i = 0; i < n; ++i) {
print_result("got consecutive blocks", tmp_blocks[i], 16);
print_result("Block found: ", tmp_blocks[i], 16);
}
i = 0;
if (!found_0_1) {
while (i < n - 1) {
if (IsBlock0PCF7931(tmp_blocks[i]) && IsBlock1PCF7931(tmp_blocks[i + 1])) {
found_0_1 = 1;
memcpy(memory_blocks[0], tmp_blocks[i], 16);
memcpy(memory_blocks[1], tmp_blocks[i + 1], 16);
memory_blocks[0][ALLOC] = memory_blocks[1][ALLOC] = 1;
// block 1 tells how many blocks are going to be sent
max_blocks = MAX((memory_blocks[1][14] & 0x7f), memory_blocks[1][15]) + 1;
found_blocks = 2;
// i = 0;
// if (!found_0_1) {
// while (i < n - 1) {
// if (IsBlock0PCF7931(tmp_blocks[i]) && IsBlock1PCF7931(tmp_blocks[i + 1])) {
// found_0_1 = 1;
// memcpy(memory_blocks[0], tmp_blocks[i], 16);
// memcpy(memory_blocks[1], tmp_blocks[i + 1], 16);
// memory_blocks[0][ALLOC] = memory_blocks[1][ALLOC] = 1;
// // block 1 tells how many blocks are going to be sent
// maxBlocks = MAX((memory_blocks[1][14] & 0x7f), memory_blocks[1][15]) + 1;
// found_blocks = 2;
Dbprintf("Found blocks 0 and 1. PCF is transmitting %d blocks.", max_blocks);
// Dbprintf("Found blocks 0 and 1. PCF is transmitting %d blocks.", maxBlocks);
// handle the following blocks
for (j = i + 2; j < n; ++j) {
memcpy(memory_blocks[found_blocks], tmp_blocks[j], 16);
memory_blocks[found_blocks][ALLOC] = 1;
++found_blocks;
}
break;
}
++i;
}
} else {
// Trying to re-order blocks
// Look for identical block in memory blocks
while (i < n - 1) {
// skip all zeroes blocks
if (memcmp(tmp_blocks[i], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) {
for (j = 1; j < max_blocks - 1; ++j) {
if (!memcmp(tmp_blocks[i], memory_blocks[j], 16) && !memory_blocks[j + 1][ALLOC]) {
memcpy(memory_blocks[j + 1], tmp_blocks[i + 1], 16);
memory_blocks[j + 1][ALLOC] = 1;
if (++found_blocks >= max_blocks) goto end;
}
}
}
if (memcmp(tmp_blocks[i + 1], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) {
for (j = 0; j < max_blocks; ++j) {
if (!memcmp(tmp_blocks[i + 1], memory_blocks[j], 16) && !memory_blocks[(j == 0 ? max_blocks : j) - 1][ALLOC]) {
if (j == 0) {
memcpy(memory_blocks[max_blocks - 1], tmp_blocks[i], 16);
memory_blocks[max_blocks - 1][ALLOC] = 1;
} else {
memcpy(memory_blocks[j - 1], tmp_blocks[i], 16);
memory_blocks[j - 1][ALLOC] = 1;
}
if (++found_blocks >= max_blocks) goto end;
}
}
}
++i;
}
}
// // handle the following blocks
// for (j = i + 2; j < n; ++j) {
// memcpy(memory_blocks[found_blocks], tmp_blocks[j], 16);
// memory_blocks[found_blocks][ALLOC] = 1;
// ++found_blocks;
// }
// break;
// }
// ++i;
// }
// } else {
// // Trying to re-order blocks
// // Look for identical block in memory blocks
// while (i < n - 1) {
// // skip all zeroes blocks
// if (memcmp(tmp_blocks[i], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) {
// for (j = 1; j < maxBlocks - 1; ++j) {
// if (!memcmp(tmp_blocks[i], memory_blocks[j], 16) && !memory_blocks[j + 1][ALLOC]) {
// memcpy(memory_blocks[j + 1], tmp_blocks[i + 1], 16);
// memory_blocks[j + 1][ALLOC] = 1;
// if (++found_blocks >= maxBlocks) goto end;
// }
// }
// }
// if (memcmp(tmp_blocks[i + 1], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) {
// for (j = 0; j < maxBlocks; ++j) {
// if (!memcmp(tmp_blocks[i + 1], memory_blocks[j], 16) && !memory_blocks[(j == 0 ? maxBlocks : j) - 1][ALLOC]) {
// if (j == 0) {
// memcpy(memory_blocks[maxBlocks - 1], tmp_blocks[i], 16);
// memory_blocks[maxBlocks - 1][ALLOC] = 1;
// } else {
// memcpy(memory_blocks[j - 1], tmp_blocks[i], 16);
// memory_blocks[j - 1][ALLOC] = 1;
// }
// if (++found_blocks >= maxBlocks) goto end;
// }
// }
// }
// ++i;
// }
// }
++tries;
if (BUTTON_PRESS()) {
if (g_dbglevel >= DBG_EXTENDED)
@ -342,13 +399,15 @@ void ReadPCF7931(bool ledcontrol) {
goto end;
}
} while (found_blocks < max_blocks);
} while (found_blocks < maxBlocks);
end:
/*
Dbprintf("-----------------------------------------");
Dbprintf("Memory content:");
Dbprintf("-----------------------------------------");
for (i = 0; i < max_blocks; ++i) {
for (i = 0; i < maxBlocks; ++i) {
if (memory_blocks[i][ALLOC])
print_result("Block", memory_blocks[i], 16);
else
@ -356,7 +415,7 @@ end:
}
Dbprintf("-----------------------------------------");
if (found_blocks < max_blocks) {
if (found_blocks < maxBlocks) {
Dbprintf("-----------------------------------------");
Dbprintf("Blocks with unknown position:");
Dbprintf("-----------------------------------------");
@ -365,39 +424,54 @@ end:
Dbprintf("-----------------------------------------");
}
*/
reply_mix(CMD_ACK, 0, 0, 0, 0, 0);
}
static void RealWritePCF7931(uint8_t *pass, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol) {
static void RealWritePCF7931(
uint8_t *pass,
uint16_t init_delay,
int8_t offsetPulseWidth, int8_t offsetPulsePosition,
uint8_t address, uint8_t byte, uint8_t data,
bool ledcontrol){
uint32_t tab[1024] = {0}; // data times frame
uint32_t u = 0;
uint8_t parity = 0;
bool comp = 0;
//BUILD OF THE DATA FRAME
//alimentation of the tag (time for initializing)
// ToDo: This could be optimized/automated. e.g. Read one cycle, find PMC and calculate time.
// I dont understand, why 8192/2
AddPatternPCF7931(init_delay, 0, 8192 / 2 * T0_PCF, tab);
// why "... + 70"? Why not "... + x * T0"?
// I think he just added 70 to be somewhere in The PMC window, which is 32T0 (=32*8 = 256)
// 3*T0 = PMC width
// 29*T0 = rest of PMC window (total 32T0 = 3+29)
// after the PMC, it directly goes to the password indication bit.
AddPatternPCF7931(8192 / 2 * T0_PCF + 319 * T0_PCF + 70, 3 * T0_PCF, 29 * T0_PCF, tab);
//password indication bit
AddBitPCF7931(1, tab, l, p);
AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition);
//password (on 56 bits)
AddBytePCF7931(pass[0], tab, l, p);
AddBytePCF7931(pass[1], tab, l, p);
AddBytePCF7931(pass[2], tab, l, p);
AddBytePCF7931(pass[3], tab, l, p);
AddBytePCF7931(pass[4], tab, l, p);
AddBytePCF7931(pass[5], tab, l, p);
AddBytePCF7931(pass[6], tab, l, p);
//programming mode (0 or 1)
AddBitPCF7931(0, tab, l, p);
AddBytePCF7931(pass[0], tab, offsetPulseWidth, offsetPulsePosition);
AddBytePCF7931(pass[1], tab, offsetPulseWidth, offsetPulsePosition);
AddBytePCF7931(pass[2], tab, offsetPulseWidth, offsetPulsePosition);
AddBytePCF7931(pass[3], tab, offsetPulseWidth, offsetPulsePosition);
AddBytePCF7931(pass[4], tab, offsetPulseWidth, offsetPulsePosition);
AddBytePCF7931(pass[5], tab, offsetPulseWidth, offsetPulsePosition);
AddBytePCF7931(pass[6], tab, offsetPulseWidth, offsetPulsePosition);
//programming mode (0 or 1) -> 0 = byte wise; 1 = block wise programming
AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition);
//block address on 6 bits
for (u = 0; u < 6; ++u) {
if (address & (1 << u)) { // bit 1
++parity;
AddBitPCF7931(1, tab, l, p);
AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition);
} else { // bit 0
AddBitPCF7931(0, tab, l, p);
AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition);
}
}
@ -405,56 +479,48 @@ static void RealWritePCF7931(uint8_t *pass, uint16_t init_delay, int32_t l, int3
for (u = 0; u < 4; ++u) {
if (byte & (1 << u)) { // bit 1
parity++;
AddBitPCF7931(1, tab, l, p);
AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition);
} else // bit 0
AddBitPCF7931(0, tab, l, p);
AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition);
}
//data on 8 bits
for (u = 0; u < 8; u++) {
if (data & (1 << u)) { // bit 1
parity++;
AddBitPCF7931(1, tab, l, p);
AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition);
} else //bit 0
AddBitPCF7931(0, tab, l, p);
AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition);
}
//parity bit
if ((parity % 2) == 0)
AddBitPCF7931(0, tab, l, p); //even parity
AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition); //even parity
else
AddBitPCF7931(1, tab, l, p);//odd parity
AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition);//odd parity
//time access memory
AddPatternPCF7931(5120 + 2680, 0, 0, tab);
//conversion of the scale time
for (u = 0; u < 500; ++u)
tab[u] = (tab[u] * 3) / 2;
//compensation of the counter reload
while (!comp) {
comp = 1;
for (u = 0; tab[u] != 0; ++u)
if (tab[u] > 0xFFFF) {
tab[u] -= 0xFFFF;
comp = 0;
}
}
// time access memory (640T0)
// Not sure why 335*T0, but should not matter. Since programming should be finished at that point
AddPatternPCF7931((640 + 335)* T0_PCF, 0, 0, tab);
SendCmdPCF7931(tab, ledcontrol);
}
/* Write on a byte of a PCF7931 tag
* @param address : address of the block to write
@param byte : address of the byte to write
@param data : data to write
* @param byte : address of the byte to write
* @param data : data to write
*/
void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol) {
void WritePCF7931(
uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7,
uint16_t init_delay,
int8_t offsetPulseWidth, int8_t offsetPulsePosition,
uint8_t address, uint8_t byte, uint8_t data,
bool ledcontrol) {
if (g_dbglevel >= DBG_INFO) {
Dbprintf("Initialization delay : %d us", init_delay);
Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", l, p);
Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", offsetPulseWidth, offsetPulsePosition);
}
Dbprintf("Password (LSB first on each byte): %02x %02x %02x %02x %02x %02x %02x", pass1, pass2, pass3, pass4, pass5, pass6, pass7);
@ -464,15 +530,15 @@ void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, ui
uint8_t password[7] = {pass1, pass2, pass3, pass4, pass5, pass6, pass7};
RealWritePCF7931(password, init_delay, l, p, address, byte, data, ledcontrol);
RealWritePCF7931(password, init_delay, offsetPulseWidth, offsetPulsePosition, address, byte, data, ledcontrol);
}
/* Send a trame to a PCF7931 tags
/* Send a frame to a PCF7931 tags
* @param tab : array of the data frame
*/
void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) {
void SendCmdPCF7931(uint32_t *tab, bool ledcontrol) {
uint16_t u = 0, tempo = 0;
if (g_dbglevel >= DBG_INFO) {
@ -485,6 +551,20 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) {
if (ledcontrol) LED_A_ON();
// rescale the values to match the time of the timer below.
for (u = 0; u < 500; ++u) {
tab[u] = (tab[u] * 3) / 2;
}
// compensation for the counter overflow
// only one overflow should be possible.
for (u = 0; tab[u] != 0; ++u)
if (tab[u] > 0xFFFF) {
tab[u] -= 0xFFFF;
break;
}
// steal this pin from the SSP and use it to control the modulation
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
@ -493,7 +573,7 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) {
AT91C_BASE_PMC->PMC_PCER |= (0x1 << AT91C_ID_TC0);
AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz
AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz (48Mhz clock, 32 = prescaler (div3))
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN;
// Assert a sync signal. This sets all timers to 0 on next active clock edge
@ -503,19 +583,19 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) {
for (u = 0; tab[u] != 0; u += 3) {
// modulate antenna
HIGH(GPIO_SSC_DOUT);
while (tempo != tab[u]) {
while ((uint32_t)tempo < tab[u]) {
tempo = AT91C_BASE_TC0->TC_CV;
}
// stop modulating antenna
LOW(GPIO_SSC_DOUT);
while (tempo != tab[u + 1]) {
while ((uint32_t)tempo < tab[u + 1]) {
tempo = AT91C_BASE_TC0->TC_CV;
}
// modulate antenna
HIGH(GPIO_SSC_DOUT);
while (tempo != tab[u + 2]) {
while ((uint32_t)tempo < tab[u + 2]) {
tempo = AT91C_BASE_TC0->TC_CV;
}
}
@ -528,32 +608,36 @@ void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol) {
}
/* Add a byte for building the data frame of PCF7931 tags
/* Add a byte for building the data frame of PCF7931 tags.
* See Datasheet of PCF7931 diagramm on page 8. This explains pulse widht & positioning
* Normally, no offset should be required.
* @param b : byte to add
* @param tab : array of the data frame
* @param l : offset on low pulse width
* @param p : offset on low pulse positioning
* @param offsetPulseWidth : offset on low pulse width in µs (default pulse widht is 6T0)
* @param offsetPulsePosition : offset on low pulse positioning in µs
*/
bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int32_t l, int32_t p) {
bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition) {
uint32_t u;
for (u = 0; u < 8; ++u) {
if (byte & (1 << u)) { //bit is 1
if (AddBitPCF7931(1, tab, l, p) == 1) return true;
AddBitPCF7931(1, tab, offsetPulseWidth, offsetPulsePosition);
} else { //bit is 0
if (AddBitPCF7931(0, tab, l, p) == 1) return true;
AddBitPCF7931(0, tab, offsetPulseWidth, offsetPulsePosition);
}
}
return false;
}
/* Add a bits for building the data frame of PCF7931 tags
/* Add a bits for building the data frame of PCF7931 tags.
* See Datasheet of PCF7931 diagramm on page 8. This explains pulse widht & positioning
* Normally, no offset should be required.
* @param b : bit to add
* @param tab : array of the data frame
* @param l : offset on low pulse width
* @param p : offset on low pulse positioning
* @param offsetPulseWidth : offset on low pulse width in µs (default pulse widht is 6T0)
* @param offsetPulsePosition : offset on low pulse positioning in µs
*/
bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) {
bool AddBitPCF7931(bool b, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition) {
uint8_t u = 0;
//we put the cursor at the last value of the array
@ -561,23 +645,22 @@ bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) {
if (b == 1) { //add a bit 1
if (u == 0)
tab[u] = 34 * T0_PCF + p;
tab[u] = 34 * T0_PCF + offsetPulsePosition;
else
tab[u] = 34 * T0_PCF + tab[u - 1] + p;
tab[u] = 34 * T0_PCF + tab[u - 1] + offsetPulsePosition;
tab[u + 1] = 6 * T0_PCF + tab[u] + offsetPulseWidth;
tab[u + 2] = 88 * T0_PCF + tab[u + 1] - offsetPulseWidth - offsetPulsePosition;
tab[u + 1] = 6 * T0_PCF + tab[u] + l;
tab[u + 2] = 88 * T0_PCF + tab[u + 1] - l - p;
return false;
} else { //add a bit 0
if (u == 0)
tab[u] = 98 * T0_PCF + p;
tab[u] = 98 * T0_PCF + offsetPulsePosition;
else
tab[u] = 98 * T0_PCF + tab[u - 1] + p;
tab[u] = 98 * T0_PCF + tab[u - 1] + offsetPulsePosition;
tab[u + 1] = 6 * T0_PCF + tab[u] + offsetPulseWidth;
tab[u + 2] = 24 * T0_PCF + tab[u + 1] - offsetPulseWidth - offsetPulsePosition;
tab[u + 1] = 6 * T0_PCF + tab[u] + l;
tab[u + 2] = 24 * T0_PCF + tab[u + 1] - l - p;
return false;
}
return true;
}
@ -592,8 +675,8 @@ bool AddPatternPCF7931(uint32_t a, uint32_t b, uint32_t c, uint32_t *tab) {
uint32_t u = 0;
for (u = 0; tab[u] != 0; u += 3) {} //we put the cursor at the last value of the array
tab[u] = (u == 0) ? a : a + tab[u - 1];
tab[u + 1] = b + tab[u];
tab[u] = (u == 0) ? a : a + tab[u - 1]; // if it is the first value of the array, nothing needs to be added.
tab[u + 1] = b + tab[u]; // otherwise always add up the values, because later on it is compared to a counter
tab[u + 2] = c + tab[u + 1];
return true;

View file

@ -18,14 +18,20 @@
#include "common.h"
typedef enum{
FALLING,
RISING
} EdgeType;
size_t DemodPCF7931(uint8_t **outBlocks, bool ledcontrol);
bool IsBlock0PCF7931(uint8_t *block);
bool IsBlock1PCF7931(const uint8_t *block);
void ReadPCF7931(bool ledcontrol);
void SendCmdPCF7931(const uint32_t *tab, bool ledcontrol);
bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int32_t l, int32_t p);
bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p);
void SendCmdPCF7931(uint32_t *tab, bool ledcontrol);
bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition);
bool AddBitPCF7931(bool b, uint32_t *tab, int8_t offsetPulseWidth, int8_t offsetPulsePosition);
bool AddPatternPCF7931(uint32_t a, uint32_t b, uint32_t c, uint32_t *tab);
void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol);
void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int8_t offsetPulseWidth, int8_t offsetPulsePosition, uint8_t address, uint8_t byte, uint8_t data, bool ledcontrol);
#endif

View file

@ -49,9 +49,9 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_send_request_iso14a");
uint8_t * buf1 = BigBuf_malloc(ISO7816_MAX_FRAME);
uint8_t * buf2 = BigBuf_malloc(ISO7816_MAX_FRAME);
if(buf1 == NULL || buf2 == NULL){
uint8_t *buf1 = BigBuf_malloc(ISO7816_MAX_FRAME);
uint8_t *buf2 = BigBuf_malloc(ISO7816_MAX_FRAME);
if (buf1 == NULL || buf2 == NULL) {
res = PM3_EMALLOC;
goto out;
}
@ -103,19 +103,19 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re
nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf);
bool is_cmd_check = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK;
if(is_cmd_check && break_on_nr_mac){
if (is_cmd_check && break_on_nr_mac) {
memcpy(response, nfc_tx_buf, nfc_tx_len);
*response_len = nfc_tx_len;
if (g_dbglevel >= DBG_INFO) {
DbpString("NR-MAC: ");
Dbhexdump((*response_len)-1, response+1, false);
Dbhexdump((*response_len) - 1, response + 1, false);
}
res = PM3_SUCCESS;
goto out;
}
bool is_cmd_update = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE;
if(is_cmd_update && prevent_epurse_update && nfc_tx_buf[0] == 0x87 && nfc_tx_buf[1] == 0x02){
if (is_cmd_update && prevent_epurse_update && nfc_tx_buf[0] == 0x87 && nfc_tx_buf[1] == 0x02) {
// block update(2) command and fake the response to prevent update of epurse
// NFC TX BUFFERS PREPARED BY SAM LOOKS LIKE:
@ -124,8 +124,8 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re
// NFC RX BUFFERS EXPECTED BY SAM WOULD LOOK LIKE:
// #2(FF FF FF FF) #1(C9 FD FF FF) 3A 47
memcpy(nfc_rx_buf+0, nfc_tx_buf+6, 4);
memcpy(nfc_rx_buf+4, nfc_tx_buf+0, 4);
memcpy(nfc_rx_buf + 0, nfc_tx_buf + 6, 4);
memcpy(nfc_rx_buf + 4, nfc_tx_buf + 0, 4);
AddCrc(nfc_rx_buf, 8);
nfc_rx_len = 10;
@ -155,7 +155,7 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re
}
if (res != PM3_SUCCESS ) {
if (res != PM3_SUCCESS) {
res = PM3_ECARDEXCHANGE;
goto out;
}
@ -358,7 +358,7 @@ int sam_picopass_get_pacs(PacketCommandNG *c) {
// implicit StartSspClk() happens here
Iso15693InitReader();
if(!select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod)){
if (!select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod)) {
goto err;
}

View file

@ -129,9 +129,9 @@ static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t r
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_send_request_iso14a");
uint8_t * buf1 = BigBuf_malloc(ISO7816_MAX_FRAME);
uint8_t * buf2 = BigBuf_malloc(ISO7816_MAX_FRAME);
if(buf1 == NULL || buf2 == NULL){
uint8_t *buf1 = BigBuf_malloc(ISO7816_MAX_FRAME);
uint8_t *buf2 = BigBuf_malloc(ISO7816_MAX_FRAME);
if (buf1 == NULL || buf2 == NULL) {
res = PM3_EMALLOC;
goto out;
}

View file

@ -658,7 +658,7 @@ void rdv40_spiffs_safe_print_tree(void) {
}
}
Dbprintf("[%04x] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink);
Dbprintf("[%04u] " _YELLOW_("%5i") " B |-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink);
printed = true;
}
if (printed == false) {

View file

@ -548,7 +548,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_INTERNAL);
}
// this checks for overflow of the multiplication of block_count+1 with SPIFFS_PAGES_PER_BLOCK(fs)
if (((uint32_t)(-1)) / SPIFFS_PAGES_PER_BLOCK(fs) > (block_count+1)) {
if (((uint32_t)(-1)) / SPIFFS_PAGES_PER_BLOCK(fs) > (block_count + 1)) {
// checking with +1 block count to avoid overflow also in inner loop, which adds one page...
// would exceed value storable in uint32_t
SPIFFS_DBG("Overflow: pages per block %04x with block count "_SPIPRIbl" results in overflow\n", SPIFFS_PAGES_PER_BLOCK(fs), block_count);
@ -556,13 +556,13 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
}
// because loop indices are using spiffs_page_ix type,
// that type can hold a large enough value
if (total_blocks > ((spiffs_page_ix)-1)) {
if (total_blocks > ((spiffs_page_ix) - 1)) {
SPIFFS_DBG("Avoiding infinite loop, total_blocks "_SPIPRIpg" too large for spiffs_page_ix type\n", total_blocks);
SPIFFS_CHECK_RES(SPIFFS_ERR_INTERNAL);
}
// because loop indices are using spiffs_page_ix type,
// that type can hold a large enough value
if (total_blocks_plus_one_page > ((spiffs_page_ix)-1) || total_blocks_plus_one_page < total_blocks) {
if (total_blocks_plus_one_page > ((spiffs_page_ix) - 1) || total_blocks_plus_one_page < total_blocks) {
SPIFFS_DBG("Avoiding infinite loop, total_blocks_plus_one_page "_SPIPRIpg" too large for spiffs_page_ix type\n", total_blocks_plus_one_page);
SPIFFS_CHECK_RES(SPIFFS_ERR_INTERNAL);
}
@ -586,7 +586,7 @@ static s32_t spiffs_page_consistency_check_i(spiffs *fs) {
0);
// traverse each page except for lookup pages
spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block;
while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block+1)) {
while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block + 1)) {
//if ((cur_pix & 0xff) == 0)
// SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n",
// cur_pix, cur_block, total_blocks, block_count);

View file

@ -887,7 +887,12 @@ ifneq (,$(INSTALLBIN))
endif
ifneq (,$(INSTALLSHARE))
$(Q)$(INSTALLSUDO) $(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)
# hack ahead: inject installation path into pm3_resources.py
$(Q)sed -i 's|^TOOLS_PATH \?= \?None|TOOLS_PATH="$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)"|' pyscripts/pm3_resources.py
$(Q)sed -i 's|^DICTS_PATH \?= \?None|DICTS_PATH="$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)/dictionaries"|' pyscripts/pm3_resources.py
$(Q)$(INSTALLSUDO) $(CP) $(INSTALLSHARE) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)
$(Q)sed -i 's|^TOOLS_PATH \?=.*|TOOLS_PATH = None|' pyscripts/pm3_resources.py
$(Q)sed -i 's|^DICTS_PATH \?=.*|DICTS_PATH = None|' pyscripts/pm3_resources.py
endif
@true

View file

@ -180,8 +180,10 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab
// parse params
for (int i = 0; i < len; i++) {
switch (state) {
case PS_FIRST: // first char
case PS_FIRST: { // first char
if (!clueData || str[i] == '-') { // first char before space is '-' - next element - option OR not "clueData" for not-option fields
state = PS_OPTION;
@ -193,24 +195,32 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab
}
}
spaceptr = NULL;
case PS_ARGUMENT:
if (state == PS_FIRST)
}
case PS_ARGUMENT: {
if (state == PS_FIRST) {
state = PS_ARGUMENT;
if (str[i] == '"') {
}
if (str[i] == '"' || str[i] == '\'') {
state = PS_QUOTE;
break;
}
if (isSpace(str[i])) {
spaceptr = bufptr;
state = PS_FIRST;
}
*bufptr = str[i];
bufptr++;
break;
case PS_OPTION:
}
case PS_OPTION: {
if (isSpace(str[i])) {
state = PS_FIRST;
*bufptr = 0x00;
bufptr++;
argv[argc++] = bufptr;
@ -220,17 +230,22 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab
*bufptr = str[i];
bufptr++;
break;
case PS_QUOTE:
if (str[i] == '"') {
}
case PS_QUOTE: {
if (str[i] == '"' || str[i] == '\'') {
*bufptr++ = 0x00;
state = PS_FIRST;
} else {
if (isSpace(str[i]) == false) {
*bufptr++ = str[i];
}
// if (isSpace(str[i]) == false) {
*bufptr++ = str[i];
// }
}
break;
}
}
if (bufptr > bufptrend) {
PrintAndLogEx(ERR, "ERROR: Line too long\n");
fflush(stdout);
@ -296,8 +311,9 @@ int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int
int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) {
*datalen = 0;
if (!argstr->count)
if (!argstr->count) {
return 0;
}
uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0};
int ibuf = 0;
@ -319,8 +335,9 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int
ibuf = MIN(ibuf, (sizeof(tmpstr) / 2));
tmpstr[ibuf] = 0;
if (ibuf == 0)
if (ibuf == 0) {
return 0;
}
if (ibuf > maxdatalen) {
PrintAndLogEx(ERR, "Parameter error: string too long (%i chars), expected MAX %i chars\n", ibuf, maxdatalen);

View file

@ -2175,6 +2175,7 @@ D144BD193063
# Brazil transport Sec 8 / A
50d4c54fcdf5
#
# TEKKEN 6 Namco Data Card
# Bandai Namco Passport [fka Banapassport] / Sega Aime Card
# Dumped on the Flipper Devices Discord Server
6090D00632F5
@ -2211,6 +2212,40 @@ C8382A233993
7B304F2A12A6
FC9418BF788B
#
# Super Street Fighter 4 Capcom NESYS Card
4B6F74696174
6F746961744B
4176696E7520
76696E752041
576C61737265
6C6173726557
416962616320
696261632041
42622074656E
622074656E42
416174363030
617436303041
5475206F7469
75206F746954
41726576696E
726576696E41
4B63206C6173
63206C61734B
41656E696261
656E69626141
5A3030622074
30306220745A
557469617436
746961743655
48696E75206F
696E75206F48
496173726576
617372657649
52626163206C
626163206C52
4F2074656E69
2074656E694F
#
# Guest Cashless Prepaid Arcade Payment Cards
168168168168
198407157610

View file

@ -19,7 +19,7 @@ desc = [[
]]
usage = [[
script run t55_chk [-s start_year] [-e end_year] [-d | -y]
script run lf_t55xx_chk [-s start_year] [-e end_year] [-d | -y]
]]
options = [[
-h this help
@ -29,10 +29,10 @@ options = [[
-y search method: YYYYMMDD
]]
examples = [[
script run t55_chk -s 1999 -d -> start 1999, end is current year, method 01011999
script run t55_chk -s 1999 -y -> start 1999, end is current year, method 19990101
script run t55_chk -s 1999 -e 2001 -y -> start 1999, end year 2001, method 19990101
script run t55_chk -s 1999 -e 2001 -d -> start 1999, end year 2001, method 01011999
script run lf_t55xx_chk -s 1999 -d -> start 1999, end is current year, method 01011999
script run lf_t55xx_chk -s 1999 -y -> start 1999, end is current year, method 19990101
script run lf_t55xx_chk -s 1999 -e 2001 -y -> start 1999, end year 2001, method 19990101
script run lf_t55xx_chk -s 1999 -e 2001 -d -> start 1999, end year 2001, method 01011999
]]
local function help()

View file

@ -14,10 +14,10 @@ desc = [[
useful if the password is, for example, a date of birth.
]]
usage = [[
script run t55_chk_date
script run lf_t55xx_chk_date
]]
arguments = [[
script run t55_chk_date -h : this help
script run lf_t55xx_chk_date -h : this help
]]
local DEBUG = true

View file

@ -1,101 +0,0 @@
local getopt = require('getopt')
local utils = require('utils')
local ac = require('ansicolors')
local os = require('os')
local dash = string.rep('--', 32)
local dir = os.getenv('HOME') .. '/.proxmark3/logs/'
local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+"))
local command = core.console
author = ' Author: jareckib - 15.02.2025'
version = ' version v1.00'
desc = [[
This simple script first checks if a password has been set for the T5577.
It uses the dictionary t55xx_default_pwds.dic for this purpose. If a password
is found, it uses the wipe command to erase the T5577. Then the reanimation
procedure is applied. If the password is not found or doesn't exist the script
only performs the reanimation procedure. The script revives 99% of blocked tags.
]]
usage = [[
script run t55_fix
]]
arguments = [[
script run t55_fix -h : this help
]]
local function help()
print()
print(author)
print(version)
print(desc)
print(ac.cyan..' Usage'..ac.reset)
print(usage)
print(ac.cyan..' Arguments'..ac.reset)
print(arguments)
end
local function read_log_file(logfile)
local file = io.open(logfile, "r")
if not file then
return nil
end
local content = file:read("*all")
file:close()
return content
end
local function extract_password(log_content)
for line in log_content:gmatch("[^\r\n]+") do
local password = line:match('%[%+%] found valid password: %[ (%x%x%x%x%x%x%x%x) %]')
if password then
return password
end
end
return nil
end
local function reanimate_t5577(password)
if password then
command('clear')
print(dash)
print(" Using found password to wipe: " .. password)
print(dash)
command('lf t55 wipe -p ' .. password)
else
command('clear')
print(dash)
print(ac.yellow.." No valid password found, proceeding with reanimation."..ac.reset)
print(dash)
end
command('lf t55 write -b 0 -d 000880E8 -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -t -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -t -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -t -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -t -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --r0 -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --r1 -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --r2 -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --r3 -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --pg1 --r0 -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --pg1 --r1 -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --pg1 --r2 -p 00000000')
command('lf t55 write -b 0 -d 000880E0 --pg1 --r3 -p 00000000')
command('lf t55 detect')
local file = io.open(logfile, "w+")
file:write("")
file:close()
print(dash)
print('all done!')
end
local function main(args)
for o, a in getopt.getopt(args, 'h') do
if o == 'h' then return help() end
end
command('lf t55 chk')
local log_content = read_log_file(logfile)
local password = log_content and extract_password(log_content) or nil
reanimate_t5577(password)
end
main(args)

View file

@ -1,4 +1,5 @@
#!/usr/bin/env python3
"""This script recovers Fudan FM11RF08S cards, including functionalities for Bambu tags decoding."""
# ------------------------------------------------------------------------------
# Imports
@ -43,17 +44,18 @@ try:
from colors import color
except ModuleNotFoundError:
def color(s, fg=None):
"""Return the string as such, without color."""
_ = fg
return str(s)
def initlog():
"""Print and Log: init globals
"""Print and Log: init globals.
globals:
- logbuffer (W)
- logfile (W)
"""
globals:
- logbuffer (W)
- logfile (W)
"""
global logbuffer
global logfile
logbuffer = ''
@ -61,12 +63,12 @@ globals:
def startlog(uid, dpath, append=False):
"""Print and Log: set logfile and flush logbuffer
"""Print and Log: set logfile and flush logbuffer.
globals:
- logbuffer (RW)
- logfile (RW)
"""
globals:
- logbuffer (RW)
- logfile (RW)
"""
global logfile
global logbuffer
@ -81,13 +83,12 @@ globals:
def lprint(s='', end='\n', flush=False, prompt="[" + color("=", fg="yellow") + "] ", log=True):
"""Print and Log
globals:
- logbuffer (RW)
- logfile (R)
"""
"""Print and Log.
globals:
- logbuffer (RW)
- logfile (R)
"""
s = f"{prompt}" + f"\n{prompt}".join(s.split('\n'))
print(s, end=end, flush=flush)
@ -102,11 +103,11 @@ globals:
def main():
"""== MAIN ==
"""== MAIN ==.
globals:
- p (W)
"""
globals:
- p (W)
"""
global p
p = pm3.pm3() # console interface
initlog()
@ -143,7 +144,7 @@ globals:
else:
# FIXME: recovery() is only for RF08S. TODO for the other ones with a "darknested" attack
keyfile = recoverKeys(uid=uid, kdf=[["Bambu v1", kdfBambu1]])
if keyfile == False:
if keyfile is False:
lprint("Script failed - aborting")
return
key = loadKeys(keyfile)
@ -181,11 +182,11 @@ globals:
def getPrefs():
"""Get PM3 preferences
"""Get PM3 preferences.
globals:
- p (R)
"""
globals:
- p (R)
"""
p.console("prefs show --json")
prefs = json.loads(p.grabbed_output)
dpath = prefs['file.default.dumppath'] + os.path.sep
@ -193,7 +194,7 @@ globals:
def checkVer():
"""Assert python version"""
"""Assert python version."""
required_version = (3, 8)
if sys.version_info < required_version:
print(f"Python version: {sys.version}")
@ -203,7 +204,7 @@ def checkVer():
def parseCli():
"""Parse the CLi arguments"""
"""Parse the CLi arguments."""
parser = argparse.ArgumentParser(description='Full recovery of Fudan FM11RF08S cards.')
parser.add_argument('-n', '--nokeys', action='store_true', help='extract data even if keys are missing')
@ -222,15 +223,15 @@ def parseCli():
def getBackdoorKey():
"""Find backdoor key
[=] # | sector 00 / 0x00 | ascii
[=] ----+-------------------------------------------------+-----------------
[=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \\........Y.%._p.
r"""Find backdoor key.
globals:
- p (R)
"""
[=] # | sector 00 / 0x00 | ascii
[=] ----+-------------------------------------------------+-----------------
[=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \........Y.%._p.
globals:
- p (R)
"""
# FM11RF08S FM11RF08 FM11RF32
dklist = ["A396EFA4E24F", "A31667A8CEC1", "518b3354E760"]
@ -259,14 +260,14 @@ globals:
def getUIDfromBlock0(blk0):
"""Extract UID from block 0"""
"""Extract UID from block 0."""
uids = blk0[0:11] # UID string : "11 22 33 44"
uid = bytes.fromhex(uids.replace(' ', '')) # UID (bytes) : 11223344
return uid
def decodeBlock0(blk0):
"""Extract data from block 0"""
"""Extract data from block 0."""
lprint()
lprint(" UID BCC ++---- RF08* ID -----++")
lprint(" ! ! SAK !! !!")
@ -346,7 +347,7 @@ def decodeBlock0(blk0):
def fudanValidate(blk0, live=False):
"""Fudan validation"""
"""Fudan validation."""
url = "https://rfid.fm-uivs.com/nfcTools/api/M1KeyRest"
hdr = "Content-Type: application/text; charset=utf-8"
post = f"{blk0.replace(' ', '')}"
@ -387,10 +388,10 @@ def fudanValidate(blk0, live=False):
def loadKeys(keyfile):
"""Load keys from file
"""Load keys from file.
If keys cannot be loaded AND --recover is specified, then run key recovery
"""
If keys cannot be loaded AND --recover is specified, then run key recovery
"""
key = [[b'' for _ in range(2)] for _ in range(17)] # create a fresh array
lprint("\nLoad keys from file... " + color(f"{keyfile}", fg="yellow"))
@ -408,15 +409,15 @@ If keys cannot be loaded AND --recover is specified, then run key recovery
def recoverKeys(uid, kdf=[[]]):
"""Run key recovery script"""
"""Run key recovery script."""
badrk = 0 # 'bad recovered key' count (ie. not recovered)
keys = False
lprint(f"\nTrying KDFs:");
keys = []
lprint("\nTrying KDFs:")
for fn in kdf:
lprint(f" {fn[0]:s}", end='')
keys = fn[1](uid)
if keys != False:
if len(keys) > 0:
lprint(" .. Success", prompt='')
break
lprint(" .. Fail", prompt='')
@ -427,7 +428,7 @@ def recoverKeys(uid, kdf=[[]]):
r = recovery(quiet=False, keyset=keys)
lprint('`-._,-\'"`-._,-"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,-\'"`-._,')
if r == False:
if r is False:
return False
keyfile = r['keyfile']
@ -453,14 +454,28 @@ def recoverKeys(uid, kdf=[[]]):
lprint("", prompt='')
return keyfile
def kdfBambu1(uid):
"""Derive keys from a given UID using the Bambu HKDF algorithm and validates the card data.
This function generates two keys (keyA and keyB) using the Bambu HKDF algorithm with a predefined salt and context.
It then attempts to read block 13 from sector 3 of the card using keyA. If successful, it decodes the data
and checks if it matches a specific date format. If the data is valid, it returns a list of derived keys.
Args:
uid (bytes): The UID of the card.
Returns:
list: A list of derived keys if the card data is valid.
bool: False if any step in the process fails.
"""
from Cryptodome.Protocol.KDF import HKDF
from Cryptodome.Hash import SHA256
from Cryptodome.Hash import SHA256
# Generate all keys
try:
# extracted from Bambu firmware
salt = bytes([0x9a,0x75,0x9c,0xf2,0xc4,0xf7,0xca,0xff,0x22,0x2c,0xb9,0x76,0x9b,0x41,0xbc,0x96])
salt = bytes([0x9a, 0x75, 0x9c, 0xf2, 0xc4, 0xf7, 0xca, 0xff, 0x22, 0x2c, 0xb9, 0x76, 0x9b, 0x41, 0xbc, 0x96])
keyA = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-A\0")
keyB = HKDF(uid, 6, salt, SHA256, 16, context=b"RFID-B\0")
except Exception as e:
@ -469,7 +484,7 @@ def kdfBambu1(uid):
# --- Grab block 13 (in sector 3) ---
cmd = f"hf mf rdbl -c 0 --key {keyA[3].hex()} --blk 12"
#lprint(f" `{cmd}`", flush=True, log=False, end='')
# lprint(f" `{cmd}`", flush=True, log=False, end='')
for retry in range(5):
p.console(cmd)
@ -502,13 +517,13 @@ def kdfBambu1(uid):
return keys
def verifyKeys(key):
"""Verify keys
globals:
- p (R)
"""
"""Verify keys.
globals:
- p (R)
"""
badk = 0
mad = False
@ -563,16 +578,15 @@ globals:
def readBlocks(bdkey, fast=False):
r"""Read all block data - INCLUDING advanced verification blocks.
[=] # | sector 00 / 0x00 | ascii
[=] ----+-------------------------------------------------+-----------------
[=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \........Y.%._p.
globals:
- p (R)
"""
Read all block data - INCLUDING advanced verification blocks
[=] # | sector 00 / 0x00 | ascii
[=] ----+-------------------------------------------------+-----------------
[=] 0 | 5C B4 9C A6 D2 08 04 00 04 59 92 25 BF 5F 70 90 | \\........Y.%._p.
globals:
- p (R)
"""
data = []
blkn = list(range(0, 63 + 1)) + list(range(128, 135 + 1))
@ -634,9 +648,10 @@ globals:
def patchKeys(data, key):
"""Patch keys in to data
3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i......
"""
"""Patch keys in to data.
3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i......
"""
lprint("\nPatching keys in to data")
for sec in range(0, 16 + 1):
@ -662,7 +677,7 @@ def patchKeys(data, key):
def dumpData(data, blkn):
"""Dump data"""
"""Dump data."""
lprint()
lprint("===========")
lprint(" Card Data")
@ -708,14 +723,14 @@ def detectBambu(data):
def dumpBambu(data):
"""Dump bambu details
"""Dump bambu details.
https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md
https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md
6 18 30 42 53
| | | | |
3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i......
"""
6 18 30 42 53
| | | | |
3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i......
"""
try:
lprint()
lprint("===========")
@ -833,14 +848,14 @@ https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md
# The Access bits on both (used) Sectors is the same: 78 77 88
# Let's reorganise that according to the official spec Fig 9.
# Let's reorganize that according to the official spec Fig 9.
# Access C1 C2 C3
# ========== ===========
# 78 77 88 --> 78 87 87
# ab cd ef --> cb fa ed
# The second nybble of each byte is the inverse of the first nybble.
# It is there to trap tranmission errors, so we can just ignore it/them.
# It is there to trap transmission errors, so we can just ignore it/them.
# So our Access Control value is : {c, f, e} == {7, 8, 8}
@ -903,13 +918,13 @@ https://github.com/Bambu-Research-Group/RFID-Tag-Guide/blob/main/README.md
# IF YOU PLAN TO CHANGE ACCESS BITS, RTFM, THERE IS MUCH TO CONSIDER !
# ==============================================================================
def dumpAcl(data):
"""Dump ACL
"""Dump ACL.
6 18 24 27 30 33 42 53
| | | | | | | |
3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i......
ab cd ef
"""
6 18 24 27 30 33 42 53
| | | | | | | |
3 | 00 00 00 00 00 00 87 87 87 69 00 00 00 00 00 00 | .........i......
ab cd ef
"""
aclkh = [] # key header
aclk = [""] * 8 # key lookup
aclkx = [] # key output
@ -1010,10 +1025,10 @@ def dumpAcl(data):
def diskDump(data, uid, dpath):
"""Full Dump"""
"""Full Dump."""
dump18 = f'{dpath}hf-mf-{uid.hex().upper()}-dump18.bin'
lprint(f'\nDump card data to file... ' + color(dump18, fg='yellow'))
lprint('\nDump card data to file... ' + color(dump18, fg='yellow'))
bad = False
try:
@ -1037,12 +1052,11 @@ def diskDump(data, uid, dpath):
def dumpMad(dump18):
"""Dump MAD
globals:
- p (R)
"""
"""Dump MAD.
globals:
- p (R)
"""
lprint()
lprint("====================================")
lprint(" MiFare Application Directory (MAD)")

View file

@ -1,17 +1,18 @@
#!/usr/bin/env python3
"""
Combine several attacks to recover all FM11RF08S keys.
# Combine several attacks to recover all FM11RF08S keys
#
# Conditions:
# * Presence of the backdoor with known key
#
# Duration strongly depends on some key being reused and where.
# Examples:
# * 32 random keys: ~20 min
# * 16 random keys with keyA==keyB in each sector: ~30 min
# * 24 random keys, some reused across sectors: <1 min
#
# Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info
Conditions:
* Presence of the backdoor with known key
Duration strongly depends on some key being reused and where.
Examples:
* 32 random keys: ~20 min
* 16 random keys with keyA==keyB in each sector: ~30 min
* 24 random keys, some reused across sectors: <1 min
Doegox, 2024, cf https://eprint.iacr.org/2024/1275 for more info
"""
import os
import sys
@ -19,13 +20,17 @@ import time
import subprocess
import argparse
import json
import re
import pm3
from pm3_resources import find_tool, find_dict
# optional color support
try:
# pip install ansicolors
from colors import color
except ModuleNotFoundError:
def color(s, fg=None):
"""Return the string as such, without color."""
_ = fg
return str(s)
@ -42,41 +47,52 @@ BACKDOOR_KEYS = ["A396EFA4E24F", "A31667A8CEC1", "518B3354E760"]
NUM_SECTORS = 16
NUM_EXTRA_SECTORS = 1
DICT_DEF = "mfc_default_keys.dic"
DEFAULT_KEYS = set()
if __name__ == '__main__':
DIR_PATH = os.path.dirname(os.path.abspath(sys.argv[0]))
else:
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
if os.path.basename(os.path.dirname(DIR_PATH)) == 'client':
# dev setup
TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH,
"..", "..", "tools", "mfc", "card_only"))
DICT_DEF_PATH = os.path.normpath(os.path.join(DIR_PATH,
"..", "dictionaries", DICT_DEF))
else:
# assuming installed
TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH,
"..", "tools"))
DICT_DEF_PATH = os.path.normpath(os.path.join(DIR_PATH,
"dictionaries", DICT_DEF))
tools = {
"staticnested_1nt": os.path.join(f"{TOOLS_PATH}", "staticnested_1nt"),
"staticnested_2x1nt": os.path.join(f"{TOOLS_PATH}", "staticnested_2x1nt_rf08s"),
"staticnested_2x1nt1key": os.path.join(f"{TOOLS_PATH}", "staticnested_2x1nt_rf08s_1key"),
}
for tool, bin in tools.items():
if not os.path.isfile(bin):
if os.path.isfile(bin + ".exe"):
tools[tool] = bin + ".exe"
else:
print(f"Cannot find {bin}, abort!")
exit()
staticnested_1nt_path = find_tool("staticnested_1nt")
staticnested_2x1nt_path = find_tool("staticnested_2x1nt_rf08s")
staticnested_2x1nt1key_path = find_tool("staticnested_2x1nt_rf08s_1key")
def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debug=False, supply_chain=False, quiet=True, keyset=False):
def match_key(line):
"""
Extract a 12-character hexadecimal key from a given string.
Args:
line (str): The input string to search for the hexadecimal key.
Returns:
str or None: The 12-character hexadecimal key in uppercase if found, otherwise None.
"""
match = re.search(r'([0-9a-fA-F]{12})', line)
if match:
return match.group(1).upper()
else:
return None
def recovery(init_check=False, final_check=False, keep=False, no_oob=False,
debug=False, supply_chain=False, quiet=True, keyset=[]):
"""
Perform recovery operation for FM11RF08S cards.
Args:
init_check (bool): If True, check for default keys initially.
final_check (bool): If True, perform a final check and dump keys.
keep (bool): If True, keep the generated dictionaries after processing.
no_oob (bool): If True, do not include out-of-bounds sectors.
debug (bool): If True, print debug information.
supply_chain (bool): If True, use supply-chain attack data.
quiet (bool): If True, suppress output messages.
keyset (list): A list of key pairs to use for the recovery process.
Returns:
dict: A dictionary containing the following keys:
- 'keyfile': Path to the generated binary key file.
- 'found_keys': List of found keys for each sector.
- 'dump_file': Path to the generated dump file.
- 'data': List of data blocks for each sector.
"""
def show(s='', prompt="[" + color("=", fg="yellow") + "] ", **kwargs):
if not quiet:
s = f"{prompt}" + f"\n{prompt}".join(s.split('\n'))
@ -107,10 +123,10 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
found_keys = [["", ""] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)]
if keyset != False:
n = min(len(found_keys),len(keyset))
if len(keyset) > 0:
n = min(len(found_keys), len(keyset))
show(f"{n} Key pairs supplied: ")
for i in range(0, n):
for i in range(n):
found_keys[i] = keyset[i]
show(f" Sector {i:2d} : A = {found_keys[i][0]:12s} B = {found_keys[i][1]:12s}")
@ -136,8 +152,9 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
for line in p.grabbed_output.split('\n'):
if "Wrong" in line or "error" in line:
break
if "Saved" in line:
nonces_with_data = line[line.index("`"):].strip("`")
matched = "Saved to json file "
if matched in line:
nonces_with_data = line[line.index(matched)+len(matched):]
if nonces_with_data != "":
break
@ -171,8 +188,8 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
data[blk] = dict_nwd["blocks"][f"{blk}"]
show("Generating first dump file")
dumpfile = f"{save_path}hf-mf-{uid:08X}-dump.bin"
with (open(dumpfile, "wb")) as f:
dump_file = f"{save_path}hf-mf-{uid:08X}-dump.bin"
with (open(dump_file, "wb")) as f:
for sec in range(NUM_SECTORS):
for b in range(4):
d = data[(sec * 4) + b]
@ -185,7 +202,7 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
kb = "FFFFFFFFFFFF"
d = ka + d[12:20] + kb
f.write(bytes.fromhex(d))
show(f"Data has been dumped to `{dumpfile}`")
show(f"Data has been dumped to `{dump_file}`")
elapsed_time1 = time.time() - start_time
minutes = int(elapsed_time1 // 60)
@ -193,14 +210,18 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
show("----Step 1: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
color(f"{seconds:2}", fg="yellow") + " seconds -----------")
if os.path.isfile(DICT_DEF_PATH):
show(f"Loading {DICT_DEF}")
with open(DICT_DEF_PATH, 'r', encoding='utf-8') as file:
dict_def = "mfc_default_keys.dic"
try:
dict_path = find_dict(dict_def)
with open(dict_path, 'r', encoding='utf-8') as file:
for line in file:
if line[0] != '#' and len(line) >= 12:
DEFAULT_KEYS.add(line[:12])
else:
show(f"Warning, {DICT_DEF} not found.")
show(f"Loaded {dict_def}")
except FileNotFoundError:
show(f"Warning, {dict_def} not found.")
except Exception as e:
raise Exception(f"Error loading {dict_def}: {e}")
dict_dnwd = None
def_nt = ["" for _ in range(NUM_SECTORS)]
@ -233,12 +254,12 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
continue
if found_keys[sec][0] == "" and found_keys[sec][1] == "" and nt[sec][0] != nt[sec][1]:
for key_type in [0, 1]:
cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}",
cmd = [staticnested_1nt_path, f"{uid:08X}", f"{real_sec}",
nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]]
if debug:
print(' '.join(cmd))
subprocess.run(cmd, capture_output=True)
cmd = [tools["staticnested_2x1nt"],
cmd = [staticnested_2x1nt_path,
f"keys_{uid:08x}_{real_sec:02}_{nt[sec][0]}.dic", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][1]}.dic"]
if debug:
print(' '.join(cmd))
@ -254,15 +275,16 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
all_keys.update(keys_set)
if dict_dnwd is not None and sec < NUM_SECTORS:
# Prioritize keys from supply-chain attack
cmd = [tools["staticnested_2x1nt1key"], def_nt[sec], "FFFFFFFFFFFF",
cmd = [staticnested_2x1nt1key_path, def_nt[sec], "FFFFFFFFFFFF",
f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic"]
if debug:
print(' '.join(cmd))
result = subprocess.run(cmd, capture_output=True, text=True).stdout
keys_def_set = set()
for line in result.split('\n'):
if "MATCH:" in line:
keys_def_set.add(line[12:])
matched = match_key(line)
if matched is not None:
keys_def_set.add(matched)
keys_set.difference_update(keys_def_set)
else:
# Prioritize default keys
@ -285,7 +307,7 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
key_type = 0
else:
key_type = 1
cmd = [tools["staticnested_1nt"], f"{uid:08X}", f"{real_sec}",
cmd = [staticnested_1nt_path, f"{uid:08X}", f"{real_sec}",
nt[sec][key_type], nt_enc[sec][key_type], par_err[sec][key_type]]
if debug:
print(' '.join(cmd))
@ -299,15 +321,16 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
all_keys.update(keys_set)
if dict_dnwd is not None and sec < NUM_SECTORS:
# Prioritize keys from supply-chain attack
cmd = [tools["staticnested_2x1nt1key"], def_nt[sec], "FFFFFFFFFFFF",
cmd = [staticnested_2x1nt1key_path, def_nt[sec], "FFFFFFFFFFFF",
f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic"]
if debug:
print(' '.join(cmd))
result = subprocess.run(cmd, capture_output=True, text=True).stdout
keys_def_set = set()
for line in result.split('\n'):
if "MATCH:" in line:
keys_def_set.add(line[12:])
matched = match_key(line)
if matched is not None:
keys_def_set.add(matched)
keys_set.difference_update(keys_def_set)
else:
# Prioritize default keys
@ -440,8 +463,9 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
for line in p.grabbed_output.split('\n'):
if "aborted via keyboard" in line:
abort = True
if "found:" in line:
found_keys[sec][key_type] = line[30:].strip()
matched = match_key(line)
if matched is not None:
found_keys[sec][key_type] = matched
show_key(real_sec, key_type, found_keys[sec][key_type])
if nt[sec][0] == nt[sec][1] and found_keys[sec][key_type ^ 1] == "":
found_keys[sec][key_type ^ 1] = found_keys[sec][key_type]
@ -466,8 +490,9 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
for line in p.grabbed_output.split('\n'):
if "aborted via keyboard" in line:
abort = True
if "found:" in line:
found_keys[sec][key_type] = line[30:].strip()
matched = match_key(line)
if matched is not None:
found_keys[sec][key_type] = matched
show_key(real_sec, key_type, found_keys[sec][key_type])
if abort:
break
@ -487,11 +512,12 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
for line in p.grabbed_output.split('\n'):
if "aborted via keyboard" in line:
abort = True
if "found:" in line:
found_keys[sec][0] = line[30:].strip()
found_keys[sec][1] = line[30:].strip()
show_key(real_sec, 0, found_keys[sec][key_type])
show_key(real_sec, 1, found_keys[sec][key_type])
matched = match_key(line)
if matched is not None:
found_keys[sec][0] = matched
found_keys[sec][1] = matched
show_key(real_sec, 0, found_keys[sec][0])
show_key(real_sec, 1, found_keys[sec][1])
if abort:
break
@ -509,14 +535,15 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}_filtered.dic"
else:
dic = f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type_target]}.dic"
cmd = [tools["staticnested_2x1nt1key"], nt[sec][key_type_source], found_keys[sec][key_type_source], dic]
cmd = [staticnested_2x1nt1key_path, nt[sec][key_type_source], found_keys[sec][key_type_source], dic]
if debug:
print(' '.join(cmd))
result = subprocess.run(cmd, capture_output=True, text=True).stdout
keys = set()
for line in result.split('\n'):
if "MATCH:" in line:
keys.add(line[12:])
matched = match_key(line)
if matched is not None:
keys.add(matched)
if len(keys) > 1:
kt = ['a', 'b'][key_type_target]
cmd = f"hf mf fchk --blk {real_sec * 4} -{kt} --no-default"
@ -528,8 +555,9 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
for line in p.grabbed_output.split('\n'):
if "aborted via keyboard" in line:
abort = True
if "found:" in line:
found_keys[sec][key_type_target] = line[30:].strip()
matched = match_key(line)
if matched is not None:
found_keys[sec][key_type_target] = matched
elif len(keys) == 1:
found_keys[sec][key_type_target] = keys.pop()
if found_keys[sec][key_type_target] != "":
@ -551,7 +579,10 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
cmd = f"hf mf fchk -f keys_{uid:08x}.dic --no-default --dump"
if debug:
print(cmd)
p.console(cmd, capture=False, quiet=False)
p.console(cmd, capture=True, quiet=False)
for line in p.grabbed_output.split('\n'):
if "Found keys have been dumped to" in line:
keyfile = line[line.index("`"):].strip("`")
else:
show()
show(color("found keys:", fg="green"), prompt=plus)
@ -590,22 +621,22 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
if unknown:
show(" --[ " + color("FFFFFFFFFFFF", fg="yellow") +
" ]-- has been inserted for unknown keys", prompt="[" + color("=", fg="yellow") + "]")
show("Generating final dump file", prompt=plus)
dumpfile = f"{save_path}hf-mf-{uid:08X}-dump.bin"
with (open(dumpfile, "wb")) as f:
for sec in range(NUM_SECTORS):
for b in range(4):
d = data[(sec * 4) + b]
if b == 3:
ka = found_keys[sec][0]
kb = found_keys[sec][1]
if ka == "":
ka = "FFFFFFFFFFFF"
if kb == "":
kb = "FFFFFFFFFFFF"
d = ka + d[12:20] + kb
f.write(bytes.fromhex(d))
show("Data has been dumped to `" + color(dumpfile, fg="yellow")+"`", prompt=plus)
show("Generating final dump file", prompt=plus)
dump_file = f"{save_path}hf-mf-{uid:08X}-dump.bin"
with (open(dump_file, "wb")) as f:
for sec in range(NUM_SECTORS):
for b in range(4):
d = data[(sec * 4) + b]
if b == 3:
ka = found_keys[sec][0]
kb = found_keys[sec][1]
if ka == "":
ka = "FFFFFFFFFFFF"
if kb == "":
kb = "FFFFFFFFFFFF"
d = ka + d[12:20] + kb
f.write(bytes.fromhex(d))
show("Data has been dumped to `" + color(dump_file, fg="yellow")+"`", prompt=plus)
# Remove generated dictionaries after processing
if not keep:
@ -631,11 +662,27 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, debu
seconds = int(elapsed_time % 60)
show("---- TOTAL: " + color(f"{minutes:2}", fg="yellow") + " minutes " +
color(f"{seconds:2}", fg="yellow") + " seconds -----------")
return {'keyfile': keyfile, 'found_keys': found_keys, 'dumpfile': dumpfile, 'data': data}
return {'keyfile': keyfile, 'found_keys': found_keys, 'dump_file': dump_file, 'data': data}
def main():
"""
Parse command-line arguments and initiate the recovery process.
Command-line arguments:
-x, --init-check: Run an initial fchk for default keys.
-y, --final-check: Run a final fchk with the found keys.
-n, --no-oob: Do not save out of bounds keys.
-k, --keep: Keep generated dictionaries after processing.
-d, --debug: Enable debug mode.
-s, --supply-chain: Enable supply-chain mode. Look for hf-mf-XXXXXXXX-default_nonces.json.
The supply-chain mode json can be produced from the json saved by
"hf mf isen --collect_fm11rf08s --key A396EFA4E24F" on a wiped card, then processed with
jq '{Created: .Created, FileType: "fm11rf08s_default_nonces", nt: .nt | del(.["32"]) | map_values(.a)}'.
This function calls the recovery function with the parsed arguments.
"""
parser = argparse.ArgumentParser(description='A script combining staticnested* tools '
'to recover all keys from a FM11RF08S card.')
parser.add_argument('-x', '--init-check', action='store_true', help='Run an initial fchk for default keys')

View file

@ -0,0 +1,198 @@
### Crappy helper script for USCUID-UL, v0.2.4.2
## Written and tested by Eltrick
# It is recommended that you are able to backdoor read main blocks
# in case changing from one type to another messes up keys/pwd
# unless you know what you're doing.
## For the uninitiated, the keys are stored in the following locations
## per the corresponding datasheets
# UL11 - PWD - page 18d
# UL21 - PWD - page 39d
# UL-C - KEY - pages 44d to 47d
# NTAG 213 - PWD - page 43d
# NTAG 215 - PWD - page 133d
# NTAG 216 - PWD - page 229d
import argparse
import pm3
try:
# pip install ansicolors
from colors import color
except ModuleNotFoundError:
def color(s, fg=None):
_ = fg
return str(s)
HEX_DIGITS = "0123456789ABCDEF"
MEMORY_CONFIG = { "C3": "UL11", "3C": "UL21", "00": "UL-C", "A5": "NTAG 213", "5A": "NTAG 215", "AA": "NTAG 216", "55": "Unknown IC with 238 pages" }
KNOWN_CONFIGS = ["C30004030101000B03", "3C0004030101000E03", "000000000000000000", "A50004040201000F03", "5A0004040201001103", "AA0004040201001303"]
parser = argparse.ArgumentParser(description='A script to help with raw USCUID-UL commands. Out of everything until -s, only one functionality can be used at a time, prioritised in order listed below.')
parser.add_argument('-r', '--read', action='store_true', help='Read and parse config from card')
parser.add_argument('-t', '--type', help='Type to change to: 1-UL11; 2-UL21; 3-UL-C; 4-NTAG213; 5-NTAG215; 6-NTAG216')
parser.add_argument('-c', '--cfg', help='Config to write')
parser.add_argument('-p', '--parse', help='Config to parse')
parser.add_argument('-b', '--bdr', help='Page num to read with backdoor')
parser.add_argument('-w', '--wbd', help='First page num to write with backdoor')
parser.add_argument('-u', '--uid', help='New UID to write')
parser.add_argument('-d', '--data', help='Page data to write if using -w, multiple of 4 bytes')
parser.add_argument('-s', '--sig', help='Signature to write with backdoor')
parser.add_argument('--gen1a', action='store_true', help='Use gen1a (40/43) magic wakeup')
parser.add_argument('--gdm', action='store_true', help='Use gdm alt (20/23) magic wakeup')
args = parser.parse_args()
card_config = args.read
ul_type = args.type
config = args.cfg
parse = args.parse
backdoor_block = args.bdr
write_backdoor = args.wbd
data = args.data
signature = args.sig
gen1a = args.gen1a
alt = args.gdm
uid = args.uid
field_on = False
p = pm3.pm3()
ERROR = "[" + color("-", "red") + "] "
SUCCESS = "[" + color("+", "green") + "] "
def verify_config(config: str) -> bool:
if len(config) != 32:
print(ERROR + "Configuration data must be 16 bytes.")
return False
if set(config) > set(HEX_DIGITS):
print(ERROR + "Configuration data must be in hex.")
return False
return True
def parse_config(config: str):
print(SUCCESS + "" + config)
cfg_magic_wup = config[0:4]
cfg_wup_style = config[4:6]
cfg_regular_available = config[6:8]
cfg_auth_type = config[8:10]
cfg_cuid = config[12:14]
cfg_memory_config = config[14:16]
log_magic_wup = "Magic wakeup " + ("en" if cfg_magic_wup != "8500" else "dis") + "abled" + (" with config access" if cfg_magic_wup == "7AFF" else "")
log_wup_style = "Magic wakeup style " + ("Gen1a 40(7)/43" if cfg_wup_style == "00" else ("GDM 20(7)/23" if cfg_wup_style == "85" else "unknown"))
log_regular_available = "Config " + ("" if cfg_regular_available == "A0" else "un") + "available in regular mode"
log_auth_type = "Auth type " + ("1B - PWD" if cfg_auth_type == "00" else "1A - 3DES")
log_cuid = "CUID " + ("dis" if cfg_cuid == "A0" else "en") + "abled"
log_memory_config = "Maximum memory configuration: " + (MEMORY_CONFIG[cfg_memory_config] if cfg_memory_config in MEMORY_CONFIG.keys() else "unknown")
print(SUCCESS + "^^^^............................ " + log_magic_wup)
print(SUCCESS + "....^^.......................... " + log_wup_style)
print(SUCCESS + "......^^........................ " + log_regular_available)
print(SUCCESS + "........^^...................... " + log_auth_type)
print(SUCCESS + "..........^^.................... unknown")
print(SUCCESS + "............^^.................. " + log_cuid)
print(SUCCESS + "..............^^................ " + log_memory_config)
print(SUCCESS + "................^^^^^^^^^^^^^^^^ version info")
def try_auth_magic(enforced = False):
if enforced and not (gen1a | alt):
print(ERROR + "Magic wakeup required. Please select one.")
exit()
if gen1a ^ alt:
p.console("hf 14a raw -akb 7 " + ("40" if gen1a else "20"))
p.console("hf 14a raw -k " + ("43" if gen1a else "23"))
def write_config(config: str):
try_auth_magic()
for i in range(4):
p.console("hf 14a raw -" + ("s" if (i == 0 and not (gen1a or alt)) else "") + ("k" if i != 3 else "") + "c" + f" E2{i:02x}" + config[8*i:8*i+8], False, False)
def grab_config() -> str:
try_auth_magic()
p.console("hf 14a raw -c" + ("s" if not (gen1a or alt) else "") + " E050")
return p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "")
if gen1a and alt:
print(ERROR + "Please only choose one magic wakeup type.")
exit()
if card_config:
config_grab = grab_config()
if not verify_config(config_grab):
print(ERROR + "Failed to grab config data from card.")
exit()
parse_config(config_grab)
elif ul_type != None:
ul_type_num = int(ul_type) - 1
if ul_type_num < 0 or ul_type_num >= len(KNOWN_CONFIGS):
print(ERROR + "Type specified is non-existent.")
exit()
old_config = grab_config()
new_config = old_config[0:8] + ("0A" if ul_type_num == 2 else "00") + old_config[10:14] + KNOWN_CONFIGS[ul_type_num]
write_config(new_config)
elif config != None:
config = config.upper()
if not verify_config(config):
exit()
write_config(config)
elif parse != None:
parse = parse.upper()
if not verify_config(parse):
exit()
parse_config(parse)
elif backdoor_block != None:
block = int(backdoor_block)
try_auth_magic(True)
p.console(f"hf 14a raw -c 30{block:02x}")
print(p.grabbed_output.split("\n")[-2][4:-9].replace(" ", ""))
elif write_backdoor != None:
write_backdoor_num = int(write_backdoor)
if data == None:
print(ERROR + "Specify data to write to the block.")
exit()
if len(data) % 8 != 0:
print(ERROR + "Data must be a multiple of 4 bytes.")
exit()
try_auth_magic(True)
for i in range(len(data) // 8):
p.console("hf 14a raw -" + ("k" if i != (len(data) // 8 - 1) else "") + f"c A2{(write_backdoor_num + i):02x}{data[8*i:8*i+8]}", False, False)
elif uid != None:
if len(uid) != 14:
print(ERROR + "UID must be 7 bytes.")
exit()
try_auth_magic()
p.console(f"hf 14a raw -kc" + ("s" if not (gen1a or alt) else "") + " 3002")
block_2 = p.grabbed_output.split("\n")[-2][4:-9].replace(" ", "")[:8]
uid_bytes = [int(uid[2*x:2*x+2], 16) for x in range(7)]
bcc_0 = 0x88 ^ uid_bytes[0] ^ uid_bytes[1] ^ uid_bytes[2]
new_block_0 = ""
for i in range(3):
new_block_0 += f"{uid_bytes[i]:02x}"
new_block_0 += f"{bcc_0:02x}"
bcc_1 = uid_bytes[3] ^ uid_bytes[4] ^ uid_bytes[5] ^ uid_bytes[6]
new_block_1 = uid[6:]
new_block_2 = f"{bcc_1:02x}" + block_2[2:]
p.console("hf 14a raw -kc A200" + new_block_0, False, False)
p.console("hf 14a raw -kc A201" + new_block_1, False, False)
p.console("hf 14a raw -c A202" + new_block_2, False, False)
elif signature != None:
if len(signature) != 64:
print(ERROR + "Signature must be 32 bytes.")
exit()
try_auth_magic(True)
signature_pages = [signature[8*x:8*x+8] for x in range(8)]
for i in range(8, 16):
p.console("hf 14a raw -c" + ("k" if i != 15 else "") + f" A2F{i:01x}{signature_pages[i - 8]}", False, False)
# Always try to HALT
p.console("hf 14a raw -c 5000")

View file

@ -284,6 +284,7 @@ FRA_OrganizationalAuthority_Contract_Provider = {
},
0x091: {
1: InterticHelper('Strasbourg', 'CTS', Describe_Usage_4), # More dump needed, not only tram !
5: InterticHelper('Strasbourg', 'CTS / new', Describe_Usage_4), # More dump needed, not only tram !
},
0x502: {
83: InterticHelper('Annecy', 'Sibra', Describe_Usage_2),

View file

@ -1,3 +1,5 @@
#!/usr/bin/env python3
# paxton_convert.py - Convert Paxton Net2 and Switch2 to EM4102
# Author jareckib <jareckib@hotmail.com>
# Based on Equipter's tutorial - Downgrade Paxton Net to EM410x
@ -19,68 +21,95 @@
import sys
def hex_to_bin(hex_string):
return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string))
def remove_last_two_bits(binary_str):
return binary_str[:-2]
def split_into_5bit_chunks(binary_str):
return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)]
def remove_parity_bit(chunks):
return [chunk[1:] for chunk in chunks if len(chunk) == 5]
def convert_to_hex(chunks):
return [format(int(chunk, 2), 'X') for chunk in chunks]
def convert_to_decimal(chunks):
return [int(chunk, 2) for chunk in chunks]
def find_until_before_f(hex_values):
result = []
for value in hex_values:
if value == 'F':
break
result.append(value)
return result
def process_block(block):
binary_str = hex_to_bin(block)
binary_str = remove_last_two_bits(binary_str)
chunks = split_into_5bit_chunks(binary_str)
no_parity_chunks = remove_parity_bit(chunks)
return no_parity_chunks
def calculate_id_net(blocks):
all_hex_values = []
for block in blocks:
hex_values = convert_to_hex(process_block(block))
all_hex_values.extend(hex_values)
selected_hex_values = find_until_before_f(all_hex_values)
if not selected_hex_values:
raise ValueError("Error: No valid data found in blocks 4 and 5.")
combined_hex = ''.join(selected_hex_values)
if not combined_hex.isdigit():
raise ValueError("Error: Invalid data in blocks 4 and 5.")
decimal_id = int(combined_hex)
stripped_hex_id = format(decimal_id, 'X').upper()
padded_hex_id = stripped_hex_id.zfill(10)
return decimal_id, padded_hex_id
def calculate_id_switch(blocks):
all_decimal_values = []
for block in blocks:
decimal_values = convert_to_decimal(process_block(block))
all_decimal_values.extend(decimal_values)
if len(all_decimal_values) < 15:
raise ValueError("Error: Not enough data after processing blocks 4, 5, 6, and 7.")
id_positions = [9, 11, 13, 15, 2, 4, 6, 8]
id_numbers = [all_decimal_values[pos-1] for pos in id_positions]
decimal_id = int(''.join(map(str, id_numbers)))
padded_hex_id = format(decimal_id, 'X').upper().zfill(10)
return decimal_id, padded_hex_id
def input_block_data(block_number):
while True:
block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip()
if len(block_data) != 8 or not all(c in '0123456789abcdefABCDEF' for c in block_data):
print("Error: Data must be 4 bytes (8 characters) in hex. Try again.")
else:
return block_data
block_4 = input_block_data(4)
block_5 = input_block_data(5)
if block_5[3] == 'F' or block_5[3] == 'f':
print("Identified Paxton Net2")
blocks = [block_4, block_5]
try:
decimal_id, padded_hex_id = calculate_id_net(blocks)
print('Calculations for block 4 and block 5:')
@ -89,11 +118,13 @@ if block_5[3] == 'F' or block_5[3] == 'f':
print('Use the following command in Proxmark3: lf em 410x clone --id {}'.format(padded_hex_id))
except ValueError as e:
print(e)
else:
print("Identified Paxton Switch2")
block_6 = input_block_data(6)
block_7 = input_block_data(7)
blocks = [block_4, block_5, block_6, block_7]
try:
decimal_id, padded_hex_id = calculate_id_switch(blocks)
print('Calculated data from blocks 4, 5, 6, 7:')
@ -102,4 +133,5 @@ else:
print('Use the following command in Proxmark3: lf em 410x clone --id {}'.format(padded_hex_id))
except ValueError as e:
print(e)
print('If EM4102 does not work, this option is probably disabled. Sorry for the inconvenience.')

View file

@ -1,3 +1,5 @@
#!/usr/bin/env python3
# paxton_net.py - Convert Paxton Net2 to EM4102
# Author jareckib <jareckib@hotmail.com>
# Based on Equipter's tutorial - Downgrade Paxton Net to EM410x
@ -17,16 +19,22 @@
# GNU General Public License for more details.
import sys
def hex_to_bin(hex_string):
return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string))
def remove_last_two_bits(binary_str):
return binary_str[:-2]
def split_into_5bit_chunks(binary_str):
return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)]
def remove_parity_bit(chunks):
return [chunk[1:] for chunk in chunks if len(chunk) == 5]
def convert_to_hex(chunks):
return [format(int(chunk, 2), 'X') for chunk in chunks]
def find_until_before_f(hex_values):
result = []
for value in hex_values:
@ -34,6 +42,7 @@ def find_until_before_f(hex_values):
break
result.append(value)
return result
def process_block(block):
binary_str = hex_to_bin(block)
binary_str = remove_last_two_bits(binary_str)
@ -41,21 +50,30 @@ def process_block(block):
no_parity_chunks = remove_parity_bit(chunks)
hex_values = convert_to_hex(no_parity_chunks)
return hex_values
def calculate_id(blocks):
all_hex_values = []
for block in blocks:
hex_values = process_block(block)
all_hex_values.extend(hex_values)
selected_hex_values = find_until_before_f(all_hex_values)
if not selected_hex_values:
raise ValueError("Error: No valid data found in blocks 4 and 5.")
combined_hex = ''.join(selected_hex_values)
if not combined_hex.isdigit():
raise ValueError("Error: Invalid data in blocks 4 and 5.")
decimal_id = int(combined_hex)
stripped_hex_id = format(decimal_id, 'X').upper()
padded_hex_id = stripped_hex_id.zfill(10)
return combined_hex, decimal_id, stripped_hex_id, padded_hex_id
def input_block_data(block_number):
while True:
block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip()
@ -75,6 +93,7 @@ blocks = [
block_4,
block_5,
]
try:
result_hex, result_decimal, stripped_hex_id, padded_hex_id = calculate_id(blocks)
print('Calculations for block 4 and block 5:')

View file

@ -1,3 +1,5 @@
#!/usr/bin/env python3
# paxton_switch.py - Convert Paxton Switch2 to EM4102
# Author jareckib <jareckib@hotmail.com>
# Based on Equipter's tutorial - Downgrade Paxton Net to EM410x
@ -16,35 +18,48 @@
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
import sys
def hex_to_bin(hex_string):
return ''.join(format(byte, '08b') for byte in bytearray.fromhex(hex_string))
def remove_last_two_bits(binary_str):
return binary_str[:-2]
def split_into_5bit_chunks(binary_str):
return [binary_str[i:i+5] for i in range(0, len(binary_str), 5)]
def remove_parity_bit(chunks):
return [chunk[1:] for chunk in chunks if len(chunk) == 5]
def convert_to_decimal(chunks):
return [int(chunk, 2) for chunk in chunks]
def process_block(block):
binary_str = hex_to_bin(block)
binary_str = remove_last_two_bits(binary_str)
chunks = split_into_5bit_chunks(binary_str)
no_parity_chunks = remove_parity_bit(chunks)
decimal_values = convert_to_decimal(no_parity_chunks)
return decimal_values
def calculate_id(blocks):
all_decimal_values = []
for block in blocks:
decimal_values = process_block(block)
all_decimal_values.extend(decimal_values)
if len(all_decimal_values) < 15:
raise ValueError("Error: Not enough data after processing blocks 4, 5, 6, and 7.")
id_positions = [9, 11, 13, 15, 2, 4, 6, 8]
id_numbers = [all_decimal_values[pos-1] for pos in id_positions]
decimal_id = int(''.join(map(str, id_numbers)))
padded_hex_id = format(decimal_id, 'X').upper().zfill(10)
return decimal_id, padded_hex_id
def input_block_data(block_number):
while True:
block_data = input("Enter data for block {} (4 bytes in hex): ".format(block_number)).strip()
@ -52,6 +67,8 @@ def input_block_data(block_number):
print("Error: Data must be 4 bytes (8 characters) in hex. Try again.")
else:
return block_data
block_4 = input_block_data(4)
block_5 = input_block_data(5)
block_6 = input_block_data(6)
@ -62,6 +79,7 @@ blocks = [
block_6,
block_7,
]
try:
decimal_id, padded_hex_id = calculate_id(blocks)
print('Calculated data from blocks 4, 5, 6, 7:')

View file

@ -0,0 +1,92 @@
"""
Helper library to locate resources for pm3 scripts.
This module provides functionality to locate tools and dictionaries required
for pm3 scripts. It determines the paths based on the directory structure
and whether the script is being run in a development setup or an installed setup.
Functions:
find_tool(tool_name):
Finds the specified tool in the tools directory.
Args:
tool_name (str): The name of the tool to find.
Returns:
str: The full path to the tool if found, otherwise None.
find_dict(dict_name):
Find the specified dictionary in the dicts directory.
Args:
dict_name (str): The name of the dict to find.
Returns:
str: The full path to the dict if found, otherwise None.
"""
import os
# Install script can hardcode paths in the following variables
TOOLS_PATH = None
DICTS_PATH = None
if __name__ == "__main__":
print("This is a library, don't use it as a script")
exit()
DIR_PATH = os.path.dirname(os.path.abspath(__file__))
if TOOLS_PATH is None:
if os.path.basename(os.path.dirname(DIR_PATH)) == 'client':
# dev setup
DEV_TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "..", "tools", "mfc", "card_only"))
if os.path.isdir(DEV_TOOLS_PATH):
TOOLS_PATH = DEV_TOOLS_PATH
if TOOLS_PATH is None:
# assuming installed without having defined TOOLS_PATH
TEST_TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "tools"))
if os.path.isdir(TEST_TOOLS_PATH):
TOOLS_PATH = TEST_TOOLS_PATH
if DICTS_PATH is None:
DEV_DICTS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "dictionaries"))
if os.path.isdir(DEV_DICTS_PATH):
DICTS_PATH = DEV_DICTS_PATH
def find_tool(tool_name):
"""Find the specified tool in the tools directory.
Args:
tool_name (str): The name of the tool to find.
Returns:
str: The full path to the tool if found, otherwise None.
"""
if TOOLS_PATH is not None:
tool = os.path.join(TOOLS_PATH, tool_name)
if os.path.isfile(tool):
return tool
elif os.path.isfile(tool + ".exe"):
return tool + ".exe"
# if not found, search in the user PATH
for path in os.environ["PATH"].split(os.pathsep):
env_tool = os.path.join(path, tool_name)
if os.path.isfile(env_tool):
return env_tool
elif os.path.isfile(env_tool + ".exe"):
return env_tool + ".exe"
raise FileNotFoundError(f"Cannot find {tool_name}, abort!")
def find_dict(dict_name):
"""Find the specified dictionary in the dicts directory.
Args:
dict_name (str): The name of the dict to find.
Returns:
str: The full path to the dict if found, otherwise None.
"""
if DICTS_PATH is not None:
dictionary = os.path.join(DICTS_PATH, dict_name)
if os.path.isfile(dictionary):
return dictionary
raise FileNotFoundError(f"Cannot find {dict_name}, abort!")

View file

@ -335,6 +335,14 @@
"Description": "Card Application Directory (CAD)",
"Type": "pacs"
},
{
"AID": "F484D1",
"Vendor": "HID",
"Country": "US",
"Name": "Unknown DESFire EV1",
"Description": "Access Control",
"Type": "pacs"
},
{
"AID": "F484E3",
"Vendor": "HID",
@ -511,6 +519,46 @@
"Description": "Multiservice Student Card",
"Type": "student"
},
{
"AID": "764970",
"Vendor": "Ubiquiti Inc",
"Country": "N/A",
"Name": "UniFi Access VID App",
"Description": "Contains issuer-signed static credential information used for KDF & other authentication operations",
"Type": "pacs"
},
{
"AID": "84D3FC",
"Vendor": "Ubiquiti Inc",
"Country": "N/A",
"Name": "UniFi Access FCD App",
"Description": "Contains static credential information used for KDF & other authentication operations",
"Type": "pacs"
},
{
"AID": "416343",
"Vendor": "Ubiquiti Inc",
"Country": "N/A",
"Name": "UniFi Access ACC App",
"Description": "Application created after enrollment into the system, containins unique authentication info",
"Type": "pacs"
},
{
"AID": "454955",
"Vendor": "Ubiquiti Inc",
"Country": "N/A",
"Name": "UniFi Access Touch Pass Apple Wallet Express",
"Description": "AID value is 'UIE' (UniFi Express) reversed. This app is selectable with or without auth",
"Type": "pacs"
},
{
"AID": "534955",
"Vendor": "Ubiquiti Inc",
"Country": "N/A",
"Name": "UniFi Access Touch Pass Apple Wallet Secure",
"Description": "AID value is 'UIS' (UniFi Secure) reversed. This app is selectable only after manual auth",
"Type": "pacs"
},
{
"AID": "535501",
"Vendor": "TU Delft",
@ -1034,7 +1082,7 @@
{
"AID": "087522",
"Vendor": "Umo Mobility via Cubic Transportation Systems",
"Country": "US",
"Country": "US / CA",
"Name": "Umo Mobility Card",
"Description": "Umo Mobility Card",
"Type": "transport"
@ -1159,14 +1207,6 @@
"Description": "DEL Delhi Metro (App 5)",
"Type": "transport"
},
{
"AID": "444D05",
"Vendor": "Delhi Metro Rail Corporation Limited",
"Country": "IN",
"Name": "Delhi Metro Travel Card (DEL)",
"Description": "DEL Delhi Metro (App 5)",
"Type": "transport"
},
{
"AID": "444D06",
"Vendor": "Delhi Metro Rail Corporation Limited",
@ -1219,7 +1259,7 @@
"AID": "578000",
"Vendor": "Norwegian Public Roads Administration (NPRA)",
"Country": "NO",
"Name": "NORTIC (Norway Public Transport Card)",
"Name": "NORTIC (Norway Public Transport Card) (Card Issuer App)",
"Description": "Norwegian Ticketing Interoperable Concept // FID 0C: Card Issuer Header",
"Type": "transport"
},
@ -1227,7 +1267,7 @@
"AID": "578001",
"Vendor": "Norwegian Public Roads Administration (NPRA)",
"Country": "NO",
"Name": "NORTIC (Norway Public Transport Card)",
"Name": "NORTIC (Norway Public Transport Card) (Transport App)",
"Description": "FIDs 01: Product Retailer; 02: Service Provider; 03: Special Event; 04: Stored Value; 05: General Event Log; 06: SV Reload Log; 0A: Environment; 0C: Card Holder",
"Type": "transport"
},
@ -1242,7 +1282,7 @@
{
"AID": "677F8E",
"Vendor": "Umo Mobility via Cubic Transportation Systems",
"Country": "US",
"Country": "US / CA",
"Name": "Umo Mobility Card",
"Description": "Umo Mobility Card",
"Type": "transport"
@ -1258,7 +1298,7 @@
{
"AID": "992CB5",
"Vendor": "Umo Mobility via Cubic Transportation Systems",
"Country": "US",
"Country": "US / CA",
"Name": "Umo Mobility Card",
"Description": "Umo Mobility Card",
"Type": "transport"
@ -1274,7 +1314,7 @@
{
"AID": "A4237D",
"Vendor": "Umo Mobility via Cubic Transportation Systems",
"Country": "US",
"Country": "US / CA",
"Name": "Umo Mobility Card",
"Description": "Umo Mobility Card",
"Type": "transport"
@ -1282,7 +1322,7 @@
{
"AID": "C65B80",
"Vendor": "Umo Mobility via Cubic Transportation Systems",
"Country": "US",
"Country": "US / CA",
"Name": "Umo Mobility Card",
"Description": "Umo Mobility Card",
"Type": "transport"
@ -1353,10 +1393,10 @@
},
{
"AID": "F21050",
"Vendor": "Metro Christchurch via INIT",
"Country": "NZ",
"Name": "Metrocard (CHC)",
"Description": "FIDs: 00: Backup Data; 01/02: Trip History; 03: Card Balance",
"Vendor": "Metro Christchurch via INIT / Arc",
"Country": "NZ / CA",
"Name": "Metrocard (CHC) / Arc (YEG)",
"Description": "CHC FIDs: 00: Backup Data; 01/02: Trip History; 03: Card Balance",
"Type": "transport"
},
{
@ -1400,11 +1440,19 @@
"Type": "transport"
},
{
"AID": "F21201",
"Vendor": "Green Bay Metro Transit via Genfare",
"AID": "F21191",
"Vendor": "Metropolitan Transportation Commission via Cubic",
"Country": "US",
"Name": "Tap-N-Go Card (GRB)",
"Description": "GRB Tap-N-Go Card",
"Name": "Clipper Card (Mobile)",
"Description": "",
"Type": "transport"
},
{
"AID": "F21201",
"Vendor": "Green Bay Metro Transit via Genfare / Winnipeg Transit",
"Country": "US / CA",
"Name": "Tap-N-Go Card (GRB) / peggo card (YWG)",
"Description": "GRB Tap-N-Go Card / YWG peggo card",
"Type": "transport"
},
{

View file

@ -82,20 +82,25 @@ int GetModels(char *Models[], int *count, uint8_t *width) {
SETBMP();
if (width[0] == 0) { //reveng -D
*count = mcount();
if (!*count) {
PrintAndLogEx(WARNING, "no preset models available");
return 0;
}
for (int mode = 0; mode < *count; ++mode) {
mbynum(&model, mode);
mcanon(&model);
size_t size = (model.name && *model.name) ? strlen(model.name) : 7;
char *tmp = calloc(size + 1, sizeof(char));
if (tmp == NULL) {
PrintAndLogEx(WARNING, "out of memory?");
return 0;
}
if (model.name != NULL) {
memcpy(tmp, model.name, size);
Models[mode] = tmp;
@ -113,18 +118,21 @@ int GetModels(char *Models[], int *count, uint8_t *width) {
PrintAndLogEx(WARNING, "cannot search for non-Williams compliant models");
return 0;
}
praloc(&model.spoly, (unsigned long)width[0]);
praloc(&model.init, (unsigned long)width[0]);
praloc(&model.xorout, (unsigned long)width[0]);
if (!plen(model.spoly))
if (!plen(model.spoly)) {
palloc(&model.spoly, (unsigned long)width[0]);
else
} else {
width[0] = (uint8_t)plen(model.spoly);
}
/* special case if qpoly is zero, search to end of range */
if (!ptst(qpoly))
if (!ptst(qpoly)) {
rflags &= ~R_HAVEQ;
}
int pass;
@ -135,31 +143,41 @@ int GetModels(char *Models[], int *count, uint8_t *width) {
*/
/* scan against preset models */
if (~uflags & C_NOPCK) {
pass = 0;
int Cnt = 0;
do {
int psets = mcount();
while (psets) {
mbynum(&pset, --psets);
/* skip if different width, or refin or refout don't match */
if (plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT))
if (plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT)) {
continue;
}
/* skip if the preset doesn't match specified parameters */
if (rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly))
if (rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) {
continue;
if (rflags & R_HAVEI && psncmp(&model.init, &pset.init))
}
if (rflags & R_HAVEI && psncmp(&model.init, &pset.init)) {
continue;
if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout))
}
if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) {
continue;
}
//for additional args (not used yet, maybe future?)
apoly = pclone(pset.xorout);
if (pset.flags & P_REFOUT)
if (pset.flags & P_REFOUT) {
prev(&apoly);
}
for (qptr = apolys; qptr < pptr; ++qptr) {
crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0);
@ -183,6 +201,7 @@ int GetModels(char *Models[], int *count, uint8_t *width) {
PrintAndLogEx(WARNING, "out of memory?");
return 0;
}
width[Cnt] = width[0];
memcpy(tmp, pset.name, size);
Models[Cnt++] = tmp;
@ -199,6 +218,7 @@ int GetModels(char *Models[], int *count, uint8_t *width) {
prevch(qptr, ibperhx);
}
}
} while (~rflags & R_HAVERI && ++pass < 2);
}
//got everything now free the memory...
@ -208,6 +228,7 @@ int GetModels(char *Models[], int *count, uint8_t *width) {
pfree(qptr);
}
}
if (uflags & C_NOBFS && ~rflags & R_HAVEP) {
PrintAndLogEx(WARNING, "no models found");
return 0;
@ -217,24 +238,30 @@ int GetModels(char *Models[], int *count, uint8_t *width) {
PrintAndLogEx(WARNING, "cannot search for crossed-endian models");
return 0;
}
pass = 0;
int args = 0;
do {
model_t *candmods = reveng(&model, qpoly, rflags, args, apolys);
model_t *mptr = candmods;
if (mptr && plen(mptr->spoly)) {
uflags |= C_RESULT;
}
while (mptr && plen(mptr->spoly)) {
mfree(mptr++);
}
free(candmods);
if (~rflags & R_HAVERI) {
model.flags ^= P_REFIN | P_REFOUT;
for (qptr = apolys; qptr < pptr; ++qptr) {
prevch(qptr, ibperhx);
}
}
} while (~rflags & R_HAVERI && ++pass < 2);
for (qptr = apolys; qptr < pptr; ++qptr) {

View file

@ -1207,76 +1207,79 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen
}
uint16_t cmdc = 0;
if (chainingin)
if (chainingin) {
cmdc = ISO14A_SEND_CHAINING;
}
// "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes
// https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size
// here length PM3_CMD_DATA_SIZE=512
// timeout must be authomatically set by "get ATS"
if (datain)
if (datain) {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0x1FF), 0, datain, datainlen & 0x1FF);
else
} else {
SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0);
}
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, timeout)) {
const uint8_t *recv = resp.data.asBytes;
int iLen = resp.oldarg[0];
uint8_t res = resp.oldarg[1];
int dlen = iLen - 2;
if (dlen < 0)
dlen = 0;
*dataoutlen += dlen;
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
PrintAndLogEx(DEBUG, "ERR: APDU: Buffer too small(%d), needs %d bytes", *dataoutlen, maxdataoutlen);
return PM3_EAPDU_FAIL;
}
// I-block ACK
if ((res & 0xF2) == 0xA2) {
*dataoutlen = 0;
*chainingout = true;
return PM3_SUCCESS;
}
if (!iLen) {
PrintAndLogEx(DEBUG, "ERR: APDU: No APDU response");
return PM3_EAPDU_FAIL;
}
// check apdu length
if (iLen < 2 && iLen >= 0) {
PrintAndLogEx(DEBUG, "ERR: APDU: Small APDU response, len %d", iLen);
return PM3_EAPDU_FAIL;
}
// check block TODO
if (iLen == -2) {
PrintAndLogEx(DEBUG, "ERR: APDU: Block type mismatch");
return PM3_EAPDU_FAIL;
}
memcpy(dataout, recv, dlen);
// chaining
if ((res & 0x10) != 0) {
*chainingout = true;
}
// CRC Check
if (iLen == -1) {
PrintAndLogEx(DEBUG, "ERR: APDU: ISO 14443A CRC error");
return PM3_EAPDU_FAIL;
}
} else {
if (WaitForResponseTimeout(CMD_ACK, &resp, timeout) == false) {
PrintAndLogEx(DEBUG, "ERR: APDU: Reply timeout");
return PM3_EAPDU_FAIL;
}
const uint8_t *recv = resp.data.asBytes;
int iLen = resp.oldarg[0];
uint8_t res = resp.oldarg[1];
int dlen = iLen - 2;
if (dlen < 0) {
dlen = 0;
}
*dataoutlen += dlen;
if (maxdataoutlen && *dataoutlen > maxdataoutlen) {
PrintAndLogEx(DEBUG, "ERR: APDU: Buffer too small(%d), needs %d bytes", *dataoutlen, maxdataoutlen);
return PM3_EAPDU_FAIL;
}
// I-block ACK
if ((res & 0xF2) == 0xA2) {
*dataoutlen = 0;
*chainingout = true;
return PM3_SUCCESS;
}
if (iLen == 0) {
PrintAndLogEx(DEBUG, "ERR: APDU: No APDU response");
return PM3_EAPDU_FAIL;
}
// check apdu length
if (iLen < 2 && iLen >= 0) {
PrintAndLogEx(DEBUG, "ERR: APDU: Small APDU response, len %d", iLen);
return PM3_EAPDU_FAIL;
}
// check block TODO
if (iLen == -2) {
PrintAndLogEx(DEBUG, "ERR: APDU: Block type mismatch");
return PM3_EAPDU_FAIL;
}
memcpy(dataout, recv, dlen);
// chaining
if ((res & 0x10) != 0) {
*chainingout = true;
}
// CRC Check
if (iLen == -1) {
PrintAndLogEx(DEBUG, "ERR: APDU: ISO 14443A CRC error");
return PM3_EAPDU_FAIL;
}
return PM3_SUCCESS;
}
@ -1300,18 +1303,18 @@ int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bo
*dataoutlen = 0;
res = CmdExchangeAPDU(chainBlockNotLast, &datain[clen], vlen, vActivateField, dataout, maxdataoutlen, dataoutlen, &chaining);
if (res != PM3_SUCCESS) {
if (leaveSignalON == false)
if (leaveSignalON == false) {
DropField();
}
return 200;
}
// check R-block ACK
// TODO check this one...
if ((*dataoutlen == 0) && (chaining != chainBlockNotLast)) {
if (leaveSignalON == false)
if (leaveSignalON == false) {
DropField();
}
return 201;
}
@ -1323,6 +1326,7 @@ int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bo
}
break;
}
} while (clen < datainlen);
} else {
@ -2358,7 +2362,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
tc1 = (card.ats[1] & 0x40) == 0x40;
int16_t fsci = card.ats[1] & 0x0f;
PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ T0 TA1 is%s present, TB1 is%s present, "
PrintAndLogEx(INFO, " ..." _YELLOW_("%02X") "............ T0 TA1 is%s present, TB1 is%s present, "
"TC1 is%s present, FSCI is %d (FSC = %d)",
card.ats[1],
(ta1 ? "" : _RED_(" NOT")),
@ -2380,7 +2384,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if (card.ats[pos] & 0x04) strcat(dr, "8, ");
if (strlen(ds) != 0) ds[strlen(ds) - 2] = '\0';
if (strlen(dr) != 0) dr[strlen(dr) - 2] = '\0';
PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... TA1 different divisors are%s supported, "
PrintAndLogEx(INFO, " ......" _YELLOW_("%02X") "......... TA1 different divisors are%s supported, "
"DR: [%s], DS: [%s]",
card.ats[pos],
((card.ats[pos] & 0x80) ? _RED_(" NOT") : ""),
@ -2395,7 +2399,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
uint32_t sfgi = card.ats[pos] & 0x0F;
uint32_t fwi = card.ats[pos] >> 4;
PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... TB1 SFGI = %d (SFGT = %s%d/fc), FWI = " _YELLOW_("%d") " (FWT = %d/fc)",
PrintAndLogEx(INFO, " ........." _YELLOW_("%02X") "...... TB1 SFGI = %d (SFGT = %s%d/fc), FWI = " _YELLOW_("%d") " (FWT = %d/fc)",
card.ats[pos],
(sfgi),
sfgi ? "" : "(not needed) ",
@ -2407,7 +2411,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
}
if (tc1 && (card.ats_len > pos + 2)) {
PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... TC1 NAD is%s supported, CID is%s supported",
PrintAndLogEx(INFO, " ............" _YELLOW_("%02X") "... TC1 NAD is%s supported, CID is%s supported",
card.ats[pos],
(card.ats[pos] & 0x01) ? "" : _RED_(" NOT"),
(card.ats[pos] & 0x02) ? "" : _RED_(" NOT")
@ -2546,11 +2550,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
, sprint_ascii(card.ats + pos, calen)
);
}
PrintAndLogEx(NORMAL, "");
}
}
}
if (do_aid_search) {
@ -2781,7 +2782,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) {
if ((isMagic & MAGIC_FLAG_GEN_2) == MAGIC_FLAG_GEN_2) {
PrintAndLogEx(HINT, "Hint: use `" _YELLOW_("hf mf") "` commands");
} else {
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf`") " commands");
PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf info`"));
}
}
@ -3715,58 +3716,61 @@ int CmdHF14AAIDSim(const char *Cmd) {
CLIParserInit(&ctx, "hf 14a simaid",
"Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID, and filter for AID Values\n"
"These AID Values can be responded to and include extra APDU commands on GetData after response\n",
"hf 14a simaid -t 3 -> MIFARE Desfire\n"
"hf 14a simaid -t 4 -> ISO/IEC 14443-4\n"
"hf 14a simaid -t 11 -> Javacard (JCOP)\n"
"hf 14a simaid -t 3 --aid a000000000000000000000 --response 9000 --apdu 9000 -> AID, Response and APDU\n"
"hf 14a simaid -t 3 --rats 05788172220101 --response 01009000 --apdu 86009000 -> Custom RATS Added\n"
"hf 14a simaid -t 3 --rats 05788172220101 -x -> Enumerate AID Values\n"
"hf 14a simaid -t 3 -> MIFARE Desfire\n"
"hf 14a simaid -t 4 -> ISO/IEC 14443-4\n"
"hf 14a simaid -t 11 -> Javacard (JCOP)\n"
"hf 14a simaid -t 3 --aid a000000000000000000000 --selectaid_response 9000 --getdata_response 9000 -> Custom AID and responses\n"
"hf 14a simaid -t 3 --ats 0578817222 --selectaid_response 01009000 --getdata_response 86009000 -> Custom ATS and responses\n"
"hf 14a simaid -t 3 --ats 0578817222 -x -> Enumerate AID Values\n"
);
void *argtable[] = {
arg_param_begin,
arg_int1("t", "type", "<1-12> ", "Simulation type to use"),
arg_str0("u", "uid", "<hex>", "<4|7|10> hex bytes UID"),
arg_str0("r", "rats", "<hex>", "<0-20> hex bytes RATS"),
arg_str0("a", "aid", "<hex>", "<0-100> hex bytes for AID to respond to (Default: A000000000000000000000)"),
arg_str0("e", "response", "<hex>", "<0-100> hex bytes for APDU Response to AID Select (Default: 9000)"),
arg_str0("p", "apdu", "<hex>", "<0-100> hex bytes for APDU Response to Get Data request after AID (Default: 9000)"),
arg_str0("r", "ats", "<hex>", "<0-20> hex bytes ATS"),
arg_str0("a", "aid", "<hex>", "<0-30> hex bytes for AID to respond to (Default: A000000000000000000000)"),
arg_str0("e", "selectaid_response", "<hex>", "<0-100> hex bytes for APDU Response to AID Select (Default: 9000)"),
arg_str0("p", "getdata_response", "<hex>", "<0-100> hex bytes for APDU Response to Get Data request after AID (Default: 9000)"),
arg_lit0("x", "enumerate", "Enumerate all AID values via returning Not Found and print them to console "),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
int tagtype = arg_get_int_def(ctx, 1, 1);
bool enumerate = arg_get_lit(ctx, 7);
int uid_len = 0;
int rats_len = 0;
int ats_len = 0;
int aid_len = 0;
int respond_len = 0;
int apdu_len = 0;
int selectaid_response_len = 0;
int getdata_response_len = 0;
uint8_t uid[10] = {0};
uint8_t rats[20] = {0};
uint8_t aid[30] = {0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t response[100] = {0x90, 0x00};
uint8_t apdu[100] = {0x90, 0x00};
uint8_t ats[20] = {0};
uint8_t aid[30] = {0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
uint8_t default_aid_len = 11;
uint8_t selectaid_response[100] = {0x90, 0x00};
uint8_t default_selectaid_response_len = 2;
uint8_t getdata_response[100] = {0x90, 0x00};
uint8_t default_getdata_response_len = 2;
int tagtype = arg_get_int_def(ctx, 1, 1);
CLIGetHexWithReturn(ctx, 2, uid, &uid_len);
CLIGetHexWithReturn(ctx, 3, rats, &rats_len);
CLIGetHexWithReturn(ctx, 3, ats, &ats_len);
CLIGetHexWithReturn(ctx, 4, aid, &aid_len);
CLIGetHexWithReturn(ctx, 5, response, &respond_len);
CLIGetHexWithReturn(ctx, 6, apdu, &apdu_len);
CLIGetHexWithReturn(ctx, 5, selectaid_response, &selectaid_response_len);
CLIGetHexWithReturn(ctx, 6, getdata_response, &getdata_response_len);
bool enumerate = arg_get_lit(ctx, 7);
CLIParserFree(ctx);
// default value fill for the AID, response, and apdu
// default value fill for the AID, selectaid_response, and getdata_response
if (aid_len == 0) {
aid_len = 11;
aid_len = default_aid_len;
}
if (respond_len == 0) {
respond_len = 2;
if (selectaid_response_len == 0) {
selectaid_response_len = default_selectaid_response_len;
}
if (apdu_len == 0) {
apdu_len = 2;
if (getdata_response_len == 0) {
getdata_response_len = default_getdata_response_len;
}
uint16_t flags = 0;
@ -3776,19 +3780,39 @@ int CmdHF14AAIDSim(const char *Cmd) {
FLAG_SET_UID_IN_DATA(flags, uid_len);
if (IS_FLAG_UID_IN_EMUL(flags)) {
PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID");
CLIParserFree(ctx);
return PM3_EINVARG;
}
PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uid_len, sprint_hex(uid, uid_len));
useUIDfromEML = false;
}
if (rats_len > 0) {
flags |= FLAG_RATS_IN_DATA;
if (ats_len > sizeof(ats)) {
PrintAndLogEx(ERR, "Provided ATS too long");
return PM3_EINVARG;
}
if (aid_len > sizeof(aid)) {
PrintAndLogEx(ERR, "Provided AID too long");
return PM3_EINVARG;
}
CLIParserFree(ctx);
if (selectaid_response_len > sizeof(selectaid_response)) {
PrintAndLogEx(ERR, "Provided SelectAID response too long");
return PM3_EINVARG;
}
if (getdata_response_len > sizeof(getdata_response)) {
PrintAndLogEx(ERR, "Provided GetData response too long");
return PM3_EINVARG;
}
if (ats_len > 0) {
flags |= FLAG_ATS_IN_DATA;
}
if (enumerate) {
flags |= FLAG_ENUMERATE_AID;
}
if (tagtype > 12) {
PrintAndLogEx(ERR, "Undefined tag %d", tagtype);
@ -3803,31 +3827,31 @@ int CmdHF14AAIDSim(const char *Cmd) {
uint8_t tagtype;
uint16_t flags;
uint8_t uid[10];
uint8_t rats[20];
uint8_t ats[20];
uint8_t aid[30];
uint8_t response[100];
uint8_t apdu[100];
int aid_len;
int respond_len;
int apdu_len;
bool enumerate;
uint8_t selectaid_response[100];
uint8_t getdata_response[100];
uint32_t ats_len;
uint32_t aid_len;
uint32_t selectaid_response_len;
uint32_t getdata_response_len;
} PACKED payload;
payload.tagtype = tagtype;
payload.flags = flags;
payload.enumerate = enumerate;
// Copy data to payload
memcpy(payload.uid, uid, uid_len);
memcpy(payload.rats, rats, rats_len);
memcpy(payload.ats, ats, ats_len);
memcpy(payload.aid, aid, aid_len);
memcpy(payload.response, response, respond_len);
memcpy(payload.apdu, apdu, apdu_len);
memcpy(payload.selectaid_response, selectaid_response, selectaid_response_len);
memcpy(payload.getdata_response, getdata_response, getdata_response_len);
// copy the lengths data to the payload
memcpy(&payload.aid_len, &aid_len, sizeof(aid_len));
memcpy(&payload.respond_len, &respond_len, sizeof(respond_len));
memcpy(&payload.apdu_len, &apdu_len, sizeof(apdu_len));
payload.ats_len = ats_len;
payload.aid_len = aid_len;
payload.selectaid_response_len = selectaid_response_len;
payload.getdata_response_len = getdata_response_len;
clearCommandBuffer();
SendCommandNG(CMD_HF_ISO14443A_SIM_AID, (uint8_t *)&payload, sizeof(payload));
@ -3837,14 +3861,17 @@ int CmdHF14AAIDSim(const char *Cmd) {
bool keypress = kbd_enter_pressed();
while (keypress == false) {
if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0)
if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) {
continue;
}
if (resp.status != PM3_SUCCESS)
if (resp.status != PM3_SUCCESS) {
break;
}
if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK)
if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) {
break;
}
keypress = kbd_enter_pressed();
}

View file

@ -282,9 +282,9 @@ static const productName_t uidmapping[] = {
static int CmdHF15Help(const char *Cmd);
static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) {
int reason = 0;
int index = -1;
index = originality_check_verify(uid, 8, signature, 32, PK_MFC);
int index = originality_check_verify(uid, 8, signature, 32, PK_MFC);
if (index >= 0) {
reason = 1;
} else {
@ -306,11 +306,12 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) {
}
}
}
PrintAndLogEx(NORMAL, "");
int ret = originality_check_print(signature, 32, index);
if (ret != PM3_SUCCESS) {
return ret;
}
switch (reason) {
case 1:
PrintAndLogEx(INFO, " Params used: UID and signature, plain");
@ -825,6 +826,8 @@ static int NxpSysInfo(uint8_t *uid) {
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, _CYAN_(" Password protection configuration"));
PrintAndLogEx(INFO, " Page prot. ptr. " _YELLOW_("%d"), d[1]);
PrintAndLogEx(INFO, " Page L read.... %s"
, (d[2] & 0x01) ? _RED_("password") : _GREEN_("no password")
);
@ -3202,6 +3205,101 @@ static int CmdHF15SlixWritePassword(const char *Cmd) {
return resp.status;
}
static int CmdHF15SlixProtectPage(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf 15 slixprotectpage",
"Defines protection pointer address of user mem and access cond. for pages",
"hf 15 slixprotectpage -w deadbeef -p 3 -h 3");
void *argtable[] = {
arg_param_begin,
arg_str0("r", "readpw", "<hex>", "read password, 4 hex bytes"),
arg_str0("w", "writepw", "<hex>", "write password, 4 hex bytes"),
arg_int0("p", "ptr", "<dec>", "protection pointer page (0-78), if 0 entire user mem"),
arg_int1("l", "lo", "<dec>", "page protection flags of lo page (0-None, 1-ReadPR, 2-WritePR)"),
arg_int1("i", "hi", "<dec>", "page protection flags of hi page (0-None, 1-ReadPR, 2-WritePR)"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
struct p {
uint8_t read_pwd[4];
uint8_t write_pwd[4];
uint8_t divide_ptr;
uint8_t prot_status;
} PACKED payload = {0};
int pwdlen = 0;
CLIGetHexWithReturn(ctx, 1, payload.read_pwd, &pwdlen);
if (pwdlen > 0 && pwdlen != 4) {
PrintAndLogEx(WARNING, "read password must be 4 hex bytes if provided");
CLIParserFree(ctx);
return PM3_ESOFT;
}
CLIGetHexWithReturn(ctx, 2, payload.write_pwd, &pwdlen);
if (pwdlen > 0 && pwdlen != 4) {
PrintAndLogEx(WARNING, "write password must be 4 hex bytes if provided");
CLIParserFree(ctx);
return PM3_ESOFT;
}
payload.divide_ptr = (uint8_t)arg_get_int_def(ctx, 3, 0);
if (payload.divide_ptr > 78) {
PrintAndLogEx(WARNING, "protection pointer page is invalid (is %d but should be <=78).", payload.divide_ptr);
CLIParserFree(ctx);
return PM3_ESOFT;
}
pwdlen = arg_get_int_def(ctx, 4, 0);
if (pwdlen > 3) {
PrintAndLogEx(WARNING, "page protection flags must be between 0 and 3");
CLIParserFree(ctx);
return PM3_ESOFT;
}
payload.prot_status = (uint8_t)pwdlen;
pwdlen = arg_get_int_def(ctx, 5, 0);
if (pwdlen > 3) {
PrintAndLogEx(WARNING, "page protection flags must be between 0 and 3");
CLIParserFree(ctx);
return PM3_ESOFT;
}
payload.prot_status |= (uint8_t)pwdlen << 4;
PrintAndLogEx(INFO, "Trying to set page protection pointer to " _YELLOW_("%d"), payload.divide_ptr);
PrintAndLogEx(INFO, _YELLOW_("LO") " page access %s%s", (payload.prot_status & 0x01) ? _RED_("R") : _GREEN_("r"), (payload.prot_status & 0x02) ? _RED_("W") : _GREEN_("w"));
PrintAndLogEx(INFO, _YELLOW_("HI") " page access %s%s", (payload.prot_status & 0x10) ? _RED_("R") : _GREEN_("r"), (payload.prot_status & 0x20) ? _RED_("W") : _GREEN_("w"));
PacketResponseNG resp;
clearCommandBuffer();
SendCommandNG(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, (uint8_t *)&payload, sizeof(payload));
if (WaitForResponseTimeout(CMD_HF_ISO15693_SLIX_PROTECT_PAGE, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "timeout while waiting for reply");
DropField();
return PM3_ESOFT;
}
switch (resp.status) {
case PM3_ETIMEOUT: {
PrintAndLogEx(WARNING, "no tag found");
break;
}
case PM3_EWRONGANSWER: {
PrintAndLogEx(WARNING, "Protection flags were not accepted, locked? ( " _RED_("fail") " )");
break;
}
case PM3_SUCCESS: {
PrintAndLogEx(SUCCESS, "Page protection written ( " _GREEN_("ok") " ) ");
break;
}
}
return resp.status;
}
static int CmdHF15AFIPassProtect(const char *Cmd) {
CLIParserContext *ctx;
@ -3512,6 +3610,7 @@ static command_t CommandTable[] = {
{"slixeasenable", CmdHF15SlixEASEnable, IfPm3Iso15693, "Enable EAS mode on SLIX ISO-15693 tag"},
{"slixprivacydisable", CmdHF15SlixDisable, IfPm3Iso15693, "Disable privacy mode on SLIX ISO-15693 tag"},
{"slixprivacyenable", CmdHF15SlixEnable, IfPm3Iso15693, "Enable privacy mode on SLIX ISO-15693 tag"},
{"slixprotectpage", CmdHF15SlixProtectPage, IfPm3Iso15693, "Protect pages on SLIX ISO-15693 tag"},
{"passprotectafi", CmdHF15AFIPassProtect, IfPm3Iso15693, "Password protect AFI - Cannot be undone"},
{"passprotecteas", CmdHF15EASPassProtect, IfPm3Iso15693, "Password protect EAS - Cannot be undone"},
{"-----------", CmdHF15Help, IfPm3Iso15693, "-------------------------- " _CYAN_("afi") " ------------------------"},

View file

@ -767,23 +767,35 @@ static bool emrtd_select_and_read(uint8_t *dataout, size_t *dataoutlen, uint16_t
static const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 };
static const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 };
static const uint8_t jpeg2k_cs_header[4] = { 0xFF, 0x4F, 0xFF, 0x51 };
static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path) {
size_t offset;
int datalen = 0;
char suffix[5] = { '\0' };
// This is a hacky impl that just looks for the image header. I'll improve it eventually.
// based on mrpkey.py
// Note: Doing file_length - 6 to account for the longest data we're checking.
// Checks first byte before the rest to reduce overhead
for (offset = 0; offset < file_length - 6; offset++) {
if ((file_contents[offset] == 0xFF && memcmp(jpeg_header, file_contents + offset, 4) == 0) ||
(file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0)) {
if (file_contents[offset] == 0xFF) {
if (memcmp(jpeg_header, file_contents + offset, 4) == 0) {
datalen = file_length - offset;
strcpy(suffix, ".jpg");
break;
} else if (memcmp(jpeg2k_cs_header, file_contents + offset, 4) == 0) {
datalen = file_length - offset;
// no standardized extension for codestream data, using .jpc
strcpy(suffix, ".jpc");
break;
}
} else if (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0) {
strcpy(suffix, ".jp2");
datalen = file_length - offset;
break;
}
}
// If we didn't get any data, return false.
if (datalen == 0) {
return PM3_ESOFT;
@ -797,7 +809,7 @@ static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const c
strncat(filepath, PATHSEP, 2);
strcat(filepath, dg_table[EF_DG2].filename);
saveFile(filepath, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen);
saveFile(filepath, suffix, file_contents + offset, datalen);
free(filepath);
return PM3_SUCCESS;
@ -1865,14 +1877,13 @@ static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_s
PrintAndLogEx(INFO, "------------------------ " _CYAN_("EF_SOD") " ------------------------");
PrintAndLogEx(INFO, "Document Security Object");
PrintAndLogEx(INFO, "contains the digital signatures of the passport data");
PrintAndLogEx(INFO, "");
if (hash_algo == -1) {
PrintAndLogEx(SUCCESS, "Hash algorithm... " _YELLOW_("Unknown"));
PrintAndLogEx(INFO, "");
} else {
PrintAndLogEx(SUCCESS, "Hash algorithm... " _YELLOW_("%s"), hashalg_table[hash_algo].name);
PrintAndLogEx(INFO, "");
uint8_t all_zeroes[64] = { 0x00 };

View file

@ -66,7 +66,6 @@ static uint8_t empty[PICOPASS_BLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
static uint8_t zeros[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static int CmdHelp(const char *Cmd);
static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len);
static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][PICOPASS_BLOCK_SIZE] = {
{ 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 },
@ -1346,10 +1345,7 @@ static int CmdHFiClassEView(const char *Cmd) {
PrintAndLogEx(NORMAL, "");
printIclassDumpContents(dump, 1, blocks, bytes, dense_output);
if (verbose) {
print_iclass_sio(dump, bytes);
}
print_iclass_sio(dump, bytes, verbose);
free(dump);
return PM3_SUCCESS;
@ -1400,7 +1396,7 @@ static int CmdHFiClassESetBlk(const char *Cmd) {
static bool iclass_detect_new_pacs(uint8_t *d) {
uint8_t n = 0;
while (n++ < (PICOPASS_BLOCK_SIZE / 2)) {
while (n++ < (PICOPASS_BLOCK_SIZE >> 1)) {
if (d[n] && d[n + 1] == 0xA6) {
return true;
}
@ -1418,7 +1414,7 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) {
uint8_t pad = d[offset];
PrintAndLogEx(INFO, "%u , %u", offset, pad);
PrintAndLogEx(DEBUG, "%u , %u", offset, pad);
char *binstr = (char *)calloc((PICOPASS_BLOCK_SIZE * 8) + 1, sizeof(uint8_t));
if (binstr == NULL) {
@ -1428,17 +1424,16 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) {
uint8_t n = PICOPASS_BLOCK_SIZE - offset - 2;
bytes_2_binstr(binstr, d + offset + 2, n);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n));
PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
PrintAndLogEx(DEBUG, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n));
PrintAndLogEx(DEBUG, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
binstr[strlen(binstr) - pad] = '\0';
PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
PrintAndLogEx(DEBUG, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
size_t hexlen = 0;
uint8_t hex[16] = {0};
binstr_2_bytes(hex, &hexlen, binstr);
PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen));
PrintAndLogEx(DEBUG, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen));
uint32_t top = 0, mid = 0, bot = 0;
if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) {
@ -1450,9 +1445,8 @@ static int iclass_decode_credentials_new_pacs(uint8_t *d) {
free(binstr);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Wiegand decode");
wiegand_message_t packed = initialize_message_object(top, mid, bot, 0);
HIDTryUnpack(&packed);
PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - Wiegand") " ----------------------------");
decode_wiegand(top, mid, bot, 0);
return PM3_SUCCESS;
}
@ -1473,8 +1467,7 @@ static void iclass_decode_credentials(uint8_t *data) {
if (has_values && encryption == None) {
// todo: remove preamble/sentinel
PrintAndLogEx(INFO, "Block 7 decoder");
PrintAndLogEx(INFO, "------------------------ " _CYAN_("Block 7 decoder") " --------------------------");
if (has_new_pacs) {
iclass_decode_credentials_new_pacs(b7);
} else {
@ -1489,11 +1482,8 @@ static void iclass_decode_credentials(uint8_t *data) {
char *pbin = binstr;
while (strlen(pbin) && *(++pbin) == '0');
PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), pbin);
PrintAndLogEx(INFO, "Wiegand decode");
wiegand_message_t packed = initialize_message_object(top, mid, bot, 0);
HIDTryUnpack(&packed);
PrintAndLogEx(SUCCESS, "Binary... %zu - " _GREEN_("%s"), strlen(pbin), pbin);
decode_wiegand(top, mid, bot, 0);
}
} else {
@ -1712,11 +1702,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
}
printIclassDumpContents(decrypted, 1, (decryptedlen / 8), decryptedlen, dense_output);
if (verbose) {
print_iclass_sio(decrypted, decryptedlen);
}
print_iclass_sio(decrypted, decryptedlen, verbose);
PrintAndLogEx(NORMAL, "");
// decode block 6
@ -1747,7 +1733,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) {
}
}
PrintAndLogEx(INFO, "-----------------------------------------------------------------");
PrintAndLogEx(INFO, "-------------------------------------------------------------------");
free(decrypted);
}
@ -2865,8 +2851,9 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) {
return PM3_SUCCESS;
bool use_sc = IsCardHelperPresent(verbose);
if (use_sc == false)
if (use_sc == false) {
return PM3_SUCCESS;
}
// crypto helper available.
PrintAndLogEx(INFO, "----------------------------- " _CYAN_("Cardhelper") " -----------------------------");
@ -2916,8 +2903,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) {
PrintAndLogEx(SUCCESS, " bin : %s", pbin);
PrintAndLogEx(INFO, "");
PrintAndLogEx(INFO, "------------------------------ " _CYAN_("Wiegand") " -------------------------------");
wiegand_message_t packed = initialize_message_object(top, mid, bot, 0);
HIDTryUnpack(&packed);
decode_wiegand(top, mid, bot, 0);
}
} else {
PrintAndLogEx(INFO, "no credential found");
@ -3187,7 +3173,7 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le
picopass_hdr_t *hdr = (picopass_hdr_t *)iclass_dump;
if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) {
if (memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE) == 0) {
// Legacy AIA
*is_legacy = true;
@ -3209,7 +3195,7 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le
}
}
}
} else if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) {
} else if (memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE) == 0) {
// SE AIA
*is_se = true;
@ -3241,28 +3227,32 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le
}
// print ASN1 decoded array in TLV view
static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len) {
void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose) {
bool is_legacy, is_se, is_sr;
uint8_t *sio_start;
size_t sio_length;
detect_credential(iclass_dump, dump_len, &is_legacy, &is_se, &is_sr, &sio_start, &sio_length);
// sanity checks
if (sio_start == NULL) {
return;
}
if (dump_len < sio_length + (sio_start - iclass_dump)) {
// SIO length exceeds the size of the dump we have, bail
// SIO length exceeds the size of the dump
return;
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "---------------------------- " _CYAN_("SIO - RAW") " ----------------------------");
PrintAndLogEx(INFO, "--------------------------- " _CYAN_("SIO - RAW") " -----------------------------");
print_hex_noascii_break(sio_start, sio_length, 32);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - ASN1 TLV") " --------------------------");
asn1_print(sio_start, sio_length, " ");
PrintAndLogEx(NORMAL, "");
if (verbose) {
PrintAndLogEx(INFO, "----------------------- " _CYAN_("SIO - ASN1 TLV") " ---------------------------");
asn1_print(sio_start, sio_length, " ");
PrintAndLogEx(NORMAL, "");
}
}
void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize, bool dense_output) {
@ -3460,8 +3450,9 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e
if (is_legacy)
PrintAndLogEx(HINT, _YELLOW_("yellow") " = legacy credential");
if (is_se)
if (is_se) {
PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SE credential");
}
if (is_sr)
PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SR credential");
@ -3519,10 +3510,7 @@ static int CmdHFiClassView(const char *Cmd) {
print_picopass_info((picopass_hdr_t *) dump);
printIclassDumpContents(dump, startblock, endblock, bytes_read, dense_output);
iclass_decode_credentials(dump);
if (verbose) {
print_iclass_sio(dump, bytes_read);
}
print_iclass_sio(dump, bytes_read, verbose);
free(dump);
return PM3_SUCCESS;
@ -3880,7 +3868,7 @@ static int CmdHFiClassCheckKeys(const char *Cmd) {
arg_lit0(NULL, "vb6kdf", "use the VB6 elite KDF instead of a file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIExecWithReturn(ctx, Cmd, argtable, false);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
@ -4661,11 +4649,11 @@ static int CmdHFiClassLookUp(const char *Cmd) {
memcpy(CCNR + 8, macs, 4);
memcpy(MAC_TAG, macs + 4, 4);
PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s"), sprint_hex(csn, sizeof(csn)));
PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, sizeof(epurse)));
PrintAndLogEx(SUCCESS, " MACS: %s", sprint_hex(macs, sizeof(macs)));
PrintAndLogEx(SUCCESS, " CCNR: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR)));
PrintAndLogEx(SUCCESS, "TAG MAC: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG)));
PrintAndLogEx(SUCCESS, "CSN....... " _GREEN_("%s"), sprint_hex(csn, sizeof(csn)));
PrintAndLogEx(SUCCESS, "Epurse.... %s", sprint_hex(epurse, sizeof(epurse)));
PrintAndLogEx(SUCCESS, "MACS...... %s", sprint_hex(macs, sizeof(macs)));
PrintAndLogEx(SUCCESS, "CCNR...... " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR)));
PrintAndLogEx(SUCCESS, "TAG MAC... %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG)));
// Run time
uint64_t t1 = msclock();
@ -4744,7 +4732,7 @@ typedef struct {
uint8_t use_raw;
uint8_t use_elite;
uint32_t keycnt;
uint8_t csn[8];
uint8_t csn[PICOPASS_BLOCK_SIZE];
uint8_t cc_nr[12];
uint8_t *keys;
union {
@ -4821,8 +4809,9 @@ void GenerateMacFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elite,
}
}
for (int i = 0; i < iclass_tc; i++)
for (int i = 0; i < iclass_tc; i++) {
pthread_join(threads[i], NULL);
}
}
static void *bf_generate_mackey(void *thread_arg) {
@ -5436,7 +5425,7 @@ static int CmdHFiClassSAM(const char *Cmd) {
data[0] = flags;
int cmdlen = 0;
if (CLIParamHexToBuf(arg_get_str(ctx, 8), data+1, PM3_CMD_DATA_SIZE-1, &cmdlen) != PM3_SUCCESS){
if (CLIParamHexToBuf(arg_get_str(ctx, 8), data + 1, PM3_CMD_DATA_SIZE - 1, &cmdlen) != PM3_SUCCESS) {
CLIParserFree(ctx);
return PM3_ESOFT;
}
@ -5448,7 +5437,7 @@ static int CmdHFiClassSAM(const char *Cmd) {
}
clearCommandBuffer();
SendCommandNG(CMD_HF_SAM_PICOPASS, data, cmdlen+1);
SendCommandNG(CMD_HF_SAM_PICOPASS, data, cmdlen + 1);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_SAM_PICOPASS, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "SAM timeout");
@ -5506,11 +5495,11 @@ static int CmdHFiClassSAM(const char *Cmd) {
const uint8_t *mediaType = oid + 2 + oid_length;
const uint8_t mediaType_data = mediaType[2];
PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data));
} else if(breakOnNrMac && d[0] == 0x05) {
PrintAndLogEx(SUCCESS, "Nr-MAC: " _GREEN_("%s"), sprint_hex_inrow(d+1, 8));
if(verbose){
} else if (breakOnNrMac && d[0] == 0x05) {
PrintAndLogEx(SUCCESS, "Nr-MAC: " _GREEN_("%s"), sprint_hex_inrow(d + 1, 8));
if (verbose) {
PrintAndLogEx(INFO, "Replay Nr-MAC to dump SIO:");
PrintAndLogEx(SUCCESS, " hf iclass dump -k \"%s\" --nr", sprint_hex_inrow(d+1, 8));
PrintAndLogEx(SUCCESS, " hf iclass dump -k \"%s\" --nr", sprint_hex_inrow(d + 1, 8));
}
} else {
print_hex(d, resp.length);

View file

@ -43,4 +43,5 @@ uint32_t picopass_elite_rng(void);
uint32_t picopass_elite_lcg(void);
uint8_t picopass_elite_nextByte(void);
void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uint8_t *keyBlock);
void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose);
#endif

View file

@ -31,6 +31,7 @@
#include "protocols.h"
#include "cmdhficlass.h"
#include "mifare/mifaredefault.h" // mifare consts
#include "cmdhfseos.h"
enum MifareAuthSeq {
masNone,
@ -70,8 +71,14 @@ static uint8_t *gs_mfuc_key = NULL;
*/
uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) {
if (n < 3) return 2;
if (isResponse && (n == 5)) return 2;
if (n < 3) {
return 2;
}
if (isResponse && (n == 5)) {
return 2;
}
if (d[1] == 0x50 &&
d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT &&
d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) {
@ -80,6 +87,25 @@ uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) {
return check_crc(CRC_14443_A, d, n);
}
uint8_t seos_CRC_check(bool isResponse, uint8_t *d, uint8_t n) {
if (n < 3) {
return 2;
}
// 5 bytes response Card busy 0xFA have crc, the rest is most likely 14a anticollision
if ((n == 5) && (d[0] != 0xFA)) {
return 2;
}
if (d[1] == 0x50 &&
d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT &&
d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) {
return 2;
}
return check_crc(CRC_14443_A, d, n);
}
uint8_t mifare_CRC_check(bool isResponse, uint8_t *data, uint8_t len) {
switch (MifareAuthState) {
case masNone:
@ -273,9 +299,11 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i
snprintf(exp, size, "HALT");
MifareAuthState = masNone;
break;
case ISO14443A_CMD_RATS:
snprintf(exp, size, "RATS - FSDI=%x, CID=%x", (cmd[1] & 0xF0) >> 4, (cmd[1] & 0x0F));
case ISO14443A_CMD_RATS: {
uint16_t fsdi2fsd[] = {16, 24, 32, 40, 48, 64, 96, 128, 256, 512, 1024, 2048, 4096, 4096, 4096, 4096};
snprintf(exp, size, "RATS - FSDI=%x (FSD=%u), CID=%x", (cmd[1] & 0xF0) >> 4, fsdi2fsd[(cmd[1] & 0xF0) >> 4], (cmd[1] & 0x0F));
break;
}
/* Actually, PPSS is Dx
case ISO14443A_CMD_PPS:
snprintf(exp, size, "PPS");
@ -421,14 +449,14 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i
snprintf(exp, size, "?");
break;
}
case NTAG_I2C_FASTWRITE:
case NTAG_I2C_FASTWRITE: {
if (cmdsize == 69)
snprintf(exp, size, "FAST WRITE (" _MAGENTA_("%d-%d") ")", cmd[1], cmd[2]);
else
snprintf(exp, size, "?");
break;
default:
}
default: {
if ((cmd[0] & 0xF0) == 0xD0 && (cmdsize == 4 || cmdsize == 5)) {
snprintf(exp, size, "PPS - CID=%x", cmd[0] & 0x0F) ;
} else if ((cmd[0] & 0xF0) == 0x60 && (cmdsize == 4)) {
@ -437,8 +465,11 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i
} else {
return PM3_ESOFT;
}
}
}
} else {
if (gs_mfuc_state == 1) {
if ((cmd[0] == 0xAF) && (cmdsize == 11)) {
// register RndB
@ -448,6 +479,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i
gs_mfuc_state = 0;
}
}
if (gs_mfuc_state == 3) {
if ((cmd[0] == 0x00) && (cmdsize == 11)) {
// register RndA'
@ -1746,49 +1778,115 @@ void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
}
}
void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool isResponse) {
if (cmd[0] == 0xFA && cmdsize == 5) {
snprintf(exp, size, (isResponse) ? "BUSY" : "DONE?");
return;
}
// it's basically a ISO14443a tag, so try annotation from there
if (applyIso14443a(exp, size, cmd, cmdsize, false) != PM3_SUCCESS) {
int pos = 0;
switch (cmd[0]) {
case 2:
case 3:
pos = 2;
break;
case 0:
case 2:
case 3: {
pos = 1;
break;
default:
}
default: {
pos = 2;
break;
}
}
if (memcmp(cmd + pos, "\x00\xa4\x04\x00\x0a", 5) == 0) {
snprintf(exp, size, "SELECT AID");
if (memcmp(cmd + pos, "\x00\xA4\x04\x00", 4) == 0) {
uint8_t n = cmd[pos + 4];
snprintf(exp, size, "SELECT AID " _WHITE_("%s"), sprint_hex_inrow(cmd + pos + 4 + 1, n));
return;
}
if (memcmp(cmd + pos, "\x80\xA5\x00\x00", 4) == 0) {
snprintf(exp, size, "SELECT GDF");
return;
}
if (memcmp(cmd + pos, "\x80\xA5\x04\x00", 4) == 0) {
snprintf(exp, size, "SELECT ADF / OID");
uint8_t n = cmd[pos + 4 + 2];
snprintf(exp, size, "SELECT OID " _WHITE_("%s"), sprint_hex_inrow(cmd + pos + 4 + 2 + 1, n));
return;
}
if (memcmp(cmd + pos, "\x00\x87\x00\x01\x04\x7c\x02\x81\x00", 9) == 0) {
snprintf(exp, size, "GET CHALLENGE");
if (memcmp(cmd + pos, "\x80\xA5\x07", 3) == 0) {
uint8_t ks = cmd[pos + 3];
snprintf(exp, size, "SELECT GDF " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks);
return;
}
if (memcmp(cmd + pos, "\x00\x87\x00\x01\x2c", 5) == 0) {
snprintf(exp, size, "MUTUAL AUTHENTICATION");
if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) {
uint8_t ks = cmd[pos + 3];
if (memcmp(cmd + pos + 3 + 1, "\x04\x7c\x02\x81\x00", 5) == 0) {
snprintf(exp, size, "GET CHALLENGE " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks);
}
return;
}
if (memcmp(cmd + pos, "\x0c\xcb\x3f\xff", 4) == 0) {
if (memcmp(cmd + pos, "\x00\x87\x00", 3) == 0) {
uint8_t ks = cmd[pos + 3];
if (memcmp(cmd + pos + 3 + 1, "\x2C\x7C\x2A\x82\x28", 5) == 0) {
snprintf(exp, size, "MUTUAL AUTHENTICATION " _WHITE_("(") " key " _MAGENTA_("%02X") " )", ks);
}
return;
}
if (memcmp(cmd + pos, "\x0C\xCB\x3F\xFF", 4) == 0) {
snprintf(exp, size, "GET DATA");
return;
}
// apply ISO7816 annotations?
// if (annotateIso7816(exp, size, cmd, cmdsize) == 0) {
// }
// apply SEOS annotations?
if (memcmp(cmd + pos, "\x0C\xDB\x3F\xFF", 4) == 0) {
snprintf(exp, size, "UPDATE DATA");
return;
}
if (memcmp(cmd + pos, "\x0C\xED\x06\x00", 4) == 0) {
snprintf(exp, size, "DELETE DATA");
return;
}
if (memcmp(cmd + pos, "\x0C\x41\x0C\x03", 4) == 0) {
snprintf(exp, size, "CREATE ADF");
return;
}
if (isResponse) {
if (memcmp(cmd + pos, "\xCD\x02", 2) == 0) {
uint8_t ea = cmd[pos + 2];
uint8_t ha = cmd[pos + 3];
char eas[10] = {0};
if (ea == SEOS_ENCRYPTION_2K3DES) {
strcat(eas, "2K3DES");
} else if (ea == SEOS_ENCRYPTION_3K3DES) {
strcat(eas, "3K3DES");
} else if (ea == SEOS_ENCRYPTION_AES) {
strcat(eas, "AES");
}
char has[10] = {0};
if (ha == SEOS_HASHING_SHA1) {
strcat(has, "SHA1");
} else if (ha == SEOS_HASHING_SHA256) {
strcat(has, "SHA256");
}
snprintf(exp, size, "%s / %s", eas, has);
return;
}
}
}
}
@ -1828,11 +1926,13 @@ void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
uint16_t address = (cmd[2] << 7) | cmd[1] >> 1;
if (cmdBit == LEGIC_READ)
if (cmdBit == LEGIC_READ) {
snprintf(exp, size, "READ Byte(%d)", address);
}
if (cmdBit == LEGIC_WRITE)
if (cmdBit == LEGIC_WRITE) {
snprintf(exp, size, "WRITE Byte(%d)", address);
}
break;
}
case 21: {
@ -1852,8 +1952,9 @@ void annotateLegic(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
break;
}
case 12:
default:
default: {
break;
}
}
}

View file

@ -46,6 +46,7 @@ uint8_t felica_CRC_check(uint8_t *d, uint8_t n);
uint8_t mifare_CRC_check(bool isResponse, uint8_t *data, uint8_t len);
uint8_t iso15693_CRC_check(uint8_t *d, uint8_t n);
uint8_t iclass_CRC_check(bool isResponse, uint8_t *d, uint8_t n);
uint8_t seos_CRC_check(bool isResponse, uint8_t *d, uint8_t n);
int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response);
@ -68,7 +69,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize,
void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);
void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);
void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize);
void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool isResponse);
bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount);
bool NTParityChk(AuthData_t *ad, uint32_t ntx);

View file

@ -682,7 +682,7 @@ static int CmdHfLTOWriteBlock(const char *Cmd) {
int res = wrblLTO(blk, block_data, true);
if (res == PM3_SUCCESS)
PrintAndLogEx(HINT, "Try use 'hf lto rdbl' for verification");
PrintAndLogEx(HINT, "Try `" _YELLOW_("hf lto rdbl") "` to verify");
return res;
}

View file

@ -70,7 +70,6 @@ static int usage_hf14_keybrute(void) {
int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) {
int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFC);
PrintAndLogEx(NORMAL, "");
return originality_check_print(signature, signature_len, index);
}
@ -122,13 +121,6 @@ static char *GenerateFilename(const char *prefix, const char *suffix) {
// Each entry also stores whether the key was "found", defaults to false (0)
static int initSectorTable(sector_t **src, size_t items) {
// typedef struct {
// uint64_t Key[2];
// uint8_t foundKey[2];
// } sector_t;
// This allocates based on the size of a single item
(*src) = calloc(items, sizeof(sector_t));
if (*src == NULL) {
return PM3_EMALLOC;
@ -307,43 +299,43 @@ static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) {
if (blockno >= MIFARE_1K_MAXBLOCK) {
PrintAndLogEx(INFO,
_BACK_BLUE_("%s| %3d | " _YELLOW_("%s"))
_BACK_BLUE_(_MAGENTA_("%s"))
_BACK_BLUE_("%02X ")
_BACK_BLUE_(_YELLOW_("%s"))
_BACK_BLUE_("| " _YELLOW_("%s"))
,
secstr,
blockno,
keya,
acl,
d[9],
keyb,
ascii
);
_BACK_BLUE_("%s| %3d | " _YELLOW_("%s"))
_BACK_BLUE_(_MAGENTA_("%s"))
_BACK_BLUE_("%02X ")
_BACK_BLUE_(_YELLOW_("%s"))
_BACK_BLUE_("| " _YELLOW_("%s"))
,
secstr,
blockno,
keya,
acl,
d[9],
keyb,
ascii
);
} else {
PrintAndLogEx(INFO, "%s| %3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"),
secstr,
blockno,
keya,
acl,
d[9],
keyb,
ascii
);
secstr,
blockno,
keya,
acl,
d[9],
keyb,
ascii
);
}
} else {
int32_t value = 0;
if (verbose && mfc_value(d, &value)) {
PrintAndLogEx(INFO, "%s| %3d | " _CYAN_("%s") " %"PRIi32, secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value);
}
if (blockno >= MIFARE_1K_MAXBLOCK) {
// MFC Ev1 signature blocks.
PrintAndLogEx(INFO, _BACK_BLUE_("%s| %3d | %s"), secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE));
} else {
PrintAndLogEx(INFO, "%s| %3d | %s", secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE));
int32_t value = 0;
if (verbose && mfc_value(d, &value)) {
PrintAndLogEx(INFO, "%s| %3d | " _CYAN_("%s") " %"PRIi32, secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE), value);
} else {
PrintAndLogEx(INFO, "%s| %3d | %s", secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE));
}
}
}
}
@ -746,6 +738,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n
free(fptr);
free(keyA);
free(keyB);
PrintAndLogEx(SUCCESS, "\nSucceeded in dumping all blocks");
return PM3_SUCCESS ;
}
@ -2138,7 +2131,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) {
e_sector[sectorNo].foundKey[trgKeyType] = 1;
e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6);
// mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false);
// mf_check_keys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false);
continue;
default :
PrintAndLogEx(ERR, "unknown error.\n");
@ -2542,8 +2535,8 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
int outfnlen = 0;
char outfilename[127] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)outfilename, 127, &outfnlen);
char outfilename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)outfilename, FILE_PATH_SIZE, &outfnlen);
bool slow = arg_get_lit(ctx, 7);
@ -2739,7 +2732,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) {
}
// read uid to generate a filename for the key file
char suffix[FILE_PATH_SIZE];
char suffix[FILE_PATH_SIZE + strlen(outfilename)];
if (outfnlen) {
snprintf(suffix, sizeof(suffix) - strlen(outfilename), "-key-%s.bin", outfilename);
} else {
@ -2955,7 +2948,7 @@ noValidKeyFound:
// Try the found keys are reused
if (bytes_to_num(tmp_key, MIFARE_KEY_SIZE) != 0) {
// <!> The fast check --> mfCheckKeys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false, verbose);
// <!> The fast check --> mf_check_keys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false, verbose);
// <!> Returns false keys, so we just stick to the slower mfchk.
for (int i = 0; i < sector_cnt; i++) {
for (int j = MF_KEY_A; j <= MF_KEY_B; j++) {
@ -4251,6 +4244,7 @@ static int CmdHF14AMfSim(const char *Cmd) {
PrintAndLogEx(INFO, "Note: option -e implies -i");
flags |= FLAG_INTERACTIVE;
}
if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) {
PrintAndLogEx(WARNING, "Option -e requires -x or -y");
return PM3_EINVARG;
@ -4263,8 +4257,9 @@ static int CmdHF14AMfSim(const char *Cmd) {
, (uidlen == 0) ? "n/a" : sprint_hex(uid, uidlen)
);
PrintAndLogEx(INFO, "Options [ numreads: %d, flags: 0x%04x ]"
PrintAndLogEx(INFO, "Options [ numreads: %d, flags: %d (0x%04x) ]"
, exitAfterNReads
, flags
, flags);
struct {
@ -4277,46 +4272,56 @@ static int CmdHF14AMfSim(const char *Cmd) {
payload.flags = flags;
payload.exitAfter = exitAfterNReads;
memcpy(payload.uid, uid, uidlen);
payload.atqa = (atqa[1] << 8) | atqa[0];
payload.sak = sak[0];
clearCommandBuffer();
if (flags & FLAG_INTERACTIVE) {
if ((flags & FLAG_INTERACTIVE) == FLAG_INTERACTIVE) {
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or a key to abort simulation");
} else {
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or send another cmd to abort simulation");
}
bool cont;
do {
cont = false;
SendCommandNG(CMD_HF_MIFARE_SIMULATE, (uint8_t *)&payload, sizeof(payload));
if (flags & FLAG_INTERACTIVE) {
if ((flags & FLAG_INTERACTIVE) == FLAG_INTERACTIVE) {
PacketResponseNG resp;
sector_t *k_sector = NULL;
bool keypress = kbd_enter_pressed();
while (keypress == false) {
if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) {
if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) {
keypress = kbd_enter_pressed();
continue;
}
if (resp.status != PM3_SUCCESS)
if (resp.status != PM3_SUCCESS) {
break;
}
if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK)
if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) {
break;
}
const nonces_t *data = (nonces_t *)resp.data.asBytes;
readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose);
if (setEmulatorMem) {
cont = true;
}
break;
}
if (keypress) {
if ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) {
// inform device to break the sim loop since client has exited
@ -5485,7 +5490,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) {
return PM3_EFILE;
}
PrintAndLogEx(INFO, "Copying to magic gen1a card");
PrintAndLogEx(INFO, "Copying to magic gen1a MIFARE Classic " _GREEN_("%s"), s);
PrintAndLogEx(INFO, "." NOLF);
int blockno = 0;
@ -5533,7 +5538,11 @@ static int CmdHF14AMfCLoad(const char *Cmd) {
return PM3_EFILE;
}
PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from file", block_cnt);
PrintAndLogEx(SUCCESS,
"Card loaded " _YELLOW_("%d") " blocks from %s"
, block_cnt
, (fill_from_emulator ? "emulator memory" : "file")
);
PrintAndLogEx(INFO, "Done!");
return PM3_SUCCESS;
}
@ -6293,8 +6302,7 @@ static int CmdHF14AMfMAD(const char *Cmd) {
PrintAndLogEx(SUCCESS, "Binary... " _GREEN_("%s"), pbin);
PrintAndLogEx(INFO, "Wiegand decode");
wiegand_message_t packed = initialize_message_object(top, mid, bot, 0);
HIDTryUnpack(&packed);
decode_wiegand(top, mid, bot, 0);
}
}
@ -7851,7 +7859,7 @@ static int CmdHF14AMfView(const char *Cmd) {
if (bytes_read == MIFARE_MINI_MAX_BYTES)
block_cnt = MIFARE_MINI_MAXBLOCK;
else if (bytes_read == MIFARE_1K_EV1_MAX_BYTES)
block_cnt = MIFARE_1K_EV1_MAXBLOCK;
block_cnt = MIFARE_1K_EV1_MAXBLOCK;
else if (bytes_read == MIFARE_2K_MAX_BYTES)
block_cnt = MIFARE_2K_MAXBLOCK;
else if (bytes_read == MIFARE_4K_MAX_BYTES)
@ -8252,13 +8260,14 @@ static int CmdHF14AGen4Load(const char *cmd) {
arg_param_begin,
arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"),
arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"),
arg_lit0(NULL, "1k+", "MIFARE Classic Ev1 1k / S50"),
arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"),
arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"),
arg_str0("p", "pwd", "<hex>", "password 4bytes"),
arg_lit0("v", "verbose", "verbose output"),
arg_str0("f", "file", "<fn>", "Specify a filename for dump file"),
arg_lit0(NULL, "emu", "from emulator memory"),
arg_int0(NULL, "start", "<dec>", "index of block to start writing (default 0)"),
arg_int0(NULL, "start", "<dec>", "index of block to start writing (def 0)"),
arg_int0(NULL, "end", "<dec>", "index of block to end writing (default last block)"),
arg_param_end
};
@ -8266,23 +8275,24 @@ static int CmdHF14AGen4Load(const char *cmd) {
CLIExecWithReturn(ctx, cmd, argtable, false);
bool m0 = arg_get_lit(ctx, 1);
bool m1 = arg_get_lit(ctx, 2);
bool m2 = arg_get_lit(ctx, 3);
bool m4 = arg_get_lit(ctx, 4);
bool m1ev1 = arg_get_lit(ctx, 3);
bool m2 = arg_get_lit(ctx, 4);
bool m4 = arg_get_lit(ctx, 5);
int pwd_len = 0;
uint8_t pwd[4] = {0};
CLIGetHexWithReturn(ctx, 5, pwd, &pwd_len);
CLIGetHexWithReturn(ctx, 6, pwd, &pwd_len);
bool verbose = arg_get_lit(ctx, 6);
bool verbose = arg_get_lit(ctx, 7);
int fnlen = 0;
char filename[FILE_PATH_SIZE] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
CLIParamStrToBuf(arg_get_str(ctx, 8), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
bool fill_from_emulator = arg_get_lit(ctx, 8);
bool fill_from_emulator = arg_get_lit(ctx, 9);
int start = arg_get_int_def(ctx, 9, 0);
int end = arg_get_int_def(ctx, 10, -1);
int start = arg_get_int_def(ctx, 10, 0);
int end = arg_get_int_def(ctx, 11, -1);
CLIParserFree(ctx);
@ -8292,14 +8302,14 @@ static int CmdHF14AGen4Load(const char *cmd) {
return PM3_EINVARG;
}
if ((m0 + m1 + m2 + m4) > 1) {
if ((m0 + m1 + m2 + m4 + m1ev1) > 1) {
PrintAndLogEx(WARNING, "Only specify one MIFARE Type");
return PM3_EINVARG;
} else if ((m0 + m1 + m2 + m4) == 0) {
} else if ((m0 + m1 + m2 + m4 + m1ev1) == 0) {
m1 = true;
}
char s[6];
char s[8];
memset(s, 0, sizeof(s));
uint16_t block_cnt = MIFARE_1K_MAXBLOCK;
if (m0) {
@ -8308,6 +8318,9 @@ static int CmdHF14AGen4Load(const char *cmd) {
} else if (m1) {
block_cnt = MIFARE_1K_MAXBLOCK;
strncpy(s, "1K", 3);
} else if (m1ev1) {
block_cnt = MIFARE_1K_EV1_MAXBLOCK;
strncpy(s, "1K Ev1", 7);
} else if (m2) {
block_cnt = MIFARE_2K_MAXBLOCK;
strncpy(s, "2K", 3);
@ -8386,16 +8399,13 @@ static int CmdHF14AGen4Load(const char *cmd) {
}
if (verbose) {
if (fnlen != 0) {
PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename);
PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt);
} else {
if (fnlen == 0) {
PrintAndLogEx(INFO, "Read %d blocks from emulator memory", block_cnt);
}
}
PrintAndLogEx(INFO, "Copying to magic gen4 GTU MIFARE Classic " _GREEN_("%s"), s);
PrintAndLogEx(INFO, "Starting block: %d. Ending block: %d.", start, end);
PrintAndLogEx(INFO, "Block... %d - %d", start, end);
// copy to card
for (uint16_t blockno = start; blockno <= end; blockno++) {
@ -8423,10 +8433,15 @@ static int CmdHF14AGen4Load(const char *cmd) {
}
PrintAndLogEx(NORMAL, "\n");
if (data != NULL) free(data);
if (data != NULL) {
free(data);
}
PrintAndLogEx(SUCCESS, "Card loaded " _YELLOW_("%d") " blocks from %s", end - start + 1,
(fill_from_emulator ? "emulator memory" : "file"));
PrintAndLogEx(SUCCESS,
"Card loaded " _YELLOW_("%d") " blocks from %s"
, end - start + 1
, (fill_from_emulator ? "emulator memory" : "file")
);
PrintAndLogEx(INFO, "Done!");
return PM3_SUCCESS;
}
@ -9623,7 +9638,7 @@ static int CmdHF14AMfInfo(const char *Cmd) {
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) {
PrintAndLogEx(DEBUG, "iso14443a card select timeout");
return 0;
return PM3_ETIMEOUT;
}
iso14a_card_select_t card;
@ -9721,9 +9736,9 @@ static int CmdHF14AMfInfo(const char *Cmd) {
}
}
uint8_t k08s[6] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F};
uint8_t k08[6] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1};
uint8_t k32[6] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60};
uint8_t k08s[MIFARE_KEY_SIZE] = {0xA3, 0x96, 0xEF, 0xA4, 0xE2, 0x4F};
uint8_t k08[MIFARE_KEY_SIZE] = {0xA3, 0x16, 0x67, 0xA8, 0xCE, 0xC1};
uint8_t k32[MIFARE_KEY_SIZE] = {0x51, 0x8B, 0x33, 0x54, 0xE7, 0x60};
if (mf_read_block(0, 4, k08s, blockdata) == PM3_SUCCESS) {
PrintAndLogEx(SUCCESS, "Backdoor key..... " _YELLOW_("%s"), sprint_hex_inrow(k08s, sizeof(k08s)));
fKeyType = MF_KEY_BD;
@ -9818,7 +9833,7 @@ static int CmdHF14AMfInfo(const char *Cmd) {
}
if (e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) {
PrintAndLogEx(SUCCESS, "Dorma Kaba SAFLOK detected");
PrintAndLogEx(SUCCESS, "dormakaba Saflok detected");
}
} else {
@ -9841,7 +9856,7 @@ static int CmdHF14AMfInfo(const char *Cmd) {
res = detect_classic_static_nonce();
if (res == NONCE_STATIC) {
PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes"));
PrintAndLogEx(SUCCESS, "Static nonce... " _YELLOW_("yes"));
}
@ -10219,7 +10234,6 @@ static command_t CommandTable[] = {
{"gdmparsecfg", CmdHF14AGen4_GDM_ParseCfg, AlwaysAvailable, "Parse config block to card"},
{"gdmsetblk", CmdHF14AGen4_GDM_SetBlk, IfPm3Iso14443a, "Write block to card"},
{"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"},
// {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"},
{"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"},
{"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"},
{"ndefwrite", CmdHFMFNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"},

View file

@ -175,14 +175,26 @@ typedef struct aidhdr {
} PACKED aidhdr_t;
typedef struct {
const uint32_t aidnum;
const char *aid;
const char *comment;
} mfdesCommonAID_t;
/*
PACS application id(s) - HID Factory, CP1000 Standard, Mobile, Custom and Elite
We have HID Factory, Field Encoder == CP1000 (?)
No mobile, Custom or Elite
*/
static const mfdesCommonAID_t commonAids[] = {
// AID, name/comment
{ "\xf4\x81\x2f", "Gallagher card data application" },
{ "\xf4\x81\x20", "Gallagher card application directory" }, // Can be 0xF48120 - 0xF4812B, but I've only ever seen 0xF48120
{ 0x53494F, "\x53\x49\x4F", "SIO DESFire EV1 - HID Factory" },
{ 0xD3494F, "\xD3\x49\x4F", "SIO DESFire EV1 - Field Encoder" },
{ 0xD9494F, "\xD9\x49\x4F", "SIO DESFire EV1 - Field Encoder" },
{ 0xF484E3, "\xF4\x84\xE3", "SE Enhanced" },
{ 0xF484E4, "\xF4\x84\xE4", "SE Enhanced" },
{ 0xF4812F, "\xf4\x81\x2f", "Gallagher card data application" },
{ 0xF48120, "\xf4\x81\x20", "Gallagher card application directory" }, // Can be 0xF48120 - 0xF4812B, but I've only ever seen 0xF48120
{ 0xF47300, "\xf4\x73\x00", "Inner Range card application" },
};
static int CmdHelp(const char *Cmd);
@ -311,15 +323,13 @@ static char *getTypeStr(uint8_t type) {
return buf;
}
static char noCommentStr[1] = { 0x00 };
static const char *getAidCommentStr(uint8_t *aid) {
static const char *getAidCommentStr(uint32_t aid) {
for (int i = 0; i < ARRAYLEN(commonAids); i++) {
if (memcmp(aid, commonAids[i].aid, 3) == 0) {
if (aid == commonAids[i].aidnum) {
return commonAids[i].comment;
}
}
return noCommentStr;
return "";
}
static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
@ -355,6 +365,10 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
if (type == 0x81 && major == 0x42 && minor == 0x00)
return DESFIRE_EV2;
// Apple Wallet DESFire Applet
if (type == 0x91 && major == 0x62 && minor == 0x01)
return DESFIRE_EV2;
// Plus EV1
if (type == 0x02 && major == 0x11 && minor == 0x00)
return PLUS_EV1;
@ -377,7 +391,7 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
// ref: https://www.nxp.com/docs/en/application-note/AN12343.pdf p7
static nxp_producttype_t getProductType(const uint8_t *versionhw) {
uint8_t product = versionhw[2];
uint8_t product = versionhw[1];
if (product == 0x01)
return DESFIRE_PHYSICAL;
@ -394,7 +408,7 @@ static nxp_producttype_t getProductType(const uint8_t *versionhw) {
static const char *getProductTypeStr(const uint8_t *versionhw) {
uint8_t product = versionhw[2];
uint8_t product = versionhw[1];
if (product == 0x01)
return "MIFARE DESFire native IC (physical card)";
@ -454,7 +468,6 @@ int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, si
}
int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFDES);
PrintAndLogEx(NORMAL, "");
return originality_check_print(signature, signature_len, index);
}
@ -752,7 +765,7 @@ static int CmdHF14ADesInfo(const char *Cmd) {
if (major == 2 && minor == 2)
PrintAndLogEx(INFO, "\t2.2 - DESFire Ev2 XL, Originality check, proximity check, EAL5");
if (major == 3 && minor == 0)
PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6 ?");
PrintAndLogEx(INFO, "\t3.0 - DESFire Ev3, Originality check, proximity check, badass EAL6");
if (major == 0xA0 && minor == 0)
PrintAndLogEx(INFO, "\tx.x - DUOX, Originality check, proximity check, EAL6++");
@ -801,12 +814,16 @@ static int CmdHF14ADesInfo(const char *Cmd) {
}
if (aidbuflen > 2) {
uint8_t j = aidbuflen / 3;
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list"));
PrintAndLogEx(SUCCESS, "AIDs: " NOLF);
for (int i = 0; i < aidbuflen; i += 3)
PrintAndLogEx(NORMAL, "%s %06x" NOLF, (i == 0) ? "" : ",", DesfireAIDByteToUint(&aidbuf[i]));
PrintAndLogEx(NORMAL, "\n");
PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list") " ( " _YELLOW_("%u") " found )", j);
j = 0;
for (int i = 0; i < aidbuflen; i += 3, j++) {
uint32_t aid = DesfireAIDByteToUint(&aidbuf[i]);
PrintAndLogEx(SUCCESS, _YELLOW_("%06X") ", %s", aid, getAidCommentStr(aid));
}
}
DesfireFillPICCInfo(&dctx, &PICCInfo, true);
@ -817,7 +834,7 @@ static int CmdHF14ADesInfo(const char *Cmd) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Free memory"));
if (PICCInfo.freemem != 0xffffffff) {
PrintAndLogEx(SUCCESS, " Available free memory on card : " _GREEN_("%d bytes"), PICCInfo.freemem);
PrintAndLogEx(SUCCESS, " Available free memory on card... " _GREEN_("%d") " bytes", PICCInfo.freemem);
} else {
PrintAndLogEx(SUCCESS, " Card doesn't support 'free mem' cmd");
}
@ -1805,7 +1822,7 @@ static int CmdHF14aDesMAD(const char *Cmd) {
AppListS AppList = {{0}};
DesfireFillAppList(&dctx, &PICCInfo, AppList, false, false, false); // no deep scan, no scan files
PrintAndLogEx(SUCCESS, "# Applications... " _GREEN_("%zu"), PICCInfo.appCount);
PrintAndLogEx(SUCCESS, "# Applications.... " _GREEN_("%zu"), PICCInfo.appCount);
if (PICCInfo.freemem == 0xffffffff) {
PrintAndLogEx(SUCCESS, "Free memory...... " _YELLOW_("n/a"));
} else {
@ -3265,11 +3282,8 @@ static int CmdHF14ADesGetAIDs(const char *Cmd) {
if (buflen >= 3) {
PrintAndLogEx(INFO, "---- " _CYAN_("AID list") " ----");
for (int i = 0; i < buflen; i += 3) {
const char *commentStr = getAidCommentStr(&buf[i]);
if ((void *) commentStr == &noCommentStr)
PrintAndLogEx(INFO, "AID: %06x", DesfireAIDByteToUint(&buf[i]));
else
PrintAndLogEx(INFO, "AID: %06x (%s)", DesfireAIDByteToUint(&buf[i]), commentStr);
uint32_t aid = DesfireAIDByteToUint(&buf[i]);
PrintAndLogEx(SUCCESS, _YELLOW_("%06X") " %s", aid, getAidCommentStr(aid));
}
} else {
PrintAndLogEx(INFO, "There is no applications on the card");
@ -5590,7 +5604,7 @@ static int CmdHF14ADesLsApp(const char *Cmd) {
SetAPDULogging(APDULogging);
CLIParserFree(ctx);
PrintAndLogEx(INPLACE, _YELLOW_("It may take up to 15 seconds. Processing...."));
PrintAndLogEx(INFO, "It may take up to " _YELLOW_("15") " seconds. Processing...");
res = DesfireSelectAndAuthenticateEx(&dctx, securechann, 0x000000, noauth, verbose);
if (res != PM3_SUCCESS) {
@ -5602,7 +5616,6 @@ static int CmdHF14ADesLsApp(const char *Cmd) {
AppListS AppList = {{0}};
DesfireFillAppList(&dctx, &PICCInfo, AppList, !nodeep, scanfiles, true);
printf("\33[2K\r"); // clear current line before printing
PrintAndLogEx(NORMAL, "");
// print zone

View file

@ -192,7 +192,6 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) {
// --- GET SIGNATURE
static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) {
int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFP);
PrintAndLogEx(NORMAL, "");
return originality_check_print(signature, signature_len, index);
}
@ -1813,7 +1812,7 @@ static int CmdHFMFPMAD(const char *Cmd) {
}
uint8_t sector0[16 * 4] = {0};
uint8_t sector10[16 * 4] = {0};
uint8_t sector16[16 * 4] = {0};
if (mfpReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector0, verbose)) {
PrintAndLogEx(NORMAL, "");
@ -1833,19 +1832,19 @@ static int CmdHFMFPMAD(const char *Cmd) {
MAD1DecodeAndPrint(sector0, swapmad, verbose, &haveMAD2);
if (haveMAD2) {
if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector10, verbose)) {
if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector16, verbose)) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(ERR, "error, read sector " _YELLOW_("0x10") ". Card doesn't have MAD or doesn't have MAD on default keys");
return PM3_ESOFT;
}
MAD2DecodeAndPrint(sector10, swapmad, verbose);
MAD2DecodeAndPrint(sector16, swapmad, verbose);
}
if (aidlen == 2 || decodeholder) {
uint16_t mad[7 + 8 + 8 + 8 + 8] = {0};
size_t madlen = 0;
if (MADDecode(sector0, sector10, mad, &madlen, swapmad)) {
if (MADDecode(sector0, sector16, mad, &madlen, swapmad)) {
PrintAndLogEx(ERR, "can't decode MAD");
return PM3_EWRONGANSWER;
}
@ -1990,9 +1989,9 @@ int CmdHFMFPNDEFRead(const char *Cmd) {
memcpy(ndefkey, key, 16);
}
uint8_t sector0[16 * 4] = {0};
uint8_t sector10[16 * 4] = {0};
uint8_t data[4096] = {0};
uint8_t sector0[MIFARE_1K_MAXBLOCK] = {0};
uint8_t sector16[MIFARE_1K_MAXBLOCK] = {0};
uint8_t data[MIFARE_4K_MAX_BYTES] = {0};
int datalen = 0;
if (verbose)
@ -2016,7 +2015,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) {
if (verbose)
PrintAndLogEx(INFO, "reading MAD v2 sector");
if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector10, verbose)) {
if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector16, verbose)) {
PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys");
PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -k `") " with your custom key");
return PM3_ESOFT;
@ -2025,7 +2024,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) {
uint16_t mad[7 + 8 + 8 + 8 + 8] = {0};
size_t madlen = 0;
res = MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen, false);
res = MADDecode(sector0, (haveMAD2 ? sector16 : NULL), mad, &madlen, false);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "can't decode MAD");
return res;
@ -2034,7 +2033,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) {
PrintAndLogEx(INFO, "reading data from tag");
for (int i = 0; i < madlen; i++) {
if (ndefAID == mad[i]) {
uint8_t vsector[16 * 4] = {0};
uint8_t vsector[MIFARE_1K_MAXBLOCK] = {0};
if (mfpReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector, false)) {
PrintAndLogEx(ERR, "error, reading sector %d", i + 1);
return PM3_ESOFT;

View file

@ -68,7 +68,7 @@ static uint8_t default_aes_keys[][16] = {
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF
{ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33
{ 0x47, 0x45, 0x4D, 0x58, 0x50, 0x52, 0x45, 0x53, 0x53, 0x4F, 0x53, 0x41, 0x4D, 0x50, 0x4C, 0x45 }, // gemalto
{ 0x56, 0x4c, 0x67, 0x56, 0x99, 0x69, 0x64, 0x9f, 0x17, 0xC6, 0xC6, 0x16, 0x01, 0x10, 0x4D, 0xCA } // Virtual Dorma Kaba
{ 0x56, 0x4c, 0x67, 0x56, 0x99, 0x69, 0x64, 0x9f, 0x17, 0xC6, 0xC6, 0x16, 0x01, 0x10, 0x4D, 0xCA } // Virtual dormakaba
};
static uint8_t default_3des_keys[][16] = {
@ -669,6 +669,9 @@ static int try_default_aes_keys(bool override) {
}
static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool hasAuthKey, uint8_t *authkey, uint8_t *pack, uint8_t packSize) {
if (ul_select(card) == false) {
return PM3_ESOFT;
}
if (hasAuthKey && (tagtype & MFU_TT_UL_C)) {
//will select card automatically and close connection on error
@ -676,12 +679,7 @@ static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool has
PrintAndLogEx(WARNING, "Authentication Failed UL-C");
return PM3_ESOFT;
}
} else {
if (ul_select(card) == false) {
return PM3_ESOFT;
}
if (hasAuthKey) {
if (ulev1_requestAuthentication(authkey, pack, packSize) == PM3_EWRONGANSWER) {
DropField();
@ -1419,7 +1417,6 @@ static int ulev1_print_signature(uint64_t tagtype, uint8_t *uid, uint8_t *signat
} else if (signature_len == 48) {
index = originality_check_verify(uid, 7, signature, signature_len, PK_MFULAES);
}
PrintAndLogEx(NORMAL, "");
return originality_check_print(signature, signature_len, index);
}
@ -2069,6 +2066,7 @@ uint64_t GetHF14AMfU_Type(void) {
uint8_t version[10] = {0x00};
int len = ulev1_getVersion(version, sizeof(version));
DropField();
switch (len) {
case 0x0A: {
/*
@ -2160,6 +2158,7 @@ uint64_t GetHF14AMfU_Type(void) {
tagtype = MFU_TT_UNKNOWN;
break;
}
// This is a test from cards that doesn't answer to GET_VERSION command
// UL vs UL-C vs NTAG203 vs FUDAN FM11NT021 (which is NTAG213 compatiable)
if (tagtype & (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203)) {
@ -2451,7 +2450,6 @@ static int CmdHF14AMfUInfo(const char *Cmd) {
}
// check signature
int index = originality_check_verify_ex(card.uid, 7, signature, sizeof(signature), PK_ST25TN, false, true);
PrintAndLogEx(NORMAL, "");
originality_check_print(signature, sizeof(signature), index);
}
@ -4071,8 +4069,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) {
PacketResponseNG resp;
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_READBL, 2, 0, 0, NULL, 0);
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
PrintAndLogEx(WARNING, "command execution time out");
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
@ -4084,8 +4082,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) {
// block 1 write and block2 write
hf14a_config config;
SendCommandNG(CMD_HF_ISO14443A_GET_CONFIG, NULL, 0);
if (!WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000)) {
PrintAndLogEx(WARNING, "command execution time out");
if (WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000) == false) {
PrintAndLogEx(WARNING, "command execute timeout");
return PM3_ETIMEOUT;
}
memcpy(&config, resp.data.asBytes, sizeof(hf14a_config));
@ -4103,8 +4101,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) {
data[3] = 0x88 ^ uid[0] ^ uid[1] ^ uid[2];
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 0, 0, 0, data, sizeof(data));
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
PrintAndLogEx(WARNING, "command execution time out");
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
@ -4115,8 +4113,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) {
data[3] = uid[6];
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 1, 0, 0, data, sizeof(data));
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
PrintAndLogEx(WARNING, "command execution time out");
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}
@ -4127,8 +4125,8 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) {
data[3] = oldblock2[3];
clearCommandBuffer();
SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, 2, 0, 0, data, sizeof(data));
if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) {
PrintAndLogEx(WARNING, "command execution time out");
if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) {
PrintAndLogEx(WARNING, "Command execute timeout");
return PM3_ETIMEOUT;
}

View file

@ -753,16 +753,15 @@ static int select_ADF_decrypt(const char *selectADFOID, uint8_t *CRYPTOGRAM_encr
}
}
return PM3_SUCCESS;
return PM3_ESOFT;
};
static int seos_mutual_auth(uint8_t *randomICC, uint8_t *CRYPTOGRAM_Diversifier, uint8_t diversifier_len, uint8_t *mutual_auth_randomIFD, uint8_t *mutual_auth_keyICC, uint8_t *randomIFD, uint8_t randomIFD_len, uint8_t *keyIFD, uint8_t keyIFD_len, int encryption_algorithm, int hash_algorithm, int key_index) {
uint8_t response[PM3_CMD_DATA_SIZE];
// ---------------- Diversify Keys ----------------
uint8_t undiversified_key[16] = { 0x00 };
memcpy(undiversified_key, keys[key_index].readKey, 16);
uint8_t mk[16] = { 0x00 };
memcpy(mk, keys[key_index].readKey, 16);
uint8_t keyslot = 0x01; // up to 0x0F
uint8_t AES_key[24] = {0x00};
uint8_t MAC_key[24] = {0x00};
@ -776,8 +775,8 @@ static int seos_mutual_auth(uint8_t *randomICC, uint8_t *CRYPTOGRAM_Diversifier,
return PM3_ESOFT;
}
seos_kdf(true, undiversified_key, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, AES_key, encryption_algorithm, hash_algorithm);
seos_kdf(false, undiversified_key, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, MAC_key, encryption_algorithm, hash_algorithm);
seos_kdf(true, mk, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, AES_key, encryption_algorithm, hash_algorithm);
seos_kdf(false, mk, keyslot, adfOID, sizeof(adfOID), CRYPTOGRAM_Diversifier, diversifier_len, MAC_key, encryption_algorithm, hash_algorithm);
memcpy(&MAC_key[16], &MAC_key[0], 8);
memcpy(&AES_key[16], &AES_key[0], 8);
@ -843,7 +842,7 @@ static int seos_mutual_auth(uint8_t *randomICC, uint8_t *CRYPTOGRAM_Diversifier,
bool activate_field = false;
bool keep_field_on = true;
uint8_t aMUTUAL_AUTH[102];
uint8_t aMUTUAL_AUTH[102] = {0};
int aMUTUAL_AUTH_n = 0;
param_gethex_to_eol(mutual_auth, 0, aMUTUAL_AUTH, sizeof(aMUTUAL_AUTH), &aMUTUAL_AUTH_n);
int res = ExchangeAPDU14a(aMUTUAL_AUTH, aMUTUAL_AUTH_n, activate_field, keep_field_on, response, sizeof(response), &resplen);
@ -976,7 +975,9 @@ static int seos_aid_select(void) {
// if we made it here, its a success and we break :)
break;
}
if (i == ARRAYLEN(known_AID_map)) {
return PM3_ESOFT;
}
return res;
};
@ -1000,7 +1001,7 @@ static int seos_pacs_adf_select(char *oid, int oid_len, uint8_t *get_data, int g
uint16_t selectedOIDLen = strlen(selectedOID);
char selectedOIDLenHex[3];
snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen) / 2);
snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen >> 1) & 0xFF);
char selectedADF[strlen(ADFprefix) + strlen(selectedOIDLenHex) + selectedOIDLen + 1];
snprintf(selectedADF, sizeof(selectedADF), "%s%s%s", ADFprefix, selectedOIDLenHex, selectedOID);
@ -1112,10 +1113,9 @@ static int seos_adf_select(char *oid, int oid_len, int key_index) {
const char *ADFprefix = "06";
char selectedOID[100];
snprintf(selectedOID, sizeof(selectedOID), "%s", oid);
uint16_t selectedOIDLen = strlen(selectedOID);
char selectedOIDLenHex[3];
snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen) / 2);
snprintf(selectedOIDLenHex, sizeof(selectedOIDLenHex), "%02X", (selectedOIDLen >> 1) & 0xFF);
char selectedADF[strlen(ADFprefix) + strlen(selectedOIDLenHex) + selectedOIDLen + 1];
snprintf(selectedADF, sizeof(selectedADF), "%s%s%s", ADFprefix, selectedOIDLenHex, selectedOID);
@ -1381,7 +1381,7 @@ static int CmdHfSeosPACS(const char *Cmd) {
uint8_t get_data[] = {0x5c, 0x02, 0xff, 0x00};
int oid_len = 0;
uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02};
uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02, 0x02};
CLIGetHexWithReturn(ctx, 1, oid_hex, &oid_len);
int key_index = arg_get_int_def(ctx, 2, 0);
@ -1390,7 +1390,7 @@ static int CmdHfSeosPACS(const char *Cmd) {
// Fall back to default OID
if (oid_len == 0) {
oid_len = 16;
oid_len = 17;
}
// convert OID hex to literal string
@ -1440,7 +1440,7 @@ static int CmdHfSeosADF(const char *Cmd) {
CLIGetHexWithReturn(ctx, 1, get_data, &get_data_len);
int oid_len = 0;
uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02};
uint8_t oid_hex[256] = {0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x01, 0x18, 0x01, 0x01, 0x02, 0x02};
CLIGetHexWithReturn(ctx, 2, oid_hex, &oid_len);
int key_index = arg_get_int_def(ctx, 3, 0);
@ -1453,7 +1453,7 @@ static int CmdHfSeosADF(const char *Cmd) {
// Catching when the OID value is not supplied
if (oid_len == 0) {
oid_len = 16;
oid_len = 17;
}
// convert OID hex to literal string
@ -1669,7 +1669,7 @@ static int CmdHfSeosSAM(const char *Cmd) {
data[0] = flags;
int cmdlen = 0;
if (CLIParamHexToBuf(arg_get_str(ctx, 5), data+1, PM3_CMD_DATA_SIZE-1, &cmdlen) != PM3_SUCCESS){
if (CLIParamHexToBuf(arg_get_str(ctx, 5), data + 1, PM3_CMD_DATA_SIZE - 1, &cmdlen) != PM3_SUCCESS) {
CLIParserFree(ctx);
return PM3_ESOFT;
}
@ -1681,7 +1681,7 @@ static int CmdHfSeosSAM(const char *Cmd) {
}
clearCommandBuffer();
SendCommandNG(CMD_HF_SAM_SEOS, data, cmdlen+1);
SendCommandNG(CMD_HF_SAM_SEOS, data, cmdlen + 1);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_SAM_SEOS, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "SAM timeout");
@ -1756,7 +1756,7 @@ static command_t CommandTable[] = {
{"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"},
{"sam", CmdHfSeosSAM, IfPm3Smartcard, "SAM tests"},
{"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"},
{"info", CmdHfSeosInfo, IfPm3NfcBarcode, "Tag information"},
{"info", CmdHfSeosInfo, IfPm3Iso14443a, "Tag information"},
{"pacs", CmdHfSeosPACS, AlwaysAvailable, "Extract PACS Information from card"},
{"adf", CmdHfSeosADF, AlwaysAvailable, "Read an ADF from the card"},
{"gdf", CmdHfSeosGDF, AlwaysAvailable, "Read an GDF from card"},

View file

@ -21,6 +21,12 @@
#include "common.h"
#define SEOS_ENCRYPTION_2K3DES 0x02
#define SEOS_ENCRYPTION_3K3DES 0x03
#define SEOS_ENCRYPTION_AES 0x09
#define SEOS_HASHING_SHA1 0x06
#define SEOS_HASHING_SHA256 0x07
int infoSeos(bool verbose);
int CmdHFSeos(const char *Cmd);
int seos_kdf(bool encryption, uint8_t *masterKey, uint8_t keyslot,

View file

@ -150,7 +150,6 @@ static void print_st25ta_system_info(uint8_t *d, uint8_t n) {
static int print_st25ta_signature(uint8_t *uid, uint8_t *signature) {
int index = originality_check_verify_ex(uid, 7, signature, 32, PK_ST25TA, false, true);
PrintAndLogEx(NORMAL, "");
return originality_check_print(signature, 32, index);
}

View file

@ -108,6 +108,8 @@ static void em410x_construct_emul_graph(uint8_t *uid, uint8_t clock, uint8_t gap
// print 64 bit EM410x ID in multiple formats
void printEM410x(uint32_t hi, uint64_t id, bool verbose, int type) {
if (!id && !hi) return;
if (verbose == false) {
if (type & 0x1) { // Short ID
PrintAndLogEx(SUCCESS, "EM 410x ID "_GREEN_("%010" PRIX64), id);
@ -252,6 +254,11 @@ static int ask_em410x_binary_decode(bool verbose, uint32_t *hi, uint64_t *lo, ui
return PM3_ESOFT;
}
if (!lo && !hi) {
PrintAndLogEx(DEBUG, "DEBUG: Error - Em410x decoded to all zeros");
return PM3_ESOFT;
}
PrintAndLogEx(DEBUG, "DEBUG: Em410x idx: %zu, Len: %zu, Printing DemodBuffer:", *idx, *size);
if (g_debugMode) {
printDemodBuff(0, false, false, true);

View file

@ -30,12 +30,10 @@
static int CmdHelp(const char *Cmd);
// Each record is 4 bytes long ... a single line in the dump output
// Reads each record from `data`, reverses the four bytes, and writes to `words`
static void em4x50_prepare_result(const uint8_t *data, int first_record_inclusive, int last_record_inclusive, em4x50_word_t *words) {
static void em4x50_prepare_result(const uint8_t *data, int fwr, int lwr, em4x50_word_t *words) {
// restructure received result in "em4x50_word_t" structure
for (int i = first_record_inclusive; i <= last_record_inclusive; i++) {
for (int i = fwr; i <= lwr; i++) {
for (int j = 0; j < 4; j++) {
words[i].byte[j] = data[i * 4 + (3 - j)];
}
@ -641,8 +639,10 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) {
return PM3_ESOFT;
}
em4x50_read_data_response_t *o = (em4x50_read_data_response_t *)resp.data.asBytes;
em4x50_word_t words[EM4X50_NO_WORDS] = {0};
em4x50_prepare_result(resp.data.asBytes, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words);
em4x50_prepare_result((uint8_t *)o->words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words);
if (out != NULL) {
memcpy(out, &words, sizeof(em4x50_word_t) * EM4X50_NO_WORDS);

View file

@ -98,7 +98,7 @@ static int demod_guard_raw(uint8_t *raw, uint8_t rlen) {
// but will leave the g_GraphBuffer intact.
// if successful it will push askraw data back to g_DemodBuffer ready for emulation
int demodGuard(bool verbose) {
(void) verbose; // unused so far
(void) verbose;
//Differential Biphase
//get binary from ask wave
if (ASKbiphaseDemod(0, 64, 0, 0, false) != PM3_SUCCESS) {
@ -285,7 +285,7 @@ static int CmdGuardClone(const char *Cmd) {
return PM3_EINVARG;
}
fmtlen &= 0x7f;
fmtlen &= 0x7F;
uint32_t facilitycode = (fc & 0x000000FF);
uint32_t cardnumber = (cn & 0x00FFFFFF);
@ -317,7 +317,7 @@ static int CmdGuardClone(const char *Cmd) {
free(bs);
PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with Facility Code: " _GREEN_("%u") " Card Number: " _GREEN_("%u") " xorKey: " _GREEN_("%u")
PrintAndLogEx(INFO, "Preparing to clone Guardall to " _YELLOW_("%s") " with fc: " _GREEN_("%u") " cn: " _GREEN_("%u") " xor: " _GREEN_("%u")
, cardtype
, facilitycode
, cardnumber
@ -375,7 +375,7 @@ static int CmdGuardSim(const char *Cmd) {
return PM3_ESOFT;
}
PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - xorKey: " _YELLOW_("%u") " Facility Code: " _YELLOW_("%u") " CardNumber: " _YELLOW_("%u")
PrintAndLogEx(SUCCESS, "Simulating Guardall Prox - xorKey: " _YELLOW_("%u") " fc: " _YELLOW_("%u") " cn: " _YELLOW_("%u")
, xorval
, facilitycode
, cardnumber

View file

@ -160,8 +160,7 @@ int demodHID(bool verbose) {
return PM3_ESOFT;
}
wiegand_message_t packed = initialize_message_object(hi2, hi, lo, 0);
if (HIDTryUnpack(&packed) == false) {
if (!decode_wiegand(hi2, hi, lo, 0)) { // if failed to unpack wiegand
printDemodBuff(0, false, false, true);
}
PrintAndLogEx(INFO, "raw: " _GREEN_("%08x%08x%08x"), hi2, hi, lo);
@ -544,6 +543,7 @@ static int CmdHIDBrute(const char *Cmd) {
}
wiegand_card_t card_hi, card_low;
cardformatdescriptor_t card_descriptor = HIDGetCardFormat(format_idx).Fields;
memset(&card_hi, 0, sizeof(wiegand_card_t));
char field[3] = {0};
@ -575,11 +575,13 @@ static int CmdHIDBrute(const char *Cmd) {
PrintAndLogEx(INFO, "Facility code.... %u", card_hi.FacilityCode);
PrintAndLogEx(INFO, "Card number...... %" PRIu64, card_hi.CardNumber);
PrintAndLogEx(INFO, "Delay............ " _YELLOW_("%d"), delay);
if (strcmp(field, "fc") == 0) {
PrintAndLogEx(INFO, "Field............ " _YELLOW_("fc"));
} else if (strcmp(field, "cn") == 0) {
PrintAndLogEx(INFO, "Field............ " _YELLOW_("cn"));
}
switch (direction) {
case 0:
PrintAndLogEx(INFO, "Direction........ " _YELLOW_("both"));
@ -594,6 +596,7 @@ static int CmdHIDBrute(const char *Cmd) {
break;
}
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Started bruteforcing HID Prox reader");
PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or " _GREEN_("<Enter>") " to abort simulation");
@ -623,13 +626,13 @@ static int CmdHIDBrute(const char *Cmd) {
return PM3_ESOFT;
}
if (strcmp(field, "fc") == 0) {
if (card_hi.FacilityCode < 0xFF) {
if (card_hi.FacilityCode < card_descriptor.MaxFC) {
card_hi.FacilityCode++;
} else {
fin_hi = true;
}
} else if (strcmp(field, "cn") == 0) {
if (card_hi.CardNumber < 0xFFFF) {
if (card_hi.CardNumber < card_descriptor.MaxCN) {
card_hi.CardNumber++;
} else {
fin_hi = true;

View file

@ -105,8 +105,8 @@ static int CmdLFPCF7931Config(const char *Cmd) {
arg_lit0("r", "reset", "Reset configuration to default values"),
arg_str0("p", "pwd", "<hex>", "Password, 7bytes, LSB-order"),
arg_u64_0("d", "delay", "<dec>", "Tag initialization delay (in us)"),
arg_int0(NULL, "lw", "<dec>", "offset, low pulses width (in us)"),
arg_int0(NULL, "lp", "<dec>", "offset, low pulses position (in us)"),
arg_int0(NULL, "lw", "<dec>", "offset, low pulses width (in us), optional!"),
arg_int0(NULL, "lp", "<dec>", "offset, low pulses position (in us), optional!"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);

View file

@ -305,6 +305,7 @@ static int CmdPyramidClone(const char *Cmd) {
uint8_t *bs = calloc(128, sizeof(uint8_t));
if (bs == NULL) {
PrintAndLogEx(WARNING, "failed to allocate memory");
return PM3_EMALLOC;
}

View file

@ -1550,6 +1550,7 @@ bool testKnownConfigBlock(uint32_t block0) {
case T55X7_NEXWATCH_CONFIG_BLOCK:
case T55X7_JABLOTRON_CONFIG_BLOCK:
case T55X7_PYRONIX_CONFIG_BLOCK:
case T55X7_TEXECOM_CONFIG_BLOCK:
return true;
}
return false;
@ -2298,6 +2299,9 @@ static void printT5x7KnownBlock0(uint32_t b0) {
case T55X7_PYRONIX_CONFIG_BLOCK:
snprintf(s + strlen(s), sizeof(s) - strlen(s), "Pyronix ");
break;
case T55X7_TEXECOM_CONFIG_BLOCK:
snprintf(s + strlen(s), sizeof(s) - strlen(s), "Texecom ");
break;
default:
break;
}

View file

@ -43,6 +43,7 @@
#define T55X7_SECURAKEY_CONFIG_BLOCK 0x000C8060 // ASK, Manchester, data rate 40, 3 data blocks
#define T55X7_UNK_CONFIG_BLOCK 0x000880FA // ASK, Manchester, data rate 32, 7 data blocks STT, Inverse ...
#define T55X7_PYRONIX_CONFIG_BLOCK 0x00088C40 // ASK, Manchester, data rate 32, 2 data blocks
#define T55X7_TEXECOM_CONFIG_BLOCK 0x001C8020 // ASK, Manchester, data rate 128, 1 data block
// FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag
// we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo,

View file

@ -596,8 +596,8 @@ static int PivGetData(Iso7816CommandChannel channel, const uint8_t tag[], size_t
// Answer can be chained. Let's use a dynamically allocated buffer.
size_t capacity = PM3_CMD_DATA_SIZE;
struct tlvdb_root *root = calloc(1, sizeof(*root) + capacity);
if (root == NULL) {
PrintAndLogEx(WARNING, "failed to allocate memory");
return PM3_EMALLOC;
}
root->len = 0;

View file

@ -1067,8 +1067,10 @@ static int CmdSmartBruteforceSFI(const char *Cmd) {
smart_loadjson("aidlist", &root);
uint8_t *buf = calloc(PM3_CMD_DATA_SIZE, sizeof(uint8_t));
if (!buf)
if (buf == NULL) {
PrintAndLogEx(WARNING, "failed to allocate memory");
return PM3_EMALLOC;
}
PrintAndLogEx(INFO, "Selecting card");
if (!smart_select(false, NULL)) {

View file

@ -164,6 +164,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
}
// extract MFC
/*
switch (frame[0]) {
case MIFARE_AUTH_KEYA: {
if (data_len > 3) {
@ -176,9 +177,11 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
break;
}
}
*/
// extract MFU-C
// extract MFU-C KEY when written.
switch (frame[0]) {
case MIFARE_ULC_AUTH_1: {
if (data_len != 4) {
break;
@ -195,7 +198,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
break;
}
PrintAndLogEx(INFO, "MFU-C AUTH");
PrintAndLogEx(INFO, "Found a MFU-C authententication attempt");
PrintAndLogEx(INFO, "3DES %s " NOLF, sprint_hex_inrow(next_hdr->frame + 1, 8));
next_hdr = (tracelog_hdr_t *)(trace + tracepos);
@ -203,6 +206,8 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
if (next_hdr->frame[0] == MIFARE_ULC_AUTH_2 && next_hdr->data_len == 19) {
PrintAndLogEx(NORMAL, "%s", sprint_hex_inrow(next_hdr->frame + 1, 16));
} else {
PrintAndLogEx(NORMAL, "( " _RED_("partial") " )");
}
return tracepos;
@ -323,7 +328,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
case MFDES_AUTHENTICATE: {
// Assume wrapped or unwrapped
PrintAndLogEx(INFO, "AUTH NATIVE (keyNo %d)", frame[pos + long_jmp]);
PrintAndLogEx(INFO, "Found a MFDES Auth NATIVE (keyNo %d)", frame[pos + long_jmp]);
if (next_record_is_response(tracepos, trace) == false) {
break;
}
@ -348,7 +353,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
}
case MFDES_AUTHENTICATE_ISO: {
// Assume wrapped or unwrapped
PrintAndLogEx(INFO, "AUTH ISO (keyNo %d)", frame[pos + long_jmp]);
PrintAndLogEx(INFO, "Found a MFDES Auth ISO (keyNo %d)", frame[pos + long_jmp]);
if (next_record_is_response(tracepos, trace) == false) {
break;
}
@ -379,7 +384,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
}
case MFDES_AUTHENTICATE_AES: {
// Assume wrapped or unwrapped
PrintAndLogEx(INFO, "AUTH AES (keyNo %d)", frame[pos + long_jmp]);
PrintAndLogEx(INFO, "Found a MFDES Auth AES (keyNo %d)", frame[pos + long_jmp]);
if (next_record_is_response(tracepos, trace)) {
break;
}
@ -403,7 +408,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
return tracepos;
}
case MFDES_AUTHENTICATE_EV2F: {
PrintAndLogEx(INFO, "AUTH EV2 First");
PrintAndLogEx(INFO, "Found a MFDES Auth EV2 First");
uint16_t tmp = extractChall_ev2(tracepos, trace, pos, long_jmp);
if (tmp == 0)
break;
@ -412,7 +417,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t
}
case MFDES_AUTHENTICATE_EV2NF: {
PrintAndLogEx(INFO, "AUTH EV2 Non First");
PrintAndLogEx(INFO, "Found a MFDES Auth EV2 Non First");
uint16_t tmp = extractChall_ev2(tracepos, trace, pos, long_jmp);
if (tmp == 0)
break;
@ -556,9 +561,11 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
case ISO_14443A:
case MFDES:
case LTO:
case SEOS:
crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len);
break;
case SEOS:
crcStatus = seos_CRC_check(hdr->isResponse, frame, data_len);
break;
case ISO_7816_4:
crcStatus = iso14443A_CRC_check(hdr->isResponse, frame, data_len) == 1 ? 3 : 0;
crcStatus = iso14443B_CRC_check(frame, data_len) == 1 ? 4 : crcStatus;
@ -798,6 +805,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
case ICLASS:
annotateIclass(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
break;
case SEOS:
annotateSeos(explanation, sizeof(explanation), frame, data_len, hdr->isResponse);
break;
default:
break;
}
@ -834,9 +844,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr
case PROTO_CRYPTORF:
annotateCryptoRF(explanation, sizeof(explanation), frame, data_len);
break;
case SEOS:
annotateSeos(explanation, sizeof(explanation), frame, data_len);
break;
case PROTO_FMCOS20:
annotateFMCOS20(explanation, sizeof(explanation), frame, data_len);
break;

View file

@ -33,6 +33,46 @@
static int CmdHelp(const char *Cmd);
#define PACS_EXTRA_LONG_FORMAT 18 // 144 bits
#define PACS_LONG_FORMAT 12 // 96 bits
#define PACS_FORMAT 6 // 44 bits
static int wiegand_new_pacs(uint8_t *padded_pacs, uint8_t plen) {
uint8_t d[PACS_EXTRA_LONG_FORMAT] = {0};
memcpy(d, padded_pacs, plen);
uint8_t pad = d[0];
char *binstr = (char *)calloc((PACS_EXTRA_LONG_FORMAT * 8) + 1, sizeof(uint8_t));
if (binstr == NULL) {
PrintAndLogEx(INFO, "failed to allocate memory");
return PM3_EMALLOC;
}
uint8_t n = plen - 1;
bytes_2_binstr(binstr, d + 1, n);
binstr[strlen(binstr) - pad] = '\0';
size_t tlen = 0;
uint8_t tmp[16] = {0};
binstr_2_bytes(tmp, &tlen, binstr);
PrintAndLogEx(SUCCESS, "Wiegand raw.... " _YELLOW_("%s"), sprint_hex_inrow(tmp, tlen));
uint32_t top = 0, mid = 0, bot = 0;
if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) {
PrintAndLogEx(ERR, "Binary string contains none <0|1> chars");
free(binstr);
return PM3_EINVARG;
}
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - Wiegand") " ---------------------------");
decode_wiegand(top, mid, bot, strlen(binstr));
free(binstr);
return PM3_SUCCESS;
}
int CmdWiegandList(const char *Cmd) {
CLIParserContext *ctx;
@ -116,16 +156,18 @@ int CmdWiegandDecode(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "wiegand decode",
"Decode raw hex or binary to wiegand format",
"wiegand decode --raw 2006f623ae"
"wiegand decode --raw 2006F623AE\n"
"wiegand decode --new 06BD88EB80 -> 4..8 bytes, new padded format "
);
void *argtable[] = {
arg_param_begin,
arg_str0("r", "raw", "<hex>", "raw hex to be decoded"),
arg_str0("b", "bin", "<bin>", "binary string to be decoded"),
arg_str0("n", "new", "<hex>", "new padded pacs as raw hex to be decoded"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
CLIExecWithReturn(ctx, Cmd, argtable, false);
int hlen = 0;
char hex[40] = {0};
CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)hex, sizeof(hex), &hlen);
@ -133,6 +175,11 @@ int CmdWiegandDecode(const char *Cmd) {
int blen = 0;
uint8_t binarr[100] = {0x00};
int res = CLIParamBinToBuf(arg_get_str(ctx, 2), binarr, sizeof(binarr), &blen);
int plen = 0;
uint8_t phex[8] = {0};
res = CLIParamHexToBuf(arg_get_str(ctx, 3), phex, sizeof(phex), &plen);
CLIParserFree(ctx);
if (res) {
@ -155,13 +202,20 @@ int CmdWiegandDecode(const char *Cmd) {
return PM3_EINVARG;
}
PrintAndLogEx(INFO, "Input bin len... %d", blen);
} else if (plen) {
return wiegand_new_pacs(phex, plen);
} else {
PrintAndLogEx(ERR, "Empty input");
return PM3_EINVARG;
}
wiegand_message_t packed = initialize_message_object(top, mid, bot, blen);
HIDTryUnpack(&packed);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "------------------------- " _CYAN_("Wiegand") " ---------------------------");
decode_wiegand(top, mid, bot, blen);
return PM3_SUCCESS;
}

View file

@ -107,6 +107,37 @@ int asn1_print(uint8_t *asn1buf, size_t asn1buflen, const char *indent) {
return PM3_SUCCESS;
}
int asn1_get_tag_length(const uint8_t *data, size_t *n, size_t *offset, size_t total_length) {
if (*offset >= total_length) {
return -1;
}
if (data[*offset] & 0x80) {
// Long form: number of length bytes is indicated by the lower 7 bits
size_t len_bytes = data[*offset] & 0x7F;
*offset += 1;
if (*offset + len_bytes > total_length) {
return -1;
}
*n = 0;
for (size_t i = 0; i < len_bytes; i++) {
*n = (*n << 8) | data[*offset];
*offset += 1;
}
} else {
// Short form: length is directly represented
*n = data[*offset];
*offset += 1;
}
return 0;
}
typedef struct {
const char *hex;

View file

@ -25,6 +25,8 @@
int asn1_print(uint8_t *asn1buf, size_t asn1buflen, const char *indent);
int ecdsa_asn1_get_signature(uint8_t *signature, size_t signaturelen, uint8_t *rval, uint8_t *sval);
int asn1_get_tag_length(const uint8_t *data, size_t *n, size_t *offset, size_t total_length);
int asn1_selftest(void);
#endif /* asn1utils.h */

View file

@ -136,25 +136,28 @@ const ecdsa_publickey_ng_t manufacturer_public_keys[] = {
};
// return pk if match index else -1
// returns index of pk if match else -1
int originality_check_verify(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type) {
return originality_check_verify_ex(data, data_len, signature, signature_len, type, false, false);
}
int originality_check_verify_ex(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t signature_len, pk_type_t type, bool reverse, bool hash) {
// test if signature is null
// test if signature is all zeros
bool is_zero = true;
for (uint8_t i = 0; i < signature_len; i++) {
if (signature[i] != 0) {
is_zero = false;
break;
}
}
if (is_zero) {
return -1;
}
uint8_t tmp_data[data_len];
uint8_t tmp_signature[signature_len];
if (reverse) {
reverse_array_copy(data, data_len, tmp_data);
reverse_array_copy(signature, signature_len, tmp_signature);
@ -180,17 +183,24 @@ int originality_check_verify_ex(uint8_t *data, uint8_t data_len, uint8_t *signat
}
int originality_check_print(uint8_t *signature, int signature_len, int index) {
PrintAndLogEx(NORMAL, "");
if ((index < 0) || (index >= ARRAYLEN(manufacturer_public_keys))) {
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16));
if (signature_len > 16) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16));
}
if (signature_len > 32) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16));
}
if (signature_len > 48) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48));
}
PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed"));
return PM3_ESOFT;
}
@ -200,23 +210,30 @@ int originality_check_print(uint8_t *signature, int signature_len, int index) {
if (manufacturer_public_keys[index].keylen > 16) {
PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 32);
}
if (manufacturer_public_keys[index].keylen > 32) {
PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 64);
}
if (manufacturer_public_keys[index].keylen > 48) {
PrintAndLogEx(INFO, " : %.32s", manufacturer_public_keys[index].value + 96);
}
PrintAndLogEx(INFO, " Elliptic curve parameters: %s", mbedtls_ecp_curve_info_from_grp_id(manufacturer_public_keys[index].grp_id)->name);
PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 16));
if (signature_len > 16) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 16, 16));
}
if (signature_len > 32) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 32, 16));
}
if (signature_len > 48) {
PrintAndLogEx(INFO, " : %s", sprint_hex_inrow(signature + 48, signature_len - 48));
}
PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful"));
return PM3_SUCCESS;
}

View file

@ -2122,9 +2122,9 @@ static int CmdEMVScan(const char *Cmd) {
uint8_t psenum = (channel == CC_CONTACT) ? 1 : 2;
char filename[FILE_PATH_SIZE] = {0};
int fnlen = 0;
CLIParamStrToBuf(arg_get_str(ctx, 12), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen);
uint8_t filename[FILE_PATH_SIZE] = {0};
int filenamelen = sizeof(filename) - 1; // CLIGetStrWithReturn does not guarantee string to be null-terminated
CLIGetStrWithReturn(ctx, 12, filename, &filenamelen);
CLIParserFree(ctx);
@ -2507,7 +2507,7 @@ static int CmdEMVRoca(const char *Cmd) {
void *argtable[] = {
arg_param_begin,
arg_lit0("t", "selftest", "Self test"),
arg_lit0(NULL, "test", "Perform self tests"),
arg_lit0("a", "apdu", "Show APDU requests and responses"),
arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"),
arg_param_end
@ -2981,7 +2981,7 @@ static command_t CommandTable[] = {
{"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"},
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdEMVList, AlwaysAvailable, "List ISO7816 history"},
{"test", CmdEMVTest, AlwaysAvailable, "Crypto logic selftest"},
{"test", CmdEMVTest, AlwaysAvailable, "Perform crypto logic self tests"},
{"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("Operations") " ---------------------"},
{"challenge", CmdEMVGenerateChallenge, IfPm3Iso14443, "Generate challenge"},
{"exec", CmdEMVExec, IfPm3Iso14443, "Executes EMV contactless transaction"},

View file

@ -809,7 +809,7 @@ int saveFileJSONrootEx(const char *preferredName, const void *root, size_t flags
if (res == 0) {
if (verbose) {
PrintAndLogEx(SUCCESS, "Saved to json file `" _YELLOW_("%s") "`", filename);
PrintAndLogEx(SUCCESS, "Saved to json file " _YELLOW_("%s"), filename);
}
free(filename);
return PM3_SUCCESS;
@ -1105,7 +1105,9 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) {
int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, nfc_df_e ft) {
if (data == NULL) return PM3_EINVARG;
if (data == NULL) {
return PM3_EINVARG;
}
*datalen = 0;
int retval = PM3_SUCCESS;
@ -1137,16 +1139,17 @@ int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, s
memset(line, 0, sizeof(line));
if (fgets(line, sizeof(line), f) == NULL) {
if (feof(f))
if (feof(f)) {
break;
}
fclose(f);
PrintAndLogEx(FAILED, "file reading error");
return PM3_EFILE;
}
if (line[0] == '#')
if (line[0] == '#') {
continue;
}
str_cleanrn(line, sizeof(line));
str_lower(line);
@ -2409,7 +2412,6 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo
// larger keys than expected is skipped
if (strlen(line) > keylen) {
PrintAndLogEx(INFO, "larger %zu - %s", strlen(line), line);
continue;
}
@ -2626,6 +2628,7 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool
fclose(f);
if (verbose) {
switch (*dump_type) {
case NFC_DF_MFU:
PrintAndLogEx(INFO, "Detected MIFARE Ultralight / NTAG based dump format");
@ -3081,6 +3084,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl
PrintAndLogEx(WARNING, "fail, cannot allocate memory");
return PM3_EMALLOC;
}
res = loadFileJSON(fn, *pdump, maxdumplen, dumplen, NULL);
if (res == PM3_SUCCESS) {
return res;
@ -3107,7 +3111,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl
break;
}
case FLIPPER: {
nfc_df_e dumptype;
nfc_df_e dumptype = NFC_DF_UNKNOWN;
res = detect_nfc_dump_format(fn, &dumptype, true);
if (res != PM3_SUCCESS) {
break;

View file

@ -282,8 +282,31 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale
*/
int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t keylen, uint32_t *keycnt);
/**
* @brief Utility function to load data safely from a DICTIONARY textfile. This method takes a preferred name.
* E.g. mfc_default_keys.dic
*
* @param preferredName
* @param suffix
* @param pdata A pointer to a pointer (for reverencing the loaded dictionary)
* @param keylen the number of bytes a key per row is
* @param verbose print messages if true
* @return 0 for ok, 1 for failz
*/
int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, void **pdata, uint8_t keylen, uint32_t *keycnt, bool verbose);
/**
* @brief Utility function to load data from a XML textfile. This method takes a preferred name.
* E.g. dumpdata-15.xml
*
* @param preferredName
* @param data The data array to store the loaded bytes from file
* @param maxdatalen maximum size of data array in bytes
* @param datalen the number of bytes loaded from file
* @return 0 for ok, 1 for failz
*/
int loadFileXML_safe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen);
int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen);
/**

View file

@ -1008,7 +1008,7 @@ void DesfirePrintAIDFunctions(uint32_t appid) {
if ((aid[2] >> 4) == 0xF) {
uint16_t short_aid = ((aid[2] & 0xF) << 12) | (aid[1] << 4) | (aid[0] >> 4);
PrintAndLogEx(SUCCESS, " AID mapped to MIFARE Classic AID (MAD): " _YELLOW_("%02X"), short_aid);
PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X : " _YELLOW_("%s"), short_aid >> 8, nxp_cluster_to_text(short_aid >> 8));
PrintAndLogEx(SUCCESS, " MAD AID Cluster 0x%02X..... " _YELLOW_("%s"), short_aid >> 8, nxp_cluster_to_text(short_aid >> 8));
MADDFDecodeAndPrint(short_aid, false);
} else {
AIDDFDecodeAndPrint(aid);
@ -1016,53 +1016,64 @@ void DesfirePrintAIDFunctions(uint32_t appid) {
}
int DesfireSelectAndAuthenticateEx(DesfireContext_t *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose) {
if (verbose)
if (verbose) {
DesfirePrintContext(dctx);
}
// needs card uid for diversification
if (dctx->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER)
if (dctx->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) {
DesfireGetCardUID(dctx);
}
bool isosw = false;
if (dctx->cmdSet == DCCISO) {
dctx->cmdSet = DCCNativeISO;
isosw = true;
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "Switch to " _CYAN_("native") " for select");
}
}
int res;
if (aid == 0x000000) {
res = DesfireAnticollision(verbose);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire anticollision " _RED_("error") ".");
PrintAndLogEx(ERR, "Desfire anticollision " _RED_("fail"));
return 200;
}
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "Anticollision " _GREEN_("ok"));
}
} else {
res = DesfireSelectAIDHex(dctx, aid, false, 0);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire select " _RED_("error") ".");
PrintAndLogEx(ERR, "Desfire select " _RED_("fail"));
return 200;
}
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "App %06x " _GREEN_("selected"), aid);
}
}
if (isosw)
if (isosw) {
dctx->cmdSet = DCCISO;
}
if (noauth == false) {
res = DesfireAuthenticate(dctx, secureChannel, verbose);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res));
PrintAndLogEx(ERR, "Desfire authenticate " _RED_("fail") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res));
return res;
}
if (DesfireIsAuthenticated(dctx)) {
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated"));
}
} else {
return 201;
}
@ -1087,7 +1098,7 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s
res = DesfireSelectAIDHex(dctx, id, false, 0);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire select " _RED_("error") ".");
PrintAndLogEx(ERR, "Desfire select " _RED_("error"));
return 200;
}
if (verbose)
@ -1097,7 +1108,7 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s
} else {
res = DesfireSelectEx(dctx, true, way, id, NULL);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire %s select " _RED_("error") ".", DesfireSelectWayToStr(way));
PrintAndLogEx(ERR, "Desfire %s select " _RED_("error"), DesfireSelectWayToStr(way));
return 202;
}
if (verbose)
@ -1107,12 +1118,13 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s
if (selectfile) {
res = DesfireSelectEx(dctx, false, ISWIsoID, isofileid, NULL);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error") ".");
PrintAndLogEx(ERR, "Desfire iso file select " _RED_("error"));
return 203;
}
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "Application %s file iso id %04x is " _GREEN_("selected"), DesfireWayIDStr(way, id), isofileid);
}
}
if (!noauth) {
@ -1123,8 +1135,9 @@ int DesfireSelectAndAuthenticateW(DesfireContext_t *dctx, DesfireSecureChannel s
}
if (DesfireIsAuthenticated(dctx)) {
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated"));
}
} else {
return 201;
}
@ -1864,17 +1877,21 @@ int DesfireFillAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS ap
void DesfirePrintPICCInfo(DesfireContext_t *dctx, PICCInfo_t *PICCInfo) {
PrintAndLogEx(SUCCESS, "------------------------------------ " _CYAN_("PICC level") " -------------------------------------");
if (PICCInfo->freemem == 0xffffffff)
PrintAndLogEx(SUCCESS, "Applications count: " _GREEN_("%zu") " free memory " _YELLOW_("n/a"), PICCInfo->appCount);
else
PrintAndLogEx(SUCCESS, "Applications count: " _GREEN_("%zu") " free memory " _GREEN_("%d") " bytes", PICCInfo->appCount, PICCInfo->freemem);
if (PICCInfo->freemem == 0xffffffff) {
PrintAndLogEx(SUCCESS, "# applications....... " _YELLOW_("%zu"), PICCInfo->appCount);
} else {
PrintAndLogEx(SUCCESS, "# applications....... " _YELLOW_("%zu"), PICCInfo->appCount);
}
PrintAndLogEx(SUCCESS, "");
if (PICCInfo->authCmdCheck.checked) {
PrintAndLogEx(SUCCESS, "PICC level auth commands: ");
PrintAndLogEx(SUCCESS, "PICC level auth commands");
DesfireCheckAuthCommandsPrint(&PICCInfo->authCmdCheck);
}
if (PICCInfo->numberOfKeys > 0) {
PrintKeySettings(PICCInfo->keySettings, PICCInfo->numKeysRaw, false, true);
PrintAndLogEx(SUCCESS, "PICC key 0 version: %d (0x%02x)", PICCInfo->keyVersion0, PICCInfo->keyVersion0);
PrintAndLogEx(SUCCESS, "PICC key "_YELLOW_("0") " version: %d (0x%02x)", PICCInfo->keyVersion0, PICCInfo->keyVersion0);
}
}
@ -1886,14 +1903,14 @@ void DesfirePrintAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS
PrintAndLogEx(SUCCESS, "--------------------------------- " _CYAN_("Applications list") " ---------------------------------");
for (int i = 0; i < PICCInfo->appCount; i++) {
PrintAndLogEx(SUCCESS, _CYAN_("Application number: 0x%02X"), appList[i].appNum);
PrintAndLogEx(SUCCESS, " ISO id.... " _GREEN_("0x%04X"), appList[i].appISONum);
PrintAndLogEx(SUCCESS, " DF name... " _GREEN_("%s") " ( %s)", appList[i].appDFName, sprint_hex((uint8_t *)appList[i].appDFName, sizeof(appList[i].appDFName)));
PrintAndLogEx(SUCCESS, "Application ID....... " _CYAN_("0x%02X"), appList[i].appNum);
PrintAndLogEx(SUCCESS, " ISO id............ " _GREEN_("0x%04X"), appList[i].appISONum);
PrintAndLogEx(SUCCESS, " DF name........... " _GREEN_("%s") " ( %s )", appList[i].appDFName, sprint_hex_inrow((uint8_t *)appList[i].appDFName, sizeof(appList[i].appDFName)));
DesfirePrintAIDFunctions(appList[i].appNum);
if (PICCInfo->authCmdCheck.checked) {
PrintAndLogEx(SUCCESS, "Auth commands: ");
PrintAndLogEx(SUCCESS, "Auth commands");
DesfireCheckAuthCommandsPrint(&appList[i].authCmdCheck);
PrintAndLogEx(SUCCESS, "");
}
@ -1902,7 +1919,7 @@ void DesfirePrintAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS
PrintKeySettings(appList[i].keySettings, appList[i].numKeysRaw, true, true);
if (appList[i].numberOfKeys > 0) {
PrintAndLogEx(SUCCESS, "Key versions [0..%d]: " NOLF, appList[i].numberOfKeys - 1);
PrintAndLogEx(SUCCESS, "Key versions [0..%d] " NOLF, appList[i].numberOfKeys - 1);
for (uint8_t keyn = 0; keyn < appList[i].numberOfKeys; keyn++) {
PrintAndLogEx(NORMAL, "%s %02x" NOLF, (keyn == 0) ? "" : ",", appList[i].keyVersions[keyn]);
}
@ -2254,7 +2271,7 @@ int DesfireUpdateRecord(DesfireContext_t *dctx, uint8_t fnum, uint32_t recnum, u
}
static void PrintKeySettingsPICC(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) {
PrintAndLogEx(SUCCESS, "PICC level rights:");
PrintAndLogEx(SUCCESS, "PICC level rights");
PrintAndLogEx(SUCCESS, "[%c...] CMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)"));
PrintAndLogEx(SUCCESS, "[.%c..] CMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : "YES");
PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with CMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : "YES");
@ -2263,27 +2280,27 @@ static void PrintKeySettingsPICC(uint8_t keysettings, uint8_t numkeys, bool prin
if (print2ndbyte) {
DesfirePrintCardKeyType(numkeys >> 6);
PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f);
PrintAndLogEx(SUCCESS, "Key cnt.... " _YELLOW_("%d"), numkeys & 0x0F);
}
}
static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) {
// Access rights.
PrintAndLogEx(SUCCESS, "Application level rights:");
PrintAndLogEx(SUCCESS, "Application level rights");
uint8_t rights = ((keysettings >> 4) & 0x0F);
switch (rights) {
case 0x0:
PrintAndLogEx(SUCCESS, "-- AMK authentication is necessary to change any key (default)");
PrintAndLogEx(SUCCESS, " - AMK authentication is necessary to change any key (default)");
break;
case 0xE:
PrintAndLogEx(SUCCESS, "-- Authentication with the key to be changed (same KeyNo) is necessary to change a key");
PrintAndLogEx(SUCCESS, " - Authentication with the key to be changed (same KeyNo) is necessary to change a key");
break;
case 0xF:
PrintAndLogEx(SUCCESS, "-- All keys (except AMK,see Bit0) within this application are frozen");
PrintAndLogEx(SUCCESS, " - All keys (except AMK,see Bit0) within this application are frozen");
break;
default:
PrintAndLogEx(SUCCESS,
"-- Authentication with the specified key " _YELLOW_("(0x%02x)") " is necessary to change any key.\n"
" - Authentication with the specified key " _YELLOW_("(0x%02x)") " is necessary to change any key.\n"
"A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n"
"For keys other then the master or change key, an authentication with the same key is needed.",
rights & 0x0f
@ -2299,10 +2316,10 @@ static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print
if (print2ndbyte) {
DesfirePrintCardKeyType(numkeys >> 6);
PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f);
if (numkeys & 0x20)
PrintAndLogEx(SUCCESS, "Key cnt.... " _YELLOW_("%d"), numkeys & 0x0F);
if (numkeys & 0x20) {
PrintAndLogEx(SUCCESS, "iso file id: enabled");
PrintAndLogEx(SUCCESS, "");
}
}
}

View file

@ -504,16 +504,16 @@ uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorithm keyType) {
void DesfirePrintCardKeyType(uint8_t keyType) {
switch (keyType) {
case 00:
PrintAndLogEx(SUCCESS, "Key: 2TDEA");
PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("2TDEA"));
break;
case 01:
PrintAndLogEx(SUCCESS, "Key: 3TDEA");
PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("3TDEA"));
break;
case 02:
PrintAndLogEx(SUCCESS, "Key: AES");
PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("AES"));
break;
default:
PrintAndLogEx(SUCCESS, "Key: unknown: 0x%02x", keyType);
PrintAndLogEx(SUCCESS, "Key type... " _YELLOW_("unknown") " - 0x%02x", keyType);
break;
}
}

View file

@ -69,8 +69,13 @@ static int open_mad_file(json_t **root, bool verbose) {
goto out;
}
if (verbose)
PrintAndLogEx(SUCCESS, "Loaded file " _YELLOW_("`%s`") " (%s) %zu records.", path, _GREEN_("ok"), json_array_size(*root));
if (verbose) {
PrintAndLogEx(SUCCESS, "Loaded file `" _YELLOW_("%s") "` " _GREEN_("%zu") " records ( " _GREEN_("ok") " )"
, path
, json_array_size(*root)
);
}
out:
free(path);
return retval;
@ -183,7 +188,7 @@ static uint16_t madGetAID(const uint8_t *sector, bool swapmad, int MADver, int s
}
}
int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) {
int MADCheck(uint8_t *sector0, uint8_t *sector16, bool verbose, bool *haveMAD2) {
if (sector0 == NULL)
return PM3_EINVARG;
@ -217,13 +222,13 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2)
PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector0[16], _GREEN_("ok"));
}
if (mad_ver == 2 && sector10) {
int res2 = madCRCCheck(sector10, true, 2);
if (mad_ver == 2 && sector16) {
int res2 = madCRCCheck(sector16, true, 2);
if (res == PM3_SUCCESS)
res = res2;
if (verbose && !res2)
PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector10[0], _GREEN_("ok"));
PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector16[0], _GREEN_("ok"));
}
// MA (multi-application card)
@ -236,27 +241,30 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2)
return res;
}
int MADDecode(uint8_t *sector0, uint8_t *sector10, uint16_t *mad, size_t *madlen, bool swapmad) {
int MADDecode(uint8_t *sector0, uint8_t *sector16, uint16_t *mad, size_t *madlen, bool swapmad) {
*madlen = 0;
bool haveMAD2 = false;
int res = MADCheck(sector0, sector10, false, &haveMAD2);
int res = MADCheck(sector0, sector16, false, &haveMAD2);
if (res != PM3_SUCCESS) {
PrintAndLogEx(WARNING, "Not a valid MAD");
return res;
}
for (int i = 1; i < 16; i++) {
// 7 + 8 == 15
for (int i = 1; i <= 16; i++) {
mad[*madlen] = madGetAID(sector0, swapmad, 1, i);
(*madlen)++;
}
if (haveMAD2) {
// mad2 sector (0x10 == 16dec) here
// mad2 sector (0x10 == 16) here
mad[*madlen] = 0x0005;
(*madlen)++;
// 7 + 8 + 8 == 23
for (int i = 1; i < 24; i++) {
mad[*madlen] = madGetAID(sector10, swapmad, 2, i);
mad[*madlen] = madGetAID(sector16, swapmad, 2, i);
(*madlen)++;
}
}
@ -347,8 +355,16 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMA
aid
);
} else {
char fmt[60];
snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i, aid, "%s");
char fmt[80];
snprintf(fmt
, sizeof(fmt)
, (ibs == i) ?
_MAGENTA_(" %02d [%04X] %s") :
" %02d [" _GREEN_("%04X") "] %s"
, i
, aid
, "%s"
);
print_aid_description(mad_known_aids, aid, fmt, verbose);
prev_aid = aid;
}
@ -400,8 +416,16 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) {
aid
);
} else {
char fmt[60];
snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i + 16, aid, "%s");
char fmt[80];
snprintf(fmt
, sizeof(fmt)
, (ibs == i) ?
_MAGENTA_(" %02d [%04X] %s") :
" %02d [" _GREEN_("%04X") "] %s"
, i + 16
, aid
, "%s"
);
print_aid_description(mad_known_aids, aid, fmt, verbose);
prev_aid = aid;
}
@ -415,7 +439,7 @@ int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose) {
open_mad_file(&mad_known_aids, false);
char fmt[128];
snprintf(fmt, sizeof(fmt), " MAD AID Function 0x%04X :" _YELLOW_("%s"), short_aid, "%s");
snprintf(fmt, sizeof(fmt), " MAD AID Function 0x%04X... " _YELLOW_("%s"), short_aid, "%s");
print_aid_description(mad_known_aids, short_aid, fmt, verbose);
close_mad_file(mad_known_aids);
return PM3_SUCCESS;
@ -429,8 +453,9 @@ bool HasMADKey(uint8_t *d) {
}
int DetectHID(uint8_t *d, uint16_t manufacture) {
if (d == NULL)
if (d == NULL) {
return -1;
}
// find HID
for (int i = 1; i < 16; i++) {
@ -456,16 +481,16 @@ int convert_mad_to_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen)
}
uint8_t sector0[MFBLOCK_SIZE * 4] = {0};
uint8_t sector10[MFBLOCK_SIZE * 4] = {0};
uint8_t sector16[MFBLOCK_SIZE * 4] = {0};
memcpy(sector0, in, sizeof(sector0));
if (ilen == MIFARE_4K_MAX_BYTES) {
memcpy(sector10, in + (MF_MAD2_SECTOR * 4 * MFBLOCK_SIZE), sizeof(sector10));
memcpy(sector16, in + (MF_MAD2_SECTOR * 4 * MFBLOCK_SIZE), sizeof(sector16));
}
uint16_t mad[7 + 8 + 8 + 8 + 8] = {0};
size_t madlen = 0;
if (MADDecode(sector0, sector10, mad, &madlen, false)) {
if (MADDecode(sector0, sector16, mad, &madlen, false)) {
PrintAndLogEx(ERR, "can't decode MAD");
return PM3_ESOFT;
}

View file

@ -21,8 +21,8 @@
#include "common.h"
int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2);
int MADDecode(uint8_t *sector0, uint8_t *sector10, uint16_t *mad, size_t *madlen, bool swapmad);
int MADCheck(uint8_t *sector0, uint8_t *sector16, bool verbose, bool *haveMAD2);
int MADDecode(uint8_t *sector0, uint8_t *sector16, uint16_t *mad, size_t *madlen, bool swapmad);
int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMAD2);
int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose);
int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose);

View file

@ -291,9 +291,16 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh
if ((singleSectorParams >> 15) & 1) {
if (curr_keys) {
uint64_t foo = bytes_to_num(resp.data.asBytes, 6);
// uint64_t foo = bytes_to_num(resp.data.asBytes, 6);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, _GREEN_("Key %s for block %2i found: %012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo);
// PrintAndLogEx(SUCCESS, "found Key %s for block %2i found: " _GREEN_("%012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo);
PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]",
singleSectorParams & 0xFF,
((singleSectorParams >> 8) & 1) ? 'B' : 'A',
sprint_hex_inrow(resp.data.asBytes, MIFARE_KEY_SIZE)
);
return PM3_SUCCESS;
}
}
@ -1029,19 +1036,25 @@ int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8
// EMULATOR
int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount) {
return mf_eml_get_mem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE);
}
size_t size = blocksCount * MFBLOCK_SIZE;
int mf_eml_get_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) {
size_t size = ((size_t) blocksCount) * blockBtWidth;
if (size > PM3_CMD_DATA_SIZE) {
return PM3_ESOFT;
}
struct {
uint8_t blockno;
uint16_t blockno;
uint8_t blockcnt;
uint8_t blockwidth;
} PACKED payload;
payload.blockno = blockNum;
payload.blockcnt = blocksCount;
payload.blockwidth = blockBtWidth;
clearCommandBuffer();
SendCommandNG(CMD_HF_MIFARE_EML_MEMGET, (uint8_t *)&payload, sizeof(payload));
@ -1052,8 +1065,9 @@ int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount) {
return PM3_ETIMEOUT;
}
if (resp.status == PM3_SUCCESS)
if (resp.status == PM3_SUCCESS) {
memcpy(data, resp.data.asBytes, size);
}
return resp.status;
}
@ -1065,7 +1079,7 @@ int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount) {
int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) {
struct p {
uint8_t blockno;
uint16_t blockno;
uint8_t blockcnt;
uint8_t blockwidth;
uint8_t data[];
@ -1219,7 +1233,7 @@ int mf_chinese_set_block(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t p
if (!isOK) {
uint8_t reason = (resp.oldarg[1] & 0xFF);
if ( reason == 4) {
if (reason == 4) {
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(WARNING, "GDM magic write signature block failed");
} else if (reason == 5) {

View file

@ -92,6 +92,7 @@ int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, uint8_t
int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *sector);
int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount);
int mf_eml_get_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth);
int mf_elm_set_mem(uint8_t *data, int blockNum, int blocksCount);
int mf_eml_set_mem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth);

View file

@ -202,6 +202,7 @@ const static vocabulary_t vocabulary[] = {
{ 0, "hf 15 slixeasenable" },
{ 0, "hf 15 slixprivacydisable" },
{ 0, "hf 15 slixprivacyenable" },
{ 0, "hf 15 slixprotectpage" },
{ 0, "hf 15 passprotectafi" },
{ 0, "hf 15 passprotecteas" },
{ 0, "hf 15 findafi" },

View file

@ -344,12 +344,14 @@ static int l_WaitForResponseTimeout(lua_State *L) {
return returnToLuaWithError(L, "You need to supply at least command to wait for");
// extract first param. cmd byte to look for
if (n >= 1)
if (n >= 1) {
cmd = (uint32_t)luaL_checkinteger(L, 1);
}
// extract second param. timeout value
if (n >= 2)
ms_timeout = luaL_checkinteger(L, 2);
if (n >= 2) {
ms_timeout = (size_t)luaL_checkinteger(L, 2);
}
PacketResponseNG resp;
if (WaitForResponseTimeout(cmd, &resp, ms_timeout) == false) {
@ -740,8 +742,9 @@ static int l_reveng_models(lua_State *L) {
int count = 0;
uint8_t in_width = (uint8_t)luaL_checkinteger(L, 1);
if (in_width > 89)
if (in_width > 89) {
return returnToLuaWithError(L, "Width cannot exceed 89, got %d", in_width);
}
uint8_t width[NMODELS];
memset(width, 0, sizeof(width));
@ -749,8 +752,9 @@ static int l_reveng_models(lua_State *L) {
width[0] = in_width;
if (!GetModels(models, &count, width))
if (!GetModels(models, &count, width)) {
return returnToLuaWithError(L, "didn't find any models");
}
lua_newtable(L);
for (int i = 0; i < count; i++) {

View file

@ -152,10 +152,10 @@ void FillFileNameByUID(char *filenamePrefix, const uint8_t *uid, const char *ext
int len = strlen(filenamePrefix);
for (int j = 0; j < uidlen; j++) {
for (int i = 0; i < uidlen; i++) {
// This is technically not the safest option, but there is no way to make this work without changing the function signature
// Possibly todo for future PR, but given UID lenghts are defined by program and not variable, should not be an issue
snprintf(filenamePrefix + len + j * 2, 3, "%02X", uid[j]);
snprintf(filenamePrefix + len + i * 2, 3, "%02X", uid[i]);
}
strcat(filenamePrefix, ext);
@ -196,7 +196,7 @@ bool CheckStringIsHEXValue(const char *value) {
}
for (size_t i = 0; i < strlen(value); i++) {
if (!isxdigit(value[i])) {
if (isxdigit(value[i]) == 0) {
return false;
}
}
@ -220,11 +220,13 @@ void ascii_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len
}
size_t m = (min_str_len > i) ? min_str_len : 0;
if (m > hex_max_len)
if (m > hex_max_len) {
m = hex_max_len;
}
for (; i < m; i++, tmp++)
for (; i < m; i++, tmp++) {
*tmp = ' ';
}
// remove last space
*tmp = '\0';
@ -234,8 +236,9 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len,
const size_t min_str_len, const size_t spaces_between, bool uppercase) {
// sanity check
if (buf == NULL || hex_len < 1)
if (buf == NULL || hex_len < 1) {
return;
}
// 1. hex string length.
// 2. byte array to be converted to string
@ -252,18 +255,22 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len,
*(tmp++) = b2s((hex_data[i] >> 4), uppercase);
*(tmp++) = b2s(hex_data[i], uppercase);
for (size_t j = 0; j < spaces_between; j++)
for (size_t j = 0; j < spaces_between; j++) {
*(tmp++) = ' ';
}
}
i *= (2 + spaces_between);
size_t m = (min_str_len > i) ? min_str_len : 0;
if (m > hex_max_len)
m = hex_max_len;
while (m--)
if (m > hex_max_len) {
m = hex_max_len;
}
while (m--) {
*(tmp++) = ' ';
}
// remove last space
*tmp = '\0';
@ -274,9 +281,9 @@ void hex_to_buffer(uint8_t *buf, const uint8_t *hex_data, const size_t hex_len,
void print_hex(const uint8_t *data, const size_t len) {
if (data == NULL || len == 0) return;
for (size_t i = 0; i < len; i++)
for (size_t i = 0; i < len; i++) {
PrintAndLogEx(NORMAL, "%02x " NOLF, data[i]);
}
PrintAndLogEx(NORMAL, "");
}
@ -621,10 +628,13 @@ char *sprint_breakdown_bin(color_t color, const char *bs, int width, int padn, i
}
int hex_to_bytes(const char *hexValue, uint8_t *bytesValue, size_t maxBytesValueLen) {
char buf[4] = {0};
int indx = 0;
int bytesValueLen = 0;
while (hexValue[indx]) {
if (hexValue[indx] == '\t' || hexValue[indx] == ' ') {
indx++;
continue;
@ -684,6 +694,7 @@ void bytes_to_bytebits(const void *src, const size_t srclen, void *dest) {
uint32_t i = srclen * 8;
size_t j = srclen;
while (j--) {
uint8_t b = s[j];
d[--i] = (b >> 0) & 1;
@ -740,20 +751,33 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) {
*en = 0;
// skip spaces
while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++;
while (line[*bg] == ' ' || line[*bg] == '\t') {
(*bg)++;
}
if (*bg >= len) {
return 1;
}
for (i = 0; i < paramnum; i++) {
while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0')(*bg)++;
while (line[*bg] == ' ' || line[*bg] == '\t')(*bg)++;
if (line[*bg] == '\0') return 1;
while (line[*bg] != ' ' && line[*bg] != '\t' && line[*bg] != '\0') {
(*bg)++;
}
while (line[*bg] == ' ' || line[*bg] == '\t') {
(*bg)++;
}
if (line[*bg] == '\0') {
return 1;
}
}
*en = *bg;
while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0')(*en)++;
while (line[*en] != ' ' && line[*en] != '\t' && line[*en] != '\0') {
(*en)++;
}
(*en)--;
@ -763,7 +787,9 @@ int param_getptr(const char *line, int *bg, int *en, int paramnum) {
int param_getlength(const char *line, int paramnum) {
int bg, en;
if (param_getptr(line, &bg, &en, paramnum)) return 0;
if (param_getptr(line, &bg, &en, paramnum)) {
return 0;
}
return en - bg + 1;
}
@ -775,10 +801,13 @@ char param_getchar(const char *line, int paramnum) {
char param_getchar_indx(const char *line, int indx, int paramnum) {
int bg, en;
if (param_getptr(line, &bg, &en, paramnum)) return 0x00;
if (param_getptr(line, &bg, &en, paramnum)) {
return 0;
}
if (bg + indx > en)
if (bg + indx > en) {
return '\0';
}
return line[bg + indx];
}
@ -795,7 +824,9 @@ uint8_t param_get8(const char *line, int paramnum) {
*/
uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) {
uint8_t val = param_get8ex(line, paramnum, 255, 10);
if ((int8_t) val == -1) return 1;
if ((int8_t) val == -1) {
return 1;
}
(*destination) = val;
return 0;
}
@ -808,49 +839,56 @@ uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination) {
uint8_t param_isdec(const char *line, int paramnum) {
int bg, en;
//TODO, check more thorougly
if (!param_getptr(line, &bg, &en, paramnum)) return 1;
if (!param_getptr(line, &bg, &en, paramnum)) {
return 1;
}
// return strtoul(&line[bg], NULL, 10) & 0xff;
return 0;
}
uint8_t param_get8ex(const char *line, int paramnum, int deflt, int base) {
int bg, en;
if (!param_getptr(line, &bg, &en, paramnum))
if (param_getptr(line, &bg, &en, paramnum) == 0) {
return strtoul(&line[bg], NULL, base) & 0xff;
else
} else {
return deflt;
}
}
uint32_t param_get32ex(const char *line, int paramnum, int deflt, int base) {
int bg, en;
if (!param_getptr(line, &bg, &en, paramnum))
if (param_getptr(line, &bg, &en, paramnum) == 0) {
return strtoul(&line[bg], NULL, base);
else
} else {
return deflt;
}
}
uint64_t param_get64ex(const char *line, int paramnum, int deflt, int base) {
int bg, en;
if (!param_getptr(line, &bg, &en, paramnum))
if (param_getptr(line, &bg, &en, paramnum) == 0) {
return strtoull(&line[bg], NULL, base);
else
} else {
return deflt;
}
}
float param_getfloat(const char *line, int paramnum, float deflt) {
int bg, en;
if (!param_getptr(line, &bg, &en, paramnum))
if (param_getptr(line, &bg, &en, paramnum) == 0) {
return strtof(&line[bg], NULL);
else
} else {
return deflt;
}
}
int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) {
int bg, en, i;
uint32_t temp;
if (param_getptr(line, &bg, &en, paramnum)) return 1;
if (param_getptr(line, &bg, &en, paramnum)) {
return 1;
}
*hexcnt = en - bg + 1;
@ -860,7 +898,9 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt)
}
for (i = 0; i < *hexcnt; i += 2) {
if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) return 1;
if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) {
return 1;
}
sscanf((char[]) {line[bg + i], line[bg + i + 1], 0}, "%X", &temp);
data[i / 2] = temp & 0xff;
@ -873,14 +913,16 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda
int bg, en;
if (param_getptr(line, &bg, &en, paramnum))
if (param_getptr(line, &bg, &en, paramnum)) {
return 1;
}
*datalen = 0;
char buf[5] = {0};
int indx = bg;
while (line[indx]) {
if (line[indx] == '\t' || line[indx] == ' ') {
indx++;
continue;
@ -910,9 +952,10 @@ int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxda
indx++;
}
if (strlen(buf) > 0)
if (strlen(buf) > 0) {
//error when not completed hex bytes
return 3;
}
return 0;
}
@ -927,6 +970,7 @@ int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxda
char buf[5] = {0};
int indx = bg;
while (line[indx]) {
if (line[indx] == '\t' || line[indx] == ' ') {
indx++;
continue;
@ -992,11 +1036,14 @@ int hextobinarray_n(char *target, char *source, int sourcelen) {
char *start = source;
// process 4 bits (1 hex digit) at a time
while (sourcelen--) {
char x = *(source++);
// capitalize
if (x >= 'a' && x <= 'f') {
x -= 32;
}
// convert to numeric value
if (x >= '0' && x <= '9') {
x -= '0';
@ -1006,6 +1053,7 @@ int hextobinarray_n(char *target, char *source, int sourcelen) {
PrintAndLogEx(INFO, "(hextobinarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start);
return 0;
}
// output
for (i = 0 ; i < 4 ; ++i, ++count) {
*(target++) = (x >> (3 - i)) & 1;
@ -1053,15 +1101,20 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz
uint32_t t = 0; // written target chars
uint32_t r = 0; // consumed bits
uint8_t w = 0; // wrong bits separator printed
for (size_t s = 0 ; s < srclen; s++) {
if ((source[s] == 0) || (source[s] == 1)) {
w = 0;
x += (source[s] << (3 - i));
i++;
if (i == 4) {
if (t >= targetlen - 2) {
return r;
}
snprintf(target + t, targetlen - t, "%X", x);
t++;
r += 4;
@ -1069,10 +1122,13 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz
i = 0;
}
} else {
if (i > 0) {
if (t >= targetlen - 5) {
return r;
}
snprintf(target + t, targetlen - t, "%X[%i]", x, i);
t += 4;
r += i;
@ -1080,13 +1136,17 @@ int binarray_2_hex(char *target, const size_t targetlen, const char *source, siz
i = 0;
w = 1;
}
if (w == 0) {
if (t >= targetlen - 2) {
return r;
}
snprintf(target + t, targetlen - t, " ");
t++;
}
r++;
}
}
@ -1107,9 +1167,9 @@ int binstr_2_binarray(uint8_t *target, char *source, int length) {
while (length--) {
char x = *(source++);
// convert from binary value
if (x >= '0' && x <= '1')
if (x >= '0' && x <= '1') {
x -= '0';
else {
} else {
PrintAndLogEx(WARNING, "(binstring2binarray) discovered unknown character %c %d at idx %d of %s", x, x, (int16_t)(source - start), start);
return 0;
}
@ -1165,8 +1225,9 @@ void hex_xor_token(uint8_t *d, const uint8_t *x, int dn, int xn) {
// return parity bit required to match type
uint8_t GetParity(const uint8_t *bits, uint8_t type, int length) {
int x;
for (x = 0 ; length > 0 ; --length)
for (x = 0 ; length > 0 ; --length) {
x += bits[length - 1];
}
x %= 2;
return x ^ type;
}
@ -1190,24 +1251,30 @@ void wiegand_add_parity_swapped(uint8_t *target, const uint8_t *source, uint8_t
// Pack a bitarray into a uint32_t.
uint32_t PackBits(uint8_t start, uint8_t len, const uint8_t *bits) {
if (len > 32) return 0;
if (len > 32) {
return 0;
}
int i = start;
int j = len - 1;
uint32_t tmp = 0;
for (; j >= 0; --j, ++i)
for (; j >= 0; --j, ++i) {
tmp |= bits[i] << j;
}
return tmp;
}
uint64_t HornerScheme(uint64_t num, uint64_t divider, uint64_t factor) {
uint64_t remaind = 0, quotient = 0, result = 0;
remaind = num % divider;
quotient = num / divider;
if (!(quotient == 0 && remaind == 0))
if (!(quotient == 0 && remaind == 0)) {
result += HornerScheme(quotient, divider, factor) * factor + remaind;
}
return result;
}
@ -1228,15 +1295,17 @@ int detect_num_CPUs(void) {
return sysinfo.dwNumberOfProcessors;
#else
int count = sysconf(_SC_NPROCESSORS_ONLN);
if (count <= 0)
if (count <= 0) {
count = 1;
}
return count;
#endif
}
void str_lower(char *s) {
for (size_t i = 0; i < strlen(s); i++)
for (size_t i = 0; i < strlen(s); i++) {
s[i] = tolower(s[i]);
}
}
void str_upper(char *s) {
@ -1244,8 +1313,9 @@ void str_upper(char *s) {
}
void strn_upper(char *s, size_t n) {
for (size_t i = 0; i < n; i++)
for (size_t i = 0; i < n; i++) {
s[i] = toupper(s[i]);
}
}
// check for prefix in string
bool str_startswith(const char *s, const char *pre) {
@ -1265,8 +1335,9 @@ bool str_endswith(const char *s, const char *suffix) {
// Replace unprintable characters with a dot in char buffer
void clean_ascii(unsigned char *buf, size_t len) {
for (size_t i = 0; i < len; i++) {
if (!isprint(buf[i]))
if (isprint(buf[i]) == 0) {
buf[i] = '.';
}
}
}
@ -1279,12 +1350,12 @@ void str_cleanrn(char *buf, size_t len) {
// replace char in buffer
void str_creplace(char *buf, size_t len, char from, char to) {
for (size_t i = 0; i < len; i++) {
if (buf[i] == from)
if (buf[i] == from) {
buf[i] = to;
}
}
}
char *str_dup(const char *src) {
return str_ndup(src, strlen(src));
}
@ -1365,8 +1436,9 @@ int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str)
for (;;) {
int res = sscanf(&str[i], "%1u", &n);
if ((res != 1) || (n > 1))
if ((res != 1) || (n > 1)) {
break;
}
*hi2 = (*hi2 << 1) | (*hi >> 31);
*hi = (*hi << 1) | (*lo >> 31);
@ -1388,8 +1460,9 @@ int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const uint8_t *ar
int i = 0;
for (; i < arrlen; i++) {
uint8_t n = arr[i];
if (n > 1)
if (n > 1) {
break;
}
*hi2 = (*hi2 << 1) | (*hi >> 31);
*hi = (*hi << 1) | (*lo >> 31);
@ -1445,17 +1518,20 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_
for (size_t i = 0; i < max; i++) {
// compare only first byte
if (src[i] != pattern[0])
if (src[i] != pattern[0]) {
continue;
}
// try to match rest of the pattern
for (int j = plen - 1; j >= 1; j--) {
if (src[i + j] != pattern[j])
if (src[i + j] != pattern[j]) {
break;
}
if (j == 1)
if (j == 1) {
return i;
}
}
}
return -1;
@ -1467,17 +1543,20 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_
int byte_strrstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen) {
for (int i = srclen - plen; i >= 0; i--) {
// compare only first byte
if (src[i] != pattern[0])
if (src[i] != pattern[0]) {
continue;
}
// try to match rest of the pattern
for (int j = plen - 1; j >= 1; j--) {
if (src[i + j] != pattern[j])
if (src[i + j] != pattern[j]) {
break;
}
if (j == 1)
if (j == 1) {
return i;
}
}
}
return -1;

File diff suppressed because it is too large Load diff

View file

@ -36,17 +36,23 @@ typedef struct {
bool hasIssueLevel;
bool hasOEMCode;
bool hasParity;
uint32_t MaxFC; // max Facility Code
uint64_t MaxCN; // max CardNumber
uint32_t MaxIL; // max IssueLevel
uint32_t MaxOEM;// max OEM
} cardformatdescriptor_t;
// Structure for defined Wiegand card formats available for packing/unpacking
typedef struct {
const char *Name;
bool (*Pack)(wiegand_card_t *card, wiegand_message_t *packed, bool preamble);
bool (*Pack)(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bool preamble);
bool (*Unpack)(wiegand_message_t *packed, wiegand_card_t *card);
const char *Descrp;
const char *Description;
uint32_t Bits; // Number of bits in this format
cardformatdescriptor_t Fields;
} cardformat_t;
bool validate_card_limit(int format_idx, wiegand_card_t *card);
void HIDListFormats(void);
int HIDFindCardFormat(const char *format);
cardformat_t HIDGetCardFormat(int idx);
@ -54,6 +60,7 @@ bool HIDPack(int format_idx, wiegand_card_t *card, wiegand_message_t *packed, bo
bool HIDTryUnpack(wiegand_message_t *packed);
void HIDPackTryAll(wiegand_card_t *card, bool preamble);
void HIDUnpack(int idx, wiegand_message_t *packed);
bool decode_wiegand(uint32_t top, uint32_t mid, uint32_t bot, int n);
int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose);
void print_wiegand_code(wiegand_message_t *packed);
void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed);

View file

@ -128,11 +128,25 @@ bool set_nonlinear_field(wiegand_message_t *data, uint64_t value, uint8_t numBit
return result;
}
static uint8_t get_length_from_header(wiegand_message_t *data) {
uint8_t get_length_from_header(wiegand_message_t *data) {
/**
* detect if message has "preamble" / "sentinel bit"
* Right now we just calculate the highest bit set
* 37 bit formats is hard to detect since it doesnt have a sentinel bit
*
* (from http://www.proxmark.org/forum/viewtopic.php?pid=5368#p5368)
* 0000 0010 0000 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx 26-bit
* 0000 0010 0000 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx 27-bit
* 0000 0010 0000 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx 28-bit
* 0000 0010 0000 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx 29-bit
* 0000 0010 0000 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 30-bit
* 0000 0010 0000 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 31-bit
* 0000 0010 0001 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 32-bit
* 0000 0010 001x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 33-bit
* 0000 0010 01xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 34-bit
* 0000 0010 1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 35-bit
* 0000 0011 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 36-bit
* 0000 000x xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 37-bit
* 0000 00xx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx 38-bit
*/
uint8_t len = 0;
uint32_t hfmt = 0; // for calculating card length
@ -140,29 +154,15 @@ static uint8_t get_length_from_header(wiegand_message_t *data) {
if ((data->Top & 0x000FFFFF) > 0) { // > 64 bits
hfmt = data->Top & 0x000FFFFF;
len = 64;
} else if (data->Mid > 0) { // < 63-32 bits
// detect HID format b38 set
if (data->Mid & 0xFFFFFFC0) {
hfmt = data->Mid;
len = 32;
} else {
PrintAndLogEx(DEBUG, "hid preamble detected");
len = 32;
if ((data->Mid ^ 0x20) == 0) { hfmt = data->Bot; len = 0; }
else if ((data->Mid & 0x10) == 0) { hfmt = data->Mid & 0x1F; }
else if ((data->Mid & 0x08) == 0) { hfmt = data->Mid & 0x0F; }
else if ((data->Mid & 0x04) == 0) { hfmt = data->Mid & 0x07; }
else if ((data->Mid & 0x02) == 0) { hfmt = data->Mid & 0x03; }
else if ((data->Mid & 0x01) == 0) { hfmt = data->Mid & 0x01; }
else { hfmt = data->Mid & 0x3F;}
}
} else {
hfmt = data->Bot;
len = 0;
} else if (data->Mid & 0xFFFFFFC0) { // handle 38bit and above format
hfmt = data->Mid;
len = 31; // remove leading 1 (preamble) in 38-64 bits format
} else if (((data->Mid >> 5) & 1) == 1) { // bit 38 is set => 26-36bit format
hfmt = (((data->Mid & 31) << 6) | (data->Bot >> 26)); // get bits 27-37 to check for format len bit
len = 25;
} else { // if bit 38 is not set => 37bit format
hfmt = 0;
len = 37;
}
while (hfmt > 0) {
@ -170,10 +170,6 @@ static uint8_t get_length_from_header(wiegand_message_t *data) {
len++;
}
// everything less than 26 bits found, assume 26 bits
if (len < 26)
len = 26;
return len;
}
@ -193,13 +189,15 @@ wiegand_message_t initialize_message_object(uint32_t top, uint32_t mid, uint32_t
bool add_HID_header(wiegand_message_t *data) {
// Invalid value
if (data->Length > 84 || data->Length == 0)
if (data->Length > 84 || data->Length == 0) {
return false;
}
if (data->Length == 48) {
data->Mid |= 1U << (data->Length - 32); // Example leading 1: start bit
return true;
}
if (data->Length >= 64) {
data->Top |= 0x09e00000; // Extended-length header
data->Top |= 1U << (data->Length - 64); // leading 1: start bit

View file

@ -52,6 +52,7 @@ bool set_nonlinear_field(wiegand_message_t *data, uint64_t value, uint8_t numBit
wiegand_message_t initialize_message_object(uint32_t top, uint32_t mid, uint32_t bot, int n);
uint8_t get_length_from_header(wiegand_message_t *data);
bool add_HID_header(wiegand_message_t *data);
#endif

View file

@ -1,133 +0,0 @@
local os = require("os")
local ac = require('ansicolors')
local getopt = require('getopt')
local dir = os.getenv('HOME') .. '/proxmark3/client/dictionaries/'
local dictionary_path = dir .. 'T5577date.dic'
local cyan = ac.cyan
local res = ac.reset
author = ' Author: jareckib - created 02.02.2025'
version = ' version v1.01'
desc = [[
A simple script for searching the password for T5577. The script creates a
dictionary starting from the entered starting year to the entered ending year.
There are two search methods - DDMMYYYY or YYYYMMDD. Checking the entire year
takes about 1 minute and 50 seconds. Date from 1900 to 2100. The script may be
useful if the password is, for example, a date of birth.
]]
usage = [[
script run t55_chk [-s start_year] [-e end_year] [-d | -y]
]]
options = [[
-h Show this help message
-s Starting year (required)
-e Ending year (default: current year)
-d Search method: DDMMYYYY
-y Search method: YYYYMMDD
]]
examples = [[
script run t55_chk -s 1999 -d - start from 1999, end year is current year, method 01011999
script run t55_chk -s 1999 -y - start from 1999, end year is current year, method 19990101
script run t55_chk -s 1999 -e 2001 -y - start from 1999, end year 2001, method 19990101
script run t55_chk -s 1999 -e 2001 -d - start from 1999, end year 2001, method 01011999
]]
local function help()
print(ac.green..author..res)
print(version)
print(desc)
print(cyan..' Usage:'..res)
print(usage)
print(cyan..' Options:'..res)
print(options)
print(cyan..' Examples:'..res)
print(examples)
end
local days_in_month = {
[1] = 31, [2] = 28, [3] = 31, [4] = 30, [5] = 31, [6] = 30,
[7] = 31, [8] = 31, [9] = 30, [10] = 31, [11] = 30, [12] = 31
}
local function generate_dictionary(start_year, end_year, mode)
local file = io.open(dictionary_path, "w")
if not file then
print(ac.yellow .. ' ERROR: ' .. ac.reset .. 'Cannot create T5577date.dic')
return false
end
for year = start_year, end_year do
for month = 1, 12 do
local days_in_current_month = days_in_month[month]
if month == 2 and ((year % 4 == 0 and year % 100 ~= 0) or (year % 400 == 0)) then
days_in_current_month = 29
end
for day = 1, days_in_current_month do
local month_str = string.format("%02d", month)
local day_str = string.format("%02d", day)
local year_str = tostring(year)
local entry = (mode == "1") and (year_str .. month_str .. day_str) or (day_str .. month_str .. year_str)
file:write(entry .. "\n")
end
end
end
file:close()
return true
end
local function oops(err)
core.console('clear')
print( string.rep('--',39) )
print( string.rep('--',39) )
print(ac.red..' ERROR:'..res.. err)
print( string.rep('--',39) )
print( string.rep('--',39) )
return nil, err
end
local function main(args)
if #args == 0 then return help() end
local start_year, end_year, mode = nil, nil, nil
local current_year = tonumber(os.date("%Y"))
for o, a in getopt.getopt(args, 'hs:e:dy') do
if o == 'h' then return help() end
if o == 's' then
start_year = tonumber(a)
if not start_year then return oops('Invalid start year') end
end
if o == 'e' then
end_year = tonumber(a)
if not end_year then return oops('Invalid end year (-e)') end
end
if o == 'd' then mode = "d" end
if o == 'y' then mode = "y" end
end
if not start_year then return oops('Starting year is required') end
if start_year < 1900 or start_year > 2100 then
return oops('Start year must be between 1900 and 2100')
end
if args[#args] == "-e" then return oops('Ending year cannot be empty') end
if not end_year then end_year = current_year end
if end_year < 1900 or end_year > 2100 then
return oops('End year must be between 1900 and 2100')
end
if end_year < start_year then return oops('End year cannot be earlier than start year') end
if not mode then return oops('You must select searching method'..cyan..' -d'..res.. ' or '..cyan.. '-y'..res) end
if generate_dictionary(start_year, end_year, mode) then
print(ac.green .. " File created: " .. dictionary_path .. res)
print(cyan .. " Starting password testing on T5577..." .. res)
core.console('lf t55 chk -f ' .. dictionary_path)
else
return oops('Problem saving the file')
end
end
main(args)

View file

@ -17,6 +17,7 @@
//-----------------------------------------------------------------------------
#include "commonutil.h"
#include <string.h>
#include "stdbool.h"
/* Similar to FpgaGatherVersion this formats stored version information
* into a string representation. It takes a pointer to the struct version_information_t,
@ -403,25 +404,34 @@ void Uint8byteToMemBe(uint8_t *data, uint64_t value) {
}
// Rotate Left - Ultralight, Desfire
void rol(uint8_t *data, const size_t len) {
void rol(uint8_t *data, const size_t n) {
uint8_t first = data[0];
for (size_t i = 0; i < len - 1; i++) {
for (size_t i = 0; i < n - 1; i++) {
data[i] = data[i + 1];
}
data[len - 1] = first;
data[n - 1] = first;
}
// Rotate Right - Ultralight, Desfire
void ror(uint8_t *data, const size_t len) {
uint8_t last = data[len - 1];
void ror(uint8_t *data, const size_t n) {
uint8_t last = data[n - 1];
for (int i = len - 1; i > 0; i--) {
for (int i = n - 1; i > 0; i--) {
data[i] = data[i - 1];
}
data[0] = last;
}
void xor(uint8_t *dest, const uint8_t *src, size_t n) {
const uint8_t *s = src;
uint8_t *d = dest;
for (; n > 0; n--) {
*d++ ^= *s++;
}
}
void lsl(uint8_t *data, size_t len) {
for (size_t n = 0; n < len - 1; n++) {

Some files were not shown because too many files have changed in this diff Show more