【PowerShell】特定のプロセス起動を監視したいですか?~Register-WMIEvent コマンドレット
ぜんぜん話は違うのですが、日本の SQL Server サポートチームの Blog が気合入っています(笑)。今後ともご贔屓にしてくださいませ。評判が悪かったら元に戻すと言っていたので、4月には元に戻っている可能性があります。
さて、かなり前のことになりますが、以下の資料を SlideShare に投稿しました。このスライドでは、VBScript を使用して、WMI のイベントをリアルタイムに収集するスクリプトについて解説しています。
このスライドで解説している手法を使用すると、システム内に発生したさまざまなイベントを待ち合わせて、その次のアクションを自動的に実行することができます。
例えば、次のような処理が簡単に自作できます。
- 監査ログを監視して、ユーザーが作成されことを検出したらホームディレクトリを自動的に作成する
- 特定のサービスを監視して、停止したらメールする
- 特定の誰かがログオンしたらメッセージを表示する
- 特定のプログラムが起動したら強制的にKILLする
などなど。
※上記スライドには MOF を使用してスクリプトをサービス化する手法についても書かれています
【PowerShell 1.0 の場合】
これと同じことが PowerShell でできないだろうか....と思いつつすっかり忘れていましたが、実は初期の PowerShell の頃から同じことは実現可能でした。
(参考)Windows PowerShell 2.0 - WMI Event Monitoring
上記の参考リンクでは、以下の様なスクリプトを紹介しています(ちょっとだけ変えています)。このスクリプトでは、WMI の __InstanceCreationEvent というイベント監視用クラスを使用し、Win32_Process を監視しています。クラス名に「Creation」という言葉が入っている通り、「何らかのプロセスが起動したらコンソールにプロセス名を表示する」という動作をします。
$a = 0
$timespan = New-Object System.TimeSpan(0, 0, 1)
$scope = New-Object System.Management.ManagementScope("\\.\root\cimV2")
$query = New-Object System.Management.WQLEventQuery `
("__InstanceCreationEvent",$timespan, "TargetInstance ISA 'Win32_Process'" )
$watcher = New-Object System.Management.ManagementEventWatcher($scope,$query)
do
{
$b = $watcher.WaitForNextEvent()
$b.TargetInstance.Name
}
while ($a -ne 1)
このスクリプトの記載をご覧いただければわかる通り、VBScript とほとんど同じです。Do ~ While でイベントを待ち合わせるのも同じですね。
もし、「プロセスの停止」を待ち合わせるのであれば、クラス部分を __InstanceDeletionEvent に置き換えます。
非常に便利なのですが、この書き方だとスクリプトを常駐しておかなければならないという面倒くささがあります。
【PowerShell 2.0 の場合】
PowerShell 2.0 では、専用のコマンドレットが提供されました。
Register-WMIEvent コマンドレットです。
このコマンドレットにより、上記のスライドに書かれた「スクリプトのサービス化」なんてことをせずとも、WMIイベントを待ち合わせるジョブがシステムに登録されます。
※ 以前、田辺さんが紹介されていたのを思い出しました(Windows PowerShell V2 の新機能)。
そこで、上記のスクリプトを Register-WMIEvent で置き換えると、以下ようなかんじになります。
少し長いですが、これで1行です。
Register-WMIEvent -query "Select * From __InstanceCreationEvent within 3
Where TargetInstance ISA 'Win32_Process'"
-sourceIdentifier "NewProcess"
-Action { $Event.SourceEventArgs.NewEvent.TargetInstance.Name |
Out-File -FilePath "c:\tmp\log.txt" -Append}
-query の部分は vbscript と変わらないですね。
上記コマンドレットを実行すると、新しいプロセスが起動するびに、- Action {} 部分が実行されます。
ここでは起動したプロセスの Name を log.txt というテキストファイルに Out-File しています。
$Event は、このコマンドレットに組み込みの変数で、WMI のイベントが格納されています。このコマンドレット内でした使用することができません。
$Event.SourceEventArgs.NewEvent.TargetInstance は、イベント(ここではプロセスの起動)が発生したときに返される Win32_Process のインスタンスです。
ためしに、以下のように修正して実行し、log.txt の結果を見てみましょう。
Register-WMIEvent -query "Select * From __InstanceCreationEvent within 3
Where TargetInstance ISA 'Win32_Process'"
-sourceIdentifier "testProcess"
-Action { $Event.SourceEventArgs.NewEvent.TargetInstance | Get-Member |
Out-File -FilePath "c:\tmp\log.txt" -Append}
以下のような結果が格納されているはずです。Win32_Process だってことがわかりますね。
TypeName: System.Management.ManagementBaseObject#root\CIMV2\Win32_Process
Name MemberType Definition
---- ---------- ----------
AttachDebugger Method System.Management.ManagementBaseObject...
GetOwner Method System.Management.ManagementBaseObject...
GetOwnerSid Method System.Management.ManagementBaseObject...
SetPriority Method System.Management.ManagementBaseObject...
Terminate Method System.Management.ManagementBaseObject...
Caption Property System.String Caption {get;set;}
CommandLine Property System.String CommandLine {get;set;}
CreationClassName Property System.String CreationClassName {get;s...
CreationDate Property System.String CreationDate {get;set;}
CSCreationClassName Property System.String CSCreationClassName {get...
CSName Property System.String CSName {get;set;}
Description Property System.String Description {get;set;}
ExecutablePath Property System.String ExecutablePath {get;set;}
ExecutionState Property System.UInt16 ExecutionState {get;set;}
Handle Property System.String Handle {get;set;}
HandleCount Property System.UInt32 HandleCount {get;set;}
InstallDate Property System.String InstallDate {get;set;}
KernelModeTime Property System.UInt64 KernelModeTime {get;set;}
MaximumWorkingSetSize Property System.UInt32 MaximumWorkingSetSize {g...
MinimumWorkingSetSize Property System.UInt32 MinimumWorkingSetSize {g...
Name Property System.String Name {get;set;}
OSCreationClassName Property System.String OSCreationClassName {get...
OSName Property System.String OSName {get;set;}
OtherOperationCount Property System.UInt64 OtherOperationCount {get...
OtherTransferCount Property System.UInt64 OtherTransferCount {get;...
PageFaults Property System.UInt32 PageFaults {get;set;}
PageFileUsage Property System.UInt32 PageFileUsage {get;set;}
ParentProcessId Property System.UInt32 ParentProcessId {get;set;}
PeakPageFileUsage Property System.UInt32 PeakPageFileUsage {get;s...
PeakVirtualSize Property System.UInt64 PeakVirtualSize {get;set;}
PeakWorkingSetSize Property System.UInt32 PeakWorkingSetSize {get;...
Priority Property System.UInt32 Priority {get;set;}
PrivatePageCount Property System.UInt64 PrivatePageCount {get;set;}
ProcessId Property System.UInt32 ProcessId {get;set;}
QuotaNonPagedPoolUsage Property System.UInt32 QuotaNonPagedPoolUsage {...
QuotaPagedPoolUsage Property System.UInt32 QuotaPagedPoolUsage {get...
QuotaPeakNonPagedPoolUsage Property System.UInt32 QuotaPeakNonPagedPoolUsa...
QuotaPeakPagedPoolUsage Property System.UInt32 QuotaPeakPagedPoolUsage ...
ReadOperationCount Property System.UInt64 ReadOperationCount {get;...
ReadTransferCount Property System.UInt64 ReadTransferCount {get;s...
SessionId Property System.UInt32 SessionId {get;set;}
Status Property System.String Status {get;set;}
TerminationDate Property System.String TerminationDate {get;set;}
ThreadCount Property System.UInt32 ThreadCount {get;set;}
UserModeTime Property System.UInt64 UserModeTime {get;set;}
VirtualSize Property System.UInt64 VirtualSize {get;set;}
WindowsVersion Property System.String WindowsVersion {get;set;}
WorkingSetSize Property System.UInt64 WorkingSetSize {get;set;}
WriteOperationCount Property System.UInt64 WriteOperationCount {get...
WriteTransferCount Property System.UInt64 WriteTransferCount {get;...
__CLASS Property System.String __CLASS {get;set;}
__DERIVATION Property System.String[] __DERIVATION {get;set;}
__DYNASTY Property System.String __DYNASTY {get;set;}
__GENUS Property System.Int32 __GENUS {get;set;}
__NAMESPACE Property System.String __NAMESPACE {get;set;}
__PATH Property System.String __PATH {get;set;}
__PROPERTY_COUNT Property System.Int32 __PROPERTY_COUNT {get;set;}
__RELPATH Property System.String __RELPATH {get;set;}
__SERVER Property System.String __SERVER {get;set;}
__SUPERCLASS Property System.String __SUPERCLASS {get;set;}
ここで注意していただきたいのが –SourceIdentifier パラメタです。-SourceIdentifier には、このジョブの識別名を指定します。ここでは NewProcess と指定しています。
同じ名前の識別名は指定できないので注意してください。
登録しておけば、あとは何もせずともずっと監視が続けられます。もちろん、OS を再起動しても消えません。
監視を終了するには、以下のように Unregister-Event コマンドレットを使用します。
Unregister-Event NewProcess
今回は単純な例をご紹介しましたが、もっともっと複雑な処理を実装することもできます。
それについては今後。
p.s.
テストを繰り返していると以下のエラーに見舞われることがあります。そのときは Windows Management Instrumentation サービスを再起動してください。そういう意味では...本番環境ではテストをしないほうがよいです。
Register-WmiEvent : クォータ違反です
発生場所 行:1 文字:18
+ Register-WMIEvent <<<< -query "Select * From __InstanceCreationEvent within
3 Where TargetInstance ISA 'Win32_Process'" -sourceIdentifier "NewProcess" -Act
ion { $Event.SourceEventArgs.NewEvent.TargetInstance.Name | Out-File -FilePath
"c:\tmp\log.txt" -Append}
+ CategoryInfo : NotSpecified: (:) [Register-WmiEvent]、Management
Exception
+ FullyQualifiedErrorId : System.Management.ManagementException,Microsoft.
PowerShell.Commands.RegisterWmiEventCommand
これ、解消されていなかったんだなぁ....。