Блог → Защита программых средств: ставим метки на HDD

Продолжая серия статей о методах защиты программных продуктов, начатую в начале года, сегодня я расскажу ещё об одном способе защиты, а именно - о том, как выставлять некопируемые метки на жёстких дисках (HDD). Если вы ещё не успели ознакомиться с предыдущими заметками, вы можете сделать это прямо сейчас, либо вернуться к ним потом, при наличии интереса к освещаемому вопросу. Я же перехожу к описанию методов получения некопируемых меток на жестких магнитных дисках (ЖМД, или же более привычная нам аббривеатура HDD).

Особенностями жесткого магнитного диска являются:
- недоступность рабочих поверхностей HDD, в силу чего исключено нанесение физической метки;
- большой объем информационного поля - поэтому затруднено копирование "диск в диск".

Следовательно, основным методом создания некопируемой метки на HDD является форматирование дорожек за пределами рабочего поля. При этом должны быть выполнены следующие требования:
- сохранность метки при операциях реорганизации диска, например, с использованием средства SpeedDisk;
- невидимость метки или ее неповторимость на другом диске;
- сохранность метки при уничтожении защищенной от копирования информации (программ) на HDD в целях создания архивных копий.

В связи с этим, не рекомендуется размещать метку в загрузочные сектора, поскольку при этом она будет обнаружена антивирусными средствами, проверяющими эти зоны на наличие вирусов. Также не рекомендуется "привязывать" программы и данные к их местоположению на винчестере, поскольку реорганизация диска либо будет неэффективна (если информация помечена как неперемещаемая), либо защищенные программы будут восприниматься как нелегальные копии, так как их местоположение на диске изменится. Возможны следующие подходы к простановке метки:
1. Анализ существующих особенностей винчестера:
- привязка к местоположению BAD-кластеров;
- измерение временных параметров электромеханической части HDD.
2. Создание собственных отличительных меток:
- пометка ложных BAD-кластеров;
- использование операций длинного чтения/записи;
- нестандартное форматирование неиспользуемой дорожки HDD.

Для привязки к местоположению BAD-кластеров на винчестере необходимо определить тип FAT таблицы, и найти в ней элементы, соответствующие BAD-кластерам, после чего запомнить их положение, а в дальнейшем проверять их наличие на фиксированных местах. Код, приведённый ниже, проиллюстрирует то, о чём я рассказываю (обратите внимание, что в 12-и битовой FAT, некоторые элементы попадают на границу между секторами, поэтому не все сектора начинаются с нового элемента, перед ним могут располагаться 1 или 2 байта от предыдущей пары элементов).

#include <dos.h>
#include <io.h>

// буферы для чтения секторов
unsigned char buffer[512],buf[512];
int tg,tgl,SecNum,ByteNum;

// номер первого сектора FAT и число секторов в FAT
int i_FatlSec,i_FatSecs;
int i_LoopOffs,i_Ind;
int sw_fat; /* флаг типа FAT */
int cur_disk;

// Определение текущего диска
cur_dsk=getdisk();

// Определение типа FAT
absread(cur_dsk,1,0,buf);
i_FatSecs = *((int *)(buf+0x16));
if(i_FatSecs * 341 / 4096)
sw_fat = l; /* 16-битовая FAT */
else
sw_fat=0; /* 12-битовая FAT */

// Определение номера начального сектора FAT
i_FatlSec = *((int *)(buf+0x0e));

// Цикл поиска BAD-кпастера
tg=0 ;
for(a=0;a
// Чтение очередного сектора FAT
absread(cur_dsk, 1, i_FatlSec + a, buffer);
if(sw_fat == 1)
for(j =0; j < 256; j += 1){

if(buffer[j*2] == 0xf7 && buffer[j*2 + 1] == Oxff) {

SecNum = a;
ByteNum = j*2;
tg=l; /* BAD-кластер найден */
break;

}
}
else {

i_LoopOffs = a % 3;
for(j=0;j<170;j++) {

i_Ind = j*3 + i_LoopOffs;
if(buffer[i_Ind] == 0xf7 && (buffer[i_Ind+l] & OxOf) == OxOf) {

SecNum = a;
ByteNum = i_Ind;
tg=l; /* Обнаружен BAD-кластер */
break;

}

if((buffer[i_Ind+l] & OxfO) == 0x70 && (buffer[i_Ind+2] == Oxff) {

SecNum = a;
ByteNum = i_Ind + 1;
tg=l; /* Обнаружен BAD-кластер */

break;
}
}
}

if(tg == 1) /* Обнаружен BAD-кластер, выход из цикла */
break;
}


Необходимо заметить, что простановка ложных BAD-кластеров может быть замечена пользователем защиты, особенно, если до начала эксплуатации защищенной программы у него их не было. Поэтому, проставлять ложный BAD-кластер необходимо либо в первое, от начала FAT, свободное место (вероятность, что при переносе на другой компьютер этот кластер будет занят, велика, а редактировать цепочки FAT рискнет не всякий), либо в самый конец FAT, так чтобы его не показывали средства типа SpeedDisk.

Следующим способом простановки некопируемых меток является применение операций длинного чтения и длинной записи. Эти операции выполняются только на жёстком диске, и работают с расширенной до 516 байтов (512 + 4) длиной сектора. Следовательно, если произвести операцию длинной записи, а затем операцию обычного чтения, то дополнительные 4 байта информации не будут обнаружены. Считать записанную информацию полностью можно только операцией длинного чтения. Кроме того, обычная запись сектора не разрушает дополнительной информации в 4 байтах, поэтому метка может быть скрыта под обычной информацией.

Однако, использовать один или несколько секторов для хранения метки недостаточно, поскольку информация в дополнительных байтах может быть легко обнаружена путем поиска по всему винчестеру. Поэтому, этот метод рекомендуется либо в сочетании с другими, либо должны использоваться метки в большом числе секторов и операции длинного чтения должны быть замаскированы от трассировки по дисковым прерываниям (я ещё расскажу об этом подробнее). Ниже привожу пример кода для длинной записи.

; Компиляция в .СОМ-файл
.MODEL TINY
.CODE
org lOOh
start:

push cs ;DS = CS
pop ds

;ah = 2 - функция обычного чтения
mov ah,2

; al - сколько секторов считать
mov al,l

; смещение буфера с информацией
mov bx,offset bf

; ch — дорожка, cl — сектор
mov cx,0003h

; сегмент буфера
mov dx,ds
mov es,dx

; dh - головка, dl - идентификатор дисковода для жесткого диска
; dl = 80h, если их два, то 80h и 81 h соответственно
mov dx,0080h
int 13h

; ah = OBh - функция длинной записи
mov ah,OBh
mov al,l

; смещение буфера с информацией
mov bx,offset bf

; ch - дорожка, cl - сектор
mov cx,0003h

; сегмент буфера
mov dx,ds
mov es,dx

; dh - головка, dl - идентификатор дисковода
mov dx,0080h
int 13h

; обнулим дополнение в 4 байта
mov word ptr dop,0
mov word ptr dop+2,0

; ah = OAh - функция длинного чтения
; контрольное чтение
mov ah,OAh
mov al,1

; смещение буфера с информацией
mov bx,offset bf

; ch - дорожка, cl - сектор
mov cx,0003h

; сегмент буфера
mov dx,ds
mov es,dx

; dh - головка, dl - идентификатор дисковода
mov dx,0080h
int 13h

; вывод на экран дополнительных байт
mov ah,09h
mov dx,offset dop
int 21h
mov ax,4C00h
int 21h

; буфер - 512 + 4 + символ окончания печати
bf db 512 dup (0)
dop db "ABCD"
db "$"
end start