пятница, 22 августа 2014 г.

Программирование и реверс смарт-карт

Сегодня мы обсудим смарт-карты. Это белая пластинка, выглядящая вот так: 
Внутри находиться память: ROM, EEPROM, RAM. Процессор частотой до 5 МГц, криптографический сопроцессор. Разрядность процессора 8, 16, 32.
При подключение к компьютеру, карта обменивается с ним APDU сообщениями. Хост-система(компьютер) посылает карте APDU запрос, карта высылает назад APDU ответ.

APDU запрос выглядит так:
Обязательные:
 CLA - класс инструкций
 INS - код инструкции
 P1, P2 - параметры
Опциональные:
 Lc - длинна данных
 Data - данные
 Le - длинна ожидаемого ответа

APDU ответ:
Обязательные:
SW1, SW2 - слово состояния, говорит нам о результате выполнения команды.
например: 90 00 - команда выполнена успешно, всё ок.
Опциональные:
Data - данные

Вот так выглядит железка для подключения карты:
Есть два вида таких железок, в первой, той что на картинке, нужно соединять карту с устройством желтыми контактами на поверхности карты.
Во второй железке, контакт не обязателен, нужно просто поднести карту.

Программирование смарт-карт

Смарт-карта программируется на Java Card. Это обрезанный аналог Java, вырезано всё "лишнее" для работы на карте.
Поддерживается:
  • короткие примитивные типы данных: boolean, byte, short
  • одномерные массивы
  • пакеты, классы, интерфейсы и исключительные ситуации
  • объектно-ориентированные свойства Java: наследование, виртуальные функции, перезагрузка методов, динамическое создание объектов, области видимости.
Не поддерживаются:
  • длинные примитивные типы данных: long, double, float
  • символы и строки
  • многомерные массивы
  • динамическая загрузка классов
  • сборка мусора
  • многопоточность
  • сериализация и клонирование объектов
Всё, что можно, оптимизируется и рассчитывается на компьютере, после полученный байт-код загружается на карту. Программа для смарт-карт имеет расширение *.cap.

Для получения cap-файла нужно специальным конвертером преобразовать class-файл в cap. 
Для получения cap-файла мы будем использовать плагин для Eclipse. Необходимый софт можно скачать здесь.
Сначала нужно установить Eclipse, для этого распакуйте eclipse-java-kepler-SR2-win32.zip в любую удобную для вас папку. Теперь нужно установить плагин для конвертирования в CAP. Для этого распакуйте eclipse-jcde-0.2.zip/plugins в "путь до Eclipse"/plugins. Теперь в Eclipse у нас появились следующие кнопки:

JCWDE - запускает эмулятор который слушает APDU сообщения на некотором порту.
CREF - запуск эмулятора, исполняющего скрипты
Java Card - конвертирование и работа со скриптами для эмулятора.

Сначала нужно подключить jcdk(его можно найти в архиве). Для этого перейдите в окно Preferences(Java card->Preference) и укажите путь до папки с jcdk в Java card Home.
Теперь можно создавать проект File->New->Other.
Затем выберет Java Card Project.
Введите имя проекта и кликнете Finish. Теперь у вас создан проект для Java Card. Создайте новый класс. Рассмотрим общую структуру программы
package task3;

//Подключаем все необходимые библиотеки для программирования смарт-карт
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.SystemException;
import javacard.framework.Util;
import javacardx.framework.util.ArrayLogic;

import javacardx.external.Memory;
import javacardx.external.MemoryAccess;

//Создаём наш класс, он должен быть унаследован от Applet
public class task3 extends Applet {

private static final byte INS_GETDATA = (byte)0xCA; //Код инструкции
private static final byte CLA_START = (byte)0xB0; //Класс инструкций
//Значения первого параметра
private static final byte P1_NAME = (byte)0x01;
private static final byte P1_DEPT = (byte)0x02;
private static final byte P1_PASS = (byte)0x03;
//Данные которые хранятся на карте
private byte[] name;
private byte[] dept;
private byte[] pass;
//Заполняем наши данные, строки не поддерживаются Java Card, поэтому мы используем символьные массивы
private task3() {
name = new byte[]{0x49, 0x76, 0x61, 0x6e, 0x6f, 0x76, 0x35};
dept = new byte[]{0x4f, 0x52, 0x27, 0x31, 0x27, 0x3d, 0x27, 0x31, 0x27};
pass = new byte[]{0x4f, 0x52, 0x27, 0x31, 0x27, 0x3d, 0x27, 0x31, 0x27};
}

//Метод инициализации карты
public static void install(byte bArray[], short bOffset, byte bLength)
throws ISOException {
new task3().register(); //Обязательно нужно вызвать для регистрации 
}

//Метод вызывается каждый раз для обработки APDU запроса
public void process(APDU apdu) throws ISOException {
byte[] apduBuffer = apdu.getBuffer(); //Получаем наш APDU запрос
byte apduCla = apduBuffer[ISO7816.OFFSET_CLA]; //Смотрим класс команд
byte apduIns = apduBuffer[ISO7816.OFFSET_INS]; //Код инструкции
byte apduP1 = apduBuffer[ISO7816.OFFSET_P1]; //Значение первого параметра
if(apduCla == CLA_START){
switch (apduIns) {
case INS_GETDATA : //Если класс и инструкция, те которые нам нужны
byte[] data = getData(apduP1); //Обрабатываем наш запрос
//Отсылаем на хост-систему запрашиваемые данные
Util.arrayCopy(data, (short) 0 , apduBuffer, (short) 0, (short)data.length);
apdu.setOutgoingAndSend((short) 0 , (short) data.length);
break;

default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
break;
}
}
}
//В зависимости от P1 возвращает запрашиваемые данные
private byte[] getData(byte p1){
byte[] mass = null;
switch (p1) {
case P1_NAME:
mass = name;
break;
case P1_DEPT:
mass = dept;
break;
case P1_PASS:
mass = pass;
break;
default:
ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2);
break;
}
return mass;
}

}
Итак:
  • Класс должен быть унаследован от Applet
  • Иметь методы install и process
  • В install должен быть создан register.
Теперь мы хотим скомпилировать наш проект, для этого выберете нужный пакет в дереве слева.
Теперь станут активными кнопки в выпадающем меню Java Card.
Нужно указать AID пакета - уникальный идентификатор. 
Введите туда ваш AID пакета, например: 0x49:0x6e:0x6a:0x65:0x63:0x74:0x69:0x6f
Теперь можно перейти к конвертированию, после клика по Convert будет предложено ввести Applet AID, можно ввести, например: 0x49:0x6e:0x6a:0x65:0x63:0x74:0x69:0x6f:0x6e

После конвертирования в папке с проектом в каталоге bin появиться cap-файл, который нужно загрузить на карту. Предположим, что у нас под рукой не оказалось карты, давайте рассмотрим как работать с CREF. Для работы эмулятора нужно запускать скрипты, которые будут отправлять APDU на карту. Для создания скрипта в выпадающем меню Java Card выберете Generate Script. Программа создаст несколько скриптов:
В cap- download.script можно добавлять свои APDU команды в конец файла, перед powerdown;
Эти команды будут отправлены на эмулируемую смарт-карту при запуске программы. Чтобы запустить наш скрипт нужно включить эмулятор. Для запуска эмулятора  CREF->Start. Эмулятор нужно запускать заново после каждого использования. Теперь мы можем запустить выполнение скрипта, выберете нужный скрипт в дереве и запустите выполнение скрипта Java Card->Ran Script. В консоле внизу можно увидеть ответы карты на наши запросы(обратите внимание, там может быть две консоли, одна с ответами APDU, а вторая с информацией).
Теперь загрузим нашу программу на карту. Для загрузки на карту используется утилита CardManager.jar(см. в архиве). 
Программа становиться активна только при подключённом картридере. После подключения карты и подсоединения карты, нажмите на кнопку с большой 1. После OpenFile, выберете CAP-файл с нашей программой. Для загрузки на карту Install. После окончания загрузки вытащите и снова подключите карту, должна выполниться загруженная программа. Для отсылки APDU на карту перейдите на вкладку Send  APDU. Здесь можно заполнить коды инструкции и данные, после отправить их на карту. 

Реверс смарт-карт

Предположим к нам в руки попала карта и cap-файл установленный на ней. При подключении, карта на любой APDU отвечает кодом 90 00 и никаких данных не возвращает. Встаёт вопрос, что нужно ввести чтобы получить осмысленные данные. Если cap-файл написан на java значит наверняка есть возможность получить его исходный код. Здесь есть два пути решения проблемы:
Наверняка есть ещё, но мы их рассматривать не будем. Начнём с рассмотрения Decap tool, так как он выдаёт байт-код, а байт-код это здорово. Запускаем программу JCSI_1.2.jar.
Теперь загружаем исследуемую программу(большая кнопка Open).
После загрузки запускаем decap, кнопка справа. В открывшемся окне слева будет дерево, отображающие структуру cap-файла. Для того чтобы просмотреть байт-код приложения нужно найти в этом дереве методы:
Теперь для отображения кода метода нужно выбрать интересующий метод и в выпавшей ветки кликнуть по нижней длинной строке
Справа отобразиться байт-код метода
Теперь можно анализировать этот байт-код, но это сложно и долго. Поэтому переходим ко второму способу. С помощью утилиты Normalizer мы можем преобразовать cap-файл в class. Normalizer можно найти в JCDK3.0.4_ClassicEdition/bin. Запускаем normalizer.bat через cmd.
Для получения справки: normalizer.bat help normalize. Для конвертации в cap нужно выполнить
normalizer.bat normalize -i путь_до_сap -p ../api_export_files -o  путь_куда_положить_class. После у нас получить обычный class файл, который можно декомпилировать. Для декомпиляции можно воспользоваться утилитой, лежащей в архиве в папке revers.
 Нужно акцентировать внимание на том, что декомпилятор немного врёт. Карта не поддерживает тип int, поэтому все переменные типа int нужно поменять на short. Ну вот наверно и всё:)
Вернёмся к нашей задаче. CAP файл для неё можно найти в архиве под именем TASK.cap. Там нужно просто проанализировать код, он обрабатывает APDU запрос кучей проверок и если они все прошли, то начинает выводить осмысленный текст.
 Осмысленный текст выделен синим на картинке. Вот как-то так.