]> git.zerfleddert.de Git - proxmark3-svn/blame - client/scripts/legic.lua
@Marshmellow42 additions:
[proxmark3-svn] / client / scripts / legic.lua
CommitLineData
733eb420 1--[[
2(example) Legic-Prime Layout with 'Kaba Group Header'
3 +----+----+----+----+----+----+----+----+
4 0x00|MCD |MSN0|MSN1|MSN2|MCC | 60 | ea | 9f |
5 +----+----+----+----+----+----+----+----+
6 0x08| ff | 00 | 00 | 00 | 11 |Bck0|Bck1|Bck2|
7 +----+----+----+----+----+----+----+----+
8 0x10|Bck3|Bck4|Bck5|BCC | 00 | 00 |Seg0|Seg1|
9 +----+----+----+----+----+----+----+----+
10 0x18|Seg2|Seg3|SegC|Stp0|Stp1|Stp2|Stp3|UID0|
11 +----+----+----+----+----+----+----+----+
12 0x20|UID1|UID2|kghC|
13 +----+----+----+
4e8fa8b4 14MCD = Manufacturer ID
15MSN = Manufacturer SerialNumber
1660 ea = DCF Low + DCF high
179f = raw byte which holds the bits for OLE,WRP,WRC,RD
18ff = unknown but important
1900 = unimportant
2011 = unknown but important
21Bck = header-backup-area
2200 00 = Year (00 = 2000) & Week (not important)
23Seg = Segment Header
24SegC = Crc8 over the Segment Header
25Stp = Stamp (could be more as 4 - up to 7)
26UID = dec User-ID for online-Mapping
27kghC = crc8 over MCD + MSN0..MSN2 + UID
28
29
30(example) Legic-Cash on MIM256/1024 tag' (37 bytes)
31 +----+----+----+----+----+----+----+----+
32 0x00|Seg0|Seg1|Seg2|Seg3|SegC|STP0|STP1|STP2|
33 +----+----+----+----+----+----+----+----+
34 0x08|STP3|STP4|STP5|STP6| 01 |CURh|CURl|LIMh|
35 +----+----+----+----+----+----+----+----+
36 0x10|LIMm|LIMl|CHKh|CHKl|BALh|BALm|BALl|LRBh|
37 +----+----+----+----+----+----+----+----+
38 0x18|LRBm|LRBl|CHKh|CHKl|SHDh|SHDm|SHDl|LRSh|
39 +----+----+----+----+----+----+----+----+
40 0x20|LRSm|LRSl| CV |CHKh|CHKl|
41 +----+----+----+----+----+
42STP = Stamp (seems to be always 7 bytes)
4301 = unknown but important
44CUR = currency in HEX (ISO 4217)
45LIM = Cash-Limit
46CHK = crc16 over byte-addr 0x05..0x12
47BAL = Balance
48LRB = ID of the reader that changed the balance
49CHK = crc16 over BAL + LRB
50SHD = shadow Balance
51LRS = ID of the reader that changed the shadow balance (?? should be always the same as LRB)
52CV = Counter value for transactions
53CHK = crc16 over SHD + LRS + CV
733eb420 54--]]
55
56example = "script run legic"
57author = "Mosci"
4e8fa8b4 58version = "1.0.1"
733eb420 59desc =
60[[
61
62This script helps you to read, create and modify Legic Prime Tags (MIM22, MIM256, MIM1024)
63it's kinda interactive with following commands in three categories:
64
4e8fa8b4 65 Data I/O Segment Manipulation Token-Data
66 ----------------- -------------------- -----------------
67 rt => read Tag as => add Segment mt => make Token
68 wt => write Tag es => edit Segment Header et => edit Token data
69 ct => copy io Tag ed => edit Segment Data tk => toggle KGH-Flag
7f0cb92e 70 tc => copy oi Tag rs => remove Segment
4e8fa8b4 71 cc => check Segment-CRC File I/O
72 di => dump inTag ck => check KGH -----------------
73 do => dump outTag lf => load File
74 ds => dump Segments sf => save File
75 lc => dump Legic-Cash xf => xor to File
76 d3p => dump 3rd Party Cash
77 r3p => raw 3rd Party Cash
78
733eb420 79
733eb420 80 rt: 'read tag' - reads a tag placed near to the PM3
81 wt: 'write tag' - writes the content of the 'virtual inTag' to a tag placed near to th PM3
82 without the need of changing anything - MCD,MSN,MCC will be read from the tag
83 before and applied to the output.
84 ct: 'copy tag' - copy the 'virtual Tag' to a second 'virtual TAG' - not usefull yet, but inernally needed
85 tc: 'copy tag' - copy the 'second virtual Tag' to 'virtual TAG' - not usefull yet, but inernally needed
86 di: 'dump inTag' - shows the current content of the 'virtual Tag'
87 do: 'dump outTag' - shows the current content of the 'virtual outTag'
733eb420 88 ds: 'dump Segments' - will show the content of a selected Segment
89 as: 'add Segment' - will add a 'empty' Segment to the inTag
90 es: 'edit Segment' - edit the Segment-Header of a selected Segment (len, WRP, WRC, RD, valid)
91 all other Segment-Header-Values are either calculated or not needed to edit (yet)
7f0cb92e 92 ed: 'edit data' - edit the Data of a Segment (ADF-Aera / Stamp & Payload specific Data)
93 et: 'edit Token' - edit Data of a Token (CDF-Area / SAM, SAM64, SAM63, IAM, GAM specific Data)
94 mt: 'make Token' - create a Token 'from scratch' (guided)
733eb420 95 rs: 'remove segment' - removes a Segment (except Segment 00, but this can be set to valid=0 for Master-Token)
96 cc: 'check Segment-CRC'- checks & calculates (if check failed) the Segment-CRC of all Segments
97 ck: 'check KGH-CRC' - checks the and calculates a 'Kaba Group Header' if one was detected
98 'Kaba Group Header CRC calculation'
99 tk: 'toggle KGH' - toglle the (script-internal) flag for kgh-calculation for a segment
100 xc: 'etra c' - show string that was used to calculate the kgh-crc of a segment
4e8fa8b4 101dlc: 'dump Legic-Cash' - show balance and checksums of a legic-Cash Segment
102d3p: 'dump 3rd Party' - show balance, history and checksums of a (yet) unknown 3rd Party Cash-Segment
103r3p: 'raw 3rd Party' - show balance, history and checksums of a (yet) unknown 3rd Party Cash-Segment
104e3p: 'edit 3rd Party' - edit Data in 3rd Party Cash Segment
733eb420 105 lf: 'load file' - load a (xored) file from the local Filesystem into the 'virtual inTag'
106 sf: 'save file' - saves the 'virtual inTag' to the local Filesystem (xored with Tag-MCC)
107 xf: 'xor file' - saves the 'virtual inTag' to the local Filesystem (xored with choosen MCC - use '00' for plain values)
108
109]]
4e8fa8b4 110currentTag="inTAG"
7f0cb92e 111---
112-- requirements
733eb420 113local utils = require('utils')
114local getopt = require('getopt')
115
7f0cb92e 116---
117-- global variables / defines
733eb420 118local bxor = bit32.bxor
119local bbit = bit32.extract
120local input = utils.input
121local confirm = utils.confirm
122
4e8fa8b4 123---
124-- curency-codes for Legic-Cash-Segments (ISO 4217)
125local currency = {
126 ["03d2"]="EUR",
127 ["0348"]="USD",
128 ["033A"]="GBP",
129 ["02F4"]="CHF"
130}
131
7f0cb92e 132---
733eb420 133-- This is only meant to be used when errors occur
134function oops(err)
135 print("ERROR: ",err)
136 return nil, err
137end
138
139---
140-- Usage help
141function help()
142 print(desc)
7f0cb92e 143 print(version)
733eb420 144 print("Example usage")
145 print(example)
146end
147
148---
149-- table check helper
150function istable(t)
151 return type(t) == 'table'
152end
153
154---
7f0cb92e 155-- xor single byte
733eb420 156function xorme(hex, xor, index)
157 if ( index >= 23 ) then
158 return ('%02x'):format(bxor( tonumber(hex,16) , tonumber(xor,16) ))
159 else
160 return hex
161 end
162end
163
164---
165-- (de)obfuscate bytes
166function xorBytes(inBytes, crc)
167 local bytes = {}
168 for index = 1, #inBytes do
169 bytes[index] = xorme(inBytes[index], crc, index)
170 end
171 if (#inBytes == #bytes) then
172 -- replace crc
173 bytes[5] = string.sub(crc,-2)
174 return bytes
175 else
176 print("error: byte-count missmatch")
177 return false
178 end
179end
180
181---
182-- check availability of file
183function file_check(file_name)
184 local file_found=io.open(file_name, "r")
185 if file_found==nil then
186 return false
187 else
188 return true
189 end
190end
191
192---
193-- read file into virtual-tag
194function readFile(filename)
195 local bytes = {}
196 local tag = {}
197 if (file_check(filename)==false) then
198 return oops("input file: "..filename.." not found")
199 else
200 bytes = getInputBytes(filename)
201 if (bytes == false) then return oops('couldnt get input bytes')
202 else
203 -- make plain bytes
204 bytes = xorBytes(bytes,bytes[5])
205 -- create Tag for plain bytes
206 tag=createTagTable()
207 -- load plain bytes to tag-table
208 tag=bytesToTag(bytes, tag)
209 end
210 end
211 return tag
212end
213
214---
215-- write bytes to file
733eb420 216function writeFile(bytes, filename)
217 if (filename~='MylegicClone.hex') then
218 if (file_check(filename)) then
219 local answer = confirm("\nthe output-file "..filename.." alredy exists!\nthis will delete the previous content!\ncontinue?")
220 if (answer==false) then return print("user abort") end
221 end
222 end
223 local line
224 local bcnt=0
225 local fho,err = io.open(filename, "w")
226 if err then oops("OOps ... faild to open output-file ".. filename) end
227 bytes=xorBytes(bytes, bytes[5])
228 for i = 1, #bytes do
229 if (bcnt == 0) then
230 line=bytes[i]
231 elseif (bcnt <= 7) then
232 line=line.." "..bytes[i]
233 end
234 if (bcnt == 7) then
235 -- write line to new file
236 fho:write(line.."\n")
237 -- reset counter & line
238 bcnt=-1
239 line=""
240 end
241 bcnt=bcnt+1
242 end
243 fho:close()
244 print("\nwrote ".. #bytes .." bytes to " .. filename)
245 return true
246end
247
7f0cb92e 248---
249-- put certain bytes into a new table
250function bytesToTable(bytes, bstart, bend)
251 local t={}
252 for i=0, (bend-bstart) do
253 t[i]=bytes[bstart+i]
254 end
255 return t
256end
257
733eb420 258---
259-- read from pm3 into virtual-tag
260function readFromPM3()
261 local tag, bytes, infile
262 --if (confirm("is the Tag placed onto the Proxmak3 and ready for reading?")) then
263 --print("reading Tag ...")
264 --infile=input("input a name for the temp-file:", "legic.temp")
265 --if (file_check(infile)) then
266 -- local answer = confirm("\nthe output-file "..infile.." alredy exists!\nthis will delete the previous content!\ncontinue?")
267 -- if (answer==false) then return print("user abort") end
268 --end
269 infile="legic.temp"
270 core.console("hf legic reader")
271 core.console("hf legic save "..infile)
272 --print("read temp-file into virtual Tag ...")
273 tag=readFile(infile)
274 return tag
275 --else return print("user abort"); end
276end
277
7f0cb92e 278---
279-- write virtual Tag to real Tag
280function writeToTag(plainBytes, taglen, filename)
281 local bytes
282 if(utils.confirm("\nplace your empty tag onto the PM3 to read & write\n") == false) then
283 return
284 end
285
286 -- write data to file
287 if (taglen > 0) then
288 WriteBytes = utils.input("enter number of bytes to write?", taglen)
289
290 -- load file into pm3-buffer
291 if (type(filename)~="string") then filename=input("filename to load to pm3-buffer?","legic.temp") end
292 cmd = 'hf legic load '..filename
293 core.console(cmd)
294
295 -- write pm3-buffer to Tag
296 for i=0, WriteBytes do
297 if ( i<5 or i>6) then
298 cmd = ('hf legic write 0x%02x 0x01'):format(i)
299 core.console(cmd)
300 --print(cmd)
301 elseif (i == 6) then
302 -- write DCF in reverse order (requires 'mosci-patch')
303 cmd = 'hf legic write 0x05 0x02'
304 core.console(cmd)
305 --print(cmd)
306 else
307 print("skipping byte 0x05 - will be written next step")
308 end
309 utils.Sleep(0.2)
310 end
311 end
312end
313
733eb420 314---
315-- read file into table
316function getInputBytes(infile)
317 local line
318 local bytes = {}
319 local fhi,err = io.open(infile)
320 if err then oops("faild to read from file ".. infile); return false; end
321 while true do
322 line = fhi:read()
323 if line == nil then break end
324 for byte in line:gmatch("%w+") do
325 table.insert(bytes, byte)
326 end
327 end
328 fhi:close()
329 print(#bytes .. " bytes from "..infile.." loaded")
330 return bytes
331end
332
333---
733eb420 334-- create tag-table helper
335function createTagTable()
336 local t={
337 ['MCD'] = '00',
338 ['MSN0']= '11',
339 ['MSN1']= '22',
340 ['MSN2']= '33',
341 ['MCC'] = 'cc',
342 ['DCFl']= 'ff',
343 ['DCFh']= 'ff',
344 ['Type']= 'GAM',
345 ['OLE'] = 0,
346 ['Stamp_len']= 18,
347 ['WRP'] = '00',
348 ['WRC'] = '00',
349 ['RD'] = '00',
350 ['raw'] = '9f',
351 ['SSC'] = 'ff',
352 ['data']= {},
353 ['bck'] = {},
354 ['MTC'] = {},
355 ['SEG'] = {}
356 }
357 return t
358end
359
360---
361-- put bytes into tag-table
362function bytesToTag(bytes, tag)
363 if(istable(tag)) then
364 tag.MCD =bytes[1];
365 tag.MSN0=bytes[2];
366 tag.MSN1=bytes[3];
367 tag.MSN2=bytes[4];
368 tag.MCC =bytes[5];
369 tag.DCFl=bytes[6];
370 tag.DCFh=bytes[7];
371 tag.raw =bytes[8];
372 tag.SSC =bytes[9];
373 tag.Type=getTokenType(tag.DCFl);
374 tag.OLE=bbit("0x"..tag.DCFl,7,1)
375 tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4))
376 tag.WRC=("%d"):format(bbit("0x"..bytes[8],4,3))
377 tag.RD=("%d"):format(bbit("0x"..bytes[8],7,1))
378 tag.Stamp_len=(tonumber(0xfc,10)-tonumber(bbit("0x"..tag.DCFh,0,8),10))
379 tag.data=bytesToTable(bytes, 10, 13)
380 tag.Bck=bytesToTable(bytes, 14, 20)
381 tag.MTC=bytesToTable(bytes, 21, 22)
382
383 print("Tag-Type: ".. tag.Type)
384 if (tag.Type=="SAM" and #bytes>23) then
385 tag=segmentsToTag(bytes, tag)
386 print((#tag.SEG+1).." Segment(s) found")
7f0cb92e 387 -- unsegmented Master-Token
388 -- only tag-data
389 else
390 for i=0, #tag.Bck do
391 table.insert(tag.data, tag.Bck[i])
392 end
393 tag.data[#tag.data]=tag.MTC[0]
394 tag.Bck=nil
395 --tag.MTC[0]=tag.MTC[1]
396 --tag.MTC[1]=nil
733eb420 397 end
398 print(#bytes.." bytes for Tag processed")
399 return tag
400 end
401 return oops("tag is no table in: bytesToTag ("..type(tag)..")")
402end
403
404---
7f0cb92e 405-- read Tag-Table in bytes-table
406function tagToBytes(tag)
733eb420 407 if (istable(tag)) then
7f0cb92e 408 local bytes = {}
409 local i, i2
410 -- main token-data
411 table.insert(bytes, tag.MCD)
412 table.insert(bytes, tag.MSN0)
413 table.insert(bytes, tag.MSN1)
414 table.insert(bytes, tag.MSN2)
415 table.insert(bytes, tag.MCC)
416 table.insert(bytes, tag.DCFl)
417 table.insert(bytes, tag.DCFh)
418 table.insert(bytes, tag.raw)
419 table.insert(bytes, tag.SSC)
420 -- raw token data
421 for i=0, #tag.data do
422 table.insert(bytes, tag.data[i])
733eb420 423 end
7f0cb92e 424 -- backup data
425 if(istable(tag.Bck)) then
426 for i=0, #tag.Bck do
427 table.insert(bytes, tag.Bck[i])
733eb420 428 end
733eb420 429 end
7f0cb92e 430 -- token-create-time / master-token crc
431 for i=0, #tag.MTC do
432 table.insert(bytes, tag.MTC[i])
733eb420 433 end
7f0cb92e 434 -- process segments
435 if (type(tag.SEG[0])=='table') then
436 for i=0, #tag.SEG do
437 for i2=1, #tag.SEG[i].raw+1 do
438 table.insert(bytes, #bytes+1, tag.SEG[i].raw[i2])
733eb420 439 end
7f0cb92e 440 table.insert(bytes, #bytes+1, tag.SEG[i].crc)
441 for i2=0, #tag.SEG[i].data-1 do
442 table.insert(bytes, #bytes+1, tag.SEG[i].data[i2])
733eb420 443 end
733eb420 444 end
7f0cb92e 445end
446 -- fill with zeros
447 for i=#bytes+1, 1024 do
448 table.insert(bytes, i, '00')
449 end
450 print(#bytes.." bytes of Tag dumped")
451 return bytes
452 end
453 return oops("tag is no table in tagToBytes ("..type(tag)..")")
733eb420 454end
455
456---
7f0cb92e 457-- make token
458function makeToken()
459 local mt={
460 ['Type'] = {"SAM", "SAM63", "SAM64", "IAM", "GAM"},
461 ['DCF'] = {"60ea", "31fa", "30fa", "80fa", "f0fa"},
462 ['WRP'] = {"15", "2", "2", "2", "2"},
463 ['WRC'] = {"01", "02", "02", "00", "00"},
464 ['RD'] = {"01", "00", "00", "00", "00"},
465 ['Stamp'] = {"00", "00", "00", "00", "00"},
466 ['Segment'] = {"0d", "c0", "04", "00", "be", "01", "02", "03", "04", "01", "02", "03", "04"}
467 }
468 ttype=""
469 for k, v in pairs(mt.Type) do
470 ttype=ttype..k..") "..v.." "
733eb420 471 end
7f0cb92e 472 mtq=tonumber(input("select number for Token-Type\n"..ttype, '1'), 10)
473 if (type(mtq)~="number") then return print("selection invalid!")
474 elseif (mtq>#mt.Type) then return print("selection invalid!")
475 else print("Token-Type '"..mt.Type[mtq].."' selected") end
476 local raw=calcHeaderRaw(mt.WRP[mtq], mt.WRC[mtq], mt.RD[mtq])
477 local mtCRC="00"
478
479 bytes={"01", "02", "03", "04", "cb", string.sub(mt.DCF[mtq], 0, 2), string.sub(mt.DCF[mtq], 3), raw,
480 "00", "00", "00", "00", "00", "00", "00", "00",
481 "00", "00", "00", "00", "00", "00"}
482 if (mtq==1) then
483 for i=0, #mt.Segment do
484 table.insert(bytes, mt.Segment[i])
733eb420 485 end
7f0cb92e 486 bytes[9]="ff"
733eb420 487 end
7f0cb92e 488 -- fill bytes
489 for i=#bytes, 1023 do table.insert(bytes, "00") end
490 -- if Master-Token -> calc Master-Token-CRC
491 if (mtq>1) then bytes[22]=calcMtCrc(bytes) end
492 local tempTag=createTagTable()
493 -- remove segment if MasterToken
494 if (mtq>1) then tempTag.SEG[0]=nil end
495 return bytesToTag(bytes, tempTag)
733eb420 496 end
497
7f0cb92e 498---
499-- edit token-data
500function editTag(tag)
501 -- for simulation it makes sense to edit everything
4e8fa8b4 502 local edit_sim="MCD MSN0 MSN1 MSN2 MCC DCFl DCFh WRP WRC RD"
7f0cb92e 503 -- on real tags it makes only sense to edit DCF, WRP, WRC, RD
504 local edit_real="DCFl DCFh WRP WRC RD"
505 if (confirm("do you want to edit non-writeable values (e.g. for simulation)?")) then
506 edit_tag=edit_sim
507 else edit_tag=edit_real end
508
509 if(istable(tag)) then
510 for k,v in pairs(tag) do
511 if(type(v)~="table" and type(v)~="boolean" and string.find(edit_tag, k)) then
512 tag[k]=input("value for: "..k, v)
733eb420 513 end
514 end
515
7f0cb92e 516 if (tag.Type=="SAM") then ttype="Header"; else ttype="Stamp"; end
517 if (confirm("do you want to edit "..ttype.." Data?")) then
518 -- master-token specific
519 if(istable(tag.Bck)==false) then
520 -- stamp-data length=(0xfc-DCFh)
521 -- on MT: SSC holds the Starting Stamp Character (Stamp0)
522 tag.SSC=input(ttype.."0: ", tag.SSC)
523 -- rest of stamp-bytes are in tag.data 0..n
524 for i=0, (tonumber(0xfc ,10)-("%d"):format('0x'..tag.DCFh))-2 do
525 tag.data[i]=input(ttype.. i+1 ..": ", tag.data[i])
733eb420 526 end
733eb420 527 else
7f0cb92e 528 --- on credentials byte7 should always be 9f and byte8 ff
529 -- on Master-Token not (even on SAM63/64 not)
530 -- tag.SSC=input(ttype.."0: ", tag.SSC)
531 for i=0, #tag.data do
532 tag.data[i]=input(ttype.. i ..": ", tag.data[i])
533 end
534end
733eb420 535 end
7f0cb92e 536
537 bytes=tagToBytes(tag)
538
539 --- check data-consistency (calculate tag.raw)
540 bytes[8]=calcHeaderRaw(tag.WRP, tag.WRC, tag.RD)
541
542 --- Master-Token specific
543 -- should be triggered if a SAM was converted to a non-SAM (user-Token to Master-Token)
544 -- or a Master-Token has being edited (also SAM64 & SAM63 - which are in fact Master-Token)
545 if(tag.Type~="SAM" or bytes[6]..bytes[7]~="60ea") then
546 -- calc new Master-Token crc
547 bytes[22]=calcMtCrc(bytes)
548 else
549 -- ensure tag.SSC set to 'ff' on credential-token (SAM)
550 bytes[9]='ff'
551 -- if a Master-Token was converted to a Credential-Token
552 -- lets unset the Time-Area to 00 00 (will contain Stamp-Data on MT)
553 bytes[21]='00'
554 bytes[22]='00'
733eb420 555end
556
7f0cb92e 557 tag=bytesToTag(bytes, tag)
733eb420 558 end
559end
560
561---
7f0cb92e 562-- calculates header-byte (addr 0x07)
563function calcHeaderRaw(wrp, wrc, rd)
564 local res
565 wrp=("%02x"):format(tonumber(wrp, 10))
566 rd=tonumber(rd, 16)
567 res=("%02x"):format(tonumber(wrp, 16)+tonumber(wrc.."0", 16)+((rd>0) and tonumber("8"..(rd-1), 16) or 0))
568 return res
733eb420 569end
570
571---
572-- dump virtual Tag-Data
573function dumpTag(tag)
574 local i, i2
575 local res
576 local dp=0
577 local raw=""
578 -- sytstem area
579 res ="\nCDF: System Area"
580 res= res.."\n"..dumpCDF(tag)
7f0cb92e 581 -- segments (user-token area)
582 if(tag.Type=="SAM") then
733eb420 583 res = res.."\n\nADF: User Area"
584 for i=0, #tag.SEG do
585 res=res.."\n"..dumpSegment(tag, i).."\n"
586 end
587 end
588 return res
589end
590
733eb420 591---
592-- get segmemnt-data from byte-table
593function getSegmentData(bytes, start, index)
594 local segment={
595 ['index'] = '00',
596 ['flag'] = 'c',
597 ['valid'] = 0,
598 ['last'] = 0,
599 ['len'] = 13,
600 ['raw'] = {'00', '00', '00', '00'},
601 ['WRP'] = 4,
602 ['WRC'] = 0,
603 ['RD'] = 0,
604 ['crc'] = '00',
605 ['data'] = {},
606 ['kgh'] = false
607 }
608 if (bytes[start]) then
609 local i
610 -- #index
611 segment.index = index
612 -- flag = high nibble of byte 1
613 segment.flag = string.sub(bytes[start+1],0,1)
614 -- valid = bit 6 of byte 1
615 segment.valid = bbit("0x"..bytes[start+1],6,1)
616 -- last = bit 7 of byte 1
617 segment.last = bbit("0x"..bytes[start+1],7,1)
618 -- len = (byte 0)+(bit0-3 of byte 1)
619 segment.len = tonumber(bbit("0x"..bytes[start+1],0,4)..bytes[start],16)
620 -- raw segment header
621 segment.raw = {bytes[start], bytes[start+1], bytes[start+2], bytes[start+3]}
622 -- wrp (write proteted) = byte 2
623 segment.WRP = tonumber(bytes[start+2],16)
624 -- wrc (write control) - bit 4-6 of byte 3
625 segment.WRC = tonumber(bbit("0x"..bytes[start+3],4,3),16)
626 -- rd (read disabled) - bit 7 of byte 3
627 segment.RD = tonumber(bbit("0x"..bytes[start+3],7,1),16)
628 -- crc byte 4
629 segment.crc = bytes[start+4]
630 -- segment-data starts at segment.len - segment.header - segment.crc
631 for i=0, (segment.len-5) do
632 segment.data[i]=bytes[start+5+i]
633 end
634 return segment
635 else return false;
636 end
637end
638
639---
640-- put segments from byte-table to tag-table
641function segmentsToTag(bytes, tag)
642 if(#bytes>23) then
643 local start=23
644 local i=-1
645 if (istable(tag)) then
646 repeat
647 i=i+1
648 tag.SEG[i]=getSegmentData(bytes, start, ("%02d"):format(i))
649 if (tag.Type=="SAM") then
650 if (checkKghCrc(tag, i)) then tag.SEG[i].kgh=true end
651 end
652 start=start+tag.SEG[i].len
653 until ((tag.SEG[i].valid==0) or tag.SEG[i].last==1 or i==126)
654 return tag
655 else return oops("tag is no table in: segmentsToTag ("..type(tag)..")") end
656 else print("no Segments: must be a MIM22") end
657end
658
733eb420 659---
7f0cb92e 660-- regenerate segment-header (after edit)
661function regenSegmentHeader(segment)
662 local seg=segment
663 local raw = segment.raw
733eb420 664 local i
7f0cb92e 665 -- len bit0..7 | len=12bit=low nibble of byte1..byte0
666 raw[1]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),0,8))
667 -- high nibble of len bit6=valid , bit7=last of byte 1 | ?what are bit 5+6 for? maybe kgh?
668 raw[2]=("%02x"):format(bbit("0x"..("%03x"):format(seg.len),4.4)..bbit("0x"..("%02x"):format((seg.valid*64)+(seg.last*128)),0,8))
669 -- WRP
670 raw[3]=("%02x"):format(bbit("0x"..("%02x"):format(seg.WRP),0,8))
671 -- WRC + RD
672 raw[4]=("%02x"):format(bbit("0x"..("%03x"):format(seg.WRC),4,3)..bbit("0x"..("%02x"):format(seg.RD*128),0,8))
673 -- flag
674 seg.flag=string.sub(raw[2],0,1)
675 --print(raw[1].." "..raw[2].." "..raw[3].." "..raw[4])
676 if(#seg.data>(seg.len-5)) then
677 print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
678 print("Data-Length has being reduced: removing ".. #seg.data-(seg.len-5) .." bytes from Payload");
679 for i=(seg.len-5), #seg.data-1 do
680 table.remove(seg.data)
733eb420 681end
7f0cb92e 682 elseif (#seg.data<(seg.len-5)) then
683 print("current payload: ".. #seg.data .." - desired payload: ".. seg.len-5)
684 print("Data-Length has being extended: adding "..(seg.len-5)-#seg.data.." bytes to Payload");
685 for i=#seg.data, (seg.len-5)-1 do
686 table.insert(seg.data, '00')
733eb420 687end
7f0cb92e 688 end
689 return seg
733eb420 690end
691
692---
7f0cb92e 693-- determine TagType (bits 0..6 of DCFlow)
694function getTokenType(DCFl)
695 --[[
696 0x00–0x2f IAM
697 0x30–0x6f SAM
698 0x70–0x7f GAM
699 ]]--
700 local tt = tonumber(bbit("0x"..DCFl,0,7),10)
701 if (tt >= 0 and tt <= 47) then tt = "IAM"
702 elseif (tt == 49) then tt = "SAM63"
703 elseif (tt == 48) then tt = "SAM64"
704 elseif (tt >= 50 and tt <= 111) then tt = "SAM"
705 elseif (tt >= 112 and tt <= 127) then tt = "GAM"
706 else tt = "???" end
707 return tt
708 end
733eb420 709
710---
7f0cb92e 711-- dump tag-system area
712function dumpCDF(tag)
713 local res=""
714 local i=0
715 local raw=""
733eb420 716 local bytes
7f0cb92e 717 if (istable(tag)) then
718 res = res.."MCD: "..tag.MCD..", MSN: "..tag.MSN0.." "..tag.MSN1.." "..tag.MSN2..", MCC: "..tag.MCC.."\n"
719 res = res.."DCF: "..tag.DCFl.." "..tag.DCFh..", Token_Type="..tag.Type.." (OLE="..tag.OLE.."), Stamp_len="..tag.Stamp_len.."\n"
720 res = res.."WRP="..tag.WRP..", WRC="..tag.WRC..", RD="..tag.RD..", raw="..tag.raw..((tag.raw=='9f') and (", SSC="..tag.SSC.."\n") or "\n")
721
722 -- credential (end-user tag)
723 if (tag.Type=="SAM") then
724 res = res.."Remaining Header Area\n"
725 for i=0, (#tag.data) do
726 res = res..tag.data[i].." "
733eb420 727 end
7f0cb92e 728 res = res.."\nBackup Area\n"
729 for i=0, (#tag.Bck) do
730 res = res..tag.Bck[i].." "
731 end
732 res = res.."\nTime Area\n"
733 for i=0, (#tag.MTC) do
734 res = res..tag.MTC[i].." "
735 end
736
737 -- Master Token specific
733eb420 738 else
7f0cb92e 739 res = res .."Master-Token Area\nStamp: "
740 res= res..tag.SSC.." "
741 for i=0, tag.Stamp_len-2 do
742 res = res..tag.data[i].." "
733eb420 743 end
7f0cb92e 744 res=res.."\nunused payload\n"
745 for i=0, (#tag.data-tag.Stamp_len-1) do
746 res = res..tag.data[i].." "
733eb420 747 end
7f0cb92e 748 bytes=tagToBytes(tag)
749 local mtcrc=calcMtCrc(bytes)
750 res=res.."\nMaster-Token CRC: "
751 res = res ..tag.MTC[1].." ("..((tag.MTC[1]==mtcrc) and "valid" or "error")..")"
752 end
753 return res
754 else print("no valid Tag in dumpCDF") end
755end
756
757---
758-- dump single segment
759function dumpSegment(tag, index)
760 local i=index
761 local i2
762 local dp=0 --data-position in table
763 local res="" --result
764 local raw="" --raw-header
765 -- segment
766 if ( (istable(tag.SEG[i])) and tag.Type=="SAM") then
767 if (istable(tag.SEG[i].raw)) then
768 for k,v in pairs(tag.SEG[i].raw) do
769 raw=raw..v.." "
770 end
771 end
772
773 -- segment header
774 res = res.."Segment "..("%02d"):format(tag.SEG[i].index)..": "
775 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).."), "
776 res = res .."len="..("%04d"):format(tag.SEG[i].len)..", WRP="..("%02x"):format(tag.SEG[i].WRP)..", WRC="..("%02x"):format(tag.SEG[i].WRC)..", "
777 res = res .."RD="..("%02x"):format(tag.SEG[i].RD)..", CRC="..tag.SEG[i].crc.." "
778 res = res .."("..(checkSegmentCrc(tag, i) and "valid" or "error")..")"
779 raw=""
780
781 -- WRC protected
782 if (tag.SEG[i].WRC>0) then
4e8fa8b4 783 res = res .."\nWRC protected area:\n"
7f0cb92e 784 for i2=dp, tag.SEG[i].WRC-1 do
785 res = res..tag.SEG[i].data[dp].." "
786 dp=dp+1
787 end
788 end
789
790 -- WRP mprotected
4e8fa8b4 791 if ((tag.SEG[i].WRP-tag.SEG[i].WRC)>0) then
792 res = res .."\nRemaining write protected area:\n"
793 for i2=dp, (tag.SEG[i].WRP-tag.SEG[i].WRC)-1 do
7f0cb92e 794 res = res..tag.SEG[i].data[dp].." "
795 dp=dp+1
796 end
797 end
798
799 -- payload
800 if (#tag.SEG[i].data-dp>0) then
801 res = res .."\nRemaining segment payload:\n"
802 for i2=dp, #tag.SEG[i].data-2 do
803 res = res..tag.SEG[i].data[dp].." "
804 dp=dp+1
805 end
806 if (tag.SEG[i].kgh) then
807 res = res..tag.SEG[i].data[dp].." (KGH: "..(checkKghCrc(tag, i) and "valid" or "error")..")"
808 else res = res..tag.SEG[i].data[dp] end
809 end
810 dp=0
811 return res
812 else
813 return print("Segment not found")
733eb420 814 end
815end
816
817---
818-- edit segment helper
819function editSegment(tag, index)
820 local k,v
821 local edit_possible="valid len RD WRP WRC Stamp Payload"
822 if (istable(tag.SEG[index])) then
823 for k,v in pairs(tag.SEG[index]) do
824 if(string.find(edit_possible,k)) then
825 tag.SEG[index][k]=tonumber(input(k..": ", v),10)
826 end
827 end
828 else print("Segment with Index: "..("%02d"):format(index).." not found in Tag")
829 return false
830 end
831 regenSegmentHeader(tag.SEG[index])
832 print("\n"..dumpSegment(tag, index).."\n")
833 return tag
834end
835
836---
837-- edit Segment Data
838function editSegmentData(data)
4e8fa8b4 839 local lc=check4LegicCash(data)
840 io.write("\n")
733eb420 841 if (istable(data)) then
842 for i=0, #data-1 do
843 data[i]=input("Data"..i..": ", data[i])
844 end
4e8fa8b4 845 if (lc) then data=fixLegicCash(data) end
733eb420 846 return data
847 else
848 print("no Segment-Data found")
849 end
850end
851
852---
853-- list available segmets in virtual tag
854function segmentList(tag)
855 local i
856 local res = ""
857 if (istable(tag.SEG[0])) then
858 for i=0, #tag.SEG do
859 res = res .. tag.SEG[i].index .. " "
860 end
861 return res
862 else print("no Segments found in Tag")
863 return false
864 end
865end
866
867---
868-- helper to selecting a segment
869function selectSegment(tag)
870 local sel
7f0cb92e 871 if (istable(tag.SEG[0])) then
733eb420 872 print("availabe Segments:\n"..segmentList(tag))
873 sel=input("select Segment: ", '00')
874 sel=tonumber(sel,10)
875 if (sel) then return sel
876 else return '0' end
877 else
878 print("\nno Segments found")
879 return false
880 end
881end
882
883---
884-- add segment
885function addSegment(tag)
886 local i
887 local segment={
888 ['index'] = '00',
889 ['flag'] = 'c',
890 ['valid'] = 1,
891 ['last'] = 1,
892 ['len'] = 13,
893 ['raw'] = {'0d', '40', '04', '00'},
894 ['WRP'] = 4,
895 ['WRC'] = 0,
896 ['RD'] = 0,
897 ['crc'] = '00',
898 ['data'] = {},
899 ['kgh'] = false
900 }
901 if (istable(tag.SEG[0])) then
902 tag.SEG[#tag.SEG].last=0
903 table.insert(tag.SEG, segment)
904 for i=0, 8 do
905 tag.SEG[#tag.SEG].data[i]=("%02x"):format(i)
906 end
907 tag.SEG[#tag.SEG].index=("%02d"):format(#tag.SEG)
908 return tag
909 else
910 print("no Segment-Table found")
911 end
912end
913
914---
7f0cb92e 915-- delete segment (except segment 00)
733eb420 916function delSegment(tag, index)
7f0cb92e 917 if (istable(tag.SEG[0])) then
733eb420 918 local i
919 if (type(index)=="string") then index=tonumber(index,10) end
920 if (index > 0) then
921 table.remove(tag.SEG, index)
922 for i=0, #tag.SEG do
923 tag.SEG[i].index=("%02d"):format(i)
924 end
925 end
926 if(istable(tag.SEG[#tag.SEG])) then tag.SEG[#tag.SEG].last=1 end
927 return tag
928end
7f0cb92e 929end
930
4e8fa8b4 931---
932-- return bytes 'sstrat' to 'send' from a table
933function dumpTable(tab, header, tstart, tend)
934 res=""
935 for i=tstart, tend do
936 res=res..tab[i].." "
937 end
938 if (string.len(header)==0) then return res
939 else return (header.." #"..(tend-tstart+1).."\n"..res) end
940end
941
942function dump3rdPartyCash1(tag , seg)
943 local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
944 local stamp=tag.SEG[seg].data[0].." "..tag.SEG[seg].data[1].." "..tag.SEG[seg].data[2]
945 local datastamp=tag.SEG[seg].data[20].." "..tag.SEG[seg].data[21].." "..tag.SEG[seg].data[22]
946 local balance=tonumber(tag.SEG[seg].data[32]..tag.SEG[seg].data[33] ,16)
947 local balancecrc=utils.Crc8Legic(uid..tag.SEG[seg].data[32]..tag.SEG[seg].data[33])
948 local mirror=tonumber(tag.SEG[seg].data[35]..tag.SEG[seg].data[36] ,16)
949 local mirrorcrc=utils.Crc8Legic(uid..tag.SEG[seg].data[35]..tag.SEG[seg].data[36])
950 local lastbal0=tonumber(tag.SEG[seg].data[39]..tag.SEG[seg].data[40] ,16)
951 local lastbal1=tonumber(tag.SEG[seg].data[41]..tag.SEG[seg].data[42] ,16)
952 local lastbal2=tonumber(tag.SEG[seg].data[43]..tag.SEG[seg].data[44] ,16)
953
954 test=""
955 -- display decoded/known stuff
956 print("\n------------------------------")
957 print("Tag-ID:\t\t "..uid)
958 print("Stamp:\t\t "..stamp)
959 print("UID-Mapping: \t\t"..("%06d"):format(tonumber(tag.SEG[seg].data[46]..tag.SEG[seg].data[47]..tag.SEG[seg].data[48], 16)))
960 print("------------------------------")
961 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])..")")
962 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])..")")
963 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])..")")
964
965 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])..")")
966 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])..")")
967 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])..")")
968 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])..")")
969 print("------------------------------")
970 print(string.format("Balance:\t\t %3.2f", balance/100).." ".."("..compareCrc(balancecrc, tag.SEG[seg].data[34])..")")
971 print(string.format("Shadow:\t\t\t %3.2f", mirror/100).." ".."("..compareCrc(balancecrc, tag.SEG[seg].data[37])..")")
972 print("------------------------------")
973 print(string.format("History 1:\t\t %3.2f", lastbal0/100))
974 print(string.format("History 2:\t\t %3.2f", lastbal1/100))
975 print(string.format("History 3:\t\t %3.2f", lastbal2/100))
976 print("------------------------------\n")
977end
978---
979-- compare two bytes
980function compareCrc(calc, guess)
981 calc=("%02x"):format(calc)
982 if (calc==guess) then return "valid"
983 else return "error "..calc.."!="..guess end
984end
985
986---
987-- compare 4 bytes
988function compareCrc16(calc, guess)
989 calc=("%04x"):format(calc)
990 if (calc==guess) then return "valid"
991 else return "error "..calc.."!="..guess end
992end
993
994---
995-- repair / fix (yet known) crc's of a '3rd Party Cash-Segment'
996-- not all bytes know until yet !!
997function fix3rdPartyCash1(uid, data)
998 if(#data==95) then
999 -- checksum 1
1000 data[31]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 19, 30)))
1001 -- checksum 2
1002 data[34]=("%02x"):format(utils.Crc8Legic(uid..data[32]..data[33]))
1003 -- checksum 3
1004 data[37]=("%02x"):format(utils.Crc8Legic(uid..data[35]..data[36]))
1005 -- checksum 4
1006 data[55]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 46, 54)))
1007 -- checksum 5
1008 data[62]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)))
1009 -- checksum 6
1010 data[73]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 63, 72)))
1011 -- checksum 7
1012 data[89]=("%02x"):format(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)))
1013 return data
1014 end
1015end
1016
1017---
1018-- edit uid 3rd party cash
1019function edit3rdUid(mapid, uid, data)
1020 mapid=("%06x"):format(tonumber(mapid, 10))
1021 data[46]=string.sub(mapid, 0 ,2)
1022 data[47]=string.sub(mapid, 3 ,4)
1023 data[48]=string.sub(mapid, 5 ,6)
1024 return fix3rdPartyCash1(uid, data)
1025end
1026
1027---
1028-- edit balance 3rd party cash
1029function edit3rdCash(new_cash, uid, data)
1030 new_cash=("%04x"):format(new_cash)
1031 data[32]=string.sub(new_cash, 0,2)
1032 data[33]=string.sub(new_cash, 3,4)
1033 data[34]=("%02x"):format(utils.Crc8Legic(uid..new_cash))
1034 data[35]=string.sub(new_cash, 0,2)
1035 data[36]=string.sub(new_cash, 3,4)
1036 data[37]=("%02x"):format(utils.Crc8Legic(uid..new_cash))
1037 data[39]=string.sub(new_cash, 0,2)
1038 data[40]=string.sub(new_cash, 3,4)
1039 data[41]='00'
1040 data[42]='00'
1041 data[43]='00'
1042 data[44]='00'
1043 return fix3rdPartyCash1(uid, data)
1044end
1045
1046---
1047-- repair / fix crc's of a 'Legic-Cash-Segment'
1048function fixLegicCash(data)
1049 if(#data==32 and data[7]=="01") then
1050 local crc1, crc2, crc3
1051 crc1=("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12)))
1052 crc2=("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20)))
1053 crc3=("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29)))
1054 data[13]=string.sub(crc1, 0, 2)
1055 data[14]=string.sub(crc1, 3, 4)
1056 data[21]=string.sub(crc2, 0, 2)
1057 data[22]=string.sub(crc2, 3, 4)
1058 data[30]=string.sub(crc3, 0, 2)
1059 data[31]=string.sub(crc3, 3, 4)
1060 return data
1061 end
1062end
1063
1064---
1065-- chack for signature of a '3rd Party Cash-Segment'
1066-- not all bytes know until yet !!
1067function check43rdPartyCash1(uid, data)
1068 if(#data==95) then
1069 -- too explicit checking will avoid fixing ;-)
1070 if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 19, 30)), data[31])=="valid") then
1071 --if (compareCrc(utils.Crc8Legic(uid..data[32]..data[33]), data[34])=="valid") then
1072 --if (compareCrc(utils.Crc8Legic(uid..data[35]..data[36]), data[37])=="valid") then
1073 --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 56, 61)), data[62])=="valid") then
1074 --if (compareCrc(utils.Crc8Legic(uid..dumpTable(data, "", 74, 88)), data[89])=="valid") then
1075 io.write("3rd Party Cash-Segment detected ")
1076 return true
1077 --end
1078 --end
1079 --end
1080 --end
1081 end
1082 end
1083 return false
1084end
1085
1086---
1087-- chack for signature of a 'Legic-Cash-Segment'
1088function check4LegicCash(data)
1089 if(#data==32) then
1090 local stamp_len=(#data-25)
1091 local stamp=""
1092 for i=0, stamp_len-1 do
1093 stamp=stamp..data[i].." "
1094 end
1095 if (data[7]=="01") then
1096 if (("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12))) == data[13]..data[14]) then
1097 if (("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20))) == data[21]..data[22]) then
1098 if (("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29))) == data[30]..data[31]) then
1099 io.write("Legic-Cash Segment detected ")
1100 return true
1101 end
1102 end
1103 end
1104 end
1105 end
1106 return false
1107end
7f0cb92e 1108---
1109-- calculate Master-Token crc
1110function calcMtCrc(bytes)
1111 --print(#bytes)
1112 local cmd=bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[7]..bytes[6]..bytes[8]
1113 local len=(tonumber(0xfc ,10)-("%d"):format('0x'..bytes[7]))
1114 for i=1, len do
1115 cmd=cmd..bytes[8+i]
1116 end
1117 local res=("%02x"):format(utils.Crc8Legic(cmd))
1118 return res
1119end
1120
1121---
1122-- check all segmnet-crc
1123function checkAllSegCrc(tag)
1124 if (istable(tag.SEG[0])) then
1125 for i=0, #tag.SEG do
1126 crc=calcSegmentCrc(tag, i)
1127 tag.SEG[i].crc=crc
1128 end
1129 else return print("Matser-Token / unsegmented Tag") end
1130end
1131
1132---
1133-- check all segmnet-crc
1134function checkAllKghCrc(tag)
1135 if (istable(tag.SEG[0])) then
1136 for i=0, #tag.SEG do
1137 crc=calcKghCrc(tag, i)
1138 if (tag.SEG[i].kgh) then
1139 tag.SEG[i].data[#tag.SEG[i].data-1]=crc
1140 end
1141 end
1142 end
1143end
1144
1145---
1146-- build kghCrc credentials
1147function kghCrcCredentials(tag, segid)
1148 if (istable(tag) and istable(tag.SEG[0])) then
1149 local x='00'
1150 if (type(segid)=="string") then segid=tonumber(segid,10) end
1151 if (segid>0) then x='93' end
1152 local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2..("%02x"):format(tag.SEG[segid].WRP)
1153 cred = cred..("%02x"):format(tag.SEG[segid].WRC)..("%02x"):format(tag.SEG[segid].RD)..x
1154 for i=0, #tag.SEG[segid].data-2 do
1155 cred = cred..tag.SEG[segid].data[i]
1156 end
1157 return cred
1158 end
1159end
1160
1161---
1162-- validate kghCRC to segment in tag-table
1163function checkKghCrc(tag, segid)
1164 if (type(tag.SEG[segid])=='table') then
1165 if (tag.data[3]=="11" and tag.raw=="9f" and tag.SSC=="ff") then
1166 local data=kghCrcCredentials(tag, segid)
1167 if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].data[tag.SEG[segid].len-5-1]) then return true; end
1168 else return false; end
1169 else oops("'Kaba Group header' detected but no Segment-Data found") end
1170end
1171
1172---
1173-- calcuate kghCRC for a given segment
1174function calcKghCrc(tag, segid)
1175 if (istable(tag.SEG[0])) then
1176 -- check if a 'Kaber Group Header' exists
1177 local i
1178 local data=kghCrcCredentials(tag, segid)
1179 return ("%02x"):format(utils.Crc8Legic(data))
1180 end
1181end
1182
1183---
1184-- build segmentCrc credentials
1185function segmentCrcCredentials(tag, segid)
1186 if (istable(tag.SEG[0])) then
1187 local cred = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
1188 cred = cred ..tag.SEG[segid].raw[1]..tag.SEG[segid].raw[2]..tag.SEG[segid].raw[3]..tag.SEG[segid].raw[4]
1189 return cred
1190 else return print("Master-Token / unsegmented Tag!") end
1191end
1192
1193---
1194-- validate segmentCRC for a given segment
1195function checkSegmentCrc(tag, segid)
1196 local data=segmentCrcCredentials(tag, segid)
1197 if (("%02x"):format(utils.Crc8Legic(data))==tag.SEG[segid].crc) then
1198 return true
1199 end
1200 return false
1201end
1202
1203---
1204-- calculate segmentCRC for a given segment
1205function calcSegmentCrc(tag, segid)
1206 if (istable(tag.SEG[0])) then
1207 -- check if a 'Kaber Group Header' exists
1208 local data=segmentCrcCredentials(tag, segid)
1209 return ("%02x"):format(utils.Crc8Legic(data))
1210 end
1211end
733eb420 1212
1213---
1214-- helptext for modify-mode
1215function modifyHelp()
1216 local t=[[
1217
4e8fa8b4 1218 Data I/O Segment Manipulation Token-Data
1219 ----------------- -------------------- -----------------
1220 rt => read Tag as => add Segment mt => make Token
1221 wt => write Tag es => edit Segment Header et => edit Token data
1222 ct => copy io Tag ed => edit Segment Data tk => toggle KGH-Flag
7f0cb92e 1223 tc => copy oi Tag rs => remove Segment
4e8fa8b4 1224 tt => toggle Tag cc => check Segment-CRC File I/O
1225 di => dump inTag ck => check KGH -----------------
1226 do => dump outTag e3p => edit 3rd Party Cash lf => load File
1227 ds => dump Segment sf => save File
1228 dlc => dump Legic-Cash xf => xor to File
1229 d3p => dump 3rd Party Cash
1230 r3p => raw 3rd Party Cash
1231
1232
1233 q => quit
733eb420 1234 ]]
1235 return t
1236end
1237
4e8fa8b4 1238
733eb420 1239---
1240-- modify Tag (interactive)
1241function modifyMode()
4e8fa8b4 1242 local i, backupTAG, outTAG, inTAG, outfile, infile, sel, segment, bytes, outbytes
1243
733eb420 1244 actions = {
1245 ["h"] = function(x)
4e8fa8b4 1246 print(" Version: "..version);
1247 print(modifyHelp().."\n".."tags im Memory: "..(istable(inTAG) and ((currentTag=='inTAG') and "*mainTAG" or "mainTAG") or "").." "..(istable(backupTAG) and ((currentTag=='backupTAG') and "*backupTAG" or "backupTAG") or ""))
733eb420 1248 end,
4e8fa8b4 1249 ["rt"] = function(x)
1250 inTAG=readFromPM3();
1251 --actions.di()
1252 end,
733eb420 1253 ["wt"] = function(x)
7f0cb92e 1254 if(istable(inTAG.SEG)) then
733eb420 1255 local taglen=22
7f0cb92e 1256 if (istable(inTAG.Bck)) then
733eb420 1257 for i=0, #inTAG.SEG do
1258 taglen=taglen+inTAG.SEG[i].len+5
1259 end
7f0cb92e 1260 end
4e8fa8b4 1261
1262 local uid_old=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
733eb420 1263 -- read new tag (output tag)
1264 outTAG=readFromPM3()
1265 outbytes=tagToBytes(outTAG)
1266 -- copy 'inputbuffer' to 'outputbuffer'
1267 inTAG.MCD = outbytes[1]
1268 inTAG.MSN0 = outbytes[2]
1269 inTAG.MSN1 = outbytes[3]
1270 inTAG.MSN2 = outbytes[4]
1271 inTAG.MCC = outbytes[5]
7f0cb92e 1272 -- recheck all segments-crc/kghcrc (only on a credential)
1273 if(istable(inTAG.Bck)) then
733eb420 1274 checkAllSegCrc(inTAG)
1275 checkAllKghCrc(inTAG)
4e8fa8b4 1276 local uid_new=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1277 for i=0, #inTAG.SEG do
1278 if (check43rdPartyCash1(uid_old, inTAG.SEG[i].data)) then
1279 io.write(" - fixing known checksums ... ")
1280 inTAG.SEG[i].data=fix3rdPartyCash1(uid_new, inTAG.SEG[i].data)
1281 io.write(" done\n")
1282 end
1283 end
7f0cb92e 1284 end
733eb420 1285 --get bytes from ready outTAG
1286 bytes=tagToBytes(inTAG)
7f0cb92e 1287 -- mater-token-crc
1288 if (inTAG.Type~="SAM") then bytes[22]=calcMtCrc(bytes) end
733eb420 1289 if (bytes) then
1290 writeFile(bytes, 'MylegicClone.hex')
1291 writeToTag(bytes, taglen, 'MylegicClone.hex')
7f0cb92e 1292 actions.rt('')
733eb420 1293 end
1294 end
1295 end,
4e8fa8b4 1296 ---
1297 -- switich and copy virtual tags
1298
733eb420 1299 ["ct"] = function(x)
4e8fa8b4 1300 print("copy mainTAG to backupTAG")
1301 outTAG=deepCopy(inTAG)
1302 backupTAG=deepCopy(inTAG)
733eb420 1303 end,
1304 ["tc"] = function(x)
4e8fa8b4 1305 print("copy backupTAG to mainTAG")
1306 inTAG=deepCopy(backupTAG)
1307 end,
1308 ["tt"] = function(x)
1309 print("toggle to "..((currentTag=='inTAG') and "backupTAG" or "mainTAG"))
1310 if(currentTag=="inTAG") then
1311 outTAG=deepCopy(inTAG)
1312 inTAG=deepCopy(backupTAG)
1313 currentTag='backupTAG'
1314 else
1315 inTAG=deepCopy(outTAG)
1316 currentTag='inTAG'
1317 end
733eb420 1318 end,
1319 ["lf"] = function(x)
7f0cb92e 1320 if (file_check(x)) then filename=x
1321 else filename=input("enter filename: ", "legic.temp") end
733eb420 1322 inTAG=readFile(filename)
1323 end,
1324 ["sf"] = function(x)
1325 if(istable(inTAG)) then
1326 outfile=input("enter filename:", "legic.temp")
1327 bytes=tagToBytes(inTAG)
1328 --bytes=xorBytes(bytes, inTAG.MCC)
1329 if (bytes) then
1330 writeFile(bytes, outfile)
1331 end
1332 end
1333 end,
1334 ["xf"] = function(x)
1335 if(istable(inTAG)) then
1336 outfile=input("enter filename:", "legic.temp")
1337 crc=input("enter new crc: ('00' for a plain dump)", inTAG.MCC)
1338 print("obfuscate witth: "..crc)
1339 bytes=tagToBytes(inTAG)
1340 bytes[5]=crc
1341 if (bytes) then
1342 writeFile(bytes, outfile)
1343 end
1344 end
1345 end,
4e8fa8b4 1346 ["di"] = function(x)
1347 if (istable(inTAG)) then
1348 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1349 if(istable(inTAG.SEG[0])) then
1350 for i=0, #inTAG.SEG do
1351 if(check43rdPartyCash1(uid, inTAG.SEG[i].data)) then
1352 io.write("in Segment index: "..inTAG.SEG[i].index.."\n")
1353 elseif(check4LegicCash(inTAG.SEG[i].data)) then
1354 io.write("in Segment index: "..inTAG.SEG[i].index.."\n")
1355 lc=true;
1356 lci=inTAG.SEG[i].index;
1357 end
1358 end
1359 end
1360 print("\n"..dumpTag(inTAG).."\n")
1361 if (lc) then actions["dlc"](lci) end
1362 end
1363 end,
1364 ["do"] = function(x) if (istable(backupTAG)) then print("\n"..dumpTag(backupTAG).."\n") end end,
733eb420 1365 ["ds"] = function(x)
7f0cb92e 1366 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
1367 else sel=selectSegment(inTAG) end
733eb420 1368 if (sel) then print("\n"..(dumpSegment(inTAG, sel) or "no Segments available").."\n") end
1369 end,
1370 ["es"] = function(x)
7f0cb92e 1371 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
1372 else sel=selectSegment(inTAG) end
733eb420 1373 if (sel) then
7f0cb92e 1374 if(istable(inTAG.SEG[0])) then
733eb420 1375 inTAG=editSegment(inTAG, sel)
1376 inTAG.SEG[sel]=regenSegmentHeader(inTAG.SEG[sel])
1377 else print("no Segments in Tag") end
1378 end
1379 end,
1380 ["as"] = function(x)
1381 if (istable(inTAG.SEG[0])) then
1382 inTAG=addSegment(inTAG)
1383 inTAG.SEG[#inTAG.SEG-1]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG-1])
1384 inTAG.SEG[#inTAG.SEG]=regenSegmentHeader(inTAG.SEG[#inTAG.SEG])
7f0cb92e 1385 else print("Master-Token / unsegmented Tag!")
733eb420 1386 end
1387 end,
1388 ["rs"] = function(x)
7f0cb92e 1389 if (istable(inTAG.SEG[0])) then
1390 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
1391 else sel=selectSegment(inTAG) end
733eb420 1392 inTAG=delSegment(inTAG, sel)
1393 for i=0, #inTAG.SEG do
1394 inTAG.SEG[i]=regenSegmentHeader(inTAG.SEG[i])
1395 end
1396 end
1397 end,
1398 ["ed"] = function(x)
7f0cb92e 1399 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
1400 else sel=selectSegment(inTAG) end
733eb420 1401 if (sel) then
1402 inTAG.SEG[sel].data=editSegmentData(inTAG.SEG[sel].data)
1403 end
1404 end,
7f0cb92e 1405 ["et"] = function(x)
1406 if (istable(inTAG)) then
1407 editTag(inTAG)
1408 end
1409 end,
1410 ["mt"] = function(x) inTAG=makeToken(); actions.di() end,
733eb420 1411 ["ts"] = function(x)
7f0cb92e 1412 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
1413 else sel=selectSegment(inTAG) end
733eb420 1414 regenSegmentHeader(inTAG.SEG[sel])
1415 end,
1416 ["tk"] = function(x)
7f0cb92e 1417 if (istable(inTAG) and istable(inTAG.SEG[0])) then
1418 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
1419 else sel=selectSegment(inTAG) end
733eb420 1420 if(inTAG.SEG[sel].kgh) then inTAG.SEG[sel].kgh=false
1421 else inTAG.SEG[sel].kgh=true end
7f0cb92e 1422 end
733eb420 1423 end,
1424 ["k"] = function(x)
7f0cb92e 1425 if (type(x)=="string" and string.len(x)>0) then
733eb420 1426 print(("%02x"):format(utils.Crc8Legic(x)))
7f0cb92e 1427 end
733eb420 1428 end,
4e8fa8b4 1429 ["xb"] = function(x)
1430 end,
733eb420 1431 ["xc"] = function(x)
7f0cb92e 1432 if (istable(inTAG) and istable(inTAG.SEG[0])) then
1433 if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10)
1434 else sel=selectSegment(inTAG) end
1435 print("k "..kghCrcCredentials(inTAG, sel))
1436 end
733eb420 1437 end,
4e8fa8b4 1438 ["dlc"] = function(x)
1439 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1440 -- if segment index was user defined
1441 if (type(x)=="string" and string.len(x)>0) then
1442 x=tonumber(x,10)
1443 print(string.format("User-Selected Index %02d", x))
1444 -- or try to find match
1445 else x=autoSelectSegment(inTAG, "legiccash") end
1446 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1447 if (istable(inTAG.SEG[x])) then
1448 io.write("in Segment "..inTAG.SEG[x].index.." :\n")
1449 print("--------------------------------\n\tLegic-Cash Values\n--------------------------------")
1450 local limit, curr, balance, rid, tcv
1451 -- currency of balance & limit
1452 curr=currency[inTAG.SEG[x].data[8]..inTAG.SEG[x].data[9]]
1453 -- maximum balance
1454 limit=string.format("%4.2f", tonumber(inTAG.SEG[x].data[10]..inTAG.SEG[x].data[11]..inTAG.SEG[x].data[12], 16)/100)
1455 -- current balance
1456 balance=string.format("%4.2f", tonumber(inTAG.SEG[x].data[15]..inTAG.SEG[x].data[16]..inTAG.SEG[x].data[17], 16)/100)
1457 -- reader-id who wrote last transaction
1458 rid=tonumber(inTAG.SEG[x].data[18]..inTAG.SEG[x].data[19]..inTAG.SEG[x].data[20], 16)
1459 -- transaction counter value
1460 tcv=tonumber(inTAG.SEG[x].data[29], 16)
1461 print("Currency:\t\t "..curr)
1462 print("Limit:\t\t\t "..limit)
1463 print("Balance:\t\t "..balance)
1464 print("Transaction Counter:\t "..tcv)
1465 print("Reader-ID:\t\t "..rid.."\n--------------------------------\n")
1466 end
1467 --end
1468 end,
1469 ["df"] = function(x)
1470 actions["lf"](x)
1471 res=""
1472 for i=0, #inTAG.SEG[1].data do
1473 res=res..inTAG.SEG[1].data[i]
1474 end
1475 print(res)
1476 end,
1477 ["d3p"] = function(x)
1478 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1479 -- if segment index was user defined
1480 if (type(x)=="string" and string.len(x)>0) then
1481 x=tonumber(x,10)
1482 print(string.format("User-Selected Index %02d", x))
1483 -- or try to find match
1484 else x=autoSelectSegment(inTAG, "3rdparty") end
1485
1486 if (istable(inTAG) and istable(inTAG.SEG[x]) and inTAG.SEG[x].len == 100) then
1487 uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1488 if (check43rdPartyCash1(uid, inTAG.SEG[x].data)) then
1489 dump3rdPartyCash1(inTAG, x)
1490 end
1491 end
1492 end,
1493 ["r3p"] = function(x)
1494 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1495 -- if segment index was user defined
1496 if (type(x)=="string" and string.len(x)>0) then
1497 x=tonumber(x,10)
1498 print(string.format("User-Selected Index %02d", x))
1499 -- or try to find match
1500 else x=autoSelectSegment(inTAG, "3rdparty") end
1501 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1502 if (istable(inTAG.SEG[x])) then
1503 print("\n\t\tStamp : "..dumpTable(inTAG.SEG[x].data, "", 0 , 2))
1504 print("\t\tBlock 0: "..dumpTable(inTAG.SEG[x].data, "", 3 , 18))
1505 print()
1506 print("\t\tBlock 1: "..dumpTable(inTAG.SEG[x].data, "", 19, 30))
1507 print("checksum 1: Tag-ID .. Block 1 => LegicCrc8 = "..inTAG.SEG[x].data[31].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(inTAG.SEG[x].data, "", 19, 30)), inTAG.SEG[x].data[31])..")")
1508 print()
1509 print("\t\tBlock 2: "..dumpTable(inTAG.SEG[x].data, "", 32, 33))
1510 print("checksum 2: Block 2 => LegicCrc8 = "..inTAG.SEG[x].data[34].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(inTAG.SEG[x].data, "", 32, 33)), inTAG.SEG[x].data[34])..")")
1511 print()
1512 print("\t\tBlock 3: "..dumpTable(inTAG.SEG[x].data, "", 35, 36))
1513 print("checksum 3: Block 3 => LegicCrc8 = "..inTAG.SEG[x].data[37].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(inTAG.SEG[x].data, "", 35, 36)), inTAG.SEG[x].data[37])..")")
1514 print()
1515 print("\t\tyet unknown: "..inTAG.SEG[x].data[38])
1516 print()
1517 print("\t\tHisatory 1: "..dumpTable(inTAG.SEG[x].data, "", 39, 40))
1518 print("\t\tHisatory 2: "..dumpTable(inTAG.SEG[x].data, "", 41, 42))
1519 print("\t\tHisatory 3: "..dumpTable(inTAG.SEG[x].data, "", 43, 44))
1520 print()
1521 print("\t\tyet unknown: "..inTAG.SEG[x].data[45])
1522 print()
1523 print("\t\tKGH-UID HEX: "..dumpTable(inTAG.SEG[x].data, "", 46, 48))
1524 print("\t\tBlock 4: "..dumpTable(inTAG.SEG[x].data, "", 49, 54))
1525 print("checksum 4: Tag-ID .. KGH-UID .. Block 4 => LegicCrc8 = "..inTAG.SEG[x].data[55].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(inTAG.SEG[x].data, "", 46, 54)), inTAG.SEG[x].data[55])..")")
1526 print()
1527 print("\t\tBlock 5: "..dumpTable(inTAG.SEG[x].data, "", 56, 61))
1528 print("checksum 5: Tag-ID .. Block 5 => LegicCrc8 = "..inTAG.SEG[x].data[62].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(inTAG.SEG[x].data, "", 56, 61)), inTAG.SEG[x].data[62])..")")
1529 print()
1530 print("\t\tBlock 6: "..dumpTable(inTAG.SEG[x].data, "", 63, 72))
1531 print("checksum 6: Tag-ID .. Block 6 => LegicCrc8 = "..inTAG.SEG[x].data[73].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(inTAG.SEG[x].data, "", 63, 72)), inTAG.SEG[x].data[73])..")")
1532 print()
1533 print("\t\tBlock 7: "..dumpTable(inTAG.SEG[x].data, "", 74, 88))
1534 print("checksum 7: Tag-ID .. Block 7 => LegicCrc8 = "..inTAG.SEG[x].data[89].." ("..compareCrc(utils.Crc8Legic(uid..dumpTable(inTAG.SEG[x].data, "", 74, 88)), inTAG.SEG[x].data[89])..")")
1535 print()
1536 print("\t\tBlock 8: "..dumpTable(inTAG.SEG[x].data, "", 90, 94))
1537 end
1538 end,
1539 ["e3p"] = function(x)
1540 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1541 -- if segment index was user defined
1542 if (type(x)=="string" and string.len(x)>0) then
1543 x=tonumber(x,10)
1544 print(string.format("User-Selected Index %02d", x))
1545 -- or try to find match
1546 else x=autoSelectSegment(inTAG, "3rdparty") end
1547
1548 if (istable(inTAG) and istable(inTAG.SEG[x]) and inTAG.SEG[x].len == 100) then
1549 --if (check43rdPartyCash1(uid, inTAG.SEG[x].data)) then
1550 -- change Balance
1551 if (confirm("\nedit Balance?")) then
1552 local new_cash=input("enter new Balance without comma or currency", "100")
1553 inTAG.SEG[x].data=edit3rdCash(new_cash, uid, inTAG.SEG[x].data)
1554 end
1555 -- change User-ID (used for online-account-mapping)
1556 if (confirm("\nedit UserID-Mapping?")) then
1557 local new_mapid=input("enter new UserID (6-digit value)", "012345")
1558 inTAG.SEG[x].data=edit3rdUid(new_mapid, uid, inTAG.SEG[x].data)
1559 end
1560 if (confirm("\nedit Stamp?")) then
1561 local new_stamp=input("enter new Stamp", getSegmentStamp(inTAG.SEG[x]))
1562 inTAG.SEG[x].data=editStamp(new_stamp, uid, inTAG.SEG[x].data)
1563 new_stamp=getSegmentStamp(inTAG.SEG[x], 'true')
1564 print("stamp_bytes: "..#new_stamp)
1565 -- replace stamp in 'block 1' also
1566 io.write("editing stamp in Block 1 also ")
1567 for i=20, (20+#new_stamp-1) do
1568 inTAG.SEG[x].data[i]=new_stamp[i-19]
1569 io.write(".");
1570 end
1571 print(" done")
1572 -- fix known checksums
1573 inTAG.SEG[x].data=fix3rdPartyCash1(uid, inTAG.SEG[x].data)
1574 end
1575
1576 -- print out new settings
1577 dump3rdPartyCash1(inTAG, x)
1578 --end
1579 end
1580 end,
1581 ["gs"] = function(x)
1582 if(type(x)=="string" and string.len(x)>=2) then x=tonumber(x, 10)
1583 else x=selectSegment(inTAG) end
1584 local stamp=getSegmentStamp(inTAG.SEG[x])
1585 print("Stamp : "..stamp)
1586 stamp=str2bytes(stamp)
1587 print("lenght: "..#stamp)
1588 end,
1589 ["c6"] = function(x) local crc16=string.format("%4.04x", utils.Crc16(x))
1590 print(string.sub(crc16, 0,2).." "..string.sub(crc16, 3,4))
1591 end,
733eb420 1592 ["cc"] = function(x) if (istable(inTAG)) then checkAllSegCrc(inTAG) end end,
4e8fa8b4 1593 ["cb"] = function(x)
1594 if (istable(inTAG)) then
1595 print("purge BackupArea")
1596 inTAG=clearBackupArea(inTAG)
1597 end
1598 end,
1599 ["f3p"] = function(x)
1600 if(type(x)=="string" and string.len(x)>=2) then x=tonumber(x, 10)
1601 else x=selectSegment(inTAG) end
1602 if (istable(inTAG.SEG[x])) then
1603 local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2
1604 inTAG.SEG[x].data=fix3rdPartyCash1(uid, inTAG.SEG[x].data)
1605 end
1606 end,
733eb420 1607 ["ck"] = function(x) if (istable(inTAG)) then checkAllKghCrc(inTAG) end end,
733eb420 1608 }
1609 print("modify-modus! enter 'h' for help or 'q' to quit")
1610 repeat
1611 ic=input("Legic command? ('h' for help - 'q' for quit)", "h")
1612 -- command actions
4e8fa8b4 1613 if (type(actions[string.lower(string.sub(ic,0,3))])=='function') then
1614 actions[string.lower(string.sub(ic,0,3))](string.sub(ic,5))
733eb420 1615 elseif (type(actions[string.lower(string.sub(ic,0,2))])=='function') then
1616 actions[string.lower(string.sub(ic,0,2))](string.sub(ic,4))
4e8fa8b4 1617 elseif (type(actions[string.lower(string.sub(ic,0,1))])=='function') then
1618 actions[string.lower(string.sub(ic,0,1))](string.sub(ic,3))
7f0cb92e 1619 else actions.h('') end
733eb420 1620 until (string.sub(ic,0,1)=="q")
1621end
1622
4e8fa8b4 1623function clearBackupArea(tag)
1624 for i=1, #tag.Bck do
1625 tag.Bck[i]='00'
1626 end
1627 return tag
1628end
1629
1630function getSegmentStamp(seg, bytes)
1631 local stamp=""
1632 local stamp_len=7
1633 --- the 'real' stamp on MIM is not really easy to tell for me since the 'data-block' covers stamp0..stampn+data0..datan
1634 -- there a no stamps longer than 7 bytes & they are write-protected by default , and I have not seen user-credntials
1635 -- with stamps smaller 3 bytes (except: Master-Token)
1636 -- WRP -> Read/Write Protection
1637 -- WRC -> Read/Write Condition
1638 -- 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
1639 if (seg.WRP<7) then stamp_len=(seg.WRP) end
1640 for i=1, (stamp_len) do
1641 stamp=stamp..seg.data[i-1]
1642 end
1643 if (bytes) then
1644 stamp=str2bytes(stamp)
1645 return stamp
1646 else return stamp end
1647end
1648
1649function str2bytes(s)
1650 local res={}
1651 if (string.len(s)%2~=0) then return print("stamp should be a even hexstring e.g.: deadbeef or 0badc0de") end
1652 for i=1, string.len(s), 2 do
1653 table.insert(res, string.sub(s,i,(i+1)))
1654 end
1655 return res
1656end
1657
1658function editStamp(new_stamp, uid, data)
1659 local stamp=str2bytes(new_stamp)
1660 for i=0, #stamp-1 do
1661 data[i]=stamp[i+1]
1662 end
1663 -- now fill in stamp
1664 for i=0, (string.len(new_stamp)/2)-1 do
1665 data[i]=stamp[i+1]
1666 end
1667 return fix3rdPartyCash1(uid, data)
1668end
1669
1670function autoSelectSegment(tag, s)
1671 local uid=tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
1672 local x=#tag.SEG+1
1673 local res = false
1674 io.write("autoSelect ")
1675 --- search for desired segment-type
1676 -- 3rd Party Segment
1677 if (s=="3rdparty") then
1678 repeat
1679 io.write(". ")
1680 x=x-1
1681 res=check43rdPartyCash1(uid, tag.SEG[x].data)
1682 until ( res or x==0 )
1683 end
1684 -- Legic-Cash Segment
1685 if (s=="legiccash") then
1686 repeat
1687 io.write(". ")
1688 x=x-1
1689 res=check4LegicCash(tag.SEG[x].data)
1690 until ( res or x==0 )
1691 end
1692 ---
1693 -- segment found
1694 if (res) then
1695 io.write("\nautoselected Index: "..string.format("%02d", x).."\n")
1696 return x
1697 end
1698 ---
1699 -- nothing found
1700 io.write("no Segment found\n")
1701 return -1
1702end
1703
7f0cb92e 1704--- main function
733eb420 1705function main(args)
1706 if (#args == 0 ) then modifyMode() end
1707 --- variables
4e8fa8b4 1708 local inTAG, backupTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs
733eb420 1709 -- just a spacer for better readability
1710 print()
1711 --- parse arguments
1712 for o, a in getopt.getopt(args, 'hrmi:do:c:') do
1713 -- display help
1714 if o == "h" then return help(); end
1715 -- read tag from PM3
1716 if o == "r" then inTAG=readFromPM3() end
1717 -- input file
1718 if o == "i" then inTAG=readFile(a) end
1719 -- dump virtual-Tag
1720 if o == "d" then dfs=true end
1721 -- interacive modifying
1722 if o == "m" then interactive=true; modifyMode() end
1723 -- xor (e.g. for clone or plain file)
1724 if o == "c" then cfs=true; crc=a end
1725 -- output file
1726 if o == "o" then outfile=a; ofs=true end
1727 end
1728
1729 -- file conversion (output to file)
1730 if (ofs) then
1731 -- dump infile / tag-read
1732 if (dfs) then
1733 print("-----------------------------------------")
1734 print(dumpTag(inTAG))
1735 end
1736 bytes=tagToBytes(inTAG)
733eb420 1737 if (cfs) then
7f0cb92e 1738 -- xor willl be done in function writeFile
1739 -- with the value of byte[5]
733eb420 1740 bytes[5]=crc
1741 end
1742 -- write to outfile
1743 if (bytes) then
1744 writeFile(bytes, outfile)
7f0cb92e 1745 --- read real tag into virtual tag
1746 -- inTAG=readFromPM3() end
1747 --- or simply use the bytes that where wriiten
733eb420 1748 inTAG=bytesToTag(bytes, inTAG)
1749 -- show new content
1750 if (dfs) then
1751 print("-----------------------------------------")
7f0cb92e 1752 print(dumpTag(inTAG))
733eb420 1753 end
1754 end
1755 end
1756
1757end
1758
4e8fa8b4 1759-- Creates a complete/deep copy of the data
1760function deepCopy(object)
1761 local lookup_table = {}
1762 local function _copy(object)
1763 if type(object) ~= "table" then
1764 return object
1765 elseif lookup_table[object] then
1766 return lookup_table[object]
1767 end
1768 local new_table = {}
1769 lookup_table[object] = new_table
1770 for index, value in pairs(object) do
1771 new_table[_copy(index)] = _copy(value)
1772 end
1773 return setmetatable(new_table, getmetatable(object))
1774 end
1775 return _copy(object)
1776end
1777
733eb420 1778--- start
1779main(args)
Impressum, Datenschutz