mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-19 21:03:48 -07:00
UPD: pm3_mfdread.py - updates from original repo (@zhovner) 96d0ac184a
This commit is contained in:
parent
d15cf5ff2f
commit
d2dd6eed6a
1 changed files with 96 additions and 57 deletions
|
@ -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("║ Sector ║Block║ Data ║ Access ║ A | Acc. | B ║")
|
print("╔═════════╦═══════╦══════════════════════════════════╦════════╦═════════════════════════════════════╗")
|
||||||
print("║ ║ ║ ║ ║ r w | r w | r w [info] ║")
|
print("║ Sector ║ Block ║ Data ║ Access ║ A | Acc. | B ║")
|
||||||
print("║ ║ ║ ║ ║ r | w | i | d/t/r ║")
|
print("║ ║ ║ ║ ║ r w | r w | r w [info] ║")
|
||||||
|
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):
|
||||||
|
@ -219,6 +257,7 @@ def main(args):
|
||||||
with open(filename, "rb") as f:
|
with open(filename, "rb") as f:
|
||||||
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:])
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue