1 local cmds
= require('commands')
2 local getopt
= require('getopt')
3 local lib14a
= require('read14a')
4 local utils
= require('utils')
5 local cl
= require('ansicolors')
6 local bxor
= bit32
.bxor
9 copyright
= '\nLicensed under GNU GPL v3.0. Team orangeBlue.'
10 author
= 'Team '..cl
.yellow
..'orange'..cl
.cyan
..'Blue'..cl
.reset
-- Disinformation
13 Script to set UID on USCUID using any means possible.
15 This script is compatible with the ICs listed below:
19 * Other chips, showing up as "Gen 4 GDM"
21 This script does *NOT* claim full compatibility with the ICs listed below:
26 Unfortunately, these are cut down versions.
27 Checks show that they only acknowledge bytes 0-1, 7, 8, and 15 of the configuration.
29 * WARNING: The config commands are inversed. Nothing will work.
33 Set the first 2 bytes of your config to 7AFF and use -t 4.
37 -- Set UID 7 bytes long via 20-23 wakeup
38 1. script run hf_mf_uscuid_prog -t 2 -u 04A72B85489F51
40 -- Set UID 4 bytes long via 40-43 wakeup
41 2. script run hf_mf_uscuid_prog -t 4 -u A72B571
44 3. script run hf_mf_uscuid_prog -S 0
47 script run hf_mf_uscuid_uid_prog [-h] [-u <uid>] [-t] [-3] [-s <signature>] [-w 1] [-R -B <blk>] [-S -E <sec>] [-g -c -b -2 -7 -d -a -n -r <0/1>]
51 -t Magic wakeup type (2 for 0x20, 4 for 0x40)
55 -3 Update UID for F3 Perso
56 -w 1 Wipe tag (take caution!)
58 -B Read backdoor block
59 -E Read backdoor sector
64 Unmarked data will not be edited.
67 To ENABLE an option, pass "1"
68 To DISABLE an option, pass "0"
71 -b Block key B if readable by ACL
72 -c Gen1 command (1 for 20-23; 0 for 40-43)
75 -n Static encrypted nonces
78 -7 CL2 mode (1 for F0 unfused; 0 for off)
82 Welcome, proxmark user!
83 Here's a secret changelog of this script as its' life started.
85 v0.1 - Initial developer release. Super unstable, coding the basic UID programming functions, mainly 7 byte because it was just easier.
86 v0.6 - Basic UID editor release. Flexible programming of UIDs (4<->7 byte conversions and programming)
87 v0.7 - Signature support! If USCUID supports it, so should we.
88 v0.8a - Incomplete release. Try the newly added functions at your own risk!
89 v0.8 - Now with wiping the tag! Why not. Helps.
90 v0.9 - Manual configurator. Very well.
91 v1.0 - Memory access. Just like in the proxmark client.
93 -- [[ Start introducing functions that get called later on ]] --
95 local function oops(err
)
96 print(cl
.red
.."[!]"..cl
.reset
..' ERROR:', err
)
97 core
.clearCommandBuffer()
101 local function help()
106 print(cl
.cyan
..'Usage'..cl
.reset
)
108 print(cl
.cyan
..'Arguments'..cl
.reset
)
110 print(cl
.cyan
..'Example usage'..cl
.reset
)
114 -- Sorry, didn't care to figure out custom bit amounts with the 14a lua lib. So here's this thing
116 local function wupc2()
118 [0] = 'hf 14a raw -akb 7 20',
119 [1] = 'hf 14a raw -k 23',
124 local function wupc()
126 [0] = 'hf 14a raw -akb 7 40',
127 [1] = 'hf 14a raw -k 43',
131 local function makenuid(uid
)
132 core
.console('ana nuid -d '..uid
)
135 local function sendCmds(cmds
)
138 core
.console(cmds
[i
])
139 core
.clearCommandBuffer()
144 local function wakeupmagic(writetype
)
145 if writetype
== "2" then
147 elseif writetype
== "4" then
152 local function calculate_block0(useruid
)
153 local uidbytes
= utils
.ConvertHexToBytes(useruid
)
155 local bcc
= bxor(uidbytes
[i
], uidbytes
[i
+1]);
158 local length
= #useruid
// 2;
161 for i
= 3, length
, 1 do
162 bcc
= bxor(bcc
, uidbytes
[i
])
167 for i
= 1, length
, 1 do
168 block0
= block0
..string.format('%02X', uidbytes
[i
])
171 return block0
..string.format('%02X', bcc
)
174 local function cltwo_block0(uid
)
176 payload
= payload
.. "884400000000000000"
180 local function SectorHeader(sector
)
181 if sector
== nil then return end
183 print("["..cl
.yellow
.."="..cl
.reset
.."] # | sector "..cl
.green
..string.format("%02d", sector
)..cl
.reset
.." / "..cl
.green
..string.format("0x%02X", sector
)..cl
.reset
)
184 print("["..cl
.yellow
.."="..cl
.reset
.."] ----+------------------------------------------------")
187 local function BlockParser(data
, block
)
188 if data
== nil or block
== nil then return end
189 if block
== "0" or block
== 0 then -- for block 0
190 print("["..cl
.yellow
.."="..cl
.reset
.."] "..string.format("%02d", block
).." | "..cl
.red
..string.sub(data
,1,2).." "..string.sub(data
,3,4).." "..string.sub(data
,5,6).." "..string.sub(data
,7,8).." "..string.sub(data
,9,10).." "..string.sub(data
,11,12).." "..string.sub(data
,13,14).." "..string.sub(data
,15,16).." "..string.sub(data
,17,18).." "..string.sub(data
,19,20).." "..string.sub(data
,21,22).." "..string.sub(data
,23,24).." "..string.sub(data
,25,26).." "..string.sub(data
,27,28).." "..string.sub(data
,29,30).." "..string.sub(data
,31,32)..cl
.reset
)
191 elseif (block
+1)%4 == 0 then -- for ST
192 print("["..cl
.yellow
.."="..cl
.reset
.."] "..string.format("%02d", block
).." | "..cl
.yellow
..string.sub(data
,1,2).." "..string.sub(data
,3,4).." "..string.sub(data
,5,6).." "..string.sub(data
,7,8).." "..string.sub(data
,9,10).." "..string.sub(data
,11,12).." "..cl
.magenta
..string.sub(data
,13,14).." "..string.sub(data
,15,16).." "..string.sub(data
,17,18).." "..cl
.reset
..string.sub(data
,19,20).." "..cl
.yellow
..string.sub(data
,21,22).." "..string.sub(data
,23,24).." "..string.sub(data
,25,26).." "..string.sub(data
,27,28).." "..string.sub(data
,29,30).." "..string.sub(data
,31,32)..cl
.reset
)
194 print("["..cl
.yellow
.."="..cl
.reset
.."] "..string.format("%02d", block
).." | "..string.sub(data
,1,2).." "..string.sub(data
,3,4).." "..string.sub(data
,5,6).." "..string.sub(data
,7,8).." "..string.sub(data
,9,10).." "..string.sub(data
,11,12).." "..string.sub(data
,13,14).." "..string.sub(data
,15,16).." "..string.sub(data
,17,18).." "..string.sub(data
,19,20).." "..string.sub(data
,21,22).." "..string.sub(data
,23,24).." "..string.sub(data
,25,26).." "..string.sub(data
,27,28).." "..string.sub(data
,29,30).." "..string.sub(data
,31,32))
198 local function sendRaw(rawdata
, keep
)
199 flags
= lib14a
.ISO14A_COMMAND
.ISO14A_RAW
+ lib14a
.ISO14A_COMMAND
.ISO14A_APPEND_CRC
200 if keep
== true then flags
= flags
+ lib14a
.ISO14A_COMMAND
.ISO14A_NO_DISCONNECT
end
201 local command
= Command
:newMIX
{cmd
= cmds
.CMD_HF_ISO14443A_READER
,
202 arg1
= flags
, -- Send raw
203 arg2
= string.len(rawdata
) / 2, -- arg2 contains the length, which is half the length of the ASCII-string rawdata
206 local ignore_response
= false
207 local result
, err
= command
:sendMIX(ignore_response
)
209 --local count,cmd,arg1,arg2,arg3,data = bin.unpack('LLLLH512',result)
210 local p
= command
.parse(result
)
213 returned_bytes
= string.sub(data
, 1, arg1
* 2)
214 if #returned_bytes
> 0 then return returned_bytes
else return nil end
217 -- Functions to work with configuration data (E000, E100 cmds)
218 local function readconf()
219 configbuffer
= sendRaw("E000", true)
220 if string.len(configbuffer
) ~= 36 then
221 oops("Tag sent wrong length of config!")
225 return utils
.ConvertHexToBytes(string.sub(configbuffer
, 1, 32))
228 local function writeconf(configbuffer
)
229 configbuffer
=utils
.ConvertBytesToHex(configbuffer
)
230 print(cl
.yellow
.."[|]".. cl
.reset
.." The new config is: "..configbuffer
)
231 if sendRaw("E100", true) == "0A" then
232 if sendRaw(configbuffer
, true) == "0A" then
233 print(cl
.yellow
.."[/]".. cl
.reset
.." Config updated successfully")
235 oops("Tag did not ACK config update!")
239 else oops("Tag did not ACK `E100` command!")
244 -- End config functions
246 -- [[ All have been created ]] --
250 if args
== nil or #args
== 0 then return help() end
251 -- Save data to process
252 local writetype
= "4"
255 local f3perso
= false
256 local signature
= nil
259 local targetblk
= nil
260 local targetbblk
= nil
261 local targetbsec
= nil
262 local targetsec
= nil
263 -- ConfigStar data. These are all booleans. If nil, ignored.
268 local cl2mode
= nil -- Sorry, I'll only parse either 5A or 00. You can turn on the rest yourself using perso
269 local shadowmode
= nil
270 local magicauth
= nil
274 local configwrite
= nil
277 -- Note: wrong order of arguments makes the script just not work. Like in some cases the script dies and doesnt call anything, in others it wants data for bool arguments. DESIGN???
278 for o
,a
in getopt
.getopt(args
, 'g:c:b:2:7:d:a:n:r:u:t:s:R:B:S:E:hw3') do
279 if o
== "h" then return help() end
280 if o
== 'u' then uid
= a
end
281 if o
== 's' then signature
= a
end
282 if o
== 't' then writetype
= a
end
283 if o
== '3' then f3perso
= true end
284 if o
== 'R' then targetblk
= a
end
285 if o
== 'B' then targetbblk
= a
end
286 if o
== 'S' then targetsec
= a
end
287 if o
== 'E' then targetbsec
= a
end
288 if o
== 'w' then wipe
= true end
289 -- So one odd thing I noticed is the bool args like -h, -w don't work without a 2nd argument. So you now must do -h 1.. what? Why?
291 if o
== 'g' then if a
== "1" then gen1
= true elseif a
== "0" then gen1
= false end end
292 if o
== 'c' then if a
== "1" then gen1com
= true elseif a
== "0" then gen1com
= false end end
293 if o
== 'b' then if a
== "1" then keyblock
= true elseif a
== "0" then keyblock
= false end end
294 if o
== '2' then if a
== "1" then cuid
= true elseif a
== "0" then cuid
= false end end
295 if o
== '7' then if a
== "1" then cl2mode
= true elseif a
== "0" then cl2mode
= false end end
296 if o
== 'd' then if a
== "1" then shadowmode
= true elseif a
== "0" then shadowmode
= false end end
297 if o
== 'a' then if a
== "1" then magicauth
= true elseif a
== "0" then magicauth
= false end end
298 if o
== 'n' then if a
== "1" then statenc
= true elseif a
== "0" then statenc
= false end end
299 if o
== 'r' then if a
== "1" then sigsec
= true elseif a
== "0" then sigsec
= false end end
302 if gen1
~= nil or gen1com
~= nil or keyblock
~= nil or cuid
~= nil or cl2mode
~= nil or shadowmode
~= nil or magicauth
~= nil or statenc
~= nil or sigsec
~= nil then
306 if targetbblk
then if tonumber(targetbblk
) > 63 then oops("Block is above 63") return 1 end end
307 if targetblk
then if tonumber(targetblk
) > 63 then oops("Block is above 63") return 1 end end
308 if targetsec
then if tonumber(targetsec
) > 15 then oops("Sector is above 15") return 1 end end
309 if targetbsec
then if tonumber(targetbsec
) > 15 then oops("Sector is above 15") return 1 end end
311 -- Alright, here's the logic.
312 -- 1. Set the write type (0x20, 0x40, 8000 auth, etc...)
314 -- 3. Form data to write
318 print(cl
.red
.."[/]"..cl
.reset
.." Wipe issued! Nullifying other arguments!")
319 print(cl
.red
.."[-]"..cl
.reset
.." DO NOT REMOVE YOUR TAG!")
325 wakeupmagic(writetype
)
326 if sendRaw("F000", true) ~= "0A" then
327 oops("DANGER! Tag did not ACK wipe command. The field has NOT been reset.")
328 print("[ ] If you think the wipe succeeded, immediately do this:")
329 print("hf 14a raw -kc E100; hf 14a raw -c 7AFF0000000000000000000000000008")
333 writeconf(utils
.ConvertHexToBytes("7AFF0000000000000000005A00000008"))
335 sendRaw("F800", true) -- here you only wipe the backdoor blocks and they're not super critical so might as well not check.
336 sendRaw("A000", true) -- By this point I just rely on the tag.
337 sendRaw("DE7715B8040804000000000000000000", true)
340 blk
=string.format("%02x", 4 * i
+ 3):gsub("0x","")
341 sendRaw("A0"..blk
, true)
342 sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF", true)
343 sendRaw("A8"..blk
, true)
344 sendRaw("FFFFFFFFFFFFFF078069FFFFFFFFFFFF", true)
347 sendRaw("A807", true)
348 sendRaw("75CCB59C9BED70F0F8694B791BEA7BCC", true)
349 print(cl
.yellow
.."[-]"..cl
.reset
.." Wipe completed successfully")
354 if targetblk
or targetbblk
or targetsec
or targetbsec
then
358 wakeupmagic(writetype
)
361 if targetblk
or targetsec
then
363 data
= sendRaw("30"..string.format("%02x", targetblk
), false)
368 SectorHeader(targetblk
// 4)
370 SectorHeader(targetsec
)
374 BlockParser(data
, targetblk
)
377 BlockParser(sendRaw("30"..string.format("%02x", targetsec
* 4 + i
), true), targetsec
* 4 + i
)
381 elseif targetbblk
or targetbsec
then
383 data
= sendRaw("38"..string.format("%02x", targetbblk
), false)
388 SectorHeader(targetbblk
// 4)
390 SectorHeader(targetbsec
)
394 BlockParser(data
, targetbblk
)
397 BlockParser(sendRaw("38"..string.format("%02x", targetbsec
* 4 + i
), true), targetbsec
* 4 + i
)
400 -- Actually is there an sprintf_hex in lua?
407 if writetype
== "2" or writetype
== "4" then
408 if string.len(uid
) == 8 then
409 payload
= calculate_block0(uid
)
412 payload
= payload
.. "08"
414 payload
= payload
.. "04000000000000000000"
415 elseif string.len(uid
) == 14 then
416 -- Same logic, but with raw anticollision data because that's what the tag accepts. :P
417 payload
= calculate_block0("88"..string.sub(uid
, 1, 6))
418 payload
= payload
.. "04"
419 payload
= payload
.. calculate_block0(string.sub(uid
, 7, 14))
420 payload
= payload
.. "08"
421 payload
= payload
.. "00000000"
425 core
.clearCommandBuffer()
426 -- Now, let's write! 1. We wake up the tag in magic mode.
427 -- 2. We will deal with the "easier" 7 byte UID stuff
428 if string.len(uid
) == 14 then
430 wakeupmagic(writetype
)
431 if f3perso
== true then
432 print("[?] WARNING: F3 perso write is set, but 7 byte UID is passed. Ignoring -3 argument")
435 local configdata
= readconf()
437 if configdata
[10] ~= 0x5A and configdata
[10] ~= 0xC3 and configdata
[10] ~= 0xA5 then -- Enable CL2 mode if necessary
438 print("[?] WARNING: Tag is not in 7 byte UID mode. Automatically updating to F0 unfused")
439 print(cl
.yellow
.."[-]".. cl
.reset
.." This is because the configuration byte responsible for CL2 was not found to be equal to 0x5A, 0xC3 or 0xA5, but rather: ".. string.format("%02x", configdata
[10]))
440 print(cl
.yellow
.."[\\]".. cl
.reset
.." The old config is: ".. utils
.ConvertBytesToHex(configdata
))
441 configdata
[10] = 0x5A
442 writeconf(configdata
)
445 if sendRaw("A800", true) ~= "0A" then
446 oops("Tag did not ACK `A800` command!")
451 print("[?] WARNING: nUID should be updated with this value:")
453 print(cl
.yellow
.."[/]".. cl
.reset
.." Use `--f3d` to update nUID for Perso F3 only.")
455 if sendRaw(payload
, true) ~= "0A" then
456 oops("Tag did not ACK data to write!")
461 print(cl
.yellow
.."[-]".. cl
.reset
.." Updating real block 0")
462 if sendRaw("A000", true) ~= "0A" then
463 oops("Tag did not ACK `A000` command!")
468 if sendRaw(cltwo_block0(uid
), false) ~="0A" then
469 oops("Tag did not ACK data to write!")
472 -- Now, let's work with 4 byte UIDs.
473 elseif string.len(uid
) == 8 then
475 wakeupmagic(writetype
)
476 local configdata
= readconf()
478 if configdata
[10] == 0x69 or f3perso
== true then -- If we have Perso: F3, then write backdoor blk 1
480 if f3perso
== true then
481 print ("[?] WARNING: F3 flag enabled. Updating UID used for F3 perso")
484 if sendRaw("A801", true) ~= "0A" then
485 oops("Tag did not ACK `A801` command!")
490 else -- Otherwise write real block 0.
491 if configdata
[10] == 0x5a or configdata
[10] == 0xc3 or configdata
[10] == 0xa5 then -- Disable CL2 if necessary
492 print("[?] WARNING: Tag is not in 4 byte UID mode. Automatically disabling")
493 print(cl
.yellow
.."[-]".. cl
.reset
.." This is because the configuration byte responsible for CL2 was found to be equal to: ".. string.format("%02x", configdata
[10]))
494 print(cl
.yellow
.."[\\]".. cl
.reset
.." The old config is: ".. utils
.ConvertBytesToHex(configdata
))
495 configdata
[10] = 0x00
496 writeconf(configdata
)
499 if sendRaw("A000", true) ~= "0A" then
500 oops("Tag did not ACK `A000` command!")
506 if sendRaw(payload
, false) ~= "0A" then
507 oops("Tag did not ACK data to write!")
514 wakeupmagic(writetype
)
515 local configdata
= readconf()
516 if configdata
[14] ~= 0x5A then
517 print("[?] WARNING: Signature sector is not enabled. Automatically enabling")
518 configdata
[14] = 0x5A
519 writeconf(configdata
)
522 if sendRaw("A805", true) ~= "0A" then
523 oops("Tag did not ACK `A805` command!")
528 if sendRaw(string.sub(signature
,1,32), true) ~= "0A" then
529 oops("Tag did not ACK data 1 to write!")
534 if sendRaw("A806", true) ~= "0A" then
535 oops("Tag did not ACK `A806` command!")
540 if sendRaw(string.sub(signature
,33,64), false) ~= "0A" then
541 oops("Tag did not ACK data 2 to write!")
549 print(cl
.yellow
.."[|]"..cl
.reset
.." Welcome to ConfigStar!")
550 wakeupmagic(writetype
)
553 if (gen1
== false and magicauth
== false) or ((config
[1]==0x85 and config
[2] == 0x00) and magicauth
==false) or ((config
[12]==0x00) and gen1
== false) then
554 oops("What you are about to do is potentially dangerous. \nIf you really want to continue (potentially leaving your tag in an unusable state), enter this line as given, without quotation marks:\n \"yes\"")
555 local ans
= io
.read()
560 print(cl
.red
.."[/]"..cl
.reset
.." Brace yourself.")
565 -- Prepare for disappointment
569 elseif gen1
== false then
574 if gen1com
== true then
576 elseif gen1com
== false then
580 if keyblock
== true then
582 elseif keyblock
== false then
588 elseif cuid
== false then
592 if cl2mode
== true then
594 elseif cl2mode
== false then
598 if shadowmode
== true then
600 elseif shadowmode
== false then
604 if magicauth
== true then
606 elseif magicauth
== false then
610 if statenc
== true then
612 elseif statenc
== false then
616 if sigsec
== true then
618 elseif sigsec
== false then
623 print(cl
.yellow
.."[\\]"..cl
.reset
.." Completed!")