Bystroushaak's blog / Czech section / Programování / Cracking telefonních karet / AdvancedTCardReader

AdvancedTCardReader

Při psaní článku Cesta k emulátoru telefonní karty jsem navrhl několik metod prolomení ochrany telefonní karty. Jednou z těchto metod bylo i prolomení hashe pomocí analýzy vstupů a výstupů. Aby bylo něco takového možné, je nutné prvně nasbírat dostatečný počet challenge/response. K tomu jsem napsal krátký program ve Wiringu (v podstatě se jedná o C++ obohacené o arduino api) pro Arduino diecimila.

Během zkoušení programu jsem narazil na zvláštní věc - algoritmus používaný v českých telefonních kartách vrací pro kvadraticky zvětšující se množinu vstupů stejné výstupy. Zatím jsem nedělal žádné pokusy zjistit jak daleko to sahá, ale v praxi to znamená, že vytvoření emulátoru by nemuselo být tak složité - stačilo by zmapovat tabulku vstupů. Mám v plánu na to napsat script v pythonu, který bude zjišťovat rozsahy a ukládat je do databáze, pustím se do toho jakmile budu mít náladu..

Další možnost je, že někde v programu je chyba, pokud jí někdo najdete, budu rád pokud mi dáte vědět.

Ukázka komunikace

Type 'h' for help.
> h
Commands:
    d
         - print dump
    c [p]
         - print one c/r result with random number, or [p] as parameter
    r [p]
         - start with c/r from zero, or [p]
         - type anything to interrupt loop

Format:
    dump (dec dec2 ... decn)
    challenge:response (dec:dec)
    ...
> d
161 43 99 0 10 118 38 35 0 0 0 0 0 254 185 30 

> r
161 43 99 0 10 118 38 35 0 0 0 0 0 254 185 30 
0:62214
1:10594
2:48316
3:48316
4:37074
5:37074
6:37074
7:37074
8:43400
9:43400
10:43400
11:43400
12:43400
13:43400
14:43400
15:43400
16:18722
17:18722
18:18722
19:18722
20:18722
21:18722
22:18722
23:18722
24:18722
25:18722
26:18722
27:18722
28:18722
29:18722
30:18722
31:18722
32:24876
33:24876
34:24876
35:24876
36:24876
37:24876
38:24876
39:24876
40:24876
41:24876
42:24876
43:24876
44:24876
45:24876
46:24876
47:24876
48:24876
49:24876
50:24876
51:24876
52:24876
53:24876
54:24876
55:24876
56:24876
57:24876
58:24876
59:24876
60:24876
61:24876
62:24876
63:24876
64:62544
65:62544
66:62544
67:62544
68:62544
69:62544
70:62544
71:62544
72:62544
73:62544
74:62544
75:62544
76:62544
77:62544
78:62544
79:62544
80:62544
81:62544
82:62544
83:62544
84:62544
85:62544
86:62544
87:62544
88:62544
89:62544
90:62544
91:62544
92:62544
93:62544
94:62544
95:62544
96:62544
97:62544
98:62544
99:62544
100:62544
101:62544
102:62544
103:62544
104:62544
105:62544
106:62544
107:62544
108:62544
109:62544
110:62544
111:62544
112:62544
113:62544
114:62544
115:62544
116:62544
117:62544
118:62544
119:62544
120:62544
121:62544
122:62544
123:62544
124:62544
125:62544
126:62544
127:62544
128:25500
129:25500
130:25500
131:25500
132:25500
133:25500
134:25500
135:25500
136:25500
137:25500
138:25500
139:25500
140:25500
141:25500
142:25500
143:25500
144:25500
145:25500
146:25500
147:25500
148:25500
149:25500
150:25500 

Source code

/* AdvancedTCardReader v0.9.4 (25.07.2010) by Bystroushaak (bystrousak@kitakitsune.org)
 * Lang:     Wiring
 * Platform: Arduino Diecimila
 * Licence:  CC by-nc-sa (http://creativecommons.org/licenses/by-nc-sa/3.0/cz/)
 *
 * This program allows read content of phone cards and also shows results of 
 * challenge/response security hash alghoritm.
 * 
 * Program works in simple command line mode.
 *
 * Pins:
 *       ________________________
 *      |          |             |
 *      | 1 UCC    |      4 GND  |
 *      |_______   |  ___________|
 *      |       \ /   \          |
 *      | 2 RST  |     |  5 VPP  |
 *      |_______/ \   /._________|
 *      |          | |           |
 *      | 3 CLK    | |    6 I/O  |
 *      `----------^-^-----------'
 *
 * Parameters:
 *  UCC:  -0.3 .. 6V
 *  ICC:   5 mA
 *
 *  UI/O: -0.3 .. 6V
 *
 *  I/O0: 0 .. 0.8V
 *  I/O1: 3.5 .. 3.5V
 *
 *  Temp: -20 .. 55 °C
*/

#define LEN 128       // card length - 128b in Czech republic
#define MAX_NUM 281474976710656LL // 2 ** 48 (maximum range of challenge)
#define CMD_BUFF_LEN 18 // len(str(MAX_NUM)) + 1 (cmd) + 1 (space) + 1 (for sure)

// port definition
#define CLK 13
#define RST 12
#define VPP 11        // just formal definition, this port isn't used
#define IO  10

// CLK puls makro
#define CLKpuls() digitalWrite(CLK, HIGH); digitalWrite(CLK, LOW);

/*= setup ======================================================================
 * Initialize pins and serial line.
 *
 * Argument(s): None.
 * Returns: Nothing.
*/
void setup(){
    pinMode(RST, OUTPUT);
    pinMode(CLK, OUTPUT);
    pinMode(IO, INPUT);

    digitalWrite(RST, LOW);
    digitalWrite(CLK, LOW);

    // serial initialization
    Serial.begin(9600);
    delay(200);         // serial comunication needs some time to initialization

    // initialization for random numbers
    randomSeed(analogRead(0));
}
//==============================================================================

/*= resetSeq ===================================================================
 * Reset sequence:
 *
 *    _____________
 * __|  :       :  |__  RST
 *   :  :_______:  :
 * __:__| >=1ms |__:__  CLK
 *
 *
 * Argument(s): None.
 * Returns: Nothing.
*/
void resetSeq(void){
    digitalWrite(RST, HIGH);
    digitalWrite(CLK, HIGH);

    delay(1);

    digitalWrite(CLK, LOW);
    digitalWrite(RST, LOW);
}
//==============================================================================


/*= writeSeq ===================================================================
 * Write sequence:
 *
 *    __
 * __|  |______________  RST
 *   :  :  :________:
 * __:__:__| >=10ms |__  CLK
 *
 *
 * Argument(s): None.
 * Returns: Nothing.
*/
void writeSeq(void){
    digitalWrite(RST, HIGH);
    digitalWrite(RST, LOW);

    digitalWrite(CLK, HIGH);
    delay(10);
    digitalWrite(CLK, LOW);
}
//==============================================================================


/*= readDump ===================================================================
 * Reads informations from card into *dump.
 *
 * Argument(s): byte *dump
 * Returns: Nothing.
*/
void readDump(byte *dump){
    *dump = 0;

    // reset address counter
    resetSeq();

    // reads informations
    for (uint16_t i = 0, n = 128; i < LEN; i++){
        if (digitalRead(IO)){
            *dump += n;
        }
        n >>= 1;

        // saves every 8 readed bits into *dump
        if ((i + 1) % 8 == 0){
            *(++dump) = 0;
            n = 128;
        }

        // increment address counter
        CLKpuls();
    }
}
//==============================================================================


/*= printDump ==================================================================
 * Prints dump into serial line.
 *
 * Argument(s): byte *dump
 * Returns: Nothing.
*/
void printDump(byte *dump){
    for (uint16_t i = 0; i < LEN / 8; i++){
        Serial.print(dump[i], DEC);
        Serial.print(' ');
    }

    Serial.println();
}
//==============================================================================


/*= response ===================================================================
 * Send challenge to the card and wait for an response.
 *
 * Argument(s): uint64_t challenge
 * Returns: uint16_t response
*/
uint16_t response(uint64_t challenge){
    resetSeq();

    // increment address counter to 110
    for (uint8_t i = 0; i < 110; i++){
        CLKpuls();
    }

    writeSeq();

    for (uint8_t i = 0; i < 177; i++){
        CLKpuls();
    }

    // write challenge bits to the card
    pinMode(IO, OUTPUT);
    for (int16_t i = 47; i >= 0; i--){
        if (challenge >> i)
            digitalWrite(IO, HIGH);
        else
            digitalWrite(IO, LOW);

        CLKpuls();
    }
    pinMode(IO, INPUT);

    // read response
    uint16_t n = 32768, response = 0;
    for (uint8_t i = 0; i < 16; i++){
        for (uint8_t j = 0; j < 160; j++){
            CLKpuls();
        }

        if (digitalRead(IO))
            response += n;

        n >>= 1;
    }

    return response;
}
//==============================================================================


/*= printParameter =============================================================
 * Convert uint64_t to string and print it.
 * (Arduino haven't overloaded Serial.print() for uint64_t)
 *
 * Argument(s): uint64_t parameter
 * Returns: None.
*/
void printParameter(uint64_t parameter){
    uint64_t tmp = parameter, newtmp;
    uint8_t rest = 0, cnt = 0;
    char buffer[16];

    if (tmp == 0){
        Serial.print(0, DEC);
        return;
    }

    // convert uint64_t to string (thx to w (irc://#shadowfall@monka.hysteria.cz) [multiplying is 3kB smaller than dividing])
    while (tmp > 0){
        newtmp = tmp / 10;
        buffer[cnt++] = '0' + tmp - newtmp * 10;
        tmp = newtmp;
    }

    // reverse buffer
    for (; cnt > 0;){
        Serial.print(buffer[--cnt]);
    }
}
//==============================================================================



//==============================================================================
//=========================== Main function ====================================
//==============================================================================
void loop(){
    byte dump[LEN / 8];

    uint64_t parameter;

    char cmd[CMD_BUFF_LEN];
    uint8_t cnt = 0;

    // ---

    Serial.println("Type 'h' for help.");
    Serial.print("> ");

    while (1){
        cnt = 0;
        parameter = 0;

        // read command, if available
        if (Serial.available()){
            while (Serial.available() && cnt < CMD_BUFF_LEN) {
                cmd[cnt++] = (char) Serial.read();
                delay(1);
            }
            cmd[cnt++] = 0;
        }

        // parse parameter
        if (cnt > 1){
            parameter = 0;

            for (int i = 0; i < cnt; i++){
                if (cmd[i] >= '0' && cmd[i] <= '9'){
                    parameter *= 10;
                    parameter += cmd[i] - '0';
                }
            }
        }

        // execute command
        if (cnt > 0){
            Serial.println(cmd);

            if (parameter > MAX_NUM){
                Serial.println("Parameter too big! Maximum is 2^48.");
                continue;
            }

            switch(cmd[0]){
                case 'h':
                    Serial.println("Commands:");
                    Serial.println("\td\n\t\t - print dump");
                    Serial.println("\tc [p]\n\t\t - print one c/r result with random number, or [p] as parameter");
                    Serial.println("\tr [p]\n\t\t - start with c/r from zero, or [p]");
                    Serial.println("\t\t - type anything to interrupt loop");
                    Serial.println("\nFormat:\n\tdump (dec dec2 ... decn)\n\tchallenge:response (dec:dec)\n\t...");

                    break;
                case 'd':
                    readDump(dump);
                    printDump(dump);

                    break;
                case 'c':
                    readDump(dump);
                    printDump(dump);

                    // if haven't parameter, generate random number
                    if (cnt <= 2){
                        parameter = (uint64_t) random(16777216) * random(16777216);
                    }

                    //Serial.print((unsigned long int) parameter, DEC);
                    printParameter(parameter);
                    Serial.print(":");
                    Serial.println((unsigned long int) response(parameter), DEC);

                    break;
                case 'r':
                    readDump(dump);
                    printDump(dump);

                    // show c/r from parameter to 2**48
                    for (; parameter < MAX_NUM; parameter++){
                        // break loop
                        if (Serial.available()){
                            break;
                        }

                        printParameter(parameter);
                        Serial.print(":");
                        Serial.println((unsigned long int) response(parameter), DEC);
                    }

                    break;
                default:
                    Serial.print("Unknown command \"");
                    Serial.print(cmd);
                    Serial.println("\"");

                    break;
            }

            Serial.print("> ");
        }
    }
}
//==============================================================================


/*   ______________________
 *  /                      \  __
 *  | I hope it works ^-^  | /  \
 *  \_____________________ / \__/ O . \`.,'/         _----.__
 *                                    /_  _\`------./     ; /
 *                                      \/` .____. ;;-__.--'
 *                                        |//     \))
 *
*/
Become a Patron