2017-08-29 18 views
2

我已經編寫了在本地系統帳戶下運行的C#服務。我用它在用戶登錄到終端服務器時產生一個進程。該服務執行OnSessionChange方法並接收具有相應SessionID的SessionChangeDescription消息。使用CreateProcessAsUser和CreateEnvironmentBlock創建進程後未設置客戶端名稱

我使用此SessionID從WTSQueryUserToken獲得用戶的訪問令牌。我將此令牌轉換爲主令牌並將其傳遞給CreateEnvironmentBlock以檢索指向用戶環境變量的指針。經過一些進一步的準備工作後,我打電話給CreateProcessAsUser函數,最終在他的winsta0\default桌面上生成我最近登錄的用戶的進程。

當我使用ProcessExplorer調查流程時,我發現流程上下文中沒有CLIENTNAME環境變量。然而,應用程序需要這個變量。

我想知道我做錯了什麼。或者也許我錯過了一些東西。應該加載用戶配置文件,因爲我在用戶登錄時會作出反應。

是否有可能存在計時問題?還是CLIENTNAME var以任何其他方式應用於進程?

這裏是我如何調用該函數CreateEnvironmentBlock

private static IntPtr GetEnvironmentFromToken(IntPtr token) 
    { 
     // Get a pointer to the environment variables from the specified user access token 
     IntPtr newEnvironment = IntPtr.Zero; 
     if (!WinApi.CreateEnvironmentBlock(ref newEnvironment, token, false)) 
     { 
      newEnvironment = IntPtr.Zero; 
     } 

     return newEnvironment; 
    } 

如果您需要了解更多信息或代碼樣本,隨意問。

+0

你叫'CreateEnvironmentBlock' - 所以有什麼問題的樣子 - 是'newEnvironment'中存在'CLIENTNAME'字符串?和'我把這個令牌轉換成一個主令牌' - 'WTSQueryUserToken'獲得主訪問令牌 - 所以沒有必要轉換 – RbMm

+0

@RbMm我會寫一些代碼,通過'newEnvironment'數組。如果我使用'DuplicateTokenEx'將主令牌轉換爲主令牌,會造成一些傷害嗎? – wolf633

+0

不,如果你打電話給'DuplicateTokenEx'm但是爲了你需要複製它,這將不會有什麼壞處?你可以像從WTSQueryUserToken中返回一樣使用它。你需要看'PWSTR sz =(PWSTR)lpEnvironment; \t \t \t而(* SZ) \t \t \t { \t \t \t \t DbgPrint( 「%S \ n」 個,SZ); \t \t \t \t sz + = wcslen(sz)+ 1; \t \t \t}'是'CLIENTNAME'出現。如果它沒有出現在塊中 - 它將不會出現在子進程中。如果它存在 - 可能是你不使用'CreateProcessAsUser'中的'CREATE_UNICODE_ENVIRONMENT'標誌 – RbMm

回答

2

的環境變量不僅取決於從用戶SID的SessionID太多,因爲有些變量是每個會話。

我們可以在註冊表中查看HKEY_USERS\<SID>\Volatile Environment用戶環境變量。和SessionId這裏存在子密鑰。下的子鍵 - 每個會話變量 enter image description here

所以CreateEnvironmentBlock必須做下一個 - 獲取用戶SID從令牌,開放HKEY_USERS\<SID>\Volatile Environment鍵,查詢它的值。

則必須查詢通過GetTokenInformation(hToken, TokenSessionId,)和查詢Volatile Environment\SessionId子鍵SessionId從令牌

但由於錯誤系統使用SessionId從當前進程PEB而不是從它獲取它的標記。下一個代碼是在系統DLL:

WCHAR buf[MAX_PATH]; 
StringCchPrintfW(buf, RTL_NUMBER_OF(buf), 
    L"%s\\%d", L"Volatile Environment", RtlGetCurrentPeb()->SessionId); 

enter image description here

MOV RAX,GS:[60H] // RAX - > PEB
2c0h這在PEB偏移的SessionID的

當您從服務中執行應用程序 - PEB中的SessionId爲0時,結果CLIENTNAMESESSIONNAME未添加到環境塊中。

這是常見的系統錯誤。爲測試你可以運行兩個cmd.exe - 一個沒有提升(執行從explorer.exe)和一個作爲管理員(執行從svchost.exe -k netsvcs),然後運行在兩個set命令 - 它顯示環境字符串。你可以注意到在沒有提升cmd.exe存在字符串SESSIONNAME=Console(或SESSIONNAME=RDP-Tcp#N如果你從rdp運行它)和,如果你在rdp,CLIENTNAME=DESKTOP-xxx。但在提升(作爲管理員運行)cmd.exe - 沒有這個字符串。這是因爲從的svchost.exe -k netsvcs具有SessionId == 0

用於修復被稱爲CreateEnvironmentBlock這可以是2路:

容易,但不正確:

 _PEB* peb = RtlGetCurrentPeb(); 
     DWORD _SessionId = peb->SessionId, SessionId, rcb; 

     if (GetTokenInformation(hToken, TokenSessionId, &SessionId, sizeof(SessionId), &rcb)) 
     { 
      peb->SessionId = SessionId; 
     } 

     PVOID Environment; 
     BOOL fOk = CreateEnvironmentBlock(&Environment, hToken, FALSE); 

     peb->SessionId = _SessionId; 

想法這裏 - 臨時替代SessionIdPEBSessionId從令牌。這將是工作。 bu在這裏壞 - 如果另一個並行線程將使用SessionIdPEB

另一種方式,相對較大的代碼,但正確 - 你自己通過SessionId子鍵和擴展環境塊。

void AddSessionEnv(HANDLE hToken, PVOID Environment, PVOID* pNewEnvironment) 
{ 
    SIZE_T cb = 1, len; 
    PWSTR sz = (PWSTR)Environment; 
    while (*sz) 
    { 
     len = wcslen(sz) + 1; 
     sz += len; 
     cb += len; 
    } 

    DWORD SessionId, rcb; 
    if (GetTokenInformation(hToken, TokenSessionId, &SessionId, sizeof(SessionId), &rcb)) 
    { 
     PROFILEINFO pi = { sizeof(pi), PI_NOUI, L"*" }; 
     if (LoadUserProfileW(hToken, &pi)) 
     { 
      WCHAR SubKey[48]; 
      swprintf(SubKey, L"Volatile Environment\\%d", SessionId); 
      HKEY hKey; 

      if (ERROR_SUCCESS == RegOpenKeyExW((HKEY)pi.hProfile, SubKey, 0, KEY_READ, &hKey)) 
      { 
       cb *= sizeof(WCHAR); 

       ULONG cbNeed = 0x200, cbAllocated; 
       PVOID NewEnvironment; 
       do 
       { 
        if (NewEnvironment = LocalAlloc(0, cb + (cbAllocated = cbNeed))) 
        { 
         cbNeed = AddSessionEnv(hKey, (PWSTR)NewEnvironment, cbAllocated); 

         if (cbNeed && cbAllocated >= cbNeed) 
         { 
          memcpy((PBYTE)NewEnvironment + cbNeed, Environment, cb); 
          *pNewEnvironment = NewEnvironment; 
          break; 
         } 

         LocalFree(NewEnvironment); 
        } 

       } while (cbNeed); 

       RegCloseKey(hKey); 
      } 
      UnloadUserProfile(hToken, pi.hProfile); 
     } 
    } 
} 

static volatile UCHAR guz; 

ULONG AddSessionEnv(HANDLE hKey, PWSTR sz, ULONG Length) 
{ 
    LONG status; 

    PVOID stack = alloca(guz); 

    ULONG TotalLength = 0, DataLength, Index = 0, cb = 0, rcb = sizeof(KEY_VALUE_FULL_INFORMATION) + 256; 

    union { 
     PVOID buf; 
     PKEY_VALUE_FULL_INFORMATION pkvfi; 
    }; 

    do 
    { 
     do 
     { 
      if (cb < rcb) cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack); 

      if (0 <= (status = ZwEnumerateValueKey(hKey, Index, KeyValueFullInformation, buf, cb, &rcb)) && 
       pkvfi->Type == REG_SZ && 
       (DataLength = pkvfi->DataLength) && 
       !(DataLength & (sizeof(WCHAR) - 1))) 
      { 
       static const UNICODE_STRING CharSet = { 2 * sizeof(WCHAR), 2 * sizeof(WCHAR), L"="}; 

       USHORT NonInclusivePrefixLength; 
       UNICODE_STRING Name = { (USHORT)pkvfi->NameLength, Name.Length, pkvfi->Name }; 

       // not add strings which containing 0 or `=` symbol or emply 
       if (Name.Length && RtlFindCharInUnicodeString(0, &Name, &CharSet, &NonInclusivePrefixLength) == STATUS_NOT_FOUND) 
       { 
        UNICODE_STRING Value = { 
         (USHORT)DataLength, 
         Value.Length, 
         (PWSTR)RtlOffsetToPointer(pkvfi, pkvfi->DataOffset) 
        }; 

        PWSTR szEnd = (PWSTR)RtlOffsetToPointer(Value.Buffer, DataLength - sizeof(WCHAR)); 

        if (!*szEnd) Value.Length -= sizeof(WCHAR); 

        // not add empty strings or containing 0 or `=` symbol 
        if (Value.Length && RtlFindCharInUnicodeString(0, &Value, &CharSet, &NonInclusivePrefixLength) == STATUS_NOT_FOUND) 
        { 
         ULONG cbNeed = Name.Length + 2 * sizeof(WCHAR) + Value.Length; 

         if (Length >= cbNeed) 
         { 
          sz += 1 + swprintf(sz, L"%wZ=%wZ", &Name, &Value), Length -= cbNeed; 
         } 
         else 
         { 
          Length = 0; 
         } 

         TotalLength += cbNeed; 
        } 
       } 
      } 

     } while (status == STATUS_BUFFER_OVERFLOW || status == STATUS_BUFFER_TOO_SMALL); 

     Index++; 

    } while (status != STATUS_NO_MORE_ENTRIES); 

    return TotalLength; 
} 

,並使用此代碼:

 PVOID Environment, NewEnvironment = 0; 

     if (CreateEnvironmentBlock(&Environment, hToken, FALSE)) 
     { 
      AddSessionEnv(hToken, Environment, &NewEnvironment); 

      CreateProcessAsUserW(hToken, *, CREATE_UNICODE_ENVIRONMENT, 
       NewEnvironment ? NewEnvironment : Environment, *); 

      if (NewEnvironment) 
      { 
       LocalFree(NewEnvironment); 
      } 
      DestroyEnvironmentBlock(Environment); 
     } 

RtlFindCharInUnicodeString的定義,我用,對於舒適

enum { 
    RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END = 1, 
    RTL_FIND_CHAR_IN_UNICODE_STRING_COMPLEMENT_CHAR_SET = 2, 
    RTL_FIND_CHAR_IN_UNICODE_STRING_CASE_INSENSITIVE = 4 
}; 

NTSYSAPI 
NTSTATUS 
NTAPI 
RtlFindCharInUnicodeString(
           ULONG Flags, 
           PCUNICODE_STRING StringToSearch, 
           PCUNICODE_STRING CharSet, 
           USHORT *NonInclusivePrefixLength 
           ); 
1

經過一些實驗,看起來CreateEnvironmentBlock只設置CLIENTNAME如果進程在與令牌關聯的同一個遠程桌面會話中運行。假冒沒有任何區別。這可以說是Windows中的一個錯誤。

要解決此問題,您可以自己將CLIENTNAME添加到環境塊中,也可以在用戶會話中啓動進程以代表您調用CreateEnvironmentBlock函數。

+0

是的,你幾乎正確 - 'CreateEnvironmentBlock'使用調用者進程'SessionId'來檢查環境變量,但不使用來自令牌的'SessionId'。但有些環境依賴於'SessionId' - 查看'HKEY_USERS \ S - * \ volatile volatile \ SessionId' - 通常是'CLIENTNAME'和'SESSIONNAME'。我如何運行一些進程正常 - 如果存在字符串'SESSIONNAME =控制檯'在它env。但是如果以admin的身份運行它 - 沒有這個字符串,因爲在這種情況下,進程是'svchost.exe'的exec,它運行在會話0 – RbMm

+0

@RbMm中,當我在不匹配的會話中運行我的測試代碼時與令牌相關的會話),它根本沒有設置「CLIENTNAME」。我認爲它可能會檢索與運行代碼的會話相關聯的客戶端名稱,但它沒有。我想這是因爲目標用戶帳戶在'Volatile Environment'內沒有相應的會話條目。 –

+0

我用'SESSIONNAME'字符串測試,注意它存在於所有沒有升級的進程中。但如果我運行進程作爲adim - 沒有這個變種。可以很容易地在自己的代碼中測試這個 - 運行測試程序,查詢這個字符串是否升高而不升高。這是因爲'CreateEnvironmentBlock'使用來自* PEB *的'SessionId'而不是來自給定的令牌。關於'CLIENTNAME',它是如何在遠程桌面設置的 - 需要檢查。但在所有情況下,它從註冊節點 – RbMm

相關問題