Получение списка открытых handle-ов, получение имени объекта по handle-у объекта и закрытие handle-ов открытых другими процессами. Получение списка открытых handle-ов Для получения списка открытых в системе handle-ов можно использовать функцию NtQuerySystemInformation. NTSYSAPI NTSTATUS NTAPI NtQuerySystemInformation( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL ); Функция экспортируется ntdll.dll. Для использования экспортов ntdll нужно использовать LoadLibrary, GetProcAddress. Нужно передать параметр SystemInformationClass=SystemHandleInformation. Функция вернет PSYSTEM_HANDLE_INFORMATION, где aSH - массив структур SYSTEM_HANDLE, а uCount - количество элементов в массиве. typedef struct _SYSTEM_HANDLE_INFORMATION { ULONG uCount; SYSTEM_HANDLE aSH[]; } SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; Перед вызовом функции лучше включить привилегии отладки, иначе она не вернет handle-ы системных процессов. typedef struct _SYSTEM_HANDLE { ULONG uIdProcess; UCHAR ObjectType; // OB_TYPE_* (OB_TYPE_TYPE, etc.) UCHAR Flags; // HANDLE_FLAG_* (HANDLE_FLAG_INHERIT, etc.) USHORT Handle; POBJECT pObject; ACCESS_MASK GrantedAccess; } SYSTEM_HANDLE, *PSYSTEM_HANDLE; Поле ObjectType - тип объекта ядра. Типы объектов для XP/Server 2003, которые я встречал: DESKTOP 18 WINDOW_STATION 17 DIRECTORY 2 KEYED_EVENT 16 FILE 28 REGISTRY_KEY 20 SEMAPHORE 13 SECTION 19 THREAD 6 EVENT 9 MUTANT 11 PORT 21 IO_COMPLETION 27 PROCESS 5 TIMER 14 WMI_GUID 29 TOKEN 4 SYMBOLIC_LINK 3 WAITABLE_PORT 22 JOB 7 Типы объектов для Windows 2000: DESKTOP 16 WINDOW_STATION 15 DIRECTORY 2 FILE 26 REGISTRY_KEY 18 SEMAPHORE 12 SECTION 17 THREAD 6 EVENT 8 MUTANT 10 PORT 19 PROCESS 5 TOKEN 4 SYMBOLIC_LINK 3 TIMER 13 IO_COMPLETION 25 WAITABLE_PORT 20 Чтобы получить информацию о handle-е нужно открыть владеющий им процесс функцей OpenProcess. Имя exe файла процесса можно получить функцией GetModuleFileNameEx передав ей как параметр hModule NULL. Информацию об имени открытого объекта получить сложнее. Сначала делаем копию handle для доступа из нашего процесса функцией DuplicateHandle. Для получения имени обекта можно использовать разные способы. Получение с помощью функций из ntdll.dll Используются функции NtQueryInformationFile и NtQueryObject NtQueryInformationFile с параметром FileNameInformation возвращает имя файла, но без имени устройства. Функция NtQueryObject с параметром ObjectNameInformation возвращает имя файла вместе с именем устройства. При выполнении эти функции могут зависнуть. Для того, чтобы избежать зависания нужно производить получение имени файла в отдельном потоке и по истечении некоторого времени считать поток зависшим и убивать его. Время ожидания не должно быть слишком маленьким. Если время ожидания установить 10-20 миллисекунд, то иногда получается, что ни один из запускаемых потоков не успевает получить имя файла и процедура получения имен файлов работает очень долго и имена файлов получить не удается. При запусках у меня получалось, что время в 300 миллисекунд хорошо подходит и зависающих потоков мало. Кроме того, функция NtQueryObject на некоторых объектах зависала и TerminateThread после этого не мог завершить работу потока, а процесс после этого не удавалось завершить без перезагрузки компьютера. Оказалось, что такой эффект происходит, если вызвать NtQueryObject на файле с именем \net\NtControlPipe2. Функция NtQueryInformationFile для этого файла работает нормально. Чтобы не было зависания сначала для каждого файла вызываем NtQueryInformationFile, проверяем, не является ли имя \\net\\NtControlPipe2 а потом вызываем NtQueryObject. Если объект является файлом, то имя, полученное функцией NtQueryObject содержит имя устройства на котором расположен файл. Чтобы получить имя диска по имени устройства нужно использовать функцию QueryDosDevice. Получение имени файла с помощью драйвера. Handle, полученный в нашем процессе функцией DuplicateHandle передается драйверу. Драйвер может использовать ObQueryNameString для получения имени объекта. Но проблема здесь возникает та же, что и при получении имени файла в режиме пользователя. Функция ObQueryNameString при вызове на файлах с именами \net\NtControlPipe#, где # - цифра зависает и процесс после этого не получается убить. Чтобы этого избежать делаем так: вызываем ObReferenceObjectByHandle, чтобы получить указатель на объект. Определяем тип объекта: получаем указатель на заголовок объекта и сравниваем указатель в поле ObjectType заголовка с указателем, на который указывает переменная ядра IoFileObjectType. Если они равны, то это объект типа файл и для него нужно выполнить проверку. В структуре FILE_OBJECT -> FileName имя файла записано без имени устройства. Но нам нужно только проверить его на наличие слова pipe. Если имя файла среди тех, что вызывают зависание, то для этого объекта ObQueryNameString не вызываем, иначе получаем имя объекта с помощью ObQueryNameString и возвращаем пользователю. Сравнение методов получения имени объектов. Возможно, NtQueryInformationFile возвращает строку из FILE_OBJECT -> FileName, а NtQueryObject возвращает результат вызова ObQueryNameString, т.к похожи возвращаемые ими значения и поведение функций. Методом с драйвером не получается получить имена для устройств \Device\Tcp и т.п. Метод с драйвером работает значительно быстрее. Интересно, что если использовать Nt... функции для получения имени файла, то MS Visual Studio после этого начинает долго обращаться к диску и надолго становится недоступной, если использовать драйвер - этого не происходит. Получение имени из заголовка объекта ядра. В драйвере, зная, handler нужно получить указатель на объект ядра с помощью функции ObReferenceObjectByHandle. Перед объектом ядра в памяти находится заголовок объекта ядра: typedef struct _OBJECT_HEADER { /*000*/ DWORD PointerCount; // number of references /*004*/ DWORD HandleCount; // number of open handles /*008*/ POBJECT_TYPE ObjectType; /*00C*/ BYTE NameOffset; // -> OBJECT_NAME /*00D*/ BYTE HandleDBOffset; // -> OBJECT_HANDLE_DB /*00E*/ BYTE QuotaChargesOffset; // -> OBJECT_QUOTA_CHARGES /*00F*/ BYTE ObjectFlags; // OB_FLAG_* /*010*/ union { // OB_FLAG_CREATE_INFO ? ObjectCreateInfo : QuotaBlock /*010*/ PQUOTA_BLOCK QuotaBlock; /*010*/ POBJECT_CREATE_INFO ObjectCreateInfo; /*014*/ }; /*014*/ PSECURITY_DESCRIPTOR SecurityDescriptor; /*018*/ } OBJECT_HEADER, * POBJECT_HEADER, **PPOBJECT_HEADER; Заголовок находится сразу перед объектом, так что его адрес можно получить вычев размер структуры заголовка (0x18) из адреса объекта. Если поле NameOffset не равно нулю, то оно равно количеству байт, которое надо вычесть из адреса заголовка, чтобы получить адрес структуры OBJECT_NAME (для большинства объектов это поле равно нулю, если оно не равно нулю, обычно его значение = 0x10). typedef struct _OBJECT_NAME { /*000*/ POBJECT_DIRECTORY Directory; /*004*/ UNICODE_STRING Name; /*00C*/ DWORD Reserved; /*010*/ } OBJECT_NAME, * POBJECT_NAME, **PPOBJECT_NAME; В поле Name - имя объекта. Если у объекта есть поле Name, то обычно оно равно последней части имени, позвращаемого функцией ObQueryNameString. Ссылки Про объекты ядра можно хорошо узнать из книги Sven Schreiber "Undocumented Windows 2000 secrets". По функциям native api - Gary Nebbett "Windows NT/2000 Native API Reference".