Commit | Line | Data |
---|---|---|
79b19c5f BRH |
1 | -- |
2 | -- lf_bulk_program.lua - A tool to clone a large number of tags at once. | |
3 | -- Updated 2017-04-18 | |
4 | -- | |
5 | -- The getopt-functionality is loaded from pm3/client/lualibs/getopt.lua | |
6 | -- Have a look there for further details | |
7 | getopt = require('getopt') | |
8 | bit32 = require('bit32') | |
9 | ||
10 | usage = [[ script run lf_bulk_program.lua -f facility -b base_id_num -c count | |
11 | ||
e069547c | 12 | e.g: |
79b19c5f BRH |
13 | script run lf_bulk_program.lua -f 1 -b 1000 -c 10 |
14 | ]] | |
15 | author = "Brian Redbeard" | |
16 | desc =[[ | |
17 | Perform bulk enrollment of 26 bit H10301 style RFID Tags | |
18 | For more info, check the comments in the code | |
19 | ]] | |
20 | ||
21 | --[[Implement a function to simply visualize the bitstream in a text format | |
22 | --This is especially helpful for troubleshooting bitwise math issues]]-- | |
23 | function toBits(num,bits) | |
24 | -- returns a table of bits, most significant first. | |
25 | bits = bits or math.max(1, select(2, math.frexp(num))) | |
26 | local t = {} -- will contain the bits | |
27 | for b = bits, 1, -1 do | |
28 | t[b] = math.fmod(num, 2) | |
29 | num = math.floor((num - t[b]) / 2) | |
30 | end | |
31 | return table.concat(t) | |
32 | end | |
33 | ||
34 | --[[Likely, I'm an idiot, but I couldn't find any parity functions in Lua | |
e069547c | 35 | This can also be done with a combination of bitwise operations (in fact, |
79b19c5f BRH |
36 | is the canonically "correct" way to do it, but my brain doesn't just |
37 | default to this and so counting some ones is good enough for me]]-- | |
38 | local function evenparity(s) | |
39 | local _, count = string.gsub(s, "1", "") | |
e069547c | 40 | |
79b19c5f BRH |
41 | local p = count % 2 |
42 | if (p == 0) then | |
43 | return(false) | |
44 | else | |
45 | return(true) | |
46 | end | |
47 | end | |
e069547c | 48 | |
79b19c5f BRH |
49 | |
50 | local function isempty(s) | |
51 | return s == nil or s == '' | |
52 | end | |
53 | ||
54 | --[[The Proxmark3 "clone" functions expect the data to be in hex format so | |
55 | take the card id number and facility ID as arguments and construct the | |
56 | hex. This should be easy enough to extend to non 26bit formats]]-- | |
57 | local function cardHex(i,f) | |
58 | fac = bit32.lshift(f,16) | |
59 | id = bit32.bor(i, fac) | |
e069547c | 60 | stream=toBits(id,24) |
79b19c5f BRH |
61 | |
62 | --As the function defaults to even parity and returns a boolean, | |
63 | --perform a 'not' function to get odd parity | |
e069547c TH |
64 | high = evenparity(string.sub(stream,1,12)) and 1 or 0 |
65 | low = not evenparity(string.sub(stream,13)) and 1 or 0 | |
66 | ||
79b19c5f BRH |
67 | bits = bit32.bor(bit32.lshift(id,1), low) |
68 | bits = bit32.bor(bits, bit32.lshift(high,25)) | |
69 | ||
70 | --Since the lua library bit32 is (obviously) 32 bits and we need to | |
71 | --encode 36 bits to properly do a 26 bit tag with the preamble we need | |
72 | --to create a higher order and lower order component which we will | |
73 | --then assemble in the return. The math above defines the proper | |
74 | --encoding as per HID/Weigand/etc. These bit flips are due to the | |
e069547c | 75 | --format length check on bit 38 (cmdlfhid.c:64) and |
79b19c5f BRH |
76 | --bit 31 (cmdlfhid.c:66). |
77 | preamble = bit32.bor(0, bit32.lshift(1,5)) | |
78 | bits = bit32.bor(bits, bit32.lshift(1,26)) | |
79 | ||
80 | return ("%04x%08x"):format(preamble,bits) | |
e069547c | 81 | |
79b19c5f BRH |
82 | end |
83 | ||
84 | local function main(args) | |
85 | ||
86 | --I really wish a better getopt function would be brought in supporting | |
87 | --long arguments, but it seems this library was chosen for BSD style | |
88 | --compatibility | |
89 | for o, a in getopt.getopt(args, 'f:b:c:h') do | |
e069547c TH |
90 | if o == 'f' then |
91 | if isempty(a) then | |
79b19c5f BRH |
92 | print("You did not supply a facility code, using 0") |
93 | facility = 0 | |
e069547c | 94 | else |
79b19c5f BRH |
95 | facility = a |
96 | end | |
e069547c TH |
97 | elseif o == 'b' then |
98 | if isempty(a) then | |
79b19c5f BRH |
99 | print("You must supply the flag -b (base id)") |
100 | return | |
101 | else | |
102 | baseid = a | |
103 | end | |
e069547c TH |
104 | elseif o == 'c' then |
105 | if isempty(a) then | |
79b19c5f BRH |
106 | print("You must supply the flag -c (count)") |
107 | return | |
108 | else | |
109 | count = a | |
110 | end | |
111 | elseif o == 'h' then | |
112 | print(desc) | |
113 | print(usage) | |
114 | return | |
115 | end | |
116 | end | |
117 | ||
118 | --Due to my earlier complaints about how this specific getopt library | |
119 | --works, specifying ":" does not enforce supplying a value, thus we | |
120 | --need to do these checks all over again. | |
121 | ||
e069547c | 122 | if isempty(baseid) then |
79b19c5f BRH |
123 | print("You must supply the flag -b (base id)") |
124 | print(usage) | |
125 | return | |
126 | end | |
127 | ||
e069547c | 128 | if isempty(count) then |
79b19c5f BRH |
129 | print("You must supply the flag -c (count)") |
130 | print(usage) | |
131 | return | |
132 | end | |
133 | ||
134 | --If the facility ID is non specified, ensure we code it as zero | |
e069547c | 135 | if isempty(facility) then |
79b19c5f | 136 | print("Using 0 for the facility code as -f was not supplied") |
e069547c | 137 | facility = 0 |
79b19c5f BRH |
138 | end |
139 | ||
140 | --The next baseid + count function presents a logic/UX conflict | |
141 | --where users specifying -c 1 (count = 1) would try to program two | |
142 | --tags. This makes it so that -c 0 & -c 1 both code one tag, and all | |
143 | --other values encode the expected amount. | |
144 | if tonumber(count) > 0 then count = count -1 end | |
145 | ||
146 | endid = baseid + count | |
147 | ||
e069547c | 148 | for cardnum = baseid,endid do |
79b19c5f BRH |
149 | local card = cardHex(cardnum, facility) |
150 | print("Press enter to program card "..cardnum..":"..facility.." (hex: "..card..")") | |
151 | --This would be better with "press any key", but we'll take | |
152 | --what we can get. | |
153 | io.read() | |
154 | core.console( ('lf hid clone %s'):format(card) ) | |
155 | end | |
156 | end | |
157 | ||
158 | ||
159 | main(args) |