UWSC UAC環境下で昇格ダイアログ起動と降格起動
UWSC公式掲示板に、管理者権限で起動したUWSCを一時的に降格する方法はないか、という質問があった。
CreateProcessWithTokenWでやれば良いとのことだったので、面白そうだからやってみた。
スクリプト
OPTION EXPLICIT IFB GET_UWSC_NAME = "UserToken.uws" THEN IFB UT.IsUserAnAdmin() THEN IFB MSGBOX("管理者権限持ってます。<#CR>一般権限で再起動しますか?", BTN_YES OR BTN_NO) = BTN_YES THEN MSGBOX(UT.RunWithToken(GET_UWSC_DIR + "\uwsc.exe", GET_UWSC_NAME)) ENDIF IFB MSGBOX("一時的に一般権限にしますか?", BTN_YES OR BTN_NO) = BTN_YES THEN DIM hToken = UT.GetToken() IFB VARTYPE(hToken) < VAR_BSTR THEN // 正常は数値。エラーは文字列 IF !UT.ImpersonateLoggedOnUser(hToken) THEN MSGBOX("権限偽装に失敗しました") UT.CloseHandle(hToken) // トークンが不要になったらCloseHandleする ELSE MSGBOX(hToken) ENDIF IFB UT.IsUserAnAdmin() THEN MSGBOX("管理者権限あるままです") // 指定トークンに管理者権限があるとこっち ELSE MSGBOX("管理者権限ありません<#CR>、、、けどプロセスはありで作られます、、、") ENDIF UT.RevertToSelf() // 偽装はここまで IFB UT.IsUserAnAdmin() THEN MSGBOX("管理者権限もどりました") ELSE MSGBOX("管理者権限もどりません???") // こっちにはならないはず ENDIF ENDIF ELSE IFB MSGBOX("管理者権限持ってません。<#CR>管理者権限で再起動しますか?", BTN_YES OR BTN_NO) = BTN_YES THEN UT.RunAs(GET_UWSC_DIR + "\uwsc.exe", GET_UWSC_NAME) ENDIF ENDIF ENDIF MODULE UT // 昇格実行用 PROCEDURE RunAs(path, para=EMPTY, cd=EMPTY) IF cd = EMPTY THEN cd = GET_CUR_DIR WITH CREATEOLEOBJ("Shell.Application") .ShellExecute(path, para, cd, "runas", 1) ENDWITH FEND // 指定プロセスIDのトークンを取得する // プロセスID省略時は、シェルのトークンを取得する // 正常は数値が返る。使い終わったらCloseHandleすること FUNCTION GetToken(pid=-1) IF pid = -1 THEN GetWindowThreadProcessId(GetShellWindow(), pid) DIM hProc = OpenProcess($02000000, FALSE, pid) // MAXIMUM_ALLOWED IFB hProc THEN DIM hToken IFB OpenProcessToken(hProc, $F01FF, hToken) THEN // TOKEN_ALL_ACCESS DIM hTokenDup IFB DuplicateTokenEx(hToken, $02000000, 0, 3, 1, hTokenDup) THEN RESULT = hTokenDup ELSE RESULT = "DuplicateTokenEx error." ENDIF CloseHandle(hToken) ELSE RESULT = "OpenProcessToken error." ENDIF CloseHandle(hProc) ELSE RESULT = "OpenProcess error." ENDIF FEND // 指定したトークンでプロセスを起動する // 要管理者権限 // 正常はプロセスID FUNCTION RunWithToken(path, para=EMPTY, cd=EMPTY, hToken=NULL) IF cd = EMPTY THEN cd = GET_CUR_DIR DIM sui[16], pi[3], res, bClose = (hToken = NULL) IF bClose THEN hToken = GetToken() SETCLEAR(sui, 0) // いらんけどなんとなく初期化 sui[0] = 4 * 17 // これはいる // コマンドラインの指定が、、、何か変で小細工 IFB LENGTH(para) THEN IF LENGTH(path) THEN para = path + " " + para res = CreateProcessWithTokenW(hToken, 0, NULL, para, 0, NULL, cd, sui, pi) ELSE res = CreateProcessWithTokenW(hToken, 0, path, para, 0, NULL, cd, sui, pi) ENDIF IFB res THEN // スレッドハンドル・プロセスハンドルは閉じる。終了待ちするなら使うけど CloseHandle(pi[1]) CloseHandle(pi[0]) RESULT = pi[2] ELSE RESULT = "CreateProcessWithTokenW error." ENDIF IF bClose THEN CloseHandle(hToken) FEND // 管理者権限有無確認用 DEF_DLL IsUserAnAdmin():BOOL:shell32 // スレッド権限偽装用。Imp...からRevert...までが指定トークンで偽装される DEF_DLL ImpersonateLoggedOnUser(DWORD):BOOL:advapi32 DEF_DLL RevertToSelf():BOOL:advapi32 // その他必要Win32API DEF_DLL GetShellWindow():HWND:user32 DEF_DLL GetWindowThreadProcessId(HWND,var DWORD):DWORD:user32 DEF_DLL OpenProcess(DWORD,BOOL,DWORD):DWORD:kernel32 DEF_DLL OpenProcessToken(DWORD,DWORD,var DWORD):BOOL:advapi32 DEF_DLL CloseHandle(DWORD):BOOL:kernel32 DEF_DLL DuplicateTokenEx(DWORD,DWORD,DWORD,int,int,var DWORD):BOOL:advapi32 DEF_DLL CreateProcessWithTokenW(DWORD,DWORD,wstring,var wstring,DWORD,pWchar,wstring,DWORD[],DWORD[]):BOOL:advapi32 TEXTBLOCK struct_STARTUPINFO DWORD cb; LPTSTR lpReserved; LPTSTR lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE hStdError; ENDTEXTBLOCK // STARTUPINFO, *LPSTARTUPINFO; TEXTBLOCK struct_PROCESS_INFORMATION HANDLE hProcess; HANDLE hThread; DWORD dwProcessId; DWORD dwThreadId; ENDTEXTBLOCK // PROCESS_INFORMATION, *LPPROCESS_INFORMATION; ENDMODULE
はまったポイント
備考
Powershellで特権Powershellの起動は「start powershell -Verb runas」