ログオフなしにAdministrator特権を持つアプリケーションを起動する方法(改訂版)

戻る

拙著「Win32サブルーチンズ」で,「ログオフなしにAdministrator特権を持つ アプリケーションを起動する方法」を紹介しました.
しかし,Win32 API LogonUser を実行することを主眼としていたため, これによって実行されるアプリケーションの実行環境について注意を払っていません でした.
また,WindowsNT4.0 Service Pack 3 のセキュリティ強化により,以前のOSで 動作していた部分まで正常に動作しなくなってしまいました.
そこで,もう少し実行するアプリケーションの動作を安定させることと, Win32 API LogonUser をサービス以外(デスクトップ上のアプリケーション)から動作 させる方法についても言及します.

23-1 以前のバージョンの問題点

以前のバージョンで発生した問題について簡単にまとめてみます.
(1) サービスの終了が正しく行えないことがある
(2) 起動したアプリケーションから,IME, プリンタが正しく利用できない
(3) USER32.DLLやKERNEL32.DLLが異常終了することがある

●(1)について
(1) は,単純なバグですが,なぜかService Pack 3 をインストールしたWindowsNT4.0 では頻繁に現れます.サービスコントローラから終了イベントを受信したときに, サービスコントローラに終了イベントを受信したことを通知するまえに,内部の 終了イベントをONにしていたため,サービスコントローラに通知する前に,サービス が終了してしまい,通知処理が行われないことがありました.
このバグについては,サービスコントローラに通知を行ってから,終了イベントを 発行するように修正しています.
(* 「Win32サブルーチンズ」で紹介している「Windows95/NTデーモンプロセスの 作り方がわかりたい」のサンプルにも同様の問題がある.)
●(2),(3)について
(2),(3) については,WindowsNTのセキュリティに関わる問題です.
WindowsNT4.0 Service Pack3 上で,ログオンしたユーザのトークンハンドルを利用 した場合,偽装してデスクトップにウィンドウを表示させる方法ではアプリケーション を正しく実行できなくなってしまいました.
つまり,ウィンドウステーション・デスクトップは,デスクトップを持っているユーザ であれば問題なく実行できるようになっていたようですが,Service Pack 3からは, 現在実行中のユーザでなければ,ウィンドウステーション・デスクトップを操作 することができなくなったようです.
 これについては,名前付きパイプのハンドルで偽装すれば動作させることができます が,偽装をやめてSYSTEMユーザの権限のままでも実行させることができるようなので ,偽装の処理を削除しました.


リスト23-1:修正されたServiceCtrlの終了イベント処理

VOID ServiceCtrl(DWORD dwCtrlCode)
{
   DWORD  dwState = SERVICE_RUNNING;

    switch(dwCtrlCode){
        case SERVICE_CONTROL_STOP:
            /* サービスが停止処理を開始したことを通知してから終了する */
            ReportSCManager(SERVICE_STOP_PENDING, NO_ERROR, 0);
            SetEvent(_hServDoneEvent);
           return;

        case SERVICE_CONTROL_INTERROGATE:
           break;
                       :
                       :
                       :

23-2 HKEY_CURRENT_USER の情報を利用するアプリケーション

前節まではバグ修正ですが,ここからは機能追加になります.
サービスから起動されたアプリケーションは,SYSTEM の持ち物であるため, システムの設定は,すべてSYSTEMユーザのものを利用します.
しかし,SYSTEMユーザは,デスクトップを持たないため,レジストリの HKEY_CURRENT_USER
キーは,Default が利用されます.
(* SYSTEMユーザは,[Ctl]+[Alt]+[Del]のログオンダイアログボックスからログオンできない)

HKEY_CURRENT_USER は,デスクトップにログオンすることによって作成され, ログオンしたユーザ固有の情報を格納します.このため,HKEY_CURRENT_USER に 格納されている情報を利用して実行される処理(たとえば印刷)を行う アプリケーションは正しく動作することができません.

23-3 HKEY_CURRENT_USER の正体

Windows3.1では,アプリケーションの環境設定をINIファイルに保存していましたが, WindowsNTでは設定をレジストリに保存し,ユーザごとに異なる設定を保存することが できるようになっています.レジストリキーのうち,サービスの設定や各ユーザ共通の 情報は,HKEY_LOCAL_MACHINE に,各ユーザ固有の設定は HKEY_CURRENT_USER に格納 します.
HKEY_CURRENT_USER は,ユーザマネージャでユーザを追加したときでなく, ユーザは初めてデスクトップにログオンしたときに生成され,ユーザ固有の情報は ファイルまたはディレクトリに格納されます.
ユーザ情報がどこに格納されたかを知るには,
HKEY_LOCAL_MACHINE
\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
\S-X-X-XX-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX
を参照します.
ここで,"S"で始まる数値文字列はユーザを示すSID(Security IDentifier)と言われる 識別子で,ユーザごとに異なる値は割り当てられます.
ユーザが追加されると,システムはこの識別子でシステム内のユーザを識別します. ユーザ情報は,このパス内のProfileImagePath に設定されているディレクトリ(NT4.x) またはファイル(NT3.xx)に保存されます.
 WindowsNT4.0からはWindows95と同様に,スタートメニューで表示される アプリケーションへのショートカットが,ファイルとして保存されるようになっている ため,ProfileImagePathはディレクトリを指しています.レジストリの情報は,必ず "ProfileImagePathが指すディレクトリ\NTUSER.DAT" ファイルに保存されます.  WindowsNTではユーザがログオンすると,そのユーザの情報を探し出して HKEY_CURRENT_USER キーからアクセスできるようにします.もし,ログオンしたとき にユーザへの情報が存在しないときには %SystemRoot%\Default User ディレクトリに 格納されているファイルがコピーされます.このため,レジストリの初期値は ".DEFAULT" が利用されることになります.

23-4 ログオンせずにHKEY_CURRENT_USER を利用するには

レジストリは,HKEY_XXXXX ごとにハイブ形式(レジストリ階層の最上段をルートと するキー、 サブキー、 値の集合体)で格納されます.
システムやアプリケーションのユーザ固有情報は,システムにログオンしたユーザ ごとに異なるため,ログオンが起こるたびに Win32 API RegLoadKey で, HKEY_CURRENT_USER が差し替えられます.
他のデスクトップから,Win32 API LogonUser でログオンし,CreateProessAsUser で プロセスを生成した場合,実行されたデスクトップの HKEY_CURRENT_USERを指します. これと同様にサービスから生成した場合,SYSTEM ユーザにはデスクトップがありません ので,.DEFAULT を利用します.
このため,他のデスクトップから Win32 API LogonUser でログオンしたユーザの HKEY_CURRENT_USER を利用するには,一時的に Win32 API RegLoadKey で, HKEY_CURRENT_USER を差し替える必要があります.
Win32 API RegLoadKey は,呼び出し側プロセスがSE_RESTORE_NAME特権を持って いなけれな正しく動作しません.
HKEY_CURRENT_USER を差し替えるには,次の手順で処理を行う必要があります.
(1)起動したプロセスのユーザトークンからユーザのSIDを取得します.
(2)取得したSIDを文字列の書式"S-X-X-XX-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX" に変換します.
(3)HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList \S-X-X-XX-XXXXXXXXXX-XXXXXXXXXX-XXXXXXXXXX-XXXX から,ユーザプロファイルの格納場所を取得します.
(4)プロセスにSE_RESTORE_NAME特権を設定します.
(5)Win32 API RegLoadKeyで HKEY_CURRENT_USER に,取得したプロファイルを関連付けます.
(6)プロセスを起動します.
(7)プロセスの終了の後,Win32 API RegUnLoadKeyでHKEY_CURRENT_USERを元に戻します.

表23-1:レジストリロード・アンロード関数
LONG RegLoadKey(HKEY hKey, LPCTSTR lpszSubKey, LPCTSTR lpszFile)
HKEY_USERまたはHKEY_LOCAL_MACHINEにサブキーを作成し、
指定されたファイル内の登録情報をそのキーに格納します.
呼び出し側プロセスがSE_RESTORE_NAME特権が必要です.

引数
HKEY hKey ... サブキーを作成するキー
 HKEY_LOCAL_MACHINE または HKEY_USERSを指定する
LPCTSTR lpszSubKey ... サブキーを示す文字列を指すポインタ
LPCTSTR lpszFile ... 登録情報を持つファイル名を指すポインタ

戻り値
正常終了 ERROR_SUCCESS
異常終了 エラーコード
LONG RegUnLoadKey(HKEY hKey, LPCTSTR lpszSubKey)
指定されたキーとサブキーをレジストリからアンロードします.

引数
HKEY hKey ... サブキーを作成するキー
 HKEY_LOCAL_MACHINE または HKEY_USERS を指定する
LPCTSTR lpszSubKey ... サブキーを示す文字列を指すポインタ

戻り値
正常終了 ERROR_SUCCESS
異常終了 エラーコード

23-5 通常のアプリケーションからWin32 API LogonUserを利用するには

Win32 API LogonUser, CreateProcessAsUser は,システムであらかじめ設定されて いるグループに属しているユーザで,特別な特権を設定しない限りは利用することが できません.しかし,通常のサービスは,SYSTEMユーザとして動作しますので, これらの API を問題なく実行することができます.
 デスクトップからログオンしたユーザから,これらのAPIを実行するには, ユーザまたはユーザが所属するグループにいくつかの特権を設定する必要があります.

表23-2:Win32 API LogonUser, CreateProcessAsUserで必要な特権
特権名ヘッダファイルの定義ユーザマネージャでの設定
SeTbcPrivilegeSE_TBC_NAMEオペレーティングシステムの一部として動作
SeAssignPrimarySE_ASSINGPRIMARYTOKEN_NAMEプロセストークンの置き換え
SeIncreaseQuotaSE_INCREASE_QUOTA_NAMEクオータの増加

これらの特権は,プロセスでなくユーザに設定されている必要があります.
ユーザマネージャの「原則(P)」- 「ユーザの権利(U)」で表示されるダイアログ ボックスで「高度なユーザ権利の表示(G)」チェックボックスをチェックしなければ 表の特権は表示されません.表の特権のうち,SeTbcPrivilege 以外は,Administrators グループのユーザには必ず設定されています.なお,ユーザマネージャで特権を追加 した場合,次にログオンしたときから有効になります.
ここまでの設定で,Win32 API LogonUser, CreateProcessUser を実行することが できるようになります.しかし,デスクトップを利用できるプロセスは,デスクトップ を所有しているユーザと同一でなければアクセスすることができません.
 ウィンドウを表示させるなど,ある特定のユーザにしかアクセス権が設定 されていないリソースにアクセスするアプリケーションを起動したときには, サービスから起動する場合と同様に,起動したプロセスのユーザがデスクトップなどの リソースにアクセス権を設定する必要があります.
アクセス権がない状態でプロセスを起動させると,KERNEL32.DLLやUSER32.DLLが 異常終了し,異常終了したことを示すダイアログボックスが表示されます.  また,HKEY_CURRENT_USERも,起動したプロセスの親プロセスと同一のものを 指しますので,Win32 API LogonUser のものを利用するには,処理を追加する必要が あります.

23-6 困ったこと

サービスからアプリケーションを実行すると,実行されたアプリケーションは, ログオフを行っても終了しません.逆に,デスクトップから特定のプロセスを起動し, デスクトップをログオフしても動作し続けるプロセスを生成することができます.  また,SU.EXEでアプリケーションを起動すると,スクリーンセーバが起動できなく なります.
(* Windows2000では正常に動作する.)
Win32 API GetUserObjectSecurity で取得したSD(SecurityDescriptor)に Win32 API LogonUser で取得したトークンのアクセス権を追加する処理も試して みましたが,よい結果が得られませんでした.
このため,デスクトップへのアクセス権の設定関連の処理は,以前のままに なっています.
今回の修正で参考にした資料を参考文献に記述していますので,完全に動作する バージョンが必要な方はぜひチャレンジしてみてください.


表23-3:ユーザオブジェクトのセキュリティを操作する関数
BOOL GetUserObjectSecurity(HANDLE hObject,
 PSECURITY_INFORMATION lpSi, PSECURITY_DESCRIPTOR lpSd,
 DWORD dwSd, LPDWORD lpdwSdRequired)
ユーザーオブジェクトのセキュリティを取得します
引数
HANDLE hObject ... ユーザーオブジェクトのハンドル
PSECURITY_INFORMATION lpSi ... 要求するセキュリティ情報アドレス
PSECURITY_DESCRIPTOR lpSd ... セキュリティ記述子アドレス
DWORD dwSd ... セキュリティ記述子バッファサイズ
LPDWORD lpdwSdRequired ... 必要なバッファを指す

戻り値
正常終了 TRUE
異常終了 FALSE
BOOL SetUserObjectSecurity(HANDLE hObject,
 PSECURITY_INFORMATION lpSi, PSECURITY_DESCRIPTOR lpSd)
ユーザーオブジェクトのセキュリティを設定します

引数
HANDLE hObject ... ユーザーオブジェクトのハンドル
PSECURITY_INFORMATION lpSi ... 要求するセキュリティ情報アドレス
PSECURITY_DESCRIPTOR lpSd ... セキュリティ記述子アドレス

戻り値
正常終了 TRUE
異常終了 FALSE

23-7 プログラムについて

WindowsNT 4.0 Service Pack 3 で動作するスーパーユーザデーモンです.
WindowsNT はログオンしたユーザが,他のユーザとしてアプリケーションを実行する ことを想定していない構造になっているようですので,すべてのアプリケーションが 正しく動作する保証はできませんので,十分注意して利用してください.

●サーバー
まず,サービスをインストールします.
実行するユーザは「Administrators」グループのアクセス権を持っている必要が あります.
>sud -install
コントロールパネルの「サービス」から "Super User Service" を起動させます.
インストールを解除するときは,サービスを停止させ,
>sud -uninstall
を実行します.
サービスの起動・停止は,コントロールパネルの「サービス」の "Super User Service" を起動・停止を行うか,
コマンドラインから
>NET START sud
で起動し, >NET STOP sud
で停止します.

●クライアント
スーパユーザ サーバに,指定したユーザが持つアクセス権でアプリケーションの 実行を依頼します.プロセス間通信には,名前付きパイプを利用します.
パイプのパスは,ローカルパスであるため,他のマシンへの接続はできません.
スーパユーザ サーバの起動後,
>su ユーザ名
または
>su ユーザ名 実行ファイルフルパス
で起動します.
パスワード入力後,ログオンに成功すると実行ファイルが起動されます.
アプリケーションが終了するまで,su は制御を戻さず,アプリケーションが終了すると 終了コードを表示してから終了します.

●ソースファイル
SUD.EXE (サービス)
 SERVICE.C (サービス処理ソースファイル)
 SUD.C (本体ソースファイル)
 MAKEFILE (メイクファイル)
SU.EXE (クライアント)
 SU.C (ソースファイル)
 MAKEFILE (メイクファイル)