Shell Execute From Explorer - c++

Trying to follow this sample:
https://devblogs.microsoft.com/oldnewthing/20040520-00/?p=39243
void ShellExecuteFromExplorer(PCWSTR pszFile, PCWSTR pszParameters = nullptr, PCWSTR pszDirectory = nullptr, PCWSTR pszOperation = nullptr, int nShowCmd = SW_SHOWNORMAL)
{
CComPtr<IShellFolderViewDual> spFolderView;
GetDesktopAutomationObject(IID_PPV_ARGS(&spFolderView));
CComPtr<IDispatch> spdispShell;
spFolderView->get_Application(&spdispShell);
CComQIPtr<IShellDispatch2>(spdispShell)
->ShellExecute(CComBSTR(pszFile),
CComVariant(pszParameters ? pszParameters : L""),
CComVariant(pszDirectory ? pszDirectory : L""),
CComVariant(pszOperation ? pszOperation : L""),
CComVariant(nShowCmd));
}
In this case, how do i retrieve the pid from the process being launch from the ShellExecute?

not need use this way for create unelevated process. for instance we can use shell process (explorer) as parent for new process with help PROC_THREAD_ATTRIBUTE_PARENT_PROCESS. example of code
ULONG ExecFromShell(_In_opt_ PCWSTR lpApplicationName,
_Inout_opt_ PWSTR lpCommandLine)
{
if (HWND hwnd = GetShellWindow())
{
ULONG dwProcessId, dwThreadId;
if (dwThreadId = GetWindowThreadProcessId(hwnd, &dwProcessId))
{
if (HANDLE hProcess = OpenProcess(PROCESS_CREATE_PROCESS|READ_CONTROL,
FALSE, dwProcessId))
{
PROCESS_INFORMATION pi;
STARTUPINFOEXW si { sizeof(si)};
SIZE_T s = 0;
ULONG dwError;
__0:
switch (dwError = InitializeProcThreadAttributeList(
si.lpAttributeList, 1, 0, &s) ? NOERROR : GetLastError())
{
case ERROR_INSUFFICIENT_BUFFER:
if (!si.lpAttributeList)
{
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)alloca(s);
goto __0;
}
break;
case NOERROR:
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hProcess, sizeof(hProcess), 0, 0))
{
ULONG cb = 0;
SECURITY_ATTRIBUTES sa = { sizeof(sa) };
__1:
switch (GetKernelObjectSecurity(hProcess,
DACL_SECURITY_INFORMATION|LABEL_SECURITY_INFORMATION,
sa.lpSecurityDescriptor, cb, &cb) ? NOERROR : GetLastError())
{
case ERROR_INSUFFICIENT_BUFFER:
if (!sa.lpSecurityDescriptor)
{
sa.lpSecurityDescriptor = alloca(cb);
goto __1;
}
break;
case NOERROR:
if (CreateProcessW(lpApplicationName, lpCommandLine, &sa, &sa,
FALSE, EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
dwError = GetLastError();
}
break;
}
}
else
{
dwError = GetLastError();
}
DeleteProcThreadAttributeList(si.lpAttributeList);
break;
}
CloseHandle(hProcess);
return dwError;
}
return GetLastError();
}
}
return ERROR_NOT_FOUND;
}

In this case, how do i retrieve the pid from the process being launch from the ShellExecute?
You can't. just like ::ShellExecute(), IShellDispatch2::ShellExecute() simply does not provide any information about the new process.

Related

Get dos_name of usb on inserting or removal

I have designed a program to detect whenever a usb device is connected or removed. I want to know the either the dos path of the connected device or a Guid like this \\?\Volume{17ee3574-7082-4881-aeae-07893db4e957}\
But instead dbcc_name gives me \\?\STORAGE#Volume#_??_USBSTOR#Disk&Ven_JetFlash&Prod_Transcend_8GB&Rev_1100#546IYBDAPBE1075Q&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}#{53f5630d-b6bf-11d0-94f2-00a0c91efb8b}
Is there anyway to obtain the guid of the connected logical drive or its dos name. My code is:
#define CLS_NAME L"USB_LISTENER_CLASS"
#define HID_CLASSGUID {0x4d1e55b2, 0xf16f, 0x11cf,{ 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30}}
LRESULT message_handler(HWND__* hwnd, UINT uint, WPARAM wparam, LPARAM lparam)
{
switch (uint)
{
case WM_NCCREATE: // before window creation
return true;
break;
case WM_CREATE: // the actual creation of the window
{
// you can get your creation params here..like GUID..
LPCREATESTRUCT params = (LPCREATESTRUCT)lparam;
GUID InterfaceClassGuid = *((GUID*)params->lpCreateParams);
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_VOLUME;
HDEVNOTIFY dev_notify = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
if (dev_notify == NULL)
{
throw std::runtime_error("Could not register for device Notifications!");
}
}
break;
case WM_DEVICECHANGE:
{
PDEV_BROADCAST_HDR(lpdb) = (PDEV_BROADCAST_HDR)lparam;
PDEV_BROADCAST_DEVICEINTERFACE_W lpdbv = (PDEV_BROADCAST_DEVICEINTERFACE_W)lpdb;
switch (wparam)
{
case DBT_DEVICEARRIVAL: //A device or piece of media has been inserted and is now available.
std::cout << "Device Arrived" << std::endl;
std::cout << "GUID od inserted device: " << lpdbv->dbcc_name;
break;
case DBT_DEVICEREMOVECOMPLETE:
std::cout << "Device Removed" << std::endl;
break;
}
}
}
return 0L;
}
void UsbListener::RegisterListener()
{
HWND hWnd = NULL;
WNDCLASSEXW wx;
ZeroMemory(&wx, sizeof(wx));
wx.cbSize = sizeof(WNDCLASSEX);
wx.lpfnWndProc = reinterpret_cast<WNDPROC>(message_handler);
wx.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
wx.style = CS_HREDRAW | CS_VREDRAW;
wx.hInstance = GetModuleHandle(0);
wx.hbrBackground = (HBRUSH)(COLOR_WINDOW);
wx.lpszClassName = CLS_NAME;
GUID guid = HID_CLASSGUID;
if (RegisterClassExW(&wx))
{
hWnd = CreateWindowW(
CLS_NAME, L"DeviceNotificationWindow", WS_ICONIC, 0, 0, CW_USEDEFAULT, 0, HWND_MESSAGE, NULL, GetModuleHandle(0), (void*)&guid
);
}
if (hWnd == NULL)
{
throw std::runtime_error("Could not create message window!");
}
std::cout <<std::endl<< "Listening..." << std::endl;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
I want to know the Dos name of the exact drive that is inserted or removed so that UI can display. I am new to winapi so please bear with the code,
at the begin you need open Mount Manager and save it handle in class associated with your window, for not open/close it multiple time, bit only once
#include <mountmgr.h>
HANDLE hMountMgr = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, 0, 0, OPEN_EXISTING, 0, 0);
for query dos like name (if it exist !) you need first get device name via IOCTL_MOUNTDEV_QUERY_DEVICE_NAME which need send to device, which interface name you have. so you need first open it.
then you can send IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH to Mount Manager and got what you want
volatile UCHAR guz;
ULONG GetDosVolumePath(HANDLE hMountMgr, PCWSTR InterfaceLink, PWSTR* ppszDosPath)
{
*ppszDosPath = 0;
HANDLE hFile = CreateFileW(InterfaceLink, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE) return GetLastError();
union {
PVOID buf;
PMOUNTDEV_NAME pmdn;
};
ULONG dwError;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb, InputBufferLength = sizeof(MOUNTDEV_NAME) + 0x40;
do
{
if (cb < InputBufferLength)
{
cb = RtlPointerToOffset(buf = alloca(InputBufferLength - cb), stack);
}
dwError = BOOL_TO_ERROR(DeviceIoControl(hFile, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, 0, 0, pmdn, cb, &rcb, 0));
InputBufferLength = FIELD_OFFSET(MOUNTDEV_NAME, Name) + pmdn->NameLength;
} while ( dwError == ERROR_MORE_DATA);
CloseHandle(hFile);
if (dwError == NOERROR)
{
union {
PVOID pv;
PMOUNTMGR_VOLUME_PATHS pmvp;
};
cb = 0, rcb = sizeof(MOUNTMGR_VOLUME_PATHS) + 8;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(pv = alloca(rcb - cb), pmdn);
}
dwError = BOOL_TO_ERROR(DeviceIoControl(hMountMgr,
IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATH,
pmdn, InputBufferLength, pmvp, cb, &rcb, 0));
if (dwError == NOERROR)
{
*ppszDosPath = _wcsdup(pmvp->MultiSz);
}
rcb = FIELD_OFFSET(MOUNTMGR_VOLUME_PATHS, MultiSz) + pmvp->MultiSzLength;
} while (dwError == ERROR_MORE_DATA);
}
return dwError;
}

CreateProcessAsUser too fast to track process

We call CreateProcessAsUser() and, after having checked the result, we start tracking (WMI) the process that might create other processes.
In 1 case, the first process is so fast that it creates another process and terminates before we can start tracking it.
I even tried to not check the result and start immediately to track after the call to CreateProcessAsUser(), but it's not fast enough.
My idea is to start the process from a launcher.exe so we can track all the generated processes.
Is there any other alternative solution? We have the PID of the terminated process.
if we start child process and want way when it and all the children processes terminate we can use job object. general steps
create new job object via CreateJobObjectW
set JobObjectAssociateCompletionPortInformation with
SetInformationJobObject
create new process in suspended state (use CREATE_SUSPENDED
flag)
add process to job via AssignProcessToJobObject
resume new process with ResumeThread
now system will be send notifications to our completion port when new process will be started or exit. when no more process in job - will be JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO message post to port - Indicates that the active process count has been decremented to 0
of course we need create I/O completion port too by CreateIoCompletionPort and one or more (if only for this task - single thread more than enough) threads which will be call GetQueuedCompletionStatus on port until end signal.
we can for example use lpCompletionKey as pointer to object with virtual functions, and every object know how handle action event. demo code:
struct __declspec(novtable) PortTask
{
virtual bool OnIoCompletion(OVERLAPPED* lpOverlapped, ULONG NumberOfBytesTransferred) = 0;
};
struct EndTask : public PortTask
{
virtual bool OnIoCompletion(OVERLAPPED* /*lpOverlapped*/, ULONG /*NumberOfBytesTransferred*/)
{
DbgPrint("%s<%p>\n", __FUNCTION__, this);
delete this;
return false;
}
};
struct IOPort
{
HANDLE CompletionPort;
LONG dwRefCount;
IOPort() : dwRefCount(1), CompletionPort(0) {
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
~IOPort(){
if (CompletionPort) CloseHandle(CompletionPort);
DbgPrint("%s<%p>\n", __FUNCTION__, this);
}
void AddRef(){
InterlockedIncrementNoFence(&dwRefCount);
}
void Release(){
if (!InterlockedDecrement(&dwRefCount)) {
delete this;
}
}
static ULONG WINAPI PortThread(PVOID This)
{
union {
ULONG_PTR CompletionKey;
PortTask* pTask;
};
ULONG NumberOfBytesTransferred;
OVERLAPPED* lpOverlapped;
HANDLE CompletionPort = reinterpret_cast<IOPort*>(This)->CompletionPort;
while (GetQueuedCompletionStatus(CompletionPort, &NumberOfBytesTransferred, &CompletionKey, &lpOverlapped, INFINITE) &&
pTask->OnIoCompletion(lpOverlapped, NumberOfBytesTransferred)) continue;
reinterpret_cast<IOPort*>(This)->Release();
return 0;
}
ULONG Create()
{
if (CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1))
{
AddRef();
if (HANDLE hThread = CreateThread(0, 0, PortThread, this, 0, 0))
{
CloseHandle(hThread);
return NOERROR;
}
Release();
}
return GetLastError();
}
ULONG Stop()
{
if (EndTask* pTask = new EndTask)
{
if (!PostQueuedCompletionStatus(CompletionPort, 0, (ULONG_PTR)pTask, 0))
{
ULONG dwError = GetLastError();
delete pTask;
return dwError;
}
return NOERROR;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
};
struct ActiveProcessZeroTask : public PortTask
{
//HWND hwnd; // in real code you send some message to hwnd instead thread
HANDLE _hJob;
ULONG _dwThreadId;
ActiveProcessZeroTask() : _hJob(0), _dwThreadId(GetCurrentThreadId()) { }
~ActiveProcessZeroTask() {
CloseHandle(_hJob);
PostThreadMessageW(_dwThreadId, WM_QUIT, 0, 0);
}
virtual bool OnIoCompletion(OVERLAPPED* dwProcessId, ULONG MessageId)
{
DbgPrint("%s<%p>(%x %p)\n", __FUNCTION__, this, MessageId, dwProcessId);
switch (MessageId)
{
case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
DbgPrint("%p - ACTIVE_PROCESS_ZERO\n", dwProcessId);
delete this;
break;
case JOB_OBJECT_MSG_NEW_PROCESS:
DbgPrint("%p - NEW_PROCESS\n", dwProcessId);
break;
case JOB_OBJECT_MSG_EXIT_PROCESS:
DbgPrint("%p - EXIT_PROCESS\n", dwProcessId);
break;
case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS:
DbgPrint("%p - ABNORMAL_EXIT_PROCESS\n", dwProcessId);
break;
}
return true;
}
ULONG Create(HANDLE CompletionPort, PCWSTR ApplicationName)
{
if (HANDLE hJob = CreateJobObjectW(0, 0))
{
_hJob = hJob;
JOBOBJECT_ASSOCIATE_COMPLETION_PORT jacp = { this, CompletionPort };
if (SetInformationJobObject(hJob, JobObjectAssociateCompletionPortInformation, &jacp, sizeof(jacp)))
{
STARTUPINFO si = { sizeof(si)};
PROCESS_INFORMATION pi;
if (CreateProcessW(ApplicationName, 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi))
{
ULONG dwError = NOERROR;
if (!AssignProcessToJobObject(hJob, pi.hProcess) ||
!ResumeThread(pi.hThread))
{
dwError = GetLastError();
TerminateProcess(pi.hProcess, 0);
}
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return dwError;
}
}
}
return GetLastError();
}
};
void demo()
{
if (IOPort* port = new IOPort)
{
if (port->Create() == NOERROR)
{
MessageBoxW(0, 0, L"just for demo #1", MB_ICONINFORMATION);
// exec cmd for demo
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
if (ActiveProcessZeroTask* pTask = new ActiveProcessZeroTask)
{
if (pTask->Create(port->CompletionPort, ApplicationName) != NOERROR)
{
delete pTask;
}
}
}
// wait all childs exit
MessageBoxW(0, 0, L"Wait for MSG_ACTIVE_PROCESS_ZERO", MB_ICONINFORMATION);
// stop track thread
if (port->Stop() != NOERROR) __debugbreak();
}
port->Release();
}
{
MSG msg;
// remove Wm_QUIT
while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) continue;
MessageBoxW(0, 0, L"just for demo #2", MB_ICONINFORMATION);
}
}
You need to implement an explicit synchronization mechanism (semaphore). Bellow an algorithm :
In the parent process :
semaphore my_semaphore = CreateSemaphore ("my_semphaore");
args my_arguments ("my_semphaore");
CreateProcessAsUser (my_arguments);
Create_Asynchronous_Thread { releasesemaphore (my_semaphore );} // unblock shild process}
waitforSingleObject (shild_process_PID)
In the shild process :
// do something ...
semaphore my_semaphore = CreateSemaphore ("my_semphaore");
// do something (parent is blocked)
waitforsingleobject (my_semaphore);

How can I "unelevate" a process

I have an elevated process that has been started after the user has answered "Yes" in the UAC dialog.
The process is started fine, everything works as expected.
Now I need to "unelevate" that process at some point, in other words the process should become not elevated exactly as if it had been launched normally by the user.
Sample scenario
User A is logged on
User A launches process P1 which will be elevated via UAC
Elevated process P1 lauchches process P2 and P2 should'nt be elevated and should run again under user A.
Is there a way to do this?
the elevated process have linked token - it refers to non elevated user session. we can use this linked token in 2 ways:
first way:
get it as TokenPrimary (for this we need have SE_TCB_PRIVILEGE
when we query this token)
call CreateProcessAsUser with this token. for this we need also
SE_ASSIGNPRIMARYTOKEN_PRIVILEGE and SE_INCREASE_QUOTA_PRIVILEGE
for get all this privileges - enumerate processes, query it tokens,
and if process token have all this 3 privileges - impersonate with
it, before call CreateProcessAsUser. because elevated token have
SE_DEBUG_PRIVILEGE the task is possible
second way:
query the logon session id from linked token (AuthenticationId
from TOKEN_STATISTICS)
found process with the same AuthenticationId in process token.
use this process as parent process by help
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
implementation for way 1:
static volatile UCHAR guz;
ULONG RunNonElevated(HANDLE hToken, HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
ULONG err;
PVOID stack = alloca(guz);
ULONG cb = 0, rcb = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[SE_MAX_WELL_KNOWN_PRIVILEGE]);
union {
PVOID buf;
PTOKEN_PRIVILEGES ptp;
};
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (GetTokenInformation(hToken, TokenPrivileges, buf, cb, &rcb))
{
if (ULONG PrivilegeCount = ptp->PrivilegeCount)
{
int n = 3;
BOOL fAdjust = FALSE;
PLUID_AND_ATTRIBUTES Privileges = ptp->Privileges;
do
{
switch (Privileges->Luid.LowPart)
{
case SE_ASSIGNPRIMARYTOKEN_PRIVILEGE:
case SE_INCREASE_QUOTA_PRIVILEGE:
case SE_TCB_PRIVILEGE:
if (!(Privileges->Attributes & SE_PRIVILEGE_ENABLED))
{
Privileges->Attributes |= SE_PRIVILEGE_ENABLED;
fAdjust = TRUE;
}
if (!--n)
{
err = NOERROR;
if (DuplicateTokenEx(hToken,
TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE,
0, SecurityImpersonation, TokenImpersonation,
&hToken))
{
if (fAdjust)
{
AdjustTokenPrivileges(hToken, FALSE, ptp, rcb, NULL, NULL);
err = GetLastError();
}
if (err == NOERROR)
{
if (SetThreadToken(0, hToken))
{
TOKEN_LINKED_TOKEN tlt;
if (GetTokenInformation(hMyToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
STARTUPINFO si = { sizeof (si) };
PROCESS_INFORMATION pi;
if (!CreateProcessAsUserW(tlt.LinkedToken, lpApplicationName, lpCommandLine,
NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
err = GetLastError();
}
CloseHandle(tlt.LinkedToken);
if (err == NOERROR)
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
}
else
{
err = GetLastError();
}
SetThreadToken(0, 0);
}
else
{
err = GetLastError();
}
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
}
} while (Privileges++, --PrivilegeCount);
}
return ERROR_NOT_FOUND;
}
} while ((err = GetLastError()) == ERROR_INSUFFICIENT_BUFFER);
return err;
}
ULONG RunNonElevated(HANDLE hMyToken, PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
static TOKEN_PRIVILEGES tp = {
1, { { { SE_DEBUG_PRIVILEGE } , SE_PRIVILEGE_ENABLED } }
};
AdjustTokenPrivileges(hMyToken, FALSE, &tp, sizeof(tp), NULL, NULL);
ULONG err = NOERROR;
// much more effective of course use NtQuerySystemInformation(SystemProcessesAndThreadsInformation) here
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0), hToken;
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
if (Process32FirstW(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID))
{
if (OpenProcessToken(hProcess, TOKEN_QUERY|TOKEN_DUPLICATE, &hToken))
{
err = RunNonElevated(hToken, hMyToken, lpApplicationName, lpCommandLine);
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
CloseHandle(hProcess);
}
else
{
err = GetLastError();
}
}
} while (err && Process32NextW(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
return err;
}
ULONG RunNonElevated(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_ELEVATION_TYPE tet;
ULONG rcb;
if (GetTokenInformation(hToken, ::TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
RunNonElevated(hToken, lpApplicationName, lpCommandLine);
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
implementation for way 2:
ULONG CreateProcessEx(HANDLE hProcess,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
SIZE_T Size = 0;
STARTUPINFOEX si = { sizeof(si) };
PROCESS_INFORMATION pi;
InitializeProcThreadAttributeList(0, 1, 0, &Size);
ULONG err = GetLastError();
if (err = ERROR_INSUFFICIENT_BUFFER)
{
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)alloca(Size);
if (InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &Size))
{
if (UpdateProcThreadAttribute(si.lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), 0, 0) &&
CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, 0,
EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
err = GetLastError();
}
DeleteProcThreadAttributeList(si.lpAttributeList);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(LUID AuthenticationId,
PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
ULONG err = ERROR_NOT_FOUND;
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32W pe = { sizeof(pe) };
ULONG rcb;
if (Process32First(hSnapshot, &pe))
{
err = ERROR_NOT_FOUND;
BOOL found = FALSE;
do
{
if (pe.th32ProcessID && pe.th32ParentProcessID)
{
if (HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION|PROCESS_CREATE_PROCESS, FALSE, pe.th32ProcessID))
{
HANDLE hToken;
if (OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
{
TOKEN_STATISTICS ts;
if (GetTokenInformation(hToken, TokenStatistics, &ts, sizeof(ts), &rcb))
{
if (ts.AuthenticationId.LowPart == AuthenticationId.LowPart &&
ts.AuthenticationId.HighPart == AuthenticationId.HighPart)
{
found = TRUE;
err = CreateProcessEx(hProcess,
lpApplicationName,
lpCommandLine);
}
}
CloseHandle(hToken);
}
CloseHandle(hProcess);
}
}
} while (!found && Process32Next(hSnapshot, &pe));
}
else
{
err = GetLastError();
}
CloseHandle(hSnapshot);
}
else
{
err = GetLastError();
}
return err;
}
ULONG CreateProcessEx(PCWSTR lpApplicationName,
PWSTR lpCommandLine)
{
HANDLE hToken;
ULONG err = NOERROR;
if (OpenProcessToken(NtCurrentProcess(), TOKEN_QUERY, &hToken))
{
union {
TOKEN_ELEVATION_TYPE tet;
TOKEN_LINKED_TOKEN tlt;
};
ULONG rcb;
if (GetTokenInformation(hToken, TokenElevationType, &tet, sizeof(tet), &rcb))
{
if (tet == TokenElevationTypeFull)
{
if (GetTokenInformation(hToken, TokenLinkedToken, &tlt, sizeof(tlt), &rcb))
{
TOKEN_STATISTICS ts;
BOOL fOk = GetTokenInformation(tlt.LinkedToken, TokenStatistics, &ts, sizeof(ts), &rcb);
CloseHandle(tlt.LinkedToken);
if (fOk)
{
err = CreateProcessEx(ts.AuthenticationId,
lpApplicationName,
lpCommandLine);
}
else
{
err = GetLastError();
}
}
else
{
err = GetLastError();
}
}
else
{
err = ERROR_ALREADY_ASSIGNED;
}
}
else
{
err = GetLastError();
}
CloseHandle(hToken);
}
else
{
err = GetLastError();
}
return err;
}
and test:
WCHAR ApplicationName[MAX_PATH];
if (GetEnvironmentVariableW(L"ComSpec", ApplicationName, RTL_NUMBER_OF(ApplicationName)))
{
WCHAR cmdline[] = L"cmd.exe /k whoami /priv /groups\r\n";
CreateProcessEx(ApplicationName, cmdline);
RunNonElevated(ApplicationName, cmdline);
}
for way #2 theoretical we can not found process with same logon id (AuthenticationId) as in our linked token. but way #1 always must work. always exist system process which have SeTcbPrivilege (for get primary form of linked token) + SeAssignPrimaryTokenPrivilege (for CreateProcessAsUser) (SeIncreaseQuotaPrivilege listen in msdn as typical require for CreateProcessAsUser but in my test this worked even if this privilege not enabled ). however all system processes (running as LocalSystem) have this 3 privilege in token (begin from smss.exe) and some system processes always run in system.
so way #1 must never fail and preferred. also we can here use for example inherited handles from our process, for interact with child process. this is impossible in way #2. it shown rather for completeness of the picture
at begin we check TOKEN_ELEVATION_TYPE and do job, only if it is TokenElevationTypeFull. in case TokenElevationTypeLimited we not elevated process - so nothing todo.
case TokenElevationTypeDefault mean or UAC if off (LUA disabled) or we run as built-in Administrator, and lua not filter tokens for this account (so all processes is "elevated" or more exactly it tokens not filtered via CreateRestrictedToken(..LUA_TOKEN..) ) - in this case also no sense try run "not elevated" process under this user

EnumDesktopWindows Error: (183) Cannot create a file when that file already exists

Anyone know why returns 183 in call EnumDesktopWindows
This process is an service running in System LocalService
I'm trying to put the window in the top, because the process starts minimized.
Thank for Help
My Code:
BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam )
{
DWORD dwPID;
GetWindowThreadProcessId( hwnd, &dwPID );
if( dwPID == lParam ) {
SetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE );
SwitchToThisWindow(hwnd, true);
SetFocus( hwnd );
return FALSE;
}
return TRUE;
}
BOOL CALLBACK EnumDesktopProc(LPTSTR lpszDesktop, LPARAM lParam) {
HDESK hDesk = OpenDesktop(lpszDesktop, NULL, FALSE, GENERIC_ALL);
if(hDesk != NULL) {
if(!EnumDesktopWindows(hDesk,&EnumWindowsProc, lParam)) {
//This call returns (183) Cannot create a file when that file already exists
}
CloseDesktop(hDesk);
}
return TRUE;
}
BOOL CALLBACK EnumWindowStationProc(LPTSTR lpszWindowStation, LPARAM lParam)
{
HWINSTA hWinStat = OpenWindowStation(lpszWindowStation,FALSE,WINSTA_ENUMDESKTOPS|WINSTA_ENUMERATE);
if(hWinStat) {
SetProcessWindowStation(hWinStat);
EnumDesktops(hWinStat,&EnumDesktopProc,lParam);
CloseWindowStation(hWinStat);
}
return TRUE;
}
bool Utils::execIntoDifferentSession(const std::wstring &aPath, const std::wstring &aParams, const std::wstring &aMode)
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
BOOL bResult = FALSE;
DWORD dwSessionId,winlogonPid;
HANDLE hUserToken,hUserTokenDup,hPToken,hProcess;
DWORD dwCreationFlags;
// Log the client on to the local computer.
dwSessionId = WTSGetActiveConsoleSessionId();
//////////////////////////////////////////
// Find the winlogon process
////////////////////////////////////////
PROCESSENTRY32 procEntry;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnap == INVALID_HANDLE_VALUE)
return false;
procEntry.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(hSnap, &procEntry))
return false;
do {
if (_wcsicmp(procEntry.szExeFile, L"winlogon.exe") == 0) {
// We found a winlogon process...make sure it's running in the console session
DWORD winlogonSessId = 0;
if (ProcessIdToSessionId(procEntry.th32ProcessID, &winlogonSessId) && winlogonSessId == dwSessionId) {
winlogonPid = procEntry.th32ProcessID;
break;
}
}
} while (Process32Next(hSnap, &procEntry));
WTSQueryUserToken(dwSessionId,&hUserToken);
dwCreationFlags = NORMAL_PRIORITY_CLASS|CREATE_NEW_CONSOLE;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb= sizeof(STARTUPINFO);
si.lpDesktop = L"winsta0\\default";
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWNORMAL|SW_RESTORE;
ZeroMemory(&pi, sizeof(pi));
TOKEN_PRIVILEGES tp;
LUID luid;
hProcess = OpenProcess(MAXIMUM_ALLOWED,FALSE,winlogonPid);
if(!::OpenProcessToken(hProcess,TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY
|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY|TOKEN_ADJUST_SESSIONID
|TOKEN_READ|TOKEN_WRITE,&hPToken))
{
return false;
}
if (!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))
return false;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
DuplicateTokenEx(hPToken,MAXIMUM_ALLOWED,NULL,SecurityIdentification,TokenPrimary,&hUserTokenDup);
//Adjust Token privilege
SetTokenInformation(hUserTokenDup,TokenSessionId,(void*)dwSessionId,sizeof(DWORD));
if (!AdjustTokenPrivileges(hUserTokenDup,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),(PTOKEN_PRIVILEGES)NULL,NULL))
return false;
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
return false;
LPVOID pEnv = NULL;
if(CreateEnvironmentBlock(&pEnv,hUserTokenDup,TRUE))
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
else
pEnv = NULL;
// Launch the process in the client's logon session.
std::wstring params = aParams;
std::wstring path = aPath;
if(aMode == L"select") {
TCHAR infoBuffer[MAX_PATH];
GetSystemWindowsDirectory(infoBuffer, MAX_PATH);
std::wstring windowsDir(infoBuffer);
path = windowsDir+L"\\explorer.exe";
params = L" /n, /select,"+replaceString(aPath, L"\\\\", L"\\");
}
bResult = CreateProcessAsUser(
hUserTokenDup, // client's access token
path.c_str(), // file to execute
params.length() > 0 ? stringToLPWSTR(wideToUtf8(params)) : NULL, // command line
NULL, // pointer to process SECURITY_ATTRIBUTES
NULL, // pointer to thread SECURITY_ATTRIBUTES
FALSE, // handles are not inheritable
dwCreationFlags, // creation flags
pEnv, // pointer to new environment block
NULL, // name of current directory
&si, // pointer to STARTUPINFO structure
&pi // receives information about new process
);
EnumWindowStations(&EnumWindowStationProc, (LPARAM)(pi.dwProcessId));
// End impersonation of client.
//GetLastError Shud be 0
int rv = GetLastError();
//Perform All the Close Handles task
CloseHandle(hProcess);
CloseHandle(hUserToken);
CloseHandle(hUserTokenDup);
CloseHandle(hPToken);
return !rv;
}
Error 183 is ERROR_ALREADY_EXISTS. EnumDesktopWindows() does not set that error, so it must be a carry-over from an earlier API call. If you read the documentation says this:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682615.aspx
You must ensure that the callback function sets SetLastError if it fails.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682614.aspx
If the callback function fails, the return value is zero. The callback function can call SetLastError to set an error code for the caller to retrieve by calling GetLastError.
So try something more like this:
struct WndInfo
{
DWORD dwProcessID;
HWND hWnd;
};
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
WndInfo *pInfo = (WndInfo*) lParam;
DWORD dwPID;
GetWindowThreadProcessId(hwnd, &dwPID);
if (dwPID == pInfo->dwProcessID)
{
pInfo->hWnd = hwnd;
SetLastError(0);
return FALSE;
}
return TRUE;
}
BOOL CALLBACK EnumDesktopProc(LPTSTR lpszDesktop, LPARAM lParam)
{
HDESK hDesk = OpenDesktop(lpszDesktop, NULL, FALSE, GENERIC_ALL);
if (hDesk != NULL)
{
if (!EnumDesktopWindows(hDesk, &EnumWindowsProc, lParam))
{
if (GetLastError() != 0)
{
// handle error as needed...
}
}
CloseDesktop(hDesk);
WndInfo *pInfo = (WndInfo*) lParam;
if (pInfo->hWnd != NULL)
{
SetLastError(0);
return FALSE;
}
}
return TRUE;
}
BOOL CALLBACK EnumWindowStationProc(LPTSTR lpszWindowStation, LPARAM lParam)
{
HWINSTA hWinStat = OpenWindowStation(lpszWindowStation, FALSE, WINSTA_ENUMDESKTOPS|WINSTA_ENUMERATE);
if (hWinStat != NULL)
{
SetProcessWindowStation(hWinStat);
if (!EnumDesktops(hWinStat, &EnumDesktopProc, lParam))
{
if (GetLastError() != 0)
{
// handle error as needed...
}
}
CloseWindowStation(hWinStat);
WndInfo *pInfo = (WndInfo*) lParam;
if (pInfo->hWnd != NULL)
{
SetLastError(0);
return FALSE;
}
}
return TRUE;
}
HWND findWindowForProcess(DWORD PID)
{
WndInfo info;
info.dwProcessID = PID;
info.hWnd = NULL;
if (!EnumWindowStations(&EnumWindowStationProc, (LPARAM)&info))
{
if (GetLastError() != 0)
{
// handle error as needed...
}
}
return info.hWnd;
}
bool Utils::execIntoDifferentSession(const std::wstring &aPath, const std::wstring &aParams, const std::wstring &aMode)
{
...
bResult = CreateProcessAsUser(...);
if (bResult)
{
HWND hWnd = findWindowForProcess(pi.dwProcessId);
if (hWnd != NULL)
{
SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOMOVE);
SwitchToThisWindow(hWnd, TRUE);
SetFocus(hWnd);
}
}
...
}
With that said, since all you are really trying to do is execute a new process in a specific user session, you don't need to bother with all that enumeration logic. You don't need to find the WinLogon process at all, you already have the user's token from WTSQueryUserToken() so just duplicate+adjust that token as needed. And you are not doing anything useful in your window enumeration that the new process would not already do by default when it is started, so just get rid of that logic, too.
And then finally fix your error handling (or lack of) so you can close any handles that are open and not leak them.

CreateProcessAsUser fail,use GetLastError() to get the error code is 1314

I use win7 os and the develop environment is vs2005.
The situation is I want to create the process as current account's priviledge.(such as: in the normal account ,right click the program choice "run as admin" )
I refer to other people's way:
1.get the token of the process explorer.exe;
2.improve the priviledge;
3.use the CreateProcessAsUser to create a process.
But the CreateProcessAsUser failed,and use GetLastError() to get the error code is 1314.
Because of that, I think I'am crazy now.
Can you tell me what's wrong in my program. Thank you!!!
#include <iostream>
using namespace std;
#include "windows.h"
#include "tlhelp32.h"
BOOL GetProcessTokenByName(HANDLE &hToken, LPTSTR szProcessName)
{
// var init
STARTUPINFO st;
PROCESS_INFORMATION pi;
PROCESSENTRY32 ps;
HANDLE hSnapshot;
ZeroMemory(&st, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATIO
N));
st.cb = sizeof(STARTUPINFO);
ZeroMemory(&ps,sizeof(PROCESSENTRY32));
ps.dwSize = sizeof(PROCESSENTRY32);
// find the explorer.exe
hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0);
if(hSnapshot == INVALID_HANDLE_VALUE)
{
return FALSE;
}
if(!Process32First(hSnapshot,&ps))
{
return FALSE;
}
do
{
wprintf(_T("%s , %u\n"), ps.szExeFile, ps.th32ProcessID);
// compare the process name
if(lstrcmpi(ps.szExeFile,szProcessName)==0)
{ // find
//*lpPID = ps.th32ProcessID;
//CloseHandle(hSnapshot);
//return TRUE;
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, ps.th32ProcessID);
BOOL bRet = FALSE;
HANDLE tmpToken;
if( OpenProcessToken(hProcess, /*TOKEN_QUERY*/TOKEN_ALL_ACCESS, &tmpToken) )
{
bRet = DuplicateTokenEx(
tmpToken, //_In_ HANDLE hExistingToken,
MAXIMUM_ALLOWED, //_In_ DWORD dwDesiredAccess,
NULL, //_In_opt_ LPSECURITY_ATTRIBUTES lpTokenAttributes,
SecurityIdentification, //_In_ SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
TokenPrimary, //_In_ TOKEN_TYPE TokenType,
&hToken //_Out_ PHANDLE phNewToken
);
//DWORD dwSessionId = WTSGetActiveConsoleSessionId();
//SetTokenInformation(hToken,TokenSessionId,(void*)dwSessionId,sizeof(DWORD));
//SetPrivilege(hToken, SE_ASSIGNPRIMARYTOKEN_NAME, TRUE);
}
else
{
printf("OpenProcessToken error: %u\n", GetLastError());
}
CloseHandle (hSnapshot);
return (bRet);
}
}while(Process32Next(hSnapshot,&ps));
// didn't find
CloseHandle(hSnapshot);
return FALSE;
}
BOOL RunasUser( )
{
HANDLE hToken;
if( GetProcessTokenByName( hToken, _T("explorer.exe") ) )
{
if( hToken != INVALID_HANDLE_VALUE )
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb= sizeof(STARTUPINFO);
si.lpDesktop = TEXT("winsta0\\default");
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount =1;
if(!LookupPrivilegeValue(NULL,SE_ASSIGNPRIMARYTOKEN_NAME/*SE_DEBUG_NAME*/,&tp.Privileges[0].Luid))
{
printf("LookupPrivilegeValue value Error: %u\n",GetLastError());
}
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL) )
{
printf("Adjust Privilege value Error: %u\n",GetLastError());
}
}
printf("Adjust Privilege\n");
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount =1;
if(!LookupPrivilegeValue(NULL,SE_INCREASE_QUOTA_NAME/*SE_DEBUG_NAME*/,&tp.Privileges[0].Luid))
{
printf("LookupPrivilegeValue value Error: %u\n",GetLastError());
}
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, NULL) )
{
printf("Adjust Privilege value Error: %u\n",GetLastError());
}
}
BOOL bResult = CreateProcessAsUser(
hToken, //_In_opt_ HANDLE hToken,
_T("D:\\GetMac.exe"), //_In_opt_ LPCTSTR lpApplicationName,
NULL, //_Inout_opt_ LPTSTR lpCommandLine,
NULL, //_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
NULL, //_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
FALSE, //_In_ BOOL bInheritHandles,
NORMAL_PRIORITY_CLASS, //_In_ DWORD dwCreationFlags,
NULL, //_In_opt_ LPVOID lpEnvironment,
NULL, //_In_opt_ LPCTSTR lpCurrentDirectory,
&si, //_In_ LPSTARTUPINFO lpStartupInfo,
&pi //_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
CloseHandle(hToken);
if( bResult )
{
//succeed
return TRUE;
}
else
{ //fail
DWORD dwErr = GetLastError();
printf( "error: %u\n", dwErr );
}
}
}
else
{
printf("GetProcessTokenByName fail\n");
}
return FALSE;
}
int _tmain(int argc, _TCHAR* argv[])
{
BOOL bRet = RunasUser();
printf("result: %d\n", bRet);
system("pause");
return 0;
}