mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 13:00:42 -07:00
Merge branch 'master' into master
Signed-off-by: Jarek Barwinski <116510448+jareckib@users.noreply.github.com>
This commit is contained in:
commit
06ff7934f3
124 changed files with 3736 additions and 2282 deletions
7
.github/workflows/codeql-analysis.yml
vendored
7
.github/workflows/codeql-analysis.yml
vendored
|
@ -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
|
||||
|
||||
|
|
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -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)
|
||||
|
|
6
Makefile
6
Makefile
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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!");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))) {
|
||||
|
|
|
@ -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);
|
||||
|
|
567
armsrc/pcf7931.c
567
armsrc/pcf7931.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
|
@ -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
|
|
@ -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)
|
|
@ -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)")
|
||||
|
|
|
@ -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')
|
||||
|
|
198
client/pyscripts/hf_mfu_uscuid.py
Normal file
198
client/pyscripts/hf_mfu_uscuid.py
Normal 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")
|
|
@ -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),
|
||||
|
|
32
client/pyscripts/Paxton_convert.py → client/pyscripts/paxton_convert.py
Normal file → Executable file
32
client/pyscripts/Paxton_convert.py → client/pyscripts/paxton_convert.py
Normal file → Executable 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.')
|
19
client/pyscripts/PAXTON_NET.py → client/pyscripts/paxton_net.py
Normal file → Executable file
19
client/pyscripts/PAXTON_NET.py → client/pyscripts/paxton_net.py
Normal file → Executable 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:')
|
18
client/pyscripts/Paxton_switch.py → client/pyscripts/paxton_switch.py
Normal file → Executable file
18
client/pyscripts/Paxton_switch.py → client/pyscripts/paxton_switch.py
Normal file → Executable 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:')
|
92
client/pyscripts/pm3_resources.py
Normal file
92
client/pyscripts/pm3_resources.py
Normal 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!")
|
|
@ -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"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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") " ------------------------"},
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue