/* * Phoenix -- Videocrypt smart card reanimator for DOS. * * Copyright 1994 by Markus Kuhn * * Compiles e.g. with Borland C++ 2.0, small model. */ #define VERSION "0.2" #include #include #include #include #include #include /* for kbhit() & clrscr() */ #include /* for int86() */ #include "async.h" #define CLK 3.571e6 /* CLK frequency in Hz delivered by adapter */ #define SECOND 1000000L /* one second in microseconds */ #define RESET_ANSWER_MAX 33 char reset_answer[RESET_ANSWER_MAX]; int reset_length = 0; /* message directions */ #define IN 0 /* to decoder */ #define OUT 1 /* to card */ /* * These are some delay values (microseconds) used in the program. * Use command line option w to modify them. */ #define DELAYS 1 unsigned long delus[DELAYS] = { 1000, /* wait after each outgoing byte */ }; const unsigned char sky_keys[56] = { 0x65, 0xe7, 0x71, 0x1a, 0xb4, 0x88, 0xd7, 0x76, 0x28, 0xd0, 0x4c, 0x6e, 0x86, 0x8c, 0xc8, 0x43, 0xa9, 0xec, 0x60, 0x42, 0x05, 0xf2, 0x3d, 0x1c, 0x6c, 0xbc, 0xaf, 0xc3, 0x2b, 0xb5, 0xdc, 0x90, 0xf9, 0x05, 0xea, 0x51, 0x46, 0x9d, 0xe2, 0x60, 0x70, 0x52, 0x67, 0x26, 0x61, 0x49, 0x42, 0x09, 0x50, 0x99, 0x90, 0xa2, 0x36, 0x0e, 0xfd, 0x39 }; const unsigned char adult_keys[96] = { 0xd9, 0x45, 0x08, 0xdb, 0x7c, 0xf9, 0x56, 0xf7, 0x58, 0x18, 0x22, 0x54, 0x38, 0xcd, 0x3d, 0x94, 0x09, 0xe6, 0x8e, 0x0d, 0x9a, 0x86, 0xfc, 0x1c, 0xa0, 0x19, 0x8f, 0xbc, 0xfd, 0x8d, 0xd1, 0x57, 0x56, 0xf2, 0xb6, 0x4f, 0xc9, 0xbd, 0x2a, 0xb3, 0x9d, 0x81, 0x5d, 0xe0, 0x05, 0xb5, 0xb9, 0x26, 0x67, 0x3c, 0x65, 0xa0, 0xba, 0x39, 0xc7, 0xaf, 0x33, 0x24, 0x47, 0xa6, 0x20, 0x1e, 0x14, 0x6f, 0x48, 0x9b, 0x4d, 0xa6, 0xf9, 0xd9, 0xdf, 0x6e, 0xac, 0x84, 0xfa, 0x8b, 0x2e, 0xb6, 0x76, 0x19, 0xc1, 0xb0, 0xa3, 0xbb, 0x0c, 0xfd, 0x70, 0x72, 0xca, 0x55, 0xef, 0xa0, 0x7f, 0xbf, 0x59, 0xad }; void kernel(unsigned char *out, int *oi, const unsigned char in, const unsigned char *key) { unsigned char c; out[*oi] ^= in; c = ~(key[out[*oi] >> 4] + key[(out[*oi] & 0x0f) + 16]); c = (c << 1) | (c >> 7); /* rotate 1 bit left */ c += in; c = (c >> 3) | (c << 5); /* rotate 3 bit right */ *oi = (*oi + 1) & 7; out[*oi] ^= c; return; } int hash(unsigned char *msg, unsigned char *answ, const unsigned char *key) { int i; int oi = 0; /* index in output array answ[] */ int check = 0; /* flag for incorrect signature/checksum */ unsigned char b = 0; for (i = 0; i < 8; i++) answ[i] = 0; /* initial hashing */ for (i = 0; i < 27; i++) kernel(answ, &oi, msg[i], key); /* check the 4 signature bytes and continue hashing */ for (i = 27; i < 31; i++) { kernel(answ, &oi, b, key); kernel(answ, &oi, b, key); b = msg[i]; if (answ[oi] != msg[i]) check |= 1; /* test signature */ msg[i] = b = answ[oi]; oi = (oi + 1) & 7; } /* final hashing with last byte */ for (i = 0; i < 64; i++) kernel(answ, &oi, msg[31], key); /* set 8-bit checksum */ b = 0; for (i = 0; i < 32; i++) b += msg[i]; if (b != 0) check |= 2; msg[31] -= b; answ[7] &= 0x0f; /* only 60-bit are needed by decoder */ return check; } /* * This is the algorithm and key as used in a PIC16C84 * BSkyB clone card. It worked fine between 1994-05-18 * and 1994-06-27. It still produces the correct signature, * but not the correct hash result after 1994-06-28 * until today. * * MK, 1994-06-30 */ const unsigned char key09[216] = { 0x91, 0x61, 0x9d, 0x53, 0xb3, 0x27, 0xd5, 0xd9, 0x0f, 0x59, 0xa6, 0x6f, 0x73, 0xfb, 0x99, 0x4c, 0xfb, 0x45, 0x54, 0x8e, 0x20, 0x5f, 0xb3, 0xb1, 0x38, 0xd0, 0x6b, 0xa7, 0x40, 0x39, 0xed, 0x2a, 0xda, 0x43, 0x8d, 0x51, 0x92, 0xd6, 0xe3, 0x61, 0x65, 0x8c, 0x71, 0xe6, 0x84, 0x65, 0x87, 0x03, 0x55, 0xbc, 0x64, 0x07, 0xbb, 0x79, 0x9e, 0x40, 0x97, 0x89, 0xc4, 0x14, 0x8f, 0x8b, 0x41, 0x4d, 0x2a, 0xaa, 0xe8, 0xe1, 0x08, 0xcd, 0x82, 0x43, 0x8f, 0x6f, 0x36, 0x9b, 0x72, 0x47, 0xf2, 0xa4, 0x49, 0xdd, 0x8b, 0x6e, 0x26, 0xc6, 0xbf, 0xb7, 0xd8, 0x44, 0xc3, 0x70, 0xa3, 0x4c, 0xb6, 0xb2, 0x37, 0x9b, 0x09, 0xdf, 0x32, 0x28, 0x24, 0x86, 0x8d, 0x5f, 0xe6, 0x4b, 0x5d, 0xd0, 0x2f, 0xdb, 0xac, 0x2e, 0x78, 0x1e, 0xcc, 0x52, 0xc1, 0x61, 0xea, 0x82, 0xca, 0xb3, 0xf4, 0x8f, 0x63, 0x8e, 0x6c, 0xbc, 0xaf, 0xc3, 0x2b, 0xb5, 0xdc, 0x90, 0xf9, 0x05, 0xea, 0x51, 0x46, 0x9d, 0xe2, 0x60, 0x01, 0x35, 0x59, 0x79, 0x00, 0x00, 0x55, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10, 0x6e, 0x1c, 0xbd, 0xfe, 0x44, 0xeb, 0x79, 0xf3, 0xab, 0x5d, 0x23, 0xb3, 0x20, 0xd2, 0xe7, 0xfc, 0x00, 0x03, 0x6f, 0xd8, 0xb7, 0xf7, 0xf3, 0x55, 0x72, 0x47, 0x13, 0x7b, 0x0c, 0x08, 0x01, 0x8a, 0x2c, 0x70, 0x56, 0x0a, 0x85, 0x18, 0x14, 0x43, 0xc9, 0x46, 0x64, 0x6c, 0x9a, 0x99, 0x59, 0x0a, 0x6c, 0x40, 0xd5, 0x17, 0xb3, 0x2c, 0x69, 0x41, 0xe8, 0xe7, 0x0e }; void kernel_b(const unsigned char in, unsigned char *answ, const unsigned char sel) { unsigned char a, b, c, d; unsigned short m; int i; a = in; for (i = 0; i <= 4; i += 2) { b = answ[i] & 0x3f; if (sel <= 8 ) { if (sel == 2) b = key09[b + 0x40]; else { if (sel < 2 && b == 0) /* ??? */ b = key09[b + 0x8d]; else b = key09[b] ^ key09[b + 0x98]; } } else b = key09[b] ^ key09[b + 0x98]; /* only this one is used */ c = a + b - answ[i+1]; d = (answ[i] - answ[i+1]) ^ a; m = d * c; answ[i + 2] ^= (m & 0xff); answ[i + 3] += m >> 8; a = (a << 1) | (a >> 7); a += 0x49; } m = answ[6] * answ[7]; a = (m & 0xff) + answ[0]; if (a < answ[0]) a++; answ[0] = a + 0x39; a = (m >> 8) + answ[1]; if (a < answ[1]) a++; answ[1] = a + 0x8f; return; } int hash_b(unsigned char *msg, unsigned char *answ) { int i, j; int check = 0; unsigned char b = 0; for (i = 0; i < 8; i++) answ[i] = 0; for (i = 0; i < 27; i++) kernel_b(msg[i], answ, msg[1]); for (i = 27; i < 31; i++) { kernel_b(b, answ, msg[1]); kernel_b(b, answ, msg[1]); b = msg[i] = answ[7]; } /* final 64 iterations */ for (i = 0; i < 64; i++) kernel_b(msg[31], answ, msg[1]); /* set 8-bit checksum */ b = 0; for (i = 0; i < 32; i++) b += msg[i]; if (b != 0) check |= 2; msg[31] -= b; answ[7] &= 0x0f; /* only 60-bit are needed by decoder */ return check; } /* * The decoder requests every ~2.5 seconds an answer to a 32-byte * packet (msg) from the chip card. The card's 8-byte answer (answ) to * this request is calculated by this function using hash() and the * right key bytes. */ int decode(unsigned char *msg, unsigned char *answ) { int i; int check; /* flag for incorrect signature/checksum */ const unsigned char *key; /* pointer to the 32-byte key to be used */ /* select Sky key */ if (msg[1] >= 0x43) { /* 09 card */ check = hash_b(msg, answ); } else { /* 07 card */ if (msg[1] > 0x3a) key = sky_keys + 24; else if (msg[1] > 0x32) key = sky_keys + 8; else key = sky_keys; check = hash(msg, answ, key); /* following 0xa0 fix has been necessary since 1994-04-27 */ if ((msg[6] & 0xf0) == 0xa0) for (i = 0; i < 8; i++) answ[i] = 0; } if ((check & 1) == 0) return check | (1 << 2); /* Sky key was ok */ #if 0 /* if Sky failed, try Adult Channel */ if (msg[1] > 0x4a) key = adult_keys + 48; else if (msg[1] > 0x43) key = adult_keys + 40; else if (msg[1] > 0x3a) key = adult_keys + 32; else if (msg[1] > 0x33) key = adult_keys + 8; else key = adult_keys; check = hash(msg, answ, key); if ((check & 1) == 0) return check | (2 << 2); /* Adult key was ok */ #endif #if 0 for (i = 0; i < sizeof(sky_keys); i += 8) if ((hash(msg, answ, sky_keys + i) & 1) == 0) printf("Got it!!! (sky, i = %d)\n", i); #endif /* no known key resulted in a correct signature test */ for (i = 0; i < 8; i++) answ[i] = 0; return check; } /* * This function uses the 2nd and 3rd byte in 74h commands in order to * create the four bytes with which the command and card address have been * XORed. */ void make_xor(unsigned char msg1, unsigned char msg2, unsigned char *xor_byte) { int i; unsigned char a, b; a = msg1 ^ msg2; a = (a << 4) | (a >> 4); /* swap nibbles */ b = msg2; for (i = 0; i < 4; i++) { b = (b << 1) | (b >> 7); /* rotate left */ b += a; xor_byte[i] = b; } return; } /* * Reverse and invert all bits in each byte of a string. * E.g. 10010111b (0x97) becomes 00010110b (0x16). */ void inverse(char *data, int len) { int i, j; unsigned char c; for (i = 0; i < len; i++) { c = 0; for (j = 0; j < 8; j++) c |= ((data[i] & (1 << j)) != 0) << (7 - j); data[i] = ~c; } return; } /* * Uses a BIOS function in order to wait a specified number of * microseconds. This BIOS functions is only implemented in * IBM AT compatible BIOSes (i.e., not in old PC and XT systems)! * The timing resolution of this function isn't really down * to 1 us on most systems, but it's fine for our purposes. */ void wait_us(unsigned long microseconds) { union REGS regs; if (microseconds == 0) return; regs.h.ah = 0x86; regs.x.cx = (int) (microseconds >> 16); regs.x.dx = (int) (microseconds & 0xffff); int86(0x15, ®s, ®s); return; } /* * Start a short impulse to the centronics port in order to * allow an oscilloscope to trigger. */ void trigger_start(void) { int d; d = peek(0x40, 8); outportb(d, 0x00); return; } /* * Stop a short impulse to the centronics port in order to * allow an oscilloscope to trigger. */ void trigger_stop(void) { int d; d = peek(0x40, 8); outportb(d, 0xff); return; } void activate_com(int com) { int port, parity; switch (com) { case 1: port = COM1; break; case 2: port = COM2; break; case 3: port = COM3; break; case 4: port = COM4; break; default: printf("Port COM%d not available!\n", com); exit(1); } if (AsyncInit(port)) { printf("Can't initialize port COM%d!\n", com); exit(1); } AsyncHand(DTR | RTS); parity = (reset_answer[0] == 0x3f) ? ODD_PARITY : EVEN_PARITY; AsyncSet(9600, BITS_8 | STOP_2 | parity); return; } void deactivate_com(void) { AsyncHand(DTR | RTS); /* reset low */ AsyncStop(); } /* * Send a string of bytes to serial port and wait for each single echo. * (More efficient send methods are too fast for decoder, it seems to * need more than 2 stop bits, so we wait between individual bytes.) */ int send(char *data, int len) { int i; char c; AsyncClear(); for (i = 0; i < len; i++) { c = data[i]; if (reset_answer[0] == 0x3f) inverse(&c, 1); AsyncOut(c); while (AsyncInStat() == 0); trigger_stop(); AsyncIn(); #if 0 wait_us(delus[0]); #else delay(1); #endif } return 0; } /* * Get all bytes available from serial port FIFO and return how many * bytes were available. */ int receive(char *data, int max) { int i = 0; while (AsyncInStat() > 0 && i < max) data[i++] = AsyncIn(); if (reset_answer[0] == 0x3f) inverse(data, i); return i; } /* * Get n bytes from serial port FIFO. Don't wait more than timeout * microseconds and return how many bytes were available. */ int receive_timeout(char *data, int n, long timeout) { int i = 0; while (i < n && timeout > 0) if (AsyncInStat() > 0) data[i++] = AsyncIn(); else { wait_us(15000); timeout -= 15000; } if (reset_answer[0] == 0x3f) inverse(data, i); return i; } /* * In debugging mode, print the contents of all packets. */ void log_packet(unsigned char *header, unsigned char *data, int direction) { int i, j; if (header[1] == 0x60) /* illegal command code 0x60: reset */ printf("RESET: "); else printf("%s %02x: ", (direction == IN) ? "dcdr" : "card", header[1]); for (i = 0; i < header[4]; i += 16) { if (i > 0) printf(" "); for (j = 0; j < 16; j++) if (i + j < header[4]) printf("%02x ", data[i + j]); else printf(" "); printf(" "); for (j = 0; j < 16 && i + j < header[4]; j++) putchar((data[i + j] > 31 && data[i + j] < 127) ? data[i + j] : '.'); putchar('\n'); } return; } /* * Perform a card reset. Try up to 10 times. * * Return value: 0 correct answer to reset received * -1 reset failed */ int reset(void) { int fail_count = 0; int y, p, i, err, tck_expected; int parity; reset_length = 0; AsyncClear(); while (fail_count < 10) { AsyncHand(DTR | RTS); /* reset low */ wait_us(45000.0 / CLK * 1e6); /* wait initial CLK cycles */ AsyncClear(); AsyncHand(DTR); /* reset high */ wait_us(45000.0 / CLK * 1e6); /* wait for answer */ /* Read TS */ if (AsyncInStat() != 0) { reset_answer[0] = AsyncIn(); if (reset_answer[0] != 0x3b) inverse(reset_answer, 1); if (reset_answer[0] == 0x3f || reset_answer[0] == 0x3b) { if (AsyncInStat() < 1) wait_us(1000000L); /* Read T0 */ if (receive(reset_answer + 1, 1) == 1) { tck_expected = 0; err = 0; p = 2; /* number of bytes read so far */ /* Read TA_i, TB_i, TC_i, TD_i */ y = reset_answer[p - 1] >> 4; while (!err && y && p < RESET_ANSWER_MAX - 5 - (reset_answer[1] & 15)) { for (i = 0; !err && i < 4; i++) { if ((y >> i) & 1) { if (AsyncInStat() < 1) wait_us(1000000L); err |= receive(reset_answer + p, 1) != 1; if (i == 3) { tck_expected |= (reset_answer[p] & 15) != 0; y = reset_answer[p] >> 4; } p++; } else if (i == 3) y = 0; } } /* historic characters */ if (!err) { if (AsyncInStat() < (reset_answer[1] & 15)) wait_us(1000000L); err |= receive(reset_answer + p, reset_answer[1] & 15) != (reset_answer[1] & 15); } p += (reset_answer[1] & 15); /* TCK */ if (!err && tck_expected) { if (AsyncInStat() < 1) wait_us(1000000L); err |= receive(reset_answer + p, 1) != 1; p++; y = 0; for (i = 1; i < p; i++) y ^= reset_answer[i]; err |= y != 0; } /* everything was fine */ if (!err) { reset_length = p; parity = (reset_answer[0] == 0x3f) ? ODD_PARITY : EVEN_PARITY; if ((reset_answer[1] & 0x10) != 0 && reset_answer[2] == 0x31) /* we can't double CLK freq., but we can reduce baud rate */ AsyncSet(4800, BITS_8 | STOP_2 | parity); else AsyncSet(9600, BITS_8 | STOP_2 | parity); return 0; } } } } fail_count++; } AsyncHand(DTR | RTS); /* reset low */ return -1; } /* * send a command (T=0 protocol) to the card and send/receice data. * * Return value: 0 command succeded * -1 command failed */ int command(int instruction, unsigned char *data, int length, int direction, int trigger) { char header[5] = {0x53, 0x00, 0x00, 0x00, 0x00}; char pb; /* procedure byte */ char sw2; int i = 0, j; AsyncClear(); length &= 0xff; instruction &= 0xfe; if ((instruction & 0xf0) == 0x60 || (instruction & 0xf0) == 0x90 || (instruction & 1) != 0) return -1; header[1] = instruction; header[4] = length; if (length == 0 && direction == IN) length = 256; send(header, 5); do { do { if (receive_timeout(&pb, 1, 2 * SECOND) != 1) { fprintf(stderr, "procedure byte timeout, %d data bytes so far.\n", i); return -1; } } while (pb == 0x60); if (((pb ^ instruction) & 0xfe) == 0) { if (direction == OUT) { for (; i < length; i++) { if (i == trigger) trigger_start(); send((char *) data + i, 1); if (i == trigger) trigger_stop(); } } else for (; i < length; i++) { if (receive_timeout((char *) data + i, 1, 2 * SECOND) != 1) { fprintf(stderr, "data byte timeout, %d data bytes so far.\n", i); return -1; } } } else if (((pb ^ instruction) & 0xfe) == 0xfe) { if (direction == OUT) send((char *) data + i, 1); else if (receive_timeout((char *) data + i, 1, 2 * SECOND) != 1) { fprintf(stderr, "data byte timeout, %d data bytes so far.\n", i); return -1; } i++; } else { if ((pb & 0xf0) == 0x60 || (pb & 0xf0) == 0x90) { if (receive_timeout(&sw2, 1, 2 * SECOND)) fprintf(stderr, "SW2 timeout.\n"); fprintf(stderr, "SW1 SW2 = %02x %02x, %d data bytes so far.\n", (unsigned char) pb, (unsigned char) sw2, i); } else fprintf(stderr, "illegal procedure byte %02x, " "%d data bytes so far.\n", (unsigned char) pb, i); return -1; } } while (i < length); /* final procedure bytes */ do { if (receive_timeout(&pb, 1, 2 * SECOND) != 1) { fprintf(stderr, "final procedure byte timeout.\n"); return -1; } } while (pb == 0x60); if ((pb & 0xf0) == 0x60 || (pb & 0xf0) == 0x90) if (receive_timeout(&sw2, 1, 2 * SECOND) != 1) { fprintf(stderr, "SW2 timeout.\n"); return -1; } if ((unsigned char) pb != 0x90 || sw2 != 00) { fprintf(stderr, "SW1 SW2 = %02x %02x.\n", (unsigned char) pb, (unsigned char) sw2); return -1; } if (trigger >= length) { trigger_start(); wait_us(2000); trigger_stop(); } return 0; } int query(unsigned char *msg, unsigned char *answ, int trig, int print) { const int maxfailure = 4; int i, failures=0, trouble=0, ok; do { trouble = command(0x74, msg, 32, OUT, trig); if (trouble) fprintf(stderr, "command 74h failed.\n"); else if (msg[0] & 8) { trouble = command(0x78, answ, 8, IN, -1); if (trouble) fprintf(stderr, "command 78h failed.\n"); } if (trouble) { failures++; while (failures <= maxfailure && trouble) { if (reset()) { fprintf(stderr, "card reset failed.\n"); failures++; } else trouble = 0; } if (failures > maxfailure) { fprintf(stderr, "aborting after %d failures.\n", maxfailure); exit(1); } trouble = 1; } } while (trouble); ok = ((answ[0] & 0xfe) != 0); for (i = 1; i < 8; i++) ok |= (answ[i] != 0); if ((msg[0] & 8) && (print == 2 || (ok && print))) { for (i = 0; i < 8; i++) printf("%02x ", answ[i]); printf("\n"); } return ok; } /* create a (de-)activation command for a 09 card */ void make_command(unsigned char *msg, unsigned char cmd, unsigned char month, const unsigned char *serial) { int i; unsigned char answ[8]; /* just a dummy */ unsigned char xor_byte[4]; msg[0] = 0xe0; msg[1] = month; msg[2] = 0x0a; msg[3] = cmd; msg[4] = 42; /* arbitrary value */ msg[5] = 0x42; /* arbitrary value */ msg[6] = 0x0c; /* channel id */ memcpy(msg + 7, serial, 5); msg[7] = (msg[7] & 0x0f) | 0x80; for (i = 1; i < 16; i++) msg[i + 11] = msg[11]; make_xor(msg[1], msg[2], xor_byte); msg[3] ^= xor_byte[0]; msg[7] ^= xor_byte[0]; msg[8] ^= xor_byte[1]; msg[9] ^= xor_byte[2]; msg[10] ^= xor_byte[3]; msg[27] = msg[28] = msg[29] = msg[30] = msg[31] = 0; decode(msg, answ); return; } main(int argc, char **argv) { int com = 2; int i, j, k, c; unsigned char buf[1024], serial[6]; unsigned char answ[8]; int result; time_t now; struct tm *today; int month_code, month_offset = 0; int simulate = 0; unsigned long num; #define COMMANDS 7 struct { unsigned char cmd; char *text; } sequence[COMMANDS] = { 0x2c, "Activating Multichannels", 0x21, "Activating Sky Movies", 0x22, "Activating Movie Channel", 0x23, "Activating Sky Movies Gold", 0x26, "Activating Sky Sports", 0x28, "Activating TV Asia", 0x20, "Enabling card" }; fprintf(stderr, "\nPhoenix -- Version " VERSION " -- Videocrypt smart card reanimator\n\n"); fprintf(stderr, "Copyright 1994 by Markus Kuhn -- commercial use " "strictly forbidden\n\n"); for (i = 1; i < argc; i++) { if (isdigit(argv[i][0])) com = atoi(argv[i]); else for (j = 0; j < 999 && argv[i][j]; j++) switch(argv[i][j]) { case 'w': case 'W': /* * modify delay table, e.g. option wa200 waits 200 us after each * byte because this sets delus['a' - 'a'] = 200. */ k = tolower(argv[i][j+1]) - 'a'; if (k < 0 || k >= DELAYS || !isdigit(argv[i][j + 2])) { printf("Only wait options between wa and " "w%c possible.\n", 'a' + DELAYS - 1); exit(1); } j += 2; delus[k] = atol(argv[i] + j); while (isdigit(argv[i][j + 1])) j++; break; case 'm': case 'M': /* * modify month code */ month_offset = atoi(argv[i] + j + 1); j += 1000; break; case 's': case 'S': /* * batch mode: enter serial number and print 74h instructions */ simulate = 1; num = atol(argv[i] + j + 1) / 10L; j += 1000; serial[0] = 0x29; serial[1] = (num >> 24) & 0xff; serial[2] = (num >> 16) & 0xff; serial[3] = (num >> 8) & 0xff; serial[4] = num & 0xff; serial[5] = 0; break; case 'h': case 'H': /* * batch mode: enter serial number (instruction 70h) * as hexadezimal number */ simulate = 1; for (k = 0; k < 6; k++) { if (sscanf(argv[i] + j + 1 + k*2, "%.2x", &c) != 1) break; serial[k] = c; } j += 1000; break; default: break; } } if (!simulate) { fprintf(stderr, "Using serial port COM%d, byte delay %lu \xe6s.\n", com, delus[0]); activate_com(com); if (atexit(deactivate_com)) fprintf(stderr, "Can't call atexit()!\n"); } time(&now); today = gmtime(&now); if (!today) { fprintf(stderr, "Universal time not available!\n"); exit(1); } month_code = today->tm_year - 89; if (month_code < 0) month_code += 2000 - 1989; month_code = 12 * month_code + today->tm_mon; month_code += month_offset; if (month_code < 0 || month_code > 255) { fprintf(stderr, "Incorrect date!\n"); exit(1); } printf("Month code: 0x%02x (%04d-%02d)\n\n", month_code, month_code / 12 + 1989, (month_code % 12) + 1); if (!simulate) { if (reset()) { fprintf(stderr, "Card reset failed.\n"); exit(1); } printf("Card reset sucessful.\n\n"); if (command(0x70, serial, 6, IN, -1)) { fprintf(stderr, "Command 70h failed.\n"); exit(1); } } printf("card issue: %02d, card serial number: %08lu* (", serial[0] & 0x0f, ((unsigned long) serial[1] << 24) | ((unsigned long) serial[2] << 16) | ((unsigned long) serial[3] << 8) | (unsigned long) serial[4]); for (i = 0; i < 4; i++) printf("%02x ", serial[i]); printf("%02x)\n\n", serial[4]); /* send commands */ for (i = 0; i < COMMANDS; i++) { printf("%s ...\n", sequence[i].text); make_command(buf, sequence[i].cmd, month_code, serial); if (simulate) { for (j = 0; j < 32; j++) printf(((j & 15) == 15) ? "%02x\n" : "%02x ", buf[j]); } else { if (command(0x74, buf, 32, OUT, -1)) { fprintf(stderr, "Command 74h failed.\n"); exit(1); } } } printf("\n"); if (!simulate) printf("Card has been activated. Enjoy!\n"); return 0; }