2 if it don't works with you tag-layout - be so kind and let me know ;-)
4 Tested on Tags with those Layouts:
6 (example) Legic-Prime Layout with 'Kaba Group Header'
7 +----+----+----+----+----+----+----+----+
8 0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f |
9 +----+----+----+----+----+----+----+----+
10 0x08| ff | 00 | 00 | 00 | 11 |Bck0|Bck1|Bck2|
11 +----+----+----+----+----+----+----+----+
12 0x10|Bck3|Bck4|Bck5|BCC | 00 | 00 |Seg0|Seg1|
13 +----+----+----+----+----+----+----+----+
14 0x18|Seg2|Seg3|SegC|Stp0|Stp1|Stp2|Stp3|UID0|
15 +----+----+----+----+----+----+----+----+
19 MSN = Manufacturer SerialNumber
20 60 ea = DCF Low + DCF high
21 9f = raw byte which holds the bits for OLE,WRP,WRC,RD
22 ff = unknown but important
24 11 = unknown but important
25 Bck = header-backup-area
26 00 00 = Year (00 = 2000) & Week (not important)
28 SegC = Crc8 over the Segment Header
29 Stp = Stamp (could be more as 4 - up to 7)
30 UID = dec User-ID for online-Mapping
31 kghC = crc8 over MCD + MSN0..MSN2 + UID
34 (example) Legic-Cash on MIM256/1024 tag' (37 bytes)
35 +----+----+----+----+----+----+----+----+
36 0x00|Seg0|Seg1|Seg2|Seg3|SegC|STP0|STP1|STP2|
37 +----+----+----+----+----+----+----+----+
38 0x08|STP3|STP4|STP5|STP6| 01 |CURh|CURl|LIMh|
39 +----+----+----+----+----+----+----+----+
40 0x10|LIMm|LIMl|CHKh|CHKl|BALh|BALm|BALl|LRBh|
41 +----+----+----+----+----+----+----+----+
42 0x18|LRBm|LRBl|CHKh|CHKl|SHDh|SHDm|SHDl|LRSh|
43 +----+----+----+----+----+----+----+----+
44 0x20|LRSm|LRSl| CV |CHKh|CHKl|
45 +----+----+----+----+----+
46 STP = Stamp (seems to be always 7 bytes)
47 01 = unknown but important
48 CUR = currency in HEX (ISO 4217)
50 CHK = crc16 over byte-addr 0x05..0x12
52 LRB = ID of the reader that changed the balance
53 CHK = crc16 over BAL + LRB
55 LRS = ID of the reader that changed the shadow balance (?? should be always the same as LRB)
56 CV = Counter value for transactions
57 CHK = crc16 over SHD + LRS + CV
59 (example) Legic-Prime Layout 'gantner unsegmented user-credential'
60 +----+----+----+----+----+----+----+----+
61 0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 08 |
62 +----+----+----+----+----+----+----+----+
63 0x08|Stp0|Stp1|Stp2|Stp3|Stp4|Dat0|Dat1|uCRC| <- addr 0x08..0x0f is WRP
64 +----+----+----+----+----+----+----+----+
65 0x10|emb0| <- this is only within wrp if addr 0x07==09
68 MSN = Manufacturer SerialNumber
69 60 ea = DCF Low + DCF high
70 08 = raw byte which holds the bits for OLE,WRP,WRC,RD
71 Stp = Stamp (could be more as 4 - up to 7)
72 Dat = Online-Mapping Data
73 uCRC = crc8 over addr 0x00..0x03+0x07..0x0E
76 (example) Legic-Prime Layout 'gantner unsegmented Master-Token (IAM) with a stamp_len of 4'
77 +----+----+----+----+----+----+----+----+
78 0x00|MCD |MSN0|MSN1|MSN2|MCC | 20 | f8 | 08 |
79 +----+----+----+----+----+----+----+----+
80 0x08|Stp0|Stp1|Stp2|Stp3| 00 | 00 | 00 |CRC1|
81 +----+----+----+----+----+----+----+----+
82 0x10| 00 | 00 | 00 | 00 | 00 |CRC2|
83 +----+----+----+----+----+----+
85 MSN = Manufacturer SerialNumber
86 60 ea = DCF Low + DCF high
87 08 = raw byte which holds the bits for OLE,WRP,WRC,RD
88 Stp = Stamp (could be more as 4 - up to 7)
89 Dat = Online-Mapping Data
90 CRC1 = crc8 over addr 0x00..0x03+0x07..0x0E (special 'gantner crc8')
91 CRC2 = MCD + MSB0..2+ addr 0x06 + addr 0x05 + addr 0x07 + Stamp (regular Master-Token-CRC)
95 Known issues; needs to be fixed:
96 * last byte in last segment is handled incorrectly when it is the last bytes on the card itself (MIM256: => byte 256)
102 local utils
= require('utils')
103 local getopt
= require('getopt')
104 local ansicolors
= require('ansicolors')
107 -- global variables / defines
108 -- local bxor = bit32.bxor
109 -- local bbit = bit32.extract
110 local input
= utils
.input
111 local confirm
= utils
.confirm
114 -- init ansicolor-values & ansicolors switch
115 local colored_output
= true
124 local acy
= ansicolors
.yellow
125 local acc
= ansicolors
.cyan
126 local acr
= ansicolors
.reset
130 -- default colors (change to whatever you want)
131 function load_colors(onoff
)
134 acgreen
= ansicolors
.green
135 accyan
= ansicolors
.cyan
136 acred
= ansicolors
.red
137 acyellow
= ansicolors
.yellow
138 acblue
= ansicolors
.blue
139 acmagenta
= ansicolors
.magenta
140 acoff
= ansicolors
.reset
142 acy
= ansicolors
.yellow
143 acc
= ansicolors
.cyan
144 acr
= ansicolors
.reset
162 example
= "script run hf_legic"
163 author
= "Mosci, uhei"
168 This script helps you to read, create and modify Legic Prime Tags ( MIM22, MIM256, MIM1024 )
169 The virtual tag (and therefore the file to be saved) is always a MIM1024 tag.
170 it's kinda interactive with following commands in three categories:
172 Data I/O Segment Manipulation Token-Data
173 ----------------- -------------------- -----------------
174 ]]..acy
..[[rt]]..acr
..[[ -> read Tag ]]..acy
..[[as]]..acr
..[[ -> add Segment ]]..acy
..[[mt]]..acr
..[[ -> make Token
175 ]]..acy
..[[wt]]..acr
..[[ -> write Tag ]]..acy
..[[es]]..acr
..[[ -> edit Segment Header ]]..acy
..[[et]]..acr
..[[ -> edit Token data
176 ]]..acy
..[[ed]]..acr
..[[ => edit Segment Data ]]..acy
..[[tk]]..acr
..[[ => toggle KGH-Flag
177 File I/O ]]..acy
..[[rs]]..acr
..[[ => remove Segment
178 ----------------- ]]..acy
..[[cc]]..acr
..[[ -> check Segment-CRC
179 ]]..acy
..[[lf]]..acr
..[[ -> load bin File ]]..acy
..[[ck]]..acr
..[[ -> check KGH
180 ]]..acy
..[[sf]]..acr
..[[ -> save eml/bin File ]]..acy
..[[ds]]..acr
..[[ -> dump Segments
181 ]]..acy
..[[xf]]..acr
..[[ -> xor to File
184 (partially) known Segments Virtual Tags Script Output
185 --------------------------- ------------------------------- ------------------------
186 ]]..acy
..[[dlc]]..acr
..[[ -> dump Legic-Cash ]]..acy
..[[ct]]..acr
..[[ -> copy mainTag to backupTag ]]..acy
..[[tac]]..acr
..[[ -> toggle ansicolors
187 ]]..acy
..[[elc]]..acr
..[[ -> edit Legic-Cash ]]..acy
..[[tc]]..acr
..[[ -> copy backupTag to mainTag
188 ]]..acy
..[[d3p]]..acr
..[[ -> dump 3rd-Party-Cash ]]..acy
..[[tt]]..acr
..[[ -> switch mainTag & backupTag
189 ]]..acy
..[[e3p]]..acr
..[[ -> edit 3rd-Party-Cash ]]..acy
..[[di]]..acr
..[[ -> dump mainTag
190 ]]..acy
..[[do]]..acr
..[[ => dump backupTag
192 rt: 'read tag' - reads a tag placed near to the PM3
193 wt: 'write tag' - writes the content of the 'virtual inTag' to a tag placed near to th PM3
194 without the need of changing anything - MCD,MSN,MCC will be read from the tag
195 before and applied to the output.
197 lf: 'load file' - load a (xored) binary file (*.bin) from the local Filesystem into the 'virtual inTag'
198 sf: 'save file' - saves the 'virtual inTag' to the local Filesystem as eml and bin (xored with Tag-MCC)
199 xf: 'xor file' - saves the 'virtual inTag' to the local Filesystem (xored with chosen MCC - use '00' for plain values)
201 ct: 'copy tag' - copy the 'virtual Tag' to a second 'virtual TAG' - not useful yet, but inernally needed
202 tc: 'copy tag' - copy the 'second virtual Tag' to 'virtual TAG' - not useful yet, but inernally needed
203 tt: 'toggle tag' - copy mainTag to BackupTag and backupTag to mainTag
205 di: 'dump mainTag' - shows the current content of the 'virtual Tag'
206 do: 'dump backupTag' - shows the current content of the 'virtual outTag'
207 ds: 'dump Segments' - will show the content of a selected Segment
208 as: 'add Segment' - will add a 'empty' Segment to the inTag
209 es: 'edit Segment' - edit the Segment-Header of a selected Segment (len, WRP, WRC, RD, valid)
210 all other Segment-Header-Values are either calculated or not needed to edit (yet)
211 ed: 'edit data' - edit the Data of a Segment (ADF-Aera / Stamp & Payload specific Data)
212 et: 'edit Token' - edit Data of a Token (CDF-Area / SAM, SAM64, SAM63, IAM, GAM specific Data)
213 mt: 'make Token' - create a Token 'from scratch' (guided)
214 rs: 'remove segment' - removes a Segment (except Segment 00, but this can be set to valid=0 for Master-Token)
215 cc: 'check Segment-CRC' - checks & calculates (if check failed) the Segment-CRC of all Segments
216 ck: 'check KGH-CRC' - checks the and calculates a 'Kaba Group Header' if one was detected
217 'Kaba Group Header CRC calculation'
218 tk: 'toggle KGH' - toggle the (script-internal) flag for kgh-calculation for a segment
219 xc: 'etra c' - show string that was used to calculate the kgh-crc of a segment
221 dlc: 'dump Legic-Cash' - show balance and checksums of a Legic-Cash Segment
222 elc: 'edit Legic-Cash' - edit values of a Legic-Cash Segment
224 d3p: 'dump 3rd Party' - show balance, history and checksums of a (yet) unknown 3rd-Party Cash Segment
225 e3p: 'edit 3rd Party' - edit Data in 3rd-Party Cash Segment
227 tac: 'toggle ansicolors' - switch on and off the colored text-output of this script
228 default can be changed by setting the variable 'colored_output' to false
233 -- curency-codes for Legic-Cash-Segments (ISO 4217)
242 -- This is only meant to be used when errors occur
244 print(acred
.."ERROR: "..acoff
,err
)
251 -- the proxmark3 client can't handle such long strings
252 -- by breaking up at specific points it still looks good.
253 print(string.sub(desc
, 0, 1961))
254 print(string.sub(desc
, 1962, 3925))
255 print(string.sub(desc
, 3926, #desc
))
256 print("Version: "..version
)
257 print("Example usage: "..example
)
261 -- table check helper
263 return type(t
) == 'table'
267 -- To have two char string for a byte
268 local function padString(str
)
276 -- creates a 'deep copy' of a table (a=b only references)
277 function deepCopy(object
)
278 local lookup_table
= {}
279 local function _copy(object
)
280 if type(object
) ~= "table" then
282 elseif lookup_table
[object
] then
283 return lookup_table
[object
]
286 lookup_table
[object
] = new_table
287 for index
, value
in pairs(object
) do
288 new_table
[_copy(index
)] = _copy(value
)
290 return setmetatable(new_table
, getmetatable(object
))
297 function xorme(hex
, xor
, index
)
298 if ( index
>= 23 ) then
299 --return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) ))
300 return string.format("%x", tonumber(hex
,16) ~ tonumber(xor
,16))
307 -- (de)obfuscate bytes
308 function xorBytes(inBytes
, crc
)
310 for index
= 1, #inBytes
do
311 bytes
[index
] = xorme(inBytes
[index
], crc
, index
)
313 if (#inBytes
== #bytes
) then
315 bytes
[5] = string.sub(crc
,-2)
318 print("error: byte-count missmatch")
324 -- split csv-string into table
325 local function split(str
, sep
)
326 local sep
= sep
or ','
328 local matchfunc
= string.gmatch(str
, "([^"..sep
.."]+)")
329 if not matchfunc
then return {str
} end
330 for str
in matchfunc
do
331 table.insert(fields
, str
)
337 -- join table with a separator
338 local function join(list
, sep
)
339 local sep
= sep
or ','
341 if len
== 0 then return "" end
344 s
= s
.. sep
.. list
[i
]
350 -- check availability of file
351 function file_check(file_name
)
352 if not file_name
then return false, "" end
354 local arr
= split(file_name
, ".")
355 local ext
= table.remove(arr
)
356 local name
= join(arr
, '.')
357 local path
= core
.search_file(name
, "."..ext
)
358 if (path
== nil) then return false, "" end
360 local file_found
= io
.open(path
, "r")
361 if file_found
== nil then
370 -- put a string into a bytes-table
371 function str2bytes(s
)
372 if (string.len(s
)%2 ~= 0) then
373 return print("stamp should be a even hexstring e.g.: deadbeef or 0badc0de")
376 for i
=1, string.len(s
), 2 do
377 table.insert(res
, string.sub(s
,i
,(i
+1)))
383 -- put certain bytes into a new table
384 function bytesToTable(bytes
, bstart
, bend
)
386 for i
=0, (bend
-bstart
) do
387 t
[i
]=padString(bytes
[bstart
+i
])
393 -- read file into table
394 function getInputBytes(infile
)
398 local arr
= split(infile
, ".")
399 local ext
= table.remove(arr
)
400 local name
= join(arr
, '.')
401 local path
= core
.search_file(name
, "."..ext
)
402 if (path
== nil) then oops("failed to read from file ".. infile
); return false; end
404 local fhi
,err
= io
.open(path
,"rb")
405 if err
then oops("failed to read from file ".. path
); return false; end
407 file_data
= fhi
:read("*a");
408 for i
= 1, #file_data
do
409 bytes
[i
] = string.format("%x",file_data
:byte(i
))
412 if (bytes
[7]=='00') then return false end
413 print(#bytes
.. " bytes from "..path
.." loaded")
418 -- create tag-table helper
419 function createTagTable()
445 -- put bytes into tag-table
446 function bytesToTag(bytes
, tag)
447 if istable(tag) == false then return oops("tag is no table in: bytesToTag ("..type(tag)..")") end
449 tag.MCD
=padString(bytes
[1]);
450 tag.MSN0
=padString(bytes
[2]);
451 tag.MSN1
=padString(bytes
[3]);
452 tag.MSN2
=padString(bytes
[4]);
453 tag.MCC
=padString(bytes
[5]);
454 tag.DCFl
=padString(bytes
[6]);
455 tag.DCFh
=padString(bytes
[7]);
456 tag.raw
=padString(bytes
[8]);
457 tag.SSC
=padString(bytes
[9]);
458 tag.Type
=getTokenType(tag.DCFl
);
459 --tag.OLE=bbit("0x"..tag.DCFl,7,1)
460 tag.OLE
=(tonumber(tag.DCFl
, 16) >> 7) & 0x01
461 --tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4))
462 tag.WRP
=string.format("%02d", tonumber(bytes
[8], 16) & 0x0F)
463 --tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3))
464 tag.WRC
= string.format("%02d", (tonumber(bytes
[8], 16) >> 4) & 0x07)
465 --tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1))
466 tag.RD
=string.format("%02d", (tonumber(bytes
[8],16) >> 7) & 0x01)
467 if (tag.Type
=="SAM" and tag.raw
=='9f') then
468 tag.Stamp_len
=(0xfc - tonumber(tag.DCFh
,16) & 0xFF)
469 --tag.Stamp_len=(0xfc-bbit("0x"..tag.DCFh,0,8))
470 elseif (tag.Type
=="SAM" and (tag.raw
=='08' or tag.raw
=='09')) then
471 tag.Stamp_len
= tonumber(tag.raw
,10)
473 tag.data
=bytesToTable(bytes
, 10, 13)
474 tag.Bck
=bytesToTable(bytes
, 14, 20)
475 tag.MTC
=bytesToTable(bytes
, 21, 22)
477 print(acgreen
.."Tag-Type: ".. tag.Type
..acoff
)
478 if (tag.Type
=="SAM" and #bytes
>23) then
479 tag=segmentsToTag(bytes
, tag)
480 print(acgreen
..(#tag.SEG
+1).." Segment(s) found"..acoff
)
481 -- unsegmented Master-Token
485 table.insert(tag.data
, tag.Bck
[i
])
487 tag.data
[#tag.data
]=tag.MTC
[0]
489 --tag.MTC[0]=tag.MTC[1]
492 print(accyan
..#bytes
.." bytes for Tag processed"..acoff
)
498 -- put segments from byte-table to tag-table
499 function segmentsToTag(bytes
, tag)
503 if (istable(tag)) then
506 tag.SEG
[i
]=getSegmentData(bytes
, start
, ("%02d"):format(i
))
507 if (tag.Type
=="SAM") then
508 if (checkKghCrc(tag, i
)) then tag.SEG
[i
].kgh
=true end
510 start
=start
+tag.SEG
[i
].len
511 until ((tag.SEG
[i
].valid
==0) or tag.SEG
[i
].last
==1 or i
==126)
513 else return oops("tag is no table in: segmentsToTag ("..type(tag)..")") end
514 else print("no Segments: must be a MIM22") end
518 -- read Tag-Table in bytes-table
519 function tagToBytes(tag)
520 if istable(tag) == false then return oops("tag is no table in tagToBytes ("..type(tag)..")") end
525 table.insert(bytes
, tag.MCD
)
526 table.insert(bytes
, tag.MSN0
)
527 table.insert(bytes
, tag.MSN1
)
528 table.insert(bytes
, tag.MSN2
)
529 table.insert(bytes
, tag.MCC
)
530 table.insert(bytes
, tag.DCFl
)
531 table.insert(bytes
, tag.DCFh
)
532 table.insert(bytes
, tag.raw
)
533 table.insert(bytes
, tag.SSC
)
535 for i
=0, #tag.data
do
536 table.insert(bytes
, tag.data
[i
])
539 if(istable(tag.Bck
)) then
541 table.insert(bytes
, tag.Bck
[i
])
544 -- token-create-time / master-token crc
546 table.insert(bytes
, tag.MTC
[i
])
549 if (type(tag.SEG
[0])=='table') then
551 for i2
=1, #tag.SEG
[i
].raw
+1 do
552 table.insert(bytes
, #bytes
+1, tag.SEG
[i
].raw
[i2
])
554 table.insert(bytes
, #bytes
+1, tag.SEG
[i
].crc
)
555 for i2
=0, #tag.SEG
[i
].data
-1 do
556 table.insert(bytes
, #bytes
+1, tag.SEG
[i
].data
[i2
])
561 for i
=#bytes
+1, 1024 do
562 table.insert(bytes
, i
, '00')
570 -- write virtual Tag to real Tag
571 function writeToTag(tag)
574 local writeDCF
= false
575 if(utils
.confirm(acred
.."\nPlace the (empty) Tag onto the PM3\nand confirm writing to this Tag: "..acoff
) == false) then
578 if(utils
.confirm(acred
.."\nShould the decremental field (DCF) be written?: "..acoff
) == true) then
582 -- get used bytes / tag-len
583 if (istable(tag.SEG
)) then
584 if (istable(tag.Bck
)) then
586 taglen
= taglen
+ tag.SEG
[i
] . len
589 local uid_old
= tag.MCD
..tag.MSN0
..tag.MSN1
..tag.MSN2
591 -- read new tag into memory so we can xor the new data with the new MCC
592 outTAG
= readFromPM3()
593 outbytes
= tagToBytes(outTAG
)
594 -- copy 'inputbuffer' to 'outputbuffer'
595 tag.MCD
= outbytes
[1]
596 tag.MSN0
= outbytes
[2]
597 tag.MSN1
= outbytes
[3]
598 tag.MSN2
= outbytes
[4]
599 tag.MCC
= outbytes
[5]
600 -- recheck all segments-crc/kghcrc (only on a credential)
601 if (istable(tag.Bck
)) then
604 local uid_new
= tag.MCD
..tag.MSN0
..tag.MSN1
..tag.MSN2
606 if (check43rdPartyCash1(uid_old
, tag.SEG
[i
].data
)) then
607 io
.write(accyan
.."\nfixing known checksums"..acoff
.." ... ")
608 if (fix3rdPartyCash1(uid_new
, tag.SEG
[i
].data
)) then
609 io
.write(acgreen
.." done\n"..acoff
)
611 oops("\nsomething went wrong at the repair of the 3rd-party-cash-segment")
616 bytes
= tagToBytes(tag)
618 if (tag.Type
~= "SAM") then
619 bytes
[22] = calcMtCrc(bytes
)
622 bytes
= xorBytes(bytes
,tag.MCC
)
627 -- write data to file
629 WriteBytes
= input(acyellow
.."enter number of bytes to write?"..acoff
, taglen
)
630 -- write pm3-buffer to Tag
631 for i
=1, WriteBytes
do
633 cmd
= ("hf legic wrbl -o %d -d %s "):format(i
-1, padString(bytes
[i
]))
634 print(acgreen
..cmd
..acoff
)
636 core
.clearCommandBuffer()
639 -- write DCF in reverse order (requires 'mosci-patch')
640 cmd
= ('hf legic wrbl -o 5 -d %s%s'):format(padString(bytes
[i
-1]), padString(bytes
[i
]))
641 print(acgreen
..cmd
..acoff
)
643 core
.clearCommandBuffer()
645 print(acgreen
.."skip byte 0x05-0x06 - DCF"..acoff
)
648 print(acgreen
.."skip byte 0x05 - will be written next step"..acoff
)
650 print(acgreen
.."skip byte 0x00-0x04 - unwritable area"..acoff
)
659 -- read file into virtual-tag
660 local function readFile(filename
)
665 local res
, path
= file_check(filename
)
667 return oops("input file: "..acyellow
..filename
..acoff
.." not found")
670 bytes
= getInputBytes(path
)
671 if bytes
== false then return oops('couldnt get input bytes') end
674 bytes
= xorBytes(bytes
,bytes
[5])
675 print("create virtual tag from ".. #bytes
.. " bytes")
676 -- create Tag for plain bytes
677 tag = createTagTable()
678 -- load plain bytes to tag-table
680 tag = bytesToTag(bytes
, tag)
685 local function save_BIN(data
, filename
)
689 local fn
= filename
..ext
691 -- Make sure we don't overwrite a file
692 local res
, path
= file_check(fn
)
693 while res
== false do
694 fn
= filename
..ext
:gsub(ext
, "-"..tostring(counter
)..ext
)
695 counter
= counter
+ 1
696 res
, path
= file_check(fn
)
699 outfile
= io
.open(path
, 'wb')
703 local byte
= string.char(tonumber(data
[i
], 16))
711 -- write bytes to file
712 function writeFile(bytes
, filename
)
713 local emlext
= ".eml"
715 if (filename
~= 'MyLegicClone') then
716 res
, path
= file_check(filename
..emlext
)
718 local answer
= confirm("\nthe output-file "..path
.." already exists!\nthis will delete the previous content!\ncontinue?")
719 if not answer
then return print("user abort") end
724 local fho
, err
= io
.open(path
, "w")
726 return oops("OOps ... failed to open output-file ".. path
)
729 bytes
= xorBytes(bytes
, bytes
[5])
733 line
= padString(bytes
[i
])
734 elseif (bcnt
<= 7) then
735 line
= line
.." "..padString(bytes
[i
])
738 -- write line to new file
739 fho
:write(line
.."\n")
740 -- reset counter & line
748 print("\nwrote "..acyellow
..(#bytes
* 3)..acoff
.." bytes to " ..acyellow
..filename
..emlext
..acoff
)
751 local fn_bin
, fn_bin_num
= save_BIN(bytes
, filename
)
752 if fn_bin
and fn_bin_num
then
753 print("\nwrote "..acyellow
..fn_bin_num
..acoff
.." bytes to BINARY file "..acyellow
..fn_bin
..acoff
)
759 function getRandomTempName()
760 local upperCase
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
761 local lowerCase
= "abcdefghijklmnopqrstuvwxyz"
763 local characterSet
= upperCase
.. lowerCase
768 for i
= 1, keyLength
do
769 local rand
= math
.random(#characterSet
)
770 output
= output
.. string.sub(characterSet
, rand
, rand
)
773 output
= "hf-legic-temp-" .. output
779 -- read from pm3 into virtual-tag
780 function readFromPM3()
781 local tag, bytes
, infile
782 infile
=getRandomTempName()
783 core
.console("hf legic dump -f "..infile
)
784 tag=readFile(infile
..".bin")
786 res
, path
= file_check(infile
..".bin")
787 if res
then os
.remove(path
) end
790 res
, path
= file_check(infile
..".json")
791 if res
then os
.remove(path
) end
798 local function makeTagMap()
800 if (#tagMap
== 0) then
801 tagMap
['name'] = input(accyan
.."enter Name for this Map: "..acoff
, "newTagMap")
802 tagMap
['mappings'] = {}
804 -- insert fixed Tag-CRC
805 table.insert(tagMap
.crc8
, {name
= 'TAG-CRC', pos
= 5, seq
= {1, 4}})
808 print(accyan
.."new tagMap created"..acoff
)
813 -- save mapping to file
814 local function saveTagMap(map
, filename
)
818 if #filename
> 0 then
819 res
, path
= file_check(filename
)
821 local answer
= confirm("\nthe output-file "..acyellow
..path
..acoff
.." alredy exists!\nthis will delete the previous content!\ncontinue?")
822 if not answer
then return print("user abort") end
827 local fho
,err
= io
.open(path
, "w")
828 if err
then oops("OOps ... failed to open output-file "..acyellow
..path
..acoff
) end
830 -- write line to new file
831 for k
, v
in pairs(map
) do
833 for k2
, v2
in pairs(v
) do
834 if (k
== 'mappings') then
835 fho
:write(k
..","..k2
..","..v2
['name']..","..v2
['start']..","..v2
['end']..","..((v2
['highlight']) and "1" or "0").."\n")
836 elseif (k
== "crc8") then
838 tmp
= k
..","..k2
..","..v2
['name']..","..v2
['pos']..","
839 tmp
=tmp
..tbl2seqstr(v2
['seq'])
844 fho
:write(k
..","..v
.."\n")
853 local function toggleHighlight(tbl
)
854 if (tbl
['highlight']) then
855 tbl
['highlight'] = false
857 tbl
['highlight'] = true
863 -- return table od seqence-string
864 local function seqstr2tbl(seqstr
)
865 local s
= split(seqstr
)
868 for sk
, sv
in pairs(s
) do
871 table.insert(res
, s2
[1])
872 table.insert(res
, s2
[2])
880 -- return sequence-string from table
881 local function tbl2seqstr(seqtbl
)
883 if (istable(seqtbl
)) then
884 for sk
, sv
in pairs(seqtbl
) do
885 res
= res
..sv
..((sk
%2==0) and "," or "-")
887 if (string.sub(res
, string.len(res
))== ",") then
888 res
= string.sub(res
, 1, string.len(res
)-1)
895 -- read map-file into map
896 function loadTagMap(filename
)
897 local map
={mappings
={}, crc8
={}, crc16
={}}
904 local res
, path
= file_check(filename
)
906 return oops("input file: "..acyellow
..filename
..acoff
.." not found")
909 local fhi
,err
= io
.open(path
)
917 if (#fields
== 2) then
918 if (fields
[1] == 'offset') then
919 offset
= tonumber(fields
[2],10)
922 map
[fields
[1]]
=fields
[2]
923 elseif (fields
[1]=='mappings') then
927 temp
['name']=fields
[3]
928 temp
['start']=tonumber(fields
[4], 10)
929 temp
['end']=tonumber(fields
[5], 10)
930 if(temp
['start']>22) then
931 temp
['start']=temp
['start']+offset
932 temp
['end']=temp
['end']+offset
934 if (tonumber(fields
[6], 10)==1) then temp
['highlight']= true
935 else temp
['highlight']= false end
936 table.insert(map
['mappings'], m
, temp
)
937 elseif (fields
[1]=='crc8') then
941 temp
['name']=fields
[3]
942 temp
['pos']=tonumber(fields
[4], 10)+offset
943 local s
=string.sub(line
, string.len(fields
[1]..","..fields
[2]..","..fields
[3]..",")+1, string.len(line
))
944 temp
['seq']=seqstr2tbl(s
)
945 for k
, v
in pairs(temp
['seq']) do
946 if(tonumber(v
, 10)>22) then v
=tonumber(v
, 10)+offset
end
947 temp
['seq'][k
]=tonumber(v
, 10)
949 table.insert(map
.crc8
, temp
)
958 -- dump tagMap (mappings only)
959 function dumpTagMap(tag, tagMap
)
960 if(#tagMap
.mappings
>0) then
961 bytes
=tagToBytes(tag)
964 -- start display mappings
965 for k
, v
in pairs(tagMap
.mappings
) do
966 if ((lastend
+1)<v
['start']) then
969 if (isPosCrc8(tagMap
, v
['start'])>0) then
970 if ( checkMapCrc8(tagMap
, bytes
, isPosCrc8(tagMap
, v
['start']) ) ) then
971 io
.write("("..("%04d"):format(v
['start']).."-"..("%04d"):format(v
['end'])..") "..acgreen
..v
['name']..acoff
)
973 io
.write("("..("%04d"):format(v
['start']).."-"..("%04d"):format(v
['end'])..") "..acred
..v
['name']..acoff
)
976 io
.write("("..("%04d"):format(v
['start']).."-"..("%04d"):format(v
['end'])..") "..((v
['highlight']) and acmagenta
or acyellow
)..v
['name']..acoff
)
980 while (#v
['name'] + temp
:len()) < 20 do temp
= temp
.." " end
982 for i
=v
['start'], v
['end'] do
983 temp
=temp
..bytes
[i
].." "
994 function isPosCrc8(tagMap
, pos
)
996 if (#tagMap
.crc8
>0) then
997 for k
, v
in pairs(tagMap
.crc8
) do
998 if(v
['pos']==pos
) then res
=k
end
1006 function checkMapCrc8(tagMap
, bytes
, n
)
1008 if (#tagMap
.crc8
>0) then
1009 if(istable(tagMap
.crc8
[n
])) then
1011 for k2
, v2
in pairs(tagMap
.crc8
[n
]) do
1012 if (istable(v2
)) then
1013 temp
=temp
..tbl2seqstr(v2
)
1017 local tempres
=getSequences(bytes
, temp
)
1018 tempres
=("%02x"):format(utils
.Crc8Legic(tempres
))
1019 if (bytes
[tagMap
.crc8
[n
]['pos']]
==tempres
) then
1028 -- edit existing Map
1029 function editTagMap(tag, tagMap
)
1031 ]]..acc
..[[Data]]..acr
..[[
1033 ]]..acy
..[[dm]]..acr
..[[ - show ]]..acy
..[[dr]]..acr
..[[ - dump raw
1035 ]]..acc
..[[Mappings]]..acr
..[[
1037 ]]..acy
..[[im]]..acr
..[[ - insert ]]..acy
..[[am]]..acr
..[[ - add
1038 ]]..acy
..[[rm]]..acr
..[[ - remove ]]..acy
..[[mas]]..acr
..[[ - map all segments
1040 ]]..acc
..[[CRC8]]..acr
..[[
1042 ]]..acy
..[[ac8]]..acr
..[[ - add ]]..acy
..[[sc8]]..acr
..[[ - show
1043 ]]..acy
..[[rc8]]..acr
..[[ - remove
1045 ]]..acy
..[[q]]..acr
..[[ - exit ]]..acy
..[[h]]..acr
..[[ - Help
1048 --if(#tagMap.mappings==0) then oops("no mappings in tagMap"); return tagMap end
1049 print("tagMap edit-mode submenu")
1051 x
=input('tagMap submenu:', 'h')
1052 if (x
=='h') then print(t
)
1053 elseif (x
=='dm') then tagMmap
=dumpTagMap(tag, tagMap
)
1054 elseif (x
=='dr') then tagMmap
=dumpMap(tag, tagMap
)
1055 elseif (x
=='rc8') then
1056 if (istable(tagMap
.crc8
)) then
1057 local x1
= selectTableEntry(tagMap
.crc8
, "select number of CRC8 to remove:")
1058 if (istable(tagMap
.crc8
[x1
])) then
1059 table.remove(tagMap
.crc8
, x1
)
1062 elseif (x
=='ac8') then
1063 local p
=tonumber(input("enter byte-addr of crc8", '0'),10)
1065 local i1
=input("enter comma-seperated byte-sequences (e.g.: '1-4,23-26')", '1-4,23-26')
1066 local s1
=split(i1
, ',')
1069 for k
, v
in pairs(s1
) do
1072 table.insert(temp
.seq
, v1
[1])
1073 table.insert(temp
.seq
, v1
[2])
1077 temp
['name']=input("enter a name for the CRC8", "CRC "..(#tagMap
.crc8
+1))
1078 table.insert(tagMap
.crc8
, temp
)
1081 elseif (string.sub(x
, 1, 3)=='sc8') then
1082 local bytes
=tagToBytes(tag)
1084 -- trigger manually by sc8 <'4digit' checkadd> <'seqeuence-string'>
1085 -- e.g.: sc8 0027 1-4,23-36
1086 if (string.len(x
)>=9) then
1087 pos
=tonumber(string.sub(x
, 5, 8), 10)
1088 x
=string.sub(x
, 9, string.len(x
))
1089 print("x: "..x
.." - pos:"..pos
)
1091 x
=selectTableEntry(tagMap
.crc8
, "select CRC:")
1092 if(istable(tagMap
.crc8
[x
])) then
1093 pos
=tagMap
.crc8
[x
]['pos']
1094 x
=tbl2seqstr(tagMap
.crc8
[x
]['seq'])
1097 if (type(x
)=='string') then
1098 res
=("%02x"):format(utils
.Crc8Legic(getSequences(bytes
, x
)))
1099 print(accyan
.."Sequence:\t"..acoff
..x
)
1100 print(accyan
.."Bytes:\t\t"..acoff
..getSequences(bytes
, x
))
1101 print(accyan
.."calculated: "..acoff
..res
..accyan
.." bytes["..pos
.."]: "..acoff
..bytes
[pos
].." ("..compareCrc(utils
.Crc8Legic(getSequences(bytes
, x
)), bytes
[pos
])..")")
1103 elseif (x
=="tm") then
1104 x
=selectTableEntry(tagMap
.mappings
, "select number of Mapping:")
1105 tagMap
.mappings
[x
]=toggleHighlight(tagMap
.mappings
[x
])
1106 elseif (x
=='am') then tagMap
=addMapping(tag, tagMap
)
1107 elseif (x
=='im') then tagMap
=addMapping(tag, tagMap
, selectTableEntry(tagMap
.mappings
, "select List-Position for insert:"))
1108 elseif (x
=='rm') then tagMap
=deleteMapping(tag, tagMap
)
1109 elseif (x
=='mas') then tagMap
=mapTag(tagMap
); tagMap
=mapAllSegments(tag, tagMap
)
1110 elseif (type(actions
[string.sub(x
, 3)])=='function') then actions
[string.sub(x
, 3)]()
1113 print("exit sub-Menu")
1118 -- dump raw mapped and unmapped
1119 function dumpMap(tag, tagMap
)
1122 local bytes
= tagToBytes(tag)
1123 local stats
= getSegmentStats(bytes
)
1124 dend
=stats
[#stats
]['end']
1125 print(accyan
.."Tag uses "..dend
.." bytes:"..acoff
)
1126 for i
=dstart
, dend
do
1127 if (check4MappedByte(i
, tagMap
) and not check4MapCrc8(i
, tagMap
) and not check4Highlight(i
, tagMap
)) then io
.write(""..acyellow
)
1128 elseif (check4MapCrc8(i
, tagMap
)) then
1129 if ( checkMapCrc8(tagMap
, bytes
, isPosCrc8(tagMap
, i
) ) ) then
1130 io
.write(""..acgreen
)
1137 -- highlighted mapping
1138 if (check4Highlight(i
, tagMap
)) then io
.write(""..acmagenta
) end
1141 if (i
%8==0) then io
.write("\n")
1142 else io
.write(" ") end
1145 io
.write("\n"..acoff
)
1149 -- show bytes used for crc-calculation
1150 function getSequences(bytes
, seqstr
)
1151 if (type(seqstr
) ~= "string") then seqstr
= input("enter comma-seperated sequences (e.g.: '1-4,23-26')", '1-4,23-26') end
1152 local seqs
= split(seqstr
, ',')
1155 for k
, v
in pairs(seqs
) do
1156 local seq
= split(v
,'-')
1158 for i
= seq
[1], seq
[2] do
1159 res
= res
..bytes
[i
].." "
1162 if(string.len(res
)>0) then res
= res
.." " end
1165 oops("no sequence found in '"..seqstr
.."'")
1171 -- check if byte-addr is a know crc
1172 function check4MapCrc8(addr
, tagMap
)
1174 for i
=1, #tagMap
.crc8
do
1175 if (addr
== tagMap
.crc8
[i
]['pos']) then
1183 -- check if byte-addr is a know crc
1184 function check4MapCrc16(addr
, tagMap
)
1186 for i
=1, #tagMap
.crc16
do
1187 if (addr
== tagMap
.crc16
[i
]['pos']) then
1195 -- check if byte is mapped or not
1196 function check4MappedByte(addr
, tagMap
)
1198 for _
, v
in pairs(tagMap
.mappings
) do
1199 if (addr
>= v
['start'] and addr
<= v
['end'] ) then
1207 -- check if byte is highlighted or not
1208 function check4Highlight(addr
, tagMap
)
1210 for _
, v
in pairs(tagMap
.mappings
) do
1211 if (addr
>= v
['start'] and addr
<= v
['end'] ) then
1212 res
= v
['highlight']
1219 -- add interactive mapping
1220 function addMapping(tag, tagMap
, x
)
1221 if (type(x
) ~= "number") then x
= #tagMap
.mappings
+ 1 end
1222 local bytes
= tagToBytes(tag)
1223 local myMapping
= {}
1224 myMapping
['name'] = input(accyan
.."enter Maping-Name:"..acoff
, string.format("mapping %d", #tagMap
.mappings
+1))
1225 myMapping
['start'] = tonumber(input(accyan
.."enter start-addr:"..acoff
, '1'), 10)
1226 myMapping
['end'] = tonumber(input(accyan
.."enter end-addr:"..acoff
, #bytes
), 10)
1227 myMapping
['highlight'] = confirm("set highlighted")
1228 table.insert(tagMap
.mappings
, x
, myMapping
)
1234 function deleteMapping(tag, tagMap
)
1235 if(#tagMap
.mappings
>0) then
1236 local d
= selectTableEntry(tagMap
.mappings
, "select number of Mapping to remove:")
1237 if (type(d
)=='number') then
1238 table.remove(tagMap
.mappings
, d
)
1239 else oops("deleteMapping: got type = "..type(d
).." - expected type = 'number'")
1246 -- select a mapping from a tagmap
1247 function selectTableEntry(table, action
)
1248 if (type(action
) ~= "string") then action
= "select number of item:" end
1249 for k
, v
in pairs(table) do
1250 print(accyan
..k
..acoff
.."\t-> "..accyan
..v
['name']..acoff
)
1252 local res
= tonumber(input(action
, 0), 10)
1253 if (istable(table[res
])) then
1262 function mapAllSegments(tag, tagMap
)
1263 local bytes
=tagToBytes(tag)
1265 segs
=getSegmentStats(bytes
)
1266 if (istable(segs
)) then
1267 for k
, v
in pairs(segs
) do
1268 -- wrp (write proteted) = byte 2
1269 WRP
= tonumber(bytes
[v
['start']+2],16)
1270 -- wrc (write control) - bit 4-6 of byte 3
1271 --WRC = tonumber(bbit("0x"..bytes[v['start']+3],4,3),16)
1272 WRC
= (tonumber(bytes
[v
['start']+3],16) >> 4) & 0x07
1273 --tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." HDR", v['start'], v['start']+3)
1274 tagMap
=mapTokenData(tagMap
, 'Segment '..("%02d"):format(v
['index']).." CRC", v
['start']+4, v
['start']+4, true)
1275 table.insert(tagMap
.crc8
, {name
= 'Segment '..("%02d"):format(v
['index']).." CRC", pos
=v
['start']+4, seq
={1,4,v
['start'],v
['start']+3}} )
1278 tagMap
=mapTokenData(tagMap
, 'Segment '..("%02d"):format(v
['index']).." WRC", v
['start']+5, v
['start']+5+WRC
-1, true)
1279 elseif (WRP
>WRC
and WRC
>0) then
1281 tagMap
=mapTokenData(tagMap
, 'Segment '..("%02d"):format(v
['index']).." WRC", v
['start']+5, v
['start']+5+WRC
-1, true)
1282 tagMap
=mapTokenData(tagMap
, 'Segment '..("%02d"):format(v
['index']).." WRP", v
['start']+WRC
+5, v
['start']+5+WRP
-1, true)
1285 tagMap
=mapTokenData(tagMap
, 'Segment '..("%02d"):format(v
['index']).." WRP", v
['start']+5, v
['start']+5+WRP
-1, true)
1287 tagMap
=mapTokenData(tagMap
, 'Segment '..("%02d"):format(v
['index']).." data", v
['start']+5+WRPC
, v
['end'], false)
1290 print(#segs
.." Segments mapped")
1292 oops("autoMapSegments failed: no Segments found")
1298 -- map all token data
1299 function mapTokenData(tagMap
, mname
, mstart
, mend
, mhigh
)
1300 --if ( not mhigh ) then mhigh=false end
1301 local myMapping
= {}
1302 myMapping
['name'] = mname
1303 myMapping
['start'] = mstart
1304 myMapping
['end'] = mend
1305 myMapping
['highlight'] = mhigh
1306 table.insert(tagMap
.mappings
, myMapping
)
1312 function mapTag(tagMap
)
1314 tagMap
=mapTokenData(tagMap
, 'Tag-ID', 1, 4, true)
1315 tagMap
=mapTokenData(tagMap
, 'Tag-CRC', 5, 5, false)
1316 tagMap
=mapTokenData(tagMap
, 'DCF', 6, 7, true)
1317 tagMap
=mapTokenData(tagMap
, 'THDR-Raw/Stamp-Len', 8, 8, true)
1318 tagMap
=mapTokenData(tagMap
, 'SSC', 9, 9, true)
1319 tagMap
=mapTokenData(tagMap
, 'Header', 10, 13, false)
1320 tagMap
=mapTokenData(tagMap
, 'Backup', 14, 19, true)
1321 tagMap
=mapTokenData(tagMap
, 'Bck-CRC', 20, 20, false)
1322 tagMap
=mapTokenData(tagMap
, 'TokenTime', 21, 22, false)
1328 -- dump virtual Tag-Data
1329 function dumpTag(tag)
1335 res
=acyellow
.."\nCDF: System Area"..acoff
1336 res
= res
.."\n"..dumpCDF(tag)
1337 -- segments (user-token area)
1338 if(tag.Type
=="SAM" and tag.raw
=='9f') then
1339 res
= res
..acyellow
.."\n\nADF: User Area"..acoff
1340 for i
=0, #tag.SEG
do
1341 res
=res
.."\n"..dumpSegment(tag, i
).."\n"
1348 -- dump tag-system area
1349 function dumpCDF(tag)
1354 if (istable(tag)) then
1355 res
= res
..accyan
.."MCD: "..acoff
..tag.MCD
..accyan
.." MSN: "..acoff
..tag.MSN0
.." "..tag.MSN1
.." "..tag.MSN2
..accyan
.." MCC: "..acoff
..tag.MCC
.."\n"
1356 res
= res
.."DCF: "..tag.DCFl
.." "..tag.DCFh
..", Token_Type="..tag.Type
.." (OLE="..tag.OLE
.."), Stamp_len="..tag.Stamp_len
.."\n"
1357 res
= res
.."WRP="..tag.WRP
..", WRC="..tag.WRC
..", RD="..tag.RD
..", raw="..tag.raw
..((tag.raw
=='9f') and (", SSC="..tag.SSC
.."\n") or "\n")
1359 -- credential (end-user tag)
1360 if (tag.Type
=="SAM" and tag.raw
=='9f') then
1361 res
= res
.."Remaining Header Area\n"
1362 for i
=0, (#tag.data
) do
1363 res
= res
..tag.data
[i
].." "
1365 res
= res
.."\nBackup Area\n"
1366 for i
=0, (#tag.Bck
) do
1367 res
= res
..tag.Bck
[i
].." "
1369 res
= res
.."\nTime Area\n"
1370 for i
=0, (#tag.MTC
) do
1371 res
= res
..tag.MTC
[i
].." "
1375 -- Master Token specific
1376 elseif (tag.Type
~="SAM") then
1377 res
= res
.."Master-Token Area\nStamp: "
1378 res
= res
..tag.SSC
.." "
1379 for i
=0, tag.Stamp_len
-2 do
1380 res
= res
..tag.data
[i
].." "
1382 res
=res
.."\nunused payload\n"
1383 for i
=0, (#tag.data
-tag.Stamp_len
-1) do
1384 res
= res
..tag.data
[i
].." "
1386 bytes
=tagToBytes(tag)
1387 local mtcrc
=calcMtCrc(bytes
)
1388 res
=res
.."\nMaster-Token CRC: "
1389 res
= res
..tag.MTC
[1].." ("..((tag.MTC
[1]==mtcrc
) and "valid" or "error")..")"
1392 -- 'Gantner User-Credential' specific
1393 elseif (tag.Type
=="SAM" and (tag.raw
=='08' or tag.raw
=='09')) then
1394 print(acgreen
.."Gantner Detected"..acoff
)
1398 else print(acred
.."no valid Tag in dumpCDF"..acoff
) end
1402 -- dump single segment
1403 function dumpSegment(tag, index
)
1406 local dp
=0 --data-position in table
1407 local res
="" --result
1408 local raw
="" --raw-header
1410 if ( (istable(tag.SEG
[i
])) and tag.Type
=="SAM" and tag.raw
=="9f") then
1411 if (istable(tag.SEG
[i
].raw
)) then
1412 for k
,v
in pairs(tag.SEG
[i
].raw
) do
1418 res
= res
..accyan
.."Segment "..("%02d"):format(tag.SEG
[i
].index
)..acoff
..": "
1419 res
= res
.."raw header: "..string.sub(raw
,0,-2)..", flag="..tag.SEG
[i
].flag
..", (valid="..("%x"):format(tag.SEG
[i
].valid
)..", last="..("%x"):format(tag.SEG
[i
].last
).."), "
1420 res
= res
.."len="..("%04d"):format(tag.SEG
[i
].len
)..", WRP="..("%02x"):format(tag.SEG
[i
].WRP
)..", WRC="..("%02x"):format(tag.SEG
[i
].WRC
)..", "
1421 res
= res
.."RD="..("%02x"):format(tag.SEG
[i
].RD
)..", CRC="..tag.SEG
[i
].crc
.." "
1422 res
= res
.."("..(checkSegmentCrc(tag, i
) and acgreen
.."valid" or acred
.."error") ..acoff
..")"
1427 if ((tag.SEG
[i
].WRC
>0)) then
1428 res
= res
.."\nWRC protected area:\n"
1429 for i2
=dp
, dp
+tag.SEG
[i
].WRC
-1 do
1430 res
= res
..tag.SEG
[i
].data
[i2
].." "
1436 if (tag.SEG
[i
].WRP
>tag.SEG
[i
].WRC
) then
1437 res
= res
.."\nRemaining write protected area:\n"
1438 for i2
=dp
, dp
+(tag.SEG
[i
].WRP
-tag.SEG
[i
].WRC
)-1 do
1439 res
= res
..tag.SEG
[i
].data
[i2
].." "
1445 if (#tag.SEG
[i
].data
-dp
>0) then
1446 res
= res
.."\nRemaining segment payload:\n"
1447 for i2
=dp
, #tag.SEG
[i
].data
-2 do
1448 res
= res
..tag.SEG
[i
].data
[dp
].." "
1451 if (tag.SEG
[i
].kgh
) then
1452 res
= res
..tag.SEG
[i
].data
[dp
].." (KGH: "..(checkKghCrc(tag, i
) and acgreen
.."valid" or acred
.."error") ..acoff
..")"
1453 else res
= res
..tag.SEG
[i
].data
[dp
] end
1458 return print(acred
.."Segment not found"..acoff
)
1463 -- return bytes 'sstrat' to 'send' from a table
1464 function dumpTable(tab
, header
, tstart
, tend
)
1466 for i
=tstart
, tend
do
1467 res
=res
..tab
[i
].." "
1469 if (#header
== 0) then
1472 return (header
.." #"..(tend
-tstart
+1).."\n"..res
)
1477 -- dump 3rd Party Cash
1478 function dump3rdPartyCash1(tag , seg
)
1479 local uid
=tag.MCD
..tag.MSN0
..tag.MSN1
..tag.MSN2
1480 local stamp
=tag.SEG
[seg
].data
[0].." "..tag.SEG
[seg
].data
[1].." "..tag.SEG
[seg
].data
[2]
1481 local datastamp
=tag.SEG
[seg
].data
[20].." "..tag.SEG
[seg
].data
[21].." "..tag.SEG
[seg
].data
[22]
1482 local balance
=tonumber(tag.SEG
[seg
].data
[32]..tag.SEG
[seg
].data
[33] ,16)
1483 local balancecrc
=utils
.Crc8Legic(uid
..tag.SEG
[seg
].data
[32]..tag.SEG
[seg
].data
[33])
1484 local mirror
=tonumber(tag.SEG
[seg
].data
[35]..tag.SEG
[seg
].data
[36] ,16)
1485 local mirrorcrc
=utils
.Crc8Legic(uid
..tag.SEG
[seg
].data
[35]..tag.SEG
[seg
].data
[36])
1486 local lastbal0
=tonumber(tag.SEG
[seg
].data
[39]..tag.SEG
[seg
].data
[40] ,16)
1487 local lastbal1
=tonumber(tag.SEG
[seg
].data
[41]..tag.SEG
[seg
].data
[42] ,16)
1488 local lastbal2
=tonumber(tag.SEG
[seg
].data
[43]..tag.SEG
[seg
].data
[44] ,16)
1491 -- display decoded/known stuff
1492 print("\n------------------------------")
1493 print("Tag-ID:\t\t "..uid
)
1494 print("Stamp:\t\t "..stamp
)
1495 print("UID-Mapping: \t\t"..("%06d"):format(tonumber(tag.SEG
[seg
].data
[46]..tag.SEG
[seg
].data
[47]..tag.SEG
[seg
].data
[48], 16)))
1496 print("------------------------------")
1497 print("checksum 1:\t\t "..tag.SEG
[seg
].data
[31].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[seg
].data
, "", 19, 30)), tag.SEG
[seg
].data
[31])..")")
1498 print("checksum 2:\t\t "..tag.SEG
[seg
].data
[34].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[seg
].data
, "", 32, 33)), tag.SEG
[seg
].data
[34])..")")
1499 print("checksum 3:\t\t "..tag.SEG
[seg
].data
[37].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[seg
].data
, "", 35, 36)), tag.SEG
[seg
].data
[37])..")")
1501 print("checksum 4:\t\t "..tag.SEG
[seg
].data
[55].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[seg
].data
, "", 46, 54)), tag.SEG
[seg
].data
[55])..")")
1502 print("checksum 5:\t\t "..tag.SEG
[seg
].data
[62].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[seg
].data
, "", 56, 61)), tag.SEG
[seg
].data
[62])..")")
1503 print("checksum 6:\t\t "..tag.SEG
[seg
].data
[73].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[seg
].data
, "", 63, 72)), tag.SEG
[seg
].data
[73])..")")
1504 print("checksum 7:\t\t "..tag.SEG
[seg
].data
[89].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[seg
].data
, "", 74, 88)), tag.SEG
[seg
].data
[89])..")")
1505 print("------------------------------")
1506 print(string.format("Balance:\t\t %3.2f", balance
/100).." ".."("..compareCrc(balancecrc
, tag.SEG
[seg
].data
[34])..")")
1507 print(string.format("Shadow:\t\t\t %3.2f", mirror
/100).." ".."("..compareCrc(balancecrc
, tag.SEG
[seg
].data
[37])..")")
1508 print("------------------------------")
1509 print(string.format("History 1:\t\t %3.2f", lastbal0
/100))
1510 print(string.format("History 2:\t\t %3.2f", lastbal1
/100))
1511 print(string.format("History 3:\t\t %3.2f", lastbal2
/100))
1512 print("------------------------------\n")
1516 -- dump Legic-Cash data
1517 function dumpLegicCash(tag, x
)
1519 if istable(tag.SEG
[x
]) == false then return end
1521 io
.write("in Segment "..tag.SEG
[x
].index
.." :\n")
1522 print("--------------------------------\n\tLegic-Cash Values\n--------------------------------")
1523 local limit
, curr
, balance
, rid
, tcv
1524 -- currency of balance & limit
1525 curr
=string.upper(tag.SEG
[x
].data
[8]..tag.SEG
[x
].data
[9])
1526 if currency
[curr
] ~= nil then
1527 curr
= currency
[curr
]
1530 limit
=string.format("%4.2f", tonumber(tag.SEG
[x
].data
[10]..tag.SEG
[x
].data
[11]..tag.SEG
[x
].data
[12], 16)/100)
1532 balance
=string.format("%4.2f", tonumber(tag.SEG
[x
].data
[15]..tag.SEG
[x
].data
[16]..tag.SEG
[x
].data
[17], 16)/100)
1533 -- reader-id who wrote last transaction
1534 rid
=tonumber(tag.SEG
[x
].data
[18]..tag.SEG
[x
].data
[19]..tag.SEG
[x
].data
[20], 16)
1535 -- transaction counter value
1536 tcv
=tonumber(tag.SEG
[x
].data
[29], 16)
1537 print("Currency:\t\t "..curr
)
1538 print("Limit:\t\t\t "..limit
)
1539 print("Balance:\t\t "..balance
)
1540 print("Transaction Counter:\t "..tcv
)
1541 print("Reader-ID:\t\t "..rid
.."\n--------------------------------\n")
1546 function print3rdPartyCash1(tag, x
)
1548 if istable(tag.SEG
[x
]) == false then return end
1550 local uid
=tag.MCD
..tag.MSN0
..tag.MSN1
..tag.MSN2
1551 print("\n\t\tStamp : "..dumpTable(tag.SEG
[x
].data
, "", 0 , 2))
1552 print("\t\tBlock 0: "..dumpTable(tag.SEG
[x
].data
, "", 3 , 18))
1554 print("\t\tBlock 1: "..dumpTable(tag.SEG
[x
].data
, "", 19, 30))
1555 print("checksum 1: Tag-ID .. Block 1 => LegicCrc8 = "..tag.SEG
[x
].data
[31].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[x
].data
, "", 19, 30)), tag.SEG
[x
].data
[31])..")")
1557 print("\t\tBlock 2: "..dumpTable(tag.SEG
[x
].data
, "", 32, 33))
1558 print("checksum 2: Block 2 => LegicCrc8 = "..tag.SEG
[x
].data
[34].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[x
].data
, "", 32, 33)), tag.SEG
[x
].data
[34])..")")
1560 print("\t\tBlock 3: "..dumpTable(tag.SEG
[x
].data
, "", 35, 36))
1561 print("checksum 3: Block 3 => LegicCrc8 = "..tag.SEG
[x
].data
[37].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[x
].data
, "", 35, 36)), tag.SEG
[x
].data
[37])..")")
1563 print("\t\tyet unknown: "..tag.SEG
[x
].data
[38])
1565 print("\t\tHisatory 1: "..dumpTable(tag.SEG
[x
].data
, "", 39, 40))
1566 print("\t\tHisatory 2: "..dumpTable(tag.SEG
[x
].data
, "", 41, 42))
1567 print("\t\tHisatory 3: "..dumpTable(tag.SEG
[x
].data
, "", 43, 44))
1569 print("\t\tyet unknown: "..tag.SEG
[x
].data
[45])
1571 print("\t\tKGH-UID HEX: "..dumpTable(tag.SEG
[x
].data
, "", 46, 48))
1572 print("\t\tBlock 4: "..dumpTable(tag.SEG
[x
].data
, "", 49, 54))
1573 print("checksum 4: Tag-ID .. KGH-UID .. Block 4 => LegicCrc8 = "..tag.SEG
[x
].data
[55].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[x
].data
, "", 46, 54)), tag.SEG
[x
].data
[55])..")")
1575 print("\t\tBlock 5: "..dumpTable(tag.SEG
[x
].data
, "", 56, 61))
1576 print("checksum 5: Tag-ID .. Block 5 => LegicCrc8 = "..tag.SEG
[x
].data
[62].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[x
].data
, "", 56, 61)), tag.SEG
[x
].data
[62])..")")
1578 print("\t\tBlock 6: "..dumpTable(tag.SEG
[x
].data
, "", 63, 72))
1579 print("checksum 6: Tag-ID .. Block 6 => LegicCrc8 = "..tag.SEG
[x
].data
[73].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[x
].data
, "", 63, 72)), tag.SEG
[x
].data
[73])..")")
1581 print("\t\tBlock 7: "..dumpTable(tag.SEG
[x
].data
, "", 74, 88))
1582 print("checksum 7: Tag-ID .. Block 7 => LegicCrc8 = "..tag.SEG
[x
].data
[89].." ("..compareCrc(utils
.Crc8Legic(uid
..dumpTable(tag.SEG
[x
].data
, "", 74, 88)), tag.SEG
[x
].data
[89])..")")
1584 print("\t\tBlock 8: "..dumpTable(tag.SEG
[x
].data
, "", 90, 94))
1587 --- Token related --
1590 function makeToken()
1592 ['Type'] = {"SAM", "SAM63", "SAM64", "IAM", "GAM"},
1593 ['DCF'] = {"60ea", "31fa", "30fa", "80fa", "f0fa"},
1594 ['WRP'] = {"15", "2", "2", "2", "2"},
1595 ['WRC'] = {"01", "02", "02", "00", "00"},
1596 ['RD'] = {"01", "00", "00", "00", "00"},
1597 ['Stamp'] = {"00", "00", "00", "00", "00"},
1598 ['Segment'] = {"0d", "c0", "04", "00", "be", "01", "02", "03", "04", "01", "02", "03", "04"}
1601 for k
, v
in pairs(mt
.Type
) do
1602 ttype
= ttype
..k
..") "..v
.." "
1604 mtq
= tonumber(input("select number for Token-Type\n"..ttype
, '1'), 10)
1605 if (type(mtq
) ~= "number") then return print("selection invalid!")
1606 elseif (mtq
> #mt
.Type
) then return print("selection invalid!")
1607 else print("Token-Type '"..mt
.Type
[mtq
].."' selected") end
1608 local raw
= calcHeaderRaw(mt
.WRP
[mtq
], mt
.WRC
[mtq
], mt
.RD
[mtq
])
1611 bytes
= {"01", "02", "03", "04", "cb", string.sub(mt
.DCF
[mtq
], 0, 2), string.sub(mt
.DCF
[mtq
], 3), raw
,
1612 "00", "00", "00", "00", "00", "00", "00", "00",
1613 "00", "00", "00", "00", "00", "00"}
1615 for i
= 0, #mt
.Segment
do
1616 table.insert(bytes
, mt
.Segment
[i
])
1621 for i
= #bytes
, 1023 do table.insert(bytes
, "00") end
1623 -- if Master-Token -> calc Master-Token-CRC
1624 if (mtq
>1) then bytes
[22] = calcMtCrc(bytes
) end
1626 local tempTag
= createTagTable()
1627 -- remove segment if MasterToken
1628 if (mtq
>1) then tempTag
.SEG
[0] = nil end
1630 return bytesToTag(bytes
, tempTag
)
1635 function editTag(tag)
1636 -- for simulation it makes sense to edit everything
1637 local edit_sim
= "MCD MSN0 MSN1 MSN2 MCC DCFl DCFh WRP WRC RD"
1638 -- on real tags it makes only sense to edit DCF, WRP, WRC, RD
1639 local edit_real
= "DCFl DCFh WRP WRC RD"
1640 if (confirm(acyellow
.."do you want to edit non-writeable values (e.g. for simulation)?"..acoff
)) then
1642 else edit_tag
= edit_real
end
1644 if(istable(tag)) then
1645 for k
,v
in pairs(tag) do
1646 if(type(v
) ~= "table" and type(v
) ~= "boolean" and string.find(edit_tag
, k
)) then
1647 tag[k
] = input("value for: "..accyan
..k
..acoff
, v
)
1651 if (tag.Type
== "SAM") then ttype
= "Header"; else ttype
= "Stamp"; end
1652 if (confirm(acyellow
.."do you want to edit "..ttype
.." Data?"..acoff
)) then
1653 -- master-token specific
1654 if(istable(tag.Bck
) == false) then
1655 -- stamp-data length=(0xfc-DCFh)
1656 -- on MT: SSC holds the Starting Stamp Character (Stamp0)
1657 tag.SSC
=input(ttype
.."0: ", tag.SSC
)
1658 -- rest of stamp-bytes are in tag.data 0..n
1659 for i
=0, (tonumber(0xfc ,10)-("%d"):format('0x'..tag.DCFh
))-2 do
1660 tag.data
[i
] = input(ttype
.. i
+1 ..": ", tag.data
[i
])
1663 --- on credentials byte7 should always be 9f and byte8 ff
1664 -- on Master-Token not (even on SAM63/64 not)
1665 -- tag.SSC=input(ttype.."0: ", tag.SSC)
1666 for i
=0, #tag.data
do
1667 tag.data
[i
] = input(ttype
.. i
..": ", tag.data
[i
])
1672 bytes
= tagToBytes(tag)
1674 --- check data-consistency (calculate tag.raw)
1675 bytes
[8] = calcHeaderRaw(tag.WRP
, tag.WRC
, tag.RD
)
1677 --- Master-Token specific
1678 -- should be triggered if a SAM was converted to a non-SAM (user-Token to Master-Token)
1679 -- or a Master-Token has being edited (also SAM64 & SAM63 - which are in fact Master-Token)
1680 if(tag.Type
~= "SAM" or bytes
[6]..bytes
[7] ~= "60ea") then
1681 -- calc new Master-Token crc
1682 bytes
[22] = calcMtCrc(bytes
)
1684 -- ensure tag.SSC set to 'ff' on credential-token (SAM)
1686 -- if a Master-Token was converted to a Credential-Token
1687 -- lets unset the Time-Area to 00 00 (will contain Stamp-Data on MT)
1692 tag = bytesToTag(bytes
, tag)
1697 -- calculates header-byte (addr 0x07)
1698 function calcHeaderRaw(wrp
, wrc
, rd
)
1699 wrp
= ("%02x"):format(tonumber(wrp
, 10))
1700 rd
= tonumber(rd
, 16)
1701 local res
= ("%02x"):format(tonumber(wrp
, 16)+tonumber(wrc
.."0", 16)+((rd
>0) and tonumber("8"..(rd
-1), 16) or 0))
1706 -- determine TagType (bits 0..6 of DCFlow)
1707 function getTokenType(DCFl
)
1713 --local tt = bbit("0x"..DCFl,0,7)
1714 local tt
= tonumber(DCFl
, 16) & 0x7F
1715 if (tt
>= 0 and tt
<= 47) then tt
= "IAM"
1716 elseif (tt
== 49) then tt
= "SAM63"
1717 elseif (tt
== 48) then tt
= "SAM64"
1718 elseif (tt
>= 50 and tt
<= 111) then tt
= "SAM"
1719 elseif (tt
>= 112 and tt
<= 127) then tt
= "GAM"
1725 -- clear beackup-area of a virtual tag
1726 function clearBackupArea(tag)
1727 for i
=1, #tag.Bck
do
1733 --- Segment related --
1735 -- get segmemnt-data from byte-table
1736 function getSegmentData(bytes
, start
, index
)
1743 ['raw'] = {'00', '00', '00', '00'},
1751 if (bytes
[start
]) then
1754 segment
.index
= index
1755 -- flag = high nibble of byte 1
1756 segment
.flag
= string.sub(bytes
[start
+1],0,1)
1757 -- valid = bit 6 of byte 1
1758 --segment.valid = bbit("0x"..bytes[start+1],6,1)
1759 segment
.valid
= (tonumber(bytes
[start
+1], 16) >> 6) & 0x01
1760 -- last = bit 7 of byte 1
1761 --segment.last = bbit("0x"..bytes[start+1],7,1)
1762 segment
.last
= (tonumber(bytes
[start
+1], 16) >> 7) & 0x01
1763 -- len = (byte 0)+(bit0-3 of byte 1)
1764 --segment.len = tonumber(bbit("0x"..bytes[start+1],0,4)..bytes[start],16)
1765 segment
.len
= ((tonumber(bytes
[start
+1], 16) & 0x0F) << 8) + tonumber(bytes
[start
], 16)
1766 -- raw segment header
1767 segment
.raw
= {padString(bytes
[start
]), padString(bytes
[start
+1]), padString(bytes
[start
+2]), padString(bytes
[start
+3])}
1768 -- wrp (write proteted) = byte 2
1769 segment
.WRP
= tonumber(bytes
[start
+2],16)
1770 -- wrc (write control) - bit 4-6 of byte 3
1771 --segment.WRC = bbit("0x"..bytes[start+3],4,3)
1772 segment
.WRC
= (tonumber(bytes
[start
+3], 16) >> 4) & 0x07
1773 -- rd (read disabled) - bit 7 of byte 3
1774 --segment.RD = bbit("0x"..bytes[start+3],7,1)
1775 segment
.RD
= (tonumber(bytes
[start
+3],16) >> 7) & 0x01
1777 segment
.crc
= padString(bytes
[start
+4])
1778 -- segment-data starts at segment.len - segment.header - segment.crc
1779 for i
=0, (segment
.len
-5) do
1780 segment
.data
[i
]=padString(bytes
[start
+5+i
])
1788 -- get index, start-aadr, length and content
1789 function getSegmentStats(bytes
)
1791 local sValid
, sLast
, sLen
, sStart
, x
1796 -- valid = bit 6 of byte 1
1797 -- sValid = bbit("0x"..bytes[sStart+1],6,1)
1798 sValid
= (tonumber(bytes
[sStart
+1],16) >> 6) & 0x01
1799 -- last = bit 7 of byte 1
1800 --sLast = bbit("0x"..bytes[sStart+1],7,1)
1801 sLast
= (tonumber(bytes
[sStart
+1],16) >> 7) & 0x01
1802 -- len = (byte 0)+(bit0-3 of byte 1)
1803 --sLen = tonumber(bbit("0x"..bytes[sStart+1],0,4)..bytes[sStart],16)
1804 sLen
= ((tonumber(bytes
[start
+1],16) & 0x0F) << 8) + tonumber(bytes
[start
],16)
1805 --print("index: "..("%02d"):format(x).." Len: "..sLen.." start:"..sStart.." end: "..(sStart+sLen-1))
1808 s
['end']=sStart
+sLen
-1
1810 if ( (sStart
+sLen
-1)>sStart
) then
1811 table.insert(sStats
, s
)
1815 until (sLast
==1 or sValid
==0 or x
==126)
1816 if (#sStats
>0 ) then return sStats
1817 else return false; end
1821 -- regenerate segment-header (after edit)
1822 function regenSegmentHeader(segment
)
1824 local raw
= segment
.raw
1826 -- len bit0..7 | len=12bit=low nibble of byte1..byte0
1827 --raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8))
1828 raw
[1] = seg
.len
& 0xFF
1829 -- high nibble of len bit6=valid , bit7=last of byte 1 | ?what are bit 5+6 for? maybe kgh?
1830 --raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),8,4)+bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8))
1831 raW
[2] = (seg
.len
>> 8) & 0xF |
(seg
.valid
>> 6) |
(seg
.last
>> 7)
1833 -- raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8))
1836 raw
[4]=("%02x"):format(tonumber((seg
.WRC
*16)+(seg
.RD
*128),10))
1838 seg
.flag
=string.sub(raw
[2],0,1)
1839 --print(raw[1].." "..raw[2].." "..raw[3].." "..raw[4])
1840 if(#seg
.data
>(seg
.len
-5)) then
1841 print("current payload: ".. #seg
.data
.." - desired payload: ".. seg
.len
-5)
1842 print(acyellow
.."Data-Length has being reduced:"..acgreen
.." auto-removing "..acyellow
.. #seg
.data
-(seg
.len
-5) ..acgreen
.." bytes from Payload!"..acoff
);
1843 for i
=(seg
.len
-5), #seg
.data
-1 do
1844 table.remove(seg
.data
)
1846 elseif (#seg
.data
<(seg
.len
-5)) then
1847 print("current payload: ".. #seg
.data
.." - desired payload: ".. seg
.len
-5)
1848 print(acyellow
.."Data-Length has being extended:"..acgreen
.." auto-adding "..acyellow
..(seg
.len
-5)-#seg
.data
..acgreen
.." bytes to Payload!"..acoff
);
1849 for i
=#seg
.data
, (seg
.len
-5-1) do
1850 table.insert(seg
.data
, '00')
1857 -- edit segment helper
1858 function editSegment(tag, index
)
1860 local edit_possible
="valid len RD WRP WRC Stamp Payload"
1861 if (istable(tag.SEG
[index
])) then
1862 for k
,v
in pairs(tag.SEG
[index
]) do
1863 if(string.find(edit_possible
,k
)) then
1864 tag.SEG
[index
][k
]=tonumber(input(accyan
..k
..acoff
..": ", v
),10)
1867 else print("Segment with Index: "..("%02d"):format(index
).." not found in Tag")
1870 regenSegmentHeader(tag.SEG
[index
])
1871 print("\n"..dumpSegment(tag, index
).."\n")
1876 -- edit Segment Data
1877 function editSegmentData(data
, uid
)
1879 if istable(data
) == false then print("no Segment-Data found") end
1881 local lc
= check4LegicCash(data
, uid
)
1884 data
[i
]=input(accyan
.."Data"..i
..acoff
..": ", data
[i
])
1887 data
= fixLegicCash(data
, uid
)
1893 -- list available segmets in virtual tag
1894 function segmentList(tag)
1897 if (istable(tag.SEG
[0])) then
1898 for i
=0, #tag.SEG
do
1899 res
= res
.. tag.SEG
[i
].index
.. " "
1902 else print("no Segments found in Tag")
1908 -- helper to selecting a segment
1909 function selectSegment(tag)
1911 if (istable(tag.SEG
[0])) then
1912 print("availabe Segments:\n"..segmentList(tag))
1913 sel
=input("select Segment: ", '00')
1914 sel
=tonumber(sel
,10)
1915 if (sel
) then return sel
1918 print("\nno Segments found")
1925 function addSegment(tag)
1933 ['raw'] = {'0d', '40', '04', '00'},
1941 if (istable(tag.SEG
[0])) then
1942 tag.SEG
[#tag.SEG
].last
=0
1943 table.insert(tag.SEG
, segment
)
1945 tag.SEG
[#tag.SEG
].data
[i
]=("%02x"):format(i
)
1947 tag.SEG
[#tag.SEG
].index
=("%02d"):format(#tag.SEG
)
1950 print("no Segment-Table found")
1955 -- get only the stamp-bytes of a segment
1956 function getSegmentStamp(seg
, bytes
)
1959 --- the 'real' stamp on MIM is not really easy to tell for me since the 'data-block' covers stamp0..stampn+data0..datan
1960 -- there a no stamps longer than 7 bytes & they are write-protected by default , and I have not seen user-credntials
1961 -- with stamps smaller 3 bytes (except: Master-Token)
1962 -- WRP -> Read/Write Protection
1963 -- WRC -> Read/Write Condition
1964 -- RD depends on WRC - if WRC > 0 and RD=1: only reader with matching #WRC of Stamp-bytes in their Database have Read-Access to the Tag
1965 if (seg
.WRP
<7) then stamp_len
=(seg
.WRP
) end
1966 for i
=1, (stamp_len
) do
1967 stamp
=stamp
..seg
.data
[i
-1]
1970 stamp
=str2bytes(stamp
)
1972 else return stamp
end
1976 -- edit stamp of a segment
1977 function editStamp(new_stamp
, uid
, data
)
1978 local stamp
=str2bytes(new_stamp
)
1979 for i
=0, #stamp
-1 do
1982 -- now fill in stamp
1983 for i
=0, (string.len(new_stamp
)/2)-1 do
1986 return fix3rdPartyCash1(uid
, data
)
1990 -- autoselect special/known segments
1991 function autoSelectSegment(tag, s
)
1992 local uid
=tag.MCD
..tag.MSN0
..tag.MSN1
..tag.MSN2
1995 io
.write("autoSelect ")
1996 --- search for desired segment-type
1997 -- 3rd Party Segment
1998 if (s
=="3rdparty") then
2002 res
=check43rdPartyCash1(uid
, tag.SEG
[x
].data
)
2003 until ( res
or x
==0 )
2005 -- Legic-Cash Segment
2006 if (s
=="legiccash") then
2010 res
=check4LegicCash(tag.SEG
[x
].data
, uid
)
2011 until ( res
or x
==0 )
2016 io
.write("\nautoselected Index: "..string.format("%02d", x
).."\n")
2021 io
.write(acyellow
.."no Segment found\n"..acoff
)
2026 -- delete segment (except segment 00)
2027 function delSegment(tag, index
)
2028 if (istable(tag.SEG
[0])) then
2030 if (type(index
)=="string") then index
=tonumber(index
,10) end
2032 table.remove(tag.SEG
, index
)
2033 for i
=0, #tag.SEG
do
2034 tag.SEG
[i
].index
=("%02d"):format(i
)
2037 if(istable(tag.SEG
[#tag.SEG
])) then tag.SEG
[#tag.SEG
].last
=1 end
2043 -- edit uid 3rd party cash
2044 function edit3rdUid(mapid
, uid
, data
)
2045 mapid
=("%06x"):format(tonumber(mapid
, 10))
2046 data
[46]=string.sub(mapid
, 1 ,2)
2047 data
[47]=string.sub(mapid
, 3 ,4)
2048 data
[48]=string.sub(mapid
, 5 ,6)
2049 return fix3rdPartyCash1(uid
, data
)
2053 -- edit balance 3rd party cash
2054 function edit3rdCash(new_cash
, uid
, data
)
2055 new_cash
=("%04x"):format(new_cash
)
2056 data
[32]=string.sub(new_cash
, 0,2)
2057 data
[33]=string.sub(new_cash
, 3,4)
2058 data
[34]=("%02x"):format(utils
.Crc8Legic(uid
..new_cash
))
2059 data
[35]=string.sub(new_cash
, 0,2)
2060 data
[36]=string.sub(new_cash
, 3,4)
2061 data
[37]=("%02x"):format(utils
.Crc8Legic(uid
..new_cash
))
2062 data
[39]=string.sub(new_cash
, 0,2)
2063 data
[40]=string.sub(new_cash
, 3,4)
2068 return fix3rdPartyCash1(uid
, data
)
2072 -- edit 3rd-party cash
2073 function edit3rdPartyCash1(tag, x
)
2074 local uid
=tag.MCD
..tag.MSN0
..tag.MSN1
..tag.MSN2
2075 if (confirm("\nedit Balance?")) then
2076 local new_cash
=input("enter new Balance≤\nwithout comma and without currency-sign! (0-65535)", "100")
2077 tag.SEG
[x
].data
=edit3rdCash(new_cash
, uid
, tag.SEG
[x
].data
)
2079 -- change User-ID (used for online-account-mapping)
2080 if (confirm("\nedit UserID-Mapping?")) then
2081 local new_mapid
=input("enter new UserID (6-digit value)", "012345")
2082 tag.SEG
[x
].data
=edit3rdUid(new_mapid
, uid
, tag.SEG
[x
].data
)
2084 if (confirm("\nedit Stamp?")) then
2085 local new_stamp
=input("enter new Stamp", getSegmentStamp(tag.SEG
[x
]))
2086 tag.SEG
[x
].data
=editStamp(new_stamp
, uid
, tag.SEG
[x
].data
)
2087 new_stamp
=getSegmentStamp(tag.SEG
[x
], 'true')
2088 print("stamp_bytes: "..#new_stamp
)
2089 -- replace stamp in 'block 1' also
2090 io
.write("editing stamp in Block 1 also ")
2091 for i
=20, (20+#new_stamp
-1) do
2092 tag.SEG
[x
].data
[i
]=new_stamp
[i
-19]
2096 -- fix known checksums
2097 tag.SEG
[x
].data
=fix3rdPartyCash1(uid
, tag.SEG
[x
].data
)
2104 function editLegicCash(data
, uid
)
2105 local limit
, curr
, balance
, rid
, tcv
2106 -- currency of balance & limit
2107 curr
=currency
[data
[8]..data
[9]]
2109 limit
=string.format("%4.2f", tonumber(data
[10]..data
[11]..data
[12], 16)/100)
2111 balance
=string.format("%4.2f", tonumber(data
[15]..data
[16]..data
[17], 16)/100)
2112 -- reader-id who wrote last transaction
2113 rid
=tonumber(data
[18]..data
[19]..data
[20], 16)
2114 -- transaction counter value
2115 tcv
=tonumber(data
[29], 16)
2118 if (confirm(accyan
.."change Currency?"..acoff
)) then
2119 for k
, v
in pairs(currency
) do io
.write(k
.. " = " .. v
.. "\n") end
2120 curr
=input(accyan
.."enter the 4-digit Hex for the new Currency:"..acoff
, data
[8]..data
[9])
2121 data
[8]=string.sub(curr
, 1, 2)
2122 data
[9]=string.sub(curr
, 3, 4)
2126 if (confirm(accyan
.."change Limit?"..acoff
)) then
2127 limit
=string.format("%06x", input(accyan
.."enter the Decimal for the new Limit:"..acoff
, limit
))
2128 data
[10]=string.sub(limit
, 1, 2)
2129 data
[11]=string.sub(limit
, 3, 4)
2130 data
[12]=string.sub(limit
, 5, 6)
2134 if (confirm(accyan
.."change Balance?"..acoff
)) then
2135 balance
=string.format("%06x", input(accyan
.."enter the Decimal for the new Balance:"..acoff
, balance
))
2136 print("Balance: "..balance
)
2137 data
[15]=string.sub(balance
, 1, 2)
2138 data
[16]=string.sub(balance
, 3, 4)
2139 data
[17]=string.sub(balance
, 5, 6)
2142 -- edit transaction-counter
2143 if (confirm(accyan
.."change Transaction-Counter?"..acoff
)) then
2144 tcv
=string.format("%02x", input(accyan
.."enter the 4-digit Hex for the new Currency:"..acoff
, data
[29]))
2149 if (confirm(accyan
.."change Last-Reader-ID?"..acoff
)) then
2150 rid
=string.format("%06x", input(accyan
.."enter the Decimal for the new Balance:"..acoff
, rid
))
2151 print("Balance: "..balance
)
2152 data
[18]=string.sub(rid
, 1, 2)
2153 data
[19]=string.sub(rid
, 3, 4)
2154 data
[20]=string.sub(rid
, 5, 6)
2157 return fixLegicCash(data
, uid
)
2161 -- chack for signature of a 'Legic-Cash-Segment'
2162 function check4LegicCash(data
, uid
)
2164 print("### #data: " .. #data
)
2166 local stamp_len
=(#data
-25)
2168 for i
=0, stamp_len
-1 do
2169 stamp
=stamp
..data
[i
].." "
2171 if (data
[7]=="01") then
2172 if (("%04x"):format(utils
.Crc16Legic(dumpTable(data
, "", 0, 12), uid
)) == data
[13]..data
[14]) then
2173 if (("%04x"):format(utils
.Crc16Legic(dumpTable(data
, "", 15, 20), uid
)) == data
[21]..data
[22]) then
2174 if (("%04x"):format(utils
.Crc16Legic(dumpTable(data
, "", 23, 29), uid
)) == data
[30]..data
[31]) then
2175 io
.write(accyan
.."Legic-Cash Segment detected "..acoff
)
2186 -- chack for signature of a '3rd Party Cash-Segment' - not all bytes know until yet !!
2187 function check43rdPartyCash1(uid
, data
)
2189 -- too explicit checking will avoid fixing ;-)
2190 if (string.find(compareCrc(utils
.Crc8Legic(uid
..dumpTable(data
, "", 19, 30)), data
[31]),"valid")) then
2191 --if (compareCrc(utils.Crc8Legic(uid..data[32]..data[33]), data[34])=="valid") then
2192 --if (compareCrc(utils.Crc8Legic(uid..data[35]..data[36]), data[37])=="valid") then
2193 --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)), data[62])=="valid") then
2194 --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)), data[89])=="valid") then
2195 io
.write(accyan
.."3rd Party Cash-Segment detected "..acoff
)
2208 -- build segmentCrc credentials
2209 function segmentCrcCredentials(tag, segid
)
2210 if (istable(tag.SEG
[0])) then
2211 local cred
= tag.MCD
..tag.MSN0
..tag.MSN1
..tag.MSN2
2212 cred
= cred
..tag.SEG
[segid
].raw
[1]..tag.SEG
[segid
].raw
[2]..tag.SEG
[segid
].raw
[3]..tag.SEG
[segid
].raw
[4]
2214 else return print(acyellow
.."Master-Token / unsegmented Tag!"..acoff
) end
2218 -- build kghCrc credentials
2219 function kghCrcCredentials(tag, segid
)
2220 if (istable(tag) and istable(tag.SEG
[0])) then
2222 if (type(segid
)=="string") then segid
=tonumber(segid
,10) end
2223 if (segid
>0) then x
='93' end
2224 local cred
= tag.MCD
..tag.MSN0
..tag.MSN1
..tag.MSN2
..("%02x"):format(tag.SEG
[segid
].WRP
)
2225 cred
= cred
..("%02x"):format(tag.SEG
[segid
].WRC
)..("%02x"):format(tag.SEG
[segid
].RD
)..x
2226 for i
=0, #tag.SEG
[segid
].data
-2 do
2227 cred
= cred
..tag.SEG
[segid
].data
[i
]
2234 -- compare two bytes
2235 function compareCrc(calc
, guess
)
2236 calc
=("%02x"):format(calc
)
2237 if (calc
==guess
) then return acgreen
.."valid"..acoff
2238 else return acred
.."error "..acoff
..calc
.."!="..guess
end
2243 function compareCrc16(calc
, guess
)
2244 calc
=("%04x"):format(calc
)
2245 if (calc
==guess
) then return acgreen
.."valid"..acoff
2246 else return acred
.."error "..acoff
..calc
.."!="..guess
end
2250 -- repair / fix crc's of a 'Legic-Cash-Segment'
2251 function fixLegicCash(data
, uid
)
2252 if(#data
==32 and data
[7]=="01") then
2253 local crc1
, crc2
, crc3
2254 -- set shadow-balance equal to balance
2258 -- set shadow-last-reader to last-reader
2262 -- calculate all crc's
2263 crc1
=("%04x"):format(utils
.Crc16Legic(dumpTable(data
, "", 0, 12), uid
))
2264 crc2
=("%04x"):format(utils
.Crc16Legic(dumpTable(data
, "", 15, 20), uid
))
2265 crc3
=("%04x"):format(utils
.Crc16Legic(dumpTable(data
, "", 23, 29), uid
))
2267 data
[13]=string.sub(crc1
, 1, 2)
2268 data
[14]=string.sub(crc1
, 3, 4)
2269 data
[21]=string.sub(crc2
, 1, 2)
2270 data
[22]=string.sub(crc2
, 3, 4)
2271 data
[30]=string.sub(crc3
, 1, 2)
2272 data
[31]=string.sub(crc3
, 3, 4)
2278 -- repair / fix (yet known) crc's of a '3rd Party Cash-Segment' - not all bytes know until yet !!
2279 function fix3rdPartyCash1(uid
, data
)
2282 data
[31]=("%02x"):format(utils
.Crc8Legic(uid
..dumpTable(data
, "", 19, 30)))
2284 data
[34]=("%02x"):format(utils
.Crc8Legic(uid
..data
[32]..data
[33]))
2286 data
[37]=("%02x"):format(utils
.Crc8Legic(uid
..data
[35]..data
[36]))
2288 data
[55]=("%02x"):format(utils
.Crc8Legic(uid
..dumpTable(data
, "", 46, 54)))
2290 data
[62]=("%02x"):format(utils
.Crc8Legic(uid
..dumpTable(data
, "", 56, 61)))
2292 data
[73]=("%02x"):format(utils
.Crc8Legic(uid
..dumpTable(data
, "", 63, 72)))
2294 data
[89]=("%02x"):format(utils
.Crc8Legic(uid
..dumpTable(data
, "", 74, 88)))
2300 -- calculate Master-Token crc
2301 function calcMtCrc(bytes
)
2303 local cmd
=bytes
[1]..bytes
[2]..bytes
[3]..bytes
[4]..bytes
[7]..bytes
[6]..bytes
[8]
2304 local len
=(tonumber(0xfc ,10)-("%d"):format('0x'..bytes
[7]))
2308 local res
=("%02x"):format(utils
.Crc8Legic(cmd
))
2313 -- calculate segmentCRC for a given segment
2314 function calcSegmentCrc(tag, segid
)
2315 if (istable(tag.SEG
[0])) then
2316 -- check if a 'Kaber Group Header' exists
2317 local data
=segmentCrcCredentials(tag, segid
)
2318 return ("%02x"):format(utils
.Crc8Legic(data
))
2323 -- calcuate kghCRC for a given segment
2324 function calcKghCrc(tag, segid
)
2325 if (istable(tag.SEG
[0])) then
2326 -- check if a 'Kaber Group Header' exists
2328 local data
=kghCrcCredentials(tag, segid
)
2329 return ("%02x"):format(utils
.Crc8Legic(data
))
2334 -- check all segment-crc
2335 function checkAllSegCrc(tag)
2336 if (istable(tag.SEG
[0])) then
2337 for i
=0, #tag.SEG
do
2338 crc
=calcSegmentCrc(tag, i
)
2341 else return print(acyellow
.."Master-Token / unsegmented Tag"..acoff
) end
2345 -- check all segmnet-crc
2346 function checkAllKghCrc(tag)
2347 if (istable(tag.SEG
[0])) then
2348 for i
=0, #tag.SEG
do
2349 crc
=calcKghCrc(tag, i
)
2350 if (tag.SEG
[i
].kgh
) then
2351 tag.SEG
[i
].data
[#tag.SEG
[i
].data
-1]=crc
2358 -- validate segmentCRC for a given segment
2359 function checkSegmentCrc(tag, segid
)
2360 local data
=segmentCrcCredentials(tag, segid
)
2361 if (("%02x"):format(utils
.Crc8Legic(data
))==tag.SEG
[segid
].crc
) then
2368 -- validate kghCRC to segment in tag-table
2369 function checkKghCrc(tag, segid
)
2370 if (type(tag.SEG
[segid
])=='table') then
2371 if (tag.data
[3]=="11" and tag.raw
=="9f" and tag.SSC
=="ff") then
2372 local data
=kghCrcCredentials(tag, segid
)
2373 if (("%02x"):format(utils
.Crc8Legic(data
))==tag.SEG
[segid
].data
[tag.SEG
[segid
].len
-5-1]) then return true; end
2374 else return false; end
2375 else oops(acred
.."'Kaba Group header' detected but no Segment-Data found"..ansocolors
.reset
) end
2379 -- helptext for modify-mode
2380 function modifyHelp()
2383 Data I/O Segment Manipulation Token-Data
2384 ----------------- -------------------- ---------------------
2385 ]]..acy
..[[rt]]..acr
..[[ => read Tag ]]..acy
..[[as]]..acr
..[[ => add Segment ]]..acy
..[[mt]]..acr
..[[ => make Token
2386 ]]..acy
..[[wt]]..acr
..[[ => write Tag ]]..acy
..[[es]]..acr
..[[ => edit Segment Header ]]..acy
..[[et]]..acr
..[[ => edit Token data
2387 ]]..acy
..[[ed]]..acr
..[[ => edit Segment Data ]]..acy
..[[tk]]..acr
..[[ => toggle KGH-Flag
2388 File I/O ]]..acy
..[[rs]]..acr
..[[ => remove Segment
2389 ----------------- ]]..acy
..[[cc]]..acr
..[[ => check Segment-CRC
2390 ]]..acy
..[[lf]]..acr
..[[ => load bin File ]]..acy
..[[ck]]..acr
..[[ => check KGH
2391 ]]..acy
..[[sf]]..acr
..[[ => save eml/bin File ]]..acy
..[[ds]]..acr
..[[ => dump Segments
2392 ]]..acy
..[[xf]]..acr
..[[ => xor to File
2395 Virtual Tags tagMap (partial) known Segments
2396 -------------------------------- --------------------- ---------------------------
2397 ]]..acy
..[[ct]]..acr
..[[ => copy mainTag to backupTag ]]..acy
..[[mm]]..acr
..[[ => make (new) Map ]]..acy
..[[dlc]]..acr
..[[ => dump Legic-Cash
2398 ]]..acy
..[[tc]]..acr
..[[ => copy backupTag to mainTag ]]..acy
..[[em]]..acr
..[[ => edit Map submenu ]]..acy
..[[elc]]..acr
..[[ => edit Legic-Cash
2399 ]]..acy
..[[tt]]..acr
..[[ => switch mainTag & backupTag ]]..acy
..[[lm]]..acr
..[[ => load map from file ]]..acy
..[[d3p]]..acr
..[[ => dump 3rd-Party-Cash
2400 ]]..acy
..[[di]]..acr
..[[ => dump mainTag ]]..acy
..[[sm]]..acr
..[[ => save map to file ]]..acy
..[[e3p]]..acr
..[[ => edit 3rd-Party-Cash
2401 ]]..acy
..[[do]]..acr
..[[ => dump backupTag
2403 ]]..acy
..[[h]]..acr
..[[ => this help ]]..acy
..[[q]]..acr
..[[ => quit
2409 -- modify Tag (interactive)
2410 function modifyMode()
2411 local i
, backupTAG
, outTAG
, inTAG
, outfile
, infile
, sel
, segment
, bytes
, outbytes
, tagMap
2417 print(" Version: "..acgreen
..version
..acr
);
2419 print("\n".."tags im Memory: "..(istable(inTAG
) and ((currentTag
=='inTAG') and acgreen
.."*mainTAG"..acoff
or "mainTAG") or "").." "..(istable(backupTAG
) and ((currentTag
=='backupTAG') and acgreen
.."*backupTAG"..acoff
or "backupTAG") or ""))
2423 -- read real Tag with PM3 into virtual 'mainTAG'
2424 ["rt"] = function(x
)
2425 inTAG
=readFromPM3();
2429 -- write content of virtual 'mainTAG' to real Tag with PM3
2430 ["wt"] = function(x
)
2434 -- copy mainTAG to backupTAG
2435 ["ct"] = function(x
)
2436 print(accyan
.."copy mainTAG to backupTAG"..acoff
)
2437 backupTAG
=deepCopy(inTAG
)
2440 -- copy backupTAG to mainTAG
2441 ["tc"] = function(x
)
2442 print(accyan
.."copy backupTAG to mainTAG"..acoff
)
2443 inTAG
=deepCopy(backupTAG
)
2446 -- toggle between mainTAG and backupTAG
2447 ["tt"] = function(x
)
2448 -- copy main to temp
2449 outTAG
=deepCopy(inTAG
)
2450 -- copy backup to main
2451 inTAG
=deepCopy(backupTAG
)
2452 print(accyan
.."toggle to "..accyan
..((currentTag
=='inTAG') and "backupTAG" or "mainTAG")..acoff
)
2453 if(currentTag
=="inTAG") then currentTag
='backupTAG'
2454 else currentTag
='inTAG' end
2455 -- copy temp (main) to backup
2456 backupTAG
=deepCopy(outTAG
)
2459 -- load file into mainTAG
2460 ["lf"] = function(x
)
2461 if (x
and not x
=="" and type(x
)=='string' and file_check(x
)) then
2464 filename
= input("enter filename: ", "legic.temp")
2466 inTAG
=readFile(filename
)
2467 -- check for existing tagMap
2468 local res
, path
= file_check(filename
..".map")
2470 if(confirm(accyan
.."Mapping-File for "..acoff
..path
..accyan
.." found - load it also?"..acoff
)) then
2471 tagMap
=loadTagMap(filename
..".map")
2476 -- save values of mainTAG to a file (xored with MCC of mainTAG)
2477 ["sf"] = function(x
)
2478 if istable(inTAG
) then
2479 outfile
= input("enter filename:", "hf-legic-"..inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
)
2480 bytes
= tagToBytes(inTAG
)
2481 --bytes=xorBytes(bytes, inTAG.MCC)
2483 writeFile(bytes
, outfile
)
2488 -- save values of mainTAG to a file (xored with 'specific' MCC)
2489 ["xf"] = function(x
)
2490 if istable(inTAG
) then
2491 outfile
= input("enter filename:", "hf-legic-"..inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
)
2492 crc
= input("enter new crc: ('00' for a plain dump)", inTAG
.MCC
)
2493 print("obfuscate with: "..crc
)
2494 bytes
=tagToBytes(inTAG
)
2497 writeFile(bytes
, outfile
)
2502 -- dump mainTAG (and all Segments)
2503 ["di"] = function(x
)
2504 if (istable(inTAG
)) then
2505 local uid
=inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2506 if(istable(inTAG
.SEG
[0])) then
2507 for i
=0, #inTAG
.SEG
do
2508 if(check43rdPartyCash1(uid
, inTAG
.SEG
[i
].data
)) then
2509 io
.write(accyan
.."in Segment index: "..inTAG
.SEG
[i
].index
..acoff
.. "\n")
2510 elseif(check4LegicCash(inTAG
.SEG
[i
].data
, uid
)) then
2511 io
.write(accyan
.."in Segment index: "..inTAG
.SEG
[i
].index
..acoff
.."\n")
2513 lci
=inTAG
.SEG
[i
].index
;
2517 print("\n"..dumpTag(inTAG
).."\n")
2518 if (lc
) then actions
["dlc"](lci
) end
2523 -- dump backupTAG (and all Segments)
2524 ["do"] = function(x
) if (istable(backupTAG
)) then print("\n"..dumpTag(backupTAG
).."\n") end end,
2526 -- create a empty tagMap
2527 ["mm"] = function(x
)
2528 -- clear existing tagMap and init
2529 if (istable(inTAG
)) then
2535 ["em"] = function(x
)
2536 if (istable(inTAG
)==false) then
2537 if (confirm("no mainTAG in memory!\nread from PM3?")) then
2539 elseif (confirm("load from File?")) then
2544 if (istable(tagMap
)==false) then actions
['mm']() end
2546 tagMap
=editTagMap(inTAG
, tagMap
)
2550 ["sm"] = function(x
)
2551 if (istable(tagMap
)) then
2552 if (istable(tagMap
) and #tagMap
.mappings
>0) then
2553 print(accyan
.."Map contains "..acoff
..#tagMap
..accyan
.." mappings"..acoff
)
2554 saveTagMap(tagMap
, input(accyan
.."enter filename:"..acoff
, "Legic.map"))
2556 print(acyellow
.."no mappings in tagMap!"..acoff
)
2562 ["lm"] = function(x
)
2563 tagMap
= loadTagMap(input(accyan
.."enter filename:"..acoff
, "Legic.map"))
2566 -- dump single segment
2567 ["ds"] = function(x
)
2568 if (type(x
)=="string" and string.len(x
)>0) then
2569 sel
= tonumber(x
,10)
2571 sel
= selectSegment(inTAG
)
2573 if (sel
) then print("\n"..(dumpSegment(inTAG
, sel
) or acred
.."no Segments available") ..acoff
.."\n") end
2576 -- edit segment header
2577 ["es"] = function(x
)
2578 if (type(x
)=="string" and string.len(x
)>0) then sel
=tonumber(x
,10)
2579 else sel
=selectSegment(inTAG
) end
2581 if(istable(inTAG
.SEG
[0])) then
2582 inTAG
=editSegment(inTAG
, sel
)
2583 inTAG
.SEG
[sel
]=regenSegmentHeader(inTAG
.SEG
[sel
])
2585 print(acyellow
.."no Segments in Tag"..acoff
)
2591 ["as"] = function(x
)
2592 if (istable(inTAG
.SEG
[0])) then
2593 inTAG
=addSegment(inTAG
)
2594 inTAG
.SEG
[#inTAG
.SEG
-1]=regenSegmentHeader(inTAG
.SEG
[#inTAG
.SEG
-1])
2595 inTAG
.SEG
[#inTAG
.SEG
]=regenSegmentHeader(inTAG
.SEG
[#inTAG
.SEG
])
2596 else print(accyan
.."Master-Token / unsegmented Tag!"..acoff
)
2601 ["rs"] = function(x
)
2602 if (istable(inTAG
.SEG
[0])) then
2603 if (type(x
)=="string" and string.len(x
)>0) then sel
=tonumber(x
,10)
2604 else sel
=selectSegment(inTAG
) end
2605 inTAG
=delSegment(inTAG
, sel
)
2606 for i
=0, #inTAG
.SEG
do
2607 inTAG
.SEG
[i
]=regenSegmentHeader(inTAG
.SEG
[i
])
2612 -- edit data-portion of single segment
2613 ["ed"] = function(x
)
2614 if (type(x
) == "string" and string.len(x
)>0) then sel
=tonumber(x
,10)
2615 else sel
= selectSegment(inTAG
) end
2616 if (istable(inTAG
.SEG
[sel
])) then
2617 local uid
= inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2618 inTAG
.SEG
[sel
].data
= editSegmentData(inTAG
.SEG
[sel
].data
, uid
)
2622 -- edit Tag (MCD, MSN, MCC etc.)
2623 ["et"] = function(x
)
2624 if (istable(inTAG
)) then
2629 -- make (dummy) Token
2630 ["mt"] = function(x
) inTAG
=makeToken(); actions
.di() end,
2632 -- fix segment-crc on single segment
2633 ["ts"] = function(x
)
2634 if (type(x
)=="string" and string.len(x
)>0) then sel
=tonumber(x
,10)
2635 else sel
=selectSegment(inTAG
) end
2636 regenSegmentHeader(inTAG
.SEG
[sel
])
2639 -- toggle kgh-crc-flag on a single segment
2640 ["tk"] = function(x
)
2641 if (istable(inTAG
) and istable(inTAG
.SEG
[0])) then
2642 if (type(x
)=="string" and string.len(x
)>0) then
2643 sel
= tonumber(x
,10)
2645 sel
= selectSegment(inTAG
)
2647 if(inTAG
.SEG
[sel
].kgh
) then
2648 inTAG
.SEG
[sel
].kgh
= false
2650 inTAG
.SEG
[sel
].kgh
= true
2655 -- calculate LegicCrc8
2657 if (type(x
)=="string" and string.len(x
)>0) then
2658 print(("%02x"):format(utils
.Crc8Legic(x
)))
2663 ["xb"] = function(x
)
2666 -- print string for LegicCrc8-calculation about single segment
2667 ["xc"] = function(x
)
2668 if (istable(inTAG
) and istable(inTAG
.SEG
[0])) then
2669 if (type(x
)=="string" and string.len(x
)>0) then
2670 sel
= tonumber(x
,10)
2672 sel
= selectSegment(inTAG
)
2674 print("k "..kghCrcCredentials(inTAG
, sel
))
2678 -- fix legic-cash checksums
2679 ["flc"] = function(x
)
2680 if (type(x
)=="string" and string.len(x
)>0) then
2683 x
= selectSegment(inTAG
)
2685 local uid
=inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2686 inTAG
.SEG
[x
].data
=fixLegicCash(inTAG
.SEG
[x
].data
, uid
)
2689 -- edit legic-cash values fixLegicCash(data, uid)
2690 ["elc"] = function(x
)
2691 x
=autoSelectSegment(inTAG
, "legiccash")
2692 local uid
=inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2693 inTAG
.SEG
[x
].data
=editLegicCash(inTAG
.SEG
[x
].data
, uid
)
2696 -- dump legic-cash human-readable
2697 ["dlc"] = function(x
)
2698 -- if segment index was user defined
2699 if (type(x
)=="string" and string.len(x
)>0) then
2701 print(string.format("User-Selected Index %02d", x
))
2703 -- or try to find match
2704 x
= autoSelectSegment(inTAG
, "legiccash")
2707 dumpLegicCash(inTAG
, x
)
2710 -- dump 3rd-party-cash-segment
2711 ["d3p"] = function(x
)
2712 local uid
=inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2713 -- if segment index was user defined
2714 if (type(x
)=="string" and string.len(x
)>0) then
2716 print(string.format("User-Selected Index %02d", x
))
2718 -- or try to find match
2719 x
= autoSelectSegment(inTAG
, "3rdparty")
2721 if (istable(inTAG
) and istable(inTAG
.SEG
[x
]) and inTAG
.SEG
[x
].len
== 100) then
2722 uid
=inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2723 if (check43rdPartyCash1(uid
, inTAG
.SEG
[x
].data
)) then
2724 dump3rdPartyCash1(inTAG
, x
)
2729 -- dump 3rd-party-cash-segment (raw blocks and checksums over 'known areas')
2730 ["r3p"] = function(x
)
2731 local uid
=inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2732 -- if segment index was user defined
2733 if (type(x
)=="string" and string.len(x
)>0) then
2735 print(string.format("User-Selected Index %02d", x
))
2737 -- or try to find match
2738 x
= autoSelectSegment(inTAG
, "3rdparty")
2740 print3rdPartyCash1(inTAG
, x
)
2743 -- edit 3rd-party-cash-segment values (Balance, Mapping-UID, Stamp)
2744 ["e3p"] = function(x
)
2745 local uid
=inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2746 -- if segment index was user defined
2747 if (type(x
)=="string" and string.len(x
)>0) then
2749 print(string.format("User-Selected Index %02d", x
))
2752 -- or try to find match
2753 x
= autoSelectSegment(inTAG
, "3rdparty")
2755 if (istable(inTAG
) and istable(inTAG
.SEG
[x
]) and inTAG
.SEG
[x
].len
== 100) then
2756 inTAG
=edit3rdPartyCash1(inTAG
, x
)
2757 dump3rdPartyCash1(inTAG
, x
)
2761 -- force fixing 3rd-party-checksums
2762 ["f3p"] = function(x
)
2763 if(type(x
)=="string" and string.len(x
)>=2) then
2766 x
= selectSegment(inTAG
)
2768 if (istable(inTAG
.SEG
[x
])) then
2769 local uid
=inTAG
.MCD
..inTAG
.MSN0
..inTAG
.MSN1
..inTAG
.MSN2
2770 inTAG
.SEG
[x
].data
=fix3rdPartyCash1(uid
, inTAG
.SEG
[x
].data
)
2774 -- get stamp from single segment
2775 ["gs"] = function(x
)
2776 if(type(x
)=="string" and string.len(x
)>=2) then
2779 x
= selectSegment(inTAG
)
2781 local stamp
=getSegmentStamp(inTAG
.SEG
[x
])
2782 print("Stamp : "..stamp
)
2783 stamp
=str2bytes(stamp
)
2784 print("length: "..#stamp
)
2788 ["c6"] = function(x
) local crc16
=string.format("%4.04x", utils
.Crc16(x
))
2789 print(string.sub(crc16
, 0,2).." "..string.sub(crc16
, 3,4))
2792 -- check & fix segments-crc of all segments
2793 ["cc"] = function(x
) if (istable(inTAG
)) then checkAllSegCrc(inTAG
) end end,
2795 -- set backup-area-bytes to '00'
2796 ["cb"] = function(x
)
2797 if (istable(inTAG
)) then
2798 print(accyan
.."purge BackupArea"..acoff
)
2799 inTAG
=clearBackupArea(inTAG
)
2803 -- check and fix all segments inTAG.SEG[x].kgh toggled 'on'
2804 ["ck"] = function(x
) if (istable(inTAG
)) then checkAllKghCrc(inTAG
) end end,
2806 -- check and fix all segments inTAG.SEG[x].kgh toggled 'on'
2807 ["tac"] = function(x
)
2808 if (colored_output
) then
2809 colored_output
=false
2813 load_colors(colored_output
)
2817 -- default message / prompt
2818 ic
=input("Legic command? ('"..acy
.."h"..acr
.."' for help - '"..acy
.."q"..acr
.."' for quit)", acy
.."h"..acr
)
2819 -- command actions decisions (first match, longer commands before shorter)
2820 if (type(actions
[string.lower(string.sub(ic
,0,3))])=='function') then
2821 actions
[string.lower(string.sub(ic
,0,3))](string.sub(ic
,5))
2822 elseif (type(actions
[string.lower(string.sub(ic
,0,2))])=='function') then
2823 actions
[string.lower(string.sub(ic
,0,2))](string.sub(ic
,4))
2824 elseif (type(actions
[string.lower(string.sub(ic
,0,1))])=='function') then
2825 actions
[string.lower(string.sub(ic
,0,1))](string.sub(ic
,3))
2829 until (string.sub(ic
,0,1)=="q")
2835 -- set init colors/switch (can be toggled with 'tac' => 'toggle ansicolors')
2836 load_colors(colored_output
)
2837 if (#args
== 0 ) then modifyMode() end
2839 local inTAG
, backupTAG
, outTAG
, outfile
, interactive
, crc
2843 -- just a spacer for better readability
2846 for o
, a
in getopt
.getopt(args
, 'hrmi:do:c:') do
2848 if o
== "h" then return help(); end
2849 -- read tag from PM3
2850 if o
== "r" then inTAG
=readFromPM3() end
2852 if o
== "i" then inTAG
=readFile(a
) end
2854 if o
== "d" then dfs
=true end
2855 -- interacive modifying
2860 -- xor (e.g. for clone or plain file)
2872 -- file conversion (output to file)
2873 if ofs
== false then return end
2875 -- dump infile / tag-read
2877 print("-----------------------------------------")
2878 print(dumpTag(inTAG
))
2881 bytes
=tagToBytes(inTAG
)
2883 -- xor will be done in function writeFile
2884 -- with the value of byte[5]
2892 writeFile(bytes
, outfile
)
2894 --- read real tag into virtual tag
2895 -- inTAG=readFromPM3() end
2896 --- or simply use the bytes that where wriiten
2897 inTAG
=bytesToTag(bytes
, inTAG
)
2900 print("-----------------------------------------")
2901 print(dumpTag(inTAG
))