Как-то встала передо мной задача – написать программу-сторож, позволяющую использовать только разрешенные USB-флешки на определенных компьютерах. Привязку решил делать к серийному номеру, а так же VID и PID флешки (Vendor ID и Part ID), что в общем случае можно считать уникальной информацией.
Я сейчас не рассматриваю способы перепрошивки флешек под произвольный VID, PID, Serial, это всё можно сделать, подобрав соответствующую утилитку под производителя контроллера.
Итак, нависла задача узнать всю нужную информацию. Сейчас я не буду рассматривать разработку целого приложения, покажу лишь основную функцию. Буду даже рад, если кто-то поможет доработать программу. Да и сам я буду постоянно улучшать и модернизировать этот код.
Итак, для выбора данных воспользуемся моим любимым WMI (Windows Management Instrumentation). Почему оно мне нравится? Да потому что все работает “из коробки”, не нужно устанавливать дополнительные программы и компоненты и есть множество классов.
Вот видео по теме:
Как работать с технологией – посмотрите по тегам WMI, WMIC, а я повествую дальше.
Выковыривать данные будет из строки идентификатора устройства, которая выглядит как-то так:
USB\VID_152D&PID_2329\0FD2ACBFFFFF
В этой строке есть VID, PID и серийный номер после второй косой черты.
Важно не перепутать серийный номер флешки и серийный номер тома. Последний (можно получить командой dir) меняется при форматировании флешки и никак не годится для уникальной идентификации.
Механизм получения серийного номера флешки
Теперь задача – получить строку эту.
Средствами WMI мы получаем эту информацию из класса Win32_usbhub
, объявленного в пространстве \\root\cimv2
Итак, запускаем командную строку, там программу wmic.
Пишем там команду выбора поля DeviceID
из класса Win32_usbhub
. Забегая вперед скажу, что выдастся много устройств (корневые концентраторы, мышь и т.д.,) поэтому я сразу напишу фильтр по запоминающим устройствам.
wmic:root\cli>path win32_usbhub Where (Caption="Запоминающее устройство для USB") get DeviceID
Вот и получили наши заветные строки. Парсить их придется уже другими средствами.
Кстати, вот наработка на Delphi, кому-то может это пригодится.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, ComCtrls, StdCtrls;
type
TForm1 = class(TForm)
lst1: TListBox;
btn1: TButton;
procedure btn1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses ActiveX, ComObj;
procedure TForm1.btn1Click(Sender: TObject);
Var Enumerator: IEnumVariant;
WbemLocator, WbemServices, WbemObjectSet: Variant;
Properties: IEnumVariant;
Property_, System: OleVariant;
j: LongWord;
sProperty: string;
VID,PID,Serial: ShortString;
begin
lst1.Clear;
WbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
WbemServices := WbemLocator.ConnectServer;
WbemObjectSet := WbemServices.InstancesOf('Win32_usbhub');
Enumerator := IEnumVariant(TVarData(WbemObjectSet._NewEnum).VDispatch);
try
while Enumerator.Next(1, System, j) = S_OK do
try
try
if (System.Caption <> 'Запоминающее устройство для USB') then Continue;
except end;
Properties := IEnumVariant(TVarData(System.Properties_._NewEnum).VDispatch);
while Properties.Next(1, Property_, j) = S_OK do
begin
sProperty := VarToStr(Property_.Value);
if (Property_.Name = 'DeviceID') then
begin
VID := Copy(sProperty, Pos('VID_',sProperty)+4,4);
PID := Copy(sProperty, Pos('PID_',sProperty)+4,4);
Serial := Copy(sProperty, Pos('PID_',sProperty)+9,Length(sProperty)-Pos('PID_',sProperty)+10);
if ((Pos('\',Serial) > 0)) then
Begin
Serial := Copy(Serial,Pos('\',Serial)+1,Length(Serial)-Pos('\',Serial));
end;
lst1.Items.Add('VID: '+ VID + ' PID: ' + PID + ' SERIAL: ' + Serial);
end;
end;
except end;
except end;
end;
end.
Код, конечно, грубоват, но собран на коленке. Главное – работает. Пользуйтесь, если нужно:
Скачать проект с исполняемым файлом можно по ссылке
Таким образом, используя WMI мы получили серийный номер флешки, а так же дополнительную информацию в виде VID и PID.
А можно ли как-нибудь изменить серийный номер флешки?
Да, можно. Есть программы-прошивальщики (под каждый чип своя программа), которые можно использовать для изменения серийного номера и VID PID идентификаторов. Например, для чипов Alcor я использую Alcor Change Vid Pid Rework.
Сменить серийный номер флешки несложно. Подключаем флешку, выбираем её в порту в списке. Нажимаем Setup (открывается окно на фотках выше) вводит нужные VID PID и SN и жмём кнопку START. Флешка готова через секунду с новым серийным номером.
Кстати, я уже писал ранее, как можно отформатировать флешку в ntfs – это может оказаться полезным!
Друзья! Вступайте в нашу группу Вконтакте, чтобы не пропустить новые статьи! Хотите сказать спасибо? Ставьте Like, делайте репост! Это лучшая награда для меня от вас! Так я узнаю о том, что статьи подобного рода вам интересны и пишу чаще и с большим энтузиазмом!
Также, подписывайтесь на наш канал в YouTube! Видео выкладываются весьма регулярно и будет здорово увидеть что-то одним из первых!
А почему при определении серийного номера флешки в первом варианте вставляются символы & ?
Таким образом у меня МТС-модем определился. А серийный номер обычных флешек определяется на ура!
Всё правильно! Вот такие сложные серийные номера с идентификаторами вендора – у сложных устройств. С флешками и дисками такого не возникнет.
Узнал у знающих ребят. Если в серийном номере тома второй символ & то это динамический серийник, не постоянный. После перезагрузки системы будет определяться уже другой, так что построить нормальную систему мониторинга серийников не получится.
Выход – перешивать флешку с постоянным номером. Скоро протестирую, если что – напишу статью!
а как мне внутри указанного кода еще и размер узнать?
вопрос в том, что надо проставить соответствие DeviceID и его размера
В этом коде никак. Но можно воспользоваться дополнительно другим WMI классом. win32_diskdrive
Для сравнения, выполните команду в командной строке с вставленной флешкой:
> wmic path win32_diskdrive get pnpdeviceid, size
Увидите похожую строку серийного номера, а так же размер в байтах.
Нужно только выделить серийный номер, найти его в одной из этих строк, а так же выдать размер в байтах (предварительно пересчитать, например в МБ).
Здравствуйте, не могли бы вы мне подсказать, я повесил вашу функцию на событие WM_DEVICECHANGE, а точнее на сообщение DBT_DEVICEARRIVAL,то есть когда флешку только вставили мне нужно сразу узнать её серийный номер, в итоге выскакивает ошибка
“Не удается выполнить исходящий вызов, так как приложение обрабатывает входящий синхронный вызов.”
Задержка на 5 секунд спасает, но, может, как-то можно исправить эту ошибку?
Заранее Спасибо.
@Actek, Было бы интересно полный код посмотреть. На самом деле помочь вам может статья https://litl-admin.ru/skripting/sozdaem-potrebitel-sobytij-wmi.html где я делал потребитель событий на установку нового USB устройства.
@litladmin, Вот код:
procedure DEVICECHANGE(var Msg: TMessage); message WM_DEVICECHANGE;
procedure TForm1.DEVICECHANGE(var Msg: TMessage);
var
Enumerator: IEnumVariant;
WbemLocator, WbemServices, WbemObjectSet : Variant;
Properties : IEnumVariant;
Property1, System : OleVariant;
j : LongWord;
sProperty : string;
Vid, Pid, Serial : ShortString;
begin
case Msg.WParam of
DBT_DEVICEARRIVAL:
begin
Label1.Caption := 'Flash change';
WbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
WbemServices:= WbemLocator.ConnectServer;
WbemObjectSet:= WbemServices.InstancesOf('Win32_usbhub');
Enumerator := IEnumVariant(TVarData(WbemObjectSet._NewEnum).VDispatch);
try
while Enumerator.Next(1, System, j) = S_OK do
try
try
if (System.Caption 'Запоминающее устройство для USB') then Continue;
except end;
Properties:= IEnumVariant(TVarData(System.Properties_._NewEnum).VDispatch);
while Properties.Next(1, Property1, j) = S_OK do
begin
sProperty := VarToStr(Property1.Value);
if(Property1.Name = 'DeviceID') then
begin
VID := Copy(sProperty, Pos('VID_', sProperty)+4,4);
PID := Copy(sProperty, Pos('PID_', sProperty)+4,4);
Serial := Copy(sProperty, Pos('PID_', sProperty)+9, Length(sProperty)-Pos('PID_', sProperty)+10);
if ((Pos('\', Serial)>0)) then
begin
Serial:=Copy(Serial, Pos('\' ,Serial)+1, Length(Serial)-Pos('\', Serial));
end;
lst1.Items.Add('Vid: '+VID + ' Pid: '+PID+ ' Serial: '+ Serial);
end;
end;
except end;
except end;
end;
end;
end;
За статью спасибо, постараюсь разобраться.