Bystroushaak's blog / English section / Hardware / I used to be a phone card cracker / AdvancedTCardReader

AdvancedTCardReader

While writing A path to the phone card emulator, I suggested several methods of breaking phone card protection. One of these methods was to break the hash by analyzing inputs and outputs. In order for something like this to be possible, it is first necessary to collect a sufficient number of challenges / responses. To do this, I wrote a short program in Wiring (basically C ++ enriched with arduino API) for Arduino diecimila.

While testing the program, I came across a strange thing - the algorithm used in Czech phone cards returns the same outputs for a quadratically increasing set of inputs. So far, I haven't made any attempts to figure out how far it goes, but in practice it means that creating an emulator wouldn't have to be that complicated - just mapping the input table. I plan to write a python script that will check the ranges and store them in the database, as soon as I'm in the mood.

Another possibility is that somewhere in the program there is a bug, if someone finds it, I will be glad if you let me know.

Example of the communication

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