iClass SIO Parser

Improved SIO Parsing capabilities based on @iceman1001 black hat talk
This commit is contained in:
Antiklesys
2026-03-27 20:32:41 +08:00
parent 0ac7252215
commit 7d8c9f3f6e
2 changed files with 102 additions and 1 deletions
+1
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]
- Improved SIO parsing for `hf iclass view` based on Iceman's "Dismantling the SEOS Protocol" talk (@antiklesys)
- Added live fc/cn update to `hf iclass tagsim` refreshing the csn with each update (@antiklesys)
- Added `--live` option to `hf iclass lookup` command to perform a live recovery of the reader's key by simulating a tag and running the lookup command against both standard and elite dictionaries (@antiklesys)
- Added `hf iclass tagsim` command to quickly simulate an iclass card based on facility code and card number(@antiklesys)
+101 -1
View File
@@ -4942,7 +4942,105 @@ static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_le
*sio_length = sio_start[1] + 2;
}
// print ASN1 decoded array in TLV view
static void print_iclass_sio_decoded(uint8_t *sio, size_t sio_len) {
// Outer SEQUENCE already validated by detect_credential (short-form length, 0x30 tag)
if (sio_len < 4 || sio[0] != 0x30) {
return;
}
uint8_t *p = sio + 2;
uint8_t *end = sio + sio_len;
PrintAndLogEx(INFO, "-------------------- " _CYAN_("SIO - Insights") " -------------------------");
while (p + 2 <= end) {
uint8_t tag = *p++;
uint8_t len = *p++;
if (p + len > end) {
break;
}
uint8_t *val = p;
p += len;
switch (tag) {
case 0x81: {
// [1] IMPLICIT - Credential Template OID
// For iClass SE / Seos, the first byte is a preamble (0x01) and the actual OID is the remaining 4 bytes
if (len == 5 && val[0] == 0x01) {
PrintAndLogEx(INFO, " Relative OID : " _YELLOW_("%s") " ( Detected preamble: %02X - SE/SEOS )", sprint_hex_inrow(val, 5), val[0]);
} else {
PrintAndLogEx(INFO, " Relative OID : " _YELLOW_("%s"), sprint_hex_inrow(val, len));
}
break;
}
case 0x83: {
// [3] IMPLICIT - Tail present in SR and custom-keyed SE credentials
PrintAndLogEx(INFO, " OID Tail : " _YELLOW_("%s")" (SR / Custom-Keyed SE)", sprint_hex_inrow(val, len));
break;
}
case 0xA6: {
// [6] CONSTRUCTED - Key Info
uint8_t *ip = val;
uint8_t *ie = val + len;
while (ip + 2 <= ie) {
uint8_t itag = *ip++;
uint8_t ilen = *ip++;
if (ip + ilen > ie) break;
uint8_t *ival = ip;
ip += ilen;
if (itag == 0x80) {
// Custom Key OID (used instead of Key Reference ID for custom-keyed credentials)
PrintAndLogEx(INFO, " Custom Key OID : " _YELLOW_("%s"), sprint_hex_inrow(ival, ilen));
} else if (itag == 0x81 && ilen == 1) {
const char *ktype;
switch (ival[0]) {
case 0x00: ktype = "Elite keyed"; break;
case 0x01: ktype = "Standard keyed"; break;
default: ktype = "Custom keyed"; break;
}
PrintAndLogEx(INFO, " Key Reference ID : " _YELLOW_("0x%02X") " ( %s )", ival[0], ktype);
} else if (itag == 0x04 && ilen >= 1) {
uint8_t ctype = ival[ilen - 1];
const char *cname;
switch (ctype) {
case 0x08: cname = "EAX"; break;
case 0x09: cname = "EAX'"; break;
default: cname = "Unknown"; break;
}
PrintAndLogEx(INFO, " Crypto Suite : " _YELLOW_("%s") " ( %s )", sprint_hex_inrow(ival, ilen), cname);
}
}
break;
}
case 0xA7: {
// [7] CONSTRUCTED - PACS Encrypted Payload
uint8_t *ip = val;
uint8_t *ie = val + len;
while (ip + 2 <= ie) {
uint8_t itag = *ip++;
uint8_t ilen = *ip++;
if (ip + ilen > ie) break;
uint8_t *ival = ip;
ip += ilen;
if (itag == 0x85) {
if (ilen > 16) {
size_t data_len = ilen - 16;
PrintAndLogEx(INFO, " PACS Encrypted Data: " _YELLOW_("%s"), sprint_hex_inrow(ival, data_len));
PrintAndLogEx(INFO, " PACS MAC Signature : " _YELLOW_("%s"), sprint_hex_inrow(ival + data_len, 16));
} else if (ilen > 0) {
PrintAndLogEx(INFO, " PACS Payload : " _YELLOW_("%s"), sprint_hex_inrow(ival, ilen));
}
}
}
break;
}
default:
break;
}
}
}
// print SIO decoded fields and optionally raw ASN1 TLV
void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose) {
bool is_legacy, is_se, is_sr;
@@ -4964,6 +5062,8 @@ void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len, bool verbose) {
PrintAndLogEx(INFO, "--------------------------- " _CYAN_("SIO - RAW") " -----------------------------");
print_hex_noascii_break(sio_start, sio_length, 32);
PrintAndLogEx(NORMAL, "");
print_iclass_sio_decoded(sio_start, sio_length);
PrintAndLogEx(NORMAL, "");
if (verbose) {
PrintAndLogEx(INFO, "----------------------- " _CYAN_("SIO - ASN1 TLV") " ---------------------------");
asn1_print(sio_start, sio_length, " ");