]> git.zerfleddert.de Git - proxmark3-svn/commitdiff
CHG: @Marshmellow42 's fixes. ref: https://github.com/marshmellow42/proxmark3/commi...
authoriceman1001 <iceman@iuse.se>
Fri, 24 Feb 2017 00:14:47 +0000 (01:14 +0100)
committericeman1001 <iceman@iuse.se>
Fri, 24 Feb 2017 00:14:47 +0000 (01:14 +0100)
armsrc/Makefile
client/cmddata.c
client/cmdhfmf.c
client/cmdlfem4x.c
client/cmdmain.c
common/lfdemod.c

index 6a91501304f0fd77fd896336e2192df05e6b7a40..054f20b66f55e73bb38a849e8dad58dc96926bd5 100644 (file)
@@ -120,7 +120,7 @@ $(OBJDIR)/fullimage.data.o: $(OBJDIR)/fullimage.data.bin.z
        $(OBJCOPY) -O elf32-littlearm -I binary -B arm --rename-section .data=compressed_data $^ $@
 
 $(OBJDIR)/fullimage.elf: $(OBJDIR)/fullimage.nodata.o $(OBJDIR)/fullimage.data.o
-       $(CC) $(LDFLAGS) -Wl,-T,ldscript,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^
+       $(CC) $(LDFLAGS) -Wl,-T,ldscript,-e,_osimage_entry,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^
 
 tarbin: $(OBJS)
        $(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(OBJS:%=armsrc/%) $(OBJS:%.s19=armsrc/%.elf)
index 6a5753c72a43426c79bb9c922cba7cbaeb714b07..9e7fc8ed0bcfa8c9378523c56c554dd1e2692ac8 100644 (file)
@@ -380,8 +380,8 @@ void printEM410x(uint32_t hi, uint64_t id)
                } else{
                        //output 40 bit em id
                        PrintAndLog("\nEM TAG ID      : %010" PRIX64, id);
-                       PrintAndLog("Unique TAG ID  : %010" PRIX64, id2lo);
                        PrintAndLog("\nPossible de-scramble patterns");
+                       PrintAndLog("Unique TAG ID  : %010" PRIX64, id2lo);
                        PrintAndLog("HoneyWell IdentKey {");
                        PrintAndLog("DEZ 8          : %08" PRIu64, id & 0xFFFFFF);
                        PrintAndLog("DEZ 10         : %010" PRIu64, id & 0xFFFFFFFF);
@@ -391,7 +391,7 @@ void printEM410x(uint32_t hi, uint64_t id)
                        PrintAndLog("DEZ 3.5C       : %03" PRIu64 ".%05" PRIu64, (id & 0xFF0000) >> 16, (id & 0xFFFF));
                        PrintAndLog("DEZ 14/IK2     : %014" PRIu64, id);
                        PrintAndLog("DEZ 15/IK3     : %015" PRIu64, id2lo);
-                       PrintAndLog("DEZ 20/ZK      : %02lld%02lld%02lld%02lld%02lld%02lld%02lld%02lld%02lld%02lld",
+                       PrintAndLog("DEZ 20/ZK      : %02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64 "%02" PRIu64,
                            (id2lo & 0xf000000000) >> 36,
                            (id2lo & 0x0f00000000) >> 32,
                            (id2lo & 0x00f0000000) >> 28,
index 20bb59298630cebabff7bb686253830b7e714584..04998e181bf724f1085e9f38384407d4fcbbd226 100644 (file)
@@ -470,10 +470,9 @@ int CmdHF14AMfDump(const char *Cmd) {
        size_t bytes_read;\r
        for (sectorNo=0; sectorNo<numSectors; sectorNo++) {\r
                bytes_read = fread( keyA[sectorNo], 1, 6, fin );\r
-               if ( bytes_read == 0) {\r
+               if ( bytes_read != 6) {\r
                        PrintAndLog("File reading error.");\r
                        fclose(fin);\r
-                       fin = NULL;\r
                        return 2;\r
                }\r
        }\r
@@ -481,16 +480,14 @@ int CmdHF14AMfDump(const char *Cmd) {
        // Read keys B from file\r
        for (sectorNo=0; sectorNo<numSectors; sectorNo++) {\r
                bytes_read = fread( keyB[sectorNo], 1, 6, fin );\r
-               if ( bytes_read == 0) {\r
+               if ( bytes_read != 6) {\r
                        PrintAndLog("File reading error.");\r
                        fclose(fin);\r
-                       fin = NULL;\r
                        return 2;\r
                }\r
        }\r
        \r
        fclose(fin);\r
-       fin = NULL;\r
                        \r
        PrintAndLog("|-----------------------------------------|");\r
        PrintAndLog("|------ Reading sector access bits...-----|");\r
@@ -643,20 +640,18 @@ int CmdHF14AMfRestore(const char *Cmd) {
        size_t bytes_read;\r
        for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {\r
                bytes_read = fread( keyA[sectorNo], 1, 6, fkeys );\r
-               if ( bytes_read == 0) {\r
+               if ( bytes_read != 6) {\r
                        PrintAndLog("File reading error (dumpkeys.bin).");\r
                        fclose(fkeys);\r
-                       fkeys = NULL;\r
                        return 2;\r
                }\r
        }\r
 \r
        for (sectorNo = 0; sectorNo < numSectors; sectorNo++) {\r
                bytes_read = fread( keyB[sectorNo], 1, 6, fkeys );\r
-               if ( bytes_read == 0) {\r
+               if ( bytes_read != 6) {\r
                        PrintAndLog("File reading error (dumpkeys.bin).");\r
                        fclose(fkeys);\r
-                       fkeys = NULL;\r
                        return 2;\r
                }\r
        }\r
@@ -674,7 +669,7 @@ int CmdHF14AMfRestore(const char *Cmd) {
                        UsbCommand c = {CMD_MIFARE_WRITEBL, {FirstBlockOfSector(sectorNo) + blockNo, keyType, 0}};\r
                        memcpy(c.d.asBytes, key, 6);                    \r
                        bytes_read = fread(bldata, 1, 16, fdump);\r
-                       if ( bytes_read == 0) {\r
+                       if ( bytes_read != 16) {\r
                                PrintAndLog("File reading error (dumpdata.bin).");\r
                                fclose(fdump);\r
                                fdump = NULL;                           \r
@@ -713,7 +708,6 @@ int CmdHF14AMfRestore(const char *Cmd) {
        }\r
        \r
        fclose(fdump);\r
-       fdump = NULL;   \r
        return 0;\r
 }\r
 \r
@@ -789,7 +783,7 @@ int CmdHF14AMfNested(const char *Cmd) {
                switch (isOK) {\r
                        case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break;\r
                        case -2 : PrintAndLog("Button pressed. Aborted.\n"); break;\r
-                       case -3 : PrintAndLog("Tag isn't vulnerable to Nested Attack (its random number generator is not predictable).\n"); break;\r
+                       case -3 : PrintAndLog("Tag isn't vulnerable to Nested Attack (random number generator is not predictable).\n"); break;\r
                        case -4 : PrintAndLog("No valid key found"); break;\r
                        case -5 : \r
                                key64 = bytes_to_num(keyBlock, 6);\r
@@ -1584,7 +1578,7 @@ int CmdHF14AMfSniff(const char *Cmd){
 \r
                        if (res == 1) {                                                         // there is (more) data to be transferred\r
                                if (pckNum == 0) {                                              // first packet, (re)allocate necessary buffer\r
-                                       if (traceLen > bufsize) {\r
+                                       if (traceLen > bufsize || buf == NULL) {\r
                                                uint8_t *p;\r
                                                if (buf == NULL)                                // not yet allocated\r
                                                        p = malloc(traceLen);\r
index e6db80261a6992277942ee64f96e0d836f738444..a0bddb2af3779054c1d43746ff699e3b59b620a3 100644 (file)
@@ -702,12 +702,13 @@ bool setDemodBufferEM(uint32_t *word, size_t idx){
                PrintAndLog("DEBUG: Error - EM Parity tests failed");
                return FALSE;
        }
-       
-       if (!removeParity(DemodBuffer, idx + EM_PREAMBLE_LEN, 9, 0, 44)) {
+                  
+    // test for even parity bits and remove them. (leave out the end row of parities so 36 bits)       
+       if (!removeParity(DemodBuffer, idx + EM_PREAMBLE_LEN, 9, 0, 36)) {
                if (g_debugMode) PrintAndLog("DEBUG: Error - EM, failed removing parity");
                return FALSE;
        }
-       setDemodBuf(DemodBuffer, 40, 0);
+       setDemodBuf(DemodBuffer, 32, 0);
        *word = bytebits_to_byteLSBF(DemodBuffer, 32);
        return TRUE;
 }
@@ -778,6 +779,18 @@ int usage_lf_em4x05_write(void) {
        PrintAndLog("      lf em 4x05write 1 deadc0de 11223344");
        return 0;
 }
+int usage_lf_em4x05_info(void) {
+       PrintAndLog("Tag information EM4205/4305/4469//4569 tags.  Tag must be on antenna.");
+       PrintAndLog("");
+       PrintAndLog("Usage:  lf em 4x05info [h] <pwd>");
+       PrintAndLog("Options:");
+       PrintAndLog("       h         - this help");
+       PrintAndLog("       pwd       - password (hex) (optional)");
+       PrintAndLog("samples:");
+       PrintAndLog("      lf em 4x05info");
+       PrintAndLog("      lf em 4x05info deadc0de");
+       return 0;
+}
 
 int CmdEM4x05Dump(const char *Cmd) {
        uint8_t addr = 0;
@@ -904,10 +917,166 @@ int CmdEM4x05Write(const char *Cmd) {
        int isOk = demodEM4x05resp(&dummy);
        if (isOk)
                PrintAndLog("Write Verified");
-
+       else
+               PrintAndLog("Write could not be verified");     
        return isOk;
 }
 
+void printEM4x05config(uint32_t wordData) {
+       uint16_t datarate = (((wordData & 0x3F)+1)*2);
+       uint8_t encoder = ((wordData >> 6) & 0xF);
+       char enc[14];
+       memset(enc,0,sizeof(enc));
+
+       uint8_t PSKcf = (wordData >> 10) & 0x3;
+       char cf[10];
+       memset(cf,0,sizeof(cf));
+       uint8_t delay = (wordData >> 12) & 0x3;
+       char cdelay[33];
+       memset(cdelay,0,sizeof(cdelay));
+       uint8_t LWR = (wordData >> 14) & 0xF; //last word read
+
+       switch (encoder) {
+               case 0: snprintf(enc,sizeof(enc),"NRZ"); break;
+               case 1: snprintf(enc,sizeof(enc),"Manchester"); break;
+               case 2: snprintf(enc,sizeof(enc),"Biphase"); break;
+               case 3: snprintf(enc,sizeof(enc),"Miller"); break;
+               case 4: snprintf(enc,sizeof(enc),"PSK1"); break;
+               case 5: snprintf(enc,sizeof(enc),"PSK2"); break;
+               case 6: snprintf(enc,sizeof(enc),"PSK3"); break;
+               case 7: snprintf(enc,sizeof(enc),"Unknown"); break;
+               case 8: snprintf(enc,sizeof(enc),"FSK1"); break;
+               case 9: snprintf(enc,sizeof(enc),"FSK2"); break;
+               default: snprintf(enc,sizeof(enc),"Unknown"); break;
+       }
+
+       switch (PSKcf) {
+               case 0: snprintf(cf,sizeof(cf),"RF/2"); break;
+               case 1: snprintf(cf,sizeof(cf),"RF/8"); break;
+               case 2: snprintf(cf,sizeof(cf),"RF/4"); break;
+               case 3: snprintf(cf,sizeof(cf),"unknown"); break;
+       }
+
+       switch (delay) {
+               case 0: snprintf(cdelay, sizeof(cdelay),"no delay"); break;
+               case 1: snprintf(cdelay, sizeof(cdelay),"BP/8 or 1/8th bit period delay"); break;
+               case 2: snprintf(cdelay, sizeof(cdelay),"BP/4 or 1/4th bit period delay"); break;
+               case 3: snprintf(cdelay, sizeof(cdelay),"no delay"); break;
+       }
+       PrintAndLog("ConfigWord: %08X (Word 4)\n", wordData);
+       PrintAndLog("Config Breakdown:", wordData);
+       PrintAndLog(" Data Rate:  %02u | RF/%u", wordData & 0x3F, datarate);
+       PrintAndLog("   Encoder:   %u | %s", encoder, enc);
+       PrintAndLog("    PSK CF:   %u | %s", PSKcf, cf);
+       PrintAndLog("     Delay:   %u | %s", delay, cdelay);
+       PrintAndLog(" LastWordR:  %02u | Address of last word for default read", LWR);
+       PrintAndLog(" ReadLogin:   %u | Read Login is %s", (wordData & 0x40000)>>18, (wordData & 0x40000) ? "Required" : "Not Required");       
+       PrintAndLog("   ReadHKL:   %u | Read Housekeeping Words Login is %s", (wordData & 0x80000)>>19, (wordData & 0x80000) ? "Required" : "Not Required");    
+       PrintAndLog("WriteLogin:   %u | Write Login is %s", (wordData & 0x100000)>>20, (wordData & 0x100000) ? "Required" : "Not Required");    
+       PrintAndLog("  WriteHKL:   %u | Write Housekeeping Words Login is %s", (wordData & 0x200000)>>21, (wordData & 0x200000) ? "Required" : "Not Required"); 
+       PrintAndLog("    R.A.W.:   %u | Read After Write is %s", (wordData & 0x400000)>>22, (wordData & 0x400000) ? "On" : "Off");
+       PrintAndLog("   Disable:   %u | Disable Command is %s", (wordData & 0x800000)>>23, (wordData & 0x800000) ? "Accepted" : "Not Accepted");
+       PrintAndLog("    R.T.F.:   %u | Reader Talk First is %s", (wordData & 0x1000000)>>24, (wordData & 0x1000000) ? "Enabled" : "Disabled");
+       PrintAndLog("    Pigeon:   %u | Pigeon Mode is %s\n", (wordData & 0x4000000)>>26, (wordData & 0x4000000) ? "Enabled" : "Disabled");
+}
+
+void printEM4x05info(uint8_t chipType, uint8_t cap, uint16_t custCode, uint32_t serial) {
+       switch (chipType) {
+               case 9: PrintAndLog("\n Chip Type:   %u | EM4305", chipType); break;
+               case 4: PrintAndLog(" Chip Type:   %u | Unknown", chipType); break;
+               case 2: PrintAndLog(" Chip Type:   %u | EM4469", chipType); break;
+               //add more here when known
+               default: PrintAndLog(" Chip Type:   %u Unknown", chipType); break;
+       }
+
+       switch (cap) {
+               case 3: PrintAndLog("  Cap Type:   %u | 330pF",cap); break;
+               case 2: PrintAndLog("  Cap Type:   %u | %spF",cap, (chipType==2)? "75":"210"); break;
+               case 1: PrintAndLog("  Cap Type:   %u | 250pF",cap); break;
+               case 0: PrintAndLog("  Cap Type:   %u | no resonant capacitor",cap); break;
+               default: PrintAndLog("  Cap Type:   %u | unknown",cap); break;
+       }
+
+       PrintAndLog(" Cust Code: %03u | %s", custCode, (custCode == 0x200) ? "Default": "Unknown");
+       if (serial != 0) {
+               PrintAndLog("\n  Serial #: %08X\n", serial);
+       }
+}
+
+void printEM4x05ProtectionBits(uint32_t wordData) {
+       for (uint8_t i = 0; i < 15; i++) {
+               PrintAndLog("      Word:  %02u | %s", i, (((1 << i) & wordData ) || i < 2) ? "Is Write Locked" : "Is Not Write Locked");
+               if (i==14) {
+                       PrintAndLog("      Word:  %02u | %s", i+1, (((1 << i) & wordData ) || i < 2) ? "Is Write Locked" : "Is Not Write Locked");
+               }
+       }
+}
+
+//quick test for EM4x05/EM4x69 tag
+bool EM4x05Block0Test(uint32_t *wordData) {
+//     return (EM4x05ReadWord_ext(0,0,false,wordData) == 1);
+       return false;
+}
+
+int CmdEM4x05Info(const char *Cmd) {
+       /*
+       uint32_t pwd;
+       uint32_t wordData = 0;
+       bool usePwd = false;
+       uint8_t ctmp = param_getchar(Cmd, 0);
+       if ( ctmp == 'H' || ctmp == 'h' ) return usage_lf_em4x05_info();
+
+       // for now use default input of 1 as invalid (unlikely 1 will be a valid password...)
+       pwd = param_get32ex(Cmd, 0, 1, 16);
+       
+       if ( pwd != 1 )
+               usePwd = true;
+
+       // read word 0 (chip info)
+       // block 0 can be read even without a password.
+       if ( !EM4x05Block0Test(&wordData) ) 
+               return -1;
+       
+       uint8_t chipType = (wordData >> 1) & 0xF;
+       uint8_t cap = (wordData >> 5) & 3;
+       uint16_t custCode = (wordData >> 9) & 0x3FF;
+       
+       // read word 1 (serial #) doesn't need pwd
+       wordData = 0;
+       if (EM4x05ReadWord_ext(1, 0, false, &wordData) != 1) {
+               //failed, but continue anyway...
+       }
+       printEM4x05info(chipType, cap, custCode, wordData);
+
+       // read word 4 (config block) 
+       // needs password if one is set
+       wordData = 0;
+       if ( EM4x05ReadWord_ext(4, pwd, usePwd, &wordData) != 1 )
+               return 0;
+       
+       printEM4x05config(wordData);
+
+       // read word 14 and 15 to see which is being used for the protection bits
+       wordData = 0;
+       if ( EM4x05ReadWord_ext(14, pwd, usePwd, &wordData) != 1 ) {
+               return 0;
+       }
+       // if status bit says this is not the used protection word
+       if (!(wordData & 0x8000)) {
+               if ( EM4x05ReadWord_ext(15, pwd, usePwd, &wordData) != 1 ) {
+                       return 0;
+               }
+       }
+       if (!(wordData & 0x8000)) {
+               //something went wrong
+               return 0;
+       }
+       printEM4x05ProtectionBits(wordData);
+       
+       */
+       return 1;
+}
+
 static command_t CommandTable[] = {
        {"help",                CmdHelp,                        1, "This help"},
        {"410xdemod",   CmdEMdemodASK,          0, "[findone] -- Extract ID from EM410x tag (option 0 for continuous loop, 1 for only 1 tag)"},  
@@ -916,9 +1085,10 @@ static command_t CommandTable[] = {
        {"410xwatch",   CmdEM410xWatch,         0, "['h'] -- Watches for EM410x 125/134 kHz tags (option 'h' for 134)"},
        {"410xspoof",   CmdEM410xWatchnSpoof, 0, "['h'] --- Watches for EM410x 125/134 kHz tags, and replays them. (option 'h' for 134)" },
        {"410xwrite",   CmdEM410xWrite,         0, "<UID> <'0' T5555> <'1' T55x7> [clock rate] -- Write EM410x UID to T5555(Q5) or T55x7 tag, optionally setting clock rate"},
+       {"4x05dump",    CmdEM4x05Dump,          0, "dump EM4205/4305 tag"},
+       {"4x05info",    CmdEM4x05Info,      0, "Tag information EM4x05/EM4x69"},
        {"4x05read",    CmdEM4x05Read,          0, "read word data from EM4205/4305"},
        {"4x05write",   CmdEM4x05Write,         0, "write word data to EM4205/4305"},
-       {"4x05dump",    CmdEM4x05Dump,          0, "dump EM4205/4305 tag"},
        {"4x50read",    CmdEM4x50Read,          0, "read word data from EM4x50"},
        {"4x50write",   CmdEM4x50Write,         0, "write word data to EM4x50"},
        {"4x50dump",    CmdEM4x50Dump,          0, "dump EM4x50 tag"},
index 84d6b1b9b31b85b7ef2cb2fd1c5f89863ca61d63..374f8166b4e802cdda75919327137b9d1ab894a2 100644 (file)
@@ -50,7 +50,7 @@ static command_t CommandTable[] =
        {"hf",          CmdHF,          1, "{ High Frequency commands... }"},
        {"hw",          CmdHW,          1, "{ Hardware commands... }"},
        {"lf",          CmdLF,          1, "{ Low Frequency commands... }"},
-       {"reveng",      CmdRev,         1, "Crc calculations from the software reveng 1.40"},
+       {"reveng",      CmdRev,         1, "Crc calculations from the software reveng 1.44"},
        {"script",      CmdScript,      1, "{ Scripting commands }"},   
        {"quit",        CmdQuit,        1, "Exit program"},
        {"exit",        CmdQuit,        1, "Exit program"},
index 21695ec16136959ffed4bd4c87a3044548a01a1d..ca126df32c696f7be98961c642dd0159397e07b7 100644 (file)
@@ -74,7 +74,7 @@ size_t removeParity(uint8_t *BitStream, size_t startIdx, uint8_t pLen, uint8_t p
                        parityWd = (parityWd << 1) | BitStream[startIdx+word+bit];
                        BitStream[j++] = (BitStream[startIdx+word+bit]);
                }
-               if (word+pLen >= bLen) break;
+               if (word+pLen > bLen) break;
 
                j--; // overwrite parity with next data
                // if parity fails then return 0
@@ -158,7 +158,7 @@ bool preambleSearchEx(uint8_t *BitStream, uint8_t *preamble, size_t pLen, size_t
        uint8_t foundCnt = 0;
        for (int idx = 0; idx < *size - pLen; idx++){
                if (memcmp(BitStream+idx, preamble, pLen) == 0){
-                       if (g_debugMode) prnt("DEBUG: preamble found at %u", idx);
+                       if (g_debugMode) prnt("DEBUG: preamble found at %i", idx);
                        //first index found
                        foundCnt++;
                        if (foundCnt == 1){
@@ -205,12 +205,10 @@ size_t findModStart(uint8_t dest[], size_t size, uint8_t threshold_value, uint8_
 // actually, no arguments needed - built this way in case we want this to be a direct call from "data " cmds in the future
 uint8_t Em410xDecode(uint8_t *BitStream, size_t *size, size_t *startIdx, uint32_t *hi, uint64_t *lo)
 {
-       //allow only 1s and 0s
-       // only checking first bitvalue?!
+       // sanity check
        if (BitStream[1] > 1) return 0; 
        
-       uint32_t i = 0, idx = 0, parityBits = 0;
-       uint8_t fmtlen = 0;
+       uint8_t fmtlen;
        *startIdx = 0;
        
        // preamble 0111111111
@@ -220,23 +218,27 @@ uint8_t Em410xDecode(uint8_t *BitStream, size_t *size, size_t *startIdx, uint32_
                return 0;
        if (*size < 64) return 0;
        
-       fmtlen = (*size > 64) ? 22 : 10;
+       fmtlen = (*size == 110) ? 22 : 10;
 
-       idx = *startIdx + sizeof(preamble);
+       //skip last 4bit parity row for simplicity
+       *size = removeParity(BitStream, *startIdx + sizeof(preamble), 5, 0, fmtlen * 5);  
+       
+       switch (*size) {
+          case 40: { 
+           // std em410x format
+               *hi = 0;
+               *lo = ((uint64_t)(bytebits_to_byte(BitStream, 8)) << 32) | (bytebits_to_byte(BitStream + 8, 32));
+               break;
+           } 
+           case 88:  { 
+           // long em format
+               *hi = (bytebits_to_byte(BitStream, 24)); 
+               *lo = ((uint64_t)(bytebits_to_byte(BitStream + 24, 32)) << 32) | (bytebits_to_byte(BitStream + 24 + 32, 32));
+               break;
+           } 
+           default: return 0;
        
-       //loop through 10 or 22 sets of 5 bits (50-10p = 40 bits or 88 bits)
-       for (i=0; i < fmtlen; i++){ 
-               parityBits = bytebits_to_byte(BitStream + (i*5) + idx, 5);
-               //check even parity
-               if (parityTest(parityBits, 5, 0) == 0) return 0;
-               //set uint64 with ID from BitStream
-               for (uint8_t j = 0; j < 4; j++){
-                       *hi = (*hi << 1) | (*lo >> 63);
-                       *lo = (*lo << 1) | (BitStream[(i*5) + j + idx]);
-               }
        }
-       //skip last 5 bit parity test for simplicity.
-       // *size = 64 | 128;
        return 1;
 }
 
Impressum, Datenschutz