2 -- lf_hid_bulkclone.lua - A tool to clone a large number of tags at once.
4 -- Updated 2018-02-20 iceman
5 local getopt
= require('getopt')
6 local ansicolors
= require('ansicolors')
9 author
= "Brian Redbeard"
12 Perform bulk enrollment of 26 bit H10301 style RFID Tags
13 For more info, check the comments in the code
17 script run lf_hid_bulkclone.lua -f 1 -b 1000 -c 10
20 script run lf_hid_bulkclone.lua -f facility -b base_id_num -c count
26 -c : count, number of cards to make
29 --local bxor = bit32.bxor
31 local lshift
= bit32
.lshift
33 -- A debug printout-function
34 local function dbg(args
)
35 if not DEBUG
then return end
36 if type(args
) == 'table' then
47 -- This is only meant to be used when errors occur
48 local function oops(err
)
50 core
.clearCommandBuffer()
60 print(ansicolors
.cyan
..'Usage'..ansicolors
.reset
)
62 print(ansicolors
.cyan
..'Arguments'..ansicolors
.reset
)
64 print(ansicolors
.cyan
..'Example usage'..ansicolors
.reset
)
69 local function exitMsg(msg
)
70 print( string.rep('--',20) )
71 print( string.rep('--',20) )
75 --[[Implement a function to simply visualize the bitstream in a text format
76 --This is especially helpful for troubleshooting bitwise math issues]]--
77 local function toBits(num
,bits
)
78 -- returns a table of bits, most significant first.
79 bits
= bits
or math
.max(1, select(2, math
.frexp(num
)))
80 local t
= {} -- will contain the bits
81 for b
= bits
, 1, -1 do
82 t
[b
] = math
.fmod(num
, 2)
83 num
= math
.floor((num
- t
[b
]) / 2)
85 return table.concat(t
)
89 Likely, I'm an idiot, but I couldn't find any parity functions in Lua
90 This can also be done with a combination of bitwise operations (in fact,
91 is the canonically "correct" way to do it, but my brain doesn't just
92 default to this and so counting some ones is good enough for me
94 local function evenparity(s
)
95 local _
, count
= string.gsub(s
, '1', '')
104 local function isempty(s
)
105 return s
== nil or s
== ''
109 The Proxmark3 "clone" functions expect the data to be in hex format so
110 take the card id number and facility ID as arguments and construct the
111 hex. This should be easy enough to extend to non 26bit formats
113 local function cardHex(i
, f
)
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 --Since the lua library bit32 is (obviously) 32 bits and we need to
126 --encode 36 bits to properly do a 26 bit tag with the preamble we need
127 --to create a higher order and lower order component which we will
128 --then assemble in the return. The math above defines the proper
129 --encoding as per HID/Weigand/etc. These bit flips are due to the
130 --format length check on bit 38 (cmdlfhid.c:64) and
131 --bit 31 (cmdlfhid.c:66).
132 preamble
= bor(0, lshift(1, 5))
133 bits
= bor(bits
, lshift(1, 26))
135 return ('%04x%08x'):format(preamble
, bits
)
139 local function main(args
)
141 print( string.rep('--',20) )
142 print( string.rep('--',20) )
145 if #args
== 0 then return help() end
147 --I really wish a better getopt function would be brought in supporting
148 --long arguments, but it seems this library was chosen for BSD style
150 for o
, a
in getopt
.getopt(args
, 'f:b:c:h') do
151 if o
== 'h' then return help() end
154 print('You did not supply a facility code, using 0')
161 if isempty(a
) then return oops('You must supply the flag -b (base id)') end
165 if isempty(a
) then return oops('You must supply the flag -c (count)') end
170 --Due to my earlier complaints about how this specific getopt library
171 --works, specifying ':' does not enforce supplying a value, thus we
172 --need to do these checks all over again.
173 if isempty(baseid
) then return oops('You must supply the flag -b (base id)') end
174 if isempty(count
) then return oops('You must supply the flag -c (count)') end
176 --If the facility ID is non specified, ensure we code it as zero
177 if isempty(facility
) then
178 print('Using 0 for the facility code as -f was not supplied')
182 --The next baseid + count function presents a logic/UX conflict
183 --where users specifying -c 1 (count = 1) would try to program two
184 --tags. This makes it so that -c 0 & -c 1 both code one tag, and all
185 --other values encode the expected amount.
186 if tonumber(count
) > 0 then count
= count
- 1 end
188 endid
= baseid
+ count
190 for cardnum
= baseid
, endid
do
191 local card
= cardHex(cardnum
, facility
)
192 print('Press enter to program card '..cardnum
..':'..facility
..' (hex: '..card
..')')
193 --This would be better with 'press Enter', but we'll take what we can get.
195 core
.console( ('lf hid clone -r %s'):format(card
) )