Техника защиты компакт-дисков от копирования

Доступ посредствомчерез Cooked-Modeмоде (режим блочного чтения)


Операционная система WindowsNT выгодно отличается тем, что поддерживает режим блочного чтения с устройства, –— так называемый, cooked?modeCooked-Mode в котором все содержимое диска трактуется как один большой файл. По этому "файлу" можно перемещаться вызовом функции SetFilePointer и читать/писать отдельные сектора посредством вызовов функций ReadFile/WriteFile соответственно. Текущая позиция указателя задается в байтах (не секторах!), однако значение указателя обязано должно быть кратным логической длине сектора (512 байт для гибких/жестких дисков и 2048 байт для CD-ROM), в противном случае произойдет ошибка. Количество байт, читаемых (записываемых) за один раз, также должно укладываться в целое число секторов. Попытка прочитать сектор по "кусочкам" ни к чему не приведет.

Несмотря на всю изящность и простоту программной реализации, данному способу взаимодействия с приводом присущи серьезные недостатки. Во-первых, он не работает с файловыми системами отличными от ISO 9660/Juliet и High Sierra File System. В переводе на нормальный человеческий язык это обозначает, что для чтения секторов с аудиодисков режим блочного чтения непригоден и подходит лишь для обработки дисков с данными. Во-вторых, чтение "сырых" секторов в cooked-modeCooked-Mode  невозможно, и нам придется довольствоваться лишь той их частью, что содержит пользовательские данные (User-Data). Такое положение дел значительно ослабляет стойкость защитного механизма и позволяет легко ввести его в заблуждение. Допустим, защита, основанная на привязке к физическим дефектам поверхности носителя, пытается прочесть ключевой сектор на предмет проверки его читабельности. Поскольку содержимое кодов коррекции защитному механизму недоступно, он не может отличить действительные физические дефекты от их грубой имитации (то есть умышленного искажения кодов ECC/EDC кодов копировщиком с целью эмуляции неустранимых ошибок чтения).

Проверить, использует ли защита данный способ доступа к диску или нет можно следующим образом: просто установите точку останова на функцию CreateFile, заставив отладчик "всплывать" в том и только в том случае, если первые четыре символа имени открываемого файла равны "\\.\" (то есть функция открывает не файл, а устройство).
Например, это может выглядеть так: "bpx CreateFileA if (*esp->4=='\\\\.\\')", затем нам останется лишь убедиться в том, что за последней косой чертой следует буква именного того привода, который нам нужен (на компьютере автора это привод "\\.\G:"). Дождавшись выхода из функции CreateFile по команде "P RET" и подсмотрев возращенный ей дескриптор устройства (который будет содержаться в регистре EAX), мы сможем перехватить все вызовы функций SetFilePointer/ReadFile, анализ окрестностей которых и "разоблачит" алгоритм работы защитного механизма.

Демонстрационный пример, приведенный в листинге 1.4.5ниже, представляет собой вполне законченную утилиту для "грабежа" дисков с данными на секторном уровне с последующей записью всего "награбленного" в файл.

Листинг 2.1.4.5. [/cooked.sector.read.c] Пример, демонстрирующий технику чтения секторов в cooked-modeCooked-Mode

/*----------------------------------------------------------------------------

 *

 *                  ЧИТАЕТ СЕКТОРА С CD-ROM В БЛОЧНОМ РЕЖИМЕ

 *                  ========================================

 *

 *      данная программа работает только под Windows NT, не требуя для себя



 *      прав администратора

 *

 * Build 0x001 @ 19.05.03

---------------------------------------------------------------------------- */

#include <windows.h>

#include <winioctl.h>

#include <stdio.h>

// ПАРАМЕТРЫ ПО УМОЛЧАНИЮ

#define DEF_FN                          "sector"

#define DEF_TO                          0x666

#define DEF_FROM                        0x000

#define CDROM_SECTOR_SIZE      2048            // for MODE1/MODE2FORM1 only!

// АРГУМЕНТЫ КОМАНДНОЙ СТРОКИ

#define argCD       (argv[1])

#define argFN       ((argc > 2)?argv[2]      :DEF_FN)

#define argFROM     ((argc > 3)?atol(argv[3]):DEF_FROM)

#define argTO       ((argc>4)?(atol(argv[4])>argFROM)?atol(argv[4]):argFROM:DEF_TO)



main(int argc, char **argv)

{

int              a;

FILE             *f;

HANDLE           hCD;

char             *buf;

DWORD            x_read;

char             buf_n[1024];

     

// ПРОВЕРЯЕМ АРГУМЕНТЫ

if (argc<2)

{

     printf("USAGE: cooked.sector.read PhysCD [filename] [from] [to]\n");

     printf("\tPhysCD   - physical name of CD (\"\\\\.\\G:\")\n");

     printf("\tfilename - file name to store follow sector\n");

     printf("\tfrom     - start sector\n");

     printf("\tto       - end sector\n");

     return 0;

}

 

// TITLE

fprintf(stderr,"cooked sector reader for NT\n");

 

// ВЫДЕЛЯЕМ ПАМЯТЬ

buf=malloc(CDROM_SECTOR_SIZE);if (!buf){printf("-ERR:low memory\n");return -1;}

 

// ОТКРЫВАЕМ УСТРОЙСТВО

hCD=CreateFile(argCD, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);

if (hCD == INVALID_HANDLE_VALUE) {

     printf("-ERR: error CreateFile(%s,....)\n", argCD); return -1;

}

 

// INFO

printf("read sector from %04d to %04d in %s file\n", argFROM, argTO, argFN);

 

// ПОЗИЦИОНИРУЕМ УКАЗАТЕЛЬ НА ПЕРВЫЙ ЧИТАЕМЫЙ СЕКТОР

SetFilePointer (hCD, CDROM_SECTOR_SIZE * argFROM, NULL, FILE_BEGIN);

 

// ЧИТАЕМ СЕКТОРА ОДИН ЗА ДРУГИМ

for (a = argFROM; a <= argTO; a++)

{

     // читаем очередной сектор

     if (ReadFile(hCD, buf, CDROM_SECTOR_SIZE, &x_read, NULL) && x_read)

     {

                // записываем только что считанный сектор в файл

                sprintf(buf_n,"%s[%04d].dat",argFN, a);

                if (f=fopen(buf_n,"wb")){fwrite(buf, 1, x_read, f); fclose(f);}

                printf("sector [%04d.%04d] read\r",a, argTO);

     }

                else

     {

                printf("sector %04d read error\n",a);

     }

}

}


Содержание раздела