]> cvs.zerfleddert.de Git - proxmark3-svn/blob - client/cmdhficlass.c
Updated the README.txt
[proxmark3-svn] / client / cmdhficlass.c
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2010 iZsh <izsh at fail0verflow.com>, Hagen Fritsch
3 // Copyright (C) 2011 Gerhard de Koning Gans
4 // Copyright (C) 2014 Midnitesnake & Andy Davies & Martin Holst Swende
5 //
6 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
7 // at your option, any later version. See the LICENSE.txt file for the text of
8 // the license.
9 //-----------------------------------------------------------------------------
10 // High frequency iClass commands
11 //-----------------------------------------------------------------------------
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type
18 #include "data.h"
19 #include "proxmark3.h"
20 #include "ui.h"
21 #include "cmdparser.h"
22 #include "cmdhficlass.h"
23 #include "../include/common.h"
24 #include "util.h"
25 #include "cmdmain.h"
26 #include "loclass/des.h"
27 #include "loclass/cipherutils.h"
28 #include "loclass/cipher.h"
29 #include "loclass/ikeys.h"
30 #include "loclass/elite_crack.h"
31 #include "loclass/fileutils.h"
32
33 static int CmdHelp(const char *Cmd);
34
35 int xorbits_8(uint8_t val)
36 {
37 uint8_t res = val ^ (val >> 1); //1st pass
38 res = res ^ (res >> 1); // 2nd pass
39 res = res ^ (res >> 2); // 3rd pass
40 res = res ^ (res >> 4); // 4th pass
41 return res & 1;
42 }
43
44 int CmdHFiClassList(const char *Cmd)
45 {
46 bool ShowWaitCycles = false;
47 char param = param_getchar(Cmd, 0);
48
49 if (param != 0) {
50 PrintAndLog("List data in trace buffer.");
51 PrintAndLog("Usage: hf iclass list");
52 PrintAndLog("h - help");
53 PrintAndLog("sample: hf iclass list");
54 return 0;
55 }
56
57 // for the time being. Need better Bigbuf handling.
58 #define TRACE_SIZE 3000
59
60 uint8_t trace[TRACE_SIZE];
61 GetFromBigBuf(trace, TRACE_SIZE, 0);
62 WaitForResponse(CMD_ACK,NULL);
63
64 PrintAndLog("Recorded Activity");
65 PrintAndLog("");
66 PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer");
67 PrintAndLog("All times are in carrier periods (1/13.56Mhz)");
68 PrintAndLog("");
69 PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC ");
70 PrintAndLog("-----------|-----------|-----|-----------------------------------------------------------------------");
71
72 uint16_t tracepos = 0;
73 uint16_t duration;
74 uint16_t data_len;
75 uint16_t parity_len;
76 bool isResponse;
77 uint32_t timestamp;
78 uint32_t first_timestamp;
79 uint32_t EndOfTransmissionTimestamp;
80
81 for (;;) {
82
83 if(tracepos >= TRACE_SIZE) {
84 break;
85 }
86
87 timestamp = *((uint32_t *)(trace + tracepos));
88 if(tracepos == 0) {
89 first_timestamp = timestamp;
90 }
91
92 // Break and stick with current result if buffer was not completely full
93 if (timestamp == 0x44444444) break;
94
95 tracepos += 4;
96 duration = *((uint16_t *)(trace + tracepos));
97 tracepos += 2;
98 data_len = *((uint16_t *)(trace + tracepos));
99 tracepos += 2;
100
101 if (data_len & 0x8000) {
102 data_len &= 0x7fff;
103 isResponse = true;
104 } else {
105 isResponse = false;
106 }
107
108 parity_len = (data_len-1)/8 + 1;
109
110 if (tracepos + data_len + parity_len >= TRACE_SIZE) {
111 break;
112 }
113
114 uint8_t *frame = trace + tracepos;
115 tracepos += data_len;
116 uint8_t *parityBytes = trace + tracepos;
117 tracepos += parity_len;
118
119 char line[16][110];
120 for (int j = 0; j < data_len; j++) {
121 int oddparity = 0x01;
122 int k;
123
124 for (k=0;k<8;k++) {
125 oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01);
126 }
127
128 uint8_t parityBits = parityBytes[j>>3];
129 if (isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) {
130 sprintf(line[j/16]+((j%16)*4), "%02x! ", frame[j]);
131 } else {
132 sprintf(line[j/16]+((j%16)*4), "%02x ", frame[j]);
133 }
134
135 }
136
137 char *crc = "";
138 if (data_len > 2) {
139 uint8_t b1, b2;
140 if(!isResponse && data_len == 4 ) {
141 // Rough guess that this is a command from the reader
142 // For iClass the command byte is not part of the CRC
143 ComputeCrc14443(CRC_ICLASS, &frame[1], data_len-3, &b1, &b2);
144 if (b1 != frame[data_len-2] || b2 != frame[data_len-1]) {
145 crc = "!crc";
146 }
147 }
148 else {
149 // For other data.. CRC might not be applicable (UPDATE commands etc.)
150 ComputeCrc14443(CRC_ICLASS, frame, data_len-2, &b1, &b2);
151 if (b1 != frame[data_len-2] || b2 != frame[data_len-1]) {
152 crc = "!crc";
153 }
154 }
155 }
156
157 EndOfTransmissionTimestamp = timestamp + duration;
158
159 int num_lines = (data_len - 1)/16 + 1;
160 for (int j = 0; j < num_lines; j++) {
161 if (j == 0) {
162 PrintAndLog(" %9d | %9d | %s | %-64s| %s",
163 (timestamp - first_timestamp),
164 (EndOfTransmissionTimestamp - first_timestamp),
165 (isResponse ? "Tag" : "Rdr"),
166 line[j],
167 (j == num_lines-1)?crc:"");
168 } else {
169 PrintAndLog(" | | | %-64s| %s",
170 line[j],
171 (j == num_lines-1)?crc:"");
172 }
173 }
174
175 bool next_isResponse = *((uint16_t *)(trace + tracepos + 6)) & 0x8000;
176
177 if (ShowWaitCycles && !isResponse && next_isResponse) {
178 uint32_t next_timestamp = *((uint32_t *)(trace + tracepos));
179 if (next_timestamp != 0x44444444) {
180 PrintAndLog(" %9d | %9d | %s | fdt (Frame Delay Time): %d",
181 (EndOfTransmissionTimestamp - first_timestamp),
182 (next_timestamp - first_timestamp),
183 " ",
184 (next_timestamp - EndOfTransmissionTimestamp));
185 }
186 }
187
188 }
189
190 return 0;
191 }
192
193 int CmdHFiClassSnoop(const char *Cmd)
194 {
195 UsbCommand c = {CMD_SNOOP_ICLASS};
196 SendCommand(&c);
197 return 0;
198 }
199 #define NUM_CSNS 15
200 int CmdHFiClassSim(const char *Cmd)
201 {
202 uint8_t simType = 0;
203 uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0};
204
205 if (strlen(Cmd)<1) {
206 PrintAndLog("Usage: hf iclass sim [0 <CSN>] | x");
207 PrintAndLog(" options");
208 PrintAndLog(" 0 <CSN> simulate the given CSN");
209 PrintAndLog(" 1 simulate default CSN");
210 PrintAndLog(" 2 iterate CSNs, gather MACs");
211 PrintAndLog(" sample: hf iclass sim 0 031FEC8AF7FF12E0");
212 PrintAndLog(" sample: hf iclass sim 2");
213 return 0;
214 }
215
216 simType = param_get8(Cmd, 0);
217
218 if(simType == 0)
219 {
220 if (param_gethex(Cmd, 1, CSN, 16)) {
221 PrintAndLog("A CSN should consist of 16 HEX symbols");
222 return 1;
223 }
224 PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8));
225
226 }
227 if(simType > 2)
228 {
229 PrintAndLog("Undefined simptype %d", simType);
230 return 1;
231 }
232 uint8_t numberOfCSNs=0;
233
234 if(simType == 2)
235 {
236 UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,NUM_CSNS}};
237 UsbCommand resp = {0};
238
239 /*uint8_t csns[8 * NUM_CSNS] = {
240 0x00,0x0B,0x0F,0xFF,0xF7,0xFF,0x12,0xE0 ,
241 0x00,0x13,0x94,0x7e,0x76,0xff,0x12,0xe0 ,
242 0x2a,0x99,0xac,0x79,0xec,0xff,0x12,0xe0 ,
243 0x17,0x12,0x01,0xfd,0xf7,0xff,0x12,0xe0 ,
244 0xcd,0x56,0x01,0x7c,0x6f,0xff,0x12,0xe0 ,
245 0x4b,0x5e,0x0b,0x72,0xef,0xff,0x12,0xe0 ,
246 0x00,0x73,0xd8,0x75,0x58,0xff,0x12,0xe0 ,
247 0x0c,0x90,0x32,0xf3,0x5d,0xff,0x12,0xe0 };
248 */
249
250 uint8_t csns[8*NUM_CSNS] = {
251 0x00, 0x0B, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0,
252 0x00, 0x04, 0x0E, 0x08, 0xF7, 0xFF, 0x12, 0xE0,
253 0x00, 0x09, 0x0D, 0x05, 0xF7, 0xFF, 0x12, 0xE0,
254 0x00, 0x0A, 0x0C, 0x06, 0xF7, 0xFF, 0x12, 0xE0,
255 0x00, 0x0F, 0x0B, 0x03, 0xF7, 0xFF, 0x12, 0xE0,
256 0x00, 0x08, 0x0A, 0x0C, 0xF7, 0xFF, 0x12, 0xE0,
257 0x00, 0x0D, 0x09, 0x09, 0xF7, 0xFF, 0x12, 0xE0,
258 0x00, 0x0E, 0x08, 0x0A, 0xF7, 0xFF, 0x12, 0xE0,
259 0x00, 0x03, 0x07, 0x17, 0xF7, 0xFF, 0x12, 0xE0,
260 0x00, 0x3C, 0x06, 0xE0, 0xF7, 0xFF, 0x12, 0xE0,
261 0x00, 0x01, 0x05, 0x1D, 0xF7, 0xFF, 0x12, 0xE0,
262 0x00, 0x02, 0x04, 0x1E, 0xF7, 0xFF, 0x12, 0xE0,
263 0x00, 0x07, 0x03, 0x1B, 0xF7, 0xFF, 0x12, 0xE0,
264 0x00, 0x00, 0x02, 0x24, 0xF7, 0xFF, 0x12, 0xE0,
265 0x00, 0x05, 0x01, 0x21, 0xF7, 0xFF, 0x12, 0xE0 };
266
267 memcpy(c.d.asBytes, csns, 8*NUM_CSNS);
268
269 SendCommand(&c);
270 if (!WaitForResponseTimeout(CMD_ACK, &resp, -1)) {
271 PrintAndLog("Command timed out");
272 return 0;
273 }
274
275 uint8_t num_mac_responses = resp.arg[1];
276 PrintAndLog("Mac responses: %d MACs obtained (should be %d)", num_mac_responses, NUM_CSNS);
277
278 size_t datalen = NUM_CSNS*24;
279 /*
280 * Now, time to dump to file. We'll use this format:
281 * <8-byte CSN><8-byte CC><4 byte NR><4 byte MAC>....
282 * So, it should wind up as
283 * 8 * 24 bytes.
284 *
285 * The returndata from the pm3 is on the following format
286 * <4 byte NR><4 byte MAC>
287 * CC are all zeroes, CSN is the same as was sent in
288 **/
289 void* dump = malloc(datalen);
290 memset(dump,0,datalen);//<-- Need zeroes for the CC-field
291 uint8_t i = 0;
292 for(i = 0 ; i < NUM_CSNS ; i++)
293 {
294 memcpy(dump+i*24, csns+i*8,8); //CSN
295 //8 zero bytes here...
296 //Then comes NR_MAC (eight bytes from the response)
297 memcpy(dump+i*24+16,resp.d.asBytes+i*8,8);
298
299 }
300 /** Now, save to dumpfile **/
301 saveFile("iclass_mac_attack", "bin", dump,datalen);
302 free(dump);
303 }else
304 {
305 UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,numberOfCSNs}};
306 memcpy(c.d.asBytes, CSN, 8);
307 SendCommand(&c);
308 }
309
310 return 0;
311 }
312
313 int CmdHFiClassReader(const char *Cmd)
314 {
315 UsbCommand c = {CMD_READER_ICLASS, {0}};
316 SendCommand(&c);
317 UsbCommand resp;
318 while(!ukbhit()){
319 if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) {
320 uint8_t isOK = resp.arg[0] & 0xff;
321 uint8_t * data = resp.d.asBytes;
322
323 PrintAndLog("isOk:%02x", isOK);
324
325 if(isOK > 0)
326 {
327 PrintAndLog("CSN: %s",sprint_hex(data,8));
328 }
329 if(isOK >= 1)
330 {
331 PrintAndLog("CC: %s",sprint_hex(data+8,8));
332 }else{
333 PrintAndLog("No CC obtained");
334 }
335 } else {
336 PrintAndLog("Command execute timeout");
337 }
338 }
339
340 return 0;
341 }
342
343 int CmdHFiClassReader_Replay(const char *Cmd)
344 {
345 uint8_t readerType = 0;
346 uint8_t MAC[4]={0x00, 0x00, 0x00, 0x00};
347
348 if (strlen(Cmd)<1) {
349 PrintAndLog("Usage: hf iclass replay <MAC>");
350 PrintAndLog(" sample: hf iclass replay 00112233");
351 return 0;
352 }
353
354 if (param_gethex(Cmd, 0, MAC, 8)) {
355 PrintAndLog("MAC must include 8 HEX symbols");
356 return 1;
357 }
358
359 UsbCommand c = {CMD_READER_ICLASS_REPLAY, {readerType}};
360 memcpy(c.d.asBytes, MAC, 4);
361 SendCommand(&c);
362
363 return 0;
364 }
365
366 int CmdHFiClassReader_Dump(const char *Cmd)
367 {
368 uint8_t readerType = 0;
369 uint8_t MAC[4]={0x00,0x00,0x00,0x00};
370 uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
371 uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
372 uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
373 //uint8_t CC_temp[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
374 uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
375 uint8_t keytable[128] = {0};
376 int elite = 0;
377 uint8_t *used_key;
378 int i;
379 if (strlen(Cmd)<1)
380 {
381 PrintAndLog("Usage: hf iclass dump <Key> [e]");
382 PrintAndLog(" Key - A 16 byte master key");
383 PrintAndLog(" e - If 'e' is specified, the key is interpreted as the 16 byte");
384 PrintAndLog(" Custom Key (KCus), which can be obtained via reader-attack");
385 PrintAndLog(" See 'hf iclass sim 2'. This key should be on iclass-format");
386 PrintAndLog(" sample: hf iclass dump 0011223344556677");
387
388
389 return 0;
390 }
391
392 if (param_gethex(Cmd, 0, KEY, 16))
393 {
394 PrintAndLog("KEY must include 16 HEX symbols");
395 return 1;
396 }
397
398 if (param_getchar(Cmd, 1) == 'e')
399 {
400 PrintAndLog("Elite switch on");
401 elite = 1;
402
403 //calc h2
404 hash2(KEY, keytable);
405 printarr_human_readable("keytable", keytable, 128);
406
407 }
408
409 UsbCommand resp;
410 uint8_t key_sel[8] = {0};
411 uint8_t key_sel_p[8] = { 0 };
412
413 //HACK -- Below is for testing without access to a tag
414 uint8_t fake_dummy_test = false;
415 if(fake_dummy_test)
416 {
417 uint8_t xdata[16] = {0x01,0x02,0x03,0x04,0xF7,0xFF,0x12,0xE0, //CSN from http://www.proxmark.org/forum/viewtopic.php?pid=11230#p11230
418 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; // Just a random CC. Would be good to add a real testcase here
419 memcpy(resp.d.asBytes,xdata, 16);
420 resp.arg[0] = 2;
421 }
422
423 //End hack
424
425
426 UsbCommand c = {CMD_READER_ICLASS, {0}};
427 c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE;
428 if(!fake_dummy_test)
429 SendCommand(&c);
430
431
432
433 if (fake_dummy_test || WaitForResponseTimeout(CMD_ACK,&resp,4500)) {
434 uint8_t isOK = resp.arg[0] & 0xff;
435 uint8_t * data = resp.d.asBytes;
436
437 memcpy(CSN,data,8);
438 memcpy(CCNR,data+8,8);
439
440 PrintAndLog("isOk:%02x", isOK);
441
442 if(isOK > 0)
443 {
444 PrintAndLog("CSN: %s",sprint_hex(CSN,8));
445 }
446 if(isOK > 1)
447 {
448 if(elite)
449 {
450 //Get the key index (hash1)
451 uint8_t key_index[8] = {0};
452
453 hash1(CSN, key_index);
454 printvar("hash1", key_index,8);
455 for(i = 0; i < 8 ; i++)
456 key_sel[i] = keytable[key_index[i]] & 0xFF;
457 PrintAndLog("Pre-fortified 'permuted' HS key that would be needed by an iclass reader to talk to above CSN:");
458 printvar("k_sel", key_sel,8);
459 //Permute from iclass format to standard format
460 permutekey_rev(key_sel,key_sel_p);
461 used_key = key_sel_p;
462 }else{
463 //Perhaps this should also be permuted to std format?
464 // Something like the code below? I have no std system
465 // to test this with /Martin
466
467 //uint8_t key_sel_p[8] = { 0 };
468 //permutekey_rev(KEY,key_sel_p);
469 //used_key = key_sel_p;
470
471 used_key = KEY;
472
473 }
474
475 PrintAndLog("Pre-fortified key that would be needed by the OmniKey reader to talk to above CSN:");
476 printvar("Used key",used_key,8);
477 diversifyKey(CSN,used_key, div_key);
478 PrintAndLog("Hash0, a.k.a diversified key, that is computed using Ksel and stored in the card (Block 3):");
479 printvar("Div key", div_key, 8);
480 printvar("CC_NR:",CCNR,12);
481 doMAC(CCNR,12,div_key, MAC);
482 printvar("MAC", MAC, 4);
483
484 UsbCommand d = {CMD_READER_ICLASS_REPLAY, {readerType}};
485 memcpy(d.d.asBytes, MAC, 4);
486 if(!fake_dummy_test) SendCommand(&d);
487
488 }else{
489 PrintAndLog("Failed to obtain CC! Aborting");
490 }
491 } else {
492 PrintAndLog("Command execute timeout");
493 }
494
495 return 0;
496 }
497
498 int CmdHFiClass_iso14443A_write(const char *Cmd)
499 {
500 uint8_t readerType = 0;
501 uint8_t MAC[4]={0x00,0x00,0x00,0x00};
502 uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
503 uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
504 uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
505 uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
506
507 uint8_t blockNo=0;
508 uint8_t bldata[8]={0};
509
510 if (strlen(Cmd)<3)
511 {
512 PrintAndLog("Usage: hf iclass write <Key> <Block> <Data>");
513 PrintAndLog(" sample: hf iclass write 0011223344556677 10 AAAAAAAAAAAAAAAA");
514 return 0;
515 }
516
517 if (param_gethex(Cmd, 0, KEY, 16))
518 {
519 PrintAndLog("KEY must include 16 HEX symbols");
520 return 1;
521 }
522
523 blockNo = param_get8(Cmd, 1);
524 if (blockNo>32)
525 {
526 PrintAndLog("Error: Maximum number of blocks is 32 for iClass 2K Cards!");
527 return 1;
528 }
529 if (param_gethex(Cmd, 2, bldata, 8))
530 {
531 PrintAndLog("Block data must include 8 HEX symbols");
532 return 1;
533 }
534
535 UsbCommand c = {CMD_ICLASS_ISO14443A_WRITE, {0}};
536 SendCommand(&c);
537 UsbCommand resp;
538
539 if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) {
540 uint8_t isOK = resp.arg[0] & 0xff;
541 uint8_t * data = resp.d.asBytes;
542
543 memcpy(CSN,data,8);
544 memcpy(CCNR,data+8,8);
545 PrintAndLog("DEBUG: %s",sprint_hex(CSN,8));
546 PrintAndLog("DEBUG: %s",sprint_hex(CCNR,8));
547 PrintAndLog("isOk:%02x", isOK);
548 } else {
549 PrintAndLog("Command execute timeout");
550 }
551
552 diversifyKey(CSN,KEY, div_key);
553
554 PrintAndLog("Div Key: %s",sprint_hex(div_key,8));
555 doMAC(CCNR, 12,div_key, MAC);
556
557 UsbCommand c2 = {CMD_ICLASS_ISO14443A_WRITE, {readerType,blockNo}};
558 memcpy(c2.d.asBytes, bldata, 8);
559 memcpy(c2.d.asBytes+8, MAC, 4);
560 SendCommand(&c2);
561
562 if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) {
563 uint8_t isOK = resp.arg[0] & 0xff;
564 uint8_t * data = resp.d.asBytes;
565
566 if (isOK)
567 PrintAndLog("isOk:%02x data:%s", isOK, sprint_hex(data, 4));
568 else
569 PrintAndLog("isOk:%02x", isOK);
570 } else {
571 PrintAndLog("Command execute timeout");
572 }
573 return 0;
574 }
575
576
577 static command_t CommandTable[] =
578 {
579 {"help", CmdHelp, 1, "This help"},
580 {"list", CmdHFiClassList, 0, "List iClass history"},
581 {"snoop", CmdHFiClassSnoop, 0, "Eavesdrop iClass communication"},
582 {"sim", CmdHFiClassSim, 0, "Simulate iClass tag"},
583 {"reader",CmdHFiClassReader, 0, "Read an iClass tag"},
584 {"replay",CmdHFiClassReader_Replay, 0, "Read an iClass tag via Reply Attack"},
585 {"dump", CmdHFiClassReader_Dump, 0, "Authenticate and Dump iClass tag"},
586 {"write", CmdHFiClass_iso14443A_write, 0, "Authenticate and Write iClass block"},
587 {"replay", CmdHFiClassReader_Replay, 0, "Read an iClass tag via Reply Attack"},
588 {"dump", CmdHFiClassReader_Dump, 0, "Authenticate and Dump iClass tag"},
589 {"write", CmdHFiClass_iso14443A_write, 0, "Authenticate and Write iClass block"},
590 {NULL, NULL, 0, NULL}
591 };
592
593 int CmdHFiClass(const char *Cmd)
594 {
595 CmdsParse(CommandTable, Cmd);
596 return 0;
597 }
598
599 int CmdHelp(const char *Cmd)
600 {
601 CmdsHelp(CommandTable);
602 return 0;
603 }
Impressum, Datenschutz