程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Windows 7開發:會話 0 隔離

Windows 7開發:會話 0 隔離

編輯:關於.NET

概覽

服務是整合在Microsoft Windows操作系統中的結構。服務與用戶的應用程序 不同,因為你可以對他們進行配置,不需要一個激活的用戶登錄,就可以使這些服務在系統 啟動的時候運行,直到系統關閉。Windows中的服務,負責所有種類的後台活動,但不包括從 遠程過程調用(RPC)服務到網絡位置服務的用戶。

一些服務可能會試圖顯示一些用戶界面對話框,或者與用戶的應用程序進行通信。這些服 務都將面臨與Windows 7 的兼容性問題。如果不去討論與你的應用程序進行通信的必要的安 全准備,那麼,你的服務將不能在Windows 7上工作。

目標

在本實驗中,你將學會如何:

• 重新設計和修復一個試圖顯示 UI界面的服務

• 對服務和應用程序間共享的kernel對象,設置適當的安全和訪 問級別

系統需求

完成本實驗,你必須包含以下組件:

• Microsoft Visual Studio 2008

• Windows 7

• Windows Sysinternals進程浏覽器

練習 #1: 簡潔的服務UI 界面

在這個 練習中,你將安裝並且運行一個直接向用戶展示UI用戶界面的服務。你將看到對用戶體驗十 分有效的Windows內置的自動簡潔窗口(交互的服務偵測對話框),還有可以修改服務,使其 不會直接的展示UI用戶界面。

你還可以修改服務,使其在當前的活動用戶下,在一個獨立的進程中,使用簡潔的UI用戶 界面。

任務 1 –安裝和運行服務

作為這個任務的一部分,你需要通過 使用sc命令行安裝服務,並且首先運行它。這個服務將試圖展示一個會觸發簡潔UI用戶界面 服務的用戶對話框。

1.使用Visual Studio,打開Session0_Starter解決方案。

2.生成當前解決方案(請注意你使用的生成配置- Debug/Release,x86/x64)

3.打開管理員命令窗口:

4.點擊 Start.

5.指向所有程序。

6. 指向 Accessories.

7.右擊 Command Prompt.

8.點擊以管理員身份運行。

9.使用cd命令,導向包含應用程序的輸出目錄。例如,如果輸出目錄是 C:\Session0_Starter\Debug,那麼就是用下面的目錄,導向到相應目錄:

CMD

C:

cd C:\Session0_Starter\Debug

10.使用下面的命令, 創建TimeService服務

CMD

sc create TimeService binPath= C:\Session0_Starter\Debug\TimeService.exe

幫助

確認你已經將Step 9中的 目錄替換,並且確認在“binPath=”後,復制了空格。

11.使用組合鍵 +R,並且在Run對話框中輸入services.msc來打開MMC服務管理單元。

12.定 位到TimeService服務,右鍵點擊,並且點擊Start。

13.隨後,你將看到一個類似下圖的對話框。

14.這個就是交互服務偵測對話框,它將檢測試圖展示UI用戶界面的服務,並且顯示這個 簡潔的對話框。

15.點擊Remind me in a few minutes解除這個消息,或者點擊Show me the message切換 到安全Session 0桌面去查看服務UI用戶界面(一個消息框)。

16.返回MMC服務管理單元,定位到TimeService服務,右鍵點擊,然後點擊Stop來停止服 務。

使用WTSSendMessage (快速定位)來修改服務

作為此任務的一部分,你將使用WTSSendMessage方法向用戶展示一個消息對話框。它將給 用戶提供一個交互服務偵測對話框快速定位和替換。

1.如果你還沒有做這項操作,請根據任務1中的Step 1-5安裝TimeService服務。

2.如果你完成了任務1後還沒有做這個,請確認你已經停止了TimeService服務(參考任務 1中的Step 10)

3.使用Visual Studio,打開Session0_Starter解決方案。

4.在UI\Native解決方案文件夾中找到TimeService項目,然後打開TimeService.cpp文件 。

5.在文件中,找到第一個//TODO注釋。找到MessageBox方法調用,並使用下面的代碼進行 替換:

C++

LPWSTR lpszTitle = L"Time Change";
LPWSTR lpszText = L"Notification: 5 seconds have elapsed.\r\nWould you  like to see more details?";

DWORD dwSession = WTSGetActiveConsoleSessionId();

WTSSendMessage(WTS_CURRENT_SERVER_HANDLE, dwSession, lpszTitle,
                 static_cast<DWORD>((wcslen(lpszTitle) +  1) * sizeof(wchar_t)),
                 lpszText, static_cast<DWORD>((wcslen (lpszText) + 1) * sizeof(wchar_t)),
                 MB_YESNO|MB_ICONINFORMATION, 0 /*wait  indefinitely*/, &dwResponse, TRUE);

6.生成該解決方案。

7.重復任務1中的Step 6-7 。你應該能看到在你的主桌面,會出現一個對話框詢問你,而 不是交互服務偵測對話框。

8.點擊No解除消息。

9.停止服務(參看任務1中Step 10)。

任務 3 –使用不同的用戶憑證浏覽UI 用戶界面

作為該任務的一部分,你需要修改服務,以便於讓其在當前的活動用戶下運行一個新的交 互UI用戶界面進程,來顯示代表服務的用戶交互界面。

1.重復任務2中的Step 1-4操作。

2.在TimeService.cpp文件中找到第二個//TODO注釋。

3.以檢索到的活動的Session ID和與其相關的用戶權證(參看 http://en.wikipedia.org/wiki/Token_(Windows_NT_architecture) 用戶權證背景)來使用 WTSGetActiveConsoleSessionId 和 WTSQueryUserToken方法。這個是用來創建交互UI用戶界 面進程的權證。插入下面的代碼:

C++

BOOL bSuccess = FALSE;
STARTUPINFO si = {0};
PROCESS_INFORMATION pi = {0};
si.cb = sizeof(si);

DWORD dwSessionID = WTSGetActiveConsoleSessionId();

HANDLE hToken = NULL;
if (WTSQueryUserToken(dwSessionID, &hToken) == FALSE)
{
                 goto Cleanup;
}

4.使用DuplicateTokenEx方法來復制權證,以便於它能夠創建進程。插入下面的代碼:

C++

HANDLE hDuplicatedToken = NULL;
if (DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification,  TokenPrimary, &hDuplicatedToken) == FALSE)
{
                 goto Cleanup;
}

5.使用CreateEnvironmentBlock方法為交互進程創建一個環境塊。插入下面的代碼:

C++

LPVOID lpEnvironment = NULL;
if (CreateEnvironmentBlock(&lpEnvironment, hDuplicatedToken, FALSE) ==  FALSE)
{
                 goto Cleanup;
}

6.通過檢索服務執行的完整路徑,來獲取客戶端應用程序的完整路徑(使用 GetModuleFileName),除去文件名稱(使用PathRemoveFileSpec),然後連結客戶端應用程 序名稱。插入下面的代碼:

C++

WCHAR lpszClientPath[MAX_PATH];
if (GetModuleFileName(NULL, lpszClientPath, MAX_PATH) == 0)
{
                 goto Cleanup;
}
PathRemoveFileSpec(lpszClientPath);
wcscat_s(lpszClientPath, sizeof(lpszClientPath)/sizeof(WCHAR),  L"\\TimeServiceClient.exe");

7.使用CreateProcessAsUser方法,在目標用戶中創建一個進程:

C++

if (CreateProcessAsUser(hDuplicatedToken, lpszClientPath, NULL,  NULL, NULL, FALSE,
                                 NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT,
                                                                                                   lpEnvironment, NULL, &si, &pi) ==  FALSE)
{
                 goto Cleanup;
}
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
bSuccess = TRUE;

8.請確認在這項工作期間,你有代碼去分配空余資源。插入下面的代碼:

C++

Cleanup:
if (!bSuccess)
{
                 ShowMessage(L"An error occurred while  creating fancy client UI", L"Error");
}
if (hToken != NULL)
                 CloseHandle(hToken);
if (hDuplicatedToken != NULL)
                 CloseHandle(hDuplicatedToken);
if (lpEnvironment != NULL)
                 DestroyEnvironmentBlock (lpEnvironment);

9.生成解決方案。

10.重復任務1中的Step 6-7操作。除了交互服務偵測對話框,你應該看到在你的主桌面將 出現一個對話框詢問你問題。點擊Yes,客戶端應用程序將啟動,並且及時展現給你。

11.關閉客戶端應用程序並且停止進程(參考任務1中的Step 10)。

注意

為了達到這個練習的目的,我們簡化了示例代碼,並且在我們設計和完成這個項目的時候 ,沒有遵照所有的安全代碼標准。請在另外用戶下創建進程並且使用該進程與後台服務通信 之前,仔細考慮可能引起的安全問題。

練習 #2: 對共享對象進行安全驗證

在這個練習中,你需要安裝和運行一個服務來創建一個標准應用程序的共享kernel對象( 事件)。你將會看到,標准應用程序是不能訪問這個事件的,因為它沒有駐存在同一個 Session的命名空間中,並且沒有設置相應的訪問控制權限。

任務 1 –安裝和運行服務

作為該任務的一部分,你將使用sc命令行安裝一個服務,然後首先運行它。你將看到,當 它去試圖使用服務所創建的事件時,服務的客戶端將收到“Access Denied”的錯誤信息。

1.激活用戶賬戶控制(UAC)。在開始菜單,點擊查詢,然後輸入“User Account Control”。並在查詢結果中選擇“Change User Account Control settings”。然後確認滑塊不是設置在Never notify上的。

2.使用Visual Studio,打開Session0_Starter解決方案。

3.生成當前的解 決方案(確認你使用的生成配置- Debug/Release, x86/x64)。

4.打開一個管理員命 令行窗口,點擊Start,指向 All Programs,然後指向 Accessories,再右鍵點擊 Command Prompt。點擊 Run as administrator。

5.使用cd命令,定位到包含應用程序的輸出 目錄。例如,如果輸出目錄是C:\Session0_Starter\Debug,那麼就是用下面的命令指向相應 的目錄:

CMD

C:

cd C:\Session0_Starter\Debug

6.使用下面的 命令,創建AlertService服務注意:確認你已經使用Step 4 中的路徑,替換了服務的路徑, 並且請確認在“binPath=”後,你復制了空格。

CMD

sc create AlertService binPath= C:\Session0_Starter\Debug\AlertService.exe

7.使用快捷 鍵+R,並且在運行對話框中輸入services.msc進入MMC服務管理單元。

8.定 位到AlertService服務,右鍵點擊,並且點擊Start。

9.打開一個標准的命令行窗口。從開始菜單中,指向All Programs,點擊Accessories, 然後點擊Command Prompt(注意:不要使用管理員方式運行命令行窗口)。

10.在標准命令行窗口中重復Step 5 。

11.使用下面的命令運行AlertService客戶端應用程序,這個服務將嘗試去打開服務創建 的事件,並且用它同步(WaitForSingleObject)。

CMD

AlertServiceClient

12.注意到客戶端在打開事件時失敗,錯誤為2,此錯誤表示沒有找到相應的事件。

13.返回到MMC服務管理單元,定位到AlertService服務,右鍵點擊,然後點擊Stop來停止 該服務。

任務 2 –修改服務,以便能夠在全局命名空間中創建對象

作為該任務的一部分,你需要將對象的名稱進行更改,使其包含全局命名空間的前綴。

1.如果你還不能做這項操作,請根據任務1中的Step 1-5,安裝AlertService服務。

2.如果你在完成了任務1的前提下,還不能做這項操作,請確認AlertService服務已經停 止(查看任務1中的Step 10)。

3.使用 Visual Studio, 打開 Session0_Starter 解決方案。

4.在Security\Native解決方案的文件夾中,定位到AlertService項目,然後打開 AlertService.cpp文件。

5.在該文件中,找到標記為“STEP 1”的//TODO注釋,然後使用下面的代碼將 CreateEvent的調用替換:

C++

g_hAlertEvent = CreateEvent(NULL, FALSE, FALSE,  L"Global\\AlertServiceEvent");

6.在Security\Native解決方案的文件夾中,定位到AlertServiceClient項目,然後打開 AlertServiceClient.cpp文件。

7.在該文件中,找到標記為“STEP 1”的//TODO注釋,然後使用下面的代碼將OpenEvent 的調用替換:

C++

HANDLE hEvent = OpenEvent(SYNCHRONIZE, FALSE,  L"Global\\AlertServiceEvent");

8.生成該解決方案。

9.重復任務1中的Step 7-13 。注意現在客戶端還是不能訪問事件(這次是因為安全設置 ,而不是因為命名空間的問題)。

10.從Windows Sysinternals中運行進程浏覽器(如果你沒有,請從 http://technet.microsoft.com/en-us/sysinternals/0e18b180-9b7a-4c49-8120- c47c5a693683.aspx 下載工具)。

11.在進程列表中選擇AlertService服務(如果這個服務沒有出現在出現列表中,從File 菜單中,點擊Show processes from all users使用管理員權限重啟進程浏覽器)。

12.確認對話框下方的窗口是可見的,並且能夠顯示進程句柄(使用快捷鍵CTRL+H,或者 從View菜單中打開該窗口)。

13.在句柄列表中,找到\BaseNamedObjects\AlertServiceEvent事件,右擊該事件,並且 點擊Properties 。

14.在Properties對話框中,選擇Security標簽。請注意能夠訪問該事件的安全的組,只 有SYSTEM 和Administrators組。

任務 3 –修改服務,以便於為對象提供安全屬性(DACL 和SACL )

作為該任務的其中 一部分,你將修改服務,以便於能夠為事件對象設置適當的訪問控制權限(DACL和SACL), 使其客戶端能對其訪問。

1.重復任務2中的Step 1-4操作。

2.在文件中,找到 標記為“STEP 2”的//TODO注釋。

3.使用WTSGetActiveConsoleSessionId 和WTSQueryUserToken方法,找到當前活動的Session ID和與其相關聯的用戶權證。插入下面 的代碼:

C++

DWORD dwSessionID =  WTSGetActiveConsoleSessionId();
HANDLE hToken = NULL;
if  (WTSQueryUserToken(dwSessionID, &hToken) == FALSE)
{
                 goto Cleanup;
}

4.使用 GetTokenInformation方法檢索用戶賬號的SID(安全標識)。請注意,此時需要兩個通行證 –一個用於判斷TOKEN_USER結構,另外一個則實際的填充它。插入下面的代碼:

C++

DWORD dwLength;
TOKEN_USER* account = NULL;
if (GetTokenInformation(hToken, TokenUser, NULL, 0, &dwLength) ==  FALSE &&
                GetLastError() !=  ERROR_INSUFFICIENT_BUFFER)
{
                goto  Cleanup;
}
 
account = (TOKEN_USER*)new BYTE[dwLength];
if (GetTokenInformation(hToken, TokenUser, (LPVOID)account, dwLength,  &dwLength) == FALSE)
{
                goto  Cleanup;
}

5.使用ConvertSidToStringSid方法,將用戶賬號的SID轉換成字符串形式,然後根據它再 去構建一個SDDL字符串,用來表示服務以後創建的事件的安全說明。插入下面的代碼:

C++

LPWSTR lpszSid = NULL;
if (ConvertSidToStringSid(account->User.Sid, &lpszSid) == FALSE)
{
                 goto Cleanup;
}

WCHAR sddl[1000];
wsprintf(sddl, L"O:SYG:BAD:(A;;GA;;;SY)(A;;GA;;;%s)S:(ML;;NW;;;ME)",  lpszSid);

6.使用ConvertStringSecurityDescriptorToSecurityDescriptor方法,將SDDL安全說明 字符串,轉換成安全說明形式。插入下面的代碼:

C++

PSECURITY_DESCRIPTOR sd = NULL;
if (ConvertStringSecurityDescriptorToSecurityDescriptor(sddl,  SDDL_REVISION_1, &sd, NULL) == FALSE)
{
                 goto Cleanup;
}

7.將step 6 中創建的安全說明,序列化為一個SECURITY_ATTRIBUTES結構,並且使用下面 的代碼創建事件:

C++

SECURITY_ATTRIBUTES sa;
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = sd;
sa.nLength = sizeof(sa);

g_hAlertEvent = CreateEvent(&sa, FALSE, FALSE,  L"Global\\AlertServiceEvent");
if (g_hAlertEvent == NULL)
{
                 goto Cleanup;
}

8.定位到標記為“STEP 3”的//TODO注釋,並且插入下面的代碼,來釋放序列化事件所需 要的資源:

C++

Cleanup:
                 if (hToken != NULL)
                                 CloseHandle (hToken);
                 if (account != NULL)
                                 delete[]  account;
                 if (lpszSid != NULL)
                                 LocalFree (lpszSid);
                 if (sd != NULL)
                                 LocalFree (sd);
                 if (g_hAlertEvent == NULL)
                                 CloseHandle (g_hAlertEvent);

9.生成該解決方案。

10.重復任務1中的Step 7-13 。請注意目前客戶端已經可以成功打開事件,並且可以接收 到從AlertService服務發出的消息了。

11.重復任務2中的Step 9-13 。請注意,現在事件是允許當前的活動用戶進行訪問的,這 也就是為什麼AlertService客戶端即使沒有當前系統的管理員權限,也是能夠打開事件的原 因所在。

當心

示例中所使用的安全說明字符串(SDDL),並不是最好的表現形式。在你的應用程序中, 確認你已經為服務和應用程序共享的資源設置了最高的安全級別,並且進行了威脅分析和模 型來確保你不是創建了一個安全漏洞。

練習 #3: 為文件對象進行加密

在這個練習中,你需要安裝和運行一個服務來創建一個用戶可以訪問的日志文件。但是, 用戶是不能對這個文件進行編寫或者刪除的,除非服務對其進行了安全屬性的設置,包括允 許用戶訪問文件的整合級別。

任務 1 – 安裝和運行服務

作為當前任務的其中一部分,你需要使用installutil命令安裝一個服務,然後首先運行 這個服務。當你試圖去刪除服務所創建的文件時,將看到用戶會收到一個“Access Denied” 的錯誤。

1.使用 Visual Studio,打開 Session0_Starter 解決方案。

2.生成當前的解決方案(請注意你所使用的生成配置 - Debug/Release, x86/x64)。

3.要打開管理員命令窗口,點擊Start,指向All Programs,找到Accessories,然後右鍵 點擊Command Prompt,選中Run as administrator。

4.使用cd命令導航到部署應用程序的輸出目錄。例如,如果輸出目錄是 C:\Session0_Starter\Debug,那麼就是用下面的命令,導航到指定的目錄中:

CMD

C:

cd C:\Session0_Starter\Debug

5.使用下面的命令創建一個LoggingService服務(確認你已經使用step 4 中的路徑,替 換了服務的路徑,並且在“binPath=”之後,復制了空格)。

CMD

installutil LogService.exe

6.使用快捷鍵+R,在運行對話框中輸入services.msc打開MMC服務管理單元。

7.定位到LoggingService服務,右鍵點擊,然後單擊Start。

8.如果打開一個標准命令行窗口,點擊Start,指向All Programs,選擇Accessories,點 擊Command Prompt(注意:不要使用管理員權限運行命令行窗口)。

9.返回MMC服務管理單元,定位到LoggingService服務,右鍵點擊,然後選擇Stop來停止 服務。

10.打開一個Windows Explorer窗口(從我的電腦進入,或者直接打開),然後指向到C:\ root目錄。找到LogService.txt文件。

11.嘗試使用Windows Explorer去刪除文件(右鍵點擊文件,然後選擇Delete,或者點擊 Del鍵)。那麼這個嘗試將由於拒絕訪問錯誤而失敗,因為我們沒有授權用戶去寫或者刪除該 文件。

任務 2 –修改日志文件的整合級別

作為該任務的其中一部分,你需要修改服務所創建的日志文件的整合級別。這樣,用戶就 能夠對日志文件進行寫操作,甚至可以刪除它。

1.如果你還是不能做這項操作,那麼請參照任務1中的step 1-5安裝LoggingService服務 。

2.如果你已經完成了任務1還是不能做這項操作,請確認LoggingService服務已經停止( 參看任務1中的step 10)。

3.使用 Visual Studio,打開 Session0_Starter 解決方案。

4.在Security\Managed解決方案文件夾中,找到LogService項目,然後打開 LoggingService.cs文件。

5.在文件中,找到標記為?的//TODO注釋,然後添加下面的代碼:

C++

IntegrityLevelHelper.SetFileIntegrityLevel(@"C:\LogService.txt",  IntegrityLevel.Medium);

6.生成解決方案。

7.這次,由於日志文件已經不再受系統整合級別的保護,所以用戶可以刪除該日志文件了 。

摘要

在這個實驗中,你已經診斷了由Session 0隔離機制所造成的兩個問題,為這些問題設計 了一些修復應用程序,並且實現了這些修復。你也已經使用了快速修復策略,比如使用 WTSSendMessage方法在服務中向交互用戶發送消息,與具有良好設計的解決方案一樣,比如 為共享的kernel對象或者文件配置訪問控制,並且在當前的活動用戶下,在服務中執行進程 的UI用戶界面。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved