【问题标题】:How to get window station for a given process?如何获取给定进程的窗口站?
【发布时间】:2014-04-17 22:12:58
【问题描述】:

比方说,如果我有进程 ID 或它的句柄,我可以得到进程运行的window station 吗?

【问题讨论】:

    标签: c++ c windows winapi


    【解决方案1】:

    不是直截了当,但试试这个:

    1. 调用EnumWindowStations()枚举调用进程在同一个Session中的可用窗口站(如果你需要查询另一个Session中的进程那么这将不起作用)。

    2. 对于每个窗口站,调用EnumDesktops() 枚举其桌面。

    3. 对于每个桌面,调用EnumDesktopWindows() 枚举其顶级窗口。

    4. 对于每个窗口,调用GetWindowThreadProcessId() 以获取其进程 ID 并将其与您要查找的 ID 进行比较。

    另一种选择可能是执行以下操作:

    1. 调用OpenProcess() 以从目标进程ID 中获取HANDLE

    2. 调用NtQueryInformationProcess()获取进程的PEB结构地址。

    3. 致电ReadProcessMemory() 阅读PEB。它的ProcessParams.DesktopName 字段包含当前与进程关联的工作站/桌面的名称PEB.ProcessParams 中可用的字段比 MSDN 显示的要多)。

    4. 解析DesktopName 以提取窗口站和桌面名称。

    5. 根据需要枚举工作站,从 GetUserObjectInformation() 中寻找匹配的名称。

    【讨论】:

    • 我喜欢你的第二种方法,因为有问题的进程可能在不同的会话中运行。让我试试,我会在这里发布。不过,一个问题是NtQueryInformationProcess() API 上的注释:NtQueryInformationProcess may be altered or unavailable in future versions of Windows. Applications should use the alternate functions listed in this topic
    • 一些函数返回存储在PEB中的信息,例如GetCommandLine()GetCurrentProcessId()。这就是笔记所指的。但是没有替代函数可以访问进程的PEB 本身,因为大多数人不需要直接访问它。
    • 我用根据您的建议提出的代码更新了我的答案。谢谢!
    • 但是DesktopName 有什么偏移量? 0x78 是 32 位的,但我找不到 64 位的。有人认识他们吗?
    【解决方案2】:

    好的,这个不适合胆小的人。这是我在@RemyLebeau 的建议后提出的代码。事实证明,对于 32/64 位进程,我需要以不同的方式执行此操作。我找不到 64 位进程的确切 PEB 结构,所以我做了一些基本的逆向工程来匹配它。

    也是一个很有趣的结果:

    1. 如果我为自己的流程调用它,我会得到如下信息:Winsta0\Default
    2. 如果我在其他 GUI 进程中调用它,我会得到:Default

    总体而言,可以获得以下类型的桌面/winstation:

    • Default 用于您的常规输入桌面(您现在正在使用)
    • WinlogonWinsta0\Winlogon 用于安全桌面(例如 Windows 登录屏幕)
    • "" 或 Metro(或 Modern-UI)Windows 8 应用的空字符串。

    接下来是代码。如果有人可以评论它会很好:

    //'dwProcID' = process ID to look up window station for
    HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcID);
    if(hProc)
    {
        BOOL (WINAPI *pfnIsWow64Process)(HANDLE, PBOOL);
    
        (FARPROC&)pfnIsWow64Process = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "IsWow64Process");
    
        SYSTEM_INFO si = {0};
        ::GetNativeSystemInfo(&si);
    
        //See if 32-bit process on 64-bit OS
        BOOL bWow64Proc = TRUE;
        if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
        {
            if(pfnIsWow64Process)
                if(!pfnIsWow64Process(hProc, &bWow64Proc))
                {
                    //Error
                    _tprintf(L"ERROR in IsWow64Process: %d\n", ::GetLastError());
                }
        }
    
        NTSTATUS ntStatus;
    
    
        if(bWow64Proc)
        {
            //32-bit process
            NTSTATUS (WINAPI *pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
            (FARPROC&)pfnNtQueryInformationProcess = ::GetProcAddress(::GetModuleHandle(L"Ntdll.dll"), "NtQueryInformationProcess");
            if(pfnNtQueryInformationProcess)
            {
                PROCESS_BASIC_INFORMATION pbi = {0};
                DWORD dwsz = 0;
                if((ntStatus = pfnNtQueryInformationProcess(hProc, ProcessBasicInformation, &pbi, sizeof(pbi), &dwsz)) == 0 &&
                    dwsz <= sizeof(pbi) &&
                    pbi.PebBaseAddress)
                {
                    //Define PEB structs
                    typedef struct _RTL_DRIVE_LETTER_CURDIR
                    {
                        WORD Flags;
                        WORD Length;
                        ULONG TimeStamp;
                        STRING DosPath;
                    } RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
    
                    struct RTL_USER_PROCESS_PARAMETERS_32
                    {
                        ULONG                   MaximumLength;
                        ULONG                   Length;
                        ULONG                   Flags;
                        ULONG                   DebugFlags;
                        PVOID                   ConsoleHandle;
                        ULONG                   ConsoleFlags;
                        HANDLE                  StdInputHandle;
                        HANDLE                  StdOutputHandle;
                        HANDLE                  StdErrorHandle;
                        UNICODE_STRING          CurrentDirectoryPath;
                        HANDLE                  CurrentDirectoryHandle;
                        UNICODE_STRING          DllPath;
                        UNICODE_STRING          ImagePathName;
                        UNICODE_STRING          CommandLine;
                        PVOID                   Environment;
                        ULONG                   StartingPositionLeft;
                        ULONG                   StartingPositionTop;
                        ULONG                   Width;
                        ULONG                   Height;
                        ULONG                   CharWidth;
                        ULONG                   CharHeight;
                        ULONG                   ConsoleTextAttributes;
                        ULONG                   WindowFlags;
                        ULONG                   ShowWindowFlags;
                        UNICODE_STRING          WindowTitle;
                        UNICODE_STRING          DesktopName;
                        UNICODE_STRING          ShellInfo;
                        UNICODE_STRING          RuntimeData;
                        RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
                    };
    
                    struct PEB_32
                    {
                      BYTE                          Reserved1[2];
                      BYTE                          BeingDebugged;
                      BYTE                          Reserved2[1];
                      PVOID                         Reserved3[2];
                      void*                         Ldr;
                      RTL_USER_PROCESS_PARAMETERS_32* ProcessParameters;
                      BYTE                          Reserved4[104];
                      PVOID                         Reserved5[52];
                      void*                         PostProcessInitRoutine;
                      BYTE                          Reserved6[128];
                      PVOID                         Reserved7[1];
                      ULONG                         SessionId;
                    };
    
                    //Read PEB-32
                    PEB_32 peb32 = {0};
    
                    DWORD dwcbSzRead = 0;
                    if(ReadProcessMemory(hProc, pbi.PebBaseAddress, &peb32, sizeof(peb32), &dwcbSzRead) &&
                        dwcbSzRead == sizeof(peb32) &&
                        peb32.ProcessParameters)
                    {
                        //Read RTL_USER_PROCESS_PARAMETERS_32
                        RTL_USER_PROCESS_PARAMETERS_32 rupp32 = {0};
    
                        dwcbSzRead = 0;
                        if(ReadProcessMemory(hProc, peb32.ProcessParameters, &rupp32, sizeof(rupp32), &dwcbSzRead) &&
                            dwcbSzRead == sizeof(rupp32) &&
                            rupp32.DesktopName.Buffer)
                        {
                            //Get desktop name
                            int ncbSzLn = rupp32.DesktopName.Length + sizeof(TCHAR);
                            BYTE* pDesktopName = new (std::nothrow) BYTE[ncbSzLn];
                            if(pDesktopName)
                            {
                                dwcbSzRead = 0;
                                if(ReadProcessMemory(hProc, rupp32.DesktopName.Buffer, pDesktopName, ncbSzLn, &dwcbSzRead) &&
                                    dwcbSzRead == ncbSzLn)
                                {
                                    //Set last NULL
                                    *(TCHAR*)(pDesktopName + ncbSzLn - sizeof(TCHAR)) = 0;
    
                                    //We're done
                                    _tprintf(L"Desktop32: %s\n", (LPCTSTR)pDesktopName);
                                }
                                else
                                    _tprintf(L"ERROR in ReadProcessMemory DesktopName: %d\n", ::GetLastError());
    
                                delete[] pDesktopName;
                            }
                            else
                                _tprintf(L"ERROR DesktopName ptr\n");
                        }
                        else
                            _tprintf(L"ERROR in ReadProcessMemory RTL_USER_PROCESS_PARAMETERS_32: %d\n", ::GetLastError());
                    }
                    else
                        _tprintf(L"ERROR in ReadProcessMemory PEB-32: %d\n", ::GetLastError());
                }
                else
                    _tprintf(L"ERROR in NtQueryInformationProcess: %d\n", ntStatus);
            }
            else
                _tprintf(L"ERROR NtQueryInformationProcess API\n");
        }
        else
        {
            //64-bit process
            NTSTATUS (WINAPI *pfnNtQueryInformationProcess64)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
            NTSTATUS (WINAPI *pfnNtWow64ReadVirtualMemory64)(HANDLE, PVOID64, PVOID, ULONG64, PULONG64);
    
            (FARPROC&)pfnNtQueryInformationProcess64 = ::GetProcAddress(::GetModuleHandle(L"Ntdll.dll"), "NtWow64QueryInformationProcess64");
            (FARPROC&)pfnNtWow64ReadVirtualMemory64 = ::GetProcAddress(::GetModuleHandle(L"Ntdll.dll"), "NtWow64ReadVirtualMemory64");
    
            if(pfnNtQueryInformationProcess64 &&
                pfnNtWow64ReadVirtualMemory64)
            {
                //Define PEB structs
                struct UNICODE_STRING_64 {
                    USHORT Length;
                    USHORT MaximumLength;
                    PVOID64 Buffer;
                };
    
                struct PROCESS_BASIC_INFORMATION64
                {
                    PVOID Reserved1[2];
                    PVOID64 PebBaseAddress;
                    PVOID Reserved2[4];
                    ULONG_PTR UniqueProcessId[2];
                    PVOID Reserved3[2];
                };
    
                PROCESS_BASIC_INFORMATION64 pbi64 = {0};
                DWORD dwsz = 0;
                if((ntStatus = pfnNtQueryInformationProcess64(hProc, ProcessBasicInformation, &pbi64, sizeof(pbi64), &dwsz)) == 0 &&
                    dwsz <= sizeof(pbi64))
                {
                    struct PEB_64
                    {
                        UCHAR               InheritedAddressSpace;
                        UCHAR               ReadImageFileExecOptions;
                        UCHAR               BeingDebugged;
                        BYTE                b003;
                        ULONG               Reserved0;
                        ULONG64             Mutant;
                        ULONG64             ImageBaseAddress;
                        ULONG64             Ldr;
                        PVOID64             ProcessParameters;
                    };
    
                    //Read PEB-64
                    PEB_64 peb64 = {0};
    
                    ULONG64 uicbSzRead = 0;
                    if(pfnNtWow64ReadVirtualMemory64(hProc, pbi64.PebBaseAddress, &peb64, sizeof(peb64), &uicbSzRead) == 0 &&
                        uicbSzRead == sizeof(peb64) &&
                        peb64.ProcessParameters)
                    {
                        //Don't know the structure of RTL_USER_PROCESS_PARAMETERS_64 thus read raw bytes
                        const int ncbSz_rawRUPP64 = sizeof(DWORD) * (6 * 8) + sizeof(UNICODE_STRING_64);
                        BYTE rawRUPP64[ncbSz_rawRUPP64] = {0};
    
                        uicbSzRead = 0;
                        if(pfnNtWow64ReadVirtualMemory64(hProc, peb64.ProcessParameters, &rawRUPP64, ncbSz_rawRUPP64, &uicbSzRead) == 0 &&
                            uicbSzRead == ncbSz_rawRUPP64)
                        {
                            //Point to the location in raw byte array
                            UNICODE_STRING_64* pDesktopName = (UNICODE_STRING_64*)(rawRUPP64 + sizeof(DWORD) * (6 * 8));
    
                            //Get desktop name
                            int ncbSzLn = pDesktopName->Length + sizeof(TCHAR);
                            BYTE* pBytesDesktopName = new (std::nothrow) BYTE[ncbSzLn];
                            if(pBytesDesktopName)
                            {
                                uicbSzRead = 0;
                                if(pfnNtWow64ReadVirtualMemory64(hProc, pDesktopName->Buffer, pBytesDesktopName, ncbSzLn, &uicbSzRead) == 0 &&
                                    uicbSzRead == ncbSzLn)
                                {
                                    //Set last NULL
                                    *(TCHAR*)(pBytesDesktopName + ncbSzLn - sizeof(TCHAR)) = 0;
                                    LPCTSTR pStrDesktopName = (LPCTSTR)pBytesDesktopName;
    
                                    //We're done
                                    _tprintf(L"Desktop64: %s\n", pStrDesktopName);
                                }
                                else
                                    _tprintf(L"ERROR in NtWow64ReadVirtualMemory64 DesktopName: %d\n", ::GetLastError());
    
                                delete[] pBytesDesktopName;
                            }
                            else
                                _tprintf(L"ERROR DesktopName64 ptr\n");
                        }
                        else
                            _tprintf(L"ERROR in NtWow64ReadVirtualMemory64 RTL_USER_PROCESS_PARAMETERS_32: %d\n", ::GetLastError());
                    }
                    else
                        _tprintf(L"ERROR in NtWow64ReadVirtualMemory64 PEB-64: %d\n", ::GetLastError());
                }
                else
                    _tprintf(L"ERROR in NtQueryInformationProcess64: %d\n", ntStatus);
            }
            else
                _tprintf(L"ERROR NtWow64QueryInformationProcess64 API\n");
        }
    
        ::CloseHandle(hProc);
    }
    else
        _tprintf(L"ERROR in OpenProcess: %d\n", ::GetLastError());
    

    【讨论】:

    • 所以您只获得桌面名称而不是窗口站名称,除非您查询自己的进程?这很奇怪。这些信息是否足够,或者您还需要窗口站名称吗?
    【解决方案3】:

    按照您的链接,以下页面可能会有所帮助:

    http://msdn.microsoft.com/en-us/library/windows/desktop/ms684859(v=vs.85).aspx

    进程不在窗口站下运行,而是窗口站与进程“关联”。

    【讨论】:

    • 好的,好点。不过,我如何获得这种“关联”?换句话说,我希望GetProcessWindowStation API 被这样调用:GetProcessWindowStation(DWORD dwPid)
    • msdn.microsoft.com/en-us/library/windows/desktop/… 描述了如何为您的代码当前正在执行的进程返回 Windows 站的句柄。
    • 也就是说GetProcessWindowStation()只对调用进程有效,不能用来查询其他进程。
    • @RemyLebeau:是的,我明白了,伙计们。我正在寻找一种方法来做到这一点。
    【解决方案4】:

    似乎没有任何直接方法可以找到与给定进程关联的窗口站。

    我看到两个选项:

    • 使用代码注入在目标进程中运行GetProcessWindowStation然后GetUserObjectInformation

    • 使用EnumWindowStationsEnumDesktopsEnumDesktopWindows遍历会话中的所有窗口;然后使用GetWindowThreadProcessId 并将进程ID与目标进程的ID进行比较。当然,如果进程当前没有窗口,这将不起作用。

    您也可以尝试使用GetThreadDesktopGetUserObjectInformation 来获取桌面名称;我不确定这是否包括窗口站名称。如果这确实有效,请参阅 MSDN 中的 Traversing the Thread List 以枚举属于该进程的线程。

    【讨论】:

    • Win32 API 中没有 GetWindowProcessId()GetWindowThreadProcess() 函数。你在想GetWindowThreadProcessId()吗?你不需要使用注入来调用它。
    • 你知道,我也走这条路(使用GetThreadDesktop),但我似乎无法让它工作。我会得到一个有问题的进程的主线程 ID,但随后GetThreadDesktop 将返回ERROR_ACCESS_DENIED。我需要特殊权限才能调用它吗?
    • @Remy:不,我的意思是GetProcessWindowStation。现已更正,谢谢。
    • @c00000fd:文档没有说您需要任何特殊权限。当然,您需要以与目标进程相同的用户身份或以管理员身份运行,而且您很可能必须在同一个远程桌面会话中运行。我猜你可能也需要连接到同一个窗口站,这会让事情变得很尴尬。
    • @HarryJohnston:是的,在那种情况下GetThreadDesktop 也不起作用。谢谢。
    猜你喜欢
    • 2010-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-12
    • 2011-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多