]> git.zerfleddert.de Git - proxmark3-svn/blob - client/scripts/mfkeys.lua
chg: @piwi's code cleanup and some more.
[proxmark3-svn] / client / scripts / mfkeys.lua
1 --[[
2 This is an example of Lua-scripting within proxmark3. This is a lua-side
3 implementation of hf mf chk
4
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
7 the license.
8
9 Copyright (C) 2013 m h swende <martin at swende.se>
10 --]]
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 local getopt = require('getopt')
18
19 local OR = bit32.bor
20 local LSHIFT = bit32.lshift
21
22 example =[[
23 script run mfkeys
24 ]]
25 author = "Iceman"
26 usage = "script run mfkeys"
27 desc = ("This script implements Mifare check keys. It utilises a large list of default keys (currently %d keys).\
28 If you want to add more, just put them inside /lualibs/mf_default_keys.lua\n"):format(#keys) ..
29 [[
30
31 Arguments:
32 -h : this help
33 ]]
34
35 local TIMEOUT = 10000 -- 10 seconds
36 ---
37 -- Usage help
38 function help()
39 print(desc)
40 print("Example usage")
41 print(example)
42 end
43
44 --[[This may be moved to a separate library at some point]]
45 local utils =
46 {
47 ---
48 -- Asks the user for Yes or No
49 confirm = function(message, ...)
50 local answer
51 message = message .. " [y]/[n] ?"
52 repeat
53 io.write(message)
54 io.flush()
55 answer=io.read()
56 if answer == 'Y' or answer == "y" then
57 return true
58 elseif answer == 'N' or answer == 'n' then
59 return false
60 end
61 until false
62 end,
63 ---
64 -- Asks the user for input
65 input = function (message , default)
66 local answer
67 if default ~= nil then
68 message = message .. " (default: ".. default.. " )"
69 end
70 message = message .." \n > "
71 io.write(message)
72 io.flush()
73 answer=io.read()
74 if answer == '' then answer = default end
75
76 return answer
77 end,
78 }
79
80
81 local function checkCommand(command)
82
83 --print("Sending this command : " .. tostring(command))
84 local usbcommand = command:getBytes()
85 core.SendCommand(usbcommand)
86 local result = core.WaitForResponseTimeout(cmds.CMD_ACK,TIMEOUT)
87 if result then
88 local count,cmd,arg0 = bin.unpack('LL',result)
89 if(arg0==1) then
90 local count,arg1,arg2,data = bin.unpack('LLH511',result,count)
91 key = data:sub(1,12)
92 return key
93 else
94 --print("Key not found...")
95 return nil
96 end
97 else
98 print("Timeout while waiting for response. Increase TIMEOUT in keycheck.lua to wait longer")
99 return nil, "Timeout while waiting for device to respond"
100 end
101 end
102
103 function checkBlock(blockNo, keys, keyType)
104 -- The command data is only 512 bytes, each key is 6 bytes, meaning that we can send max 85 keys in one go.
105 -- If there's more, we need to split it up
106 local start, remaining= 1, #keys
107 local packets = {}
108 while remaining > 0 do
109 local n,data = remaining, nil
110 if remaining > 85 then n = 85 end
111 local data = table.concat(keys,"",start,n)
112 print(("Testing block %d, keytype %d, with %d keys"):format(blockNo, keyType, n))
113 local command = Command:new{cmd = cmds.CMD_MIFARE_CHKKEYS,
114 arg1 = OR(blockNo, LSHIFT(keyType,8) ),
115 arg2 = 0,
116 arg3 = n,
117 data = data}
118 local status = checkCommand(command)
119 if status then return status, blockNo end
120 start = start+n+1
121 remaining = remaining - n
122 end
123 return nil
124 end
125
126 -- A function to display the results
127 -- TODO: iceman 2016, still screws up output when a key is not found.
128 local function displayresults(results)
129 local sector, blockNo, keyA, keyB,_
130
131 print("|---|----------------|---|----------------|---|")
132 print("|sec|key A |res|key B |res|")
133 print("|---|----------------|---|----------------|---|")
134
135 for sector,_ in pairs(results) do
136 blockNo, keyA, keyB = unpack(_)
137 print(("|%03d| %s | 1 | %s | 1 |"):format(sector, keyA, keyB ))
138 end
139 print("|---|----------------|---|----------------|---|")
140
141 end
142 -- A little helper to place an item first in the list
143 local function placeFirst(akey, list)
144 akey = akey:lower()
145 if list[1] == akey then
146 -- Already at pole position
147 return list
148 end
149 local result = {akey}
150 --print(("Putting '%s' first"):format(akey))
151 for i,v in ipairs(list) do
152 if v ~= akey then
153 result[#result+1] = v
154 end
155 end
156 return result
157 end
158 local function dumptofile(results)
159 local sector, blockNo, keyA, keyB,_
160
161 if utils.confirm("Do you wish to save the keys to dumpfile?") then
162 local destination = utils.input("Select a filename to store to", "dumpkeys.bin")
163 local file = io.open(destination, "w")
164 if file == nil then
165 print("Could not write to file ", destination)
166 return
167 end
168
169 local key_a = ""
170 local key_b = ""
171
172 for sector,_ in pairs(results) do
173 blockNo, keyA, keyB = unpack(_)
174 key_a = key_a .. bin.pack("H",keyA);
175 key_b = key_b .. bin.pack("H",keyB);
176 end
177 file:write(key_a)
178 file:write(key_b)
179 file:close()
180 end
181 end
182
183 local function main( args)
184
185 -- Arguments for the script
186 for o, a in getopt.getopt(args, 'h') do
187 if o == "h" then return help() end
188 end
189
190 result, err = reader.read1443a()
191 if not result then
192 print(err)
193 return
194 end
195
196 print(("Found a %s tag"):format(result.name))
197
198 core.clearCommandBuffer()
199 local blockNo
200 local keyType = 0 -- A=0, B=1
201 local numSectors = 16
202
203 if 0x18 == result.sak then --NXP MIFARE Classic 4k | Plus 4k
204 -- IFARE Classic 4K offers 4096 bytes split into forty sectors,
205 -- of which 32 are same size as in the 1K with eight more that are quadruple size sectors.
206 numSectors = 40
207 elseif 0x08 == result.sak then -- NXP MIFARE CLASSIC 1k | Plus 2k
208 -- 1K offers 1024 bytes of data storage, split into 16 sector
209 numSectors = 16
210 elseif 0x09 == result.sak then -- NXP MIFARE Mini 0.3k
211 -- MIFARE Classic mini offers 320 bytes split into five sectors.
212 numSectors = 5
213 elseif 0x10 == result.sak then-- "NXP MIFARE Plus 2k"
214 numSectors = 32
215 else
216 print("I don't know how many sectors there are on this type of card, defaulting to 16")
217 end
218
219 result = {}
220 for sector=1,numSectors,1 do
221
222 --[[
223 The mifare Classic 1k card has 16 sectors of 4 data blocks each.
224 The first 32 sectors of a mifare Classic 4k card consists of 4 data blocks and the remaining
225 8 sectors consist of 16 data blocks.
226 --]]
227 local blockNo = sector * 4 -1
228
229 if sector > 32 then
230 blockNo = 32*4+ (sector-32)*16 -1
231 end
232
233 local keyA = checkBlock(blockNo, keys, 0)
234 if keyA then keys = placeFirst(keyA, keys) end
235 keyA = keyA or ""
236
237 local keyB = checkBlock(blockNo, keys, 1)
238 if keyB then keys = placeFirst(keyB, keys) end
239 keyB = keyB or ""
240
241 result[sector] = {blockNo, keyA, keyB }
242
243 -- Check if user aborted
244 if core.ukbhit() then
245 print("Aborted by user")
246 break
247 end
248 end
249 displayresults(result)
250 dumptofile(result)
251 end
252
253 main( args)
254
Impressum, Datenschutz