mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2024-11-11 01:55:38 +08:00
425 lines
11 KiB
Lua
425 lines
11 KiB
Lua
local utils = require('utils')
|
|
local getopt = require('getopt')
|
|
local cmds = require('commands')
|
|
local read14a = require('read14a')
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Notes
|
|
-------------------------------
|
|
---
|
|
--
|
|
--[[
|
|
---Suggestions of improvement:
|
|
--- Add support another types of dumps: BIN, JSON
|
|
--- Maybe it will be not only as `hf_mf_gen3_writer`, like a universal dump manager.
|
|
--- Hide system messages when you writing a dumps, replace it to some of like [#####----------] 40%
|
|
|
|
-- iceman notes:
|
|
-- doesn't take consideration filepaths for dump files.
|
|
-- doesn't allow A keys for authenticating when writing
|
|
-- doesn't verify that card is magic gen3.
|
|
-- doesn't take several versions of same dump ( -1, -2, -3 ) styles.
|
|
--]]
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Script hat
|
|
-------------------------------
|
|
---
|
|
--
|
|
copyright = 'RRG Team'
|
|
author = 'Winds'
|
|
version = 'v1.0.1'
|
|
desc = [[
|
|
This script gives you an easy way to write your *.eml dumps into normal MIFARE Classic and Magic Gen3 cards.
|
|
|
|
Works with both 4 and 7 bytes NXP MIFARE Classic 1K cards.
|
|
The script also has the possibility to change UID and permanent lock uid on magic Gen3 cards.
|
|
|
|
It supports the following functionality.
|
|
|
|
1. Write it to the same of current card UID.
|
|
2. Write it to magic Gen3 card.
|
|
3. Change uid to match dump on magic Gen3 card.
|
|
4. Permanent lock UID on magic Gen3 card.
|
|
5. Erase all data at the card and set the FF FF FF FF FF FF keys, and Access Conditions to 78778800.
|
|
|
|
Script works in a wizard styled way.
|
|
]]
|
|
example = [[
|
|
1. script run mfc_gen3_writer
|
|
]]
|
|
usage = [[
|
|
Give script to know if you uses an Windows OS
|
|
Select your *.eml dump from list to write to the card.
|
|
Follow the wizard.
|
|
]]
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Global variables
|
|
-------------------------------
|
|
---
|
|
--
|
|
local DEBUG = false -- the debug flag
|
|
local dumpEML -- Find all *.EML files
|
|
local files = {} -- Array for eml files
|
|
local b_keys = {} -- Array for B keys
|
|
local eml = {} -- Array for data in block 32
|
|
local num_dumps = 0 -- num of found eml dump files
|
|
local tab = string.rep('-', 64)
|
|
local empty = string.rep('0', 32) -- Writing blocks
|
|
local default_key = 'FFFFFFFFFFFF' -- Writing blocks
|
|
local default_key_type = '01' --KeyA: 00, KeyB: 01
|
|
local default_key_blk = 'FFFFFFFFFFFF7C378800FFFFFFFFFFFF' -- Writing blocks
|
|
local piswords_uid_lock = 'hf 14a raw -s -c -t 2000 90fd111100'
|
|
local piswords_uid_change = 'hf 14a raw -s -c -t 2000 90f0cccc10'
|
|
local cmd_wrbl_a = 'hf mf wrbl --blk %d -a -k %s -d %s' -- Writing blocks by A key
|
|
local cmd_wrbl_b = 'hf mf wrbl --blk %d -b -k %s -d %s' -- Writing blocks by B key
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- A debug printout-function
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function dbg(args)
|
|
if not DEBUG then return end
|
|
if type(args) == 'table' then
|
|
local i = 1
|
|
while args[i] do
|
|
dbg(args[i])
|
|
i = i+1
|
|
end
|
|
else
|
|
print('###', args)
|
|
end
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- This is only meant to be used when errors occur
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function oops(err)
|
|
print('ERROR:', err)
|
|
core.clearCommandBuffer()
|
|
return nil, err
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Usage help
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function help()
|
|
print(copyright)
|
|
print(author)
|
|
print(version)
|
|
print(desc)
|
|
print('Example usage')
|
|
print(example)
|
|
print(usage)
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- GetUID
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function GetUID()
|
|
return read14a.read(true, true).uid
|
|
end
|
|
--
|
|
local function dropfield()
|
|
read14a.disconnect()
|
|
core.clearCommandBuffer()
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Wait for tag (MFC)
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function wait()
|
|
read14a.waitFor14443a()
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Return key code 00/01 to string
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function KeyAB()
|
|
if default_key_type == '00' then
|
|
return 'KeyA'
|
|
else
|
|
return 'KeyB'
|
|
end
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Check response from Proxmark
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function getblockdata(response)
|
|
if response.Status == 0 then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Check 0xFFFFFFFFFFFF key for tag (MFC)
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function checkkey()
|
|
local status = 0
|
|
for i = 1, #eml do
|
|
cmd = Command:newNG{cmd = cmds.CMD_HF_MIFARE_READBL, data = ('%02x%02x%s'):format((i-1), default_key_type, default_key)}
|
|
if (getblockdata(cmd:sendNG(false)) == true) then
|
|
status = status + 1
|
|
print(('%s %02s %s %s %s'):format(' ', (i-1), KeyAB(), default_key, 'OK'))
|
|
else
|
|
break
|
|
end
|
|
end
|
|
if status == #eml then
|
|
return true
|
|
end
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Check user input A or B for blank tag (MFC)
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function check_user_key(user_key_type)
|
|
if user_key_type == 'A' then
|
|
return cmd_wrbl_a
|
|
elseif user_key_type == 'B' then
|
|
return cmd_wrbl_b
|
|
end
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Main function
|
|
-------------------------------
|
|
---
|
|
--
|
|
local function main(args)
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Arguments for script
|
|
-------------------------------
|
|
---
|
|
--
|
|
for o, a in getopt.getopt(args, 'hd') do
|
|
if o == 'h' then return help() end
|
|
if o == 'd' then DEBUG = true end
|
|
end
|
|
--
|
|
wait()
|
|
print(tab)
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Detect 7/4 byte card
|
|
-------------------------------
|
|
---
|
|
--
|
|
if (utils.confirm(' Are you use a Windwos OS ?') == true) then
|
|
dumpEML = 'find "." "*dump.eml"'
|
|
if string.len(GetUID()) == 14 then
|
|
eml_file_uid_start = 18
|
|
eml_file_uid_end = 31
|
|
eml_file_lengt = 40
|
|
else
|
|
eml_file_uid_start = 18
|
|
eml_file_uid_end = 25
|
|
eml_file_lengt = 34
|
|
end
|
|
else
|
|
dumpEML = "find '.' -iname '*dump.eml' -type f"
|
|
if string.len(GetUID()) == 14 then
|
|
eml_file_uid_start = 9
|
|
eml_file_uid_end = 22
|
|
eml_file_lengt = 31
|
|
else
|
|
eml_file_uid_start = 9
|
|
eml_file_uid_end = 16
|
|
eml_file_lengt = 25
|
|
end
|
|
end
|
|
print(tab)
|
|
dropfield()
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- List all EML files in /client
|
|
-------------------------------
|
|
---
|
|
--
|
|
local p = assert(io.popen(dumpEML))
|
|
for _ in p:lines() do
|
|
-- The length of eml file
|
|
if string.len(_) == eml_file_lengt then
|
|
num_dumps = num_dumps + 1
|
|
-- cut UID from eml file
|
|
files[num_dumps] = string.sub(_, eml_file_uid_start, eml_file_uid_end) -- cut numeretic UID
|
|
print(' '..num_dumps..' | '..files[num_dumps])
|
|
end
|
|
end
|
|
--
|
|
p.close()
|
|
--
|
|
if num_dumps == 0 then return oops("Didn't find any dump files") end
|
|
--
|
|
print(tab)
|
|
print(' Your card has UID '..GetUID())
|
|
print('')
|
|
print(' Select which dump to write (1 until '..num_dumps..')')
|
|
print(tab)
|
|
io.write(' --> ')
|
|
--
|
|
local uid_no = tonumber(io.read())
|
|
print(tab)
|
|
print(' You have been selected card dump No ' .. uid_no .. ', with UID: ' .. files[uid_no] .. '. Your card UID: ' .. GetUID())
|
|
--
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Load eml file
|
|
-------------------------------
|
|
---
|
|
--
|
|
local dumpfile = assert(io.open('hf-mf-' .. files[uid_no] .. '-dump.eml', 'r'))
|
|
for _ in dumpfile:lines() do table.insert(eml, _); end
|
|
dumpfile.close()
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Extract B key from EML file
|
|
-------------------------------
|
|
---
|
|
--
|
|
local b = 0
|
|
for i = 1, #eml do
|
|
if (i % 4 == 0) then
|
|
repeat
|
|
b = b + 1
|
|
-- Cut key from block
|
|
b_keys[b] = string.sub(eml[i], (#eml[i] - 11), #eml[i])
|
|
until b % 4 == 0
|
|
end
|
|
end
|
|
print(tab)
|
|
dbg(b_keys)
|
|
dbg(eml)
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Change UID on certain version of magic Gen3 card.
|
|
-------------------------------
|
|
---
|
|
--
|
|
if (utils.confirm(' Change UID ?') == true) then
|
|
wait()
|
|
core.console(piswords_uid_change .. tostring(eml[1]))
|
|
print(tab)
|
|
print(' The new card UID : ' .. GetUID())
|
|
end
|
|
print(tab)
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Lock UID
|
|
-------------------------------
|
|
---
|
|
--
|
|
if (utils.confirm(' Permanent lock UID ? (card can never change uid again) ') == true) then
|
|
wait()
|
|
core.console(piswords_uid_lock)
|
|
end
|
|
--
|
|
print(tab)
|
|
print(' Going to check the all ' .. KeyAB() .. ' by ' .. default_key)
|
|
print(tab)
|
|
--
|
|
if checkkey() == true then
|
|
print(tab)
|
|
if (utils.confirm(' Card is Empty. Write selected dump to card ?') == true) then
|
|
for i = 1, #eml do
|
|
core.console(string.format(cmd_wrbl_b, (i-1), default_key, eml[i]))
|
|
end
|
|
end
|
|
else
|
|
print(tab)
|
|
if (utils.confirm(' It this is a new blank card ? Do you wishing to change Access Conditions to using B key ' .. default_key .. ' as main ?') == true) then
|
|
print(tab)
|
|
print(' With one key type we will use, A or B ?')
|
|
print(tab)
|
|
io.write(' --> ')
|
|
local user_key_type = tostring(io.read())
|
|
print(tab)
|
|
print(' Enter 12 HEX chars of the key for access to card. By default ' .. default_key .. '.')
|
|
print(tab)
|
|
io.write(' --> ')
|
|
local user_key_input = tostring(io.read())
|
|
wait()
|
|
for i = 1, #eml do
|
|
if (i % 4 == 0) then
|
|
core.console(string.format(check_user_key(user_key_type), (i-1), user_key_input, default_key_blk))
|
|
else
|
|
core.console(string.format(check_user_key(user_key_type), (i-1), user_key_input, empty))
|
|
end
|
|
end
|
|
else
|
|
print(tab)
|
|
if (utils.confirm(' Write selected dump to card ?') == true) then
|
|
print(tab)
|
|
wait()
|
|
for i = 1, #eml do
|
|
core.console(string.format(cmd_wrbl_b, (i-1), b_keys[i], eml[i]))
|
|
end
|
|
else
|
|
print(tab)
|
|
if (utils.confirm(' Delete ALL data and write all keys to 0x' .. default_key .. ' ?') == true) then
|
|
wait()
|
|
for i = 1, #eml do
|
|
if (i % 4 == 0) then
|
|
core.console(string.format(cmd_wrbl_b, (i-1), b_keys[i], default_key_blk))
|
|
else
|
|
core.console(string.format(cmd_wrbl_b, (i-1), b_keys[i], empty))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
dropfield()
|
|
print(tab)
|
|
print('You are welcome')
|
|
end
|
|
--
|
|
---
|
|
-------------------------------
|
|
-- Start Main function
|
|
-------------------------------
|
|
---
|
|
--
|
|
main(args)
|