【PowerShell】特定のプロセス起動を監視したいですか?~Register-WMIEvent コマンドレット

imageぜんぜん話は違うのですが、日本の SQL Server サポートチームの Blog が気合入っています(笑)。今後ともご贔屓にしてくださいませ。評判が悪かったら元に戻すと言っていたので、4月には元に戻っている可能性があります。

さて、かなり前のことになりますが、以下の資料を SlideShare に投稿しました。このスライドでは、VBScript を使用して、WMI のイベントをリアルタイムに収集するスクリプトについて解説しています。

image

このスライドで解説している手法を使用すると、システム内に発生したさまざまなイベントを待ち合わせて、その次のアクションを自動的に実行することができます。

例えば、次のような処理が簡単に自作できます。

  • 監査ログを監視して、ユーザーが作成されことを検出したらホームディレクトリを自動的に作成する
  • 特定のサービスを監視して、停止したらメールする
  • 特定の誰かがログオンしたらメッセージを表示する
  • 特定のプログラムが起動したら強制的に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

 

これ、解消されていなかったんだなぁ....。