How to get window station for a given process?

梦想的初衷 提交于 2021-02-07 09:58:45

问题


Say, if I have a process ID or its handle, can I get the window station that the process runs under?


回答1:


Not straight forward, but try this:

  1. Call EnumWindowStations() to enumerate available window stations in the same Session as the calling process (if you need to query a process in another Session then this will not work).

  2. For each window station, call EnumDesktops() to enumerate its desktops.

  3. For each desktop, call EnumDesktopWindows() to enumerate its top-level windows.

  4. For each window, call GetWindowThreadProcessId() to get its process ID and compare it to the ID you are looking for.

Another option might be to do the following:

  1. Call OpenProcess() to get a HANDLE from the target process ID.

  2. Call NtQueryInformationProcess() to retrieve the address of the process's PEB structure.

  3. Call ReadProcessMemory() to read the PEB. It's ProcessParams.DesktopName field contains the name of the workstation/desktop currently associated with the process (there are many more fields available in the PEB.ProcessParams then what MSDN shows).

  4. Parse the DesktopName to extract the window station and desktop names.

  5. Enumerate workstations as needed, looking for a matching name from GetUserObjectInformation().




回答2:


OK, this one is not for the faint-hearted. Here's the code I came up with after @RemyLebeau's advice. It turns out I need to do this differently for 32/64-bit processes. I couldn't find the exact PEB structures for 64-bit processes, so I did some basic reverse-engineering to match it.

Also quite an interesting result:

  1. If I call it for my own process, I get something like this: Winsta0\Default
  2. If I call it on some other GUI process, I get this: Default

Overall, one can get the following types of desktops/winstations:

  • Default for your regular input desktop (that you're using now)
  • Winlogon or Winsta0\Winlogon for a secure desktop (for instance, Windows logon screen)
  • "" or empty string for a Metro (or Modern-UI) Windows 8 app.

So next is the code. It'd be nice if someone could review it:

//'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:


Following your link the following page might help:

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

A process does NOT run under a Window Station, rather a window station is "associated" with a process.




回答4:


There doesn't appear to be any direct method to find the window station associated with a given process.

I see two options:

  • Use code injection to run GetProcessWindowStation and then GetUserObjectInformation in the target process.

  • Use EnumWindowStations, EnumDesktops, and EnumDesktopWindows to iterate through all windows in the session; then use GetWindowThreadProcessId and compare the process ID with that of the target process. Of course that won't work if the process doesn't currently have a window.

You could also try using GetThreadDesktop and GetUserObjectInformation to get the desktop name; I'm not sure whether this includes the window station name or not. If this does work, see Traversing the Thread List in MSDN to enumerate the threads belonging to the process.



来源:https://stackoverflow.com/questions/23144350/how-to-get-window-station-for-a-given-process

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!