UAC の確認ボタンをキーボードエミュレーターで自動的に押す

UAC(ユーザーアカウント制御)を有効にしていると、たとえ管理者ユーザーでログオンしていても、管理者権限を要求するプログラムの起動時に次のような UAC 確認画面が表示されます。

この[はい]ボタンをキーボードエミュレーター(ハードウェア)で自動的に押す実験をします。

動作確認環境

  • Windows 11 Home 22H2
  • Visual Studio Community 2022 (Visual C++)
  • PowerShell 5.1

Windows API で[はい]ボタンが押せない

UAC 画面の[はい]ボタンは、セキュリティ確保のため、Windows の API で押せないようになっています。

SendInput 関数を使って、5 秒後に[はい]ボタンのアクセスキーである [Alt]+[Y] キーを押すプログラムを作ります。

#include <windows.h>
#include <stdio.h>

int main()
{
    const WORD SCANCODE_ALT = 0x38;
    const WORD SCANCODE_Y   = 0x15;

    INPUT inputs[4] = { 0 };

    // 待ち(アプリ切り替え時間)
    Sleep(5000);

    // [Alt] down
    inputs[0].type       = INPUT_KEYBOARD;
    inputs[0].ki.wVk     = 0;
    inputs[0].ki.wScan   = SCANCODE_ALT;
    inputs[0].ki.dwFlags = KEYEVENTF_SCANCODE;

    // [Y] down
    inputs[1].type       = INPUT_KEYBOARD;
    inputs[1].ki.wVk     = 0;
    inputs[1].ki.wScan   = SCANCODE_Y;
    inputs[1].ki.dwFlags = KEYEVENTF_SCANCODE;

    // [Y] up
    inputs[2] = inputs[1];
    inputs[2].ki.dwFlags |= KEYEVENTF_KEYUP;

    // [Alt] up
    inputs[3] = inputs[0];
    inputs[3].ki.dwFlags |= KEYEVENTF_KEYUP;

    // キー情報送信
    UINT uiRet = SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
    if (uiRet != ARRAYSIZE(inputs))
    {
        printf("SendInput, err=0x%x\n", GetLastError());
        return 1;
    }

    return 0;
}

Visual C++ でビルドします。

C:\tmp> cl SendAltY.c user32.lib
Microsoft(R) C/C++ Optimizing Compiler Version 19.31.31105 for x86
Copyright (C) Microsoft Corporation.  All rights reserved.

SendAltY.c
Microsoft (R) Incremental Linker Version 14.31.31105.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:SendAltY.exe
SendAltY.obj
user32.lib

本プログラムを実行し、

C:\tmp> SendAltY.exe

5 秒以内に「ペイント」をアクティブにすると、

キーボードから [Alt]+[Y] を押したときと同様、設定画面が表示されます。

しかし、UAC の画面には効きません。

同プログラムを実行し、

C:\tmp> SendAltY.exe

5 秒以内に「レジストリエディタ」の起動時に表示される UAC 画面をアクティブにしても、

無反応・無応答であり、レジストリエディタは起動しません。

試しに SendInput 関数を利用して 5 秒後に UAC 画面の[はい]ボタンの座標をクリックするプログラムを作っても、無反応・無応答でした。

みんラボのキーボード/マウスエミュレータ

そこで、物理キーボードと同等の効果があるキーボードエミュレーターを使って[はい]ボタンを押してみます。
利用するハードは、以前「キーボードエミュレータを使った自動キーボード入力でファイルを他端末にコピーする」[1] で使ったのと同じ、みんなのラボの「キーボード/マウス エミュレータ(USB 接続版)」、1650 円です。

前回は 2 台の PC を接続し、端末 #1 から 端末 #2 にキーボードのデータを送りましたが、

今回はループバック接続し、自端末にキーボードのデータを送ります。

写真にある白い四角い正方形は、単なる USB ハブです。PC に USB Type-A のポートが 1 つしかないため使用しています。

[はい]ボタンの押下に成功

仮想 COM ポートが制御できる言語であれば何でもいいのですが、今回も PowerShell でプログラムを作ります。
以下、キーボードエミュレーターを使って 5 秒後に [Alt]+[Y] キーを押すプログラムです。

# PushAltY.ps1
# みんなのラボ キーボード/マウスエミュレータ《USB接続版》を使って
# UAC 確認画面の[はい]ボタンに有効な
# [Alt]+[Y] キーを押す Windows PowerShell スクリプト。

# デバイスマネージャーを参照して、
# キーボードマウスエミュレーターを接続している COM ポートを指定のこと。
$COM_PORT_NAME = 'COM4'

# comport オブジェクトの定義
$g_comport = New-Object System.IO.Ports.SerialPort $COM_PORT_NAME, 9600

function SendDataToEmu([byte[]] $cmd)
{
    # コマンドパケット送信
    $g_comport.Write($cmd, 0, $cmd.length)

    # コマンド送信時のウェイト(必要に応じて有効化・時間調整)
    Start-Sleep -Milliseconds 100

    # アンサーパケットの読み捨て
    # HEAD#1, HEAD#2, ADDR, CMD, DATALEN, DATA..., SUM
    [byte[]] $ans = New-Object byte[] 256
    $bytes = $g_comport.Read($ans, 0, $ans.Length)
    return
}

# 開始時のウェイト(必要に応じて有効化・時間調整)
Start-Sleep -Milliseconds 5000

try
{
    # COM ポートのオープン
    $g_comport.Open()

    # [Alt]+[Y] 押下
    [byte[]] $cmdAltY = @(0x57, 0xAB, 0x00, 0x02, 0x08, 
        0x04, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c)
    SendDataToEmu $cmdAltY

    # ウェイト(必要に応じて有効化・時間調整)
    Start-Sleep -Milliseconds 300

    # キー解放
    [byte[]] $cmdRelease = @(0x57, 0xAB, 0x00, 0x02, 0x08, 
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C)
    SendDataToEmu $cmdRelease
}
finally
{
    Start-Sleep -Milliseconds 100 # 念のため

    # COM ポートのクローズ
    # 未オープン状態でクローズ指示しても例外発生しない
    $g_comport.Close()
    $g_comport.Dispose()
}

実行し、

C:\tmp> powershell .\PushAltY.ps1

5 秒以内にレジストリディタの UAC 画面をアクティブにすると、

キーボードから [Alt]+[Y] を叩いたときと同様、レジストリエディタが起動しました。

成功です。

さらに便利に

もう少し自動化し、「SkipUac <UAC の確認が必要なプログラム名>」と入力すると、UAC 画面がスキップされるようにしてみましょう。

「SkipUac.bat」という名前のバッチファイルを作ります。

start powershell .\PushAltY.ps1 & %*

「SkipUac <レジストリエディタ>」を実行します。

C:\tmp> SkipUac regedit

UAC の画面が表示され、

5 秒後に自動的に[はい]ボタンが押され、レジストリエディタが起動しました。

以下は、コマンドプロンプトを管理者として実行するコマンドです。

powershell Start-Process -Verb runas -FilePath cmd.exe

頭に「SkipUac」を付加して実行します。

C:\tmp> SkipUac powershell Start-Process -Verb runas -FilePath cmd.exe

UAC の画面が表示され、

5 秒後に自動的に[はい]ボタンが押され、管理者のコマンドプロンプトが起動しました。

おわりに

実証実験レベルではありますが、キーボードエミュレータを利用して UAC 画面をスキップさせることができました。セキュリティのレベルが下がるため普段使いすべきではありませんが、RPA なり連続運転試験なりで役に立つかもしれません。

参考文献

[1] やや低レイヤー研究所「キーボードエミュレータを使った自動キーボード入力でファイルを他端末にコピーする」https://yaya.lsv.jp/typecopy/