Merge pull request #2704 from jkramarz/feature/sam_seos

support for HID SAM communication with SEOS cards
This commit is contained in:
Iceman 2025-01-07 00:41:53 +01:00 committed by GitHub
commit a531870717
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 1142 additions and 143 deletions

View file

@ -3,6 +3,7 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Added `hf seos sam` - Added support for HID SAM SEOS communications (@jkramarz)
- Changed (extended) area accessible by spiffs into last page of FLASH (@piotrva)
- Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva)
- Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva)

View file

@ -37,7 +37,7 @@ APP_CFLAGS = $(PLATFORM_DEFS) \
SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c
SRC_HF = hfops.c
SRC_ISO15693 = iso15693.c iso15693tools.c
SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_mfc.c sam_seos.c
SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_common.c sam_mfc.c sam_seos.c
#UNUSED: mifaresniff.c
SRC_ISO14443b = iso14443b.c
SRC_FELICA = felica.c

View file

@ -2246,7 +2246,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break;
}
case CMD_HF_SAM_SEOS: {
// sam_seos_get_pacs();
sam_seos_get_pacs(packet);
break;
}

373
armsrc/sam_common.c Normal file
View file

@ -0,0 +1,373 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Routines to support MFC <-> SAM communication
//-----------------------------------------------------------------------------
#include <string.h>
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"
#include "BigBuf.h"
#include "commonutil.h"
#include "ticks.h"
#include "dbprint.h"
#include "i2c.h"
#include "iso15693.h"
#include "protocols.h"
/**
* @brief Transmits data to and receives data from a HID®'s iCLASS® SE Processor.
*
* This function sends a specified number of bytes to the SAM and receives a response.
*
* @param data Pointer to the data to be transmitted.
* @param n Number of bytes to be transmitted.
* @param resp Pointer to the buffer where the response will be stored.
* @param resplen Pointer to the variable where the length of the response will be stored.
* @return Status code indicating success or failure of the operation.
*/
int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) {
bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD");
goto out;
}
*resplen = ISO7816_MAX_FRAME;
res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD");
goto out;
}
if (*resplen < 2) {
DbpString("received too few bytes from SIM CARD");
res = false;
goto out;
}
uint16_t more_len = 0;
if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) {
more_len = resp[*resplen - 1];
} else {
// we done, return
goto out;
}
// Don't discard data we already received except the SW code.
// If we only received 1 byte, this is the echo of INS, we discard it.
*resplen -= 2;
if (*resplen == 1) {
*resplen = 0;
}
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len};
res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD 2");
goto out;
}
more_len = 255 - *resplen;
res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD 2");
goto out;
}
*resplen += more_len;
out:
return res;
}
static inline void swap_clock_counters(volatile unsigned int *a, unsigned int *b) {
unsigned int c = *a;
*a = *b;
*b = c;
}
/**
* @brief Swaps the timer counter values.
*
* AT91SAM7S512 has a single Timer-Counter, that is reused in clocks Ticks
* and CountSspClk. This function stops the current clock and restores previous
* values. It is used to switch between different clock sources.
* It probably makes communication timing off, but at least makes it work.
*/
static void swap_clocks(void) {
static unsigned int tc0, tc1, tc2 = 0;
StopTicks();
swap_clock_counters(&(AT91C_BASE_TC0->TC_CV), &tc0);
swap_clock_counters(&(AT91C_BASE_TC1->TC_CV), &tc1);
swap_clock_counters(&(AT91C_BASE_TC2->TC_CV), &tc2);
}
void switch_clock_to_ticks(void) {
swap_clocks();
StartTicks();
}
void switch_clock_to_countsspclk(void) {
swap_clocks();
StartCountSspClk();
}
/**
* @brief Sends a payload to the SAM
*
* This function prepends the payload with the necessary APDU and application
* headers and sends it to the SAM.
*
* @param addr_src 0x14 for command from NFC, 0x44 for command from application
* @param addr_dest 0x0A for command to SAM
* @param addr_reply same as add_src or 0x00 if no reply is expected
* @param payload Pointer to the data to be sent.
* @param payload_len Length of the data to be sent.
* @param response Pointer to the buffer where the response will be stored.
* @param response_len Pointer to the variable where the length of the response will be stored.
* @param length Length of the data to be sent.
* @return Status code indicating success or failure of the operation.
*/
int sam_send_payload(
const uint8_t addr_src,
const uint8_t addr_dest,
const uint8_t addr_reply,
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
) {
int res = PM3_SUCCESS;
uint8_t *buf = response;
buf[0] = 0xA0; // CLA
buf[1] = 0xDA; // INS (PUT DATA)
buf[2] = 0x02; // P1 (TLV format?)
buf[3] = 0x63; // P2
buf[4] = SAM_TX_ASN1_PREFIX_LENGTH + (uint8_t) * payload_len; // LEN
buf[5] = addr_src;
buf[6] = addr_dest;
buf[7] = addr_reply;
buf[8] = 0x00;
buf[9] = 0x00;
buf[10] = 0x00;
memcpy(
&buf[11],
payload,
*payload_len
);
uint16_t length = SAM_TX_ASN1_PREFIX_LENGTH + SAM_TX_APDU_PREFIX_LENGTH + (uint8_t) * payload_len;
LogTrace(buf, length, 0, 0, NULL, true);
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM REQUEST APDU: ");
Dbhexdump(length, buf, false);
}
if (sam_rxtx(buf, length, response, response_len) == false) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM ERROR");
res = PM3_ECARDEXCHANGE;
goto out;
}
LogTrace(response, *response_len, 0, 0, NULL, false);
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM RESPONSE APDU: ");
Dbhexdump(*response_len, response, false);
}
out:
return res;
}
/**
* @brief Retreives SAM firmware version.
*
* Used just as ping or sanity check here.
*
* @return Status code indicating success or failure of the operation.
*/
int sam_get_version(void) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_get_version");
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
uint8_t payload[] = {
0xa0, 0x02, // <- SAM command
0x82, 0x00 // <- get version
};
uint16_t payload_len = sizeof(payload);
sam_send_payload(
0x44, 0x0a, 0x44,
payload,
&payload_len,
response,
&response_len
);
// resp:
// c1 64 00 00 00
// bd 11 <- SAM response
// 8a 0f <- get version response
// 80 02
// 01 29 <- version
// 81 06
// 68 3d 05 20 26 b6 <- build ID
// 82 01
// 01
// 90 00
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_get_version");
if (response[5] != 0xbd) {
Dbprintf("Invalid SAM response");
goto error;
} else {
uint8_t *sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
if (sam_response_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get response failed");
goto error;
}
uint8_t *sam_version_an = sam_find_asn1_node(sam_response_an, 0x80);
if (sam_version_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get version failed");
goto error;
}
uint8_t *sam_build_an = sam_find_asn1_node(sam_response_an, 0x81);
if (sam_build_an == NULL) {
if (g_dbglevel >= DBG_ERROR)
DbpString("SAM get firmware ID failed");
goto error;
}
if (g_dbglevel >= DBG_INFO) {
DbpString("SAM get version successful");
Dbprintf("Firmware version: %X.%X", sam_version_an[2], sam_version_an[3]);
Dbprintf("Firmware ID: ");
Dbhexdump(sam_build_an[1], sam_build_an + 2, false);
}
goto out;
}
error:
res = PM3_ESOFT;
out:
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_get_version");
return res;
}
/**
* @brief Finds an ASN.1 node of a specified type within a given root node.
*
* This function searches through a single level of the ASN.1 structure starting
* from the root node to find a node of the specified type.
*
* @param root Pointer to the root node of the ASN.1 structure.
* @param type The type of the ASN.1 node to find.
* @return Pointer to the ASN.1 node of the specified type if found, otherwise NULL.
*/
uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type) {
const uint8_t *end = (uint8_t *) root + *(root + 1);
uint8_t *current = (uint8_t *) root + 2;
while (current < end) {
if (*current == type) {
return current;
} else {
current += 2 + *(current + 1);
}
}
return NULL;
}
/**
* @brief Appends an ASN.1 node to the end of a given node.
*
* This function appends an ASN.1 node of a specified type and length to the end of
* the ASN.1 structure at specified node level.
*
* It is the most naive solution that does not handle the case where the node to append is
* not the last node at the same level. It also does not also care about proper
* order of the nodes.
*
* @param root Pointer to the root node of the ASN.1 structure.
* @param root Pointer to the node to be appended of the ASN.1 structure.
* @param type The type of the ASN.1 node to append.
* @param data Pointer to the data to be appended.
* @param len The length of the data to be appended.
*/
void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len) {
uint8_t *end = (uint8_t *) root + *(root + 1) + 2;
*(end) = type;
*(end + 1) = len;
memcpy(end + 2, data, len);
for (uint8_t *current = (uint8_t *) root; current <= node; current += 2) {
*(current + 1) += 2 + len;
};
return;
}
void sam_send_ack(void) {
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
uint8_t payload[] = {
0xa0, 0
};
uint16_t payload_len = sizeof(payload);
sam_send_payload(
0x44, 0x0a, 0x00,
payload,
&payload_len,
response,
&response_len
);
BigBuf_free();
}

49
armsrc/sam_common.h Normal file
View file

@ -0,0 +1,49 @@
//-----------------------------------------------------------------------------
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
#ifndef __SAM_COMMON_H
#define __SAM_COMMON_H
#include "common.h"
static const uint8_t SAM_TX_APDU_PREFIX_LENGTH = 5;
static const uint8_t SAM_TX_ASN1_PREFIX_LENGTH = 6;
static const uint8_t SAM_RX_ASN1_PREFIX_LENGTH = 5;
int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen);
void switch_clock_to_ticks(void);
void switch_clock_to_countsspclk(void);
int sam_send_payload(
const uint8_t addr_src,
const uint8_t addr_dest,
const uint8_t addr_reply,
const uint8_t *const payload,
const uint16_t *payload_len,
uint8_t *response,
uint16_t *response_len
);
int sam_get_version(void);
uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type);
void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len);
void sam_send_ack(void);
#endif

View file

@ -16,7 +16,7 @@
// Routines to support MFC <-> SAM communication
//-----------------------------------------------------------------------------
#include "sam_mfc.h"
#include "sam_seos.h"
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"

View file

@ -17,5 +17,6 @@
#define __SAM_MFC_H
#include "common.h"
#include "sam_common.h"
#endif

View file

@ -16,6 +16,7 @@
// Routines to support Picopass <-> SAM communication
//-----------------------------------------------------------------------------
#include "sam_picopass.h"
#include "sam_common.h"
#include "iclass.h"
#include "crc16.h"
#include "proxmark3_arm.h"
@ -30,66 +31,79 @@
#include "optimized_cipher.h"
#include "fpgaloader.h"
static int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) {
StartTicks();
/**
* @brief Sets the card detected status for the SAM (Secure Access Module).
*
* This function informs that a card has been detected by the reader and
* initializes SAM communication with the card.
*
* @param card_select Pointer to the descriptor of the detected card.
* @return Status code indicating success or failure of the operation.
*/
static int sam_set_card_detected(picopass_hdr_t *card_select) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_set_card_detected");
bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD");
goto out;
}
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
*resplen = ISO7816_MAX_FRAME;
// a0 12
// ad 10
// a0 0e
// 80 02
// 00 04 <- Picopass
// 81 08
// 9b fc a4 00 fb ff 12 e0 <- CSN
res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD");
goto out;
}
uint8_t payload[] = {
0xa0, 18, // <- SAM command
0xad, 16, // <- set detected card
0xa0, 4 + 10,
0x80, 2, // <- protocol
0x00, 0x04, // <- Picopass
0x81, 8, // <- CSN
card_select->csn[0], card_select->csn[1], card_select->csn[2], card_select->csn[3],
card_select->csn[4], card_select->csn[5], card_select->csn[6], card_select->csn[7]
};
uint16_t payload_len = sizeof(payload);
if (*resplen < 2) {
DbpString("received too few bytes from SIM CARD");
res = false;
goto out;
}
sam_send_payload(
0x44, 0x0a, 0x44,
payload,
&payload_len,
response,
&response_len
);
uint16_t more_len = 0;
// resp:
// c1 64 00 00 00
// bd 02 <- response
// 8a 00 <- empty response (accepted)
// 90 00
if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) {
more_len = resp[*resplen - 1];
if (response[5] != 0xbd) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("Invalid SAM response");
goto error;
} else {
// we done, return
// uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
// if(sam_response_an == NULL){
// if (g_dbglevel >= DBG_ERROR)
// Dbprintf("Invalid SAM response");
// goto error;
// }
goto out;
}
// Don't discard data we already received except the SW code.
// If we only received 1 byte, this is the echo of INS, we discard it.
*resplen -= 2;
if (*resplen == 1) {
*resplen = 0;
}
uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len};
res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN);
if (res == false) {
DbpString("failed to send to SIM CARD 2");
goto out;
}
more_len = 255 - *resplen;
res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY);
if (res == false) {
DbpString("failed to receive from SIM CARD 2");
goto out;
}
*resplen += more_len;
error:
res = PM3_ESOFT;
out:
StopTicks();
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_set_card_detected");
return res;
}
@ -218,23 +232,19 @@ int sam_picopass_get_pacs(void) {
uint8_t *sam_apdu = BigBuf_calloc(ISO7816_MAX_FRAME);
// -----------------------------------------------------------------------------
// first
// a0 da 02 63 1a 44 0a 44 00 00 00 a0 12 ad 10 a0 0e 80 02 00 04 81 08 9b fc a4 00 fb ff 12 e0
hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len);
memcpy(sam_apdu + sam_len, hdr.csn, sizeof(hdr.csn));
sam_len += sizeof(hdr.csn);
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
}
print_dbg("-- 1", resp, resp_len);
// first - set detected card (0xAD)
switch_clock_to_ticks();
sam_set_card_detected(&hdr);
// -----------------------------------------------------------------------------
// second
// a0 da 02 63 0d 44 0a 44 00 00 00 a0 05 a1 03 80 01 04
hexstr_to_byte_array("a0da02630d440a44000000a005a103800104", sam_apdu, &sam_len);
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
// second - get PACS (0xA1)
// a0 05
// a1 03
// 80 01
// 04
hexstr_to_byte_array("a005a103800104", sam_apdu, &sam_len);
if (sam_send_payload(0x44, 0x0a, 0x44, sam_apdu, (uint16_t *) &sam_len, resp, &resp_len) != PM3_SUCCESS) {
res = PM3_ECARDEXCHANGE;
goto out;
}
@ -245,7 +255,7 @@ int sam_picopass_get_pacs(void) {
// Tag|c00a140a000000a110a10e8004 0c05de64 8102 0004 820201f4
// -----------------------------------------------------------------------------
// third AIA block 5
// third AIA block 5 (emulated tag <-> SAM exchange starts here)
// a0da02631c140a00000000bd14a012a010800a ffffff0006fffffff88e 81020000
// picopass legacy is fixed. wants AIA and crc. ff ff ff ff ff ff ff ff ea f5
// picpoasss SE ff ff ff 00 06 ff ff ff f8 8e
@ -300,7 +310,7 @@ int sam_picopass_get_pacs(void) {
}
// start ssp clock again...
StartCountSspClk();
switch_clock_to_countsspclk();
// NOW we auth against tag
uint8_t cmd_check[9] = { ICLASS_CMD_CHECK };
@ -325,6 +335,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA026316140A00000000BD0EA00CA00A8004311E32E981020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, mac, sizeof(mac));
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@ -355,7 +366,7 @@ int sam_picopass_get_pacs(void) {
// c1 61 c1 00 00 a1 10 a1 0e 80 04 0c 06 45 56 81 02 00 04 82 02 01 f4 90 00
// read block 6
StartCountSspClk();
switch_clock_to_countsspclk();
start_time = GetCountSspClk();
iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod);
@ -373,6 +384,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A030303030003E017432381020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@ -382,7 +394,7 @@ int sam_picopass_get_pacs(void) {
// c161c10000a110a10e8004 0606455681020004820201f49000
// read the credential blocks
StartCountSspClk();
switch_clock_to_countsspclk();
start_time = GetCountSspClk();
iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod);
@ -400,6 +412,7 @@ int sam_picopass_get_pacs(void) {
hexstr_to_byte_array("A0DA026334140A00000000BD2CA02AA0288022030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C81020000", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
switch_clock_to_ticks();
if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) {
res = PM3_ECARDEXCHANGE;
goto out;
@ -409,7 +422,13 @@ int sam_picopass_get_pacs(void) {
// -----------------------------------------------------------------------------
// TEN ask for PACS data
// A0DA02630C440A00000000BD04A0028200
// A0 DA 02 63 0C
// 44 0A 00 00 00 00
// BD 04
// A0 02
// 82 00
// (emulated tag <-> SAM exchange ends here)
hexstr_to_byte_array("A0DA02630C440A00000000BD04A0028200", sam_apdu, &sam_len);
memcpy(sam_apdu + 19, resp, resp_len);
@ -424,7 +443,12 @@ int sam_picopass_get_pacs(void) {
goto out;
}
// c164000000bd098a07 030506951f9a00 9000
// resp:
// c1 64 00 00 00
// bd 09
// 8a 07
// 03 05 06 95 1f 9a 00 <- decoded PACS data
// 90 00
uint8_t *pacs = BigBuf_calloc(resp[8]);
memcpy(pacs, resp + 9, resp[8]);
@ -439,6 +463,7 @@ out:
off:
switch_off();
StopTicks();
BigBuf_free();
return res;
}

View file

@ -17,6 +17,7 @@
#define __SAM_PICOPASS_H
#include "common.h"
#include "sam_common.h"
int sam_picopass_get_pacs(void);

View file

@ -14,9 +14,392 @@
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// Routines to support SEOS <-> SAM communication
// communication and ASN.1 messages based on https://github.com/bettse/seader/blob/main/seader.asn1
//-----------------------------------------------------------------------------
#include "sam_seos.h"
#include "sam_common.h"
#include "iclass.h"
#include "proxmark3_arm.h"
#include "iso14443a.h"
#include "iclass.h"
#include "crc16.h"
#include "proxmark3_arm.h"
#include "BigBuf.h"
#include "cmd.h"
#include "commonutil.h"
#include "ticks.h"
#include "dbprint.h"
#include "i2c.h"
#include "protocols.h"
#include "optimized_cipher.h"
#include "fpgaloader.h"
#include "pm3_cmd.h"
#include "cmd.h"
/**
* @brief Sets the card detected status for the SAM (Secure Access Module).
*
* This function informs that a card has been detected by the reader and
* initializes SAM communication with the card.
*
* @param card_select Pointer to the descriptor of the detected card.
* @return Status code indicating success or failure of the operation.
*/
static int sam_set_card_detected(iso14a_card_select_t *card_select) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_set_card_detected");
uint8_t *request = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t request_len = ISO7816_MAX_FRAME;
uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME);
uint16_t response_len = ISO7816_MAX_FRAME;
const uint8_t payload[] = {
0xa0, 8, // <- SAM command
0xad, 6, // <- set detected card
0xa0, 4, // <- detected card details
0x80, 2, // <- protocol
0x00, 0x02 // <- ISO14443A
};
memcpy(request, payload, sizeof(payload));
sam_append_asn1_node(request, request + 4, 0x81, card_select->uid, card_select->uidlen);
sam_append_asn1_node(request, request + 4, 0x82, card_select->atqa, 2);
sam_append_asn1_node(request, request + 4, 0x83, &card_select->sak, 1);
request_len = request[1] + 2;
sam_send_payload(
0x44, 0x0a, 0x44,
request,
&request_len,
response,
&response_len
);
// resp:
// c1 64 00 00 00
// bd 02 <- response
// 8a 00 <- empty response (accepted)
// 90 00
if (response[5] != 0xbd) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("Invalid SAM response");
goto error;
} else {
// uint8_t * sam_response_an = sam_find_asn1_node(response + 5, 0x8a);
// if(sam_response_an == NULL){
// if (g_dbglevel >= DBG_ERROR)
// Dbprintf("Invalid SAM response");
// goto error;
// }
goto out;
}
error:
res = PM3_ESOFT;
out:
BigBuf_free();
if (g_dbglevel >= DBG_DEBUG)
DbpString("end sam_set_card_detected");
return res;
}
/**
* @brief Copies the payload from an NFC buffer to a SAM buffer.
*
* Wraps received data from NFC into an ASN1 tree, so it can be transmitted to the SAM .
*
* @param sam_tx Pointer to the SAM transmit buffer.
* @param nfc_rx Pointer to the NFC receive buffer.
* @param nfc_len Length of the data to be copied from the NFC buffer.
*
* @return Length of SAM APDU to be sent.
*/
inline static uint16_t sam_seos_copy_payload_nfc2sam(uint8_t *sam_tx, uint8_t *nfc_rx, uint8_t nfc_len) {
// NFC resp:
// 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3
// SAM req:
// bd 1c
// a0 1a
// a0 18
// 80 12
// 6f 0c 84 0a a0 00 00 04 40 00 01 01 00 01 90 00 fb e3
// 81 02
// 00 00
const uint8_t payload[] = {
0xbd, 4,
0xa0, 2,
0xa0, 0
};
const uint8_t tag81[] = {
0x00, 0x00
};
memcpy(sam_tx, payload, sizeof(payload));
sam_append_asn1_node(sam_tx, sam_tx + 4, 0x80, nfc_rx, nfc_len);
sam_append_asn1_node(sam_tx, sam_tx + 4, 0x81, tag81, sizeof(tag81));
return sam_tx[1] + 2; // length of the ASN1 tree
}
/**
* @brief Copies the payload from the SAM receive buffer to the NFC transmit buffer.
*
* Unpacks data to be transmitted from ASN1 tree in APDU received from SAM.
*
* @param nfc_tx_buf Pointer to the buffer where the NFC transmit data will be stored.
* @param sam_rx_buf Pointer to the buffer containing the data received from the SAM.
* @return Length of NFC APDU to be sent.
*/
inline static uint16_t sam_seos_copy_payload_sam2nfc(uint8_t *nfc_tx_buf, uint8_t *sam_rx_buf) {
// SAM resp:
// c1 61 c1 00 00
// a1 21 <- nfc command
// a1 1f <- nfc send
// 80 10 <- data
// 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00
// 81 02 <- protocol
// 02 02
// 82 02 <- timeout
// 01 2e
// 85 03 <- format
// 06 c0 00
// 90 00
// NFC req:
// 00 a4 04 00 0a a0 00 00 04 40 00 01 01 00 01 00
// copy data out of c1->a1>->a1->80 node
uint16_t nfc_tx_len = (uint8_t) * (sam_rx_buf + 10);
memcpy(nfc_tx_buf, sam_rx_buf + 11, nfc_tx_len);
return nfc_tx_len;
}
/**
* @brief Sends a request to the SAM and retrieves the response.
*
* Unpacks request to the SAM and relays ISO14A traffic to the card.
* If no request data provided, sends a request to get PACS data.
*
* @param request Pointer to the buffer containing the request to be sent to the SAM.
* @param request_len Length of the request to be sent to the SAM.
* @param response Pointer to the buffer where the retreived data will be stored.
* @param response_len Pointer to the variable where the length of the retreived data will be stored.
* @return Status code indicating success or failure of the operation.
*/
static int sam_send_request_iso14a(const uint8_t *const request, const uint8_t request_len, uint8_t *response, uint8_t *response_len) {
int res = PM3_SUCCESS;
if (g_dbglevel >= DBG_DEBUG)
DbpString("start sam_send_request_iso14a");
uint8_t buf1[ISO7816_MAX_FRAME] = {0};
uint8_t buf2[ISO7816_MAX_FRAME] = {0};
uint8_t *sam_tx_buf = buf1;
uint16_t sam_tx_len;
uint8_t *sam_rx_buf = buf2;
uint16_t sam_rx_len;
uint8_t *nfc_tx_buf = buf1;
uint16_t nfc_tx_len;
uint8_t *nfc_rx_buf = buf2;
uint16_t nfc_rx_len;
if (request_len > 0) {
sam_tx_len = request_len;
memcpy(sam_tx_buf, request, sam_tx_len);
} else {
// send get pacs
static const uint8_t payload[] = {
0xa0, 19, // <- SAM command
0xBE, 17, // <- samCommandGetContentElement2
0x80, 1,
0x04, // <- implicitFormatPhysicalAccessBits
0x84, 12,
0x2B, 0x06, 0x01, 0x04, 0x01, 0x81, 0xE4, 0x38, 0x01, 0x01, 0x02, 0x04 // <- SoRootOID
};
sam_tx_len = sizeof(payload);
memcpy(sam_tx_buf, payload, sam_tx_len);
}
sam_send_payload(
0x44, 0x0a, 0x44,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
if (sam_rx_buf[1] == 0x61) { // commands to be relayed to card starts with 0x61
// tag <-> SAM exchange starts here
while (sam_rx_buf[1] == 0x61) {
switch_clock_to_countsspclk();
nfc_tx_len = sam_seos_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf);
nfc_rx_len = iso14_apdu(
nfc_tx_buf,
nfc_tx_len,
false,
nfc_rx_buf,
ISO7816_MAX_FRAME,
NULL
);
switch_clock_to_ticks();
sam_tx_len = sam_seos_copy_payload_nfc2sam(sam_tx_buf, nfc_rx_buf, nfc_rx_len - 2);
sam_send_payload(
0x14, 0x0a, 0x14,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
// last SAM->TAG
// c1 61 c1 00 00 a1 02 >>82<< 00 90 00
if (sam_rx_buf[7] == 0x82) {
// tag <-> SAM exchange ends here
break;
}
}
static const uint8_t hfack[] = {
0xbd, 0x04, 0xa0, 0x02, 0x82, 0x00
};
sam_tx_len = sizeof(hfack);
memcpy(sam_tx_buf, hfack, sam_tx_len);
sam_send_payload(
0x14, 0x0a, 0x00,
sam_tx_buf, &sam_tx_len,
sam_rx_buf, &sam_rx_len
);
}
// resp for SamCommandGetContentElement:
// c1 64 00 00 00
// bd 09
// 8a 07
// 03 05 <- include tag for pm3 client
// 06 85 80 6d c0 <- decoded PACS data
// 90 00
// resp for samCommandGetContentElement2:
// c1 64 00 00 00
// bd 1e
// b3 1c
// a0 1a
// 80 05
// 06 85 80 6d c0
// 81 0e
// 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff
// 82 01
// 07
// 90 00
if (request_len == 0) {
if (
!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03)
&&
!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0)
) {
if (g_dbglevel >= DBG_ERROR)
Dbprintf("No PACS data in SAM response");
res = PM3_ESOFT;
}
}
*response_len = sam_rx_buf[5 + 1] + 2;
memcpy(response, sam_rx_buf + 5, *response_len);
goto out;
out:
return res;
}
/**
* @brief Retrieves PACS data from SEOS card using SAM.
*
* This function is called by appmain.c
* It sends a request to the SAM to get the PACS data from the SEOS card.
* The PACS data is then returned to the PM3 client.
*
* @return Status code indicating success or failure of the operation.
*/
int sam_seos_get_pacs(PacketCommandNG *c) {
bool disconnectAfter = c->oldarg[0] & 0x01;
bool skipDetect = c->oldarg[1] & 0x01;
uint8_t *cmd = c->data.asBytes;
uint16_t cmd_len = (uint16_t) c->oldarg[2];
int res = PM3_EFAILED;
clear_trace();
I2C_Reset_EnterMainProgram();
set_tracing(true);
StartTicks();
// step 1: ping SAM
sam_get_version();
if (!skipDetect) {
// step 2: get card information
iso14a_card_select_t card_a_info;
// implicit StartSspClk() happens here
iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
if (!iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) {
goto err;
}
switch_clock_to_ticks();
// step 3: SamCommand CardDetected
sam_set_card_detected(&card_a_info);
}
// step 3: SamCommand RequestPACS, relay NFC communication
uint8_t sam_response[ISO7816_MAX_FRAME] = { 0x00 };
uint8_t sam_response_len = 0;
res = sam_send_request_iso14a(cmd, cmd_len, sam_response, &sam_response_len);
if (res != PM3_SUCCESS) {
goto err;
}
if (g_dbglevel >= DBG_INFO)
print_result("Response data", sam_response, sam_response_len);
goto out;
goto off;
err:
res = PM3_ENOPACS;
reply_ng(CMD_HF_SAM_SEOS, res, NULL, 0);
goto off;
out:
reply_ng(CMD_HF_SAM_SEOS, PM3_SUCCESS, sam_response, sam_response_len);
goto off;
off:
if (disconnectAfter) {
switch_off();
}
set_tracing(false);
StopTicks();
BigBuf_free();
return res;
}

View file

@ -17,5 +17,8 @@
#define __SAM_SEOS_H
#include "common.h"
#include "pm3_cmd.h"
int sam_seos_get_pacs(PacketCommandNG *c);
#endif

View file

@ -5434,79 +5434,14 @@ static int CmdHFiClassSAM(const char *Cmd) {
// CSN, config, epurse, NR/MAC, AIA
// PACS
// 03 05
// 06 85 80 6d c0
// first byte skip
// second byte length
// third padded
// fourth ..
uint8_t *d = resp.data.asBytes;
uint8_t n = d[1] - 1; // skip length byte
uint8_t pad = d[2];
char *binstr = (char *)calloc((n * 8) + 1, sizeof(uint8_t));
if (binstr == NULL) {
return PM3_EMALLOC;
}
bytes_2_binstr(binstr, d + 3, n);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + 2, resp.length - 2));
PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
binstr[strlen(binstr) - pad] = '\0';
PrintAndLogEx(SUCCESS, "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));
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, "Wiegand decode");
wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr));
HIDTryUnpack(&packed);
PrintAndLogEx(NORMAL, "");
if (strlen(binstr) >= 26 && verbose) {
// iCLASS Legacy
PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy"));
PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
// HID Prox II
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II"));
PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
// MIFARE Classic
char mfcbin[28] = {0};
mfcbin[0] = '1';
memcpy(mfcbin + 1, binstr, strlen(binstr));
binstr_2_bytes(hex, &hexlen, mfcbin);
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)");
PrintAndLogEx(SUCCESS, " hf mf eclr;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen));
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;");
PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K"));
PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
}
free(binstr);
HIDDumpPACSBits(d + 2, d[1], verbose);
return PM3_SUCCESS;
}

View file

@ -30,6 +30,9 @@
#include "ui.h"
#include "cmdhf14a.h" // manufacture
#include "protocols.h" // definitions of ISO14A/7816 protocol
#include "cardhelper.h"
#include "wiegand_formats.h"
#include "wiegand_formatutils.h"
#include "iso7816/apduinfo.h" // GetAPDUCodeDescription
#include "crypto/asn1utils.h" // ASN1 decode / print
#include "crypto/libpcrypto.h" // AES decrypt
@ -97,6 +100,17 @@ static const known_algo_t known_algorithm_map[] = {
{9, "AES-128_CBC_MODE"},
};
static const sioMediaTypeName_t sioMediaTypeMapping[] = {
{ 0x00, "Unknown"},
{ 0x01, "DESFire"},
{ 0x02, "MIFARE"},
{ 0x03, "iCLASS (PicoPass)"},
{ 0x04, "ISO14443AL4"},
{ 0x06, "MIFARE Plus"},
{ 0x07, "Seos"},
{ 0xFF, "INVALID VALUE"}
};
static int create_cmac (uint8_t* key, uint8_t* input, uint8_t* out, int input_len, int encryption_algorithm) {
uint8_t iv[16] = {0x00};
@ -1632,10 +1646,145 @@ static int CmdHfSeosList(const char *Cmd) {
return CmdTraceListAlias(Cmd, "hf seos", "seos -c");
}
// get a SIO media type based on the UID
// uid[8] tag uid
// returns description of the best match
static const char *getSioMediaTypeInfo(uint8_t uid) {
for (int i = 0; i < ARRAYLEN(sioMediaTypeMapping); ++i) {
if (uid == sioMediaTypeMapping[i].uid) {
return sioMediaTypeMapping[i].desc;
}
}
//No match, return default
return sioMediaTypeMapping[ARRAYLEN(sioMediaTypeMapping) - 1].desc;
}
static int CmdHfSeosSAM(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf seos sam",
"Extract PACS via a HID SAM\n",
"hf seos sam\n"
"hd seos sam -d a005a103800104 -> get PACS data\n"
);
void *argtable[] = {
arg_param_begin,
arg_lit0("v", "verbose", "verbose output"),
arg_lit0("k", "keep", "keep the field active after command executed"),
arg_lit0("n", "nodetect", "skip selecting the card and sending card details to SAM"),
arg_lit0("t", "tlv", "decode TLV"),
arg_strx0("d", "data", "<hex>", "DER encoded command to send to SAM"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool verbose = false;
if (arg_get_lit(ctx, 1)) {
verbose = true;
}
bool disconnectAfter = true;
if (arg_get_lit(ctx, 2)) {
disconnectAfter = false;
}
bool skipDetect = false;
if (arg_get_lit(ctx, 3)) {
skipDetect = true;
}
bool decodeTLV = false;
if (arg_get_lit(ctx, 4)) {
decodeTLV = true;
}
uint8_t data[PM3_CMD_DATA_SIZE] = {0};
int datalen = 0;
CLIGetHexBLessWithReturn(ctx, 5, data, &datalen, 0);
CLIParserFree(ctx);
if (IsHIDSamPresent(verbose) == false) {
return PM3_ESOFT;
}
clearCommandBuffer();
SendCommandMIX(CMD_HF_SAM_SEOS, disconnectAfter, skipDetect, datalen, data, datalen);
PacketResponseNG resp;
if (WaitForResponseTimeout(CMD_HF_SAM_SEOS, &resp, 4000) == false) {
PrintAndLogEx(WARNING, "SAM timeout");
return PM3_ETIMEOUT;
}
switch (resp.status) {
case PM3_SUCCESS:
break;
case PM3_ENOPACS:
PrintAndLogEx(SUCCESS, "No PACS data found. Card empty?");
return resp.status;
default:
PrintAndLogEx(WARNING, "SAM select failed");
return resp.status;
}
uint8_t *d = resp.data.asBytes;
// check for standard SamCommandGetContentElement response
// bd 09
// 8a 07
// 03 05 <- tag + length
// 06 85 80 6d c0 <- decoded PACS data
if (d[0] == 0xbd && d[2] == 0x8a && d[4] == 0x03) {
uint8_t pacs_length = d[5];
uint8_t *pacs_data = d + 6;
int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose);
if (res != PM3_SUCCESS) {
return res;
}
// check for standard samCommandGetContentElement2:
// bd 1e
// b3 1c
// a0 1a
// 80 05
// 06 85 80 6d c0
// 81 0e
// 2b 06 01 04 01 81 e4 38 01 01 02 04 3c ff
// 82 01
// 07
} else if (d[0] == 0xbd && d[2] == 0xb3 && d[4] == 0xa0) {
const uint8_t *pacs = d + 6;
const uint8_t pacs_length = pacs[1];
const uint8_t *pacs_data = pacs + 2;
int res = HIDDumpPACSBits(pacs_data, pacs_length, verbose);
if (res != PM3_SUCCESS) {
return res;
}
const uint8_t *oid = pacs + 2 + pacs_length;
const uint8_t oid_length = oid[1];
const uint8_t *oid_data = oid + 2;
PrintAndLogEx(SUCCESS, "SIO OID.......: " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length));
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 {
print_hex(d, resp.length);
}
if (decodeTLV) {
asn1_print(d, d[1] + 2, " ");
}
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
{"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"},
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"list", CmdHfSeosList, AlwaysAvailable, "List SEOS history"},
{"sam", CmdHfSeosSAM, IfPm3Smartcard, "SAM tests"},
{"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"},
{"info", CmdHfSeosInfo, IfPm3NfcBarcode, "Tag information"},
{"pacs", CmdHfSeosPACS, AlwaysAvailable, "Extract PACS Information from card"},
@ -1643,7 +1792,6 @@ static command_t CommandTable[] = {
{"gdf", CmdHfSeosGDF, AlwaysAvailable, "Read an GDF from card"},
{"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Utils") " -----------------------"},
{"managekeys", CmdHfSeosManageKeys, AlwaysAvailable, "Manage keys to use with SEOS commands"},
{NULL, NULL, NULL, NULL}
};

View file

@ -21,8 +21,14 @@
#include "common.h"
// structure and database for uid -> tagtype lookups
typedef struct {
uint8_t uid;
const char *desc;
} sioMediaTypeName_t;
int infoSeos(bool verbose);
int CmdHFSeos(const char *Cmd);
int seos_kdf(bool encryption, uint8_t* masterKey, uint8_t keyslot,
uint8_t* adfOid, size_t adfoid_len, uint8_t* diversifier, uint8_t diversifier_len, uint8_t* out, int encryption_algorithm, int hash_algorithm);
int seos_kdf(bool encryption, uint8_t *masterKey, uint8_t keyslot,
uint8_t *adfOid, size_t adfoid_len, uint8_t *diversifier, uint8_t diversifier_len, uint8_t *out, int encryption_algorithm, int hash_algorithm);
#endif

View file

@ -1663,3 +1663,75 @@ void HIDUnpack(int idx, wiegand_message_t *packed) {
hid_print_card(&card, FormatTable[idx]);
}
}
int HIDDumpPACSBits(const uint8_t *const data, const uint8_t length, bool verbose) {
uint8_t n = length - 1;
uint8_t pad = data[0];
char *binstr = (char *)calloc((length * 8) + 1, sizeof(uint8_t));
if (binstr == NULL) {
return PM3_EMALLOC;
}
bytes_2_binstr(binstr, data + 1, n);
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(data, length));
PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr));
binstr[strlen(binstr) - pad] = '\0';
PrintAndLogEx(SUCCESS, "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));
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, "Wiegand decode");
wiegand_message_t packed = initialize_message_object(top, mid, bot, strlen(binstr));
HIDTryUnpack(&packed);
PrintAndLogEx(NORMAL, "");
if (strlen(binstr) >= 26 && verbose) {
// iCLASS Legacy
PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy"));
PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
// HID Prox II
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II"));
PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
// MIFARE Classic
char mfcbin[28] = {0};
mfcbin[0] = '1';
memcpy(mfcbin + 1, binstr, strlen(binstr));
binstr_2_bytes(hex, &hexlen, mfcbin);
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)");
PrintAndLogEx(SUCCESS, " hf mf eclr;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;");
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen));
PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;");
PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;");
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K"));
PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr);
PrintAndLogEx(NORMAL, "");
}
free(binstr);
return PM3_SUCCESS;
}

View file

@ -54,6 +54,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);
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);
#endif

View file

@ -336,4 +336,5 @@ void WaitUS(uint32_t us) {
void StopTicks(void) {
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS;
}