diff --git a/client/pm3_mfdread.py b/client/pm3_mfdread.py new file mode 100644 index 000000000..9958adf89 --- /dev/null +++ b/client/pm3_mfdread.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# mfdread.py - Mifare dumps parser in human readable format +# Pavel Zhovner +# https://github.com/zhovner/mfdread + + + +import codecs +import sys +from struct import unpack +from datetime import datetime +from bitstring import BitArray + + +if len(sys.argv) == 1: + print(''' +------------------ +Usage: mfdread.py ./dump.mfd +Mifare dumps reader. + +''') + sys.exit(); + + + + +class bashcolors: + BLUE = '\033[34m' + RED = '\033[91m' + GREEN = '\033[32m' + WARNING = '\033[93m' + ENDC = '\033[0m' + + +def accbits_for_blocknum(accbits_str, blocknum): + ''' + Decodes the access bit string for block "blocknum". + Returns the three access bits for the block or False if the + inverted bits do not match the access bits. + ''' + bits = BitArray([0]) + inverted = BitArray([0]) + # Block 0 access bits + if blocknum == 0: + bits = BitArray([accbits_str[11], accbits_str[23], accbits_str[19]]) + inverted = BitArray([accbits_str[7], accbits_str[3], accbits_str[15]]) + + # Block 0 access bits + elif blocknum == 1: + bits = BitArray([accbits_str[10], accbits_str[22], accbits_str[18]]) + inverted = BitArray([accbits_str[6], accbits_str[2], accbits_str[14]]) + # Block 0 access bits + elif blocknum == 2: + bits = BitArray([accbits_str[9], accbits_str[21], accbits_str[17]]) + inverted = BitArray([accbits_str[5], accbits_str[1], accbits_str[13]]) + # Sector trailer / Block 3 access bits + elif blocknum == 3: + bits = BitArray([accbits_str[8], accbits_str[20], accbits_str[16]]) + inverted = BitArray([accbits_str[4], accbits_str[0], accbits_str[12]]) + + # Check the decoded bits + inverted.invert() + if bits.bin == inverted.bin: + return bits + else: + return False + + + + +def accbit_info(accbits): + ''' + 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. + ''' + decAccbits = {} + # Decode access bits for all 4 blocks of the sector + for i in range(0, 4): + decAccbits[i] = accbits_for_blocknum(accbits, i) + return decAccbits + + + + + +def print_info(data): + + blocksmatrix = [] + blockrights = {} + + # determine what dump we get 1k or 4k + if len(data) == 4096: + cardsize = 64 + elif len(data) == 1024: + cardsize = 16 + else: + print("Wrong file size: %d bytes.\nOnly 1024 or 4096 allowed." % len(data)) + sys.exit(); + + # read all sectors + for i in range(0, cardsize): + start = i * 64 + end = (i + 1) * 64 + sector = data[start:end] + sector = codecs.encode(sector, 'hex') + if not type(sector) is str: + sector = str(sector, 'ascii') + blocksmatrix.append([sector[x:x+32] for x in range(0, len(sector), 32)]) + + # add colors for each keyA, access bits, KeyB + for c in range(0, len(blocksmatrix)): + # Fill in the access bits + blockrights[c] = accbit_info(BitArray('0x'+blocksmatrix[c][3][12:20])) + # Prepare colored output of the sector trailor + keyA = bashcolors.RED + blocksmatrix[c][3][0:12] + bashcolors.ENDC + accbits = bashcolors.GREEN + blocksmatrix[c][3][12:20] + bashcolors.ENDC + keyB = bashcolors.BLUE + blocksmatrix[c][3][20:32] + bashcolors.ENDC + blocksmatrix[c][3] = keyA + accbits + keyB + + + print("File size: %d bytes. Expected %d sectors" %(len(data),cardsize)) + print("\n\tUID: " + blocksmatrix[0][0][0:8]) + print("\tBCC: " + blocksmatrix[0][0][8:10]) + print("\tSAK: " + blocksmatrix[0][0][10:12]) + 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("╔═════════╦═════╦══════════════════════════════════╦═══════════════╗") + print("║ Sector ║Block║ Data ║ Access Bits ║") + for q in range(0, len(blocksmatrix)): + print("╠═════════╬═════╬══════════════════════════════════╬═══════════════╣") + + # z is the block in each sector + for z in range(0, len(blocksmatrix[q])): + # Format the access bits. Print ERR in case of an error + accbits = "" + if isinstance(blockrights[q][z], BitArray): + accbits = bashcolors.GREEN + blockrights[q][z].bin + bashcolors.ENDC + else: + accbits = bashcolors.WARNING + "ERR" + bashcolors.ENDC + + # Add Padding after the sector number + padLen = max(1, 5 - len(str(q))) + padding = " " * padLen + # Only print the sector number in the second third row + if (z == 2): + print("║ %d%s║ %d ║ %s ║ %s ║" %(q,padding,z,blocksmatrix[q][z], accbits)) + else: + print("║ ║ %d ║ %s ║ %s ║" %(z,blocksmatrix[q][z], accbits)) + print("╚═════════╩═════╩══════════════════════════════════╩═══════════════╝") + + +def main(filename): + with open(filename, "rb") as f: + data = f.read() + print_info(data) + +if __name__ == "__main__": + main(sys.argv[1])