2         This is an example of Lua-scripting within proxmark3. This is a lua-side
 
   3         implementation of hf mf chk
 
   5         This code is licensed to you under the terms of the GNU GPL, version 2 or,
 
   6         at your option, any later version. See the LICENSE.txt file for the text of
 
   9         Copyright (C) 2013 m h swende <martin at swende.se>
 
  11 -- Loads the commands-library
 
  12 local cmds = require('commands')
 
  13 -- Load the default keys
 
  14 local keys = require('mf_default_keys')
 
  15 -- Ability to read what card is there
 
  16 local reader = require('read14a')
 
  17 -- Asks the user for input
 
  18 local utils = require('utils')
 
  21 local desc = ("This script implements check keys. \
 
  22 It utilises a large list of default keys (currently %d keys).\
 
  23 If you want to add more, just put them inside mf_default_keys.lua. "):format(#keys)
 
  25 local TIMEOUT = 10000 -- 10 seconds
 
  28 local function checkCommand(command)
 
  30         --print("Sending this command : " .. tostring(command))
 
  31         local usbcommand = command:getBytes()
 
  32         core.SendCommand(usbcommand)
 
  33         local result = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT)
 
  35                 local count,cmd,arg0 = bin.unpack('LL',result)
 
  37                         local count,arg1,arg2,data = bin.unpack('LLH511',result,count)
 
  41                         --print("Key not found...")
 
  45                 print("Timeout while waiting for response. Increase TIMEOUT in keycheck.lua to wait longer")
 
  46                 return nil, "Timeout while waiting for device to respond"
 
  51 function checkBlock(blockNo, keys, keyType)
 
  52         -- The command data is only 512 bytes, each key is 6 bytes, meaning that we can send max 85 keys in one go.
 
  53         -- If there's more, we need to split it up
 
  54         local start, remaining= 1, #keys
 
  55         local arg1 = bit32.bor(bit32.lshift(keyType, 8), blockNo)
 
  57         while remaining > 0 do
 
  58                 local n,data = remaining, nil
 
  59                 if remaining > 85 then n = 85 end
 
  60                 local data = table.concat(keys,"",start,n)
 
  62                 --print("data len", #data)
 
  63                 print(("Testing block %d, keytype %d, with %d keys"):format(blockNo, keyType, n))
 
  64                 local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS,
 
  69                 local status = checkCommand(command)
 
  70                 if status then return status, blockNo end
 
  72                 remaining = remaining - n
 
  77 -- A function to display the results
 
  78 local function displayresults(results)
 
  79         local sector, blockNo, keyA, keyB,_
 
  81         print("________________________________________")
 
  82         print("|Sector|Block|     A      |      B     |")
 
  83         print("|--------------------------------------|")
 
  85         for sector,_ in pairs(results) do
 
  86                 blockNo, keyA, keyB = unpack(_)
 
  88                 print(("| %3d  | %3d |%12s|%12s|"):format(sector, blockNo, keyA, keyB))
 
  90         print("|--------------------------------------|")
 
  94 -- A little helper to place an item first in the list
 
  95 local function placeFirst(akey, list)
 
  97         if list[1] == akey then
 
  98                 -- Already at pole position
 
 101         local result = {akey}
 
 102         --print(("Putting '%s' first"):format(akey))
 
 103         for i,v in ipairs(list) do
 
 105                         result[#result+1] = v
 
 111 local function dumptofile(results)
 
 112         local sector, blockNo, keyA, keyB,_
 
 114         if utils.confirm("Do you wish to save the keys to dumpfile?") then
 
 115                 local destination = utils.input("Select a filename to store to", "dumpkeys.bin")
 
 116                 local file = io.open(destination, "w")
 
 118                         print("Could not write to file ", destination)
 
 125                 for sector,_ in pairs(results) do
 
 126                         blockNo, keyA, keyB = unpack(_)
 
 127                         key_a = key_a .. bin.pack("H",keyA);
 
 128                         key_b = key_b .. bin.pack("H",keyB);
 
 137 local function main(args)
 
 141         result, err = reader.read14443a(false, true)
 
 146         print(("Found a %s tag"):format(result.name))
 
 149         core.clearCommandBuffer()
 
 151         local keyType = 0 -- A=0, B=1
 
 152         local numSectors = 16
 
 154         if 0x18 == result.sak then -- NXP MIFARE Classic 4k | Plus 4k
 
 155                 -- IFARE Classic 4K offers 4096 bytes split into forty sectors,
 
 156                 -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors.
 
 158         elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k
 
 159                 -- 1K offers 1024 bytes of data storage, split into 16 sector
 
 161         elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k
 
 162                 -- MIFARE Classic mini offers 320 bytes split into five sectors.
 
 164         elseif 0x10 == result.sak then -- NXP MIFARE Plus 2k
 
 167                 print("I don't know how many sectors there are on this type of card, defaulting to 16")
 
 171         for sector=1,numSectors,1 do
 
 174                 The mifare Classic 1k card has 16 sectors of 4 data blocks each.
 
 175                 The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining
 
 176                 8 sectors consist of 16 data blocks.
 
 178                 local blockNo = sector * 4 - 1
 
 181                         blockNo = 32*4 + (sector-32)*16 - 1
 
 184                 local keyA = checkBlock(blockNo, keys, 0)
 
 185                 if keyA then keys = placeFirst(keyA, keys) end
 
 188                 local keyB = checkBlock(blockNo, keys, 1)
 
 189                 if keyB then keys = placeFirst(keyB, keys) end
 
 192                 result[sector] = {blockNo, keyA, keyB}
 
 194                 -- Check if user aborted
 
 195                 if core.ukbhit() then
 
 196                         print("Aborted by user")
 
 200         displayresults(result)