Форум: "Система";
Текущий архив: 2003.07.14;
Скачать: [xml.tar.bz2];
ВнизПривязка к железу Найти похожие ветки
← →
Shurik (2003-04-27 00:57) [0]Просьба ко всем, кто занимается shareware. Подскажите, как делается привязка к железу (именно получение информации о составе аппаратной части). Желательно, чтобы работало под любую Win.
← →
Illusion (2003-04-27 14:53) [1]лучше всего привязыватся к серийному номеру жёсткого диска... Полдучается он либо (тот котрый от создателя, оч. сложно, завсит от системы... Если интерстно, есть прога DiskID32, не помню где лежит, нотам довольно грамотно все алгоритмы получения описанны). Можно так же привязатся и к тому серийнику, что даётся во время форматирвоания... правда при соледущем формате номер будет совсем другой. Этот серийник получается с пол пинка при помощи функции GetVolumeInformation.
З.Ы. Это серийник подделать с пол пинка, а первый способ НЕ работает на винтах самсунга... У них поле отвечающее за серийник пустое.
← →
N142 (2003-04-27 17:01) [2]Просто CoCreateGuid(), запоминаем в реестре.
← →
Illusion (2003-04-27 17:26) [3]уид генерится случайно, причём каждый раз. А шаровар при переинстале должен знать о том, что он уже использовался, а как он узнает что этот иуд юзался раньше? никак... А оставлять какие-то метки... Это засекается. а Информация о винте в каком-нить левом или служебном реестре... То, что нужно.
← →
Shurik (2003-04-28 04:17) [4]А нельзя ли узнать больше инфы о машине? Привязка ТОЛЬКО к винту меня не очень обрадует, даже если это будет привязка к номеру винта, а не тома. Есть же всякие разные программы-тестилки, которые выдают море инфы по железу, причём они большую часть добывают сами, а не читают из реестра.
← →
jel (2003-04-28 10:45) [5]Можно использовать MAC адрес сетевой карты, если она есть. Эти адреса уникальные.
← →
Anatoly Podgoretsky (2003-04-28 11:24) [6]Шароварщики к железу не привязываются, за это сильно бьют, а привязываются любители, у которых и воровать то нечего.
← →
NightAngel (2003-04-28 18:36) [7]Что на текущий момент имеется среди soft защит.
1) Отлов дебагеров.
2) Извращённый алгоритм распаковки чтобы было сложно найти OEP (точку входа).
3) Антидамповые приёмы.
4) Порча импорта и других секций.
5) "Съедание" начальных байт (около OEP до первого Call,Mov).
6) Динамическая распаковка.
7) Поиск протектора в самой программе + API протектора.
8) Привязка к железу, Проверка CRC.
Теперь по порядку:
Отлов дебагеров - да помогает, отлавливает в большинстве случаев, тех у кого нет Frogsice/icedump и т.д. Кроме tELock и StarForce (используют отладочные регистры в своих целях), но и это обходиться.
Извращённый алгоритм распаковки - обычно вперемешку с предыдущим, даёт только отсрочку во времени. Потом всё равно способ находят.
Антидамповые приёмы - это было в Win9х, под Win2000/XP перестали делать, из-за того, что нужно драйвер писать ради такого удовольствия. Да и толку с этого... Айсдамп дампил.
Порча импорта и других секций - единственное к чему это привело - это к тому, что крэкеры выучили структуры секций.
"Съедание" начальных байт - реализовано пока только в Asprotect, pelock (может ещё где-то...). Asprotect тут глупо делает, а вот в pelock эта идея реализована удачнее. Неплохая идея. Если так и дальше будет развиваться, то программы будут частью защиты, а не наоборот.
Динамическая распаковка - не сильно перспективная защита... Делает ограничение на скорость выполнения кода.
Поиск протектора в самой программе + API протектора - полезная штука, но не эфективная. Быстро находится и устраняется.
Привязка к железу, проверка CRC - проверка crc часто делается по файлу на диске, а толку от этого мало, всё равно все тёмные дела творятся в памяти. Привязка к железу... Где-то хорошо реализовано, где-то плохо. Если данные по железу являются кодом для распаковки - вот над этим стоит задуматься (что-то вроде электронного ключа). Вот только пользователь проапгрейдится и всё...
Защитные механизмы в программах:
1) Скрипты/псевдокод.
2) Скрэмблинг (полиморфизм, метаморфизм - перемешивание кода).
3) Криптование.
4) Exception Handling.
5) Серийный номер == часть программы.
Скрипты/псевдокод - интересная вещь, но большей частью рассчитана на то, что крэкер перестанет с этим разбираться.
Скрэмблинг - вот это пока что самое реальное, т.к. сделать это довольно просто, а вот потом читать...
Криптование - сюда видимо всё клонится. Пока что мало кому дано ломать криптосистемы. Грамотное использование криптования даёт возможность создавать практически неломаемые программы.
Например Asprotect, после него даже распакованная программа всё равно не сломана до конца, т.к. полученый дамп хоть и работает, но программа работает в режиме триал или демо. А всё потому, что части программы, которые должны стать доступны в полной версии закриптованы и без правильного ключа их расшифровать невозможно.
Exception Handling - искусственное создание ошибок для того, чтобы сбить с толку дебагер. Путает только начинающих.
Серийный номер == часть программы - тоже очень хороший подход к защите, используется вместе с криптованием. Пользователь вводит серийный номер, программа использует этот номер для вычисления адреса процедуры, которая делает программу полной версией. Или к примеру в серийнике находятся части кода процедуры, или даже сама процедура регистрации в закриптованом виде.
Как того и следовало ожидать, лучшее - то что сделано вручную. Т.е. лучшая защита - скрэмблинг+криптование+"серийный номер == часть программы". Иногда просто хватает выложить на сайт демку, а продавать полную версию. Иногда можно ограничится только "серийный номер == часть программы". Иногда глупость портит даже самую крутую защиту... Человек выкладывает патч к своей программе, а в патче лежит ехе файл без защиты... Или пользуется armadillo или asprotect, даже не заглянув в раздел настроек.
Это так... Информация к размышлению.
← →
NightAngel (2003-04-28 18:39) [8]Теперь, что касается получения информации об оборудовании.
Эти проверки просты, но не обладают высокой степенью индивидуальности.
Что можно получить:
1. Тип ПК (записан в ПЗУ/Flash).
2. Дата изготовления, серийный номер и контрольная сумма BIOS (там-же).
3. Проверка объёма оперативной памяти (её тип, изготовитель).
4. Тип центрального процессора и приблизительная скорость его работы.
5. Наличие математического сопроцессора (MMX,SSE,SSE2,3DNow).
6. Количество и тип дисководов Flopy/CD-ROM/CD-R(W)/DVD (их характеристики).
7. Параметры физического жёсткого диска(ов).
8. Количество и тип каналов для подключения внешних устройств.
Вот, пожалуй, и всё (может ещё что-то забыл...).
А вообще... Привязка к оборудованию имеет смысл, только если реализовать нечто подобное:
Система взаимодействующих элементов защищена от изъятия её из интегрирующей системы благодаря наличию разнообразных взаимодействий с внешними элементами. Нормального функционирования изъятой системы в чужой для неё интегрирующей среде можно добиться только исключительно точным перенесением вместе с ней и окружающих взаимовлияющих внешних сил. Программа, пересаженная в другую среду, должна быть отторгнута, как отторгаются от организма пересаженные чужие ткани. Для реализации механизма отторжения необходимо, чтобы программа
имела возможность предварительно "привыкнуть" к своей среде, а
взаимодействие ее с чужой средой приводило к мгновенной гибели ее
самой, окружающей среды или носило на себе следы постепенного
разрушения.
Преимущества и недостатки оценивайте сами, но перед этим прислушайтесь к словам Anatoly Podgoretsky ©.
← →
Shurik (2003-04-29 07:45) [9]2Anatoly Podgoretsky:
Что значит, "Шароварщики к железу не привязываются, за это сильно бьют"? К чему можно делать тогда ещё привязку? Паре имя юзера/пароль? Так тогда программу даже ломать не надо. Достаточно найти 1 верную пару (хотя бы купить) и выложить на обозрение - всё, можно считать, что программа ушла в "дикую природу".
2NightAngel:
"5) "Съедание" начальных байт (около OEP до первого Call,Mov)." - вот про это первый раз слышу. Можно поподробнее?
Далее, что можно получить - я знаю. Я не знаю, КАК это можно получить. По пунктам 1, 6, 7, 8 у меня нет алгоритмов. Может кто-нибудь помочь?
← →
Anatoly Podgoretsky (2003-04-29 08:21) [10]Бьют деньгами, потерей бизнеса.
А твоя привязка к железу, как то ограничивает, точно также можно инсталлировать на любом количестве компьютеров, только не надо песню, про много ступенчатое получения ключа, это еще надежнее для потери бизнеса.
Надо защита, так купи профессональную защиту и не мучься.
← →
Shurik (2003-04-29 13:47) [11]Не, ну как это "точно также можно инсталлировать на любом количестве компьютеров"? Понятно, что один и тот же ключ на другой комп не подойдёт. Значит, надо менять код программы. Ну вот вся надежда как раз на это. Что взломают не сразу. Насчёт "купи профессинальную защиту". Это какую же? Если насчёт ключа - для маленькой тулзы самое неподходящее решение. Если чисто софт - ХАЧУ САМ! ("не мучься" - отличный вариант, на все случаи жизни; зачем что-то самому придумывать, когда можно купить/попросить/стырить готовое)
И, кстати, за использование асинхронного шифрования в своих программах по шапке надавать могут?
← →
Anatoly Podgoretsky (2003-04-29 14:07) [12]Тебе выше указали - AsProtect - $99
← →
XAli (2003-04-29 18:35) [13]Лови, удачи!
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определение видеокарты
Зависимости: Windows
Автор: Gua, gua@ukr.net, ICQ:141585495, Simferopol
Copyright: Gua
Дата: 18 июля 2002 г.
***************************************************** }
function GetDisplayDevice: string;
var
lpDisplayDevice: TDisplayDevice;
begin
lpDisplayDevice.cb := sizeof(lpDisplayDevice);
EnumDisplayDevices(nil, 0, lpDisplayDevice , 0);
Result:=lpDisplayDevice.DeviceString;
end;
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение серийного номера BIOS
Зависимости: не работает в WinNT
Автор: Gua, fbsdd@ukr.net, ICQ:141585495, Simferopol
Copyright:
Дата: 03 мая 2002 г.
***************************************************** }
Function GetBiosNumber:string;
begin
result:=string(pchar(ptr($FEC71)));
end;
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Получение сведений о диске (метка/имя диска, файловая система, серийный номер)
Получение информации о любом диске.
Работает на FDD, HDD, CD, другие не пробовал.
Создайте модуль с именем HDDInfo и полностью скопируйте в него весь текст.
Зависимости: Все Windows (32S,95,NT)
Автор: cyborg, cyborg1979@newmail.ru, ICQ:114205759, Бузулук
Copyright: Собственное написание (Осипов Евгений Анатольевич)
Дата: 23 мая 2002 г.
пример
USES ..., ..., ..., HDDInfo; {Добавляем наш модуль}
{Нужно создать на форме компонент TLabel , Name которого ставим в Disks}
{И в событии главной формы OnActicate написать это:}
procedure TMyForm.FormActivate(Sender: TObject);
Var
S,SOut : String;
I : Integer;
VolumeName,FileSystemName : String;
VolumeSerialNo,MaxComponentLength,FileSystemFlags:LongWord;
begin
S:=GetDisks(DiskHDD); {Получаем список Жёстких дисков (Параметр DiskHDD)}
SOut:="";
For I:=1 to Length(S) do {Получаем информацию о всех дисках и пишем в TLabel на форме}
Begin
{Если диск существует/вставлен ...}
if GetHDDInfo(S[I], VolumeName, FileSystemName, VolumeSerialNo,
MaxComponentLength, FileSystemFlags) then {... тогда собираем информацию}
SOut:=SOut+
"Диск: "+S[I]+#13#10+
"Метка: "+VolumeName+#13#10+
"Файловая система: "+FileSystemName+#13+#10+
"Серийный номер: "+IntToHex(VolumeSerialNo,8)+#13+#10+
"Макс. длина имени файла: "+IntToStr(MaxComponentLength)+#13+#10+
"Flags: "+IntToHex(FileSystemFlags,4)+#13#10+#13#10;
End;
Disks.Caption:=SOut; {Выводим в компонент TLabel полученные данные о дисках}
end;
***************************************************** }
unit HDDInfo;
interface
Uses Windows;
Const {Константы для TypeOfDisk функции GetDisks}
DiskUnknown=0; {Неизвестные диски}
DiskNone=1; {Отсутствующие диски}
DiskFDD=DRIVE_REMOVABLE; {Съёмные диски, дискеты}
DiskHDD=DRIVE_FIXED; {Не съёиные диски, жёсткие диски}
DiskNet=DRIVE_REMOTE; {Сетевые диски}
DiskCDROM=DRIVE_CDROM; {CD ROM}
DiskRAM=DRIVE_RAMDISK; {Диски в ОЗУ}
{Получить имена нужных дисков}
function GetDisks(TypeOfDisk : Word) : String;
{Функция получения информации о диске (HDD,FDD,CD) с буквой Disk}
{
Передаваемые значения:
Disk - Буква диска
Получаемые значения:
VolumeName - Метка/Имя тома
FileSystemName - Файловая система
VolumeSerialNo - Серийный номер диска (можно привязывать к диску программы)
MaxComponentLength - Максимальная длинна имени файла
FileSystemFlags - Флаги смотрите в справке Delphi по GetVolumeInformation
Функция возвращает true, если всё прошло успешно (диск нашёлся),
и false, если возникли проблемы, например диска нет в дисководе,
либо дисковода такого вообще нет
}
Function GetHDDInfo(Disk : Char;Var VolumeName, FileSystemName : String;
Var VolumeSerialNo, MaxComponentLength, FileSystemFlags:LongWord) : Boolean;
implementation
function GetDisks(TypeOfDisk : Word) : String;{Получить имена нужных дисков}
var
DriveArray : array[1..26] of Char;
I : integer;
begin
DriveArray:="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for I := 1 to 26 do
if GetDriveType(PChar(DriveArray[I]+":\")) = TypeOfDisk then Result := Result+DriveArray[I];
end;
Function GetHDDInfo(Disk : Char;Var VolumeName, FileSystemName : String;
Var VolumeSerialNo, MaxComponentLength, FileSystemFlags:LongWord) : Boolean;
Var
_VolumeName,_FileSystemName:array [0..MAX_PATH-1] of Char;
_VolumeSerialNo,_MaxComponentLength,_FileSystemFlags:LongWord;
Begin
if GetVolumeInformation(PChar(Disk+":\"),_VolumeName,MAX_PATH,@_VolumeSerialNo,
_MaxComponentLength,_FileSystemFlags,_FileSystemName,MAX_PATH) then
Begin
VolumeName:=_VolumeName;
VolumeSerialNo:=_VolumeSerialNo;
MaxComponentLength:=_MaxComponentLength;
FileSystemFlags:=_FileSystemFlags;
FileSystemName:=_FileSystemName;
Result:=True;
End else Result:=False;
End;
end.
← →
XAli (2003-04-29 18:36) [14]Лови, удачи!
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Определение фирмы производителя CPU
Зависимости: нет
Автор: Gua, fbsdd@ukr.net, ICQ:1411585495, Simferopol
Copyright:
Дата: 03 мая 2002 г.
пример
procedure TForm1.Button1Click(Sender: TObject);
begin
MessageDlg(GetCPUVendor,mtInformation,[mbOk],0);
end;
***************************************************** }
type
TVendor = array [0..11] of char;
.........................
function GetCPUVendor : TVendor; assembler; register;
asm
PUSH EBX {Save affected register}
PUSH EDI
MOV EDI,EAX {@Result (TVendor)}
MOV EAX,0
DW $A20F {CPUID Command}
MOV EAX,EBX
XCHG EBX,ECX {save ECX result}
MOV ECX,4
@1:
STOSB
SHR EAX,8
LOOP @1
MOV EAX,EDX
MOV ECX,4
@2:
STOSB
SHR EAX,8
LOOP @2
MOV EAX,EBX
MOV ECX,4
@3:
STOSB
SHR EAX,8
LOOP @3
POP EDI {Restore registers}
POP EBX
end;
← →
XAli (2003-04-29 18:39) [15]Лови, Удачи!
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Поличение серийного номера IDE диска.
Функция получает серийный номер первого физического диска IDE (не серийный номер тома!).
Используется S.M.A.R.T. API, а под Windows NT/2K/XP запрос производится не напрямую к диску, а через miniport драйвер контроллера, что позволяет читать серийный номер не имея прав администратора.
Функция может не работать, если первый контролер в системе не ATA или если первое устройство не является винчестером, который поддерживает SMART (современные винчестеры поддерживают).
Если Вы хотите получить другие параметры диска/других дисков, то смотрите пример IdeInfo2 с моего сайта.
На Windows 9x требует наличия драйвера smartvsd.vxd (должен быть в стандартной поставке), просто скопируйте его в \windows\system\iosubsys и перезагрузите компьютер.
Зависимости: Windows, SysUtils
Автор: Alex Konshin, akonshin@earthlink.net, Boston, USA
Copyright: http://home.earthlink.net/~akonshin/index.htm
Дата: 30 декабря 2002 г.
пример
var s : String;
rc : DWORD;
begin
s := GetIdeDiskSerialNumber;
if s="" then
begin
rc := GetLastError;
if rc=0 then WriteLn("IDE drive is not support SMART feature")
else WriteLn(SysErrorMessage(rc));
end
else WriteLn("Disk serial number: """, s,"""");
end.
***************************************************** }
function GetIdeDiskSerialNumber : String;
type
TSrbIoControl = packed record
HeaderLength : ULONG;
Signature : Array[0..7] of Char;
Timeout : ULONG;
ControlCode : ULONG;
ReturnCode : ULONG;
Length : ULONG;
end;
SRB_IO_CONTROL = TSrbIoControl;
PSrbIoControl = ^TSrbIoControl;
TIDERegs = packed record
bFeaturesReg : Byte; // Used for specifying SMART "commands".
bSectorCountReg : Byte; // IDE sector count register
bSectorNumberReg : Byte; // IDE sector number register
bCylLowReg : Byte; // IDE low order cylinder value
bCylHighReg : Byte; // IDE high order cylinder value
bDriveHeadReg : Byte; // IDE drive/head register
bCommandReg : Byte; // Actual IDE command.
bReserved : Byte; // reserved for future use. Must be zero.
end;
IDEREGS = TIDERegs;
PIDERegs = ^TIDERegs;
TSendCmdInParams = packed record
cBufferSize : DWORD; // Buffer size in bytes
irDriveRegs : TIDERegs; // Structure with drive register values.
bDriveNumber : Byte; // Physical drive number to send command to (0,1,2,3).
bReserved : Array[0..2] of Byte; // Reserved for future expansion.
dwReserved : Array[0..3] of DWORD; // For future use.
bBuffer : Array[0..0] of Byte; // Input buffer.
end;
SENDCMDINPARAMS = TSendCmdInParams;
PSendCmdInParams = ^TSendCmdInParams;
TIdSector = packed record
wGenConfig : Word;
wNumCyls : Word;
wReserved : Word;
wNumHeads : Word;
wBytesPerTrack : Word;
wBytesPerSector : Word;
wSectorsPerTrack : Word;
wVendorUnique : Array[0..2] of Word;
sSerialNumber : Array[0..19] of Char;
wBufferType : Word;
wBufferSize : Word;
wECCSize : Word;
sFirmwareRev : Array[0..7] of Char;
sModelNumber : Array[0..39] of Char;
wMoreVendorUnique : Word;
wDoubleWordIO : Word;
wCapabilities : Word;
wReserved1 : Word;
wPIOTiming : Word;
wDMATiming : Word;
wBS : Word;
wNumCurrentCyls : Word;
wNumCurrentHeads : Word;
wNumCurrentSectorsPerTrack : Word;
ulCurrentSectorCapacity : ULONG;
wMultSectorStuff : Word;
ulTotalAddressableSectors : ULONG;
wSingleWordDMA : Word;
wMultiWordDMA : Word;
bReserved : Array[0..127] of Byte;
end;
PIdSector = ^TIdSector;
const
IDE_ID_FUNCTION = $EC;
IDENTIFY_BUFFER_SIZE = 512;
DFP_RECEIVE_DRIVE_DATA = $0007c088;
IOCTL_SCSI_MINIPORT = $0004d008;
IOCTL_SCSI_MINIPORT_IDENTIFY = $001b0501;
DataSize = sizeof(TSendCmdInParams)-1+IDENTIFY_BUFFER_SIZE;
BufferSize = SizeOf(SRB_IO_CONTROL)+DataSize;
W9xBufferSize = IDENTIFY_BUFFER_SIZE+16;
var
hDevice : THandle;
cbBytesReturned : DWORD;
pInData : PSendCmdInParams;
pOutData : Pointer; // PSendCmdInParams;
Buffer : Array[0..BufferSize-1] of Byte;
srbControl : TSrbIoControl absolute Buffer;
← →
XAli (2003-04-29 18:40) [16]// продолжение...
procedure ChangeByteOrder( var Data; Size : Integer );
var ptr : PChar;
i : Integer;
c : Char;
begin
ptr := @Data;
for i := 0 to (Size shr 1)-1 do
begin
c := ptr^;
ptr^ := (ptr+1)^;
(ptr+1)^ := c;
Inc(ptr,2);
end;
end;
begin
Result := "";
FillChar(Buffer,BufferSize,#0);
if Win32Platform=VER_PLATFORM_WIN32_NT then
begin // Windows NT, Windows 2000
// Get SCSI port handle
hDevice := CreateFile( "\\.\Scsi0:", GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
srbControl.HeaderLength := SizeOf(SRB_IO_CONTROL);
System.Move("SCSIDISK",srbControl.Signature,8);
srbControl.Timeout := 2;
srbControl.Length := DataSize;
srbControl.ControlCode := IOCTL_SCSI_MINIPORT_IDENTIFY;
pInData := PSendCmdInParams(PChar(@Buffer)+SizeOf(SRB_IO_CONTROL));
pOutData := pInData;
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl( hDevice, IOCTL_SCSI_MINIPORT, @Buffer,
BufferSize, @Buffer, BufferSize, cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
end
else
begin // Windows 95 OSR2, Windows 98
hDevice := CreateFile( "\\.\SMARTVSD", 0, 0, nil, CREATE_NEW, 0, 0 );
if hDevice=INVALID_HANDLE_VALUE then Exit;
try
pInData := PSendCmdInParams(@Buffer);
pOutData := PChar(@pInData^.bBuffer);
with pInData^ do
begin
cBufferSize := IDENTIFY_BUFFER_SIZE;
bDriveNumber := 0;
with irDriveRegs do
begin
bFeaturesReg := 0;
bSectorCountReg := 1;
bSectorNumberReg := 1;
bCylLowReg := 0;
bCylHighReg := 0;
bDriveHeadReg := $A0;
bCommandReg := IDE_ID_FUNCTION;
end;
end;
if not DeviceIoControl( hDevice, DFP_RECEIVE_DRIVE_DATA, pInData,
SizeOf(TSendCmdInParams)-1, pOutData, W9xBufferSize,
cbBytesReturned, nil ) then Exit;
finally
CloseHandle(hDevice);
end;
end;
with PIdSector(PChar(pOutData)+16)^ do
begin
ChangeByteOrder(sSerialNumber,SizeOf(sSerialNumber));
SetString(Result,sSerialNumber,SizeOf(sSerialNumber));
end;
end;
← →
Illusion (2003-04-29 23:55) [17]серинйик винта - тоже уникален... как и мак сетевухи... и то и то можно перепрошить... Я считаю этот способ привязки лучшим, и всегда им пользуюсь, если возникает такая необходимость...
← →
Shurik (2003-04-30 00:50) [18]Во! То что надо было! Спасибо.
Rem: конечно, если у кого ещё что есть - просьба поделиться.
← →
Shurik (2003-04-30 00:57) [19]Только сейчас заметил: чуть ранее опечатался, имел ввиду:
"И, кстати, за использование асимметричного шифрования (т.е. с открытым ключом) в своих программах по шапке надавать могут?" (Наверно о другом думал, когда писал).
Страницы: 1 вся ветка
Форум: "Система";
Текущий архив: 2003.07.14;
Скачать: [xml.tar.bz2];
Память: 0.54 MB
Время: 0.01 c