Ce code permet d'obtenir la liste des handles ouverts par un programme :
- fichiers
- sections
- clé de registre
- événement
- mutex
- ...
Ce code permet aussi de recherché un nom de handle dans tous les processus, pour savoir, par exemple, le nom de l'application qui a ouvert le fichier toto.txt... ou une clé de registre, ou un mutex...
Ce code liste aussi les programmes en execution. Il utilise des APIs natives non documentées pour NT/2000/XP non documentées...
Je précise que ce code ne fonctionne PAS pour les PROCESSUS appartenant à SYSTEM, SERVICE LOCAL, SERVICE RESEAU : ces processus ne peuvent être accéder que par du code en mode Kernel et pas par du code en mode User comme VB... pour accéder à ces processus, il faut un driver .sys...
Source / Exemple :
Option Explicit
'module ermettant d'obtenir une liste des handles de tous les processus du systeme
'structure contenant des infos sur un handle d'un processus
Public Type HandleInfo
ProcessId As Long 'ID du processus propriétaire
Flags As Byte ' 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
Handle As Integer 'valeur du handle
ObjectName As String 'nom de l'objet
NameInformation As String 'type de l'ojet
Object As Long 'adresse de l'objet
GrantedAccess As Long 'accès autorisés à l'objet
Attributes As Long 'attributs
HandleCount As Long 'nombre de handle de ce type dans le système
PointerCount As Long 'nombre de références pointeurs à cet objet dans le système
CreateTime As Currency 'date de création de l'objet
GenericRead As Long 'accès générique
GenericWrite As Long
GenericExecute As Long
GenericAll As Long
ObjectCount As Long '
PeakObjectCount As Long '
PeakHandleCount As Long '
InvalidAttributes As Long 'définit les attributs invalides pour ce type d'objet
ValidAccess As Long '
Unknown As Byte
MaintainHandleDatabase As Byte '
PoolType As Long 'type de pool utilisé par l'objet
PagedPoolUsage As Long 'paged pool utilisé
NonPagedPoolUsage As Long 'non-paged pool utilisé
End Type
'structure de donnée de handle renvoyée par NtQuerySystemInformation
Private Type SYSTEM_HANDLE_INFORMATION ' Information Class 16
ProcessId As Long
ObjectTypeNumber As Byte
Flags As Byte ' 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
Handle As Integer
Object As Long
GrantedAccess As Long
End Type
'constante pour NtQuerySystemInformation
Private Const SystemHandleInformation As Long = 16&
Private Const STATUS_INFO_LENGTH_MISMATCH As Long = &HC0000004
Private Const STATUS_SUCCESS As Long = &H0&
'constante pour NtQueryObject
Private Const ObjectBasicInformation As Long = 0 ' 0 Y N
Private Const ObjectNameInformation As Long = 1 ' 1 Y N
Private Const ObjectTypeInformation As Long = 2 ' 2 Y N
Private Const ObjectAllTypesInformation As Long = 3 ' 3 Y N
Private Const ObjectHandleInformation As Long = 4 ' 4 Y Y
'structure contenant des infos sur les type d'objet du systeme
Private Type OBJECT_BASIC_INFORMATION ' Information Class 0
Attributes As Long
GrantedAccess As Long
HandleCount As Long
PointerCount As Long
PagedPoolUsage As Long
NonPagedPoolUsage As Long
Reserved1 As Long
Reserved2 As Long
Reserved3 As Long
NameInformationLength As Long
TypeInformationLength As Long
SecurityDescriptorLength As Long
CreateTime As Currency
End Type
'structure contenant une chaine de caractere UNICODE
Private Type UNICODE_STRING
Length As Integer
MaximumLength As Integer
Buffer As Long
End Type
'structure contenant le nom d'un type d'objet
Private Type OBJECT_NAME_INFORMATION ' Information Class 1
Name As UNICODE_STRING
End Type
Private Type GENERIC_MAPPING
GenericRead As Long
GenericWrite As Long
GenericExecute As Long
GenericAll As Long
End Type
'information sur un type d'objet
Private Type OBJECT_TYPE_INFORMATION ' Information Class 2
Name As UNICODE_STRING
ObjectCount As Long
HandleCount As Long
Reserved1 As Long
Reserved2 As Long
Reserved3 As Long
Reserved4 As Long
PeakObjectCount As Long
PeakHandleCount As Long
Reserved5 As Long
Reserved6 As Long
Reserved7 As Long
Reserved8 As Long
InvalidAttributes As Long
GenericMapping As GENERIC_MAPPING
ValidAccess As Long
Unknown As Byte
MaintainHandleDatabase As Byte
PoolType As Long
PagedPoolUsage As Long
NonPagedPoolUsage As Long
End Type
'information sur les attributs de handles
Private Type OBJECT_HANDLE_ATTRIBUTE_INFORMATION ' Information Class 4
Inherit As Byte
ProtectFromClose As Byte
End Type
'renvoie des informations sur le systeme
Private Declare Function NtQuerySystemInformation Lib "ntdll" ( _
ByVal SystemInformationClass As Long, _
ByVal SystemInformation As Long, _
ByVal SystemInformationLength As Long, _
ReturnLength As Long _
) As Long
'renvoie le handle du processus appelant (-1)
Private Declare Function GetCurrentProcess Lib "kernel32.dll" () As Long
'permet de dupliquer un handle d'un prosessus vers un autre
Private Declare Function DuplicateHandle Lib "kernel32.dll" (ByVal hSourceProcessHandle As Long, ByVal hSourceHandle As Long, ByVal hTargetProcessHandle As Long, ByRef lpTargetHandle As Long, ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwOptions As Long) As Long
'autorise à dupliquer un handle
Private Const PROCESS_DUP_HANDLE As Long = (&H40)
'avec le même accès que dans le processus original
Private Const DUPLICATE_SAME_ACCESS As Long = &H2
'ouvrir le processus
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessID As Long) As Long
'fermer un handle
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
'renvoie des infos sur un type d'objet
Private Declare Function NtQueryObject Lib "ntdll" ( _
ByVal ObjectHandle As Long, _
ByVal ObjectInformationClass As Long, _
ByVal ObjectInformation As Long, _
ByVal ObjectInformationLength As Long, _
ReturnLength As Long _
) As Long
'renvoie l'addresse de l'entête de tableau SAFEARRAY
Private Declare Function VarPtrArray Lib "msvbvm60.dll" Alias "VarPtr" (Arr() As Any) As Long
Private Type SafeArray1d
cDims As Integer 'nombre de dimension
fFeatures As Integer 'falgs
cbElements As Long 'taille des elements du tableau
cLocks As Long 'tableau non redimensionnable
pvData As Long 'pointeur vers les données du tableau
cElements As Long 'nombre d'elements
lLbound As Long 'limite basse
End Type
'copie une zone mémoire dans une autre
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
'attributs des entêtes de tableau SAFEARRAY
Private Const FADF_AUTO = 1
Private Const FADF_FIXEDSIZE = 16
Private Const FADF_STATIC = 2
'buffer contenant les infos sur les handles
Dim BufferHandles() As Byte
Dim Handles() As SYSTEM_HANDLE_INFORMATION
Dim ArrHandles As SafeArray1d
'contient des infos sur un objet
Dim ObjBasic As OBJECT_BASIC_INFORMATION
'renvoie le type d'un objet
Dim BufferObjType() As Byte
Dim ObjType() As OBJECT_TYPE_INFORMATION
Dim ArrObjType As SafeArray1d
Dim m_ObjectTypeName As String
'renvoie le nom d'un objet
Dim BufferObjName() As Byte
Dim ObjName() As OBJECT_NAME_INFORMATION
Dim ArrObjName As SafeArray1d
Dim m_ObjectName As String
'nombre de handles
Dim m_cHandles As Long
Dim hProcess As Long
'crée le buffer contenant les handles
'doit etre libérer avec DestroyHandlesBuffer avant de quitter l'application
Public Sub CreateQueryHandlesBuffer()
Dim Length As Long 'longueur du buffer
Dim X As Long 'compteur
Dim ret As Long 'valeur de retour des fonctions utilisées
Dim ret2 As Long 'valeur de retour des fonctions utilisées
Length = &H100& 'longueur minimale du buffer
ReDim BufferHandles(Length) 'redimensionne le buffer
'tant que la longueur n'est pas suffisante
Do While NtQuerySystemInformation( _
SystemHandleInformation, _
ByVal VarPtr(BufferHandles(0)), _
Length, _
ret _
) = STATUS_INFO_LENGTH_MISMATCH
'on multiplie la taille du buffer par 2
Length = Length * 2
'on réalloue le buffer
ReDim BufferHandles(Length)
Loop
'on réduit la taille du buffer à la taille des données contenus dans ce buffer
ReDim Preserve BufferHandles(ret + 1)
'demande le nombre de handles
CopyMemory ByVal VarPtr(m_cHandles), ByVal VarPtr(BufferHandles(0)), 4
'initilise le tableau des handles
With ArrHandles
.cbElements = 16 'taille des elements du tableau
.cDims = 1 '1 dimension
.cElements = m_cHandles 'nombre d'elements
.fFeatures = FADF_AUTO Or FADF_FIXEDSIZE 'flag
.lLbound = 0 'option base 0
'fait pointer le tableau sur les données du buffer
.pvData = VarPtr(BufferHandles(4))
End With
'copie l'entête dans le pointeur tableau
CopyMemory ByVal VarPtrArray(Handles), VarPtr(ArrHandles), 4
End Sub
'remplit un buffer avec les infos sur l'objet spécifié par l'index
'==============================================================
'lIndex : index du handle dans le tableau de handles
'hProcess : handle du processus dans lequel le handle se trouve (<> du PID)
Private Sub RetrieveObject(lIndex As Long, hProcess As Long)
Dim Length As Long 'longueur
Dim ret As Long 'valeur de retour des fonctions utilisées
Dim ret2 As Long 'valeur de retour des fonctions utilisées
Dim hHandle As Long 'handle dupliqué dans notre processus
' on copie le handle recherché dans notre processus avec les mêmes droits d'accès
DuplicateHandle hProcess, Handles(lIndex).Handle, _
GetCurrentProcess, hHandle, 0&, 0&, DUPLICATE_SAME_ACCESS
'renvoie des infos Basic sur l'objet
ret2 = NtQueryObject(hHandle, ObjectBasicInformation, VarPtr(ObjBasic), Len(ObjBasic), ret)
'alloue un buffer à la taille des données Type
ReDim BufferObjType(ObjBasic.TypeInformationLength + 2)
'initialise le descripteur de tableau Type
With ArrObjType
.cbElements = 94 'taille des elements du tableau
.cDims = 1 'dimension
.cElements = 1 'nombre d'elements
.fFeatures = FADF_AUTO Or FADF_FIXEDSIZE 'flag
.lLbound = 0 'option base 0
'fait pointer le tableau vers les données de Type
.pvData = VarPtr(BufferObjType(0))
End With
'demande les infos Type de l'objet
ret2 = NtQueryObject(hHandle, ObjectTypeInformation, VarPtr(BufferObjType(0)), ObjBasic.TypeInformationLength + 2, ret)
'copie le descripteur dans le pointeur de tableau
CopyMemory ByVal VarPtrArray(ObjType), VarPtr(ArrObjType), 4
'alloue l'espace pour le nom de l'objet
m_ObjectTypeName = Space$(ObjType(0).Name.Length \ 2)
'copie les données du nom dans le buffer alloué
CopyMemory ByVal StrPtr(m_ObjectTypeName), ByVal ObjType(0).Name.Buffer, ObjType(0).Name.Length
'si la longueur du nom est 0
If ObjBasic.NameInformationLength = 0 Then
'on la met à MAX_PATH (en UNICODE = MAX_PATH * 2)
'cela veut dire que le NtQueryObject ne sait la longueur de la chaine
Length = 512
Else
'si on longueur est spécifié on l'utilise
Length = ObjBasic.NameInformationLength + 2
End If
'on alloue le buffer pour le nom de l'object
ReDim BufferObjName(Length)
'on initialise le descripteur de tableau Name
With ArrObjName
.cbElements = 8 'taille des elements du tableau
.cDims = 1 '1 dimension
.cElements = 1 'nombre d'elements
.fFeatures = FADF_AUTO Or FADF_FIXEDSIZE 'flags
.lLbound = 0 'option base 0
'fait pointer vers le buffer Name
.pvData = VarPtr(BufferObjName(0))
End With
'on demande le nom de l'objet
ret2 = NtQueryObject(hHandle, ObjectNameInformation, VarPtr(BufferObjName(0)), Length, ret)
'on copie les descripteur dans le pointeur de tableau
CopyMemory ByVal VarPtrArray(ObjName), VarPtr(ArrObjName), 4
'convertit le nom en String VB
'alloue un buffer chaine
m_ObjectName = Space$(ObjName(0).Name.Length \ 2)
'copie les données UNICODE du nom dans le buffer chaine
CopyMemory ByVal StrPtr(m_ObjectName), ByVal ObjName(0).Name.Buffer, ObjName(0).Name.Length
' on ferme la copie du handle recherché
CloseHandle hHandle
End Sub
'renvoie le nombre de handles pour le proicessus spécifié
'========================================================
'PID : Process ID du processus pour lequel on veut le nombre de handle
Public Function GetCountOfHandlesForProcessID(pid As Long) As Long
Dim X As Long 'compteur
'initielise
GetCountOfHandlesForProcessID = 0
'pour chaque handle du tableau
For X = 0 To m_cHandles - 1
'si le handle appartient au processus, on ajoute 1
If Handles(X).ProcessId = pid Then GetCountOfHandlesForProcessID = GetCountOfHandlesForProcessID + 1
Next
End Function
'renvoie les infos sur le premier handle du processus spécifié
'==============================================================
'IN PID : Process ID du processus pour lequel on demande les infos du premier handle
'OUT retHandle : contient des infos sur le premier handle du processus
'renvoie 0 si succès, -1 si pas de handle pour ce processus
Public Function GetHandlesForProcessIDFirst(pid As Long, retHandle As HandleInfo) As Long
Dim X As Long 'compteur
'si le handle de processus n'est pas ouvert pour le processus pid
If hProcess = 0 Then
'on ouvre le processus demandé pour dupliquer des handles
hProcess = OpenProcess(PROCESS_DUP_HANDLE, 0&, pid)
End If
'pour chaque handle
For X = 0 To m_cHandles - 1
'si le handle appartient au processus
If Handles(X).ProcessId = pid Then
'rmplit les buffer avec des informations sur le handle
RetrieveObject X, hProcess
'renvoie les données des infos du handle
With retHandle
.Attributes = ObjBasic.Attributes
.CreateTime = ObjBasic.CreateTime
.Flags = Handles(X).Flags
.GenericAll = ObjType(0).GenericMapping.GenericAll
.GenericExecute = ObjType(0).GenericMapping.GenericExecute
.GenericRead = ObjType(0).GenericMapping.GenericRead
.GenericWrite = ObjType(0).GenericMapping.GenericWrite
.GrantedAccess = Handles(X).GrantedAccess
.Handle = Handles(X).Handle
.HandleCount = ObjType(0).HandleCount
.InvalidAttributes = ObjType(0).InvalidAttributes
.MaintainHandleDatabase = ObjType(0).MaintainHandleDatabase
.NameInformation = m_ObjectTypeName
.NonPagedPoolUsage = ObjType(0).NonPagedPoolUsage
.Object = Handles(X).Object
.ObjectCount = ObjType(0).ObjectCount
.ObjectName = m_ObjectName
.PagedPoolUsage = ObjType(0).PagedPoolUsage
.PeakHandleCount = ObjType(0).PeakHandleCount
.PeakObjectCount = ObjType(0).PeakObjectCount
.PointerCount = ObjBasic.PointerCount
.PoolType = ObjType(0).PoolType
.ProcessId = Handles(X).ProcessId
.Unknown = ObjType(0).Unknown
.ValidAccess = ObjType(0).ValidAccess
End With
'renvoie SUCCESS
GetHandlesForProcessIDFirst = 0
Exit Function
End If
Next
'renvoie pas de handle pour le processus
GetHandlesForProcessIDFirst = -1
End Function
'ferme le handle du processus
'à appeler dès que l'on a plus besoin de l'énumération de handles
'==============================================================
'renvoie le résultat de CloseHandle
Public Function GetHandlesForProcessIDClose() As Long
'on ferme le handle
GetHandlesForProcessIDClose = CloseHandle(hProcess)
'on indique qu'il est fermé
hProcess = 0
End Function
'renvoie les infos sur le handle suivant du processus spécifié
'==============================================================
'IN PID : Process ID du processus pour lequel on demande les infos du premier handle
'IN Handle : handle précédent
'OUT retHandle : contient des infos sur le premier handle du processus
'renvoie 0 si succès, -1 si pas de handle pour ce processus
Public Function GetHandlesForProcessIDNext(pid As Long, Handle As Long, retHandle As HandleInfo) As Long
Dim X As Long 'compteur
Dim Before As Boolean 'indique si on a trouvé le handle précédent
Before = False
'pour chaque handle
For X = 0 To m_cHandles - 1
'si on a trouvé le handle précédent et que le handle appartient au processus
If (Handles(X).ProcessId = pid) And (Before = True) Then
'on remplit les buffer avec les informations sur le handle
RetrieveObject X, hProcess
'on revnoie les infos sur le handle
With retHandle
.Attributes = ObjBasic.Attributes
.CreateTime = ObjBasic.CreateTime
.Flags = Handles(X).Flags
.GenericAll = ObjType(0).GenericMapping.GenericAll
.GenericExecute = ObjType(0).GenericMapping.GenericExecute
.GenericRead = ObjType(0).GenericMapping.GenericRead
.GenericWrite = ObjType(0).GenericMapping.GenericWrite
.GrantedAccess = Handles(X).GrantedAccess
.Handle = Handles(X).Handle
.HandleCount = ObjType(0).HandleCount
.InvalidAttributes = ObjType(0).InvalidAttributes
.MaintainHandleDatabase = ObjType(0).MaintainHandleDatabase
.NameInformation = m_ObjectTypeName
.NonPagedPoolUsage = ObjType(0).NonPagedPoolUsage
.Object = Handles(X).Object
.ObjectCount = ObjType(0).ObjectCount
.ObjectName = m_ObjectName
.PagedPoolUsage = ObjType(0).PagedPoolUsage
.PeakHandleCount = ObjType(0).PeakHandleCount
.PeakObjectCount = ObjType(0).PeakObjectCount
.PointerCount = ObjBasic.PointerCount
.PoolType = ObjType(0).PoolType
.ProcessId = Handles(X).ProcessId
.Unknown = ObjType(0).Unknown
.ValidAccess = ObjType(0).ValidAccess
End With
'on renvoie SUCCES
GetHandlesForProcessIDNext = 0
Exit Function
End If
'si on a trouvé le handle précédent on l'indique
If (Handles(X).ProcessId = pid) And (Handles(X).Handle = Handle) Then Before = True
Next
'on renvoie pas de handle suivant
GetHandlesForProcessIDNext = -1
'on ferme le processus
CloseHandle hProcess
hProcess = 0
End Function
'libère les buffers des tableaus alloué pour eviter le plantages
Public Sub DestroyHandlesBuffer()
With ArrHandles
.cbElements = 0
.cDims = 0
.pvData = 0
End With
With ArrObjType
.cbElements = 0
.cDims = 0
.pvData = 0
End With
With ArrObjName
.cbElements = 0
.cDims = 0
.pvData = 0
End With
End Sub
Conclusion :
Pour plus d'infos sur les API Native de Windows NT/2000/XP, regarder le livre "Windows NT/2000 NATIVE API Reference" de Gary Nebbett
Ce code ne fonctionne pas sous 9x/ME.
Certain handle sont nommés, d'autres pas...
N'hésitez pas à commenter et à noter...
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.