Lister les handles (fichiers, clé de registres,...) ouverts par un programme (nt/2000/xp)

Description

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...

Codes Sources

A voir également

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.