Блокировка USB-накопителей
1. Автоматически загружаться и работать процессом, который невозможно убить через диспетчер задач не обладая правами администратора;
2. Блокировать все USB-накопители кроме тех, что есть в своеобразном списке исключения (или, так сказать, доверенном списке);
3. Только от учетной записи администратора будет возможность добавлять и удалять из списка доверенные USB-накопители (или реализовать панельку, через которую можно давать права различным пользователям это делать).
В качестве среды разработки будет Microsoft Visual Studio 2010. В качестве языка программирования предположительно C#. Уже более года не садился за программирование, до этого последние небольшие программки-проекты писал на C#, но с задачами такого уровня еще не сталкивался.
Прошу Вашей помощи, что можно почитать, может где-то есть примеры с частично похожими задачами? Буду благодарен любой помощи, ибо год "непрограммирования" дает о себе знать. Уже вторую неделю нахожусь в режиме гугления, но ничего толкового по задаче №2 найти не удалось.
Буду очень благодарен за помощь!
1. Служба, просто служба
2. Тут несколько путей. Пришедшие мне сразу на ум — либо открывать в монопольном доступе подключенные устройства, если они недозволены, либо отлавливать WM_DEVICEARRIVAL (или какое там, уже не помню) и просто устройство извлекать
3. Делается через общение с этой самой службой
Обнаружить подключение флэшки можно с помощью технологии WMI: пример.
Получить параметры физического носителя опять же с помощью WMI: пример. Именно на основе этих параметров можно составить чёрный или белый список.
И то, и другое можно сделать функциями WinAPI вместо WMI.
А вот собственно заблокировать с помощью WMI не получится: не предусмотрено в этой технологии такой возможности (если я не ошибаюсь).
Полагаю, можно использовать через pinvoke функцию API SetupDiChangeState. Как только детектируется новая флэшка, проверяем её параметры, и если она не разрешена, отключаем устройство. Проверять лень, пробуй.
А не подскажите, как это сделать или где можно почитать? Пока не получилось ничего найти по этому поводу.
Полагаю, можно использовать через pinvoke функцию API SetupDiChangeState. Как только детектируется новая флэшка, проверяем её параметры, и если она не разрешена, отключаем устройство.
Не смог найти ни одного работающего примера. Везде советуют использовать эту функцию для отключения устройств, но как ее использовать и как сформировать нужные параметры - ни слова. Буду очен благодарен за помощь.
Нашел только вот это http://stackoverflow.com/questions/4097000/how-do-i-disable-a-system-device-programatically
Но там как-то слишком много кода для этой задачи получается, везде пишут что можно обойтись SetupDiChangeState.
Да и не получилось у меня заставить работать этот код. Вставляю флешку, пытаюсь ее отключить с помощью этого кода, делаю все как написано, получаю это http://i49.tinypic.com/21doj9g.jpg
В секции "Physical Disks and Volumes" рассказывается, как применять её по отношению к устройствам. Там же рассказано, каким должен быть параметр dwShareMode
Желаю удачи!
Если же окно "Мой компьютер" открыто, и в этот момент вставлять флешку, блокировка не происходит. Как только закрываю окно, и снова вставляю флешку, блокировка срабатывает.
Как это можно победить? Я так полагаю это один из конретных случаев, и есть еще способы сделать блокировку похожим способом нерабочей.
using System.Management;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace WMIConsole
{
class Program
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern SafeFileHandle CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr SecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile
);
public static SafeFileHandle ptr;
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const uint OPEN_EXISTING = 3;
static void Main(string[] args)
{
ManagementEventWatcher watcher = null;
try
{
// Bind to local machine
ManagementScope scope = new ManagementScope("root\\CIMV2");
scope.Options.EnablePrivileges = true; //set required privilege
WqlEventQuery query = new WqlEventQuery();
query.EventClassName = "__InstanceOperationEvent";
query.WithinInterval = new TimeSpan(0, 0, 1);
query.Condition = @"TargetInstance ISA 'Win32_DiskDrive'";
watcher = new ManagementEventWatcher(scope, query);
watcher.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
watcher.Start();
Console.ReadLine(); // block main thread for test purposes
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
if (watcher != null)
watcher.Stop();
}
return;
}
static void watcher_EventArrived(object sender, EventArrivedEventArgs e)
{
//Get the Event object and display its properties (all)
foreach (PropertyData pd in e.NewEvent.Properties)
{
ManagementBaseObject mbo = null;
if ((mbo = pd.Value as ManagementBaseObject) != null)
{
ManagementClass devs = new ManagementClass(@"Win32_Diskdrive");
ManagementObjectCollection moc = devs.GetInstances();
foreach (ManagementObject mo in moc)
{
if (mo["DeviceId"].ToString() == mbo.GetPropertyValue("DeviceId").ToString())
{
foreach (ManagementObject b in mo.GetRelated("Win32_DiskPartition"))
{
foreach (ManagementObject c in b.GetRelated("Win32_LogicalDisk"))
{
ptr = CreateFile(@"\\.\" + c["Name"].ToString(), GENERIC_READ | GENERIC_WRITE,
0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
}
}
}
}
}
}
}
}
}
А почему блокировка не происходит, каково поведение программы при этом?
Ну а так как программа будет представлять собой, так сказать, средство защиты, то этот факт является большой дырой ))
UPDATE:
Так же обнаружил, что если во время блокировки открыта любая папка и сбоку этой папки есть панелька с перечислением содержимого компьютера (в данном случае жесткие диски и флешки), то блокировка тоже будет невозможна.
Т.е. что бы блокировка успешно сработала необходимо чтобы были закрыты все окна, в которых обозначена наша флешка, и из которых непосредственнно можно открыть ее.
Эхх, знать бы как этим пользоваться
UPDATE:
Похоже заставил работать код, как требуется. Пришлось использовать DeviceIoControl. По карйней мере проблема описанная выше изчезла, будем тестить)
static extern IntPtr CreateFile(
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
IntPtr hTemplateFile);
IntPtr ptr;
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool DeviceIoControl(
IntPtr hDevice,
uint dwIoControlCode,
IntPtr lpInBuffer,
uint nInBufferSize,
[Out] IntPtr lpOutBuffer,
uint nOutBufferSize,
ref uint lpBytesReturned,
IntPtr lpOverlapped);
private void button1_Click(object sender, EventArgs e)
{
const uint FILE_SHARE_READ = 0x00000001;
const uint FILE_SHARE_WRITE = 0x00000002;
const uint OPEN_EXISTING = 3;
const uint GENERIC_READ = 0x80000000;
const uint GENERIC_WRITE = 0x40000000;
const uint FSCTL_LOCK_VOLUME = 0x00090018;
string str = @"\\.\E:";
ptr = CreateFile(str, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
uint byteReturned=0;
DeviceIoControl(ptr, FSCTL_LOCK_VOLUME, IntPtr.Zero, 0, IntPtr.Zero, 0, ref byteReturned, IntPtr.Zero);
Написал для этого функцию printDevices(). Если запускаю ее по нажатию кнопки, то она отрабатывает нормально, и загружает в ListView список подключенных к ПК USB-накопителей.
Как только запускаю ее по событию DBT_DEVICEARRIVAL или DBT_DEVICEREMOVECOMPLETE, то программа крашится с ошибкой
, указывая на
Прошу Вашей помощи, самому разобраться не получилось(
{
if ((m.WParam.ToInt32() == DBT_DEVICEARRIVAL)||(m.WParam.ToInt32() == DBT_DEVICEREMOVECOMPLETE))
{
//printDevices();
}
base.WndProc(ref m);
}
void printDevices()
{
listView1.Items.Clear();
DriveIndexList.Clear();
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_DiskDrive WHERE InterfaceType = 'USB'");
foreach (ManagementObject queryObj in searcher.Get())
{
listView1.Items.Add(new ListViewItem(new[] { queryObj["Caption"].ToString(), queryObj["Index"].ToString(), "" }));
DriveIndexList.Add((UInt32)queryObj["Index"]);
}
}
catch (ManagementException ee)
{
MessageBox.Show("An error occurred while querying for WMI data: " + ee.Message);
}
}
private void btn_Add_Click(object sender, EventArgs e)
{
printDevices();
}