четверг, 6 марта 2014 г.

Шпионские динамические библиотеки DLL

На днях появилась потребность выяснить по какому протоколу общается токен при изготовлении цифровой подписи. Производитель токена не дает документацию на команды APDU. Пришлось осваивать новую для меня технику оталдки библиотек DLL. Суть метода в том, чтобы вставить между программой и самой DLL свою шпионскую библиотеку, которая будет вести журнал по используемым вызовам и писать отладочную информацию в файл.
 
Источником вдохновения послужил открытый проект APDUView, но для моей задачи оказался не применим. Устарел. Поэтому пришлось самоу писать практически с нуля.
Идея в том чтобы создать пустую библиотеку, которая будет передавать прозрачным образом все вызовы на другую библиотеку DLL. Шпионскую библиотеку назовем тем же именем WinSCard.DLL и подложим в папку приложения. Исходную библиотеку назовем словом original.dll.
  1. Создаем пустую библиотеку. Для этого надо экспортировать список ВСЕХ вызовов исходной библиотеки. Я не придумал, как это сделать средствами GNU, воспользовался бесплатной утилитой взятой где-то на просторах интернета. Полученный список вызовов вбил в файл original.def.
    ; Пример заполнения файла original.def
    LIBRARY winscard
    EXPORTS
    ClassInstall32
    SCardAccessNewReaderEvent
    SCardReleaseAllEvents
    SCardReleaseNewReaderEvent
    SCardAccessStartedEvent
    . . .
  2. Компилируем пустую библиотеку по определениям original.def. Я использую консоль MinGW/MSYS.
    # dlltool original.def --output-lib liboriginal.a
  3. Создаем файл определений для шпионской WinSCard.DLL. Каждая строчка файла определений в файле winscard.def должна содержать экспорт откуда берется функция, из какой иной библиотеки. Исключение составляет та функция, которая подвергается отладке.
    ; Пример заполнения файла winscard.def
    LIBRARY winscard
    EXPORTS
    ClassInstall32=original.ClassInstall32
    SCardAccessNewReaderEvent=original.SCardAccessNewReaderEvent
    ; надо указать все вызовы, пропускаю для краткости изложения
    ; . . .
    SCardStatusW=original.SCardStatusW
    ; команды APDU передаются через вызов SCardTransmit
    SCardTransmit
    SCardWriteCacheA=original.SCardWriteCacheA
    SCardWriteCacheW=original.SCardWriteCacheW
    g_rgSCardRawPci=original.g_rgSCardRawPci DATA
    g_rgSCardT0Pci=original.g_rgSCardT0Pci DATA
    g_rgSCardT1Pci=original.g_rgSCardT1Pci DATA
  4. Надо реализовать функцию SCardTransmit и писать в файл журнала данные, которые приходят от приложения и возвращаются функцией.
    //! \file winscard.c
    BOOL WINAPI DllMain(HINSTANCE hDllInst, DWORD fdwReason) 
    {
        switch (fdwReason) {
        case DLL_PROCESS_ATTACH:
            hOriginal = LoadLibrary("original.dll");
            Original_SCardTransmit =GetProcAddress(hOriginal,"SCardTransmit");
            break;
        case DLL_PROCESS_DETACH:
            FreeLibrary(hOriginal);
            break;
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            break;
        default:
            return FALSE;
        }
        return TRUE;
    }
    
    DLL_EXPORT LONG WINAPI SCardTransmit(. . .) {
        LONG result = (*Original_SCardTransmit)(. . .);
    //! \todo сохраняем праметры и результ в журнал
        return result;
    }
    
  5. Компилируем шпионскую библиотеку
    # gcc -shared -L. -loriginal -o winscard.dll winscard.def winscard.c
  6. Копируем исходную библиотеку WinSCard.DLL в original.dll. Копируем шпионскую библиотеку с тем же именем в папку приложения.
  7. Запускаем приложение и и наблюдаем результат.

Комментариев нет:

Отправить комментарий