问题
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:
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).
For each window station, call EnumDesktops() to enumerate its desktops.
For each desktop, call EnumDesktopWindows() to enumerate its top-level windows.
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:
Call OpenProcess() to get a
HANDLEfrom the target process ID.Call NtQueryInformationProcess() to retrieve the address of the process's PEB structure.
Call ReadProcessMemory() to read the
PEB. It'sProcessParams.DesktopNamefield 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).Parse the
DesktopNameto extract the window station and desktop names.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:
- If I call it for my own process, I get something like this:
Winsta0\Default - If I call it on some other GUI process, I get this:
Default
Overall, one can get the following types of desktops/winstations:
Defaultfor your regular input desktop (that you're using now)WinlogonorWinsta0\Winlogonfor 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
GetProcessWindowStationand thenGetUserObjectInformationin the target process.Use
EnumWindowStations,EnumDesktops, andEnumDesktopWindowsto iterate through all windows in the session; then useGetWindowThreadProcessIdand 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