Простой способ обнаружения эмуляторов ключа Guardant / Хабрахабр. При работе с ключом защиты Guardant (не важно какой модели) разработчик использует соответствующие API, при этом от него скрыт сам механизм работы с устройством, не говоря уже о протоколе обмена. Он не имеет на руках валидного хэндла устройства, пользуясь только адресом шлюза (т. Guardant. Handle) через который идет вся работа. В случае если в системе присутствует эмулятор ключа (особенно актуально для моделей до Guardant Stealth II включительно) используя данный шлюз разработчик не сможет определить, работает ли он с реальным физическим ключом, или его эмуляцией. Задавшись в свое время вопросом: «как определить наличие физического ключа?», мне пришлось немного поштудировать великолепно поданный материал за авторством Павла Агурова в книге "Интерфейс USB. Практика использования и программирования". После чего потратить время на анализ вызовов API функций из трехмегабайтного объектника, линкуемого к приложению, в котором собственно и сокрыта вся «магия» работы с ключом. В итоге появилось достаточно простое решение данной проблемы не требующее использования оригинальных Guardant API. Единственный минус — все это жутко недокументированно и техническая поддержка компании Актив даже не будет рассматривать ваши вопросы, связанные с таким использованием ключей Guardant. Ну и конечно, в какой- то момент весь данный код может попросту перестать работать из- за изменений в драйверах Guardant. Но пока что, на 2. Порядок действий будет примерно таким: Через Setup. ![]() Di. Get. Class. Devs. A() получим список всех присутствующих устройств. Проверим, имеет ли устройство отношение к ключам Guardant через проверку GUID устройства. У Guardant данный параметр равен {C2. CC2. E3- BC4. 8- 4. B7. 4- 9. 04. 3- 2. C6. 41. 3FFA7. 84})Получим символьную ссылку на каждое устройство вызовом Setup. Di. Get. Device. Registry. Property. A() с параметром SPDRP_PHYSICAL_DEVICE_OBJECT_NAME. Откроем устройство при помощи Zw. Open. File() (Create. File() тут уже к сожалению не подойдет, т. Теперь, имея на руках реальный хэндл ключа, вместо псевдохэндла (шлюза) предоставляемого Guardant API, мы можем получить описание его параметров, послав соответствующий IOCTL запрос. Правда, тут есть небольшой нюанс.
При работе с ключом защиты Guardant (не важно какой модели) разработчик за авторством Павла Агурова в книге "Интерфейс USB. купить идентичные конфигурации ПК и сделать побитные копии дисков. В статье мы рассмотрим и обойдем защиту, использующую USB-брелок — наверное, наиболее популярный электронный ключ на сегодня. Механизм системы защиты.. . создания резервных копий ключей ЭП, записанных на USB-устройства с. носитель допускается создание только одной резервной копии ключа ЭП. Начиная с Guardant Stealth III и выше, изменился протокол работы с ключом, как следствие поменялись константы IOCTL запросов и содержимое входящего и исходящего буфера. Для нормальной работы алгоритма желательно поддерживать возможности как старых, так и новых ключей, поэтому опишу различия: Для начала константы IOCTL выглядят так: Get. Dongle. Query. Record. IOCTL = $E1. B2. 00. Get. Dongle. Query. Record. Ex. IOCTL = $E1. B2. 00. 18. Первая для ключей от Guardant Stealth I/II. Вторая для Guardant Stealth III и выше (Sign/Time/Flash/Code)Отправляя первый запрос на устройство, мы будем ожидать что драйвер нам вернет следующий буфер: TDongle. Query. Record = packed record. Public. Code: DWord; // Public code. ![]() Hrw. Version: Byte; // Аппаратная версия ключа. Max. Net. Res: Byte; // Максимальный сетевой ресурс. Type: WORD; // Флаги типа ключа. ![]() ![]() Народ нужна помощь. Возможно ли сделать дуликат USB - ключа и как это сделать? Или если можно один ключь использовать на двух машинах, то как? Идентификация легальной копии продукта производится по USB-ключу, небольшому устройству размером с брелок, подключаемому к свободному USB-разъему компьютера. На тот случай, если флешка с ключом будет утеряна, полезно сделать копию usb-ключа. Это можно сделать с помощью программы Dekart Key Manager.. ID: DWord; // ID ключа. NProg: Byte; // Номер программы. Ver: Byte; // Версия. SN: WORD; // Серийный номер. Mask: WORD; // Битовая маска. GP: WORD; // Счетчик запусков GP/Счетчик времени. Real. Net. Res: WORD; // Текущий сетевой ресурс, д. Max. Net. Res. dw. Index: DWord; // Индекс для удаленного программирования. В случае более новых ключей и с учетом того, что протокол изменился, отправка первого запроса уже нам ничего не даст. Точнее запрос конечно, будет выполнен, но буфер придет пустой (обниленый). Поэтому на новые ключи мы посылаем второй запрос, который вернет данные немного в другом формате: TDongle. Query. Record. Ex = packed record. Unknown. 0: array [0. Byte. w. Mask: WORD; // Битовая маска. SN: WORD; // Серийный номер. Ver: Byte; // Версия. NProg: Byte; // Номер программы. ID: DWORD; // ID ключа. Type: WORD; // Флаги типа ключа. Unknown. 1: array [3. Byte. dw. Public. Code: DWORD. Unknown. Byte. dw. Hrw. Version: DWORD; // тип микроконтролера. Prog. Number: DWORD; // Номер программы. Unknown. 3: array [3. Byte. Здесь уже возвращается блок в 5. К сожалению по некоторым причинам я не могу вам дать полное описание данной структуры, но необходимые для данной статьи поля я в ней оставил. Общий код получения данных о установленных ключах выглядит так: procedure TEnum. Dongles. Ex. Update. Required: DWord. h. All. Devices: H_DEV. Info: DWORD. Data: SP_DEVINFO_DATA. Buff: array [0 . 9. Ansi. Char. h. Device. Handle: THandle. US: UNICODE_STRING. OA: OBJECT_ATTRIBUTES. IO: IO_STATUS_BLOCK. NTSTAT, dw. Return: DWORD. Dongle. Query. Record: TDongle. Query. Record. Dongle. Query. Record. Ex: TDongle. Query. Record. Ex. Set. Length(FDongles, 0). DWord(h. All. Devices) : = INVALID_HANDLE_VALUE. Init. Setup. API then. Update. USBDevices. All. Devices : = Setup. Di. Get. Class. Devs. A(nil, nil, 0. DIGCF_PRESENT or DIGCF_ALLCLASSES). DWord(h. All. Devices) < > INVALID_HANDLE_VALUE then. Fill. Char(Data, Sizeof(SP_DEVINFO_DATA), 0). Data. cb. Size : = Sizeof(SP_DEVINFO_DATA). Info : = 0. while Setup. Di. Enum. Device. Info(h. All. Devices, dw. Info, Data) do. dw. Required : = 0. Fill. Char(Buff[0], 1. 00, #0). Setup. Di. Get. Device. Registry. Property. A(h. All. Devices, @Data. SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, nil, @Buff[0], 1. Required). if Compare. Guid(Data. Class. Guid, Grd. GUID) then. Rtl. Init. Unicode. String(@US, String. To. Ole. Str(string(Buff))). Fill. Char(OA, Sizeof(OBJECT_ATTRIBUTES), #0). OA. Length : = Sizeof(OBJECT_ATTRIBUTES). OA. Object. Name : = @US. OA. Attributes : = OBJ_CASE_INSENSITIVE. NTSTAT : = Zw. Open. File(@h. Device. Handle. FILE_READ_DATA or SYNCHRONIZE, @OA, @IO. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE. FILE_SYNCHRONOUS_IO_NONALERT). NTSTAT = STATUS_SUCCESS then. Device. Io. Control(h. Device. Handle, Get. Dongle. Query. Record. IOCTL. nil, 0, @Dongle. Query. Record, Size. Of(TDongle. Query. Record). dw. Return, nil) and (Dongle. Query. Record. dw. ID < > 0) then. Set. Length(FDongles, Count + 1). FDongles[Count - 1]. Data : = Dongle. Query. Record. FDongles[Count - 1]. Pn. PParent. Path : =. Get. Pn. P_Parent. Path(Data. Dev. Inst). Inc(dw. Info). Continue. Move(Flash. Buffer[0], Dongle. Query. Record. Ex. Unknown. 0[0], 5. Device. Io. Control(h. Device. Handle, Get. Dongle. Query. Record. Ex. IOCTL. @Dongle. Query. Record. Ex. Unknown. 0[0]. Size. Of(TDongle. Query. Record. Ex). @Dongle. Query. Record. Ex. Unknown. 0[0]. Size. Of(TDongle. Query. Record. Ex). dw. Return, nil) then. Dongle. Query. Record. Ex. w. Mask : =. htons(Dongle. Query. Record. Ex. Mask). Dongle. Query. Record. Ex. w. SN : =. Dongle. Query. Record. Ex. w. SN). Dongle. Query. Record. Ex. ID : =. htonl(Dongle. Query. Record. Ex. ID). Dongle. Query. Record. Ex. dw. Public. Code : =. htonl(Dongle. Query. Record. Ex. Public. Code). Dongle. Query. Record. Ex. Type : =. htons(Dongle. Query. Record. Ex. Type). Set. Length(FDongles, Count + 1). Zero. Memory(@Dongle. Query. Record, Size. Of(Dongle. Query. Record)). Dongle. Query. Record. dw. Public. Code : =. Dongle. Query. Record. Ex. dw. Public. Code. Dongle. Query. Record. ID : = Dongle. Query. Record. Ex. dw. ID. Dongle. Query. Record. NProg : = Dongle. Query. Record. Ex. NProg. Dongle. Query. Record. by. Ver : = Dongle. Query. Record. Ex. Ver. Dongle. Query. Record. w. SN : = Dongle. Query. Record. Ex. SN. Dongle. Query. Record. w. Mask : = Dongle. Query. Record. Ex. Mask. Dongle. Query. Record. w. Type : = Dongle. Query. Record. Ex. Type. FDongles[Count - 1]. Data : = Dongle. Query. Record. FDongles[Count - 1]. Pn. PParent. Path : =. Get. Pn. P_Parent. Path(Data. Dev. Inst). Zw. Close(h. Device. Handle). end. Inc(dw. Info). if DWord(h. All. Devices) < > INVALID_HANDLE_VALUE then. Setup. Di. Destroy. Device. Info. List(h. All. Devices). Данная процедура перебирает все ключи и заносит информацию о них в массив структур TDongle. Query. Record, после чего вы можете вывести эти данные пользователю, ну или использовать их каким либо образом непосредственно в вашем приложении. Как видите все достаточно просто, но в объектных модулях Guardant API данный код помещен под достаточно серьезную стековую виртуальную машину и практически не доступен для анализа обычному разработчику. В принципе здесь нет ничего секретного, как видите при вызовах не используется даже шифрование передаваемых и получаемых буферов, но почему- то разработчики Guardant SDK не сочли нужным опубликовать данную информацию (правда я все- же смог получить разрешение на публикацию данного кода, т. Но не будем отвлекаться, вы вероятно заметили в вышеприведенной процедуре вызов функции Get. Pn. P_Parent. Path(). Данная функция возвращает полный путь к устройству от рута. Выглядит ее реализация следующим образом: function Get. Pn. P_Parent. Path(Value: DWORD): string. Parent: DWORD. Buffer: array [0. Ansi. Char. Len: ULONG. Result : = ''. if CM_Get_Parent(h. Parent, Value, 0) = 0 then. Len : = Length(Buffer). CM_Get_Dev. Node_Registry_Property. A(h. Parent, 1. 5, nil. Buffer[0], @Len, 0). S : = string(PAnsi. Char(@Buffer[0])). CM_Get_Parent(h. Parent, h. Parent, 0) = 0 do. Len : = Length(Buffer). CM_Get_Dev. Node_Registry_Property. A(h. Parent, 1. 5, nil. Buffer[0], @Len, 0). S : = string(PAnsi. Char(@Buffer[0])). Result : = S + '#' + Result. Result = '' then. Result : = 'не определен'. Собственно (вы будете смеяться) детектирование эмулятора будет происходить именно на базе данной строки. Обычно путь устройства выглядит следующим образом: \Device\0. Device\0. 00. 00. Device\0. 00. 00. Device\0. 00. 00. Device\NTPNP_PCI0. Device\USBPDO- 3#. В нем как минимум будет присутствовать текст NTPNP_PCI или USBPDO. Т. е. PCI шина или HCD хаб как минимум будут одним из предков. Т. к. эмулятор является все- же виртуальным устройством, то путь к нему будет выглядеть примерно так: \Device\0. Device\0. 00. 00. Соответственно на базе данной информации можно реализовать простую функцию: function Is. Dongle. Present(const Value: string): Boolean. Result : = Pos('NTPNP_PCI', Value) > 0. Result then. Result : = Pos('USBPDO', Value) > 0.
0 Comments
Leave a Reply. |
AuthorWrite something about yourself. No need to be fancy, just an overview. Archives
November 2016
Categories |