Funciones relacionadas a procesos, hilos (threads) y relacionado

Esta sección es para compartir scripts, librerías y herramientas.

Moderator: Flipeador

User avatar
Flipeador
Posts: 1126
Joined: 15 Nov 2014, 21:31
GitHub: Flipeador
Location: Argentina
Contact:

Funciones relacionadas a procesos, hilos (threads) y relacionado

12 Jul 2019, 17:04

Una aplicación consta de uno o más procesos. Un proceso, en los términos más simples, es un programa en ejecución. Uno o más hilos (threads) se ejecutan en el contexto del proceso. Un hilo es la unidad básica a la que el sistema operativo asigna el tiempo del procesador. Un hilo puede ejecutar cualquier parte del código del proceso, incluyendo las partes que están siendo ejecutadas por otro hilo.

Cuando se ejecuta un programa, el sistema operativo crea un proceso con un hilo y otros recursos, e inicia ese hilo ejecutando el código deseado en un punto de entrada particular en el código. A partir de ese momento, el programa tiene la responsabilidad de lo que hace en términos de las funciones que llama o de los objetos que crea. Incluso puede enviar mensajes al sistema operativo para pedirle que inicie más subprocesos, o incluso procesos completamente separados, con parámetros especificados por el código original.

Cada proceso proporciona los recursos necesarios para ejecutar un programa. Un proceso tiene un espacio de direcciones virtual, código ejecutable, manipuladores (handles) abiertos para los objetos del sistema, un contexto de seguridad, un identificador único del proceso, variables de entorno, una clase de prioridad, tamaños mínimos y máximos de los conjuntos de trabajo, y al menos un hilo de ejecución. Cada proceso se inicia con un solo hilo, a menudo llamado hilo principal, pero puede crear hilos adicionales a partir de cualquiera de sus hilos.
Un proceso hijo es un proceso creado por otro proceso, llamado proceso padre.


Un hilo es la entidad dentro de un proceso que se puede programar para su ejecución. Todos los hilos de un proceso comparten su espacio de direcciones virtual y recursos del sistema. Además, cada hilo mantiene manejadores de excepciones, una prioridad de programación, almacenamiento local de hilos, un identificador único de hilos y un conjunto de estructuras que el sistema utilizará para guardar el contexto del hilo hasta que se programe. Los hilos también pueden tener su propio contexto de seguridad, que puede utilizarse para hacerse pasar por clientes.



En esta publicación voy a estar poniendo en los comentarios códigos relacionados a procesos, hilos (threads) y relacionado.
Requerimientos:

  • AutoHotkey versión 2 (Unicode).
  • SO: Windows 7/8/8.1/10. Si bien el codigo podría funcionar en otras versiones de Windows, solo me interesa la compatibilidad a partir de Windows 7, en especial Windows 10.

Si algun código deja de funcionar correctamente, dejar un comentario en este tema para ver si lo podemos solucionar.
Todo el código aquí mostrado está probado únicamente en Windows 10 Versión 1903 usando AutoHotkey Versión 2.0-a103-56441b52.
Los códigos de error utilizados pueden ser: WIN32 o NTSTATUS.



Índice
User avatar
Flipeador
Posts: 1126
Joined: 15 Nov 2014, 21:31
GitHub: Flipeador
Location: Argentina
Contact:

Re: Funciones relacionadas a procesos, hilos (threads) y relacionado

13 Jul 2019, 20:29

A continuación, les voy a mostrar diferentes formas en la que podemos obtener una lista con información de todos los procesos activos en el sistema. Pueden usar cualquiera, la que escojan dependerá del uso que se le vaya a dar, algunas son mas lentas pero proveen más información, otras mas rápidas pero proveen menos información o información y funcionalidad diferente.


Usando las funciones CreateToolhelp32Snapshot, Process32First y Process32Next, podemos recuperar cierta información de todos los procesos locales en el sistema. Estas son las funciones que utiliza AutoHotkey para enumerar los procesos, por ejemplo, en la función ProcessExist.

Code: Select all

/*
    Retrieves a list with information for each process object running in the system.
    Return value:
        If the function succeeds, the return value is an array of associative objects (Map) with the following keys:
            ProcessName        The name of the executable file associated with the process (its process's image name).
            ProcessId          The process identifier that uniquely identifies the process.
            ParentProcessId    The identifier of the process that created this process (its parent process).
            BasePriority       The base priority of the process, which is the starting priority for threads created within the associated process.
            ThreadCount        The number of execution threads started by the process.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (WIN32).
*/
ProcessGetList()
{
    local hSnapshot := DLLCall("Kernel32.dll\CreateToolhelp32Snapshot", "UInt", 0x00000002  ; DWORD dwFlags. TH32CS_SNAPPROCESS.
                                                                      , "UInt", 0           ; DWORD th32ProcessID.
                                                                      , "Ptr")              ; HANDLE.
    if (hSnapshot == -1)  ; INVALID_HANDLE_VALUE == ((HANDLE)(LONG_PTR)-1).
        return 0

    local ProcessList    := [ ]                                ; An array of associative objects containing information from each process.
    local PROCESSENTRY32 := BufferAlloc(A_PtrSize==4?556:568)  ; https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/ns-tlhelp32-tagprocessentry32.
    NumPut("UInt", PROCESSENTRY32.Size, PROCESSENTRY32)        ; DWORD dwSize (sizeof PROCESSENTRY32 structure).

    if DllCall("Kernel32.dll\Process32FirstW", "Ptr", hSnapshot, "Ptr", PROCESSENTRY32)
    {
        local Ptr := { cntThreads         : PROCESSENTRY32.Ptr + (A_PtrSize==4?20:28)
                     , th32ParentProcessID: PROCESSENTRY32.Ptr + (A_PtrSize==4?24:32)
                     , pcPriClassBase     : PROCESSENTRY32.Ptr + (A_PtrSize==4?28:36)
                     , szExeFile          : PROCESSENTRY32.Ptr + (A_PtrSize==4?36:44) }
        loop
        {
            ProcessList.Push( { ProcessId      : NumGet(PROCESSENTRY32         ,   "UInt")      ; DWORD th32ProcessID.
                              , ThreadCount    : NumGet(Ptr.cntThreads         ,   "UInt")      ; DWORD cntThreads.
                              , ParentProcessId: NumGet(Ptr.th32ParentProcessID,   "UInt")      ; DWORD th32ParentProcessID.
                              , BasePriority   : NumGet(Ptr.pcPriClassBase     ,   "UInt")      ; LONG  pcPriClassBase.
                              , ProcessName    : StrGet(Ptr.szExeFile          , "UTF-16") } )  ; CHAR  szExeFile[MAX_PATH].
        }
        until !DllCall("Kernel32.dll\Process32NextW", "Ptr", hSnapshot, "Ptr", PROCESSENTRY32)
    }

    DllCall("Kernel32.dll\CloseHandle", "Ptr", hSnapshot)
    return ProcessList.Length() ? ProcessList : 0
} ; https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot

Code: Select all

List := ""
TmpFile := A_Temp . "\~ahktmp.txt"  ; Archivo temporal.

for Each, Item in ProcessGetList()
    List .= Format("ProcessId:{9}{1}`nThreads:{9}{2}`nParentProcessId:{7}{3}`nBasePriority:{8}{4}`nExeFile:{9}{5}{6}"
                 ,   Item.ProcessId,  Item.ThreadCount,  Item.ParentProcessId,  Item.BasePriority,   Item.ProcessName
                 ,   "`n-----------------------------------------------------------`n",   "`t",   "`t`t",   "`t`t`t")

FileOpen(TmpFile, "w-wd", "UTF-8").Write(List)
Run(TmpFile)


Usando la función EnumProcesses, podemos recuperar únicamente el identificador de cada proceso local. Esta es la función menos util de todas. Para recuperar información adicional debemos hacer llamadas a otras funciones con DllCall.

Code: Select all

/*
    Retrieves the process identifier for each process object running in the system.
    Parameters:
        Max:
            The maximum number of processes to retrieve. By default it retrieves the first 500.
    Return value:
        If the function succeeds, the return value is an array of process identifiers (may be empty).
        If the function fails, the return value is zero. To get extended error information, check A_LastError (WIN32).
*/
ProcessEnum(Max := 500)
{
    local Buffer := BufferAlloc(4*Max), Size := 0, Processes := [ ]
    if !DllCall("Psapi.dll\EnumProcesses", "Ptr", Buffer, "UInt", Buffer.Size, "UIntP", Size)
        return 0
    loop (Size // 4)
        Processes.Push( NumGet(Buffer,4*(A_Index-1),"UInt") )
    return Processes
} ; https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-enumprocesses

Code: Select all

List    := ""
TmpFile := A_Temp . "\~ahktmp.txt"  ; Archivo temporal.

for Each, ProcessId in ProcessEnum()
    List .= "ProcessId:`s`s" . ProcessId . "`n---------------------------------`n"

FileOpen(TmpFile, "w-wd", "UTF-8").Write(List)
Run(TmpFile)


Usando la función WTSEnumerateProcessesExW, podemos recuperar información sobre los procesos activos en el servidor especificado de Host de sesión de Escritorio Remoto (RD Session Host) o en el servidor de Host de Virtualización de Escritorio Remoto (RD Virtualization Host), ver Remote Desktop Services.

Code: Select all

/*
    Retrieves information about the active processes on the specified Remote Desktop Session Host (RD Session Host) server or Remote Desktop Virtualization Host (RD Virtualization Host) server.
    Parameters:
        Server:
            A handle to an Remote Desktop Session Host server.
            To indicate the server on which your application is running, specify 0 (WTS_CURRENT_SERVER_HANDLE).
        SessionId:
            The session for which to enumerate processes.
            To enumerate processes for all sessions on the server, specify -2 (WTS_ANY_SESSION).
    Return value:
        If the function succeeds, the return value is an array of associative objects (Map) with the following keys:
            ProcessName           The name of the executable file associated with the process (its process's image name).
            ProcessId             The process identifier that uniquely identifies the process on the RD Session Host server.
            ThreadCount           The number of execution threads started by the process.
            HandleCount           The total number of handles being used by the process in question.
            SessionId             The Remote Desktop Services session identifier for the session associated with the process.
            UserSid               The user security identifier (SID) in the primary access token of the process (Buffer object).
            WorkingSetSize        The size, in bytes, of the current working set of the process.
            PeakWorkingSetSize    The peak size, in bytes, of the working set of the process.
            PagefileUsage         The number of bytes of page file storage in use by the process.
            PeakPagefileUsage     The maximum number of bytes of page-file storage used by the process.
            UserTime              The amount of time, in milliseconds, the process has been running in user mode.
            KernelTime            The amount of time, in milliseconds, the process has been running in kernel mode.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (WIN32).
    Remarks:
        The caller must be a member of the Administrators group to enumerate processes that are running under another user session.
*/
WTSProcessEnum(Server := 0, SessionId := -2)
{
    local 

    if !DllCall("Wtsapi32.dll\WTSEnumerateProcessesExW",  "UPtr", IsObject(Server) ? Server.Handle : Server  ; HANDLE hServer,
                                                       , "UIntP", Level := 1                                 ; DWORD  *pLevel. WTS_PROCESS_INFO_EX.
                                                       ,  "UInt", SessionId                                  ; DWORD  SessionId.
                                                       , "UPtrP", pWTS_PROCESS_INFO_EX := 0                  ; LPWSTR *ppProcessInfo.
                                                       , "UIntP", Count := 0)                                ; DWORD  *pCount.
        return 0

    ProcessList := [ ]                   ; An array of associative objects containing information from each process.
    Ptr         := pWTS_PROCESS_INFO_EX  ; An array of WTS_PROCESS_INFO_EX structures.
    loop (Count)  ; The number of WTS_PROCESS_INFO_EX structures returned.
    {
        if (pUserSid := NumGet(Ptr,8+A_PtrSize))
            DllCall("Advapi32.dll\CopySid", "UInt", SidSize := DllCall("Advapi32.dll\GetLengthSid", "Ptr", pUserSid)
                                          ,  "Ptr", UserSid := BufferAlloc(SidSize)
                                          , "UPtr", pUserSid)
        ProcessList.Push( { SessionId         : NumGet(Ptr, 0, "UInt")                     ; DWORD         SessionId.
                          , ProcessId         : NumGet(Ptr, 4, "UInt")                     ; DWORD         ProcessId.
                          , ProcessName       : StrGet(NumGet(Ptr,8))                      ; LPSTR         pProcessName.
                          , UserSid           : pUserSid ? UserSid : 0                     ; PSID          pUserSid.
                          , ThreadCount       : NumGet(Ptr, 8+2*A_PtrSize, "UInt")         ; DWORD         NumberOfThreads.
                          , HandleCount       : NumGet(Ptr, 12+2*A_PtrSize, "UInt")        ; DWORD         HandleCount.
                          , PagefileUsage     : NumGet(Ptr, 16+2*A_PtrSize, "UInt")        ; DWORD         PagefileUsage.
                          , PeakPagefileUsage : NumGet(Ptr, 20+2*A_PtrSize, "UInt")        ; DWORD         PeakPagefileUsage.
                          , WorkingSetSize    : NumGet(Ptr, 24+2*A_PtrSize, "UInt")        ; DWORD         WorkingSetSize.
                          , PeakWorkingSetSize: NumGet(Ptr, 28+2*A_PtrSize, "UInt")        ; DWORD         PeakWorkingSetSize.
                          , UserTime          : NumGet(Ptr, 32+2*A_PtrSize, "UInt64")      ; LARGE_INTEGER UserTime.
                          , KernelTime        : NumGet(Ptr, 40+2*A_PtrSize, "UInt64") } )  ; LARGE_INTEGER KernelTime.
        Ptr += 48 + 2*A_PtrSize  ; Next WTS_PROCESS_INFO_EX structure.
    }

    DllCall("Wtsapi32.dll\WTSFreeMemoryExW", "Int", 1, "Ptr", pWTS_PROCESS_INFO_EX, "UInt", Count)
    return ProcessList
} ; https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtsenumerateprocessesexw

Code: Select all

List     := ""
TmpFile := A_Temp . "\~ahktmp.txt"  ; Archivo temporal.

for Each, Item in WTSProcessEnum()
    List .= Format("SessionId:{13}{1}`nProcessId:{13}{2}`nProcessName:{12}{3}`nNumberOfThreads:{11}{4}"
                . "`nHandleCount:{12}{5}`nPagefileUsage:{12}{6}{14}`nPeakPagefileUsage:`t{7}{14}"
                . "`nWorkingSetSize:{12}{8}{14}`nPeakWorkingSetSize:{11}{9}{14}{10}"
                 , Item.SessionId, Item.ProcessId, Item.ProcessName, Item.ThreadCount, Item.HandleCount
                 , Item.PagefileUsage, Item.PeakPagefileUsage, Item.WorkingSetSize, Item.PeakWorkingSetSize
                 , "`n-----------------------------------------------`n", "`t", "`t`t", "`t`t`t", "`sBytes")

FileOpen(TmpFile, "w-wd", "UTF-8").Write(List)
Run(TmpFile)


Usando la clase WMI Win32_Process, podemos recuperar información variada sobre los procesos. Usar WMI puede resultar muy lento, además de consumir más recursos, es por eso que siempre que sea posible, lo mejor es utilizar DllCall. Además de poder recuperar información, WMI nos provee ciertos métodos: AttachDebugger, Create, GetAvailableVirtualSize, GetOwner, GetOwnerSid, SetPriority, y Terminate.

Code: Select all

List   := ""
TmpFile := A_Temp . "\~ahktmp.txt"  ; Archivo temporal.

User   := ComVar()
Domain := ComVar()

for Item in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
{
    Item.GetOwner(User.ref, Domain.ref)
    List .= Format("ProcessName:{6}{1}`nProcessId:{7}{2}`nUserName:{7}{3}`nUserDomain:{7}{4}{5}"
                 , Item.Name, Item.Handle, User[], Domain[]
                 , "`n-------------------------------------`n", "`t", "`t`t")
}

FileOpen(TmpFile, "w-wd", "UTF-8").Write(List)
Run(TmpFile)


; --------------------------------------------------------------------------------
; --------------------------------------------------------------------------------


ComVar(Type := 0xC)
{
    return new Class_ComVar(Type)
}

class Class_ComVar
{
    __New(Type)
    {
        this.arr := ComObjArray(Type, 1)

        ; SafeArrayAccessData function.
        ; https://docs.microsoft.com/en-us/windows/desktop/api/oleauto/nf-oleauto-safearrayaccessdata.
        local pData := 0
        DllCall("OleAut32.dll\SafeArrayAccessData", "Ptr", ComObjValue(this.arr), "PtrP", pData)

        this.ref := ComObject(0x4000|Type, pData)
    }

    __Delete()
    {
        ; SafeArrayUnaccessData function.
        ; https://docs.microsoft.com/en-us/windows/desktop/api/oleauto/nf-oleauto-safearrayunaccessdata.
        DllCall("OleAut32.dll\SafeArrayUnaccessData", "Ptr", ComObjValue(this.arr))
    }

    __Item[]
    {
        get => this.arr[0]
        set => this.arr[0] := Value
    }
}


Usando la función NtQuerySystemInformation, podemos recuperar más información que las otras variantes DllCall. Esta forma de recuperar información de los procesos tal vez sea la menos conocida. Segun Microsoft, puede ser alterada o no disponible en futuras versiones de Windows.

Code: Select all

/*
    Retrieves a list with information for each process object running in the system.
    Return value:
        If the function succeeds, the return value is an array of associative objects (Map) with the following keys:
            ProcessName               The name of the executable file associated with the process (its process's image name).
            ProcessId                 The process identifier that uniquely identifies the process.
            ParentProcessId           The identifier of the process that created this process (its parent process).
            BasePriority              The base priority of the process, which is the starting priority for threads created within the associated process.
            ThreadCount               The number of execution threads started by the process.
            HandleCount               The total number of handles being used by the process in question.
            SessionId                 The session identifier for the session associated with the process.
            WorkingSetSize            The size, in bytes, of the current working set of the process.
            PeakWorkingSetSize        The peak size, in bytes, of the working set of the process.
            VirtualSize               The current size, in bytes, of virtual memory used by the process.
            PeakVirtualSize           The peak size, in bytes, of the virtual memory used by the process.
            PagefileUsage             The number of bytes of page file storage in use by the process.
            PeakPagefileUsage         The maximum number of bytes of page-file storage used by the process.
            PrivatePageCount          The number of memory pages allocated for the use of this process.
            QuotaPagedPoolUsage       The current quota charged to the process for paged pool usage, in bytes.
            QuotaNonPagedPoolUsage    The current quota charged to the process for nonpaged pool usage, in bytes.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
*/
ProcessGetList2()
{
    local Buffer   := BufferAlloc(1000000)  ; 1MB - I think it's enough space, we avoid two calls to NtQuerySystemInformation.
    local NtStatus := DllCall("Ntdll.dll\NtQuerySystemInformation", "Int", 5, "Ptr", Buffer, "UInt", Buffer.Size, "UIntP", 0, "UInt")

    if (NtStatus == 0)  ; STATUS_SUCCESS = 0.
    {
        local ProcessList     := []          ; An array of associative objects containing information from each process.
        local Ptr             := Buffer.Ptr  ; Pointer to structure SYSTEM_PROCESS_INFORMATION / SYSTEM_PROCESSES_INFORMATION (same data).
        local NextEntryOffset := 0           ; The start of the next item in the array is the address of the previous item plus the value in the NextEntryOffset member.

        loop
        {
            ProcessList.Push( { ThreadCount           : NumGet(Ptr, 4, "UInt")                                       ; ULONG     NumberOfThreads.
                              , ProcessName           : StrGet(NumGet(Ptr,56+A_PtrSize),NumGet(Ptr+56,"UShort")//2)  ; ImageName.Buffer (UNICODE_STRING structure).
                              , BasePriority          : NumGet(Ptr, 56+2*A_PtrSize, "UInt")                          ; KPRIORITY BasePriority.
                              , ProcessId             : NumGet(Ptr, 56+3*A_PtrSize, "UPtr")                          ; HANDLE    UniqueProcessId.
                              , ParentProcessId       : NumGet(Ptr, 56+4*A_PtrSize, "UPtr")                          ; PVOID     Reserved2/InheritedFromUniqueProcessId.
                              , HandleCount           : NumGet(Ptr, 56+5*A_PtrSize, "UInt")                          ; ULONG     HandleCount.
                              , SessionId             : NumGet(Ptr, 60+5*A_PtrSize, "UInt")                          ; ULONG     SessionId.
                              , PeakVirtualSize       : NumGet(Ptr, 64+6*A_PtrSize, "UPtr")                          ; SIZE_T    PeakVirtualSize.
                              , VirtualSize           : NumGet(Ptr, 64+7*A_PtrSize, "UPtr")                          ; SIZE_T    VirtualSize.
                              , PeakWorkingSetSize    : NumGet(Ptr, 64+9*A_PtrSize, "UPtr")                          ; SIZE_T    PeakWorkingSetSize.
                              , WorkingSetSize        : NumGet(Ptr, 64+10*A_PtrSize, "UPtr")                         ; SIZE_T    WorkingSetSize.
                              , QuotaPagedPoolUsage   : NumGet(Ptr, 64+12*A_PtrSize, "UPtr")                         ; SIZE_T    QuotaPagedPoolUsage.
                              , QuotaNonPagedPoolUsage: NumGet(Ptr, 64+14*A_PtrSize, "UPtr")                         ; SIZE_T    QuotaNonPagedPoolUsage.
                              , PagefileUsage         : NumGet(Ptr, 64+15*A_PtrSize, "UPtr")                         ; SIZE_T    PagefileUsage.
                              , PeakPagefileUsage     : NumGet(Ptr, 64+16*A_PtrSize, "UPtr")                         ; SIZE_T    PeakPagefileUsage.
                              , PrivatePageCount      : NumGet(Ptr, 64+17*A_PtrSize, "UPtr") } )                     ; SIZE_T    PrivatePageCount.
        }
        until (  (Ptr += (NextEntryOffset:=NumGet(Ptr,"UInt")))  ==  (Ptr-NextEntryOffset)  )                        ; ULONG     NextEntryOffset.
    }

    return (A_LastError := NtStatus) ? 0 : ProcessList
} ; https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation

Code: Select all

List := ""
TmpFile := A_Temp . "\~ahktmp.txt"  ; Archivo temporal.

for Each, Item in ProcessGetList2()
    List .= Format("NumberOfThreads:{18}{1}`nImageName:{20}{2}`nBasePriority:{19}{3}`nProcessId:{20}{4}`nParentProcessId:{18}{5}"
                 . "`nHandleCount:{19}{6}`nSessionId:{20}{7}`nPeakVirtualSize:{18}{8}`nVirtualSize:{19}{9}`nPeakWorkingSetSize:{18}{10}"
                 . "`nWorkingSetSize:{19}{11}`nQuotaPagedPoolUsage:`t{12}`nQuotaNonPagedPoolUsage:`t{13}`nPagefileUsage:{19}{14}"
                 . "`nPeakPagefileUsage:{18}{15}`nPrivatePageCount:{18}{16}{17}"
                 , Item.ThreadCount, Item.ProcessName, Item.BasePriority, Item.ProcessId, Item.ParentProcessId
                 , Item.HandleCount, Item.SessionId, Item.PeakVirtualSize, Item.VirtualSize, Item.PeakWorkingSetSize
                 , Item.WorkingSetSize, Item.QuotaPagedPoolUsage, Item.QuotaNonPagedPoolUsage, Item.PagefileUsage
                 , Item.PeakPagefileUsage, Item.PrivatePageCount
                 , "`n-------------------------------------------------------`n", "`t`t", "`t`t`t", "`t`t`t`t")

FileOpen(TmpFile, "w-wd", "UTF-8").Write(List)
Run(TmpFile)


Usando la función no documentada NtGetNextProcess. Este es solo un ejemplo, esta función NO debe usarse para enumerar todos los procesos, ya que abre un manipulador cada vez que es llamada, y esto requiere de permisos. Para este ejemplo, debe incluir el primer script de ésta publicación (ProcessOpen, ProcessGetID y ProcessGetNext).

Code: Select all

List    := ""
TmpFile := A_Temp . "\~ahktmp.txt"
Process := 0
while Process := ProcessGetNext(Process, 0x001000)
    List .= "ProcessId:`s" . ProcessGetID(Process) . "`n"
FileOpen(TmpFile, "w-wd", "UTF-8").Write(List)
Run(TmpFile)
User avatar
Flipeador
Posts: 1126
Joined: 15 Nov 2014, 21:31
GitHub: Flipeador
Location: Argentina
Contact:

Re: Las distintas maneras de enumerar los procesos en el sistema

13 Jul 2019, 20:48

Abrir un proceso.

Code: Select all

/*
    Opens a handle to an existing local process object and sets the access rights to this object.
    Parameters:
        Process:
            The identifier or name of the local process to be opened.
            If the specified process is the System Process (0), the function fails and the last error code is 0x00000057 (ERROR_INVALID_PARAMETER).
            If the specified process is the Idle process or one of the CSRSS processes, this function fails and the last error code is 0x00000005 (ERROR_ACCESS_DENIED) because their access restrictions prevent user-level code from opening them.
            If the specified process is the current process (-1), a "real" handle to itself is created.
        DesiredAccess:
            The access to the process object.
            This access right is checked against the security descriptor for the process.
            This parameter can be one or more of the process access rights: https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights.
            If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the contents of the security descriptor.
            0x1F0FFF    PROCESS_ALL_ACCESS
            0x000040    PROCESS_DUP_HANDLE
            0x000400    PROCESS_QUERY_INFORMATION
            0x001000    PROCESS_QUERY_LIMITED_INFORMATION
            0x000200    PROCESS_SET_INFORMATION
            0x000100    PROCESS_SET_QUOTA
            0x000800    PROCESS_SUSPEND_RESUME
            0x000001    PROCESS_TERMINATE
            0x000008    PROCESS_VM_OPERATION
            0x000010    PROCESS_VM_READ
            0x000020    PROCESS_VM_WRITE
            0x100000    SYNCHRONIZE
        Attributes:
            0x00   The processes created by this process do not inherit the handle.
            0x02   The processes created by this process will inherit the handle.
    Return value:
        If the function succeeds, the return value is an IProcess class object instance.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
    Remarks:
        To open a handle to another local process and obtain full access rights, you must enable the SeDebugPrivilege privilege.
        When all references to the returned object are released, the process handle will be closed.
*/
ProcessOpen(Process := -1, DesiredAccess := 0x1F0FFF, Attributes := 0)
{
    Process := Process       ==        -1 ? ProcessExist()  ; Current process.
             : Type(Process) == "Integer" ? Process         ; Process identifier.
             : ProcessExist(Process)                        ; Process name.

    return new IProcess(Process, DesiredAccess, Attributes)
}





class IProcess
{
    ; ===================================================================================================================
    ; INSTANCE VARIABLES
    ; ===================================================================================================================
    Handle      := 0
    CloseHandle := TRUE


    ; ===================================================================================================================
    ; CONSTRUCTOR
    ; ===================================================================================================================
    __New(ProcessId, DesiredAccess, Attributes := 0, SecurityDescriptor := 0)
    {
        local OBJECT_ATTRIBUTES := BufferAlloc(6*A_PtrSize)  ; https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes.
        NumPut("UInt", OBJECT_ATTRIBUTES.Size, OBJECT_ATTRIBUTES)  ; ULONG           Length.
        NumPut("UPtr", 0                                           ; HANDLE          RootDirectory.
             , "UPtr", 0                                           ; PUNICODE_STRING ObjectName.
             , "UInt", Attributes, OBJECT_ATTRIBUTES, A_PtrSize)   ; ULONG           Attributes. OBJ_INHERIT = 2 (InheritHandle).
        NumPut("UPtr", SecurityDescriptor                          ; PVOID           SecurityDescriptor.
             , "UPtr", 0, OBJECT_ATTRIBUTES, 4*A_PtrSize)          ; PVOID           SecurityQualityOfService.
             
        local CLIENT_ID := BufferAlloc(2*A_PtrSize)  ; https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tsts/a11e7129-685b-4535-8d37-21d4596ac057.
        NumPut("UPtr", ProcessId      ; HANDLE UniqueProcess.
             , "UPtr", 0, CLIENT_ID)  ; HANDLE UniqueThread.

        local hProcess := 0  ;  Unique process identifier.
        local NtStatus := DllCall("Ntdll.dll\NtOpenProcess", "UPtrP", hProcess               ; (out)PHANDLE       ProcessHandle.
                                                           ,  "UInt", DesiredAccess          ; ACCESS_MASK        DesiredAccess.
                                                           ,  "UPtr", OBJECT_ATTRIBUTES.Ptr  ; POBJECT_ATTRIBUTES ObjectAttributes.
                                                           ,  "UPtr", CLIENT_ID.Ptr          ; PCLIENT_ID         ClientId.
                                                           ,  "UInt")                        ; NTSYSAPI NTSTATUS  NTAPI.

        if (hProcess == 0)
            return !(A_LastError := NtStatus)
        this.Handle := hProcess
    } ; https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntddk/nf-ntddk-ntopenprocess


    ; ===================================================================================================================
    ; NESTED CLASSES
    ; ===================================================================================================================
    class FromHandle extends IProcess
    {
        ; ===================================================================================================================
        ; CONSTRUCTOR
        ; =================================================================================================================== 
        __New(hProcess)
        {
            ; Checks if the handle is valid.
            this.Handle := ProcessCheckHandle(hProcess)
            if (this.Handle == 0)
                return 0
        }
    }


    ; ===================================================================================================================
    ; DESTRUCTOR
    ; ===================================================================================================================
    __Delete()
    {
        if (this.Handle && this.CloseHandle)
            DllCall("Kernel32.dll\CloseHandle", "Ptr", this.Handle)
    } ; https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle


    ; ===================================================================================================================
    ; PUBLIC METHODS
    ; ===================================================================================================================
    /*
        Retrieves the termination status of this process.
        The handle must have the PROCESS_QUERY_LIMITED_INFORMATION access right.
        Return value:
            If the function succeeds, the return value is the process termination status.
            If the function fails, the return value is <0. To get extended error information, check A_LastError (WIN32).
    */
    GetExitCode()
    {
        local ExitCode := 0
        return DllCall("Kernel32.dll\GetExitCodeProcess", "Ptr", this.Handle, "UIntP", ExitCode)
             ? ExitCode : -A_LastError
    } ; https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess

    /*
        Terminates this process and all of its threads.
        The handle must have the PROCESS_TERMINATE access right.
        Parameters:
            ExitCode:
                The exit code to be used by the process and threads terminated as a result of this call.
        Return value:
            If the function succeeds, the return value is nonzero.
            If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
    */
    Terminate(ExitCode := 0)
    {
        A_LastError := DllCall("Ntdll.dll\NtTerminateProcess", "UPtr", this.Handle
                                                             , "UInt", ExitCode
                                                             , "UInt")
        return A_LastError == 0 ? TRUE : FALSE
    } ; https://undocumented.ntinternals.net/index.html?page=UserMode%2FUndocumented%20Functions%2FNT%20Objects%2FProcess%2FNtTerminateProcess.html
}





/*
    Opens the next process in relation to the specified process.
    Parameters:
        Process:
            A handle to the process.
            This parameter can be zero to start from the first process.
        DesiredAccess / HandleAttributes:
            See the ProcessOpen function.
    Return value:
        If the function succeeds, the return value is an IProcess class object.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
*/
ProcessGetNext(Process, DesiredAccess, HandleAttributes := 0, Flags := 0)
{
    local hProcess := 0
    A_LastError := DllCall("Ntdll.dll\NtGetNextProcess",  "UPtr", IsObject(Process) ? Process.Handle : Process
                                                       ,  "UInt", DesiredAccess
                                                       ,  "UInt", HandleAttributes
                                                       ,  "UInt", Flags
                                                       , "UPtrP", hProcess
                                                       ,  "UInt")
    return hProcess ? new IProcess.FromHandle(hProcess) : 0
}





/*
    Checks whether the specified handle belongs to a process.
    Parameters:
        Handle:
            The handle to be checked.
    Return value:
        If the handle is valid, the return value is the process handle. Otherwise, it is zero.
*/
ProcessCheckHandle(Handle)
{
    return DllCall("Kernel32.dll\GetExitCodeProcess",  "UPtr", Handle := Integer(IsObject(Handle)?Handle.Handle:Handle)
                                                    , "UIntP", 0)
        || A_LastError !== 0x00000006  ; ERROR_INVALID_HANDLE = 0x00000006.
         ? Handle : 0
}





/*
    Retrieves the process identifier of the specified process.
    Parameters:
        A handle to the process.
        The handle must have the PROCESS_QUERY_LIMITED_INFORMATION access right.
    Return value:
        If the handle is valid and the process still exists, the return value is the process identifier. Otherwise, it is zero.
    Remarks:
        Until a process terminates, its process identifier uniquely identifies it on the system.
*/
ProcessGetID(Process)
{
    return DllCall("Kernel32.dll\GetProcessId", "Ptr", IsObject(Process)?Process.Handle:Process, "UInt")
} ; https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getprocessid


Recuperar información usando la función GetProcessMemoryInfo.

Code: Select all

/*
    Retrieves information about the memory usage of the specified process.
    Parameters:
        Process:
            A handle to the process.
            The handle must have the PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access rights.
    Return value:
        If the function succeeds, the return value is an associative object (Map) with the following keys:
            WorkingSetSize                The size, in bytes, of the current working set of the process.
            PeakWorkingSetSize            The peak size, in bytes, of the working set of the process.
            QuotaPagedPoolUsage           The current quota charged to the process for paged pool usage, in bytes.
            QuotaPeakPagedPoolUsage       The peak paged pool usage, in bytes.
            QuotaNonPagedPoolUsage        The current quota charged to the process for nonpaged pool usage, in bytes.
            QuotaPeakNonPagedPoolUsage    The peak nonpaged pool usage, in bytes.
            PagefileUsage                 The number of bytes of page file storage in use by the process.
            PeakPagefileUsage             The maximum number of bytes of page-file storage used by the process.
            PageFaultCount                The number of page faults.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (WIN32).
*/
ProcessGetMemoryInfo(Process)
{
    local PROCESS_MEMORY_COUNTERS_EX := BufferAlloc(8+9*A_PtrSize)
    NumPut("UInt", PROCESS_MEMORY_COUNTERS_EX.Size, PROCESS_MEMORY_COUNTERS_EX)  ; DWORD  cb.
    if !DllCall("Psapi.dll\GetProcessMemoryInfo", "UPtr", IsObject(Process) ? Process.Handle : Process
                                                , "UPtr", PROCESS_MEMORY_COUNTERS_EX.Ptr
                                                , "UInt", PROCESS_MEMORY_COUNTERS_EX.Size)
        return 0

    local MemoryInfo := { PageFaultCount            : NumGet(PROCESS_MEMORY_COUNTERS_EX,             4, "UInt")    ; DWORD  PageFaultCount.
                        , PeakWorkingSetSize        : NumGet(PROCESS_MEMORY_COUNTERS_EX,             8, "UPtr")    ; SIZE_T PeakWorkingSetSize.
                        , WorkingSetSize            : NumGet(PROCESS_MEMORY_COUNTERS_EX,   8+A_PtrSize, "UPtr")    ; SIZE_T WorkingSetSize.
                        , QuotaPeakPagedPoolUsage   : NumGet(PROCESS_MEMORY_COUNTERS_EX, 8+2*A_PtrSize, "UPtr")    ; SIZE_T QuotaPeakPagedPoolUsage.
                        , QuotaPagedPoolUsage       : NumGet(PROCESS_MEMORY_COUNTERS_EX, 8+3*A_PtrSize, "UPtr")    ; SIZE_T QuotaPagedPoolUsage.
                        , QuotaPeakNonPagedPoolUsage: NumGet(PROCESS_MEMORY_COUNTERS_EX, 8+4*A_PtrSize, "UPtr")    ; SIZE_T QuotaPeakNonPagedPoolUsage.
                        , QuotaNonPagedPoolUsage    : NumGet(PROCESS_MEMORY_COUNTERS_EX, 8+5*A_PtrSize, "UPtr")    ; SIZE_T QuotaNonPagedPoolUsage.
                        , PagefileUsage             : NumGet(PROCESS_MEMORY_COUNTERS_EX, 8+6*A_PtrSize, "UPtr")    ; SIZE_T PagefileUsage.
                        , PeakPagefileUsage         : NumGet(PROCESS_MEMORY_COUNTERS_EX, 8+7*A_PtrSize, "UPtr")    ; SIZE_T PeakPagefileUsage.
                        , PrivateUsage              : NumGet(PROCESS_MEMORY_COUNTERS_EX, 8+8*A_PtrSize, "UPtr") }  ; SIZE_T PrivateUsage.

    return MemoryInfo
}


Recuperar información usando la función NtQueryInformationProcess.

Code: Select all

/*
    Retrieves extended basic information of the specified process.
    Parameters:
        Process:
            A handle to the process.
            The handle must have the PROCESS_QUERY_LIMITED_INFORMATION and PROCESS_VM_READ access rights.
    Return value:
        If the function succeeds, the return value is an associative object (Map) with the following keys:
            ProcessId          The process identifier that uniquely identifies the process.
            ParentProcessId    The identifier of the process that created this process (its parent process).
            BasePriority       The base priority of the process, which is the starting priority for threads created within the associated process.
            PebBaseAddress     A pointer to a PEB structure containing process information.
            AffinityMask       The process affinity mask for the specified process.
            ExitStatus         The termination status of the specified process. STILL_ACTIVE = 259.
            Flags              Bit flags. Read: https://stackoverflow.com/questions/47300622/meaning-of-flags-in-process-extended-basic-information-struct.
                0x001  IsProtectedProcess    System protected process: other processes can't read/write its VM or inject a remote thread into it.
                0x002  IsWow64Process        WOW64 process, or 32-bit process running on a 64-bit Windows.
                0x004  IsProcessDeleting     Process was terminated, but there're open handles to it.
                0x008  IsCrossSessionCreate  Process was created across terminal sessions. Ex. CreateProcessAsUser.
                0x010  IsFrozen              Immersive process is suspended (applies only to UWP processes).
                0x020  IsBackground          Immersive process is in the Background task mode. UWP process may temporarily switch into performing a background task.
                0x040  IsStronglyNamed       UWP Strongly named process. The UWP package is digitally signed. Any modifications to files inside the package can be tracked. This usually means that if the package signature is broken the UWP app will not start.
                0x080  IsSecureProcess       Isolated User Mode process (new security mode in Windows 10), with more stringent restrictions on what can "tap" into this process.
                0x100  IsSubsystemProcess    Set when the type of the process subsystem is other than Win32 (like *NIX, such as Ubuntu.).
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
*/
ProcessGetBasicInfo(Process)
{
    local PROCESS_EXTENDED_BASIC_INFORMATION := BufferAlloc(A_PtrSize==4?32:64)  ; https://stackoverflow.com/questions/47300622/meaning-of-flags-in-process-extended-basic-information-struct.
    NumPut("UInt", PROCESS_EXTENDED_BASIC_INFORMATION.Size, PROCESS_EXTENDED_BASIC_INFORMATION)
    local NtStatus := DllCall("Ntdll.dll\NtQueryInformationProcess", "UPtr", IsObject(Process) ? Process.Handle : Process
                                                                   ,  "Int", 0  ; ProcessBasicInformation.
                                                                   , "UPtr", PROCESS_EXTENDED_BASIC_INFORMATION.Ptr
                                                                   , "UInt", PROCESS_EXTENDED_BASIC_INFORMATION.Size
                                                                   , "UPtr", 0
                                                                   , "UInt")

    if (NtStatus == 0)  ; STATUS_SUCCESS.
    {
        local ProcessInfo := { ExitStatus     : NumGet(PROCESS_EXTENDED_BASIC_INFORMATION,   A_PtrSize, "UInt")    ; NTSTATUS  ExitStatus.
                             , PebBaseAddress : NumGet(PROCESS_EXTENDED_BASIC_INFORMATION, 2*A_PtrSize, "UPtr")    ; PPEB      PebBaseAddress.
                             , AffinityMask   : NumGet(PROCESS_EXTENDED_BASIC_INFORMATION, 3*A_PtrSize, "UPtr")    ; ULONG_PTR AffinityMask.
                             , BasePriority   : NumGet(PROCESS_EXTENDED_BASIC_INFORMATION, 4*A_PtrSize, "UInt")    ; KPRIORITY BasePriority.
                             , ProcessId      : NumGet(PROCESS_EXTENDED_BASIC_INFORMATION, 5*A_PtrSize, "UPtr")    ; HANDLE    UniqueProcessId.
                             , ParentProcessId: NumGet(PROCESS_EXTENDED_BASIC_INFORMATION, 6*A_PtrSize, "UPtr")    ; HANDLE    InheritedFromUniqueProcessId.
                             , Flags          : NumGet(PROCESS_EXTENDED_BASIC_INFORMATION, 7*A_PtrSize, "UInt") }  ; ULONG     Flags.

    }

    return (A_LastError := NtStatus) ? 0 : ProcessInfo
}


Ejemplo usando las funciones ProcessOpen y ProcessGetMemoryInfo para recuperar información del proceso actual.

Code: Select all

Process := ProcessOpen()  ; Abre el proceso actual.
ProcInf := ProcessGetMemoryInfo(Process)

MsgBox  Format("WorkingSetSize:`t`t`t{1}{10}`nPeakWorkingSetSize:`t`t{2}{10}`nQuotaPagedPoolUsage:`t`t{3}{10}"
             . "`nQuotaPeakPagedPoolUsage:`t{4}{10}`nQuotaNonPagedPoolUsage:`t{5}{10}"
             . "`nQuotaPeakNonPagedPoolUsage:`t{6}{10}`nPagefileUsage:`t`t`t{7}{10}"
             . "`nPeakPagefileUsage:`t`t{8}{10}`nPageFaultCount:`t`t`t{9}{10}{11}ProcessId:`t`t`t{12}"
             , ProcInf.WorkingSetSize, ProcInf.PeakWorkingSetSize, ProcInf.QuotaPagedPoolUsage
             , ProcInf.QuotaPeakPagedPoolUsage, ProcInf.QuotaNonPagedPoolUsage, ProcInf.QuotaPeakNonPagedPoolUsage
             , ProcInf.PagefileUsage, ProcInf.PeakPagefileUsage, ProcInf.PageFaultCount, " Bytes"
             , "`n-------------------------------------------------------------------`n", ProcessExist())


Recupera el nombre completo de la imagen ejecutable para el proceso especificado.

Code: Select all

/*
    Retrieves the full name of the executable image for the specified process.
    Parameters:
        Process:
            A handle to the process.
            The handle must have the PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION access right.
        Flags:
            This parameter can be one of the following values.
            0x00000000     The name should use the Win32 path format.
            0x00000001     The name should use the native system path format.
    Return value:
        If the function succeeds, the return value is a string with the full name of the executable image.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (WIN32).
*/
ProcessGetImageName(Process, Flags := 0x00000000)
{
    local Buffer := BufferAlloc(2*32767+2)
    local Size   := Buffer.Size // 2

    return DllCall("Kernel32.dll\QueryFullProcessImageNameW",  "UPtr", IsObject(Process) ? Process.Handle : Process
                                                            ,  "UInt", Flags
                                                            ,  "UPtr", Buffer.Ptr
                                                            , "UIntP", Size)
         ? StrGet(Buffer, Size, "UTF-16")
         : 0
} ; https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-queryfullprocessimagenamea
User avatar
Flipeador
Posts: 1126
Joined: 15 Nov 2014, 21:31
GitHub: Flipeador
Location: Argentina
Contact:

Re: Funciones relacionadas a procesos, hilos (threads) y relacionado

14 Jul 2019, 23:05

Ahora vamos a ver una forma de enumerar todos los manipuladores en el sistema. Para ello, vamos a necesitar las funciónes ProcessOpen, ProcessGetImageName, AdjustPrivilege, ProcessEnumHandles, HandleClose, ObjectDuplicate y ObjectQuery.

Code: Select all

/*
    Enables or disables a privilege in the current process.
    Parameters:
        Privilege:
            0      SeUnsolicitedInputPrivilege 
            2      SeCreateTokenPrivilege    
            3      SeAssignPrimaryTokenPrivilege  
            4      SeLockMemoryPrivilege  
            5      SeIncreaseQuotaPrivilege      
            6      SeMachineAccountPrivilege     
            7      SeTcbPrivilege    
            8      SeSecurityPrivilege            
            9      SeTakeOwnershipPrivilege  
            10     SeLoadDriverPrivilege    
            11     SeSystemProfilePrivilege       
            12     SeSystemtimePrivilege  
            13     SeProfileSingleProcessPrivilege
            14     SeIncreaseBasePriorityPrivilege
            21     SeAuditPrivilege               
            17     SeBackupPrivilege              
            23     SeChangeNotifyPrivilege        
            30     SeCreateGlobalPrivilege        
            15     SeCreatePagefilePrivilege      
            16     SeCreatePermanentPrivilege     
            18     SeRestorePrivilege 
            19     SeShutdownPrivilege 
            35     SeCreateSymbolicLinkPrivilege  
            20     SeDebugPrivilege    
            22     SeSystemEnvironmentPrivilege        
            24     SeRemoteShutdownPrivilege    
            25     SeUndockPrivilege   
            26     SeSyncAgentPrivilege    
            27     SeEnableDelegationPrivilege    
            28     SeManageVolumePrivilege     
            29     SeImpersonatePrivilege 
            31     SeTrustedCredManAccessPrivilege    
            32     SeRelabelPrivilege       
            33     SeIncreaseWorkingSetPrivilege  
            34     SeTimeZonePrivilege   
        Enable:
            FALSE    Disable the specified privilege.
            TRUE     Enable the specified privilege.
        IsThreadPrivilege:
            FALSE    Open current process.
            TRUE     Open current thread.
    Return value:
        If the function succeeds, the return value is non-zero.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
*/
AdjustPrivilege(Privilege, Enable := TRUE, IsThreadPrivilege := FALSE)
{
    A_LastError := DllCall("Ntdll.dll\RtlAdjustPrivilege",   "UInt", Privilege            ; ULONG.
                                                         ,  "UChar", !!Enable             ; BOOLEAN.
                                                         ,  "UChar", !!IsThreadPrivilege  ; BOOLEAN.
                                                         , "UCharP", 0                    ; PBOOLEAN.
                                                         ,   "UInt")
    ; The last parameter is supposed to return the previous value, but it doesn't work.

    return A_LastError == 0 ? TRUE : FALSE
} ; https://source.winehq.org/WineAPI/RtlAdjustPrivilege.html

Code: Select all

/*
    Enumerates all open handles in the system.
    Return value:
        If the function succeeds, the return value is an array of associative objects (Map) with the following keys:
            ProcessId          The process identifier that uniquely identifies the process.
            Handle             The object handle.
            GrantedAccess      Specifies the granted access for the handle.
            Attributes         Bitmask of flags that specify object handle attributes. This member can contain one or more of the following flags:
                0x00000002  OBJ_INHERIT                          This handle can be inherited by child processes of the current process.
                0x00000010  OBJ_PERMANENT                        https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_object_attributes
                0x00000020  OBJ_EXCLUSIVE                        //
                0x00000040  OBJ_CASE_INSENSITIVE                 //
                0x00000080  OBJ_OPENIF                           //
                0x00000100  OBJ_OPENLINK                         //
                0x00000200  OBJ_KERNEL_HANDLE                    The handle is created in system process context and can only be accessed from kernel mode.
                0x00000400  OBJ_FORCE_ACCESS_CHECK               The routine that opens the handle should enforce all access checks for the object, even if the handle is being opened in kernel mode.
                0x00000800  OBJ_IGNORE_IMPERSONATED_DEVICEMAP    
                0x00001000  OBJ_DONT_REPARSE                    
                0x00001FF2  OBJ_VALID_ATTRIBUTES
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
*/
ProcessEnumHandles()
{
    local NtStatus, Buffer
    while (NtStatus := DllCall("Ntdll.dll\NtQuerySystemInformation",  "Int", 64                                      ; SYSTEM_INFORMATION_CLASS SystemInformationClass. SystemExtendedHandleInformation = 64.
                                                                   ,  "Ptr", Buffer := BufferAlloc(A_Index*0x7A120)  ; (out)PVOID               SystemInformation.
                                                                   , "UInt", Buffer.Size                             ; ULONG                    SystemInformationLength.
                                                                   , "UPtr", 0                                       ; (out)PULONG              ReturnLength.
                                                                   , "UInt")) == 0xC0000004                          ; STATUS_INFO_LENGTH_MISMATCH = 0xC0000004.
        continue

    if (NtStatus == 0x00000000)  ; STATUS_SUCCESS = 0x00000000.
    {
        Buffer := {Buffer:Buffer, Ptr:Buffer.Ptr, Size:Buffer.Size}  ; SYSTEM_HANDLE_INFORMATION_EX structure.
        local HandleList := [ ]  ; An array of associative objects (Map) containing handle information for each process.     
        loop ( NumGet(Buffer) )  ; ULONG_PTR SYSTEM_HANDLE_INFORMATION_EX.NumberOfHandles.
        {
            HandleList.Push( { Object       : NumGet(Buffer,   2*A_PtrSize, "UPtr")      ; PVOID     Object.
                             , ProcessId    : NumGet(Buffer,   3*A_PtrSize, "UPtr")      ; ULONG_PTR UniqueProcessId.
                             , Handle       : NumGet(Buffer,   4*A_PtrSize, "UPtr")      ; ULONG_PTR HandleValue.
                             , GrantedAccess: NumGet(Buffer,   5*A_PtrSize, "UInt")      ; ULONG     GrantedAccess.
                             , Attributes   : NumGet(Buffer, 8+5*A_PtrSize, "UInt") } )  ; ULONG     HandleAttributes.
            Buffer.Ptr += 16 + (3*A_PtrSize)  ; sizeof(SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX).
        }
    }

    A_LastError := NtStatus
    return NtStatus == 0x00000000 ? HandleList : 0
} ; https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation





/*
    Enumerates all open handles in the specified process.
    Parameters:
        Process:
            A handle to the process.
            The handle must have the PROCESS_QUERY_INFORMATION access right.
    Return value:
        If the function succeeds, the return value is an array of associative objects (Map) with the following keys:
            Handle             The object handle.
            HandleCount
            PointerCount
            GrantedAccess      Specifies the granted access for the handle.
            ObjTypeIndex
            Attributes         See the ProcessEnumHandles function.    
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
    Remarks:
        This function is only available starting with Windows 8.
*/
ProcessEnumHandles2(Process)  ; WIN_8+
{
    local NtStatus, Buffer
    while (NtStatus := DllCall("Ntdll.dll\NtQueryInformationProcess", "UPtr", IsObject(Process)?Process.Handle:Process  ; HANDLE           ProcessHandle.
                                                                    ,  "Int", 51                                        ; PROCESSINFOCLASS ProcessInformationClass. ProcessHandleInformation = 51.
                                                                    ,  "Ptr", Buffer := BufferAlloc(A_Index*0x30D40)    ; (out)PVOID       ProcessInformation. InitialBufferSize = 0x30D40.
                                                                    , "UInt", Buffer.Size                               ; ULONG            ProcessInformationLength.
                                                                    , "UPtr", 0                                         ; (out)PULONG      ReturnLength.
                                                                    , "UInt")) == 0xC0000004                            ; STATUS_INFO_LENGTH_MISMATCH = 0xC0000004.
        continue

    if (NtStatus == 0x00000000)  ; STATUS_SUCCESS = 0x00000000.
    {
        Buffer := {Buffer:Buffer, Ptr:Buffer.Ptr, Size:Buffer.Size}  ; PROCESS_HANDLE_SNAPSHOT_INFORMATION structure.
        local HandleList := [ ]  ; An array of associative objects (Map) containing handle information for the specified process.
        loop ( NumGet(Buffer) )  ; ULONG_PTR PROCESS_HANDLE_SNAPSHOT_INFORMATION.NumberOfHandles.
        {
            HandleList.Push( { Handle       : NumGet(Buffer,   2*A_PtrSize, "UPtr")      ; ULONG_PTR HandleValue.
                             , HandleCount  : NumGet(Buffer,   3*A_PtrSize, "UPtr")      ; ULONG_PTR HandleCount.
                             , PointerCount : NumGet(Buffer,   4*A_PtrSize, "UPtr")      ; ULONG_PTR PointerCount.
                             , GrantedAccess: NumGet(Buffer,   5*A_PtrSize, "UInt")      ; ULONG GrantedAccess.
                             , ObjTypeIndex : NumGet(Buffer, 4+5*A_PtrSize, "UInt")      ; ULONG ObjectTypeIndex.
                             , Attributes   : NumGet(Buffer, 8+5*A_PtrSize, "UInt") } )  ; ULONG HandleAttributes.
            Buffer.Ptr += 16 + (3*A_PtrSize)  ; sizeof(PROCESS_HANDLE_TABLE_ENTRY_INFO).
        }
    }

    A_LastError := NtStatus
    return NtStatus == 0x00000000 ? HandleList : 0
} ; https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess

Code: Select all

/*
    Closes an object handle in the specified process.
    Parameters:
        Handle.
            Handle to an object of any type.
        SourceProcess:
             A handle to the source process for the handle being closed.
             If this parameter is zero, specifies that the handle belongs to this process.
    Return value:
        The return value is zero (STATUS_SUCCESS) on success, or the appropriate NTSTATUS error code on failure.
        0xC0000008  STATUS_INVALID_HANDLE         The handle is not valid.
        0xC0000235  STATUS_HANDLE_NOT_CLOSABLE    The calling thread does not have permission to close the handle.
    NTSTATUS Error Code List:
        https://davidvielmetter.com/tips/ntstatus-error-code-list/
*/
HandleClose(Handle, SourceProcess := 0)
{
    return SourceProcess == 0
         ? DllCall("Ntdll.dll\NtClose", "Ptr", Handle, "UInt")
         : DllCall("Ntdll.dll\NtDuplicateObject", "Ptr", SourceProcess, "Ptr", Handle, "Ptr", 0, "Ptr", 0, "UInt", 0, "UInt", 0, "UInt", 1, "UInt")
} ; https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntifs/nf-ntifs-ntclose





/*
    Creates a handle that is a duplicate of the specified source handle.
    Parameters:
        TargetProcess:
            A handle to the target process that is to receive the new handle.
            This parameter can be zero if the DUPLICATE_CLOSE_SOURCE flag is set in Flags.
        SourceProcess:
            A handle to the source process for the handle being duplicated.
        SourceHandle:
            The handle to duplicate.
        DesiredAccess:
            Specifies the desired access for the new handle.
            Reference: https://docs.microsoft.com/windows-hardware/drivers/kernel/access-mask.
        HandleAttributes:
            Specifies the desired attributes for the new handle.
            Reference: https://docs.microsoft.com/windows/desktop/api/ntdef/ns-ntdef-_object_attributes.
        Flags:
            A set of flags to control the behavior of the duplication operation.
            Set this parameter to zero or to the bitwise OR of one or more of the following flags.
            0x00000001  DUPLICATE_CLOSE_SOURCE       Close the source handle. The return value is «SourceHandle» if successful and «TargetProcess» is zero.
            0x00000002  DUPLICATE_SAME_ACCESS        Instead of using the DesiredAccess parameter, copy the access rights from the source handle to the target handle.
            0x00000004  DUPLICATE_SAME_ATTRIBUTES    Instead of using the HandleAttributes parameter, copy the attributes from the source handle to the target handle.
    Return value:
        If the function succeeds, the return value is the new duplicated handle. The duplicated handle is valid in the specified target process.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
    To close a handle in a process, call the function as follows:
        ZeroOrSourceHandle     := ObjectDuplicate(            0, SourceProcess, SourceHandle, 0, 0, 0x00000001)  ; Opt #1.
        ZeroOrDuplicatedHandle := ObjectDuplicate(TargetProcess, SourceProcess, SourceHandle, 0, 0, 0x00000001)  ; Opt #2.
        NtStatus               := HandleClose(SourceHandle, SourceProcess)                                       ; Opt #3.
*/
ObjectDuplicate(TargetProcess, SourceProcess, SourceHandle, DesiredAccess := 0, HandleAttributes := 0, Flags := 0)
{
    local TargetHandle := 0
    local NtStatus := DllCall("Ntdll.dll\NtDuplicateObject",  "UPtr", IsObject(SourceProcess) ? SourceProcess.Handle : SourceProcess
                                                           ,  "UPtr", IsObject(SourceHandle)  ? SourceHandle.Handle  : SourceHandle
                                                           ,  "UPtr", IsObject(TargetProcess) ? TargetProcess.Handle : TargetProcess
                                                           , "UPtrP", TargetHandle
                                                           ,  "UInt", DesiredAccess
                                                           ,  "UInt", HandleAttributes
                                                           ,  "UInt", Flags
                                                           ,  "UInt")
    A_LastError := NtStatus
    return (Flags & 0x00000001) ? (NtStatus ? 0 : (TargetProcess?TargetHandle:SourceHandle)) : TargetHandle
}





/*
    Provides information about a supplied object.
    Parameters:
        Handle:
            A handle to the object to obtain information about.
        InformationClass:
            The type of information returned in the «ObjectInformation» buffer.
            0  ObjectBasicInformation
            1  ObjectNameInformation
            2  ObjectTypeInformation
        ObjectInformation:
            A Buffer object that receives the requested information.
        ReturnLength:
            Receives the size, in bytes, of the requested key information.
            If this function returns zero (STATUS_SUCCESS), the variable contains the amount of data returned.
            If this function returns 0x80000005 (STATUS_BUFFER_OVERFLOW) or 0xC0000023 (STATUS_BUFFER_TOO_SMALL), this value can be used to determine the required buffer size.
    Return value:
        If the function succeeds, the return value is «ObjectInformation».
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
            0xC0000022  STATUS_ACCESS_DENIED           There were insufficient permissions to perform this query.
            0xC0000008  STATUS_INVALID_HANDLE          The supplied object handle is invalid.
            0xC0000004  STATUS_INFO_LENGTH_MISMATCH    The info length is not sufficient to hold the data.
*/
ObjectQuery(Handle, InformationClass, ObjectInformation, ByRef ReturnLength := 0)
{
    A_LastError := DllCall("Ntdll.dll\NtQueryObject",  "UPtr", Handle                  ; HANDLE                   Handle.
                                                    ,  "UInt", InformationClass        ; OBJECT_INFORMATION_CLASS ObjectInformationClass.
                                                    ,   "Ptr", ObjectInformation       ; (out)PVOID               ObjectInformation.
                                                    ,  "UInt", ObjectInformation.Size  ; ULONG                    ObjectInformationLength.
                                                    , "UIntP", ReturnLength            ; (out)PULONG              ReturnLength.
                                                    ,  "UInt")                         ; NTSTATUS.
    return A_LastError == 0 ? ObjectInformation : 0
}


Ahora el ejemplo usando todas estas funciones. Usando estas funciones podemos saber que proceso tiene bloqueado un archivo.
Nota: para recuperar toda la información posible, es necesario ejecutar AutoHotkey como Administrador.

Code: Select all

; Requiere: AdjustPrivilege, ProcessOpen, ProcessEnumHandles, ProcessGetImageName, HandleClose, ObjectDuplicate y ObjectQuery.


AdjustPrivilege(20)  ; SeDebugPrivilege.

HandleList := ProcessEnumHandles()
Buffer     := BufferAlloc(1000000)

Gui := GuiCreate()
LV  := Gui.AddListView("w1250 h675 -LV0x10 LV0x10000 Count" . HandleList.Length()
                     , "Process name|Process ID|Object handle|GrantedAccess|Flags|Attributes|HandleCount|PointerCount|Object type|Object name|File type|File name")

LV.Opt("-Redraw")
FileTypes := {0: "Unknown", 1:"Disk", 2:"Char", 3:"Pipe", 0x8000:"Remote"}
for Each, Item in ProcessEnumHandles()  ; https://social.technet.microsoft.com/Forums/en-US/5b78bf61-4a06-4367-bc28-a9cba3c688b5/howto-enumerate-handles?forum=windowsdevelopment
{
    if Process := ProcessOpen(Item.ProcessId, 0x40|0x1000)  ; PROCESS_DUP_HANDLE|PROCESS_QUERY_LIMITED_INFORMATION.
    {
        Attributes := HandleCount := PointerCount := ObjType := ObjName := FileName := "-"

        ; Duplicate the handle so we can query it.
        if hObject := ObjectDuplicate(-1, Process, Item)
        {
            ; Query the object basic information.
            if ObjectQuery(hObject, 0, {Ptr:Buffer.Ptr,Size:56})  ; OBJECT_BASIC_INFORMATION structure.
            {
                Attributes   := NumGet(Buffer,  0, "UInt")      ; ULONG Attributes.
               ,HandleCount  := NumGet(Buffer,  8, "UInt") - 1  ; ULONG HandleCount.
               ,PointerCount := NumGet(Buffer, 12, "UInt") - 2  ; ULONG PointerCount.
            }

            ; Query the object type.
            if ObjectQuery(hObject, 2, Buffer)  ; OBJECT_TYPE_INFORMATION structure.
                ObjType := StrGet(NumGet(Buffer,A_PtrSize), NumGet(Buffer,"UShort")//2)  ; UNICODE_STRING structure.

            ; Query the object name.
            A_LastError := 0
            FileType    := DllCall("Kernel32.dll\GetFileType", "Ptr", hObject, "UInt")  ; Retrieves the file type of the specified file.
            LastErr     := A_LastError  ; If the function worked properly and FILE_TYPE_UNKNOWN was returned, a call to GetLastError will return NO_ERROR.
            if (FileType !== 0x0003)  ; NtQueryObject may hang on file handles pointing to named pipes.
            {
                if ObjectQuery(hObject, 1, Buffer)  ; UNICODE_STRING structure.
                    ObjName  := StrGet(NumGet(Buffer,A_PtrSize), NumGet(Buffer,"UShort")//2)
                Length   := DllCall("Kernel32.dll\GetFinalPathNameByHandleW", "Ptr", hObject, "Ptr", Buffer, "UInt", Buffer.Size, "UInt", 0)
               ,FileName := StrGet(Buffer, Length, "UTF-16")
            }
            else if (ObjType == "File")
                FileName := "NtQueryObject may hang on file handles pointing to named pipes"

            HandleClose(hObject)
        }
        LV.Add(, ProcessGetImageName(Process), Item.ProcessId, Item.Handle, Format("0x{:08X}",Item.GrantedAccess)
             , Item.Attributes, Attributes, HandleCount, PointerCount, ObjType, ObjName, LastErr?"Error":FileTypes[FileType], LTrim(FileName,"\\?\"))
    }
}
LV.Opt("+Redraw")

Gui.OnEvent("Close", "ExitApp")
Gui.Show()

Gui.Title := Format("ProcessEnumHandles`s({}`sde`s{}) — ({}-Bit)", LV.GetCount(), HandleList.Length(), 8*A_PtrSize)
loop LV.GetCount("Col") - 1
    LV.ModifyCol(A_Index+1, "AutoHdr")
LV.ModifyCol(1, 400)
return


Esta es una función para recuperar información del proceso que tiene abierto el archivo especificado. El segundo código es un ejemplo que requiere las funciones ProcessOpen, ProcessEnumHandles, ObjectDuplicate, ObjectQuery y HandleClose. Si la función no logra recuperar la información, puede intentar ejecutando AutoHotkey como Administrador y habilitando el privilegio SeDebugPrivilege llamando a la función AdjustPrivilege(20), tal como se muestra en el ejemplo de arriba.

Code: Select all

/*
    Retrieves information about the process that has the specified file open.
    Parameters:
        FileName:
            The full path name of the target file.
    Return value:
        If the function succeeds and the file is opened by a process, the return value is an array of associative objects (Map) with the following keys:
            FileName    A string with the file name.
            FileType    The file type: "Directory" or "Disk".
            Item        Contains the associative object of included function ProcessEnumHandles.
        If the function fails, the return value is zero. To get extended error information, check A_LastError (NTSTATUS).
    Remarks:
        Depending on the process, Administrator rights and the SeDebugPrivilege privilege may be required.
*/
ProcessInfoFromFilename(FileName)
{
    local

    if !( SysHandleList := ProcessEnumHandles() )
        return 0

    RetValue  := [ ]
    Buffer    := BufferAlloc(0xF4240)
    FileTypes := {0: "Unknown", 1:"Disk", 2:"Char", 3:"Pipe", 0x8000:"Remote"}
    for Each, Item in SysHandleList
    {
        if Process := ProcessOpen(Item.ProcessId, 0x00000040)  ; PROCESS_DUP_HANDLE.
        {
            if hObject := ObjectDuplicate(-1, Process, Item)
            {
                if ObjectQuery(hObject, 2, Buffer)
                {
                    ObjType := StrGet(NumGet(Buffer,A_PtrSize), NumGet(Buffer,"UShort")//2)
                    Length  := 0
                    if (ObjType == "File")
                    {
                        FileType := DllCall("Kernel32.dll\GetFileType", "Ptr", hObject, "UInt")
                        if (A_LastError == 0x00000000)  ; NO_ERROR.
                            if (FileType !== 0x000003)  ; Pipe.
                                Length := DllCall("Kernel32.dll\GetFinalPathNameByHandleW", "Ptr", hObject, "Ptr", Buffer, "UInt", Buffer.Size, "UInt", 0)
                    }
                    else if (ObjType == "Directory")
                        Length := DllCall("Kernel32.dll\GetFinalPathNameByHandleW", "Ptr", hObject, "Ptr", Buffer, "UInt", Buffer.Size, "UInt", 0)
                    if (Length)
                    {
                        CurrFileName := StrGet(Buffer, Length, "UTF-16")
                        if !StrCompare(RegExReplace(CurrFileName,"^\\\\(\?|\.)\\"), FileName)
                            RetValue.Push( { Item    : Item
                                           , FileType: ObjType == "Directory" ? "Directory" : FileTypes[FileType]
                                           , FileName: CurrFileName } )
                    }
                }
                HandleClose(hObject)
            }
        }
    }

    return RetValue.Length() ? RetValue : 0
}

Code: Select all

FileName := A_ComSpec  ; Aquí la ruta completa a un archivo.
File     := FileOpen(FileName, "r")
if Arr := ProcessInfoFromFilename(FileName)
{
    for Each, Item in Arr
    {
        MsgBox(Format("FileName:`t{}`nFileType:`t`t{}`nProcessId:`t{}"
                    , Item.FileName, Item.FileType, Item.Item.ProcessId))
    }
}

Return to “Scripts y Funciones”

Who is online

Users browsing this forum: fkrause2 and 2 guests