我正在尝试从Windows 7-10上的升级过程创建一个非高效的过程。
我使用以下作为参考:
FAQ: How do I start a program as the desktop user from an elevated app?
现在,这种方法非常有效,但是由于可能的遗留检查,似乎CreateProcessWithTokenW()
只允许cmdline
参数小于或等于1024个字符。
我需要通过的cmdline
远不止于此,导致E_INVALIDARG
错误。
有没有人遇到和我一样的问题?如果是这样,你是如何解决这个绝对荒谬的1024字符限制?
也许尝试使用CreateProcessAsUserW()
代替?我知道它需要特殊权限,但如果您的进程以管理员身份运行,则有一种方法可以克服它。
我们的想法是在调用API之前模拟强大的令牌。强大的令牌被定义为拥有所有所需权限的令牌。
首先,通过像NtQuerySystemInformation()
这样的API枚举进程ID。对于每个进程ID,打开进程令牌并使用GetTokenInformation(hToken, TokenPrivileges, ...)
获取令牌拥有的特权的信息。如果令牌拥有我们想要的所有权限,我们可以通过DuplicateTokenEx(..., TokenImpersonation, &hTokenImp)
从其中复制模拟令牌(并且当然要停止枚举)。
现在我们拥有了强大的令牌,我们将检查它所需的权限,看看它们是否都已启用。使用PrivilegeCheck()
检查状态并使用AdjustTokenPrivileges()
启用它。最后,让当前线程通过SetThreadToken(NULL, hTokenImp)
模拟令牌,然后调用你喜欢的CreateProcessAsUserW()
,并停止SetThreadToken(NULL, NULL)
的模仿。
至于CreateProcessAsUserW()
的令牌,我建议你使用WTSQueryUserToken()
使用当前用户会话的主令牌,因为即使你做了OTS elevation它也会按预期工作。上次我尝试使用链接令牌,如here所述,并发现它不适用于OTS提升。
这是ANSI C中的测试代码。以管理员身份运行时,它将自动运行另一个自身实例并显示命令行长度。是的,CreateProcessAsUserW()
支持超过1024的命令行。:)
#include <stdio.h>
#include <Windows.h>
#include <objbase.h>
#include "EnumProcessesId.h"
#define MY_LuidEqual(a, b) \
( ((a)->HighPart == (b)->HighPart) && ((a)->LowPart == (b)->LowPart) )
static LPVOID MyAllocZero(SIZE_T cb)
{
LPVOID ptr = CoTaskMemAlloc(cb);
if (ptr) { ZeroMemory(ptr, cb); }
return ptr;
}
static void MyFree(LPVOID ptr)
{
CoTaskMemFree(ptr);
}
static DWORD MyWTSGetActiveConsoleSessionId(void)
{
typedef DWORD(WINAPI *fn_t)(void);
fn_t fn = (fn_t)GetProcAddress(LoadLibraryA("kernel32"),
"WTSGetActiveConsoleSessionId");
if (fn) { return fn(); }
return 0;
}
static BOOL MyWTSQueryUserToken(DWORD sessId, HANDLE *phToken)
{
typedef BOOL(WINAPI *fn_t)(DWORD, HANDLE*);
fn_t fn = (fn_t)GetProcAddress(LoadLibraryA("wtsapi32"),
"WTSQueryUserToken");
if (fn) {
return fn(sessId, phToken);
}
return FALSE;
}
static BOOL MyPrivIsEnabled(HANDLE hToken, LUID const *pPrivLuid)
{
BOOL isEnabled = FALSE;
PRIVILEGE_SET ps = { 0 };
ps.PrivilegeCount = 1;
ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED_BY_DEFAULT;
PrivilegeCheck(hToken, &ps, &isEnabled);
if (!isEnabled) {
ps.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED;
PrivilegeCheck(hToken, &ps, &isEnabled);
}
return isEnabled;
}
static HRESULT MyEnablePriv(HANDLE hToken, LUID const *pPrivLuid, BOOL enable)
{
BOOL ok = FALSE;
TOKEN_PRIVILEGES tp = { 0 };
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0;
tp.Privileges[0].Luid = *pPrivLuid;
ok = AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
return ok ? S_OK : HRESULT_FROM_WIN32(GetLastError());
}
typedef struct {
/* in : */
LUID const *pLuidAPT;
LUID const *pLuidIQN;
LUID const *pLuidTCB;
/* out : */
HANDLE hptPowerful;
} MyEnumPowerfulTokenData_t;
static BOOL CALLBACK MyEnumPowerfulTokenProc(DWORD pid, void * user)
{
DWORD const ProcessQueryLimitedInfo = 0x1000;
MyEnumPowerfulTokenData_t *pData = user;
BOOL ok = FALSE, wantContinue = TRUE;
HANDLE hProc = NULL;
HANDLE hProcToken = NULL;
DWORD i = 0, cbTP = 0;
TOKEN_PRIVILEGES *pTP = NULL;
BOOL gotAPT = FALSE, gotIQN = FALSE, gotTCB = FALSE;
/* Get process token */
hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!hProc) {
hProc = OpenProcess(ProcessQueryLimitedInfo, FALSE, pid);
}
if (!hProc) goto eof;
ok = OpenProcessToken(hProc, TOKEN_QUERY | TOKEN_DUPLICATE, &hProcToken);
if (!ok) goto eof;
/* Check if token possesses desired privileges */
GetTokenInformation(hProcToken, TokenPrivileges, NULL, 0, &cbTP);
if (!cbTP) goto eof;
pTP = MyAllocZero(cbTP);
if (!pTP) goto eof;
ok = GetTokenInformation(hProcToken, TokenPrivileges, pTP, cbTP, &cbTP);
if (!ok) goto eof;
for (i = 0; i < pTP->PrivilegeCount; ++i)
{
LUID const *pThat = &(pTP->Privileges[i].Luid);
if (gotAPT && gotIQN && gotTCB) {
wantContinue = FALSE;
pData->hptPowerful = hProcToken;
hProcToken = NULL; /* to avoid eof CloseHandle() */
break;
}
else if (!gotAPT && MY_LuidEqual(pThat, pData->pLuidAPT)) {
gotAPT = TRUE;
}
else if (!gotIQN && MY_LuidEqual(pThat, pData->pLuidIQN)) {
gotIQN = TRUE;
}
else if (!gotTCB && MY_LuidEqual(pThat, pData->pLuidTCB)) {
gotTCB = TRUE;
}
}
eof:
if (pTP) { MyFree(pTP); }
if (hProcToken) { CloseHandle(hProcToken); }
if (hProc) { CloseHandle(hProc); }
return wantContinue;
}
static HRESULT MyCreateProcess(LPWSTR szCmdLine)
{
HRESULT hr = 0;
BOOL ok = FALSE;
LUID luidAPT = { 0 }; /* SE_ASSIGNPRIMARYTOKEN_NAME */
LUID luidIQN = { 0 }; /* SE_INCREASE_QUOTA_NAME */
LUID luidTCB = { 0 }; /* SE_TCB_NAME */
MyEnumPowerfulTokenData_t enumData = { 0 };
HANDLE hptPowerful = NULL; /* primary/process token */
HANDLE hitPowerful = NULL; /* impersonation token */
HANDLE hptCurrSessUser = NULL;
DWORD dwCurrSessId = 0;
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
ok = LookupPrivilegeValue(NULL, SE_ASSIGNPRIMARYTOKEN_NAME, &luidAPT)
&& LookupPrivilegeValue(NULL, SE_INCREASE_QUOTA_NAME, &luidIQN)
&& LookupPrivilegeValue(NULL, SE_TCB_NAME, &luidTCB);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
enumData.pLuidAPT = &luidAPT;
enumData.pLuidIQN = &luidIQN;
enumData.pLuidTCB = &luidTCB;
hr = EnumProcessesId_WinNT(MyEnumPowerfulTokenProc, &enumData);
if (FAILED(hr)) goto eof;
hptPowerful = enumData.hptPowerful;
if (!hptPowerful) {
hr = E_UNEXPECTED; goto eof;
}
ok = DuplicateTokenEx(hptPowerful, TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE,
NULL, SecurityImpersonation, TokenImpersonation, &hitPowerful);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidAPT))
{
hr = MyEnablePriv(hitPowerful, &luidAPT, TRUE);
if (FAILED(hr)) goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidIQN))
{
hr = MyEnablePriv(hitPowerful, &luidIQN, TRUE);
if (FAILED(hr)) goto eof;
}
if (!MyPrivIsEnabled(hitPowerful, &luidTCB))
{
hr = MyEnablePriv(hitPowerful, &luidTCB, TRUE);
if (FAILED(hr)) goto eof;
}
ok = SetThreadToken(NULL, hitPowerful);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
dwCurrSessId = MyWTSGetActiveConsoleSessionId();
ok = MyWTSQueryUserToken(dwCurrSessId, &hptCurrSessUser);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
ok = CreateProcessAsUserW(hptCurrSessUser, NULL, szCmdLine, 0, 0, 0, 0, 0, 0, &si, &pi);
if (!ok) {
hr = HRESULT_FROM_WIN32(GetLastError()); goto eof;
}
eof:
SetThreadToken(NULL, NULL);
if (hptCurrSessUser) { CloseHandle(hptCurrSessUser); }
if (hitPowerful) { CloseHandle(hitPowerful); }
if (hptPowerful) { CloseHandle(hptPowerful); }
if (FAILED(hr)) {
printf("HRESULT = 0x%.8X \n", hr);
}
return 0;
}
int main(int argc, char **argv)
{
if (argc > 1)
{
WCHAR szMsg[999] = {0};
wsprintfW(szMsg,
L"lstrlenW(GetCommandLineW()) = %i \n",
lstrlenW(GetCommandLineW()));
MessageBoxW(NULL, szMsg, L"Test", MB_ICONINFORMATION);
}
else
{
WCHAR szMyExePath[MAX_PATH] = {0};
WCHAR szCmdLine[9999] = {0}, *p;
GetModuleFileNameW(NULL, szMyExePath, MAX_PATH);
wsprintfW(szCmdLine, L"\"%s\" ", szMyExePath);
for (p = szCmdLine; *p; ++p);
while (p < (szCmdLine + 9999 - 1))
{
*p++ = 'a';
}
MyCreateProcess(szCmdLine);
}
return 0;
}
我将把EnumProcessesId_WinNT()
的实施留给你。该函数的原型是:
/* return TRUE to continue */
typedef BOOL(CALLBACK *EnumProcessesId_Callback_t)
(DWORD pid, void * user);
EXTERN_C
HRESULT __stdcall
EnumProcessesId_WinNT(
EnumProcessesId_Callback_t fnCallback,
void *user
);
对于exec而言,我们提升的进程(在同一个会话中)不会提升,我们需要做下一步:
LUA_TOKEN
通过CreateRestrictedToken从我们的提升令牌创建受限制的令牌CreateProcessAsUser
- 请注意如果hToken是调用方主要令牌的受限版本,则不需要SE_ASSIGNPRIMARYTOKEN_NAME权限。
ULONG LowExec(PCWSTR lpApplicationName, PWSTR lpCommandLine)
{
HANDLE hToken, hLowToken;
ULONG cb = GetSidLengthRequired(1);
TOKEN_MANDATORY_LABEL tml = { { (PSID)alloca(cb) } };
ULONG dwError = NOERROR;
if (CreateWellKnownSid(WinMediumLabelSid, 0, tml.Label.Sid, &cb) &&
OpenProcessToken(NtCurrentProcess(), TOKEN_DUPLICATE | TOKEN_QUERY |
TOKEN_ADJUST_DEFAULT | TOKEN_ASSIGN_PRIMARY, &hToken))
{
BOOL fOk = CreateRestrictedToken(hToken, LUA_TOKEN, 0, 0, 0, 0, 0, 0, &hLowToken);
if (!fOk)
{
dwError = GetLastError();
}
CloseHandle(hToken);
if (fOk)
{
if (SetTokenInformation(hLowToken, ::TokenIntegrityLevel, &tml, sizeof(tml)))
{
STARTUPINFOW si = { sizeof(si)};
PROCESS_INFORMATION pi;
if (CreateProcessAsUser(hLowToken, lpApplicationName, lpCommandLine, 0, 0, TRUE, 0, 0, 0, &si, &pi))
{
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
dwError = GetLastError();
}
}
else
{
dwError = GetLastError();
}
CloseHandle(hLowToken);
}
}
else
{
dwError = GetLastError();
}
return dwError;
}