杉山利幸の公開日誌 2007年2月分

2007年3月分

2007/02/24 My Pasokon Memo Ver. up to 1.26
2007/02/17 Auto Defrag 2000 Ver. up to 1.13
2007/02/12 Visual C++ 6.0 でのマニフェストもどきの実装
2007/02/10 Reboot File Deleter は管理者として実行させないといけない
2007/02/03 Vista の「管理者として実行」で応答不要な実装はできないのかも?


2007/02/24 My Pasokon Memo Ver. up to 1.26

今日付けで My Pasokon Memo をバージョンアップしました。バージョンアップ内容は以下の通りです。

Ver.1.26 2007/02/24

文字通りです。

初めて Vista 対応したソフトを公開しました。最初の対応ソフトはやはり My Pasokon Memo が基本でしょう。


2007/02/17 Auto Defrag 2000 Ver. up to 1.13

今日付けで Auto Defrag 2000 をバージョンアップしました。バージョンアップ内容は以下の通りです。

Ver.1.13 2007/02/17

1番目、2番目の修正は文字通りです。

3番目の修正は Vista 対応です。(こういうのも Vista 対応と言うのか?!)
他のソフトの Vista 対応の時間稼ぎです。


2007/02/12 Visual C++ 6.0 でのマニフェストもどきの実装

作者は貧乏なためコンパイラーのバージョンアップをなるべく控えてきた。OSの新しい機能があればインクルードファイルを切り貼りして実現してきた。しかしマニフェストという新しい概念が Visual Studio 2005 からできたために新しいコンパイラーを買わなければならないのかと思った。しかし単純なマニフェストの機能(制限ユーザ*1で昇格プロンプト*2を出して管理者への昇格を行う)なら実現できることがわかったので、ここに公開する。
*1 せっかく Administrators に所属しているのに権限を制限される惨めなユーザ
*2 デスクトップが暗くなって、いちいち応答しなければならない、ウザいあれ
*1 *2 は一般的な言葉ではありません。
2/17 ShellExecute の引数の順序を間違えていたので修正
bool RestartAsAdminIfNeeded(LPSTR lpCmdLine, int nCmdShow);
//---------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
//
//	Adminへの昇格が必要ならリスタート
	if ( RestartAsAdminIfNeeded(lpCmdLine, nCmdShow) ) return 1;
・
・
}
//---------------------------------------------------------------------------
bool RestartAsAdminIfNeeded(LPSTR lpCmdLine, int nCmdShow)
{
//
//	Vista 以外の場合はそのまま
	OSVERSIONINFO os;
	os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	APICall(GetVersionEx(&os), "GetVersionEx");
	if ( os.dwPlatformId != VER_PLATFORM_WIN32_NT || os.dwMajorVersion < 6 )
		return false;
//
//	昇格しているとそのまま
	#define	TokenElevationType	(TOKEN_INFORMATION_CLASS)18
	typedef enum _TOKEN_ELEVATION_TYPE {
		TokenElevationTypeDefault = 1,
		TokenElevationTypeFull,
		TokenElevationTypeLimited,
	} TOKEN_ELEVATION_TYPE, *PTOKEN_ELEVATION_TYPE;
//
	HANDLE hToken = NULL;
	APICall(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken),
		"OpenProcessToken");
	DWORD len;
	TOKEN_ELEVATION_TYPE tet;
	APICall(GetTokenInformation(hToken, TokenElevationType,
		&tet, sizeof(TOKEN_ELEVATION_TYPE), &len), "GetTokenInformation");
	APICall(CloseHandle(hToken), "CloseHandle");
	if ( tet != TokenElevationTypeLimited ) return false;
//
//	再スタート
	char Self[MAX_PATH];//自分のファイル名
	APICall(GetModuleFileName(NULL, Self, MAX_PATH), "GetModuleFileName");
	char CurDir[MAX_PATH];//カレントディレクトリ
	APICall(GetCurrentDirectory(MAX_PATH, CurDir), "GetCurrentDirectory");
	ShellExecute(NULL, "runas", Self, lpCmdLine, CurDir, nCmdShow);
//
	return true;
}
//---------------------------------------------------------------------------

マニフェストにどのような機能があるか知らないが、一番良く使うであろう制限ユーザで昇格プロンプトを出して管理者への昇格を行う程度ならこのまま Visual C++ 6.0 でも大丈夫だ。よって、作者のソフトの Vista 対応において、コンパイラーはこのまま Visual C++ 6.0 で行ってみることにする。


2007/02/10 Reboot File Deleter は管理者として実行させないといけない

Windows Vista でコマンドプロンプトを立ち上げて"whoami /groups"と入力してみよう。すると以下のような結果が出る。
GROUP INFORMATION
-----------------

グループ名                             種類                 SID          属性                                    
====================================== ==================== ============ ========================================
Everyone                               よく知られたグループ S-1-1-0      固定グループ, 既定で有効, 有効なグループ
BUILTIN\Administrators                 エイリアス           S-1-5-32-544 拒否のみに使用するグループ              
BUILTIN\Users                          エイリアス           S-1-5-32-545 固定グループ, 既定で有効, 有効なグループ
NT AUTHORITY\INTERACTIVE               よく知られたグループ S-1-5-4      固定グループ, 既定で有効, 有効なグループ
NT AUTHORITY\Authenticated Users       よく知られたグループ S-1-5-11     固定グループ, 既定で有効, 有効なグループ
NT AUTHORITY\This Organization         よく知られたグループ S-1-5-15     固定グループ, 既定で有効, 有効なグループ
LOCAL                                  よく知られたグループ S-1-2-0      固定グループ, 既定で有効, 有効なグループ
NT AUTHORITY\NTLM Authentication       よく知られたグループ S-1-5-64-10  固定グループ, 既定で有効, 有効なグループ
Mandatory Label\Medium Mandatory Level 不明な種類の SID     S-1-16-8192  固定グループ, 既定で有効, 有効なグループ

この中で"BUILTIN\Administrators"の"拒否のみに使用するグループ"の所が問題だ。
これはファイルやレジストリーキーに対して Administrators に与えられている許可をすべて無視するということだ。

Reboot File Deleter ではMoveFileEx 関数を通して HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager キーの PendingFileRenameOperations にアクセスする。アクセス権限は

のように定義されているが、それが制限されてしまう。プログラム的にそのフラグを解除する方法は無さそうだ。もしそのような方法があったら、UACの意味が全くなくなってしまう(正確に言えばUAC元々意味がないと作者は思っているのだが)。

少し面倒だが、RFD.exe を右クリックして「管理者として実行(A)...」を選択すれば動きそうで、これを Vista での使用法にしてしまうかもしれない。作者が金持ちで Visual Studio 2005 を買ってマニフェストを使えばそのようなことはないのだが...


2007/02/03 Vista の「管理者として実行」で応答不要な実装はできないのかも?

Before Power Off,Multi Function Alarm 等はDOSコマンドを指定する機能があり、指定したDOSコマンドの実行を待ったり、指定時間に起動したりします。
ニーズを考えた場合、これらのDOSコマンドはパソコンのメンテナンスを伴う物が多くなり、当然管理者権限が必要になります。更に管理者権限への昇格を求めるダイアログがいちいち出ていたのでは、それらソフトの存在意義がなくなります。
よって「管理者として実行」で応答不要な実装を考えているのですが、なかなかうまくいきません。もしかすると Vista の仕様によりUAC(User Account Control)が有効な環境ではできないのかも知れません。

//
//	ShellExecute
	ShellExecute(NULL, "runas", ..., ...);

上記では確かに管理者権限で起動できるのですが、管理者権限への昇格を求めるダイアログが表示されます。

CreateProcess では当ダイアログが出ないのですが、ERROR_ELEVATION_REQUIRED というエラーが返されます。よって、CreateProcessAsUser あたりを使うことになりそうですが、色々調べていって以下のようなコーディングをしました。
//
//	OpenProcessToken
	HANDLE UserToken = NULL;//プライマリートークン
	APICall(OpenProcessToken(GetCurrentProcess(),
		TOKEN_ALL_ACCESS, &UserToken), "OpenProcessToken");
//
//	AdminToken
	TOKEN_LINKED_TOKEN tlt;
	DWORD len;
	APICall(GetTokenInformation(UserToken, TokenLinkedToken,
		&tlt, sizeof(TOKEN_LINKED_TOKEN), &len),
		"GetTokenInformation");
	APICall(CloseHandle(UserToken), "CloseHandle");
	HANDLE AdminToken = tlt.LinkedToken;//偽装トークン
//
//	DuplicateTokenEx
	HANDLE AdminToken2 = NULL;
	APICall(DuplicateTokenEx(AdminToken,
		TOKEN_QUERY|TOKEN_DUPLICATE|TOKEN_ASSIGN_PRIMARY,
		NULL, SecurityImpersonation, TokenPrimary,
		&AdminToken2), "DuplicateTokenEx");
	APICall(CloseHandle(AdminToken), "CloseHandle");
//
//	STARTUPINFO
	STARTUPINFO stinfo;
	stinfo.cb = sizeof(STARTUPINFO);
	stinfo.lpReserved = NULL;
	stinfo.lpDesktop = NULL;
	stinfo.lpTitle = NULL;
	stinfo.dwFlags = 0;
	stinfo.cbReserved2 = 0;
	stinfo.lpReserved2 = NULL;
	stinfo.cbReserved2 = NULL;
//
//	CreateProcessAsUser
	PROCESS_INFORMATION pinfo;
	APICall(CreateProcessAsUser(AdminToken2, NULL, "cmd.exe",
		NULL, NULL, FALSE, CREATE_NEW_CONSOLE|NORMAL_PRIORITY_CLASS,
		NULL, NULL, &stinfo, &pinfo), "CreateProcessAsUser");
	APICall(CloseHandle(AdminToken2), "CloseHandle");

上記で APICall は API 呼出のエラーチェックをします。実行してみると DuplicateTokenEx で ERROR_BAD_IMPERSONATION_LEVEL というエラーを出します。AdminToken を調べてみると偽装トークンで、仕方がないので DuplicateTokenEx によりプライマリートークンを得ようと思いました。しかし AdminToken に TOKEN_DUPLICATE 権限がないせいなのかよくわかりませんが、うまくいきません。

インターネットで検索するとうまくいった例もあるように思えますが、良くわかりません。なにかこの辺の事情がわかる方がいらっしゃったら教えてください。

それ以外でも Vista は問題山積です。


2007年1月分

戻る