UPD: pm3_mfdread.py - updates from original repo (@zhovner) 96d0ac184a

This commit is contained in:
iceman1001 2017-10-22 10:25:14 +02:00
commit d2dd6eed6a

View file

@ -4,31 +4,39 @@
# mfdread.py - Mifare dumps parser in human readable format # mfdread.py - Mifare dumps parser in human readable format
# Pavel Zhovner <pavel@zhovner.com> # Pavel Zhovner <pavel@zhovner.com>
# https://github.com/zhovner/mfdread # https://github.com/zhovner/mfdread
#
#Dependencies:
# easy_install bitstring
#or
# pip install bitstring
#Usage:
# mfdread.py ./dump.mfd
#
import codecs import codecs
import sys
import copy import copy
from struct import unpack import sys
from datetime import datetime from collections import defaultdict
from bitstring import BitArray from bitstring import BitArray
class Options: class Options:
FORCE_1K = False FORCE_1K = False
if len(sys.argv) == 1: if len(sys.argv) == 1:
print(''' sys.exit('''
------------------ ------------------
Usage: mfdread.py ./dump.mfd Usage: mfdread.py ./dump.mfd
Mifare dumps reader. Mifare dumps reader.
''') ''')
sys.exit();
def d(bytes):
def decode(bytes):
decoded = codecs.decode(bytes, "hex") decoded = codecs.decode(bytes, "hex")
try: try:
return str(decoded, "utf-8").rstrip('\0') return str(decoded, "utf-8").rstrip(b'\0')
except: except:
return "" return ""
@ -49,6 +57,7 @@ def accbits_for_blocknum(accbits_str, blocknum):
''' '''
bits = BitArray([0]) bits = BitArray([0])
inverted = BitArray([0]) inverted = BitArray([0])
# Block 0 access bits # Block 0 access bits
if blocknum == 0: if blocknum == 0:
bits = BitArray([accbits_str[11], accbits_str[23], accbits_str[19]]) bits = BitArray([accbits_str[11], accbits_str[23], accbits_str[19]])
@ -63,7 +72,7 @@ def accbits_for_blocknum(accbits_str, blocknum):
bits = BitArray([accbits_str[9], accbits_str[21], accbits_str[17]]) bits = BitArray([accbits_str[9], accbits_str[21], accbits_str[17]])
inverted = BitArray([accbits_str[5], accbits_str[1], accbits_str[13]]) inverted = BitArray([accbits_str[5], accbits_str[1], accbits_str[13]])
# Sector trailer / Block 3 access bits # Sector trailer / Block 3 access bits
elif blocknum == 3: elif blocknum in (3, 15):
bits = BitArray([accbits_str[8], accbits_str[20], accbits_str[16]]) bits = BitArray([accbits_str[8], accbits_str[20], accbits_str[16]])
inverted = BitArray([accbits_str[4], accbits_str[0], accbits_str[12]]) inverted = BitArray([accbits_str[4], accbits_str[0], accbits_str[12]])
@ -91,6 +100,7 @@ def accbits_to_permission_sector(accbits):
else: else:
return "" return ""
def accbits_to_permission_data(accbits): def accbits_to_permission_data(accbits):
permissions = { permissions = {
'000': "A/B | A/B | A/B | A/B [transport]", '000': "A/B | A/B | A/B | A/B [transport]",
@ -108,98 +118,126 @@ def accbits_to_permission_data(accbits):
return "" return ""
def accbit_info(accbits): def accbit_info(accbits, sector_size):
''' '''
Returns a dictionary of a access bits for all three blocks in a sector. Returns a dictionary of a access bits for all three blocks in a sector.
If the access bits for block could not be decoded properly, the value is set to False. If the access bits for block could not be decoded properly, the value is set to False.
''' '''
decAccbits = {} access_bits = defaultdict(lambda: False)
if sector_size == 15:
access_bits[sector_size] = accbits_for_blocknum(accbits, sector_size)
return access_bits
# Decode access bits for all 4 blocks of the sector # Decode access bits for all 4 blocks of the sector
for i in range(0, 4): for i in range(0, 4):
decAccbits[i] = accbits_for_blocknum(accbits, i) access_bits[i] = accbits_for_blocknum(accbits, i)
return decAccbits return access_bits
def print_info(data): def print_info(data):
blocksmatrix = [] blocksmatrix = []
blockrights = {} blockrights = {}
block_number = 0
# determine what dump we get 1k or 4k data_size = len(data)
if len(data) == 4096:
cardsize = 64 if data_size not in {4096, 1024}:
elif len(data) == 1024: sys.exit("Wrong file size: %d bytes.\nOnly 1024 or 4096 allowed." % len(data))
cardsize = 16
else:
print("Wrong file size: %d bytes.\nOnly 1024 or 4096 allowed." % len(data))
sys.exit();
if Options.FORCE_1K: if Options.FORCE_1K:
cardsize = 16 data_size = 1024
# read all sectors # read all sectors
for i in range(0, cardsize): sector_number = 0
start = i * 64 start = 0
end = (i + 1) * 64 end = 64
while True:
sector = data[start:end] sector = data[start:end]
sector = codecs.encode(sector, 'hex') sector = codecs.encode(sector, 'hex')
if not type(sector) is str: if not isinstance(sector, str):
sector = str(sector, 'ascii') sector = str(sector, 'ascii')
blocksmatrix.append([sector[x:x+32] for x in range(0, len(sector), 32)]) sectors = [sector[x:x + 32] for x in range(0, len(sector), 32)]
blocksmatrix.append(sectors)
# after 32 sectors each sector has 16 blocks instead of 4
sector_number += 1
if sector_number < 32:
start += 64
end += 64
elif sector_number == 32:
start += 64
end += 256
else:
start += 256
end += 256
if start == data_size:
break
blocksmatrix_clear = copy.deepcopy(blocksmatrix) blocksmatrix_clear = copy.deepcopy(blocksmatrix)
# add colors for each keyA, access bits, KeyB # add colors for each keyA, access bits, KeyB
for c in range(0, len(blocksmatrix)): for c in range(0, len(blocksmatrix)):
sector_size = len(blocksmatrix[c]) - 1
# Fill in the access bits # Fill in the access bits
blockrights[c] = accbit_info(BitArray('0x'+blocksmatrix[c][3][12:20])) blockrights[c] = accbit_info(BitArray('0x' + blocksmatrix[c][sector_size][12:20]), sector_size)
# Prepare colored output of the sector trailor # Prepare colored output of the sector trailor
keyA = bashcolors.RED + blocksmatrix[c][3][0:12] + bashcolors.ENDC keyA = bashcolors.RED + blocksmatrix[c][sector_size][0:12] + bashcolors.ENDC
accbits = bashcolors.GREEN + blocksmatrix[c][3][12:20] + bashcolors.ENDC accbits = bashcolors.GREEN + blocksmatrix[c][sector_size][12:20] + bashcolors.ENDC
keyB = bashcolors.BLUE + blocksmatrix[c][3][20:32] + bashcolors.ENDC keyB = bashcolors.BLUE + blocksmatrix[c][sector_size][20:32] + bashcolors.ENDC
blocksmatrix[c][3] = keyA + accbits + keyB
blocksmatrix[c][sector_size] = keyA + accbits + keyB
print("File size: %d bytes. Expected %d sectors" %(len(data),cardsize)) print("File size: %d bytes. Expected %d sectors" % (len(data), sector_number))
print("\n\tUID: " + blocksmatrix[0][0][0:8]) print("\n\tUID: " + blocksmatrix[0][0][0:8])
print("\tBCC: " + blocksmatrix[0][0][8:10]) print("\tBCC: " + blocksmatrix[0][0][8:10])
print("\tSAK: " + blocksmatrix[0][0][10:12]) print("\tSAK: " + blocksmatrix[0][0][10:12])
print("\tATQA: " + blocksmatrix[0][0][12:14]) print("\tATQA: " + blocksmatrix[0][0][12:14])
print(" %sKey A%s %sAccess Bits%s %sKey B%s" %(bashcolors.RED,bashcolors.ENDC,bashcolors.GREEN,bashcolors.ENDC,bashcolors.BLUE,bashcolors.ENDC)) print(" %sKey A%s %sAccess Bits%s %sKey B%s" % (
print("╔═════════╦═════╦══════════════════════════════════╦════════╦═════════════════════════════════════╗") bashcolors.RED, bashcolors.ENDC, bashcolors.GREEN, bashcolors.ENDC, bashcolors.BLUE, bashcolors.ENDC))
print("╔═════════╦═══════╦══════════════════════════════════╦════════╦═════════════════════════════════════╗")
print("║ Sector ║ Block ║ Data ║ Access ║ A | Acc. | B ║") print("║ Sector ║ Block ║ Data ║ Access ║ A | Acc. | B ║")
print("║ ║ ║ ║ ║ r w | r w | r w [info] ║") print("║ ║ ║ ║ ║ r w | r w | r w [info] ║")
print("║ ║ ║ ║ ║ r | w | i | d/t/r ║") print("║ ║ ║ ║ ║ r | w | i | d/t/r ║")
for q in range(0, len(blocksmatrix)): for q in range(0, len(blocksmatrix)):
print("╠═════════╬═════╬══════════════════════════════════╬════════╬═════════════════════════════════════╣") print("╠═════════╬═══════╬══════════════════════════════════╬════════╬═════════════════════════════════════╣")
n_blocks = len(blocksmatrix[q])
# z is the block in each sector # z is the block in each sector
for z in range(0, len(blocksmatrix[q])): for z in range(0, len(blocksmatrix[q])):
# Format the access bits. Print ERR in case of an error # Format the access bits. Print ERR in case of an error
accbits = ""
if isinstance(blockrights[q][z], BitArray): if isinstance(blockrights[q][z], BitArray):
accbits = bashcolors.GREEN + blockrights[q][z].bin + bashcolors.ENDC accbits = bashcolors.GREEN + blockrights[q][z].bin + bashcolors.ENDC
else: else:
accbits = bashcolors.WARNING + "ERR" + bashcolors.ENDC accbits = bashcolors.WARNING + "ERR" + bashcolors.ENDC
if (q == 0 and z == 0): if q == 0 and z == 0:
permissions = "-" permissions = "-"
elif (z == 3):
elif z == n_blocks:
permissions = accbits_to_permission_sector(blockrights[q][z]) permissions = accbits_to_permission_sector(blockrights[q][z])
else: else:
permissions = accbits_to_permission_data(blockrights[q][z]) permissions = accbits_to_permission_data(blockrights[q][z])
# Add Padding after the sector number # Print the sector number in the second third row
padLen = max(1, 5 - len(str(q))) if z == 2:
padding = " " * padLen qn = q
# Only print the sector number in the second third row
if (z == 2):
print("%d%s%d%s%s%-35s%s" %(q,padding,z,blocksmatrix[q][z], accbits, permissions, d(blocksmatrix_clear[q][z])))
else: else:
print("║ ║ %d%s%s%-35s%s" %(z,blocksmatrix[q][z], accbits, permissions, d(blocksmatrix_clear[q][z]))) qn = ""
print("╚═════════╩═════╩══════════════════════════════════╩════════╩═════════════════════════════════════╝")
print("%-5s%-3d%s%s%-35s%s" % (qn, block_number, blocksmatrix[q][z],
accbits, permissions,
decode(blocksmatrix_clear[q][z])))
block_number += 1
print("╚═════════╩═══════╩══════════════════════════════════╩════════╩═════════════════════════════════════╝")
def main(args): def main(args):
@ -220,5 +258,6 @@ def main(args):
data = f.read() data = f.read()
print_info(data) print_info(data)
if __name__ == "__main__": if __name__ == "__main__":
main(sys.argv[1:]) main(sys.argv[1:])