English 中文(简体)
启动具有行政管辖法院正常用户特权的申请程序
原标题:Launching an Application with Normal User Privileges from an Administrator-Privileged Instance
I m developing a custom installer for my application, because I need to configure some peculiar settings that InnoSetup and other similar tools don t allow. The installer requires administrator privileges to perform the necessary configurations. At the end of the installation, it starts the newly installed application. The problem is that this application is also launched with administrator privileges, inheriting the installer s permissions. This happens regardless of the method used to launch the application: CreateProcess, CreateProcessAsUser, ShellExecute, and even the obsolete WinExec. In summary, my question is: From an application running with administrator privileges, how can I start another application with the "normal" privileges of the logged Windows user?
问题回答
Raymond Chen has blog articles on this very subject: How can I launch an unelevated process from my elevated process and vice versa? TLDR; ask the user s Explorer instance to run the app, by using IShell­Dispatch2.Shell­Execute(). How can I launch an unelevated process from my elevated process, redux TLDR; ask CreateProcess() to make the user s Explorer instance be the parent of the app, by using the PROC_THREAD_ATTRIBUTE_PARENT_PROCESS attribute.
Most simply way - open the shell window, get its process id, and use this process as the parent process: inline ULONG BOOL_TO_ERROR(BOOL f) { return f ? NOERROR : GetLastError(); } ULONG RunFromShell(PCWSTR lpApplicationName, PWSTR lpCommandLine) { HWND hwnd = GetShellWindow(); if (!hwnd) { return ERROR_NOT_FOUND; } ULONG dwProcessId; if (!GetWindowThreadProcessId(hwnd, &dwProcessId)) { return GetLastError(); } STARTUPINFOEXW si = {{ sizeof(si)}}; SIZE_T s = 0; ULONG dwError; while (ERROR_INSUFFICIENT_BUFFER == (dwError = BOOL_TO_ERROR( InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &s)))) { si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)alloca(s); } if (NOERROR == dwError) { if (HANDLE hProcess = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, dwProcessId)) { if (NOERROR == (dwError = BOOL_TO_ERROR(UpdateProcThreadAttribute( si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(hProcess), 0, 0)))) { PROCESS_INFORMATION pi; if (NOERROR == (dwError = BOOL_TO_ERROR(CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, 0, EXTENDED_STARTUPINFO_PRESENT, 0, 0, &si.StartupInfo, &pi)))) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } } CloseHandle(hProcess); } } return dwError; } Another possible way, probably more correct, but requires more code, is to get the user token via WTSQueryUserToken(), and then use this token in a call to CreateProcessAsUser(). For this, you need to have SeTcbPrivilege, SeAssignPrimaryTokenPrivilege and SeIncreaseQuotaPrivilege. The admin can get these privileges, but enum process, open it tokens, one by one, check that required privilege exist in token, if found - impersonate with this token and after this call WTSQueryUserToken() + CreateProcessAsUser(). #define echo(x) x #define label(x) echo(x)##__LINE__ #define BEGIN_PRIVILEGES(name, n) static const union { TOKEN_PRIVILEGES name; struct { ULONG PrivilegeCount; LUID_AND_ATTRIBUTES Privileges[n];} label(_) = { n, { #define LAA(se) {{se}, SE_PRIVILEGE_ENABLED } #define END_PRIVILEGES }};}; BEGIN_PRIVILEGES(tp_AssignTcb, 3) LAA(SE_TCB_PRIVILEGE), LAA(SE_ASSIGNPRIMARYTOKEN_PRIVILEGE), LAA(SE_INCREASE_QUOTA_PRIVILEGE), END_PRIVILEGES NTSTATUS ImpersonateToken(_In_ const TOKEN_PRIVILEGES* RequiredSet); NTSTATUS RtlRevertToSelf(); HRESULT RunAsUser(PCWSTR lpApplicationName, PWSTR lpCommandLine) { NTSTATUS status = ImpersonateToken(&tp_AssignTcb); if (0 <= status) { HANDLE hToken; //ProcessIdToSessionId(GetCurrentProcessId(), &SessionId); if (NOERROR == (status = BOOL_TO_ERROR(WTSQueryUserToken(RtlGetCurrentPeb()->SessionId, &hToken)))) { STARTUPINFOW si = { sizeof(si) }; PROCESS_INFORMATION pi; if (NOERROR == (status = BOOL_TO_ERROR(CreateProcessAsUserW(hToken, lpApplicationName, lpCommandLine, 0, 0, 0, 0, 0, 0, &si, &pi)))) { CloseHandle(pi.hThread); CloseHandle(pi.hProcess); } CloseHandle(hToken); } RtlRevertToSelf(); } return status; } NTSTATUS RtlRevertToSelf() { HANDLE hToken = 0; return NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hToken, sizeof(hToken)); } extern const SECURITY_QUALITY_OF_SERVICE sqos = { sizeof (sqos), SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE }; extern const OBJECT_ATTRIBUTES oa_sqos = { sizeof(oa_sqos), 0, 0, 0, 0, const_cast(&sqos) }; NTSTATUS ImpersonateToken(_In_ PVOID buf, _In_ const TOKEN_PRIVILEGES* RequiredSet) { NTSTATUS status; union { PVOID pv; PBYTE pb; PSYSTEM_PROCESS_INFORMATION pspi; }; pv = buf; ULONG NextEntryOffset = 0; do { pb += NextEntryOffset; HANDLE hProcess, hToken, hNewToken; CLIENT_ID ClientId = { pspi->UniqueProcessId }; if (ClientId.UniqueProcess) { if (0 <= NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, const_cast(&oa_sqos), &ClientId)) { status = NtOpenProcessToken(hProcess, TOKEN_DUPLICATE, &hToken); NtClose(hProcess); if (0 <= status) { status = NtDuplicateToken(hToken, TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE|TOKEN_QUERY, const_cast(&oa_sqos), FALSE, TokenImpersonation, &hNewToken); NtClose(hToken); if (0 <= status) { status = NtAdjustPrivilegesToken(hNewToken, FALSE, const_cast(RequiredSet), 0, 0, 0); if (STATUS_SUCCESS == status) { status = NtSetInformationThread(NtCurrentThread(), ThreadImpersonationToken, &hNewToken, sizeof(hNewToken)); } NtClose(hNewToken); if (STATUS_SUCCESS == status) { return STATUS_SUCCESS; } } } } } } while (NextEntryOffset = pspi->NextEntryOffset); return STATUS_UNSUCCESSFUL; } NTSTATUS ImpersonateToken(_In_ const TOKEN_PRIVILEGES* RequiredSet) { NTSTATUS status; ULONG cb = 0x40000; do { status = STATUS_INSUFFICIENT_RESOURCES; if (PBYTE buf = new BYTE[cb += 0x1000]) { if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb))) { status = ImpersonateToken(buf, RequiredSet); if (status == STATUS_INFO_LENGTH_MISMATCH) { status = STATUS_UNSUCCESSFUL; } } delete [] buf; } } while(status == STATUS_INFO_LENGTH_MISMATCH); return status; }




相关问题
determining the character set to use

my delphi 2009 app has a basic translation system that uses GNUGetText. i had used some win API calls to prepare the fonts. i thought it was working correctly until recently when someone from Malta ...

Help with strange Delphi 5 IDE problems

Ok, I m going nuts here. For the last (almost) four years, I ve been putting up with some extremely bad behavior from my Delphi 5 IDE. Problems include: Seemingly random errors in coride50.bpl ...

How to write a Remote DataModule to run on a linux server?

i would like to know if there are any solution to do this. Does anyone? The big picture: I want to access data over the web, using my delphi thin clients. But i´would like to keep my server/service ...

How convert string to integer in Oxygene

In Delphi, there is a function StrToInt() that converts a string to an integer value; there is also IntToStr(), which does the reverse. These functions doesn t appear to be part of Oxygene, and I can ...

Quick padding of a string in Delphi

I was trying to speed up a certain routine in an application, and my profiler, AQTime, identified one method in particular as a bottleneck. The method has been with us for years, and is part of a "...

热门标签