:: MVP ::
|
|
:: RSS ::
|
|
|
Иногда иметь такую возможность бывает крайне полезно. Допустим, у нас есть лицензия на некую программу,
защита которой привязана к номеру тома логического раздела диска. И надо же было случиться такой беде –
полетел диск (как правило – системный) к которому эта программа привязана. Мы поставили новый диск,
переустановили Винду, установили свою “любимую” программу и ... облом! Понятно, что о покупке еще одной
лицензии речи и быть не может. Что же делать в такой ситуации? Поможет смена номера логического раздела
(конечно, при условии, что мы предварительно его запомнили). Этот способ примечателен еще и тем, что он
абсолютно легальный, так как не нарушает никаких условий лицензионных соглашений!
Вся необходимая для смены номера информация хранится в его загрузочной области. Для нас принципиальное
значение имеет файловая система, от которой зависит расположения нужных байт относительно начала загрузочной
области. Посмотрим таблицу, в которой представлена зависимость смещений от файловой системы.
Поле/Файловая система |
FAT16 |
FAT32 |
NTFS |
EXFAT |
Метка тома |
0x2B |
0x47 |
? |
? |
Файловая система |
0x36 |
0x52 |
0x3 |
0x3 |
Серийный номер |
0x27 |
0x43 |
0x48 |
0x64 |
Итак, вот план наших действий. Определяем файловую систему, показываем пользователю текущий номер
и даем ему возможность этот номер сменить.
Для реализации задуманного напишем класс, который будет иметь следующий вид:
TVolumeSN = class
private
hD: THandle;
function DiskOpen( Vol: PChar ): Boolean;
procedure DiskClose;
function ReadSector ( var Buffer: TVolumeSNBuf ): Boolean;
function WriteSector( var Buffer: TVolumeSNBuf ): Boolean;
public
function GetVolumeSN( Volume: string ): string;
function SetVolumeSN( Volume: string; NewSN: DWORD ): Boolean; overload;
function SetVolumeSN( Volume: string; NewSN: string ): Boolean; overload;
end;
|
DiskOpen – открыть диск
DiskClose – закрыть диск
ReadSector – прочитать сектор
WriteSector – записать сектор
GetVolumeSN – получить серийный номер
SetVolumeSN – изменить серийный номер
Первые 4 метода просты и не нуждаются в объяснении, остальные имеют подробные комментарии в коде.
function TVolumeSN.DiskOpen(Vol: PChar): Boolean;
begin
hD := CreateFile( Vol, GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
nil, OPEN_EXISTING, 0, 0 );
Result := hD <> INVALID_HANDLE_VALUE;
end;
procedure TVolumeSN.DiskClose;
begin
if hD <> INVALID_HANDLE_VALUE then
CloseHandle( hD );
end;
function TVolumeSN.ReadSector(var Buffer: TVolumeSNBuf): Boolean;
var
Read: DWORD;
begin
Read := 0;
// Перемещаем курсор в начало диска
if SetFilePointer( hD, 0, nil, FILE_BEGIN ) = INVALID_SET_FILE_POINTER then
Exit( False );
if not ReadFile( hD, Buffer, VOLUME_SN_BUF_SIZE, Read, nil ) then
Exit( False );
Result := True;
end;
function TVolumeSN.WriteSector(var Buffer: TVolumeSNBuf): Boolean;
var
Write: DWORD;
begin
// Перемещаем курсор в начало диска
if SetFilePointer( hD, 0, nil, FILE_BEGIN ) = INVALID_SET_FILE_POINTER then
Exit( False );
if not WriteFile( hD, Buffer, VOLUME_SN_BUF_SIZE, Write, nil ) then
Exit( False );
Result := True;
end;
function TVolumeSN.GetVolumeSN(Volume: string): string;
var
Drive: PChar;
Sector: TVolumeSNBuf;
i: Integer;
Serial: PDWORD;
FS: string;
begin
Drive := PChar( '\\.\' + Volume );
// Открываем диск
if not DiskOpen( Drive ) then
raise Exception.Create( 'Невозможно открыть диск.' + #13 +
IntToStr( GetLastError ) + ': ' +
SysErrorMessage( GetLastError ) );
// Читаем загрузочную область
if not ReadSector( Sector ) then
begin
DiskClose;
raise Exception.Create( 'Невозможно считать данные.' + #13 +
IntToStr( GetLastError ) + ': ' +
SysErrorMessage( GetLastError ) );
end;
// Поиск сигнатуры загрузочной области
for i := 1 to MAX_PBSI do
begin
// Определяем нужную сигнатуру
FS := Copy( ConvertToString( Sector ), PartialBootSectorInfo[i].FsOffs, Length( PartialBootSectorInfo[i].Fs ) );
if PartialBootSectorInfo[i].Fs = FS then
begin
Serial := @Sector[PartialBootSectorInfo[i].SerialOffs];
Result := IntToHex( DWORD( Serial^ ), 8 );
Break;
end;
end;
// Закрываем диск
DiskClose;
end;
function TVolumeSN.SetVolumeSN(Volume: string; NewSN: DWORD): Boolean;
var
Drive: PChar;
Sector: TVolumeSNBuf;
i: Integer;
FS: string;
begin
Result := False;
Drive := PChar( '\\.\' + Volume );
// Открываем диск
if not DiskOpen( Drive ) then
raise Exception.Create( 'Невозможно открыть диск.' + #13 +
IntToStr( GetLastError ) + ': ' +
SysErrorMessage( GetLastError ) );
// Читаем загрузочную область
if not ReadSector( Sector ) then
begin
DiskClose;
raise Exception.Create( 'Невозможно считать данные.' + #13 +
IntToStr( GetLastError ) + ': ' +
SysErrorMessage( GetLastError ) );
end;
// Поиск сигнатуры загрузочной области
i := 1;
while i <= MAX_PBSI do
begin
// Определяем нужную сигнатуру
FS := Copy( ConvertToString( Sector ), PartialBootSectorInfo[i].FsOffs, Length( PartialBootSectorInfo[i].Fs ) );
if PartialBootSectorInfo[i].Fs = FS then
Break;
Inc( i );
end;
if i > MAX_PBSI then
begin
DiskClose;
raise Exception.Create( 'Невозможно сменить серийный номер тома для этой файловой системы.' );
end;
// Меняем номер
PDWORD( Sector + PartialBootSectorInfo[i].SerialOffs )^ := NewSN;
// Записываем в загрузочную область
if not WriteSector( Sector ) then
begin
DiskClose;
raise Exception.Create( 'Невозможно записать данные.' + #13 +
IntToStr( GetLastError ) + ': ' +
SysErrorMessage( GetLastError ) );
end;
// Закрываем диск
DiskClose;
Result := True;
end;
function TVolumeSN.SetVolumeSN(Volume, NewSN: string): Boolean;
var
i: Integer;
begin
Result := False;
if Length( NewSN ) = 0 then
raise Exception.Create( 'Не введен серийный номер.' );
if Length( NewSN ) <> 8 then
raise Exception.Create( 'Серийный номер должен состоять из 8 символов.' );
for i := 1 to Length( NewSN ) do
if not CharInSet( NewSN[i], ['0'..'9', 'A'..'F', 'a'..'f'] ) then
raise Exception.Create( 'Некорректный формат серийного номера (XXXXXXXX, X = [0..9,A..F]).' );
SetVolumeSN( Volume, HexToInt( NewSN ) );
end;
|
Как видите, все просто. Полный исходный код, как всегда, находится в конце статьи.
Напоследок стоит заметить, что при изменении серийного номера тома жесткого диска, изменения
вступят в силу после перезагрузки компьютера. В случае изменения серийного номера тома флэшки
перезагрузка компьютера не нужна, достаточно отключить и заново подключить флэшку. Да, еще кое
что, для доступа к диску потребуются права администратора.
На этом все, успехов в лицензировании!
.: Пример к данной статье :.
|
При использовании материала - ссылка на сайт обязательна
|
|