text
[RRG-proxmark3.git] / client / luascripts / hf_mf_sim_hid.lua
blob3f393387296137cb277220d852f63cb48f27d3f4
1 --
2 -- hf_mf_sim_hid.lua - A tool to clone a large number of tags at once.
3 -- Adapted from lf_hid_bulkclone.lua
4 -- Created 16.08.2022
5 local getopt = require('getopt')
6 local ansicolors = require('ansicolors')
8 copyright = ''
9 author = "Michael Micsen"
10 version = 'v0.0.2'
11 desc = [[
12 Perform simulation of Mifare credentials with HID encoding
13 This script only supports: H10301
15 example = [[
17 script run hf_mf_sim_hid.lua -f 1 -c 10000
19 usage = [[
20 script run hf_mf_sim_hid.lua -f <dec> -c <dec>
22 arguments = [[
23 -h : this help
24 -f : facility code
25 -c : card number
27 local DEBUG = true
28 --local bxor = bit32.bxor
29 local bor = bit32.bor
30 local lshift = bit32.lshift
31 ---
32 -- A debug printout-function
33 local function dbg(args)
34 if not DEBUG then return end
35 if type(args) == 'table' then
36 local i = 1
37 while args[i] do
38 dbg(args[i])
39 i = i+1
40 end
41 else
42 print('###', args)
43 end
44 end
45 ---
46 -- This is only meant to be used when errors occur
47 local function oops(err)
48 print('ERROR:', err)
49 core.clearCommandBuffer()
50 return nil, errr
51 end
52 ---
53 -- Usage help
54 local function help()
55 print(copyright)
56 print(author)
57 print(version)
58 print(desc)
59 print(ansicolors.cyan..'Usage'..ansicolors.reset)
60 print(usage)
61 print(ansicolors.cyan..'Arguments'..ansicolors.reset)
62 print(arguments)
63 print(ansicolors.cyan..'Example usage'..ansicolors.reset)
64 print(example)
65 end
66 ---
67 -- Exit message
68 local function exitMsg(msg)
69 print( string.rep('--',20) )
70 print( string.rep('--',20) )
71 print(msg)
72 print()
73 end
74 --[[Implement a function to simply visualize the bitstream in a text format
75 --This is especially helpful for troubleshooting bitwise math issues]]--
76 local function toBits(num,bits)
77 -- returns a table of bits, most significant first.
78 bits = bits or math.max(1, select(2, math.frexp(num)))
79 local t = {} -- will contain the bits
80 for b = bits, 1, -1 do
81 t[b] = math.fmod(num, 2)
82 num = math.floor((num - t[b]) / 2)
83 end
84 return table.concat(t)
85 end
87 --[[
88 Likely, I'm an idiot, but I couldn't find any parity functions in Lua
89 This can also be done with a combination of bitwise operations (in fact,
90 is the canonically "correct" way to do it, but my brain doesn't just
91 default to this and so counting some ones is good enough for me
92 ]]--
93 local function evenparity(s)
94 local _, count = string.gsub(s, '1', '')
95 local p = count % 2
96 if (p == 0) then
97 return false
98 else
99 return true
103 local function isempty(s)
104 return s == nil or s == ''
107 --[[
108 The Proxmark3 "clone" functions expect the data to be in hex format so
109 take the card id number and facility ID as arguments and construct the
110 hex. This should be easy enough to extend to non 26bit formats
111 ]]--
112 local function cardHex(i, f)
114 fac = lshift(f, 16)
115 id = bor(i, fac)
116 stream = toBits(id, 24)
118 --As the function defaults to even parity and returns a boolean,
119 --perform a 'not' function to get odd parity
120 high = evenparity(string.sub(stream,1,12)) and 1 or 0
121 low = not evenparity(string.sub(stream,13)) and 1 or 0
122 bits = bor( lshift(id, 1), low)
123 bits = bor( bits, lshift(high, 25))
125 --Add sentinel bit
126 sentinel = lshift(1, 26)
127 bits = bor(bits, sentinel)
129 return ('%08x'):format(bits)
132 -- main
133 local function main(args)
135 print( string.rep('--',20) )
136 print( string.rep('--',20) )
137 print()
139 if #args == 0 then return help() end
141 --I really wish a better getopt function would be brought in supporting
142 --long arguments, but it seems this library was chosen for BSD style
143 --compatibility
144 for o, a in getopt.getopt(args, 'f:c:h') do
145 if o == 'h' then return help() end
146 if o == 'f' then
147 if isempty(a) then
148 print('Defaulting to facility code 0')
149 facility = 0
150 else
151 facility = a
154 if o == 'c' then
155 if isempty(a) then return oops('You must supply a card number') end
156 cardnum = a
160 --Due to my earlier complaints about how this specific getopt library
161 --works, specifying ':' does not enforce supplying a value, thus we
162 --need to do these checks all over again.
163 if isempty(cardnum) then return oops('You must supply a card number') end
165 --If the facility ID is non specified, ensure we code it as zero
166 if isempty(facility) then
167 print('Defaulting to facility code 0')
168 facility = 0
171 -- Write the MAD to read for a Mifare HID credential
172 core.console('hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000')
173 core.console('hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A')
174 --Write the sector trailer for the credential sector
175 core.console('hf mf esetblk --blk 7 -d 484944204953787788AA204752454154')
176 local cardh = cardHex(cardnum, facility)
178 print('Facility Code... ' .. facility)
179 print('Card number..... ' .. cardnum)
180 print('Hex............. ' .. cardh)
181 print('')
183 core.console( ('hf mf esetblk --blk 5 -d 020000000000000000000000%s'):format(cardh) )
184 core.console('hf mf sim --1k -i')
187 main(args)