]> git.zerfleddert.de Git - proxmark3-svn/blame - client/scripts/legic.lua
ADD: `analyse nuid` - generates NUID 4byte from a UID 7byte. Mifare Classic Ev1...
[proxmark3-svn] / client / scripts / legic.lua
CommitLineData
733eb420 1--[[
e0530dbc 2if it don't works with you tag-layout - be so kind and let me know ;-)
3
4Tested on Tags with those Layouts:
790e8eae 5
733eb420 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 +----+----+----+----+----+----+----+----+
16 0x20|UID1|UID2|kghC|
17 +----+----+----+
4e8fa8b4 18MCD = Manufacturer ID
19MSN = Manufacturer SerialNumber
2060 ea = DCF Low + DCF high
219f = raw byte which holds the bits for OLE,WRP,WRC,RD
22ff = unknown but important
2300 = unimportant
2411 = unknown but important
25Bck = header-backup-area
2600 00 = Year (00 = 2000) & Week (not important)
27Seg = Segment Header
28SegC = Crc8 over the Segment Header
29Stp = Stamp (could be more as 4 - up to 7)
30UID = dec User-ID for online-Mapping
31kghC = crc8 over MCD + MSN0..MSN2 + UID
32
33
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 +----+----+----+----+----+
46STP = Stamp (seems to be always 7 bytes)
4701 = unknown but important
48CUR = currency in HEX (ISO 4217)
49LIM = Cash-Limit
50CHK = crc16 over byte-addr 0x05..0x12
51BAL = Balance
52LRB = ID of the reader that changed the balance
53CHK = crc16 over BAL + LRB
54SHD = shadow Balance
55LRS = ID of the reader that changed the shadow balance (?? should be always the same as LRB)
56CV = Counter value for transactions
57CHK = crc16 over SHD + LRS + CV
e0530dbc 58
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
66 +----+
67MCD = Manufacturer ID
68MSN = Manufacturer SerialNumber
6960 ea = DCF Low + DCF high
7008 = raw byte which holds the bits for OLE,WRP,WRC,RD
71Stp = Stamp (could be more as 4 - up to 7)
72Dat = Online-Mapping Data
73uCRC = crc8 over addr 0x00..0x03+0x07..0x0E
74
75
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 +----+----+----+----+----+----+
84MCD = Manufacturer ID
85MSN = Manufacturer SerialNumber
8660 ea = DCF Low + DCF high
8708 = raw byte which holds the bits for OLE,WRP,WRC,RD
88Stp = Stamp (could be more as 4 - up to 7)
89Dat = Online-Mapping Data
90CRC1 = crc8 over addr 0x00..0x03+0x07..0x0E (special 'gantner crc8')
91CRC2 = MCD + MSB0..2+ addr 0x06 + addr 0x05 + addr 0x07 + Stamp (regular Master-Token-CRC)
733eb420 92--]]
93
94example = "script run legic"
95author = "Mosci"
790e8eae 96version = "1.0.3"
97
733eb420 98desc =
99[[
100
101This script helps you to read, create and modify Legic Prime Tags (MIM22, MIM256, MIM1024)
102it's kinda interactive with following commands in three categories:
103
790e8eae 104 Data I/O Segment Manipulation Token-Data
105 ----------------- -------------------- -----------------
106 rt => read Tag as => add Segment mt => make Token
107 wt => write Tag es => edit Segment Header et => edit Token data
108 ed => edit Segment Data tk => toggle KGH-Flag
109 File I/O rs => remove Segment
110 ----------------- cc => check Segment-CRC
111 lf => load File ck => check KGH
112 sf => save File ds => dump Segments
113 xf => xor to File
114
115
116 (partially) known Segments Virtual Tags Script Output
117 --------------------------- ------------------------------- ------------------------
118 dlc => dump Legic-Cash ct => copy mainTag to backupTag tac => toggle ansicolors
119 elc => edit Legic-Cash tc => copy backupTag to mainTag
120 d3p => dump 3rd-Party-Cash tt => switch mainTag & backupTag
121 e3p => edit 3rd-Party-Cash di => dump mainTag
122 do => dump backupTag
123
4e8fa8b4 124
733eb420 125
733eb420 126 rt: 'read tag' - reads a tag placed near to the PM3
127 wt: 'write tag' - writes the content of the 'virtual inTag' to a tag placed near to th PM3
128 without the need of changing anything - MCD,MSN,MCC will be read from the tag
129 before and applied to the output.
790e8eae 130
131 lf: 'load file' - load a (xored) file from the local Filesystem into the 'virtual inTag'
132 sf: 'save file' - saves the 'virtual inTag' to the local Filesystem (xored with Tag-MCC)
133 xf: 'xor file' - saves the 'virtual inTag' to the local Filesystem (xored with choosen MCC - use '00' for plain values)
134
733eb420 135 ct: 'copy tag' - copy the 'virtual Tag' to a second 'virtual TAG' - not usefull yet, but inernally needed
136 tc: 'copy tag' - copy the 'second virtual Tag' to 'virtual TAG' - not usefull yet, but inernally needed
790e8eae 137 tt: 'toggle tag' - copy mainTag to BackupTag and backupTag to mainTag
138
139 di: 'dump mainTag' - shows the current content of the 'virtual Tag'
140 do: 'dump backupTag' - shows the current content of the 'virtual outTag'
733eb420 141 ds: 'dump Segments' - will show the content of a selected Segment
142 as: 'add Segment' - will add a 'empty' Segment to the inTag
143 es: 'edit Segment' - edit the Segment-Header of a selected Segment (len, WRP, WRC, RD, valid)
144 all other Segment-Header-Values are either calculated or not needed to edit (yet)
7f0cb92e 145 ed: 'edit data' - edit the Data of a Segment (ADF-Aera / Stamp & Payload specific Data)
146 et: 'edit Token' - edit Data of a Token (CDF-Area / SAM, SAM64, SAM63, IAM, GAM specific Data)
147 mt: 'make Token' - create a Token 'from scratch' (guided)
733eb420 148 rs: 'remove segment' - removes a Segment (except Segment 00, but this can be set to valid=0 for Master-Token)
149 cc: 'check Segment-CRC'- checks & calculates (if check failed) the Segment-CRC of all Segments
150 ck: 'check KGH-CRC' - checks the and calculates a 'Kaba Group Header' if one was detected
151 'Kaba Group Header CRC calculation'
152 tk: 'toggle KGH' - toglle the (script-internal) flag for kgh-calculation for a segment
153 xc: 'etra c' - show string that was used to calculate the kgh-crc of a segment
733eb420 154
790e8eae 155dlc: 'dump Legic-Cash' - show balance and checksums of a Legic-Cash Segment
156elc: 'edit Legic-Cash' - edit values of a Legic-Cash Segment
157
158d3p: 'dump 3rd Party' - show balance, history and checksums of a (yet) unknown 3rd-Party Cash Segment
159e3p: 'edit 3rd Party' - edit Data in 3rd-Party Cash Segment
160
161tac: 'toggle ansicolors'- switch on and off the colored text-output of this script
162 default can be changed by setting the variable 'colored_output' to false
733eb420 163]]
4e8fa8b4 164currentTag="inTAG"
790e8eae 165
7f0cb92e 166---
167-- requirements
790e8eae 168local utils = require('utils')
169local getopt = require('getopt')
170local ansicolors = require('ansicolors')
733eb420 171
7f0cb92e 172---
173-- global variables / defines
733eb420 174local bxor = bit32.bxor
175local bbit = bit32.extract
176local input = utils.input
177local confirm = utils.confirm
178
790e8eae 179---
180-- init ansicolor-values & ansicolors switch
181local colored_output = true
182local acoff = ""
183local acgreen= ""
184local accyan = ""
185local acred = ""
186local acyellow = ""
187local acblue = ""
188local acmagenta = ""
189
190--- Helper ---
191---
192-- default colors (change to whatever you want)
193function load_colors(onoff)
194 if (onoff) then
195 -- colors
196 acgreen = ansicolors.green
197 accyan = ansicolors.cyan
198 acred = ansicolors.red
199 acyellow= ansicolors.yellow
200 acblue = ansicolors.blue
201 acmagenta= ansicolors.magenta
202 acoff = ansicolors.reset
203 else
204 -- 'no color'
205 acgreen = ""
206 accyan = ""
207 acred = ""
208 acyellow= ""
209 acblue = ""
210 acmagenta= ""
211 acoff = ""
212 end
213end
214
215---
4e8fa8b4 216-- curency-codes for Legic-Cash-Segments (ISO 4217)
217local currency = {
218 ["03d2"]="EUR",
219 ["0348"]="USD",
220 ["033A"]="GBP",
221 ["02F4"]="CHF"
222}
223
7f0cb92e 224---
733eb420 225-- This is only meant to be used when errors occur
226function oops(err)
790e8eae 227 print(acred.."ERROR: "..acoff ,err)
733eb420 228 return nil, err
229end
230
231---
232-- Usage help
233function help()
234 print(desc)
790e8eae 235 print("Version: "..version)
236 print("Example usage: "..example)
733eb420 237end
238
239---
240-- table check helper
241function istable(t)
242 return type(t) == 'table'
243end
244
245---
790e8eae 246-- creates a 'deep copy' of a table (a=b only references)
247function deepCopy(object)
248 local lookup_table = {}
249 local function _copy(object)
250 if type(object) ~= "table" then
251 return object
252 elseif lookup_table[object] then
253 return lookup_table[object]
254 end
255 local new_table = {}
256 lookup_table[object] = new_table
257 for index, value in pairs(object) do
258 new_table[_copy(index)] = _copy(value)
259 end
260 return setmetatable(new_table, getmetatable(object))
261 end
262 return _copy(object)
263end
264
265---
7f0cb92e 266-- xor single byte
733eb420 267function xorme(hex, xor, index)
268 if ( index >= 23 ) then
269 return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) ))
270 else
271 return hex
272 end
273end
274
275---
276-- (de)obfuscate bytes
277function xorBytes(inBytes, crc)
278 local bytes = {}
279 for index = 1, #inBytes do
280 bytes[index] = xorme(inBytes[index], crc, index)
281 end
282 if (#inBytes == #bytes) then
283 -- replace crc
284 bytes[5] = string.sub(crc,-2)
285 return bytes
286 else
287 print("error: byte-count missmatch")
288 return false
289 end
290end
291
292---
293-- check availability of file
294function file_check(file_name)
295 local file_found=io.open(file_name, "r")
296 if file_found==nil then
297 return false
298 else
299 return true
300 end
301end
302
303---
790e8eae 304-- split csv-string into table
305local function split(str, sep)
306 local sep = sep or ','
307 local fields={}
308 local matchfunc = string.gmatch(str, "([^"..sep.."]+)")
309 if not matchfunc then return {str} end
310 for str in matchfunc do
311 table.insert(fields, str)
733eb420 312 end
790e8eae 313 return fields
733eb420 314end
315
316---
790e8eae 317-- put a string into a bytes-table
318function str2bytes(s)
319 local res={}
320 if (string.len(s)%2~=0) then return print("stamp should be a even hexstring e.g.: deadbeef or 0badc0de") end
321 for i=1, string.len(s), 2 do
322 table.insert(res, string.sub(s,i,(i+1)))
733eb420 323 end
790e8eae 324 return res
733eb420 325end
326
790e8eae 327---
7f0cb92e 328-- put certain bytes into a new table
329function bytesToTable(bytes, bstart, bend)
330 local t={}
331 for i=0, (bend-bstart) do
332 t[i]=bytes[bstart+i]
333 end
334 return t
335end
336
733eb420 337---
338-- read file into table
339function getInputBytes(infile)
340 local line
341 local bytes = {}
342 local fhi,err = io.open(infile)
343 if err then oops("faild to read from file ".. infile); return false; end
344 while true do
345 line = fhi:read()
346 if line == nil then break end
347 for byte in line:gmatch("%w+") do
348 table.insert(bytes, byte)
349 end
350 end
351 fhi:close()
790e8eae 352 if (bytes[7]=='00') then return false end
733eb420 353 print(#bytes .. " bytes from "..infile.." loaded")
354 return bytes
355end
356
357---
733eb420 358-- create tag-table helper
359function createTagTable()
360 local t={
361 ['MCD'] = '00',
362 ['MSN0']= '11',
363 ['MSN1']= '22',
364 ['MSN2']= '33',
365 ['MCC'] = 'cc',
366 ['DCFl']= 'ff',
367 ['DCFh']= 'ff',
368 ['Type']= 'GAM',
369 ['OLE'] = 0,
370 ['Stamp_len']= 18,
371 ['WRP'] = '00',
372 ['WRC'] = '00',
373 ['RD'] = '00',
374 ['raw'] = '9f',
375 ['SSC'] = 'ff',
376 ['data']= {},
377 ['bck'] = {},
378 ['MTC'] = {},
379 ['SEG'] = {}
380 }
381 return t
382end
383
384---
385-- put bytes into tag-table
386function bytesToTag(bytes, tag)
387 if(istable(tag)) then
388 tag.MCD =bytes[1];
389 tag.MSN0=bytes[2];
390 tag.MSN1=bytes[3];
391 tag.MSN2=bytes[4];
392 tag.MCC =bytes[5];
393 tag.DCFl=bytes[6];
394 tag.DCFh=bytes[7];
395 tag.raw =bytes[8];
396 tag.SSC =bytes[9];
397 tag.Type=getTokenType(tag.DCFl);
398 tag.OLE=bbit("0x"..tag.DCFl,7,1)
399 tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4))
400 tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3))
401 tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1))
e0530dbc 402 if (tag.Type=="SAM" and tag.raw=='9f') then
733eb420 403 tag.Stamp_len=(tonumber(0xfc,10)-tonumber(bbit("0x"..tag.DCFh,0,8),10))
e0530dbc 404 elseif (tag.Type=="SAM" and (tag.raw=='08' or tag.raw=='09')) then
405 tag.Stamp_len = tonumber(tag.raw,10)
406 end
733eb420 407 tag.data=bytesToTable(bytes, 10, 13)
408 tag.Bck=bytesToTable(bytes, 14, 20)
409 tag.MTC=bytesToTable(bytes, 21, 22)
410
790e8eae 411 print(acgreen.."Tag-Type: ".. tag.Type..acoff)
733eb420 412 if (tag.Type=="SAM" and #bytes>23) then
413 tag=segmentsToTag(bytes, tag)
790e8eae 414 print(acgreen..(#tag.SEG+1).." Segment(s) found"..acoff)
7f0cb92e 415 -- unsegmented Master-Token
416 -- only tag-data
417 else
418 for i=0, #tag.Bck do
419 table.insert(tag.data, tag.Bck[i])
790e8eae 420 end
7f0cb92e 421 tag.data[#tag.data]=tag.MTC[0]
422 tag.Bck=nil
423 --tag.MTC[0]=tag.MTC[1]
424 --tag.MTC[1]=nil
733eb420 425 end
790e8eae 426 print(accyan..#bytes.." bytes for Tag processed"..acoff)
733eb420 427 return tag
428 end
429 return oops("tag is no table in: bytesToTag ("..type(tag)..")")
430end
431
790e8eae 432---
433-- put segments from byte-table to tag-table
434function segmentsToTag(bytes, tag)
435 if(#bytes>23) then
436 local start=23
437 local i=-1
438 if (istable(tag)) then
439 repeat
440 i=i+1
441 tag.SEG[i]=getSegmentData(bytes, start, ("%02d"):format(i))
442 if (tag.Type=="SAM") then
443 if (checkKghCrc(tag, i)) then tag.SEG[i].kgh=true end
444 end
445 start=start+tag.SEG[i].len
446 until ((tag.SEG[i].valid==0) or tag.SEG[i].last==1 or i==126)
447 return tag
448 else return oops("tag is no table in: segmentsToTag ("..type(tag)..")") end
449 else print("no Segments: must be a MIM22") end
450end
451
733eb420 452---
7f0cb92e 453-- read Tag-Table in bytes-table
454function tagToBytes(tag)
733eb420 455 if (istable(tag)) then
7f0cb92e 456 local bytes = {}
457 local i, i2
458 -- main token-data
459 table.insert(bytes, tag.MCD)
460 table.insert(bytes, tag.MSN0)
461 table.insert(bytes, tag.MSN1)
462 table.insert(bytes, tag.MSN2)
463 table.insert(bytes, tag.MCC)
464 table.insert(bytes, tag.DCFl)
465 table.insert(bytes, tag.DCFh)
466 table.insert(bytes, tag.raw)
467 table.insert(bytes, tag.SSC)
468 -- raw token data
469 for i=0, #tag.data do
470 table.insert(bytes, tag.data[i])
790e8eae 471 end
7f0cb92e 472 -- backup data
473 if(istable(tag.Bck)) then
474 for i=0, #tag.Bck do
475 table.insert(bytes, tag.Bck[i])
733eb420 476 end
790e8eae 477 end
7f0cb92e 478 -- token-create-time / master-token crc
479 for i=0, #tag.MTC do
480 table.insert(bytes, tag.MTC[i])
790e8eae 481 end
7f0cb92e 482 -- process segments
483 if (type(tag.SEG[0])=='table') then
484 for i=0, #tag.SEG do
485 for i2=1, #tag.SEG[i].raw+1 do
486 table.insert(bytes, #bytes+1, tag.SEG[i].raw[i2])
790e8eae 487 end
7f0cb92e 488 table.insert(bytes, #bytes+1, tag.SEG[i].crc)
489 for i2=0, #tag.SEG[i].data-1 do
490 table.insert(bytes, #bytes+1, tag.SEG[i].data[i2])
790e8eae 491 end
733eb420 492 end
733eb420 493 end
7f0cb92e 494 -- fill with zeros
495 for i=#bytes+1, 1024 do
496 table.insert(bytes, i, '00')
497 end
7f0cb92e 498 return bytes
499 end
500 return oops("tag is no table in tagToBytes ("..type(tag)..")")
733eb420 501end
502
790e8eae 503--- PM3 I/O ---
504---
505-- read from pm3 into virtual-tag
506function readFromPM3()
507 local tag, bytes, infile
508 infile="legic.temp"
509 core.console("hf legic reader")
510 core.console("hf legic save "..infile)
511 tag=readFile(infile)
512 return tag
513end
514
733eb420 515---
790e8eae 516-- write virtual Tag to real Tag
517function writeToTag(tag)
518 local bytes
519 local filename='MylegicClone.hex'
520 local taglen=22
521 if(utils.confirm(acred.."\nplace the (empty) Tag onto the PM3\nand confirm writing to this Tag: "..acoff) == false) then
522 return
523 end
524 -- get used bytes / tag-len
525 if(istable(tag.SEG)) then
526 if (istable(tag.Bck)) then
527 for i=0, #tag.SEG do
528 taglen=taglen+tag.SEG[i].len+5
733eb420 529 end
790e8eae 530 end
531 local uid_old=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
532 -- read new tag into memory so we can xor the new data with the new MCC
533 outTAG=readFromPM3()
534 outbytes=tagToBytes(outTAG)
535 -- copy 'inputbuffer' to 'outputbuffer'
536 tag.MCD = outbytes[1]
537 tag.MSN0 = outbytes[2]
538 tag.MSN1 = outbytes[3]
539 tag.MSN2 = outbytes[4]
540 tag.MCC = outbytes[5]
541 -- recheck all segments-crc/kghcrc (only on a credential)
542 if(istable(tag.Bck)) then
543 checkAllSegCrc(tag)
544 checkAllKghCrc(tag)
545 local uid_new=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
546 for i=0, #tag.SEG do
547 if (check43rdPartyCash1(uid_old, tag.SEG[i].data)) then
548 io.write(accyan.."\nfixing known checksums"..acoff.." ... ")
549 if (fix3rdPartyCash1(uid_new, tag.SEG[i].data)) then
550 io.write(acgreen.." done\n"..acoff)
551 else oops("\nsomething went wrong at the repair of the 3rd-party-cash-segment") end
733eb420 552 end
553 end
790e8eae 554 end
555 bytes=tagToBytes(tag)
556 -- master-token-crc
557 if (tag.Type~="SAM") then bytes[22]=calcMtCrc(bytes) end
558 if (bytes) then
559 print("write temp-file '"..filename.."'")
560 print(accyan)
561 writeFile(bytes, filename)
562 --writeToTag(bytes, taglen, 'MylegicClone.hex')
563 print(acoff)
564 end
565 end
566 -- write data to file
567 if (taglen > 0) then
568 WriteBytes = utils.input(acyellow.."enter number of bytes to write?"..acoff, taglen)
569 -- load file into pm3-buffer
570 if (type(filename)~="string") then filename=input(acyellow.."filename to load to pm3-buffer?"..acoff,"legic.temp") end
571 cmd = 'hf legic load '..filename
572 core.console(cmd)
573 -- write pm3-buffer to Tag
574 for i=0, WriteBytes do
575 if ( i<5 or i>6) then
576 cmd = ('hf legic write 0x%02x 0x01'):format(i)
577 core.console(cmd)
578 --print(cmd)
579 elseif (i == 6) then
580 -- write DCF in reverse order (requires 'mosci-patch')
581 cmd = 'hf legic write 0x05 0x02'
582 print(acgreen..cmd..acoff)
583 core.console(cmd)
584 --print(cmd)
585 else
586 print(acgreen.."skip byte 0x05 - will be written next step"..acoff)
587 end
588 utils.Sleep(0.2)
589 end
590 end
591end
592
593--- File I/O ---
594---
595-- read file into virtual-tag
596function readFile(filename)
597 print(accyan)
598 local bytes = {}
599 local tag = {}
600 if (file_check(filename)==false) then
601 return oops("input file: "..filename.." not found")
602 else
603 bytes = getInputBytes(filename)
604 if (bytes == false) then return oops('couldnt get input bytes')
605 else
606 -- make plain bytes
607 bytes = xorBytes(bytes,bytes[5])
608 print("create virtual tag from ".. #bytes .. " bytes")
609 -- create Tag for plain bytes
610 tag=createTagTable()
611 -- load plain bytes to tag-table
612 print(acoff)
613 tag=bytesToTag(bytes, tag)
614 end
615 end
616 return tag
617end
618
619---
620-- write bytes to file
621function writeFile(bytes, filename)
622 if (filename~='MylegicClone.hex') then
623 if (file_check(filename)) then
624 local answer = confirm("\nthe output-file "..filename.." alredy exists!\nthis will delete the previous content!\ncontinue?")
625 if (answer==false) then return print("user abort") end
626 end
627 end
628 local line
629 local bcnt=0
630 local fho,err = io.open(filename, "w")
631 if err then oops("OOps ... faild to open output-file ".. filename) end
632 bytes=xorBytes(bytes, bytes[5])
633 for i = 1, #bytes do
634 if (bcnt == 0) then
635 line=bytes[i]
636 elseif (bcnt <= 7) then
637 line=line.." "..bytes[i]
638 end
639 if (bcnt == 7) then
640 -- write line to new file
641 fho:write(line.."\n")
642 -- reset counter & line
643 bcnt=-1
644 line=""
645 end
646 bcnt=bcnt+1
647 end
648 fho:close()
649 print("\nwrote ".. #bytes .." bytes to " .. filename)
650 return true
651end
652
653--- Map related ---
654---
655-- make tagMap
656function makeTagMap()
657 local tagMap={}
658 if (#tagMap==0) then
659 tagMap['name']=input(accyan.."enter Name for this Map: "..acoff , "newTagMap")
660 tagMap['mappings']={}
661 tagMap['crc8']={}
662 -- insert fixed Tag-CRC
663 table.insert(tagMap.crc8, {name='TAG-CRC', pos=5, seq={1, 4}})
664 tagMap['crc16']={}
665 end
666 print(accyan.."new tagMap created"..acoff)
667 return tagMap
668end
669
670---
671-- save mapping to file
672function saveTagMap(map, filename)
673 if (string.len(filename)>0) then
674 if (file_check(filename)) then
675 local answer = confirm("\nthe output-file "..filename.." alredy exists!\nthis will delete the previous content!\ncontinue?")
676 if (answer==false) then return print("user abort") end
677 end
678 end
679
680 local line
681 local fho,err = io.open(filename, "w")
682 if err then oops("OOps ... faild to open output-file ".. filename) end
683
684 -- write line to new file
685 for k, v in pairs(map) do
686 if (istable(v)) then
687 for k2, v2 in pairs(v) do
688 if (k=='mappings') then
689 fho:write(k..","..k2..","..v2['name']..","..v2['start']..","..v2['end']..","..((v2['highlight']) and "1" or "0").."\n")
690 elseif (k=="crc8") then
691 local tmp=""
692 tmp=k..","..k2..","..v2['name']..","..v2['pos']..","
693 tmp=tmp..tbl2seqstr(v2['seq'])
694 fho:write(tmp.."\n")
695 end
696 end
697 else
698 fho:write(k..","..v.."\n")
699 end
700 end
701 fho:close()
702 return true
703end
704
705---
706-- toggle higligh
707function toggleHighlight(tbl)
708 if (tbl['highlight']) then tbl['highlight']=false
709 else tbl['highlight']=true end
710 return tbl
711end
712
713---
714-- return table od seqence-string
715function seqstr2tbl(seqstr)
716 local s=split(seqstr)
717 local res={}
718 if (#s>=1) then
719 for sk, sv in pairs(s) do
720 s2=split(sv, '-')
721 if(#s2==2) then
722 table.insert(res, s2[1])
723 table.insert(res, s2[2])
724 end
725 end
726 end
727 return res
728end
729
730---
731-- return sequence-string from table
732function tbl2seqstr(seqtbl)
733 local res=""
734 if (istable(seqtbl)) then
735 for sk, sv in pairs(seqtbl) do
736 res=res..sv..((sk%2==0) and "," or "-")
737 end
738 if (string.sub(res, string.len(res))==",") then
739 res=string.sub(res, 1, string.len(res)-1)
740 end
741 end
742 return res
743end
744
745---
746-- read map-file into map
747function loadTagMap(filename)
748 local map={mappings={}, crc8={}, crc16={}}
749 local m=0
750 local c=0
751 local line, fields
752 local temp={}
753 local offset=0
754 if (file_check(filename)==false) then
755 return oops("input file: "..filename.." not found")
756 else
757 local fhi,err = io.open(filename)
758 while true do
759 line = fhi:read()
760 if line == nil then
761 break
762 else
763 fields=split(line)
764 end
765 if (#fields==2) then
766 if (fields[1]=='offset') then
767 offset=tonumber(fields[2],10)
768 end
769 -- map-name
770 map[fields[1]]=fields[2]
771 elseif (fields[1]=='mappings') then
772 m=m+1
773 temp={}
774 -- mapping
775 temp['name']=fields[3]
776 temp['start']=tonumber(fields[4], 10)
777 temp['end']=tonumber(fields[5], 10)
778 if(temp['start']>22) then
779 temp['start']=temp['start']+offset
780 temp['end']=temp['end']+offset
781 end
782 if (tonumber(fields[6], 10)==1) then temp['highlight']= true
783 else temp['highlight']= false end
784 table.insert(map['mappings'], m, temp)
785 elseif (fields[1]=='crc8') then
786 c=c+1
787 temp={}
788 -- crc8
789 temp['name']=fields[3]
790 temp['pos']=tonumber(fields[4], 10)+offset
791 local s=string.sub(line, string.len(fields[1]..","..fields[2]..","..fields[3]..",")+1, string.len(line))
792 temp['seq']=seqstr2tbl(s)
793 for k, v in pairs(temp['seq']) do
794 if(tonumber(v, 10)>22) then v=tonumber(v, 10)+offset end
795 temp['seq'][k]=tonumber(v, 10)
796 end
797 table.insert(map.crc8, temp)
798 end
799 end
800 fhi:close()
801 end
802 return map
803end
804
805---
806-- dump tagMap (mappings only)
807function dumpTagMap(tag, tagMap)
808 if(#tagMap.mappings>0) then
809 bytes=tagToBytes(tag)
810 local temp
811 local lastend=0
812 -- start display mappings
813 for k, v in pairs(tagMap.mappings) do
814 if ((lastend+1)<v['start']) then
815 print("...")
816 end
817 if (isPosCrc8(tagMap, v['start'])>0) then
818 if ( checkMapCrc8(tagMap, bytes, isPosCrc8(tagMap, v['start']) ) ) then
819 io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acgreen..v['name']..acoff..":")
820 else
821 io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acred..v['name']..acoff..":")
822 end
823 else
824 io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..((v['highlight']) and acmagenta or acyellow)..v['name']..acoff..":")
825 end
826 temp=""
827 for i=((string.len(v['name']))/10), 2 do
828 temp=temp.."\t"
829 end
830 for i=v['start'], v['end'] do
831 temp=temp..bytes[i].." "
832 end
833 print(temp)
834 lastend=v['end']
835 end
836 end
837end
838
839---
840--
841function isPosCrc8(tagMap, pos)
842 local res=0
843 if (#tagMap.crc8>0) then
844 for k, v in pairs(tagMap.crc8) do
845 if(v['pos']==pos) then res=k end
846 end
847 end
848 return res
849end
850
851---
852-- check mapped crc
853function checkMapCrc8(tagMap, bytes, n)
854 local res=false
855 if (#tagMap.crc8>0) then
856 if(istable(tagMap.crc8[n])) then
857 temp=""
858 for k2, v2 in pairs(tagMap.crc8[n]) do
859 if (istable(v2)) then
860 temp=temp..tbl2seqstr(v2)
861 end
862 end
863 local tempres=""
864 local tempres=getSequences(bytes, temp)
865 tempres=("%02x"):format(utils.Crc8Legic(tempres))
866 if (bytes[tagMap.crc8[n]['pos']]==tempres) then
867 res=true
868 end
869 end
870 end
871 return res
872end
873
874---
875-- edit existing Map
876function editTagMap(tag, tagMap)
877 local t = [[
878 Data: dm = show dr = dump raw
879Mappings: im = insert am = add rm = remove
880 CRC8: ac8 = add sc8 = show rc8 = remove
881 : q = exit h = Help
882 ]]
883 --if(#tagMap.mappings==0) then oops("no mappings in tagMap"); return tagMap end
884 print("tagMap edit-mode submenu")
885 repeat
886 x=input('tagMap submenu:', 'h')
887 if (x=='h') then print(t)
888 elseif (x=='dm') then tagMmap=dumpTagMap(tag, tagMap)
889 elseif (x=='dr') then tagMmap=dumpMap(tag, tagMap)
890 elseif (x=='rc8') then
891 if (istable(tagMap.crc8)) then
892 local x1 = selectTableEntry(tagMap.crc8, "select number of CRC8 to remove:")
893 if (istable(tagMap.crc8[x1])) then
894 table.remove(tagMap.crc8, x1)
895 end
896 end
897 elseif (x=='ac8') then
898 local p=tonumber(input("enter byte-addr of crc8", '0'),10)
899 if (p>0) then
900 local i1=input("enter comma-seperated byte-sequences (e.g.: '1-4,23-26')", '1-4,23-26')
901 local s1=split(i1, ',')
902 if (#s1>0) then
903 local temp={seq={}}
904 for k, v in pairs(s1) do
905 v1=split(v, '-')
906 if(#v1==2) then
907 table.insert(temp.seq, v1[1])
908 table.insert(temp.seq, v1[2])
909 end
910 end
911 temp['pos']=p
912 temp['name']=input("enter a name for the CRC8", "CRC "..(#tagMap.crc8+1))
913 table.insert(tagMap.crc8, temp)
914 end
915 end
916 elseif (string.sub(x, 1, 3)=='sc8') then
917 local bytes=tagToBytes(tag)
918 local res, pos
919 -- trigger manually by sc8 <'4digit' checkadd> <'seqeuence-string'>
920 -- e.g.: sc8 0027 1-4,23-36
921 if (string.len(x)>=9) then
922 pos=tonumber(string.sub(x, 5, 8), 10)
923 x=string.sub(x, 9, string.len(x))
924 print("x: "..x.." - pos:"..pos)
925 else
926 x=selectTableEntry(tagMap.crc8, "select CRC:")
927 if(istable(tagMap.crc8[x])) then
928 pos=tagMap.crc8[x]['pos']
929 x=tbl2seqstr(tagMap.crc8[x]['seq'])
930 end
931 end
932 if (type(x)=='string') then
933 res=("%02x"):format(utils.Crc8Legic(getSequences(bytes, x)))
934 print(accyan.."Sequence:\t"..acoff..x)
935 print(accyan.."Bytes:\t\t"..acoff..getSequences(bytes, x))
936 print(accyan.."calculated: "..acoff..res..accyan.." bytes["..pos.."]: "..acoff..bytes[pos].." ("..compareCrc(utils.Crc8Legic(getSequences(bytes, x)), bytes[pos])..")")
937 end
938 elseif (x=="tm") then
939 x=selectTableEntry(tagMap.mappings, "select number of Mapping:")
940 tagMap.mappings[x]=toggleHighlight(tagMap.mappings[x])
941 elseif (x=='am') then tagMap=addMapping(tag, tagMap)
942 elseif (x=='im') then tagMap=addMapping(tag, tagMap, selectTableEntry(tagMap.mappings, "select List-Position for insert:"))
943 elseif (x=='rm') then tagMap=deleteMapping(tag, tagMap)
944 elseif (x=='mas') then tagMap=mapTag(tagMap); tagMap=mapAllSegments(tag, tagMap)
945 elseif (type(actions[string.sub(x, 3)])=='function') then actions[string.sub(x, 3)]()
946 end
947 until x=='q'
948 print("exit sub-Menu")
949 return tagMap
950end
951
952---
953-- dump raw mapped and unmapped
954function dumpMap(tag, tagMap)
955 local dstart=1
956 local dend, cnt
957 local bytes = tagToBytes(tag)
958 local stats = getSegmentStats(bytes)
959 dend=stats[#stats]['end']
960 print(accyan.."Tag uses "..dend.." bytes:"..acoff)
961 for i=dstart, dend do
962 if (check4MappedByte(i, tagMap) and not check4MapCrc8(i, tagMap) and not check4Highlight(i, tagMap)) then io.write(""..acyellow)
963 elseif (check4MapCrc8(i, tagMap)) then
964 if ( checkMapCrc8(tagMap, bytes, isPosCrc8(tagMap, i) ) ) then
965 io.write(""..acgreen)
966 else
967 io.write(""..acred)
968 end
969 else
970 io.write(""..acoff)
971 end
972 -- highlighted mapping
973 if (check4Highlight(i, tagMap)) then io.write(""..acmagenta) end
974
975 io.write(bytes[i])
976 if (i%8==0) then io.write("\n")
977 else io.write(" ") end
978 end
979
980 io.write("\n"..acoff)
981end
982
983---
984-- show bytes used for crc-calculation
985function getSequences(bytes, seqstr)
986 if (type(seqstr)~="string") then seqstr=input("enter comma-seperated sequences (e.g.: '1-4,23-26')", '1-4,23-26') end
987 local seqs=split(seqstr, ',')
988 local res = ""
989 if(#seqs>0) then
990 for k, v in pairs(seqs) do
991 local seq = split(v,'-')
992 if (#seq>=2) then
993 for i=seq[1], seq[2] do
994 res=res..bytes[i].." "
995 end
996 end
997 if(string.len(res)>0) then res=res.." " end
998 end
999 else
1000 oops("no sequence found in '"..seqstr.."'")
1001 end
1002 return res
1003end
1004
1005---
1006-- check if byte-addr is a know crc
1007function check4MapCrc8(addr, tagMap)
1008 local res=false
1009 for i=1, #tagMap.crc8 do
1010 if (addr == tagMap.crc8[i]['pos']) then
1011 res=true
1012 end
1013 end
1014 return res
1015end
1016
1017---
1018-- check if byte-addr is a know crc
1019function check4MapCrc16(addr, tagMap)
1020 local res=false
1021 for i=1, #tagMap.crc16 do
1022 if (addr == tagMap.crc16[i]['pos']) then
1023 res=true
1024 end
1025 end
1026 return res
1027end
1028
1029---
1030-- check if byte is mapped or not
1031function check4MappedByte(addr, tagMap)
1032 local res=false
1033 for _, v in pairs(tagMap.mappings) do
1034 if (addr >= v['start'] and addr <= v['end'] ) then
1035 res= true
1036 end
1037 end
1038 return res
1039end
1040
1041---
1042-- check if byte is highlighted or not
1043function check4Highlight(addr, tagMap)
1044 local res=false
1045 for _, v in pairs(tagMap.mappings) do
1046 if (addr >= v['start'] and addr <= v['end'] ) then
1047 res= v['highlight']
1048 end
1049 end
1050 return res
1051end
1052
1053---
1054-- add interactive mapping
1055function addMapping(tag, tagMap, x)
1056 if (type(x)~="number") then x=#tagMap.mappings+1 end
1057 local bytes=tagToBytes(tag)
1058 local myMapping={}
1059 myMapping['name'] =input(accyan.."enter Maping-Name:"..acoff, string.format("mapping %d", #tagMap.mappings+1))
1060 myMapping['start']=tonumber(input(accyan.."enter start-addr:"..acoff, '1'), 10)
1061 myMapping['end'] =tonumber(input(accyan.."enter end-addr:"..acoff, #bytes), 10)
1062 myMapping['highlight']=confirm("set highlighted")
1063 table.insert(tagMap.mappings, x, myMapping)
1064 return tagMap
1065end
1066
1067---
1068-- delete mapping
1069function deleteMapping(tag, tagMap)
1070 if(#tagMap.mappings>0) then
1071 local d = selectTableEntry(tagMap.mappings, "select number of Mapping to remove:")
1072 if (type(d)=='number') then
1073 table.remove(tagMap.mappings, d)
1074 else oops("deleteMapping: got type = "..type(d).." - expected type = 'number'")
1075 end
1076 end
1077 return tagMap
1078end
1079
1080---
1081-- select a mapping from a tagmap
1082function selectTableEntry(table, action)
1083 if (type(action)~="string") then action="select number of item:" end
1084 for k, v in pairs(table) do
1085 print(accyan..k..acoff.."\t-> "..accyan..v['name']..acoff)
1086 end
1087 local res = tonumber(input(action , 0), 10)
1088 if (istable(table[res])) then
1089 return res
1090 else
1091 return false
1092 end
1093end
1094
1095---
1096-- map all segments
1097function mapAllSegments(tag, tagMap)
1098 local bytes=tagToBytes(tag)
1099 local WRP,WRC,WRPC
1100 segs=getSegmentStats(bytes)
1101 if (istable(segs)) then
1102 for k, v in pairs(segs) do
1103 -- wrp (write proteted) = byte 2
1104 WRP = tonumber(bytes[v['start']+2],16)
1105 -- wrc (write control) - bit 4-6 of byte 3
1106 WRC = tonumber(bbit("0x"..bytes[v['start']+3],4,3),16)
1107 --tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." HDR", v['start'], v['start']+3)
1108 tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." CRC", v['start']+4, v['start']+4, true)
1109 table.insert(tagMap.crc8, {name = 'Segment '..("%02d"):format(v['index']).." CRC", pos=v['start']+4, seq={1,4,v['start'],v['start']+3}} )
1110 if(WRC>WRP) then
1111 WRPC=WRC
1112 tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRC", v['start']+5, v['start']+5+WRC-1, true)
1113 elseif (WRP>WRC and WRC>0) then
1114 WRPC=WRP
1115 tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRC", v['start']+5, v['start']+5+WRC-1, true)
1116 tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRP", v['start']+WRC+5, v['start']+5+WRP-1, true)
1117 else
1118 WRPC=WRP
1119 tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." WRP", v['start']+5, v['start']+5+WRP-1, true)
1120 end
1121 tagMap=mapTokenData(tagMap, 'Segment '..("%02d"):format(v['index']).." data", v['start']+5+WRPC, v['end'], false)
1122
1123 end
1124 print(#segs.." Segments mapped")
1125 else
1126 oops("autoMapSegments failed: no Segments found")
1127 end
1128 return tagMap
1129end
1130
1131---
1132-- map all token data
1133function mapTokenData(tagMap, mname, mstart, mend, mhigh)
1134 --if ( not mhigh ) then mhigh=false end
1135 local myMapping={}
1136 myMapping['name'] =mname
1137 myMapping['start']=mstart
1138 myMapping['end'] =mend
1139 myMapping['highlight']=mhigh
1140 table.insert(tagMap.mappings, myMapping)
1141 return tagMap
1142end
1143
1144---
1145-- map a map
1146function mapTag(tagMap)
1147 tagMap=makeTagMap()
1148 tagMap=mapTokenData(tagMap, 'Tag-ID', 1, 4, true)
1149 tagMap=mapTokenData(tagMap, 'Tag-CRC', 5, 5, false)
1150 tagMap=mapTokenData(tagMap, 'DCF', 6, 7, true)
1151 tagMap=mapTokenData(tagMap, 'THDR-Raw/Stamp-Len', 8, 8, true)
1152 tagMap=mapTokenData(tagMap, 'SSC', 9, 9, true)
1153 tagMap=mapTokenData(tagMap, 'Header', 10, 13, false)
1154 tagMap=mapTokenData(tagMap, 'Backup', 14, 19, true)
1155 tagMap=mapTokenData(tagMap, 'Bck-CRC', 20, 20, false)
1156 tagMap=mapTokenData(tagMap, 'TokenTime', 21, 22, false)
1157 return tagMap
1158end
1159
1160--- Dump Data ---
1161---
1162-- dump virtual Tag-Data
1163function dumpTag(tag)
1164 local i, i2
1165 local res
1166 local dp=0
1167 local raw=""
1168 -- sytstem area
1169 res =acyellow.."\nCDF: System Area"..acoff
1170 res= res.."\n"..dumpCDF(tag)
1171 -- segments (user-token area)
e0530dbc 1172 if(tag.Type=="SAM" and tag.raw=='9f') then
790e8eae 1173 res = res..acyellow.."\n\nADF: User Area"..acoff
1174 for i=0, #tag.SEG do
1175 res=res.."\n"..dumpSegment(tag, i).."\n"
1176 end
1177 end
1178 return res
1179end
1180
1181---
1182-- dump tag-system area
1183function dumpCDF(tag)
1184 local res=""
1185 local i=0
1186 local raw=""
1187 local bytes
1188 if (istable(tag)) then
1189 res = res..accyan.."MCD: "..acoff..tag.MCD..accyan.." MSN: "..acoff..tag.MSN0.." "..tag.MSN1.." "..tag.MSN2..accyan.." MCC: "..acoff..tag.MCC.."\n"
1190 res = res.."DCF: "..tag.DCFl.." "..tag.DCFh..", Token_Type="..tag.Type.." (OLE="..tag.OLE.."), Stamp_len="..tag.Stamp_len.."\n"
1191 res = res.."WRP="..tag.WRP..", WRC="..tag.WRC..", RD="..tag.RD..", raw="..tag.raw..((tag.raw=='9f') and (", SSC="..tag.SSC.."\n") or "\n")
1192
1193 -- credential (end-user tag)
e0530dbc 1194 if (tag.Type=="SAM" and tag.raw=='9f') then
790e8eae 1195 res = res.."Remaining Header Area\n"
1196 for i=0, (#tag.data) do
1197 res = res..tag.data[i].." "
1198 end
1199 res = res.."\nBackup Area\n"
1200 for i=0, (#tag.Bck) do
1201 res = res..tag.Bck[i].." "
1202 end
1203 res = res.."\nTime Area\n"
1204 for i=0, (#tag.MTC) do
1205 res = res..tag.MTC[i].." "
1206 end
1207
e0530dbc 1208
790e8eae 1209 -- Master Token specific
e0530dbc 1210 elseif (tag.Type~="SAM") then
790e8eae 1211 res = res .."Master-Token Area\nStamp: "
1212 res= res..tag.SSC.." "
1213 for i=0, tag.Stamp_len-2 do
1214 res = res..tag.data[i].." "
1215 end
1216 res=res.."\nunused payload\n"
1217 for i=0, (#tag.data-tag.Stamp_len-1) do
1218 res = res..tag.data[i].." "
1219 end
1220 bytes=tagToBytes(tag)
1221 local mtcrc=calcMtCrc(bytes)
1222 res=res.."\nMaster-Token CRC: "
1223 res = res ..tag.MTC[1].." ("..((tag.MTC[1]==mtcrc) and "valid" or "error")..")"
e0530dbc 1224
1225
1226 -- 'Gantner User-Credential' specific
1227 elseif (tag.Type=="SAM" and (tag.raw=='08' or tag.raw=='09')) then
1228 print(acgreen.."Gantner Detected"..acoff)
790e8eae 1229 end
e0530dbc 1230
790e8eae 1231 return res
1232 else print("no valid Tag in dumpCDF") end
1233end
1234
1235---
1236-- dump single segment
1237function dumpSegment(tag, index)
1238 local i=index
1239 local i2
1240 local dp=0 --data-position in table
1241 local res="" --result
1242 local raw="" --raw-header
1243 -- segment
e0530dbc 1244 if ( (istable(tag.SEG[i])) and tag.Type=="SAM" and tag.raw=="9f") then
790e8eae 1245 if (istable(tag.SEG[i].raw)) then
1246 for k,v in pairs(tag.SEG[i].raw) do
1247 raw=raw..v.." "
1248 end
1249 end
1250
1251 -- segment header
1252 res = res..accyan.."Segment "..("%02d"):format(tag.SEG[i].index)..acoff..": "
1253 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).."), "
1254 res = res .."len="..("%04d"):format(tag.SEG[i].len)..", WRP="..("%02x"):format(tag.SEG[i].WRP)..", WRC="..("%02x"):format(tag.SEG[i].WRC)..", "
1255 res = res .."RD="..("%02x"):format(tag.SEG[i].RD)..", CRC="..tag.SEG[i].crc.." "
1256 res = res .."("..(checkSegmentCrc(tag, i) and acgreen.."valid" or acred.."error") ..acoff..")"
1257 raw=""
1258
1259
1260 -- WRC protected
1261 if ((tag.SEG[i].WRC>0)) then
1262 res = res .."\nWRC protected area:\n"
1263 for i2=dp, dp+tag.SEG[i].WRC-1 do
1264 res = res..tag.SEG[i].data[i2].." "
1265 dp=dp+1
1266 end
1267 end
1268
1269 -- WRP mprotected
1270 if (tag.SEG[i].WRP>tag.SEG[i].WRC) then
1271 res = res .."\nRemaining write protected area:\n"
1272 for i2=dp, dp+(tag.SEG[i].WRP-tag.SEG[i].WRC)-1 do
1273 res = res..tag.SEG[i].data[i2].." "
1274 dp=dp+1
1275 end
1276 end
1277
1278 -- payload
1279 if (#tag.SEG[i].data-dp>0) then
1280 res = res .."\nRemaining segment payload:\n"
1281 for i2=dp, #tag.SEG[i].data-2 do
1282 res = res..tag.SEG[i].data[dp].." "
1283 dp=dp+1
1284 end
1285 if (tag.SEG[i].kgh) then
1286 res = res..tag.SEG[i].data[dp].." (KGH: "..(checkKghCrc(tag, i) and acgreen.."valid" or acred.."error") ..acoff..")"
1287 else res = res..tag.SEG[i].data[dp] end
1288 end
1289 dp=0
1290 return res
1291 else
1292 return print("Segment not found")
1293 end
1294end
1295
1296---
1297-- return bytes 'sstrat' to 'send' from a table
1298function dumpTable(tab, header, tstart, tend)
1299 res=""
1300 for i=tstart, tend do
1301 res=res..tab[i].." "
1302 end
1303 if (string.len(header)==0) then return res
1304 else return (header.." #"..(tend-tstart+1).."\n"..res) end
1305end
1306
1307---
1308-- dump 3rd Party Cash
1309function dump3rdPartyCash1(tag , seg)
1310 local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
1311 local stamp=tag.SEG[seg].data[0].." "..tag.SEG[seg].data[1].." "..tag.SEG[seg].data[2]
1312 local datastamp=tag.SEG[seg].data[20].." "..tag.SEG[seg].data[21].." "..tag.SEG[seg].data[22]
1313 local balance=tonumber(tag.SEG[seg].data[32]..tag.SEG[seg].data[33] ,16)
1314 local balancecrc=utils.Crc8Legic(uid..tag.SEG[seg].data[32]..tag.SEG[seg].data[33])
1315 local mirror=tonumber(tag.SEG[seg].data[35]..tag.SEG[seg].data[36] ,16)
1316 local mirrorcrc=utils.Crc8Legic(uid..tag.SEG[seg].data[35]..tag.SEG[seg].data[36])
1317 local lastbal0=tonumber(tag.SEG[seg].data[39]..tag.SEG[seg].data[40] ,16)
1318 local lastbal1=tonumber(tag.SEG[seg].data[41]..tag.SEG[seg].data[42] ,16)
1319 local lastbal2=tonumber(tag.SEG[seg].data[43]..tag.SEG[seg].data[44] ,16)
1320
1321 test=""
1322 -- display decoded/known stuff
1323 print("\n------------------------------")
1324 print("Tag-ID:\t\t "..uid)
1325 print("Stamp:\t\t "..stamp)
1326 print("UID-Mapping: \t\t"..("%06d"):format(tonumber(tag.SEG[seg].data[46]..tag.SEG[seg].data[47]..tag.SEG[seg].data[48], 16)))
1327 print("------------------------------")
1328 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])..")")
1329 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])..")")
1330 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])..")")
1331
1332 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])..")")
1333 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])..")")
1334 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])..")")
1335 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])..")")
1336 print("------------------------------")
1337 print(string.format("Balance:\t\t %3.2f", balance/100).." ".."("..compareCrc(balancecrc, tag.SEG[seg].data[34])..")")
1338 print(string.format("Shadow:\t\t\t %3.2f", mirror/100).." ".."("..compareCrc(balancecrc, tag.SEG[seg].data[37])..")")
1339 print("------------------------------")
1340 print(string.format("History 1:\t\t %3.2f", lastbal0/100))
1341 print(string.format("History 2:\t\t %3.2f", lastbal1/100))
1342 print(string.format("History 3:\t\t %3.2f", lastbal2/100))
1343 print("------------------------------\n")
1344end
1345
1346---
1347-- dump Legic-Cash data
1348function dumpLegicCash(tag, x)
1349 if (istable(tag.SEG[x])) then
1350 io.write("in Segment "..tag.SEG[x].index.." :\n")
1351 print("--------------------------------\n\tLegic-Cash Values\n--------------------------------")
1352 local limit, curr, balance, rid, tcv
1353 -- currency of balance & limit
1354 curr=currency[tag.SEG[x].data[8]..tag.SEG[x].data[9]]
1355 -- maximum balance
1356 limit=string.format("%4.2f", tonumber(tag.SEG[x].data[10]..tag.SEG[x].data[11]..tag.SEG[x].data[12], 16)/100)
1357 -- current balance
1358 balance=string.format("%4.2f", tonumber(tag.SEG[x].data[15]..tag.SEG[x].data[16]..tag.SEG[x].data[17], 16)/100)
1359 -- reader-id who wrote last transaction
1360 rid=tonumber(tag.SEG[x].data[18]..tag.SEG[x].data[19]..tag.SEG[x].data[20], 16)
1361 -- transaction counter value
1362 tcv=tonumber(tag.SEG[x].data[29], 16)
1363 print("Currency:\t\t "..curr)
1364 print("Limit:\t\t\t "..limit)
1365 print("Balance:\t\t "..balance)
1366 print("Transaction Counter:\t "..tcv)
1367 print("Reader-ID:\t\t "..rid.."\n--------------------------------\n")
1368 end
1369end
1370
1371---
1372-- raw 3rd-party
1373function print3rdPartyCash1(tag, x)
1374 if (istable(tag.SEG[x])) then
1375 local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
1376 print("\n\t\tStamp : "..dumpTable(tag.SEG[x].data, "", 0 , 2))
1377 print("\t\tBlock 0: "..dumpTable(tag.SEG[x].data, "", 3 , 18))
1378 print()
1379 print("\t\tBlock 1: "..dumpTable(tag.SEG[x].data, "", 19, 30))
1380 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])..")")
1381 print()
1382 print("\t\tBlock 2: "..dumpTable(tag.SEG[x].data, "", 32, 33))
1383 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])..")")
1384 print()
1385 print("\t\tBlock 3: "..dumpTable(tag.SEG[x].data, "", 35, 36))
1386 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])..")")
1387 print()
1388 print("\t\tyet unknown: "..tag.SEG[x].data[38])
1389 print()
1390 print("\t\tHisatory 1: "..dumpTable(tag.SEG[x].data, "", 39, 40))
1391 print("\t\tHisatory 2: "..dumpTable(tag.SEG[x].data, "", 41, 42))
1392 print("\t\tHisatory 3: "..dumpTable(tag.SEG[x].data, "", 43, 44))
1393 print()
1394 print("\t\tyet unknown: "..tag.SEG[x].data[45])
1395 print()
1396 print("\t\tKGH-UID HEX: "..dumpTable(tag.SEG[x].data, "", 46, 48))
1397 print("\t\tBlock 4: "..dumpTable(tag.SEG[x].data, "", 49, 54))
1398 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])..")")
1399 print()
1400 print("\t\tBlock 5: "..dumpTable(tag.SEG[x].data, "", 56, 61))
1401 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])..")")
1402 print()
1403 print("\t\tBlock 6: "..dumpTable(tag.SEG[x].data, "", 63, 72))
1404 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])..")")
1405 print()
1406 print("\t\tBlock 7: "..dumpTable(tag.SEG[x].data, "", 74, 88))
1407 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])..")")
1408 print()
1409 print("\t\tBlock 8: "..dumpTable(tag.SEG[x].data, "", 90, 94))
1410 end
1411end
1412
1413--- Token related --
1414---
1415-- make token
1416function makeToken()
1417 local mt={
1418 ['Type'] = {"SAM", "SAM63", "SAM64", "IAM", "GAM"},
1419 ['DCF'] = {"60ea", "31fa", "30fa", "80fa", "f0fa"},
1420 ['WRP'] = {"15", "2", "2", "2", "2"},
1421 ['WRC'] = {"01", "02", "02", "00", "00"},
1422 ['RD'] = {"01", "00", "00", "00", "00"},
1423 ['Stamp'] = {"00", "00", "00", "00", "00"},
1424 ['Segment'] = {"0d", "c0", "04", "00", "be", "01", "02", "03", "04", "01", "02", "03", "04"}
1425 }
1426 ttype=""
1427 for k, v in pairs(mt.Type) do
1428 ttype=ttype..k..") "..v.." "
1429 end
1430 mtq=tonumber(input("select number for Token-Type\n"..ttype, '1'), 10)
1431 if (type(mtq)~="number") then return print("selection invalid!")
1432 elseif (mtq>#mt.Type) then return print("selection invalid!")
1433 else print("Token-Type '"..mt.Type[mtq].."' selected") end
1434 local raw=calcHeaderRaw(mt.WRP[mtq], mt.WRC[mtq], mt.RD[mtq])
1435 local mtCRC="00"
1436
1437 bytes={"01", "02", "03", "04", "cb", string.sub(mt.DCF[mtq], 0, 2), string.sub(mt.DCF[mtq], 3), raw,
1438 "00", "00", "00", "00", "00", "00", "00", "00",
1439 "00", "00", "00", "00", "00", "00"}
1440 if (mtq==1) then
1441 for i=0, #mt.Segment do
1442 table.insert(bytes, mt.Segment[i])
1443 end
1444 bytes[9]="ff"
1445 end
1446 -- fill bytes
1447 for i=#bytes, 1023 do table.insert(bytes, "00") end
1448 -- if Master-Token -> calc Master-Token-CRC
1449 if (mtq>1) then bytes[22]=calcMtCrc(bytes) end
1450 local tempTag=createTagTable()
1451 -- remove segment if MasterToken
1452 if (mtq>1) then tempTag.SEG[0]=nil end
1453 return bytesToTag(bytes, tempTag)
1454end
1455
1456---
1457-- edit token-data
1458function editTag(tag)
1459 -- for simulation it makes sense to edit everything
1460 local edit_sim="MCD MSN0 MSN1 MSN2 MCC DCFl DCFh WRP WRC RD"
1461 -- on real tags it makes only sense to edit DCF, WRP, WRC, RD
1462 local edit_real="DCFl DCFh WRP WRC RD"
1463 if (confirm(acyellow.."do you want to edit non-writeable values (e.g. for simulation)?"..acoff)) then
1464 edit_tag=edit_sim
1465 else edit_tag=edit_real end
1466
1467 if(istable(tag)) then
1468 for k,v in pairs(tag) do
1469 if(type(v)~="table" and type(v)~="boolean" and string.find(edit_tag, k)) then
1470 tag[k]=input("value for: "..accyan..k..acoff, v)
1471 end
1472 end
1473
1474 if (tag.Type=="SAM") then ttype="Header"; else ttype="Stamp"; end
1475 if (confirm(acyellow.."do you want to edit "..ttype.." Data?"..acoff)) then
1476 -- master-token specific
1477 if(istable(tag.Bck)==false) then
1478 -- stamp-data length=(0xfc-DCFh)
1479 -- on MT: SSC holds the Starting Stamp Character (Stamp0)
1480 tag.SSC=input(ttype.."0: ", tag.SSC)
1481 -- rest of stamp-bytes are in tag.data 0..n
1482 for i=0, (tonumber(0xfc ,10)-("%d"):format('0x'..tag.DCFh))-2 do
1483 tag.data[i]=input(ttype.. i+1 ..": ", tag.data[i])
1484 end
1485 else
1486 --- on credentials byte7 should always be 9f and byte8 ff
1487 -- on Master-Token not (even on SAM63/64 not)
1488 -- tag.SSC=input(ttype.."0: ", tag.SSC)
7f0cb92e 1489 for i=0, #tag.data do
1490 tag.data[i]=input(ttype.. i ..": ", tag.data[i])
790e8eae 1491 end
1492 end
1493 end
7f0cb92e 1494
1495 bytes=tagToBytes(tag)
1496
1497 --- check data-consistency (calculate tag.raw)
1498 bytes[8]=calcHeaderRaw(tag.WRP, tag.WRC, tag.RD)
1499
1500 --- Master-Token specific
1501 -- should be triggered if a SAM was converted to a non-SAM (user-Token to Master-Token)
1502 -- or a Master-Token has being edited (also SAM64 & SAM63 - which are in fact Master-Token)
1503 if(tag.Type~="SAM" or bytes[6]..bytes[7]~="60ea") then
1504 -- calc new Master-Token crc
1505 bytes[22]=calcMtCrc(bytes)
1506 else
1507 -- ensure tag.SSC set to 'ff' on credential-token (SAM)
1508 bytes[9]='ff'
1509 -- if a Master-Token was converted to a Credential-Token
1510 -- lets unset the Time-Area to 00 00 (will contain Stamp-Data on MT)
1511 bytes[21]='00'
1512 bytes[22]='00'
790e8eae 1513 end
1514
7f0cb92e 1515 tag=bytesToTag(bytes, tag)
733eb420 1516 end
1517end
1518
1519---
7f0cb92e 1520-- calculates header-byte (addr 0x07)
1521function calcHeaderRaw(wrp, wrc, rd)
1522 local res
1523 wrp=("%02x"):format(tonumber(wrp, 10))
1524 rd=tonumber(rd, 16)
1525 res=("%02x"):format(tonumber(wrp, 16)+tonumber(wrc.."0", 16)+((rd>0) and tonumber("8"..(rd-1), 16) or 0))
1526 return res
733eb420 1527end
1528
790e8eae 1529---
1530-- determine TagType (bits 0..6 of DCFlow)
1531function getTokenType(DCFl)
1532 --[[
1533 0x00–0x2f IAM
1534 0x30–0x6f SAM
1535 0x70–0x7f GAM
1536 ]]--
1537 local tt = tonumber(bbit("0x"..DCFl,0,7),10)
1538 if (tt >= 0 and tt <= 47) then tt = "IAM"
1539 elseif (tt == 49) then tt = "SAM63"
1540 elseif (tt == 48) then tt = "SAM64"
1541 elseif (tt >= 50 and tt <= 111) then tt = "SAM"
1542 elseif (tt >= 112 and tt <= 127) then tt = "GAM"
1543 else tt = "???" end
1544 return tt
1545end
1546
733eb420 1547---
790e8eae 1548-- clear beackup-area of a virtual tag
1549function clearBackupArea(tag)
1550 for i=1, #tag.Bck do
1551 tag.Bck[i]='00'
733eb420 1552 end
790e8eae 1553 return tag
733eb420 1554end
1555
790e8eae 1556--- Segment related --
733eb420 1557---
1558-- get segmemnt-data from byte-table
1559function getSegmentData(bytes, start, index)
1560 local segment={
1561 ['index'] = '00',
1562 ['flag'] = 'c',
1563 ['valid'] = 0,
1564 ['last'] = 0,
1565 ['len'] = 13,
1566 ['raw'] = {'00', '00', '00', '00'},
1567 ['WRP'] = 4,
1568 ['WRC'] = 0,
1569 ['RD'] = 0,
1570 ['crc'] = '00',
1571 ['data'] = {},
1572 ['kgh'] = false
1573 }
1574 if (bytes[start]) then
1575 local i
1576 -- #index
1577 segment.index = index
1578 -- flag = high nibble of byte 1
1579 segment.flag = string.sub(bytes[start+1],0,1)
1580 -- valid = bit 6 of byte 1
1581 segment.valid = bbit("0x"..bytes[start+1],6,1)
1582 -- last = bit 7 of byte 1
1583 segment.last = bbit("0x"..bytes[start+1],7,1)
1584 -- len = (byte 0)+(bit0-3 of byte 1)
1585 segment.len = tonumber(bbit("0x"..bytes[start+1],0,4)..bytes[start],16)
1586 -- raw segment header
1587 segment.raw = {bytes[start], bytes[start+1], bytes[start+2], bytes[start+3]}
1588 -- wrp (write proteted) = byte 2
1589 segment.WRP = tonumber(bytes[start+2],16)
1590 -- wrc (write control) - bit 4-6 of byte 3
1591 segment.WRC = tonumber(bbit("0x"..bytes[start+3],4,3),16)
1592 -- rd (read disabled) - bit 7 of byte 3
1593 segment.RD = tonumber(bbit("0x"..bytes[start+3],7,1),16)
1594 -- crc byte 4
1595 segment.crc = bytes[start+4]
1596 -- segment-data starts at segment.len - segment.header - segment.crc
1597 for i=0, (segment.len-5) do
1598 segment.data[i]=bytes[start+5+i]
1599 end
1600 return segment
1601 else return false;
1602 end
1603end
1604
790e8eae 1605---
1606-- get index, start-aadr, length and content
1607function getSegmentStats(bytes)
1608 local sStats = {}
1609 local sValid, sLast, sLen, sStart, x
1610 sStart=23
1611 x=0
1612 repeat
1613 local s={}
1614 -- valid = bit 6 of byte 1
1615 sValid = bbit("0x"..bytes[sStart+1],6,1)
1616 -- last = bit 7 of byte 1
1617 sLast = bbit("0x"..bytes[sStart+1],7,1)
1618 -- len = (byte 0)+(bit0-3 of byte 1)
1619 sLen = tonumber(bbit("0x"..bytes[sStart+1],0,4)..bytes[sStart],16)
1620 --print("index: "..("%02d"):format(x).." Len: "..sLen.." start:"..sStart.." end: "..(sStart+sLen-1))
1621 s['index']=x
1622 s['start']=sStart
1623 s['end']=sStart+sLen-1
1624 s['len']=sLen
1625 if ( (sStart+sLen-1)>sStart ) then
1626 table.insert(sStats, s)
1627 end
1628 sStart=sStart+sLen
1629 x=x+1
1630 until (sLast==1 or sValid==0 or x==126)
1631 if (#sStats>0 ) then return sStats
1632 else return false; end
1633end
733eb420 1634
733eb420 1635---
7f0cb92e 1636-- regenerate segment-header (after edit)
1637function regenSegmentHeader(segment)
1638 local seg=segment
1639 local raw = segment.raw
790e8eae 1640 local i
7f0cb92e 1641 -- len bit0..7 | len=12bit=low nibble of byte1..byte0
1642 raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8))
1643 -- high nibble of len bit6=valid , bit7=last of byte 1 | ?what are bit 5+6 for? maybe kgh?
790e8eae 1644 raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),8,4)+bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8))
7f0cb92e 1645 -- WRP
1646 raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8))
1647 -- WRC + RD
790e8eae 1648 raw[4]=("%02x"):format(tonumber((seg.WRC*16)+(seg.RD*128),10))
7f0cb92e 1649 -- flag
1650 seg.flag=string.sub(raw[2],0,1)
1651 --print(raw[1].." "..raw[2].." "..raw[3].." "..raw[4])
1652 if(#seg.data>(seg.len-5)) then
1653 print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
790e8eae 1654 print(acyellow.."Data-Length has being reduced:"..acgreen.." auto-removing "..acyellow.. #seg.data-(seg.len-5) ..acgreen .." bytes from Payload!"..acoff);
7f0cb92e 1655 for i=(seg.len-5), #seg.data-1 do
1656 table.remove(seg.data)
790e8eae 1657 end
7f0cb92e 1658 elseif (#seg.data<(seg.len-5)) then
1659 print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
790e8eae 1660 print(acyellow.."Data-Length has being extended:"..acgreen.." auto-adding "..acyellow..(seg.len-5)-#seg.data ..acgreen .." bytes to Payload!"..acoff);
1661 for i=#seg.data, (seg.len-5-1) do
7f0cb92e 1662 table.insert(seg.data, '00')
7f0cb92e 1663 end
733eb420 1664 end
790e8eae 1665 return seg
733eb420 1666end
1667
1668---
1669-- edit segment helper
1670function editSegment(tag, index)
1671 local k,v
1672 local edit_possible="valid len RD WRP WRC Stamp Payload"
1673 if (istable(tag.SEG[index])) then
1674 for k,v in pairs(tag.SEG[index]) do
1675 if(string.find(edit_possible,k)) then
790e8eae 1676 tag.SEG[index][k]=tonumber(input(accyan..k..acoff..": ", v),10)
733eb420 1677 end
1678 end
1679 else print("Segment with Index: "..("%02d"):format(index).." not found in Tag")
1680 return false
1681 end
1682 regenSegmentHeader(tag.SEG[index])
1683 print("\n"..dumpSegment(tag, index).."\n")
1684 return tag
1685end
1686
1687---
1688-- edit Segment Data
1689function editSegmentData(data)
4e8fa8b4 1690 local lc=check4LegicCash(data)
1691 io.write("\n")
733eb420 1692 if (istable(data)) then
1693 for i=0, #data-1 do
790e8eae 1694 data[i]=input(accyan.."Data"..i..acoff..": ", data[i])
733eb420 1695 end
4e8fa8b4 1696 if (lc) then data=fixLegicCash(data) end
733eb420 1697 return data
1698 else
1699 print("no Segment-Data found")
1700 end
1701end
1702
1703---
1704-- list available segmets in virtual tag
1705function segmentList(tag)
1706 local i
1707 local res = ""
1708 if (istable(tag.SEG[0])) then
1709 for i=0, #tag.SEG do
1710 res = res .. tag.SEG[i].index .. " "
1711 end
1712 return res
1713 else print("no Segments found in Tag")
1714 return false
1715 end
1716end
1717
1718---
1719-- helper to selecting a segment
1720function selectSegment(tag)
1721 local sel
7f0cb92e 1722 if (istable(tag.SEG[0])) then
733eb420 1723 print("availabe Segments:\n"..segmentList(tag))
1724 sel=input("select Segment: ", '00')
1725 sel=tonumber(sel,10)
1726 if (sel) then return sel
1727 else return '0' end
1728 else
1729 print("\nno Segments found")
1730 return false
1731 end
1732end
790e8eae 1733
733eb420 1734---
1735-- add segment
1736function addSegment(tag)
1737 local i
1738 local segment={
1739 ['index'] = '00',
1740 ['flag'] = 'c',
1741 ['valid'] = 1,
1742 ['last'] = 1,
1743 ['len'] = 13,
1744 ['raw'] = {'0d', '40', '04', '00'},
1745 ['WRP'] = 4,
1746 ['WRC'] = 0,
1747 ['RD'] = 0,
1748 ['crc'] = '00',
1749 ['data'] = {},
1750 ['kgh'] = false
1751 }
1752 if (istable(tag.SEG[0])) then
1753 tag.SEG[#tag.SEG].last=0
1754 table.insert(tag.SEG, segment)
1755 for i=0, 8 do
1756 tag.SEG[#tag.SEG].data[i]=("%02x"):format(i)
1757 end
1758 tag.SEG[#tag.SEG].index=("%02d"):format(#tag.SEG)
1759 return tag
1760 else
1761 print("no Segment-Table found")
1762 end
1763end
1764
1765---
790e8eae 1766-- get only the stamp-bytes of a segment
1767function getSegmentStamp(seg, bytes)
1768 local stamp=""
1769 local stamp_len=7
1770 --- the 'real' stamp on MIM is not really easy to tell for me since the 'data-block' covers stamp0..stampn+data0..datan
1771 -- there a no stamps longer than 7 bytes & they are write-protected by default , and I have not seen user-credntials
1772 -- with stamps smaller 3 bytes (except: Master-Token)
1773 -- WRP -> Read/Write Protection
1774 -- WRC -> Read/Write Condition
1775 -- RD depends on WRC - if WRC > 0 and RD=1: only reader with matching #WRC of Stamp-bytes in thier Database have Read-Access to the Tag
1776 if (seg.WRP<7) then stamp_len=(seg.WRP) end
1777 for i=1, (stamp_len) do
1778 stamp=stamp..seg.data[i-1]
4e8fa8b4 1779 end
790e8eae 1780 if (bytes) then
1781 stamp=str2bytes(stamp)
1782 return stamp
1783 else return stamp end
4e8fa8b4 1784end
1785
4e8fa8b4 1786---
790e8eae 1787-- edit stamp of a segment
1788function editStamp(new_stamp, uid, data)
1789 local stamp=str2bytes(new_stamp)
1790 for i=0, #stamp-1 do
1791 data[i]=stamp[i+1]
1792 end
1793 -- now fill in stamp
1794 for i=0, (string.len(new_stamp)/2)-1 do
1795 data[i]=stamp[i+1]
1796 end
1797 return fix3rdPartyCash1(uid, data)
4e8fa8b4 1798end
1799
1800---
790e8eae 1801-- autoselect special/known segments
1802function autoSelectSegment(tag, s)
1803 local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
1804 local x=#tag.SEG+1
1805 local res = false
1806 io.write("autoSelect ")
1807 --- search for desired segment-type
1808 -- 3rd Party Segment
1809 if (s=="3rdparty") then
1810 repeat
1811 io.write(". ")
1812 x=x-1
1813 res=check43rdPartyCash1(uid, tag.SEG[x].data)
1814 until ( res or x==0 )
1815 end
1816 -- Legic-Cash Segment
1817 if (s=="legiccash") then
1818 repeat
1819 io.write(". ")
1820 x=x-1
1821 res=check4LegicCash(tag.SEG[x].data)
1822 until ( res or x==0 )
1823 end
1824 ---
1825 -- segment found
1826 if (res) then
1827 io.write("\nautoselected Index: "..string.format("%02d", x).."\n")
1828 return x
1829 end
1830 ---
1831 -- nothing found
1832 io.write(acyellow.."no Segment found\n"..acoff)
1833 return -1
4e8fa8b4 1834end
1835
1836---
790e8eae 1837-- delete segment (except segment 00)
1838function delSegment(tag, index)
1839 if (istable(tag.SEG[0])) then
1840 local i
1841 if (type(index)=="string") then index=tonumber(index,10) end
1842 if (index > 0) then
1843 table.remove(tag.SEG, index)
1844 for i=0, #tag.SEG do
1845 tag.SEG[i].index=("%02d"):format(i)
1846 end
1847 end
1848 if(istable(tag.SEG[#tag.SEG])) then tag.SEG[#tag.SEG].last=1 end
1849 return tag
4e8fa8b4 1850 end
1851end
1852
1853---
1854-- edit uid 3rd party cash
1855function edit3rdUid(mapid, uid, data)
1856 mapid=("%06x"):format(tonumber(mapid, 10))
790e8eae 1857 data[46]=string.sub(mapid, 1 ,2)
4e8fa8b4 1858 data[47]=string.sub(mapid, 3 ,4)
1859 data[48]=string.sub(mapid, 5 ,6)
1860 return fix3rdPartyCash1(uid, data)
1861end
1862
1863---
1864-- edit balance 3rd party cash
1865function edit3rdCash(new_cash, uid, data)
1866 new_cash=("%04x"):format(new_cash)
1867 data[32]=string.sub(new_cash, 0,2)
1868 data[33]=string.sub(new_cash, 3,4)
1869 data[34]=("%02x"):format(utils.Crc8Legic(uid..new_cash))
1870 data[35]=string.sub(new_cash, 0,2)
1871 data[36]=string.sub(new_cash, 3,4)
1872 data[37]=("%02x"):format(utils.Crc8Legic(uid..new_cash))
1873 data[39]=string.sub(new_cash, 0,2)
1874 data[40]=string.sub(new_cash, 3,4)
1875 data[41]='00'
1876 data[42]='00'
1877 data[43]='00'
1878 data[44]='00'
1879 return fix3rdPartyCash1(uid, data)
1880end
1881
1882---
790e8eae 1883-- edit 3rd-party cash
1884function edit3rdPartyCash1(tag, x)
1885 local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
1886 if (confirm("\nedit Balance?")) then
1887 local new_cash=input("enter new Balance≤\nwithout comma and without currency-sign! (0-65535)", "100")
1888 tag.SEG[x].data=edit3rdCash(new_cash, uid, tag.SEG[x].data)
1889 end
1890 -- change User-ID (used for online-account-mapping)
1891 if (confirm("\nedit UserID-Mapping?")) then
1892 local new_mapid=input("enter new UserID (6-digit value)", "012345")
1893 tag.SEG[x].data=edit3rdUid(new_mapid, uid, tag.SEG[x].data)
4e8fa8b4 1894 end
790e8eae 1895 if (confirm("\nedit Stamp?")) then
1896 local new_stamp=input("enter new Stamp", getSegmentStamp(tag.SEG[x]))
1897 tag.SEG[x].data=editStamp(new_stamp, uid, tag.SEG[x].data)
1898 new_stamp=getSegmentStamp(tag.SEG[x], 'true')
1899 print("stamp_bytes: "..#new_stamp)
1900 -- replace stamp in 'block 1' also
1901 io.write("editing stamp in Block 1 also ")
1902 for i=20, (20+#new_stamp-1) do
1903 tag.SEG[x].data[i]=new_stamp[i-19]
1904 io.write(".");
1905 end
1906 print(" done")
1907 -- fix known checksums
1908 tag.SEG[x].data=fix3rdPartyCash1(uid, tag.SEG[x].data)
1909 end
1910 return tag
4e8fa8b4 1911end
1912
1913---
790e8eae 1914-- edit Legic Cash
1915function editLegicCash(data)
1916 local limit, curr, balance, rid, tcv
1917 -- currency of balance & limit
1918 curr=currency[data[8]..data[9]]
1919 -- maximum balance
1920 limit=string.format("%4.2f", tonumber(data[10]..data[11]..data[12], 16)/100)
1921 -- current balance
1922 balance=string.format("%4.2f", tonumber(data[15]..data[16]..data[17], 16)/100)
1923 -- reader-id who wrote last transaction
1924 rid=tonumber(data[18]..data[19]..data[20], 16)
1925 -- transaction counter value
1926 tcv=tonumber(data[29], 16)
1927
1928 -- edit currency
1929 if (confirm(accyan.."change Currency?"..acoff)) then
1930 for k, v in pairs(currency) do io.write(k .. " = " .. v .. "\n") end
1931 curr=input(accyan.."enter the 4-digit Hex for the new Currency:"..acoff, data[8]..data[9])
1932 data[8]=string.sub(curr, 1, 2)
1933 data[9]=string.sub(curr, 3, 4)
4e8fa8b4 1934 end
790e8eae 1935
1936 -- edit limit
1937 if (confirm(accyan.."change Limit?"..acoff)) then
1938 limit=string.format("%06x", input(accyan.."enter the Decimal for the new Limit:"..acoff, limit))
1939 data[10]=string.sub(limit, 1, 2)
1940 data[11]=string.sub(limit, 3, 4)
1941 data[12]=string.sub(limit, 5, 6)
1942 end
1943
1944 -- edit balance
1945 if (confirm(accyan.."change Balance?"..acoff)) then
1946 balance=string.format("%06x", input(accyan.."enter the Decimal for the new Balance:"..acoff, balance))
1947 print("Balance: "..balance)
1948 data[15]=string.sub(balance, 1, 2)
1949 data[16]=string.sub(balance, 3, 4)
1950 data[17]=string.sub(balance, 5, 6)
1951 end
1952
1953 -- edit transaction-counter
1954 if (confirm(accyan.."change Transaction-Counter?"..acoff)) then
1955 tcv=string.format("%02x", input(accyan.."enter the 4-digit Hex for the new Currency:"..acoff, data[29]))
1956 data[29]=tcv
1957 end
1958
1959 -- edit reader.id
1960 if (confirm(accyan.."change Last-Reader-ID?"..acoff)) then
1961 rid=string.format("%06x", input(accyan.."enter the Decimal for the new Balance:"..acoff, rid))
1962 print("Balance: "..balance)
1963 data[18]=string.sub(rid, 1, 2)
1964 data[19]=string.sub(rid, 3, 4)
1965 data[20]=string.sub(rid, 5, 6)
1966 end
1967
1968 return fixLegicCash(data)
4e8fa8b4 1969end
1970
1971---
1972-- chack for signature of a 'Legic-Cash-Segment'
1973function check4LegicCash(data)
1974 if(#data==32) then
1975 local stamp_len=(#data-25)
1976 local stamp=""
1977 for i=0, stamp_len-1 do
1978 stamp=stamp..data[i].." "
1979 end
1980 if (data[7]=="01") then
1981 if (("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12))) == data[13]..data[14]) then
1982 if (("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20))) == data[21]..data[22]) then
1983 if (("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29))) == data[30]..data[31]) then
790e8eae 1984 io.write(accyan.."Legic-Cash Segment detected "..acoff)
4e8fa8b4 1985 return true
1986 end
1987 end
1988 end
1989 end
1990 end
1991 return false
1992end
790e8eae 1993
1994---
1995-- chack for signature of a '3rd Party Cash-Segment' - not all bytes know until yet !!
1996function check43rdPartyCash1(uid, data)
1997 if(#data==95) then
1998 -- too explicit checking will avoid fixing ;-)
1999 if (string.find(compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 19, 30)), data[31]),"valid")) then
2000 --if (compareCrc(utils.Crc8Legic(uid..data[32]..data[33]), data[34])=="valid") then
2001 --if (compareCrc(utils.Crc8Legic(uid..data[35]..data[36]), data[37])=="valid") then
2002 --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)), data[62])=="valid") then
2003 --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)), data[89])=="valid") then
2004 io.write(accyan.."3rd Party Cash-Segment detected "..acoff)
2005 return true
2006 --end
2007 --end
2008 --end
2009 --end
2010 end
2011 end
2012 return false
2013end
2014
2015--- CRC related ---
2016---
2017-- build segmentCrc credentials
2018function segmentCrcCredentials(tag, segid)
2019 if (istable(tag.SEG[0])) then
2020 local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
2021 cred = cred ..tag.SEG[segid].raw[1]..tag.SEG[segid].raw[2]..tag.SEG[segid].raw[3]..tag.SEG[segid].raw[4]
2022 return cred
2023 else return print(acyellow.."Master-Token / unsegmented Tag!"..acoff) end
2024end
2025
2026---
2027-- build kghCrc credentials
2028function kghCrcCredentials(tag, segid)
2029 if (istable(tag) and istable(tag.SEG[0])) then
2030 local x='00'
2031 if (type(segid)=="string") then segid=tonumber(segid,10) end
2032 if (segid>0) then x='93' end
2033 local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2..("%02x"):format(tag.SEG[segid].WRP)
2034 cred = cred..("%02x"):format(tag.SEG[segid].WRC)..("%02x"):format(tag.SEG[segid].RD)..x
2035 for i=0, #tag.SEG[segid].data-2 do
2036 cred = cred..tag.SEG[segid].data[i]
2037 end
2038 return cred
2039 end
2040end
2041
2042---
2043-- compare two bytes
2044function compareCrc(calc, guess)
2045 calc=("%02x"):format(calc)
2046 if (calc==guess) then return acgreen.."valid"..acoff
2047 else return acred.."error "..acoff..calc.."!="..guess end
2048end
2049
2050---
2051-- compare 4 bytes
2052function compareCrc16(calc, guess)
2053 calc=("%04x"):format(calc)
2054 if (calc==guess) then return acgreen.."valid"..acoff
2055 else return acred.."error "..acoff..calc.."!="..guess end
2056end
2057
2058---
2059-- repair / fix crc's of a 'Legic-Cash-Segment'
2060function fixLegicCash(data)
2061 if(#data==32 and data[7]=="01") then
2062 local crc1, crc2, crc3
2063 -- set shadow-balance equal to balance
2064 data[23]=data[15]
2065 data[24]=data[16]
2066 data[25]=data[17]
2067 -- set shadow-last-reader to last-reader
2068 data[26]=data[18]
2069 data[27]=data[19]
2070 data[28]=data[20]
2071 -- calculate all crc's
2072 crc1=("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12)))
2073 crc2=("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20)))
2074 crc3=("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29)))
2075 -- set crc's
2076 data[13]=string.sub(crc1, 1, 2)
2077 data[14]=string.sub(crc1, 3, 4)
2078 data[21]=string.sub(crc2, 1, 2)
2079 data[22]=string.sub(crc2, 3, 4)
2080 data[30]=string.sub(crc3, 1, 2)
2081 data[31]=string.sub(crc3, 3, 4)
2082 return data
2083 end
2084end
2085
2086---
2087-- repair / fix (yet known) crc's of a '3rd Party Cash-Segment' - not all bytes know until yet !!
2088function fix3rdPartyCash1(uid, data)
2089 if(#data==95) then
2090 -- checksum 1
2091 data[31]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 19, 30)))
2092 -- checksum 2
2093 data[34]=("%02x"):format(utils.Crc8Legic(uid..data[32]..data[33]))
2094 -- checksum 3
2095 data[37]=("%02x"):format(utils.Crc8Legic(uid..data[35]..data[36]))
2096 -- checksum 4
2097 data[55]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 46, 54)))
2098 -- checksum 5
2099 data[62]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)))
2100 -- checksum 6
2101 data[73]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 63, 72)))
2102 -- checksum 7
2103 data[89]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)))
2104 return data
2105 end
2106end
2107
7f0cb92e 2108---
2109-- calculate Master-Token crc
2110function calcMtCrc(bytes)
2111 --print(#bytes)
2112 local cmd=bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[7]..bytes[6]..bytes[8]
2113 local len=(tonumber(0xfc ,10)-("%d"):format('0x'..bytes[7]))
2114 for i=1, len do
2115 cmd=cmd..bytes[8+i]
2116 end
2117 local res=("%02x"):format(utils.Crc8Legic(cmd))
2118 return res
2119end
2120
790e8eae 2121---
2122-- calculate segmentCRC for a given segment
2123function calcSegmentCrc(tag, segid)
2124 if (istable(tag.SEG[0])) then
2125 -- check if a 'Kaber Group Header' exists
2126 local data=segmentCrcCredentials(tag, segid)
2127 return ("%02x"):format(utils.Crc8Legic(data))
2128 end
2129end
2130
2131---
2132-- calcuate kghCRC for a given segment
2133function calcKghCrc(tag, segid)
2134 if (istable(tag.SEG[0])) then
2135 -- check if a 'Kaber Group Header' exists
2136 local i
2137 local data=kghCrcCredentials(tag, segid)
2138 return ("%02x"):format(utils.Crc8Legic(data))
2139 end
2140end
2141
7f0cb92e 2142---
2143-- check all segmnet-crc
2144function checkAllSegCrc(tag)
2145 if (istable(tag.SEG[0])) then
2146 for i=0, #tag.SEG do
2147 crc=calcSegmentCrc(tag, i)
2148 tag.SEG[i].crc=crc
2149 end
790e8eae 2150 else return print(acyellow.."Master-Token / unsegmented Tag"..acoff) end
7f0cb92e 2151end
2152
2153---
2154-- check all segmnet-crc
2155function checkAllKghCrc(tag)
2156 if (istable(tag.SEG[0])) then
2157 for i=0, #tag.SEG do
2158 crc=calcKghCrc(tag, i)
2159 if (tag.SEG[i].kgh) then
2160 tag.SEG[i].data[#tag.SEG[i].data-1]=crc
2161 end
2162 end
2163 end
2164end
2165
2166---
790e8eae 2167-- validate segmentCRC for a given segment
2168function checkSegmentCrc(tag, segid)
2169 local data=segmentCrcCredentials(tag, segid)
2170 if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].crc) then
2171 return true
7f0cb92e 2172 end
790e8eae 2173 return false
7f0cb92e 2174end
2175
2176---
2177-- validate kghCRC to segment in tag-table
2178function checkKghCrc(tag, segid)
2179 if (type(tag.SEG[segid])=='table') then
2180 if (tag.data[3]=="11" and tag.raw=="9f" and tag.SSC=="ff") then
2181 local data=kghCrcCredentials(tag, segid)
2182 if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].data[tag.SEG[segid].len-5-1]) then return true; end
2183 else return false; end
790e8eae 2184 else oops(acred.."'Kaba Group header' detected but no Segment-Data found"..ansocolors.reset) end
7f0cb92e 2185end
733eb420 2186
2187---
2188-- helptext for modify-mode
2189function modifyHelp()
2190 local t=[[
4e8fa8b4 2191
790e8eae 2192 Data I/O Segment Manipulation Token-Data
2193 ----------------- -------------------- ---------------------
2194 rt => read Tag as => add Segment mt => make Token
2195 wt => write Tag es => edit Segment Header et => edit Token data
2196 ed => edit Segment Data tk => toggle KGH-Flag
2197 File I/O rs => remove Segment
2198 ----------------- cc => check Segment-CRC
2199 lf => load File ck => check KGH
2200 sf => save File ds => dump Segments
2201 xf => xor to File
2202
2203
2204 Virtual Tags tagMap (partial) known Segments
2205 -------------------------------- --------------------- ---------------------------
2206 ct => copy mainTag to backupTag mm => make (new) Map dlc => dump Legic-Cash
2207 tc => copy backupTag to mainTag em => edit Map submenu elc => edit Legic-Cash
2208 tt => switch mainTag & backupTag lm => load map from file d3p => dump 3rd-Party-Cash
2209 di => dump mainTag sm => save map to file e3p => edit 3rd-Party-Cash
2210 do => dump backupTag
4e8fa8b4 2211
790e8eae 2212 h => this help q => quit
2213 ]]
733eb420 2214 return t
2215end
2216
2217---
2218-- modify Tag (interactive)
2219function modifyMode()
790e8eae 2220 local i, backupTAG, outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes, tagMap
4e8fa8b4 2221
733eb420 2222 actions = {
790e8eae 2223 ---
2224 -- helptext
733eb420 2225 ["h"] = function(x)
4e8fa8b4 2226 print(" Version: "..version);
790e8eae 2227 print(modifyHelp().."\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 ""))
733eb420 2228 end,
790e8eae 2229 ---
2230 -- read real Tag with PM3 into virtual 'mainTAG'
4e8fa8b4 2231 ["rt"] = function(x)
2232 inTAG=readFromPM3();
2233 --actions.di()
2234 end,
790e8eae 2235 ---
2236 -- write content of virtual 'mainTAG' to real Tag with PM3
733eb420 2237 ["wt"] = function(x)
790e8eae 2238 writeToTag(inTAG)
733eb420 2239 end,
4e8fa8b4 2240 ---
790e8eae 2241 -- copy mainTAG to backupTAG
733eb420 2242 ["ct"] = function(x)
790e8eae 2243 print(accyan.."copy mainTAG to backupTAG"..acoff)
4e8fa8b4 2244 backupTAG=deepCopy(inTAG)
733eb420 2245 end,
790e8eae 2246 ---
2247 -- copy backupTAG to mainTAG
733eb420 2248 ["tc"] = function(x)
790e8eae 2249 print(accyan.."copy backupTAG to mainTAG"..acoff)
4e8fa8b4 2250 inTAG=deepCopy(backupTAG)
2251 end,
790e8eae 2252 ---
2253 -- toggle between mainTAG and backupTAG
4e8fa8b4 2254 ["tt"] = function(x)
790e8eae 2255 -- copy main to temp
2256 outTAG=deepCopy(inTAG)
2257 -- copy backup to main
2258 inTAG=deepCopy(backupTAG)
2259 print(accyan.."toggle to "..accyan..((currentTag=='inTAG') and "backupTAG" or "mainTAG")..acoff)
2260 if(currentTag=="inTAG") then currentTag='backupTAG'
2261 else currentTag='inTAG' end
2262 -- copy temp (main) to backup
2263 backupTAG=deepCopy(outTAG)
733eb420 2264 end,
790e8eae 2265 ---
2266 -- load file into mainTAG
733eb420 2267 ["lf"] = function(x)
790e8eae 2268
2269 if (type(x)=='string' and file_check(x)) then filename=x
7f0cb92e 2270 else filename=input("enter filename: ", "legic.temp") end
733eb420 2271 inTAG=readFile(filename)
790e8eae 2272 -- check for existing tagMap
2273 if (file_check(filename..".map")) then
2274 if(confirm(accyan.."Mapping-File for "..acoff..filename..accyan.." found - load it also?"..acoff)) then
2275 tagMap=loadTagMap(filename..".map")
2276 end
2277 end
733eb420 2278 end,
790e8eae 2279 ---
2280 -- save values of mainTAG to a file (xored with MCC of mainTAG)
733eb420 2281 ["sf"] = function(x)
2282 if(istable(inTAG)) then
2283 outfile=input("enter filename:", "legic.temp")
2284 bytes=tagToBytes(inTAG)
2285 --bytes=xorBytes(bytes, inTAG.MCC)
2286 if (bytes) then
2287 writeFile(bytes, outfile)
2288 end
2289 end
2290 end,
790e8eae 2291 ---
2292 -- save values of mainTAG to a file (xored with 'specific' MCC)
733eb420 2293 ["xf"] = function(x)
2294 if(istable(inTAG)) then
2295 outfile=input("enter filename:", "legic.temp")
2296 crc=input("enter new crc: ('00' for a plain dump)", inTAG.MCC)
2297 print("obfuscate witth: "..crc)
2298 bytes=tagToBytes(inTAG)
2299 bytes[5]=crc
2300 if (bytes) then
2301 writeFile(bytes, outfile)
2302 end
2303 end
2304 end,
790e8eae 2305 ---
2306 -- dump mainTAG (and all Segments)
4e8fa8b4 2307 ["di"] = function(x)
2308 if (istable(inTAG)) then
2309 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
2310 if(istable(inTAG.SEG[0])) then
2311 for i=0, #inTAG.SEG do
2312 if(check43rdPartyCash1(uid, inTAG.SEG[i].data)) then
790e8eae 2313 io.write(accyan.."in Segment index: "..inTAG.SEG[i].index ..acoff.. "\n")
4e8fa8b4 2314 elseif(check4LegicCash(inTAG.SEG[i].data)) then
790e8eae 2315 io.write(accyan.."in Segment index: "..inTAG.SEG[i].index..acoff.."\n")
4e8fa8b4 2316 lc=true;
2317 lci=inTAG.SEG[i].index;
2318 end
2319 end
2320 end
2321 print("\n"..dumpTag(inTAG).."\n")
2322 if (lc) then actions["dlc"](lci) end
790e8eae 2323 lc=false
4e8fa8b4 2324 end
2325 end,
790e8eae 2326 ---
2327 -- dump backupTAG (and all Segments)
4e8fa8b4 2328 ["do"] = function(x) if (istable(backupTAG)) then print("\n"..dumpTag(backupTAG).."\n") end end,
790e8eae 2329 ---
2330 -- create a empty tagMap
2331 ["mm"] = function(x)
2332 -- clear existing tagMap and init
2333 if (istable(inTAG)) then
2334 tagMap=makeTagMap()
2335 end
2336 end,
2337 ---
2338 -- edit a tagMap
2339 ["em"] = function(x)
2340 if (istable(inTAG)==false) then
2341 if (confirm("no mainTAG in memory!\nread from PM3?")) then
2342 actions['rt']()
2343 elseif (confirm("load from File?")) then
2344 actions['lf']()
2345 else return
2346 end
2347 end
2348 if (istable(tagMap)==false) then actions['mm']() end
2349 -- edit
2350 tagMap=editTagMap(inTAG, tagMap)
2351 end,
2352 ---
2353 -- save a tagMap
2354 ["sm"] = function(x)
2355 if (istable(tagMap)) then
2356 if (istable(tagMap) and #tagMap.mappings>0) then
2357 print(accyan.."Map contains "..acoff..#tagMap..accyan.." mappings"..acoff)
2358 saveTagMap(tagMap, input(accyan.."enter filename:"..acoff, "Legic.map"))
2359 else
2360 print(acyellow.."no mappings in tagMap!"..acoff)
2361 end
2362 end
2363 end,
2364 ---
2365 -- load a tagMap
2366 ["lm"] = function(x)
2367 tagMap=loadTagMap(input(accyan.."enter filename:"..acoff, "Legic.map"))
2368 end,
2369 ---
2370 -- dump single segment
733eb420 2371 ["ds"] = function(x)
7f0cb92e 2372 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
2373 else sel=selectSegment(inTAG) end
790e8eae 2374 if (sel) then print("\n"..(dumpSegment(inTAG, sel) or acred.."no Segments available") ..acoff.."\n") end
733eb420 2375 end,
790e8eae 2376 ---
2377 -- edit segment header
733eb420 2378 ["es"] = function(x)
7f0cb92e 2379 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
2380 else sel=selectSegment(inTAG) end
733eb420 2381 if (sel) then
7f0cb92e 2382 if(istable(inTAG.SEG[0])) then
733eb420 2383 inTAG=editSegment(inTAG, sel)
2384 inTAG.SEG[sel]=regenSegmentHeader(inTAG.SEG[sel])
790e8eae 2385 else print(acyellow.."no Segments in Tag"..acoff) end
733eb420 2386 end
2387 end,
790e8eae 2388 ---
2389 -- add segment
733eb420 2390 ["as"] = function(x)
2391 if (istable(inTAG.SEG[0])) then
2392 inTAG=addSegment(inTAG)
2393 inTAG.SEG[#inTAG.SEG-1]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG-1])
2394 inTAG.SEG[#inTAG.SEG]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG])
790e8eae 2395 else print(accyan.."Master-Token / unsegmented Tag!"..acoff)
733eb420 2396 end
2397 end,
790e8eae 2398 ---
2399 -- remove segment
733eb420 2400 ["rs"] = function(x)
7f0cb92e 2401 if (istable(inTAG.SEG[0])) then
2402 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
2403 else sel=selectSegment(inTAG) end
733eb420 2404 inTAG=delSegment(inTAG, sel)
2405 for i=0, #inTAG.SEG do
2406 inTAG.SEG[i]=regenSegmentHeader(inTAG.SEG[i])
2407 end
2408 end
2409 end,
790e8eae 2410 ---
2411 -- edit data-portion of single segment
733eb420 2412 ["ed"] = function(x)
7f0cb92e 2413 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
2414 else sel=selectSegment(inTAG) end
790e8eae 2415 if (istable(inTAG.SEG[sel])) then
733eb420 2416 inTAG.SEG[sel].data=editSegmentData(inTAG.SEG[sel].data)
2417 end
2418 end,
790e8eae 2419 ---
2420 -- edit Tag (MCD, MSN, MCC etc.)
7f0cb92e 2421 ["et"] = function(x)
2422 if (istable(inTAG)) then
2423 editTag(inTAG)
2424 end
2425 end,
790e8eae 2426 ---
2427 -- make (dummy) Token
7f0cb92e 2428 ["mt"] = function(x) inTAG=makeToken(); actions.di() end,
790e8eae 2429 ---
2430 -- fix segment-crc on single segment
2431 ["ts"] = function(x)
7f0cb92e 2432 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
2433 else sel=selectSegment(inTAG) end
733eb420 2434 regenSegmentHeader(inTAG.SEG[sel])
2435 end,
790e8eae 2436 ---
2437 -- toggle kgh-crc-flag on a single segment
2438 ["tk"] = function(x)
7f0cb92e 2439 if (istable(inTAG) and istable(inTAG.SEG[0])) then
2440 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
2441 else sel=selectSegment(inTAG) end
733eb420 2442 if(inTAG.SEG[sel].kgh) then inTAG.SEG[sel].kgh=false
2443 else inTAG.SEG[sel].kgh=true end
7f0cb92e 2444 end
733eb420 2445 end,
790e8eae 2446 ---
2447 -- calculate LegicCrc8
2448 ["k"] = function(x)
7f0cb92e 2449 if (type(x)=="string" and string.len(x)>0) then
790e8eae 2450 print(("%02x"):format(utils.Crc8Legic(x)))
7f0cb92e 2451 end
790e8eae 2452 end,
2453 ---
2454 -- noop
4e8fa8b4 2455 ["xb"] = function(x)
790e8eae 2456 end,
2457 ---
2458 -- print string for LegicCrc8-calculation about single segment
2459 ["xc"] = function(x)
7f0cb92e 2460 if (istable(inTAG) and istable(inTAG.SEG[0])) then
2461 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
2462 else sel=selectSegment(inTAG) end
2463 print("k "..kghCrcCredentials(inTAG, sel))
2464 end
733eb420 2465 end,
790e8eae 2466 ---
2467 -- fix legic-cash checksums
2468 ["flc"] = function(x)
2469 if (type(x)=="string" and string.len(x)>0) then x=tonumber(x,10)
2470 else x=selectSegment(inTAG) end
2471 inTAG.SEG[x].data=fixLegicCash(inTAG.SEG[x].data)
2472 end,
2473 ---
2474 -- edit legic-cash values fixLegicCash(data)
2475 ["elc"] = function(x)
2476 x=autoSelectSegment(inTAG, "legiccash")
2477 inTAG.SEG[x].data=editLegicCash(inTAG.SEG[x].data)
2478 end,
2479 ---
2480 -- dump legic-cash human-readable
4e8fa8b4 2481 ["dlc"] = function(x)
4e8fa8b4 2482 -- if segment index was user defined
2483 if (type(x)=="string" and string.len(x)>0) then
2484 x=tonumber(x,10)
2485 print(string.format("User-Selected Index %02d", x))
2486 -- or try to find match
2487 else x=autoSelectSegment(inTAG, "legiccash") end
790e8eae 2488 -- dump it
2489 dumpLegicCash(inTAG, x)
4e8fa8b4 2490 end,
790e8eae 2491 ---
2492 -- dump 3rd-party-cash-segment
4e8fa8b4 2493 ["d3p"] = function(x)
2494 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
2495 -- if segment index was user defined
2496 if (type(x)=="string" and string.len(x)>0) then
2497 x=tonumber(x,10)
2498 print(string.format("User-Selected Index %02d", x))
2499 -- or try to find match
790e8eae 2500 else x=autoSelectSegment(inTAG, "3rdparty") end
4e8fa8b4 2501 if (istable(inTAG) and istable(inTAG.SEG[x]) and inTAG.SEG[x].len == 100) then
2502 uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
2503 if (check43rdPartyCash1(uid, inTAG.SEG[x].data)) then
2504 dump3rdPartyCash1(inTAG, x)
2505 end
2506 end
2507 end,
790e8eae 2508 ---
2509 -- dump 3rd-party-cash-segment (raw blocks and checksums over 'known areas')
4e8fa8b4 2510 ["r3p"] = function(x)
2511 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
2512 -- if segment index was user defined
2513 if (type(x)=="string" and string.len(x)>0) then
2514 x=tonumber(x,10)
2515 print(string.format("User-Selected Index %02d", x))
2516 -- or try to find match
2517 else x=autoSelectSegment(inTAG, "3rdparty") end
790e8eae 2518 print3rdPartyCash1(inTAG, x)
4e8fa8b4 2519 end,
790e8eae 2520 ---
2521 -- edit 3rd-party-cash-segment values (Balance, Mapping-UID, Stamp)
4e8fa8b4 2522 ["e3p"] = function(x)
2523 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
2524 -- if segment index was user defined
2525 if (type(x)=="string" and string.len(x)>0) then
2526 x=tonumber(x,10)
2527 print(string.format("User-Selected Index %02d", x))
2528 -- or try to find match
790e8eae 2529 else x=autoSelectSegment(inTAG, "3rdparty") end
4e8fa8b4 2530 if (istable(inTAG) and istable(inTAG.SEG[x]) and inTAG.SEG[x].len == 100) then
790e8eae 2531 inTAG=edit3rdPartyCash1(inTAG, x)
4e8fa8b4 2532 dump3rdPartyCash1(inTAG, x)
4e8fa8b4 2533 end
2534 end,
790e8eae 2535 ---
2536 -- force fixing 3rd-party-checksums
2537 ["f3p"] = function(x)
2538 if(type(x)=="string" and string.len(x)>=2) then x=tonumber(x, 10)
2539 else x=selectSegment(inTAG) end
2540 if (istable(inTAG.SEG[x])) then
2541 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
2542 inTAG.SEG[x].data=fix3rdPartyCash1(uid, inTAG.SEG[x].data)
2543 end
2544 end,
2545 ---
2546 -- get stamp from single segment
4e8fa8b4 2547 ["gs"] = function(x)
2548 if(type(x)=="string" and string.len(x)>=2) then x=tonumber(x, 10)
2549 else x=selectSegment(inTAG) end
2550 local stamp=getSegmentStamp(inTAG.SEG[x])
2551 print("Stamp : "..stamp)
2552 stamp=str2bytes(stamp)
2553 print("lenght: "..#stamp)
2554 end,
790e8eae 2555 ---
2556 -- calculate crc16
4e8fa8b4 2557 ["c6"] = function(x) local crc16=string.format("%4.04x", utils.Crc16(x))
2558 print(string.sub(crc16, 0,2).." "..string.sub(crc16, 3,4))
2559 end,
790e8eae 2560 ---
2561 -- check & fix segments-crc of all segments
2562 ["cc"] = function(x) if (istable(inTAG)) then checkAllSegCrc(inTAG) end end,
2563 ---
2564 -- set backup-area-bytes to '00'
4e8fa8b4 2565 ["cb"] = function(x)
2566 if (istable(inTAG)) then
790e8eae 2567 print(accyan.."purge BackupArea"..acoff)
4e8fa8b4 2568 inTAG=clearBackupArea(inTAG)
2569 end
2570 end,
790e8eae 2571 ---
2572 -- check and fix all segments inTAG.SEG[x].kgh toggled 'on'
2573 ["ck"] = function(x) if (istable(inTAG)) then checkAllKghCrc(inTAG) end end,
2574 ---
2575 -- check and fix all segments inTAG.SEG[x].kgh toggled 'on'
2576 ["tac"] = function(x)
2577 if (colored_output) then
2578 colored_output=false
2579 else
2580 colored_output=true
2581 end
2582 load_colors(colored_output)
4e8fa8b4 2583 end,
733eb420 2584 }
733eb420 2585 repeat
790e8eae 2586 -- defualt message / prompt
733eb420 2587 ic=input("Legic command? ('h' for help - 'q' for quit)", "h")
790e8eae 2588 -- command actions decisions (first match, longer commands before shorter)
4e8fa8b4 2589 if (type(actions[string.lower(string.sub(ic,0,3))])=='function') then
2590 actions[string.lower(string.sub(ic,0,3))](string.sub(ic,5))
733eb420 2591 elseif (type(actions[string.lower(string.sub(ic,0,2))])=='function') then
2592 actions[string.lower(string.sub(ic,0,2))](string.sub(ic,4))
4e8fa8b4 2593 elseif (type(actions[string.lower(string.sub(ic,0,1))])=='function') then
2594 actions[string.lower(string.sub(ic,0,1))](string.sub(ic,3))
7f0cb92e 2595 else actions.h('') end
733eb420 2596 until (string.sub(ic,0,1)=="q")
2597end
2598
790e8eae 2599---
2600-- main function
733eb420 2601function main(args)
790e8eae 2602 -- set init colors/switch (can be toggled with 'tac' => 'toggle ansicolors')
2603 load_colors(colored_output)
733eb420 2604 if (#args == 0 ) then modifyMode() end
2605 --- variables
4e8fa8b4 2606 local inTAG, backupTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs
733eb420 2607 -- just a spacer for better readability
2608 print()
2609 --- parse arguments
2610 for o, a in getopt.getopt(args, 'hrmi:do:c:') do
2611 -- display help
2612 if o == "h" then return help(); end
2613 -- read tag from PM3
2614 if o == "r" then inTAG=readFromPM3() end
2615 -- input file
2616 if o == "i" then inTAG=readFile(a) end
2617 -- dump virtual-Tag
2618 if o == "d" then dfs=true end
2619 -- interacive modifying
2620 if o == "m" then interactive=true; modifyMode() end
2621 -- xor (e.g. for clone or plain file)
2622 if o == "c" then cfs=true; crc=a end
2623 -- output file
2624 if o == "o" then outfile=a; ofs=true end
2625 end
2626
2627 -- file conversion (output to file)
2628 if (ofs) then
2629 -- dump infile / tag-read
2630 if (dfs) then
2631 print("-----------------------------------------")
2632 print(dumpTag(inTAG))
2633 end
2634 bytes=tagToBytes(inTAG)
733eb420 2635 if (cfs) then
7f0cb92e 2636 -- xor willl be done in function writeFile
2637 -- with the value of byte[5]
733eb420 2638 bytes[5]=crc
2639 end
2640 -- write to outfile
2641 if (bytes) then
2642 writeFile(bytes, outfile)
7f0cb92e 2643 --- read real tag into virtual tag
2644 -- inTAG=readFromPM3() end
2645 --- or simply use the bytes that where wriiten
733eb420 2646 inTAG=bytesToTag(bytes, inTAG)
2647 -- show new content
2648 if (dfs) then
2649 print("-----------------------------------------")
7f0cb92e 2650 print(dumpTag(inTAG))
733eb420 2651 end
2652 end
2653 end
2654
2655end
2656
790e8eae 2657---
2658-- script start
733eb420 2659main(args)
Impressum, Datenschutz