Bystroushaak's blog / Czech section / Programování / Cracking telefonních karet / Homemade čtečka telefonních karet

Homemade čtečka telefonních karet

, thx to Joenash & (#freedom99, irc.c0renet.sk:6667)

Deklarace

Tento text pojednává o konstrukci čtečky karet pro telefonní automaty (ISO 7816-2) z běžně dostupných materiálů. Čtečku si samozřejmě můžete koupit hotovou, ale podle mě to za ty peníze nestojí.

V textu není řešen protokol kterým karta komunikuje - to už popsali jiní a lépe než bych to zvládl já. Pokud Vás protol zajmá, na konci dokumentu jsou odkazy na články, které se problematice věnují.

Tento text nepojednává o zápisu na kartu, protože mi to přišlo zbytečné (z části paměti kde jsou uchovány informace o kreditu Vám karta povolí jen odečítat a těch pár bitů co můžete v ostatních sekcích paměti změnit na 0 nestojí za námahu).

Potřeby

Výroba čtečky

Nejdříve je nutné nachystat si všechny nástroje a suroviny, které jsou zapotřebí k výrobě čtečky. Je také vhodné si udělat místo na stole atp..

1) Příprava pouzdra

V pouzdru na kartu musíme vyříznout díru, ke které bude později přidělána upravená čtečka SIM karet. Provedeme to tak, že vložíme kartu do pouzdra, označíme si kde se nachází kontaktní plošky čipu a po vyndání karty díru odlamovacím nožem (pokud se vám zdá tupý, odlomte z něj kus) vyřízneme. Doporučuji vyříznout díru do obou stran pouzdra - ulehčí Vám to práci s přizpůsobováním nožiček čtečky jeho přilepení.

2) Příprava čtečky SIM karet

Dál je na řadě úprava čtečky SIM karet; Štípačkami z ní odštípejte všechny nepotřebné kusy plastu tak, aby šla přiložit nožičkami těsně k pouzdru. Tuto činnost si radši dopředu promyslete, ať se nestane že odstřihnete něco, po čem se čtečka rozpadne. S ohýbáním nožiček aby pasovaly na piny karty se prozatím nezabývejte - jelikož drží v tenkém kousku plastu, dokud čtečku nezalepíte silikonem, lehce se utrhnou.

3) Přidělání čtečky SIM karet k pouzdru

Jakmile budete mít čtečku upravenou, je načase dát žhavit tavnou pistoli (nezapomeňte si pod ní dát kus papíru, lepidlo z ní často vytéká permanentně). Až bude pistole rozehřátá, vložte do pouzdra kartu a umístěte čtečku tak, aby se její pacičky dotýkaly plošek na kartě. Následně čtečku po stranách opatrně zafixujte a vyčkejte, dokud silikon (nebo co to je) neztuhne. Poté opatrně zohýbejte výstupní dráty co z ní vedou (nikoliv kontaktní pacičky!) vždy jeden dolu a jeden nahoru.

4) Příprava UTP kabelu

Nyní je vhodné připravit si kabel. Já jsem zvolil klasický UTP kabel, který by nemělo být problém sehnat (v prodejně počítačů, v elektro prodejně atp..). Je dobré si jednu stranu kabelu zafixovat (např. peanem, nebo na ní udělat uzel), aby se vodiče uvnitř nepohybovaly.

Na volné (nezafixované) straně odstraňte krytí a následně i bužírku ze třech párů vodičů. Jeden zbývající pár (např. hnědý) odstřihněte, protože není zapotřebí a v konečném důsledku by jen překážel. Je vhodné si všechny odizolované konce sestřihnout na stejnou velikost (já jsem nechal asi 3mm) a drátky pocínovat - to Vám velmi usnadní práci během připajování drátů ke čtečce.

5) Připájení pull-up rezistoru a kabelu ke čtečce

Pokud máte konce drátků pocínované, tak je čas na pájení. Jestliže jste dosud nikdy nic nepájeli, doporučuji Vám najít si nějaký návod na netu a zkoušet to podle něj tak dlouho, dokud se to nenaučíte opravdu dobře. Jakmile budete mít jistotu že pájení není problém, lokalizujte pomocí multimetru výstup z UCC a I/O plošky (viz diagram u části 9) a přidělejte mezi ně (samozřejmě na výstup - tam co budete pájet dráty) pull-up rezistor (bez něj čtečka nebude fungovat) a poté připájejte ke každému výstupu jednotlivé dráty UTP kabelu.

6) Izolace připájených drátů

Jakmile všechny dráty připájíte, tak je nutné zalepit spoje (a celkově celou vnější část čtečky kromě děr nad pacičkami) silikonem. Dávejte si přitom dobrý pozor aby se Vám kabely nedotýkaly odizolovanými částmi!

Je dobré si lepení rozložit na několik kroků a vždy počkat až předchozí strana zaschne.

7) Kontrola zkratu

Teď, když máte hlavní konstrukci hotovou, je nutné odizolovat UTP kabel i z druhé strany a proměřit (samozřejmě s vyndanou kartou) jestli někde mezi spoji nemáte zkrat.

Pokud jste pečlivě dodržovali návod, žádný tam nebude. Když by se přeci jen stalo že měřák ukáže zkrat (nulový odpor - nikoli odpor pull-up rezistoru!), je nutné nahřát šroubovák a zkratované vodiče zalepené silikonem od sebe oddělit.

8) Ohýbání nožiček čtečky

Jestliže máte vše úspěšně proměřené, je načase opatrně (!) zohýbat pacičky čtečky tak, aby dosahovali na kartu. Díky tomu že jste pouzdro (doufám) prořízli i z druhé strany, je to poměrně snadná záležitost. Dávejte si pozor na počet ohnutí - pacičky nevydrží moc pohybů a pokud s nimi budete kroutit déle než je nezbytně nutné, tak prostě upadnou.

!!! Dbejte na zachování obloukového tvaru paciček, jinak se po vložení/vysunutí karty utrhnou/ohnou (vím o čem mluvím, jednu čtečku jsem takhle zničil) !!!

9) Mapování kabelů

Vložte do čtečky kartu a zjistěte kam jste který drát připájeli. Měření provádějte tak, že z vrchní strany čtečky (dírou nad pacičkama) prostrčíte hrot měřáku, přiložíte ho na plošku karty (nikoliv na pacičku čtečky - takto zjistíte jestli má pacička správný dotek ke kartě) a pak hledáte který drát k ní pasuje.

Výsledky je dobré si někam zapsat - například takto:

Číslování pinů karty:

   ________________________
  |          |             | 
  | 1 UCC    |      4 GND  | 
  |_______   |  ___________|
  |       \ /   \          |                                     
  | 2 RST  |     |  5 VPP  | 
  |_______/ \   /._________| 
  |          | |           |
  | 3 CLK    | |    6 I/O  |
  `----------^-^-----------'

Zapojení drátů k mojí čtečce:

  1. UCC == Oranžovo-bílá
  1. RST == Zelená
  1. CLK == Zeleno-bílá
  1. GND == Modrá
  1. VPP == Modro-bílá (tento kontakt je běžně nevyužit)
  1. I/O == Oranžová

Pokud jste k dané plošce nenašli odpovídající kabel, je nutné více ohnout pacičku čtečky - samozřejmě s vysunutou kartou. Jakmile budete mít zjištěno kam patří který drát, je možné stavbu prohlásit za hotovou.

Čtení

Ke čtení jsem napsal program pro Arduino diecimilia, který umí přečíst paměť telefonní kartu a zobrazit o ní pár drobností (sériové číslo, počet jednotek na kartě). Pokud by měl někdo zájem vytvořit vlastní, v odkazech na konci článku jsou linky na články popisující protokol.

Připojte čtečku k arduinu. RST na dig. port 10, CLK na 11 a I/O na 12. K portu 13 můžete připojit tlačítko, které spustí načítání ze čtečky do interní EEPROM paměti arduina - to se hodí napřiklad když nemáte po ruce PC, ale pouze arduino a čtečku.

Program komunikuje (jak je u arduina zvykem) pomocí sériové komunikace. Ovládání můžete provádět pomocí software k arduinu, nebo vlastním programem či pomocí sériového terminálu připojeného na dig. pin 0 a 1.

Ukázka menu

  Prikazy:
      r - Nacist dump z ctecky
      n - Nacist dump z EEPROM
      u - Ulozit dump do EEPROM

      d - Zobrazit dump
      a - Zobrazit alternativni (zjednoduseny) dump

      i - Zobrazit info o karte

Ukázka dumpu

  Byte  Index    DEC  HEX     BIN
    0  000-007:  161   A1  1010 0001 
    1  008-015:   43   2B  0010 1011 
    2  016-023:   99   63  0110 0011 
    3  024-031:    0   00  0000 0000 
    4  032-039:   70   46  0100 0110 
    5  040-047:  205   CD  1100 1101 
    6  048-055:  151   97  1001 0111 
    7  056-063:   35   23  0010 0011 
    8  064-071:    0   00  0000 0000 
    9  072-079:    0   00  0000 0000 
   10  080-087:  127   7F  0111 1111 
   11  088-095:   63   3F  0011 1111 
   12  096-103:    1   01  0000 0001 
   13  104-111:  254   FE  1111 1110 
   14  112-119:   34   22  0010 0010 
   15  120-127:   97   61  0110 0001

Ukázka alternativního dumpu

  A1 2B 63 00 46 CD 97 23 00 00 7F 3F 01 FE 22 61 
  unsigned char dump[]= {0xA1, 0x2B, 0x63, 0x00, 0x46, 0xCD, 0x97, 0x23, 0x00, 0x00, 0x7F, 0x3F, 0x01, 0xFE, 0x22, 0x61};  // C array
  dump= [0xA1, 0x2B, 0x63, 0x00, 0x46, 0xCD, 0x97, 0x23, 0x00, 0x00, 0x7F, 0x3F, 0x01, 0xFE, 0x22, 0x61]  # python array

Ukázka informací o kartě

  Pocet impulzu: 49 (nikoli korun!)
  Seriove cislo: 0004640151

Zdrojový kód

/* APCReader [Arduino Phone Card Reader] v1.0.0 (09.06.09)
 * by Bystroushaak (bystrousak@kitakitsune.org) 
 *
 * Popis:
 *   Jednoduchy programek, ktery pomoci homemade ctecky precte obsah telefonni 
 *   (ISO 7816-2 - to je ta 6 pinnova, nebo 8 pinnova se dvema nezapojenyma) 
 *   karty.
 *
 * Lang:     Wiring
 * Platform: Arduino Diecimila
 * Licence:  CC by-nc-sa (http://creativecommons.org/licenses/by-nc-sa/3.0/cz/)
 *
 *******************************************************************************
 *
 * Reset a čtení:
 * -------------
 *   Address counter (ukazatel adresy) se resetuje na 0, pokud přijde CLK pulz
 * ve chvíli kdy je Reset v log. 1. {Poznamka: pokud zrovna AC ukazuje na adresu
 * z rozsahu 0 až 7, reset neproběhne}
 *
 *       __________________
 * _____|                  |_____________________________________________ Reset
 *      :                  :
 *      :        _____     :  _____       _____       _____       _____
 * _____:_______|     |____:_|     |_____|     |_____|     |_____|     |_ Clk
 *      :       :          : :     :     :     :     :     :     :     :
 * _____:_______:__________:_:_____:_____:_____:_____:_____:_____:_____:_
 * _____:___n___|_____0____:_|_____1_____|_____2_____|_____3_____|___4_:_(Address)
 *      :                  :       :           :           :           :
 * _____:                  :_______:___________:___________:___________:_
 * _____XXXXXXXXXXXXXXXXXXXX_______|___________|___________|___________|_ Data
 * Bit n                      Bit 0    Bit 1        Bit2       Bit3
 * 
 *   Address counter je inkrementován o 1 pokaždé, když na CLK přijde impulz 
 * během doby, kdy Reset zůstane v log. 0. Data na adrese jsou poslána na I/O pin
 * po každé sestupné CLK hraně. Address counter není možné dekrementovat. Pokud
 * se chceme dostat na bit který již byl přečten, musíme resetovat Address 
 * counter a inkrementovat do doby než se dostanem na požadovanou adresu. 
 *
 * (volný překlad z http://gsho.thur.de/gsho/phonecard/bin/phonecards_204.txt)
 *
 **/

#undef int          // bez tohodle se nenecha includnout stdio.h
#include <stdio.h>  // potrebne kvuli sprintf()
#include <EEPROM.h> // kvuli ukladani do EEPROM

#define RST 12
#define CLK 13
#define IO  10      // nutne pripojit prez pull-up rezistor (3-5KOhm) k VCC!
#define SNS 9       // sensor - pokud je na nej pripojeno kladne napeti, automaticky nacte dump a ulozi ho do EEPROM
#define LEN 128     // velikost pameti karty (v cechach to je 128b)

// Precte celou pamet karty. Data ulozi do pole predaneho v parametru.
void readCard(byte *dump){
  // reset adresniho pocitadla na 0
  digitalWrite(RST, HIGH);
  delay(1);
  digitalWrite(CLK, HIGH);
  delay(1);
  digitalWrite(CLK, LOW);
  delay(1);
  digitalWrite(RST, LOW);
  delay(1);
  
  // cteni obsahu pameti karty
  byte dec = 0, cnt = 0;
  for(int i = 0; i < LEN; i++){
    // prevod z binarnich cisel na dekadicka
    dec <<= 1;
    if (digitalRead(IO)){
      dec++;
    }
    // ulozeni dekadickeho cisla
    if (cnt++ == 7){
      *dump++ = dec; 
      cnt= 0;
    }
    
    // clk pulz - inkrementace Address counteru
    digitalWrite(CLK, HIGH);
    delay(1);
    digitalWrite(CLK, LOW);
    delay(1);
  }
  
  // nulovani vystupu
  digitalWrite(CLK, LOW);
  digitalWrite(RST, LOW);
}

// Vypise binarni reprezentaci promenne var
void printBin(byte var){
    char tmp = 0;
    int pocetbitu = sizeof(var) * 8;
    
    int i;
    for(i = 0; i < pocetbitu; i++){
        tmp = var >> (pocetbitu - i - 1) & 1;
        if (tmp){
            Serial.print('1');
        }else{
            Serial.print('0');
        }
        if ((i + 1) % 4 == 0){  // vypise oddelovaci mezeru po kazdych 4 vypsanych znacich
            Serial.print(' ');
        }
    }
}

// zobrazi naformatovany dump
void printDump(byte *dump){
  char buffer[30];
  
  printClr();
  Serial.println("Byte  Index    DEC  HEX     BIN");
  
  byte i, cnt= 0, n;
  for (i = 0; i < (LEN / 8); i++){
    n = *dump++;
    sprintf(buffer, " %2d  %03d-%03d:  %3d   %02X  ", i, cnt, cnt + 7, n, n);
    Serial.print(buffer);
    printBin(n);
    Serial.println();
    cnt += 8;
  }
}

// Vypise na jednom radku jako Hexa, na dalsim jako C pole
// a na poslednim jako python pole - vhodne pro pozdejsi
// zpracovani udaju
void printRaw(byte *dump){
  char buffer[2];
  byte *tmp = dump;
  
  printClr();
  
  // hexa
  int i;
  for (i = 0; i < (LEN / 8); i++){
    sprintf(buffer, "%02X ", *tmp++);
    Serial.print(buffer);
  }
  
  // c pole
  Serial.println();
  tmp= dump;
  Serial.print("unsigned char dump[]= {");
  for (i = 0; i < (LEN / 8); i++){
    sprintf(buffer, "0x%02X", *tmp++);
    Serial.print(buffer);
    if (i < ((LEN / 8) - 1)){
      Serial.print(", ");
    }
  }
  Serial.println("};  // C array");
  
  // python pole
  tmp= dump;
  Serial.print("dump= [");
  for (i = 0; i < (LEN / 8); i++){
    sprintf(buffer, "0x%02X", *tmp++);
    Serial.print(buffer);
    if (i < ((LEN / 8) - 1)){
      Serial.print(", ");
    }
  }
  Serial.print("]  # python array");
}

// Vrati hodnotu bitu na pozici n promenne var
int getBit(int var, int n){
    return var & (1 << n) && 1;
}

// parsovani dat z karty a jejich vypis
// uznavam ze tahle fce je trochu chuda, pokud nekoho napadne co jeste zobrazit, 
// popr. sezene popis pameti ceske (ne slovenske!) karty, muj mail mate..
void printInf(byte *dump){
  char buffer[40];
  
  printClr();
  
  // pocet jednotek - timhle si nejsem moc jistej, protoze moje karta je uz 
  // prosla, tidiz se nemuzu podivat v automatu kolik jednotek na ni opravdu je
  // pokud by zde nekdo nasel chybu, budu rad za kratky report na mail
  int i, j = 0, o = 0;
  for(i = 0; i < 8; i++){
    if (getBit(dump[12], i)){
      j++;
    }
    if (getBit(dump[11], i)){
      o++;
    }
  }
  sprintf(buffer, "Pocet impulzu: %d (nikoli korun!)", o * 8 + j);
  Serial.println(buffer);
  
  // seriove cislo ceske karty
  long int snum = 0;
  snum |= dump[4];
  snum <<= 8;
  snum |= dump[5];
  snum <<= 8;
  snum |= dump[6];
  sprintf(buffer, "Seriove cislo: %010lu", snum);
  Serial.println(buffer);
}

// vypise oddelovac
void printClr(void){
  Serial.println();
  for(int i = 0; i < 80; i++){
    Serial.print('-');
  }
  Serial.println();
}

void printMenu(void){
  printClr();
  Serial.println("APCReader v1.0.0 (09.06.09) by Bystroushaak (bystrousak@kitakitsune.org)");
  Serial.println();
  Serial.println("Prikazy:");
  Serial.println("    r - Nacist dump z ctecky");
  Serial.println("    n - Nacist dump z EEPROM");
  Serial.println("    u - Ulozit dump do EEPROM");
  Serial.println();
  Serial.println("    d - Zobrazit dump");
  Serial.println("    a - Zobrazit alternativni (zjednoduseny) dump");
  Serial.println();
  Serial.println("    i - Zobrazit info o karte");
  Serial.println();  
}

void dump2EEPROM(byte *dump){
  int i;
  for(i = 0; i < LEN / 8; i++){
    EEPROM.write(i, dump[i]);
  }
}

// inicializace pinu a seriove konzole
void setup(){
  pinMode(RST, OUTPUT);
  pinMode(CLK, OUTPUT);
  digitalWrite(RST, LOW);   // toto nejspis neni nutne
  digitalWrite(CLK, LOW);
  
  pinMode(IO, INPUT);
  pinMode(SNS, INPUT);
  
  Serial.begin(9600);
  delay(200);               // seriova komunikace potrebuje chvili na ustanoveni
}

void loop(){
  byte dump[LEN];
  char cmd, loaded=0;
  
  printMenu();
  
  while (true){    
    if (Serial.available() > 0) {
      cmd= (char) Serial.read();
        
      switch(cmd){
        case 'r':          // nacist dump ze ctecky
          readCard(dump);
          
          loaded = 1;
          Serial.println("Nacteno.");
          
          break;
        case 'u':         // ulozit dump do eeprom
          dump2EEPROM(dump);
          
          loaded = 1;
          Serial.println("Ulozeno.");
          
          break;
        case 'n':         // nacist dump z eeprom
          int i;
          for(i = 0; i < LEN / 8; i++){
            dump[i] = EEPROM.read(i);
          }
          
          loaded = 1;
          Serial.println("Nacteno.");
          
          break;
        case 'd':         // vypsat dump 
          if (!loaded){
            Serial.println("ZATIM NEBYL NACTEN DUMP!");
            break;
          }
          printDump(dump);
          break;
        case 'a':         // vypsat cisty dump
          if (!loaded){
            Serial.println("ZATIM NEBYL NACTEN DUMP!");
            break;
          }
          printRaw(dump);
          break;
        case 'i':         // vypsat informace o karte 
          if (!loaded){
            Serial.println("ZATIM NEBYL NACTEN DUMP!");
            break;
          }
          printInf(dump);
          break;   
      }
      
      printMenu();
    }
    
    // pokud je alespon na 200ms pripojeno na SNS (sensor) napeti, 
    // nacte dump karty ze ctecky a ulozi ji do EEPROM
    if (digitalRead(SNS)){
      delay(200);
      if (digitalRead(SNS)){
        readCard(dump);
        dump2EEPROM(dump);
      }
    }
  }
}

Nové verze

Novější verze bude s trochou štěstí (uvědomuji si, že nic netrvá věčně a co se týče internetu, platí to dvojnásob..) možné sehnat na http://kitakitsune.org (tedy pokud bude nějakých nových verzí potřeba..).

Zajímavé odkazy

Become a Patron