Important!

Blog moved to https://blog.apdu.fr/

I moved my blog from https://ludovicrousseau.blogspot.com/ to https://blog.apdu.fr/ . Why? I wanted to move away from Blogger (owne...

Monday, February 22, 2021

Reading a SIM card phone book in Python

I already wrote a SIM phone book dumper program in 2004. This first version was in Perl and was presented in "SIM card phone book listing".

You can also find more advanced tools in articles with the SIM label, like cardpeek, monosim or PSSI.

I now present a version in Python using the PySCard wrapper

 

Source code

The source code is in 2 parts: usim.py and usim_read.py files.

usim.py is:

#!/usr/bin/env python3

from smartcard.System import readers
from smartcard.util import toBytes
from smartcard.CardConnectionObserver import ConsoleCardConnectionObserver

debug = True

def usim(reader_nb):
    # get all the available readers
    r = readers()
    print("Available readers:")
    for reader in r:
        print("-", reader)

    reader = r[reader_nb]
    print("Using:", reader)

    connection = reader.createConnection()
    if debug:
        observer = ConsoleCardConnectionObserver()
        connection.addObserver(observer)
    connection.connect()

    SELECT = "A0 A4 00 00 02 "
    GET_RESPONSE = "A0 C0 00 00 "

    # Select MF
    print("Select MF")
    data, sw1, sw2 = connection.transmit(toBytes(SELECT + "3F 00"))
    if sw1 != 0x9F:
        raise(Exception("Error"))

    # Select DF Telecom
    print("Select DF Telecom")
    data, sw1, sw2 = connection.transmit(toBytes(SELECT + "7F 10"))
    if sw1 != 0x9F:
        raise(Exception("Error"))

    # Select EF ADN
    print("Select EF ADN")
    data, sw1, sw2 = connection.transmit(toBytes(SELECT + "6F 3A"))
    if (sw1, sw2) != (0x9F, 0x0F):
        raise(Exception("Error"))

    # Get Response
    print("Get Response")
    data, sw1, sw2 = connection.transmit(toBytes(GET_RESPONSE) + [sw2])
    if (sw1, sw2) != (0x90, 0x00):
        raise(Exception("Error"))

    size = data[-1]

    pin = None
    if pin:
        print(pin)
        pin = list(map(ord, pin))
        padd(pin, 8)

        # Verify CHV
        VERIFY = "A0 20 00 01 08"
        cmd = toBytes(VERIFY) + pin
        data, sw1, sw2 = connection.transmit(cmd)
        if (sw1, sw2) != (0x90, 0x00):
            raise(Exception("Wrong PIN:" + pin))

    return size, connection


if __name__ == "__main__":
    usim(0)

usim_read.py is:

#!/usr/bin/env python3

from smartcard.util import toBytes, toASCIIString
import usim


def decode_record(record):
    """
    decode_record(toBytes("43 75 73 74 6F 6D 65 72 20 43 61 72 65 FF 06 A1 80 00 07 70 00 FF FF FF FF FF FF FF"))
    >> ['Customer Care', '0800700700']
    """
    X = len(record) - 14
    name = toASCIIString(record[0:X - 1]).replace("ΓΏ", "")
    # number of bytes for the phone number
    tel_size = record[X]
    phone = record[X + 2:X + tel_size + 1]

    decoded = ""
    for n in phone:
        hex = "%02X" % n
        high = hex[0]
        low = hex[1]
        decoded += low + high
    # if the number of digits is odd we suppress the padding
    if decoded[-1] == "F":
        decoded = decoded[:-1]
    phone = decoded

    return name, phone


def usim_read(reader_nb):
    # Select the EF ADN
    (size, connection) = usim.usim(reader_nb)

    for nbr in range(1, 250):
        #  Read record
        header = [0xA0, 0xB2]
        record_idx = nbr
        cmd = header + [record_idx, 0x04, size]
        data, sw1, sw2 = connection.transmit(cmd)
        if (sw1, sw2) != (0x90, 0x00):
            return

        name, phone = decode_record(data)
        if name != "":
            print(f"{record_idx}: Name: {name}, phone: {phone}")


if __name__ == "__main__":
    import sys
    if 2 == len(sys.argv):
        reader_nb = int(sys.argv[1])
    else:
        reader_nb = 0
    usim_read(reader_nb)


Comments

The debug is enabled. So you can see the communication between the application and the card.
You ned to change only one line to remove the APDU log if needed. 

The phone book record size is not fixed for all the SIM cards. The record size is returned in the last byte of the GET RESPONSE command after the SELECT EF (Elementary File) ADN (Abbreviated dialling numbers).

You can get more details about the EF ADN and the record coding in the document ETSI TS 131 102 V16.6.0 (2021-01) chapter "4.4.2.3 EF_ADN (Abbreviated dialling numbers)". It is important to note that ETSI (European Telecommunications Standards Institute) standards are public and free.

By default the first PC/SC reader is used. But you can select another reader by passing a number as argument to the program. We will use this feature later.

No PIN is defined and verified. I am using a sysmocom sysmoUSIM-SJS1 card. This card has the user PIN disabled by default.
You can enable PIN verification by defining a PIN in usim.py. The code is already present.

USIM (Universal Subscriber Identity Module) is a new application introduced for GSM version 3 (3G) to replace/improve the SIM (subscriber identity/identification) application. The code should work the same with a SIM card or a USIM card (but untested).

Output

$ ./usim_read.py 
Available readers:
- Gemalto PC Twin Reader
Using: Gemalto PC Twin Reader
connecting to Gemalto PC Twin Reader
Select MF
> A0 A4 00 00 02 3F 00
<  [] 9F 22
Select DF Telecom
> A0 A4 00 00 02 7F 10
<  [] 9F 22
Select EF ADN
> A0 A4 00 00 02 6F 3A
<  [] 9F 0F
Get Response
> A0 C0 00 00 0F
< 00 00 21 34 6F 3A 04 00 11 FF 22 01 02 01 22 90 00
> A0 B2 01 04 22
< 4C 61 75 72 65 20 46 72 61 6E E7 6F 69 73 65 20 59 76 6F 6E 06 A1 24 66 85 10 22 FF FF FF FF FF FF FF 90 00
1: Name: Laure Francoise Yvo, phone: 4266580122
> A0 B2 02 04 22
< 4C 75 63 69 65 6E 6E 65 20 48 65 6C 65 6E 65 20 4C 75 63 69 06 A1 76 45 65 28 77 FF FF FF FF FF FF FF 90 00
2: Name: Lucienne Helene Luc, phone: 6754568277
> A0 B2 03 04 22
< 55 72 62 61 69 6E 20 47 69 6C 6C 65 73 20 4A 75 73 74 65 FF 06 A1 80 38 74 16 54 FF FF FF FF FF FF FF 90 00
3: Name: Urbain Gilles Juste, phone: 0883476145
> A0 B2 04 04 22
< 4A 65 72 6F 6D 65 20 50 61 73 63 61 6C 20 46 65 72 6E 61 6E 06 A1 80 53 42 10 86 FF FF FF FF FF FF FF 90 00
4: Name: Jerome Pascal Ferna, phone: 0835240168
[...]

The output is truncated. I do not want to include all the 255 phone numbers. 

Note that the names and numbers are random. More on that later.


Conclusion

It is easy to dump the phone book from a SIM card.

The SIM phone book is very limited (no birthday, no email address). The real phone book is, in general, in the phone itself and synchronised using CardDav with a server like Nextcloud.