はじめに
こんにちは。
徐々に気温も暖かくなり、日中はコートが無くても過ごせる陽気になりました。一方、業務に於いては、年度末ということで決算や人事異動で慌ただしい雰囲気もあります。
そんな中、日々の定常業務でJVN等の脆弱性レポートの掲載サイトにアクセスし、現場のシステムに影響を及ぼす新しい脆弱性情報が公開されていないかを確認し、画面キャプチャを取得して証跡を残しているのですが、確認するサイト数も多く、数件の画面キャプチャを事務的に取得するのが正直手間でした。
毎日の作業となるので、なんとか自動化できないかを模索していたところ「Windows PowerShell」でその実現に成功したので、作成時に試行錯誤した内容を以下に記します。
ちなみに「PowerShell」とは、Microsoft が提供するWindowsシステム管理用の「コマンドシェル」であり「スクリプト言語」のことです。Windows Server 2008 R2 / Windows 7 から標準で搭載されたWindows 管理用の CLI ツールであり、利用する PC やサーバが、それ以降の新しい OS であれば元からインストールされていますので誰でも気軽に使用することができます!
環境
OS:Windows 7 PoweShell version 2.0
実際に作成したソースコード
[code lang=”ps”]
################################################################################
# シェル名:画面キャプチャ取得シェル #
# 機能説明:指定したHPを開き特定ページの画面キャプチャを自動で取得する #
# 作成者 :XXXX #
# 作成日 :yyyy/mm/dd #
################################################################################
######################
# ①変数指定 #
######################
# アクセスURLの設定
$URL = @("https://www.ipa.go.jp/"
,"https://www.jpcert.or.jp/"
,"http://jvn.jp/index.html")
# フラグ用変数設定
$FLG = "0"
# 数値型へ変換
$FLG = [int]$FLG
######################
# ②ループ処理設定 #
######################
# URLの数だけ処理実行
foreach($URL_access in $URL){
######################
# ③IE起動設定 #
######################
# IEの起動
$ie = New-Object -ComObject InternetExplorer.Application
# IEウインドウ設定
$ie.Visible = $true
$ie.StatusBar = $false
$ie.ToolBar = $false
$ie.MenuBar = $false
$ie.AddressBar = $true
# ウィンドウの最大化
$dll_info = ‘[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);’
Add-Type -MemberDefinition $dll_info -Name NativeMethods -Namespace Win32
[Win32.NativeMethods]::ShowWindowAsync($ie.HWND, 3) | Out-Null
######################
# ④サイトアクセス #
######################
# 指定サイトへアクセス
$ie.navigate($URL_access);
# ブラウザコンテンツロード待ち
while($ie.busy){
Start-Sleep -milliseconds 100
}
Start-Sleep -Seconds 1
# 画面スクロール
If($FLG -eq 2){
Add-Type -AssemblyName System.Windows.Forms
}else{
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("{PGDN}")
}
######################
# ⑤キャプチャ取得 #
######################
# スクリーンキャプチャ取得
Add-Type -AssemblyName System.Drawing
$bitmap = new-object -TypeName System.Drawing.Bitmap -ArgumentList $ie.Width, $ie.Height
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
$graphics.CopyFromScreen($ie.Left, $ie.Top, 0, 0, $bitmap.Size)
# ファイル名設定
If($FLG -eq 0){
$bitmap.Save("IPA.png")
}elseif($FLG -eq 1){
$bitmap.Save("JPCERT.png")
}elseif($FLG -eq 2){
$bitmap.Save("JVN.png")
}else{
exit
}
# 変数カウントアップ
$FLG = $FLG + 1
######################
# ⑥IE終了 #
######################
# IEの終了
$ie.Quit();
}
[/code]
解説
ソースを書くにあたって意識した、大まかな処理の流れとしては以下となります。
1) IEを立ち上げる
2) ウィンドウの設定を行う
3) 指定したURLへアクセスする
4) 必要に応じて画面スクロールを実行する
5) 画面キャプチャを取得する
6) 保存するファイル名を指定する
実際に各セクションを作成してマージしてみましたが、当然そう簡単にはいきませんでした。
とろこどころ、必要な処理を追加等しなくてはならないので、細かく下記に記載します。
①変数指定
[code lang=”ps”]
# アクセスURLの設定
$URL = @("https://www.ipa.go.jp/"
,"https://www.jpcert.or.jp/"
,"http://jvn.jp/index.html")
# フラグ用変数設定
$FLG = "0"
# 数値型へ変換
$FLG = [int]$FLG
[/code]
アクセスURLの設定では、任意の変数[URL]を作成して、そこに画面キャプチャを取得したいサイトのURLをカンマ区切りで記載します。今回は例として3サイトで作成しました。
フラグ用変数設定では、後の構文でIF文の条件分岐が出てくるのですが、そこでキャプチャファイル名を決定する際の旗として利用します。初期値として「0」を挿入しておきます。
また、フラグの形式を数値型であると指定します。この設定がないだけで、IF文の分岐が上手くいきません。
②ループ処理設定
[code lang=”ps”]
# URLの数だけ処理実行
foreach($URL_access in $URL){
[/code]
変数指定にて作成した変数[URL]を代入して、設定したURLの数だけ{ }内の処理を実行する条件文です。
③IE起動設定
[code lang=”ps”]
# IEの起動
$ie = New-Object -ComObject InternetExplorer.Application
# IEウインドウ設定
$ie.Visible = $true
$ie.StatusBar = $false
$ie.ToolBar = $false
$ie.MenuBar = $false
$ie.AddressBar = $true
# ウィンドウの最大化
$dll_info = ‘[DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);’
Add-Type -MemberDefinition $dll_info -Name NativeMethods -Namespace Win32
[Win32.NativeMethods]::ShowWindowAsync($ie.HWND, 3) | Out-Null
[/code]
本オブジェクトから実際にキャプチャ取得をする為の動作となります。
IEの起動に関して、New-Object コマンドレットを使用して、Internet Explorer アプリケーションを表す COM オブジェクトのインスタンスを作成します。
ウィンドウ設定ではキャプチャ取得の際に、ジャマにならないよう、各項目表示を検討します。項目の詳細は以下です。
・「Visible」 アプリケーションの表示/非表示
・「StatusBar」 IE下部のステータスバーの表示/非表示
・「ToolBar」 IE上部のツールバーの表示/非表示
・「MenuBar」 IE上部のメニューバーの表示/非表示
・「AddressBar」 IE上部のURL入力欄となるアドレスバーの表示/非表示
ウィンドウの最大化を実行しないと、実行する端末の設定によってウィンドウサイズが異なっています。それにより実行結果に差異が生じてしまう為、同一動作が保証できません。割と大事なポイントです。
ここでは、まずDllを定義したあとに「Add-Type」からクラスを定義してオブジェクトを使用出来るようにします。最後に「Win32.NativeMethods」にて動作している全てのアプリのウィンドウを最大化して表示しています。
④サイトアクセス
[code lang=”ps”]
# 指定サイトへアクセス
$ie.navigate($URL_access);
# ブラウザコンテンツロード待ち
while($ie.busy){
Start-Sleep -milliseconds 100
}
Start-Sleep -Seconds 1
# 画面スクロール
If($FLG -eq 2){
Add-Type -AssemblyName System.Windows.Forms
}else{
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.SendKeys]::SendWait("{PGDN}")
}
[/code]
ウィンドウの設定が完了したら、実際にサイトへアクセスの指示をします。サイトへのアクセスはNavigateメソッドを使用します。Navigateメソッドは指定したURLをIE(InternetExplorer)で表示させます。
アクセス後はネットワークの状況により、ページロードまでの時間はまちまちであると思います。その為、ロード待ちの設定を挿入することが大切です。この設定がないことで、何も写っていない真っ白な画面キャプチャが取得されることがありました。
画面スクロールでは、{PGDN}キーを1度押下する指定で、キーボードの下矢印を押下する命令を与えています。IF文により{PGDN}キーを何回押下するか分岐をしています。3番目のJVNサイトでは画面スクロールは不要としたい為、$FLGに2(JVNサイト)が挿入されていたらスクロールせずに画面キャプチャを取得する動きとしています。
⑤キャプチャ取得
[code lang=”ps”]
# スクリーンキャプチャ取得
Add-Type -AssemblyName System.Drawing
$bitmap = new-object -TypeName System.Drawing.Bitmap -ArgumentList $ie.Width, $ie.Height
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
$graphics.CopyFromScreen($ie.Left, $ie.Top, 0, 0, $bitmap.Size)
# ファイル名設定
If($FLG -eq 0){
$bitmap.Save("IPA.png")
}elseif($FLG -eq 1){
$bitmap.Save("JPCERT.png")
}elseif($FLG -eq 2){
$bitmap.Save("JVN.png")
}else{
exit
}
# 変数カウントアップ
$FLG = $FLG + 1
[/code]
取得するページが開いたら、キャプチャの取得セクションに移行します。
まず「Add-Type」にて「System.Drawing」というアセンブリを追加します。
ファイル名に関しては、サイト名称を付与したかったのでIF文分岐にしました。配列を利用することでよりスマートになるとも思いますが、上手く実現出来ませんでした。ここで使用している[FLG]は、すぐ下の変数カウントアップでキャプチャを1件取得するごとにカウントアップさせています。
1件目のIPAサイトは、FLGに初期値の[0]が入力されているので、IF文で「$bitmap.Save(“IPA.png”)」の処理となり、その後FLGに1が足されます。
2件目のJPCERTサイトは、FLGに[1]が入力されているので、IF文で「$bitmap.Save(“JPCERT.png”)」の処理となり、その後FLGに1が足されます。
3件目のJVNサイトは、FLGに[2]が入力されているので、IF文で「$bitmap.Save(“JVN.png”)」の処理となり、その後FLGに1が足されます。
⑥IE終了
[code lang=”ps”]
# IEの終了
$ie.Quit();
[/code]
処理の最後にIEを閉じないと、サイトを開いたままの状態となってしまう為、終了処理を記載します。
実行結果
powershellを実際に実行して取得した画面キャプチャが以下です。スクロールバーを見ると、命令通り必要に応じてスクロールした後の画面がキャプチャされているのがわかります。
<IPA.png>

<JPCERT.png>

<JVN.png>

終わりに
ソースを0からガツガツ書くのは新卒以来なので、苦労する面が多々ありました。このような作業はセンスが問われるので、いかに分かり易くスマートなソースを書けるかが重要であると思います。自分にはセンスがないので、上記ソースもまだまだ改善の余地はあると思います。
ただ、そのような苦労の中、試行錯誤を重ねて要件を実現出来たときの喜びを久々に思い出すことが出来ました。業務の一環で作成していましたが、楽しささえ覚えました。開発の方が自分には向いているのでしょうか・・・
最後に、調べている過程でソース内にC言語を用いてマウススクロールを実現しているものもありました。別途作成したバッチからPowershellを呼び出すことで、同じ動作の実現が可能になるようです。