mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-01-01 05:07:03 +08:00
Some work on iclass dump and iclass list, now the dumping is a lot more stable. I think the comms should be measured and tuned a bit more, right now it kind of works thanks to retry-functionality, but the retries are probably not needed if we are a bit more careful about timing, so we don't send commands too fast for the tag to handle
This commit is contained in:
parent
2e9d4b3ff4
commit
c8dd9b092e
3 changed files with 181 additions and 157 deletions
298
armsrc/iclass.c
298
armsrc/iclass.c
|
@ -1474,100 +1474,133 @@ void setupIclassReader()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t sendCmdGetResponseWithRetries(uint8_t* command, size_t cmdsize, uint8_t* resp, uint8_t expected_size, uint8_t retries)
|
||||||
|
{
|
||||||
|
while(retries-- > 0)
|
||||||
|
{
|
||||||
|
ReaderTransmitIClass(command, cmdsize);
|
||||||
|
if(expected_size == ReaderReceiveIClass(resp)){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1;//Error
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Talks to an iclass tag, sends the commands to get CSN and CC.
|
||||||
|
* @param card_data where the CSN and CC are stored for return
|
||||||
|
* @return 0 = fail
|
||||||
|
* 1 = Got CSN
|
||||||
|
* 2 = Got CSN and CC
|
||||||
|
*/
|
||||||
|
uint8_t handshakeIclassTag(uint8_t *card_data)
|
||||||
|
{
|
||||||
|
static uint8_t act_all[] = { 0x0a };
|
||||||
|
static uint8_t identify[] = { 0x0c };
|
||||||
|
static uint8_t select[] = { 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
|
static uint8_t readcheck_cc[]= { 0x88, 0x02 };
|
||||||
|
uint8_t *resp = (((uint8_t *)BigBuf) + RECV_RESP_OFFSET);
|
||||||
|
|
||||||
|
uint8_t read_status = 0;
|
||||||
|
|
||||||
|
// Send act_all
|
||||||
|
ReaderTransmitIClass(act_all, 1);
|
||||||
|
// Card present?
|
||||||
|
if(!ReaderReceiveIClass(resp)) return read_status;//Fail
|
||||||
|
//Send Identify
|
||||||
|
ReaderTransmitIClass(identify, 1);
|
||||||
|
//We expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC
|
||||||
|
uint8_t len = ReaderReceiveIClass(resp);
|
||||||
|
if(len != 10) return read_status;//Fail
|
||||||
|
|
||||||
|
//Copy the Anti-collision CSN to our select-packet
|
||||||
|
memcpy(&select[1],resp,8);
|
||||||
|
//Select the card
|
||||||
|
ReaderTransmitIClass(select, sizeof(select));
|
||||||
|
//We expect a 10-byte response here, 8 byte CSN and 2 byte CRC
|
||||||
|
len = ReaderReceiveIClass(resp);
|
||||||
|
if(len != 10) return read_status;//Fail
|
||||||
|
|
||||||
|
//Success - level 1, we got CSN
|
||||||
|
//Save CSN in response data
|
||||||
|
memcpy(card_data,resp,8);
|
||||||
|
|
||||||
|
//Flag that we got to at least stage 1, read CSN
|
||||||
|
read_status = 1;
|
||||||
|
|
||||||
|
// Card selected, now read e-purse (cc)
|
||||||
|
ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc));
|
||||||
|
if(ReaderReceiveIClass(resp) == 8) {
|
||||||
|
//Save CC (e-purse) in response data
|
||||||
|
memcpy(card_data+8,resp,8);
|
||||||
|
|
||||||
|
//Got both
|
||||||
|
read_status = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return read_status;
|
||||||
|
}
|
||||||
|
|
||||||
// Reader iClass Anticollission
|
// Reader iClass Anticollission
|
||||||
void ReaderIClass(uint8_t arg0) {
|
void ReaderIClass(uint8_t arg0) {
|
||||||
uint8_t act_all[] = { 0x0a };
|
|
||||||
uint8_t identify[] = { 0x0c };
|
|
||||||
uint8_t select[] = { 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
||||||
uint8_t readcheck_cc[]= { 0x88, 0x02 };
|
|
||||||
|
|
||||||
uint8_t card_data[24]={0};
|
uint8_t card_data[24]={0};
|
||||||
uint8_t last_csn[8]={0};
|
uint8_t last_csn[8]={0};
|
||||||
|
|
||||||
uint8_t *resp = (((uint8_t *)BigBuf) + RECV_RESP_OFFSET);
|
|
||||||
|
|
||||||
int read_status= 0;
|
int read_status= 0;
|
||||||
bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE;
|
bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE;
|
||||||
|
bool get_cc = arg0 & FLAG_ICLASS_READER_GET_CC;
|
||||||
|
|
||||||
setupIclassReader();
|
setupIclassReader();
|
||||||
|
|
||||||
size_t datasize = 0;
|
size_t datasize = 0;
|
||||||
while(!BUTTON_PRESS())
|
while(!BUTTON_PRESS())
|
||||||
{
|
{
|
||||||
WDT_HIT();
|
|
||||||
|
|
||||||
// Send act_all
|
if(traceLen > TRACE_SIZE) {
|
||||||
ReaderTransmitIClass(act_all, 1);
|
DbpString("Trace full");
|
||||||
// Card present?
|
break;
|
||||||
if(ReaderReceiveIClass(resp)) {
|
}
|
||||||
|
WDT_HIT();
|
||||||
|
|
||||||
ReaderTransmitIClass(identify, 1);
|
read_status = handshakeIclassTag(card_data);
|
||||||
|
|
||||||
if(ReaderReceiveIClass(resp) == 10) {
|
if(read_status == 0) continue;
|
||||||
//Copy the Anti-collision CSN to our select-packet
|
if(read_status == 1) datasize = 8;
|
||||||
memcpy(&select[1],resp,8);
|
if(read_status == 2) datasize = 16;
|
||||||
//Dbprintf("Anti-collision CSN: %02x %02x %02x %02x %02x %02x %02x %02x",resp[0], resp[1], resp[2],
|
|
||||||
// resp[3], resp[4], resp[5],
|
|
||||||
// resp[6], resp[7]);
|
|
||||||
//Select the card
|
|
||||||
ReaderTransmitIClass(select, sizeof(select));
|
|
||||||
|
|
||||||
if(ReaderReceiveIClass(resp) == 10) {
|
LED_B_ON();
|
||||||
//Save CSN in response data
|
//Send back to client, but don't bother if we already sent this
|
||||||
memcpy(card_data,resp,8);
|
if(memcmp(last_csn, card_data, 8) != 0)
|
||||||
datasize += 8;
|
{
|
||||||
//Flag that we got to at least stage 1, read CSN
|
|
||||||
read_status = 1;
|
|
||||||
|
|
||||||
// Card selected
|
if(!get_cc || (get_cc && read_status == 2))
|
||||||
//Dbprintf("Readcheck on Sector 2");
|
{
|
||||||
ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc));
|
cmd_send(CMD_ACK,read_status,0,0,card_data,datasize);
|
||||||
if(ReaderReceiveIClass(resp) == 8) {
|
if(abort_after_read) {
|
||||||
//Save CC (e-purse) in response data
|
LED_A_OFF();
|
||||||
memcpy(card_data+8,resp,8);
|
return;
|
||||||
datasize += 8;
|
}
|
||||||
//Got both
|
//Save that we already sent this....
|
||||||
read_status = 2;
|
memcpy(last_csn, card_data, 8);
|
||||||
}
|
}
|
||||||
|
//If 'get_cc' was specified and we didn't get a CC, we'll just keep trying...
|
||||||
LED_B_ON();
|
}
|
||||||
//Send back to client, but don't bother if we already sent this
|
LED_B_OFF();
|
||||||
if(memcmp(last_csn, card_data, 8) != 0)
|
|
||||||
cmd_send(CMD_ACK,read_status,0,0,card_data,datasize);
|
|
||||||
|
|
||||||
//Save that we already sent this....
|
|
||||||
if(read_status == 2)
|
|
||||||
memcpy(last_csn, card_data, 8);
|
|
||||||
|
|
||||||
LED_B_OFF();
|
|
||||||
|
|
||||||
if(abort_after_read) break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(traceLen > TRACE_SIZE) {
|
|
||||||
DbpString("Trace full");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_send(CMD_ACK,0,0,0,card_data, 0);
|
cmd_send(CMD_ACK,0,0,0,card_data, 0);
|
||||||
|
|
||||||
LED_A_OFF();
|
LED_A_OFF();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) {
|
void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) {
|
||||||
uint8_t act_all[] = { 0x0a };
|
|
||||||
uint8_t identify[] = { 0x0c };
|
uint8_t card_data[24]={0};
|
||||||
uint8_t select[] = { 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
||||||
uint8_t readcheck_cc[]= { 0x88, 0x02 };
|
|
||||||
uint8_t check[] = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
uint8_t check[] = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
uint8_t read[] = { 0x0c, 0x00, 0x00, 0x00 };
|
uint8_t read[] = { 0x0c, 0x00, 0x00, 0x00 };
|
||||||
|
|
||||||
uint16_t crc = 0;
|
uint16_t crc = 0;
|
||||||
uint8_t cardsize=0;
|
uint8_t cardsize=0;
|
||||||
bool read_success=false;
|
|
||||||
uint8_t mem=0;
|
uint8_t mem=0;
|
||||||
|
|
||||||
static struct memory_t{
|
static struct memory_t{
|
||||||
|
@ -1583,102 +1616,73 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) {
|
||||||
setupIclassReader();
|
setupIclassReader();
|
||||||
|
|
||||||
|
|
||||||
for(int i=0;i<1;i++) {
|
while(!BUTTON_PRESS()) {
|
||||||
|
|
||||||
if(traceLen > TRACE_SIZE) {
|
if(traceLen > TRACE_SIZE) {
|
||||||
DbpString("Trace full");
|
DbpString("Trace full");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (BUTTON_PRESS()) break;
|
|
||||||
|
|
||||||
// Send act_all
|
uint8_t read_status = handshakeIclassTag(card_data);
|
||||||
ReaderTransmitIClass(act_all, 1);
|
if(read_status < 2) continue;
|
||||||
// Card present?
|
|
||||||
if(ReaderReceiveIClass(resp)) {
|
|
||||||
ReaderTransmitIClass(identify, 1);
|
|
||||||
if(ReaderReceiveIClass(resp) == 10) {
|
|
||||||
// Select card
|
|
||||||
memcpy(&select[1],resp,8);
|
|
||||||
ReaderTransmitIClass(select, sizeof(select));
|
|
||||||
|
|
||||||
if(ReaderReceiveIClass(resp) == 10) {
|
//for now replay captured auth (as cc not updated)
|
||||||
Dbprintf(" Selected CSN: %02x %02x %02x %02x %02x %02x %02x %02x",
|
memcpy(check+5,MAC,4);
|
||||||
resp[0], resp[1], resp[2],
|
|
||||||
resp[3], resp[4], resp[5],
|
if(sendCmdGetResponseWithRetries(check, sizeof(check),resp, 4, 5))
|
||||||
resp[6], resp[7]);
|
{
|
||||||
}
|
Dbprintf("Error: Authentication Fail!");
|
||||||
// Card selected
|
continue;
|
||||||
Dbprintf("Readcheck on Sector 2");
|
}
|
||||||
ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc));
|
|
||||||
if(ReaderReceiveIClass(resp) == 8) {
|
//first get configuration block
|
||||||
Dbprintf(" CC: %02x %02x %02x %02x %02x %02x %02x %02x",
|
read[1]=1;
|
||||||
resp[0], resp[1], resp[2],
|
uint8_t *blockno=&read[1];
|
||||||
resp[3], resp[4], resp[5],
|
crc = iclass_crc16((char *)blockno,1);
|
||||||
resp[6], resp[7]);
|
read[2] = crc >> 8;
|
||||||
}else return;
|
read[3] = crc & 0xff;
|
||||||
Dbprintf("Authenticate");
|
|
||||||
//for now replay captured auth (as cc not updated)
|
if(sendCmdGetResponseWithRetries(read, sizeof(read),resp, 10, 10))
|
||||||
memcpy(check+5,MAC,4);
|
{
|
||||||
//Dbprintf(" AA: %02x %02x %02x %02x",
|
Dbprintf("Dump config block failed");
|
||||||
// check[5], check[6], check[7],check[8]);
|
continue;
|
||||||
ReaderTransmitIClass(check, sizeof(check));
|
}
|
||||||
if(ReaderReceiveIClass(resp) == 4) {
|
|
||||||
Dbprintf(" AR: %02x %02x %02x %02x",
|
mem=resp[5];
|
||||||
resp[0], resp[1], resp[2],resp[3]);
|
memory.k16= (mem & 0x80);
|
||||||
}else {
|
memory.book= (mem & 0x20);
|
||||||
Dbprintf("Error: Authentication Fail!");
|
memory.k2= (mem & 0x8);
|
||||||
return;
|
memory.lockauth= (mem & 0x2);
|
||||||
}
|
memory.keyaccess= (mem & 0x1);
|
||||||
Dbprintf("Dump Contents");
|
|
||||||
//first get configuration block
|
cardsize = memory.k16 ? 255 : 32;
|
||||||
read_success=false;
|
WDT_HIT();
|
||||||
read[1]=1;
|
|
||||||
uint8_t *blockno=&read[1];
|
//then loop around remaining blocks
|
||||||
crc = iclass_crc16((char *)blockno,1);
|
for(char block=0; block < cardsize; block++){
|
||||||
read[2] = crc >> 8;
|
|
||||||
read[3] = crc & 0xff;
|
read[1]= block;
|
||||||
while(!read_success){
|
crc = iclass_crc16(&block ,1);
|
||||||
ReaderTransmitIClass(read, sizeof(read));
|
read[2] = crc >> 8;
|
||||||
if(ReaderReceiveIClass(resp) == 10) {
|
read[3] = crc & 0xff;
|
||||||
read_success=true;
|
|
||||||
mem=resp[5];
|
if(!sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 10))
|
||||||
memory.k16= (mem & 0x80);
|
{
|
||||||
memory.book= (mem & 0x20);
|
Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x",
|
||||||
memory.k2= (mem & 0x8);
|
block, resp[0], resp[1], resp[2],
|
||||||
memory.lockauth= (mem & 0x2);
|
resp[3], resp[4], resp[5],
|
||||||
memory.keyaccess= (mem & 0x1);
|
resp[6], resp[7]);
|
||||||
|
|
||||||
|
}else{
|
||||||
|
Dbprintf("Failed to dump block %d", block);
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (memory.k16){
|
|
||||||
cardsize=255;
|
|
||||||
}else cardsize=32;
|
|
||||||
//then loop around remaining blocks
|
|
||||||
for(uint8_t j=0; j<cardsize; j++){
|
|
||||||
read_success=false;
|
|
||||||
uint8_t *blockno=&j;
|
|
||||||
//crc_data[0]=j;
|
|
||||||
read[1]=j;
|
|
||||||
crc = iclass_crc16((char *)blockno,1);
|
|
||||||
read[2] = crc >> 8;
|
|
||||||
read[3] = crc & 0xff;
|
|
||||||
while(!read_success){
|
|
||||||
ReaderTransmitIClass(read, sizeof(read));
|
|
||||||
if(ReaderReceiveIClass(resp) == 10) {
|
|
||||||
read_success=true;
|
|
||||||
Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x",
|
|
||||||
j, resp[0], resp[1], resp[2],
|
|
||||||
resp[3], resp[4], resp[5],
|
|
||||||
resp[6], resp[7]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//If we got here, let's break
|
||||||
|
break;
|
||||||
WDT_HIT();
|
WDT_HIT();
|
||||||
}
|
}
|
||||||
|
|
||||||
LED_A_OFF();
|
LED_A_OFF();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,19 +42,38 @@ int xorbits_8(uint8_t val)
|
||||||
return res & 1;
|
return res & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ICLASS_CMD_ACTALL 0x0A
|
||||||
|
#define ICLASS_CMD_IDENTIFY 0x0C
|
||||||
|
#define ICLASS_CMD_READ 0x0C
|
||||||
|
|
||||||
|
#define ICLASS_CMD_SELECT 0x81
|
||||||
|
#define ICLASS_CMD_PAGESEL 0x84
|
||||||
|
#define ICLASS_CMD_READCHECK 0x88
|
||||||
|
#define ICLASS_CMD_CHECK 0x05
|
||||||
|
#define ICLASS_CMD_SOF 0x0F
|
||||||
|
#define ICLASS_CMD_HALT 0x00
|
||||||
|
|
||||||
|
|
||||||
void explain(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize)
|
void explain(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if(cmdsize > 1 && cmd[0] == ICLASS_CMD_READ)
|
||||||
|
{
|
||||||
|
snprintf(exp,size,"READ(%d)",cmd[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch(cmd[0])
|
switch(cmd[0])
|
||||||
{
|
{
|
||||||
case 0x0a: snprintf(exp,size,"WUP"); break;
|
case ICLASS_CMD_ACTALL: snprintf(exp,size,"ACTALL"); break;
|
||||||
case 0x0f: snprintf(exp,size,"SOF"); break;
|
case ICLASS_CMD_IDENTIFY: snprintf(exp,size,"IDENTIFY"); break;
|
||||||
case 0x0c: snprintf(exp,size,"Read config"); break;
|
case ICLASS_CMD_SELECT: snprintf(exp,size,"SELECT"); break;
|
||||||
case 0x81: snprintf(exp,size,"SELECT"); break;
|
case ICLASS_CMD_PAGESEL: snprintf(exp,size,"PAGESEL"); break;
|
||||||
case 0x88: snprintf(exp,size,"Read E-purse (CC)"); break;
|
case ICLASS_CMD_READCHECK: snprintf(exp,size,"READCHECK"); break;
|
||||||
case 0x05: snprintf(exp,size,"Reader challenge"); break;
|
case ICLASS_CMD_CHECK: snprintf(exp,size,"CHECK"); break;
|
||||||
case 0x00: snprintf(exp,size,"End"); break;
|
case ICLASS_CMD_SOF: snprintf(exp,size,"SOF"); break;
|
||||||
default: snprintf(exp,size,"?"); break;
|
case ICLASS_CMD_HALT: snprintf(exp,size,"HALT"); break;
|
||||||
|
default: snprintf(exp,size,"?"); break;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -447,7 +466,7 @@ int CmdHFiClassReader_Dump(const char *Cmd)
|
||||||
|
|
||||||
|
|
||||||
UsbCommand c = {CMD_READER_ICLASS, {0}};
|
UsbCommand c = {CMD_READER_ICLASS, {0}};
|
||||||
c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE;
|
c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE| FLAG_ICLASS_READER_GET_CC;
|
||||||
if(!fake_dummy_test)
|
if(!fake_dummy_test)
|
||||||
SendCommand(&c);
|
SendCommand(&c);
|
||||||
|
|
||||||
|
|
|
@ -167,7 +167,8 @@ typedef struct {
|
||||||
|
|
||||||
|
|
||||||
//Iclass reader flags
|
//Iclass reader flags
|
||||||
#define FLAG_ICLASS_READER_ONLY_ONCE 0x01
|
#define FLAG_ICLASS_READER_ONLY_ONCE 0x01
|
||||||
|
#define FLAG_ICLASS_READER_GET_CC 0x02
|
||||||
|
|
||||||
// CMD_DEVICE_INFO response packet has flags in arg[0], flag definitions:
|
// CMD_DEVICE_INFO response packet has flags in arg[0], flag definitions:
|
||||||
/* Whether a bootloader that understands the common_area is present */
|
/* Whether a bootloader that understands the common_area is present */
|
||||||
|
|
Loading…
Reference in a new issue