diff --git a/Software/Chameleon/ISO14443.py b/Software/Chameleon/ISO14443.py new file mode 100644 index 0000000..a04d253 --- /dev/null +++ b/Software/Chameleon/ISO14443.py @@ -0,0 +1,87 @@ +import binascii + +ReaderTrafficTypes = { + "SEL":{ + 0x93: "SEL_CL1 ", + 0x95: "SEL_CL2 ", + 0x97: "SEL_CL3 ", + }, + # 1 byte commands + "SHORTFRAME": { + 0x26: "REQA", + 0x52: "WUPA", + 0x35: "Optional Timeslot Method", + # 40 - 4F Proprietary + # 78 - 7F proprietary + # Other RFU + }, + "FSDI":{ + 0x0: "FSD:16 ", + 0x1: "FSD:24 ", + 0x2: "FSD:32 ", + 0x3: "FSD:40 ", + 0x4: "FSD:48 ", + 0x5: "FSD:64 ", + 0x6: "FSD:96 ", + 0x7: "FSD:128 ", + 0x8: "FSD:256 " + }, +} + +CardTrafficTypes = { + +} +def parseReader(data): + byteCount = len(data) + note = "" + + # short frame commands + if (byteCount == 1 and data[0] in ReaderTrafficTypes["SHORTFRAME"]): + note += ReaderTrafficTypes["SHORTFRAME"][data[0]] + + # ANTICOLLISION command + elif (byteCount<9 and byteCount > 1 and data[0] in ReaderTrafficTypes["SEL"] and data[1] & 0x88 == 0 ): + note += "ATCOLI - " + note += ReaderTrafficTypes["SEL"][data[0]] + # note += "UID_CLn:" + binascii.hexlify(data[2:7]).decode() + " " + # note += str((data[1] >> 4) & 0x0f) + "bytes + " + str(data[1] & 0x0f) + "bits " + + # SELECT Command + elif (byteCount== 9 and data[0] in ReaderTrafficTypes["SEL"] and data[1] == 0x70): + note += "SELECT - " + note += ReaderTrafficTypes["SEL"][data[0]] + note += "UID_CLn:" + binascii.hexlify(data[2:7]).decode() + " " + # note += "7bytes + 0bits " + # note += "CRC_A:"+data[8] + # HALT Command + elif (byteCount == 4 and data[0] == 0x50 and data[1] == 0x00): + note += "HALT" + # RATS + elif (byteCount == 4 and data[0] == 0xe0 and ((data[1] & 0x0f) < 15) and ((data[1] & 0xf0 >> 8) in ReaderTrafficTypes["FSDI"])): + note += "RATS - " + note += ReaderTrafficTypes["FSDI"][data[1]>>8] + note += "CID:" + str(data[1]&0x0f) + " " + # PPS Protocol and parameter selection request + # PSS0 only + elif (byteCount == 4 and (data[0]&0xf0 == 0xd0) and (data[1] == 0x01)): + note += "PSS0 - " + note += "CID:"+str(data[1]&0x0f) + " " + # PSS0+1 + elif (byteCount == 5 and (data[0]&0xf0 == 0xd0) and (data[1] == 0x11) and (data[2]&0xf0 == 0x00)): + note += "PSS0+1 - " + note += "CID:"+str(data[1]&0x0f) + " " + note += "DSI:"+str(pow(2,(data[2]>>2)&0x03)) + " " + note += "DRI:"+str(pow(2,data[2]&0x03)) + " " + # BLOCK S-block PCB DESELECT + # Without CID + elif (byteCount == 3 and data[0] == 0xc2): + note += "DESEL" + # With CID + elif (byteCount == 4 and data[0] == 0xca and data[1]&0x30 == 0x00): + note += "DESEL - " + note += "CID:"+ str(data[1]&0x0f) + " " + + return note + +def parseCard(data): + pass \ No newline at end of file diff --git a/Software/Chameleon/Log.py b/Software/Chameleon/Log.py index 168704c..61d2098 100644 --- a/Software/Chameleon/Log.py +++ b/Software/Chameleon/Log.py @@ -3,6 +3,7 @@ import struct import binascii import math +import Chameleon.ISO14443 as iso14443_3 def checkParityBit(data): byteCount = len(data) @@ -96,7 +97,7 @@ eventTypes = { TIMESTAMP_MAX = 65536 eventTypes = { i : ({'name': 'UNKNOWN', 'decoder': binaryDecoder} if i not in eventTypes.keys() else eventTypes[i]) for i in range(256) } -def parseBinary(binaryStream): +def parseBinary(binaryStream, decode=False): log = [] # Completely read file contents and process them byte by byte @@ -135,13 +136,20 @@ def parseBinary(binaryStream): if (deltaTimestamp < 0): deltaTimestamp += TIMESTAMP_MAX; + note = "" + # If we need to decode the data + if (decode): + # Decode the data from Reader + if(event == 0x44 or event == 0x45): + note = iso14443_3.parseReader(binascii.a2b_hex(logData)) # Create log entry as dict and append it to event list logEntry = { 'eventName': eventTypes[event]['name'], 'dataLength': dataLength, 'timestamp': timestamp, 'deltaTimestamp': deltaTimestamp, - 'data': logData + 'data': logData, + 'note': note } log.append(logEntry) diff --git a/Software/chamlog.py b/Software/chamlog.py index f644379..da4281a 100755 --- a/Software/chamlog.py +++ b/Software/chamlog.py @@ -19,10 +19,11 @@ def verboseLog(text): def formatText(log): formatString = '{timestamp:0>5d} ms <{deltaTimestamp:>+6d} ms>:' - formatString += '{eventName:<16} ({dataLength:<3} bytes) [{data}]\n' + formatString += '{eventName:<28} ({dataLength:<3} bytes)\t[{data:<20}]\t{note}\n' text = '' for logEntry in log: + text += formatString.format(**logEntry) return text @@ -46,6 +47,7 @@ def main(): argParser.add_argument("-t", "--type", choices=outputTypes.keys(), default='text', help="specifies output type") + argParser.add_argument("-d", "--decode", dest="decode", action='store_true', default=False) argParser.add_argument("-l", "--live", dest="live", action='store_true', help="Use live logging capabilities of Chameleon") argParser.add_argument("-c", "--clear", dest="clear", action='store_true', help="Clear Chameleon's log memory when using -p") argParser.add_argument("-m", "--mode", dest="mode", metavar="LOGMODE", help="Additionally set Chameleon's log mode after reading it's memory") @@ -69,7 +71,7 @@ def main(): while True: stream = io.BytesIO(chameleon.read()) - log = Chameleon.Log.parseBinary(stream) + log = Chameleon.Log.parseBinary(stream, args.decode) if (len(log) > 0): print(outputTypes[args.type](log)) @@ -95,7 +97,7 @@ def main(): sys.exit(2) # Parse actual logfile - log = Chameleon.Log.parseBinary(handle) + log = Chameleon.Log.parseBinary(handle, args.decode) # Print to console using chosen output type print(outputTypes[args.type](log))