Add basic ISO14443 log data parser for Reader side traffic

Can parse: SELECT, HALE, RATS, PPS, DESELECT

(cherry picked from commit aa46598)
This commit is contained in:
chenzitai
2018-08-15 23:11:41 +01:00
parent eea78c8f56
commit 74010123cd
3 changed files with 102 additions and 5 deletions
+87
View File
@@ -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
+10 -2
View File
@@ -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)
+5 -3
View File
@@ -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))