かつて山市良と呼ばれたおじさんのブログ

セイテクエンジニアのブログ  かつて山市良と呼ばれたおじさんのブログ  メモ. 依頼品: 不審なプロセスを見つけるスクリプト

 

 

メモ. 依頼品: 不審なプロセスを見つけるスクリプト

2024年07月19日配信
執筆者:山内 和朗

 製品コラム「不審なプロセスの監視(簡易版)ーBOM for Windows活用例」で使用したBOM用PowerShellスクリプト「find-susproc.ps1」が出来るまでを紹介します。製品コラムで使用してもらったのは、まだ十分な実装ではないものでしたが、その後も試行錯誤してなんとか出来上がりました。行き当たりばったり、つぎはぎだらけのスクリプトですが、意外とうまく動ごくと思います。

 

最初に作ったスクリプト

 

 依頼の内容は、不審なプロセスの実行を検出するスクリプトです。VBScriptは非推奨になり、数年先ですが廃止も見えてきたので、PowerShellスクリプトで書くことに決めました。

ITニュース. VBScript廃止までのタイムライン発表、ただし廃止時期は未定

 ローカルコンピューターで実行されているプロセスを取得するコマンドレットとしては、「Get-Process」があります。Get-Processは、ファイルバージョン(FileVersion)、コマンドライン(CommandLine)、親プロセス(Parent)、CPU使用率(CPU)、メモリ使用量(VirtualMemorySize、PrivateMemorySize、WorkingSetなど)、応答状態(Respoinding)、開始時日(StartTime)など、タスクマネージャー(Taskmgr.exe)から得られる以上の各種情報を取得できますが、シンプルな実装にするためプロセス名(Name)とパス(Path)だけを利用することにします。ちなみに、パスが無いものはコマンドラインも無いようです。コマンドラインからはより詳細な情報を取得できそうですが、複雑になるため、パスだけを考えることにします(画面1)。

 

memo13_scr01
画面1 Get-Processで取得できるプロセス名とパスから、不審なプロセスをあぶりだすことを考える

 最初に作成したスクリプトは次のようなものです(実は、これが製品コラムで使用していたものです)。スクリプトでは、$pathlist配列に信頼するパスを定義しておき、それを実行中のプロセスのパスと比較して評価します。$pathlistの値にフルパスがあれば完全一致、値がフォルダーのパスの場合は前方一致で評価します。つまり、$pathlistを細かく定義すればいくらでも詳細な監視ができるということです。スクリプトでは、円記号(¥)は文字列比較の際に厄介なので「/」に置き換え、大文字小文字を区別することなく比較し、リストにないプロセスを不審なプロセス(Suspicious process)として報告します(画面2)。

[find-susproc1.ps1]

#既知の信頼するパス
$pathlist = @("C:\Windows\Explorer.exe",`
"C:\Windows\ImmersiveControlPanel",`
"C:\Windows\Servicing",`
"C:\Windows\System32",`
"C:\Windows\SysWOW64",`
"C:\Job Director\SV\bin",`
"C:\Program Files",`
"C:\Program Files (x86)",`
"C:\Windows\SystemApps",`
"C:\Program Files\WindowsApps")

$pathlist = $pathlist.Replace("\","/")
$susproc = 0

$processes = (Get-Process |Select Id, Name, Path)
foreach ($process in $processes) {
  if ($process.path.Length -gt 0){
    $targetpath = ($process.path).Replace("\","/")
    $findinpath = $false
    foreach ($path in $pathlist) {
      if ($targetpath -like "$path") {
        $findinpath = $true
        break;
      } elseif ($targetpath -like "$path*") {
        $findinpath = $true
        break;
      }
    }
    if ($findinpath -eq $false) {
      $outstr = $outstr + $process.Name + " ( " + $process.path + " )`n"
      #Copy-Item $process.path -Destination "<Destination Path>"
      $susproc = $susproc + 1
    }
  }
}
if ($susproc -gt 0) {
  $outstr = "Suspicious process found.`n`n" + $outstr
} else {
  $outstr = "No suspicious process found."
}
Write-Host $outstr
$susproc

 

画面2
画面2 最初に作成したスクリプトはとりあえずうまく動いているように見えるが、実は全く不十分

 

パス情報の無いプロセスも無視できない

 

 最初に作成したスクリプトは、パスの無いプロセスについて何も判断していません。スクリプトに以下のようにコードを追加して(赤色の部分)、パスの無いプロセスにどんなものがあるのかWindows ServerとWindows 11で調べてみました(画面3、画面4)。

[find-susproc2.ps1]

#既知の信頼するパス
$pathlist = @("C:\Windows\Explorer.exe",`
"C:\Windows\ImmersiveControlPanel",`
"C:\Windows\Servicing",`
"C:\Windows\System32",`
"C:\Windows\SysWOW64",`
"C:\Job Director\SV\bin",`
"C:\Program Files",`
"C:\Program Files (x86)",`
"C:\Windows\SystemApps",`
"C:\Program Files\WindowsApps")

$pathlist = $pathlist.Replace("\","/")
$susproc = 0

$processes = (Get-Process |Select Id, Name, Path)
foreach ($process in $processes) {
  if ($process.path.Length -gt 0){
    ・・・(中略)・・・
  } else {
    Write-Host "Debug:" $process.Id $process.Name $Proccess.path
  }
}

if ($susproc -gt 0) {
  $outstr = "Suspicious process found.`n`n" + $outstr
} else {
  $outstr = "No suspicious process found."
}
Write-Host $outstr
$susproc

画面3
画面3 システムプロセスとウイルス対策製品のプロセスにはパス情報がない

 

画面4

画面4 ESETアンチウィルスがインストールされたWindows 11での実行結果。ウイルス対策関連のプロセスにはパス情報が無いのは共通

 

 システムプロセス(System, Secure System、Lsass、LsaIso、Idle、Memory Compression、csrss、services、smss、wininitなど)と、ウイルス対策製品(MsMpEng、NisSrv、ekrn、MpDefenderCoreServiceなど)にはパス情報が無いのがわかります。また、$pathlistに追加したほうがよいパス(Windows Update関連のC:¥Windows¥sxsの下にあるTiWorker.exeなど)も見つかりました。アイドル(Idle)プロセスは、CPUのアイドル時間の計測のために設けられた実体のない(実行可能ファイルを持たない)プロセスです。実は、システム(System)、セキュアシステム(Secure System)、メモリ圧縮プロセス(Memory Compression)も同じように実体(プロセス名.exe)を持ちません。

 

パス情報の無いプロセスへの対応

 

 パス情報の無いプロセスを例外的に扱えるように、新たに$knownprocという配列を追加し、システムプロセスやマルウェア対策のパスを正規のプロセスとして評価できるようにしました。また、パスの無い他のプロセスについては、システム環境変数PATHにプロセス名.*(.exeを想定)が存在すれば正規のプロセスとして評価するようにしました。さらに、システム環境変数PATHに登録されていないパスについても追加で評価できるように、$knownpath配列を追加しました(赤色の部分)。

 

[find-susproc3.ps1]

#既知の信頼するパス
$pathlist = @("C:\Windows\Explorer.exe",`
"C:\Windows\ImmersiveControlPanel",`
"C:\Windows\Servicing",`
"C:\Windows\sxs",`
"C:\Windows\System32",`
"C:\Windows\SysWOW64",`
"C:\Job Director\SV\bin",`
"C:\Program Files",`
"C:\Program Files (x86)",`
"C:\Windows\SystemApps",`
"C:\Program Files\WindowsApps")

#既知のプロセス(Get-ProcessのPathが空の時に使用)
$knownproc = @("idle","Memory Compression","Secure System", "vmmem","vmmemCmZygote","MsMpEng","NisSrv", "MpDefenderCoreService")

#システム環境変数PATHに含まれない信頼するパス(Get-ProcessのPathが空の時に使用)
$knownpath  = "C:\Program Files\ESET\ESET Security;C:\Program Files\Mozilla Firefox"

$pathlist = $pathlist.Replace("\","/")
$susproc = 0

$processes = (Get-Process |Select Id, Name, Path)
foreach ($process in $processes) {
  if ($process.path.Length -gt 0){
    ・・・(中略)・・・
  } else {
    $findinpath = $false
    if ($knownproc.ToUpper() -Contains ($process.Name).ToUpper()) {
      $findinpath = $true
    } else {
      $syspaths = ($ENV:Path+";"+$knownpath).Split(";")
      foreach ($syspath in $syspaths) {
        $targetstr = $syspath+"\"+$process.Name+".*"
        $targetstr = $targetstr.Replace("\\","\")
        if (Test-Path $targetstr) {
          $findinpath = $true
          break;
        }
      }
    }
    if ( -not $findinpath ) {
      $outstr = $outstr + $process.Name
       $outstr = $outstr + " ( " + $process.path + " )`n"
      #Copy-Item $process.path -Destination "<Destination Path>"
      $susproc = $susproc + 1
    }
  }
}

if ($susproc -gt 0) {
  $outstr = "Suspicious process found.`n`n" + $outstr
} else {
  $outstr = "No suspicious process found."
}
Write-Host $outstr
$susproc

 

 監視対象のサーバーでこのスクリプトを実行して、平常時に不審なプロセスが見つからない状態になるまで、$pathlist、$knownproc、$knownpathの各変数を調整していけば、そのサーバー専用(限定)で不審なプロセスの発見に役立つスクリプトになるはずです。シンプルなスクリプトですが、実行されるプロセスがある程度決まっているサーバーなら使いようがあるのではないでしょうか。厳重なセキュリティで保護したい場合は、やはりAppLockerによる監査や規則の実施強制が適しています(→製品コラム「不審なプロセスの監視ーBOM for Windows活用例」。このスクリプトはあくまでも簡易版です。

 

BOMのカスタム監視用に調整して完成

 

 最後に、作成したスクリプトをBOMのカスタム監視用に調整します(画面5)。BOMのカスタム監視では、このスクリプトを実行して不審なプロセスの数を返すようにします。そのために、このスクリプトは$susproc(またはreturn $susprocまたはWrite-Output $susproc)で終了するようにしています。また、パラメーターが指定されたときに指定されたパスにログファイルとして出力するようにコードを追加しました(赤色の部分)。なお、$knownprocに追加した「PersistentDataRepair」は(緑色の部分)、カスタム監視タスクでプログラムを実行する際に実行されることがある、BOMのコンポーネント(PersistentDataRepair.exe)です。

 

[find-susproc.ps1]

Param($Arg1)
$logfile = $Arg1

#既知の信頼するパス
$pathlist = @("C:\Windows\Explorer.exe",`
"C:\Windows\ImmersiveControlPanel",`
"C:\Windows\Servicing",`
"C:\Windows\sxs",`
"C:\Windows\System32",`
"C:\Windows\SysWOW64",`
"C:\Job Director\SV\bin",`
"C:\Program Files",`
"C:\Program Files (x86)",`
"C:\Windows\SystemApps",`
"C:\Program Files\WindowsApps")

#既知のプロセス(Get-ProcessのPathが空の時に使用)
$knownproc = @("idle","Memory Compression","Secure System", "vmmem","vmmemCmZygote","MsMpEng","NisSrv", "MpDefenderCoreService", "PersistentDataRepair")

#システム環境変数PATHに含まれない信頼するパス(Get-ProcessのPathが空の時に使用)
$knownpath  = "C:\Program Files\ESET\ESET Security;C:\Program Files\Mozilla Firefox"

$pathlist = $pathlist.Replace("\","/")
$susproc = 0

$processes = (Get-Process |Select Id, Name, Path)
foreach ($process in $processes) {
  if ($process.path.Length -gt 0){
    $targetpath = ($process.path).Replace("\","/")
    $findinpath = $false
    foreach ($path in $pathlist) {
      if ($targetpath -like "$path") {
        $findinpath = $true
        break;
      } elseif ($targetpath -like "$path*") {
        $findinpath = $true
        break;
      }
    }
    if ($findinpath -eq $false) {
      $outstr = $outstr + $process.Name + " ( " + $process.path + " )`n"
      #Copy-Item $process.path -Destination "<Destination Path>"
      $susproc = $susproc + 1
    }
  } else {
    $findinpath = $false
    if ($knownproc.ToUpper() -Contains ($process.Name).ToUpper()) {
      $findinpath = $true
    } else {
      $syspaths = ($ENV:Path+";"+$knownpath).Split(";")
      foreach ($syspath in $syspaths) {
        $targetstr = $syspath+"\"+$process.Name+".*"
        $targetstr = $targetstr.Replace("\\","\")
        if (Test-Path $targetstr) {
          $findinpath = $true
          break;
        }
      }
    }

    if ( -not $findinpath ) {
      $outstr = $outstr + $process.Name
       $outstr = $outstr + " ( " + $process.path + " )`n"
      #Copy-Item $process.path -Destination "<Destination Path>"
      $susproc = $susproc + 1
    }
  }
}

if ($susproc -gt 0) {
  $outstr = "Suspicious process found.`n`n" + $outstr
} else {
  $outstr = "No suspicious process found."
}
if ($logfile -ne $null) {
  Get-Date -Format "yyyy/MM/dd HH:mm:ss" 2>&1 > $logfile 
  $outstr 2>&1 >> $logfile
}
$susproc

 

画面5

画面5 BOMのカスタム監視用に調整したスクリプト。パラメーターなしだとプロセス数のみを返し、パラメーターにログファイルのパスを指定すると、同時にログファイルの出力も行う

 

 それでは、このスクリプトの内容を踏まえて、製品コラム「不審なプロセスの監視(簡易版)ーBOM for Windows活用例」をお楽しみください。

 

お勧めの二冊

 Windowsのプロセス、スレッド、アーキテクチャ、メモリ管理、セキュリティなどの詳細については、Windowsの内部構造を解き明かすバイブルとして開発者・システム管理者に永く読み継がれてきた「インサイドWindows(原題: Windows Internals)」の最新版、第7版 上/下をお勧めします。上では、システムプロセスやセキュアシステム、メモリ圧縮プロセスについて、詳しく解説されています。

insidewin7thp1インサイドWindows 第7版 上

ISBN     9784822253578
発行日     2018年05月01日
著者名     Pavel Yosifovich、Alex Ionescu、Mark E. Russinovich、David A. Solomon 著、山内 和朗 訳
発行元     日経BP

insidewin7thp2インサイドWindows 第7 下

ISBN     9784296080205
発行日     2022年09月05日
著者名     Andrea Allievi, Mark E. Russinovich, Alex Ionescu, David A. Solomon  著、山内 和朗 訳
発行元     日経BP

※書籍の正誤情報、およびユーティリティのこれまでの更新情報については、旧ブログサイトをご覧ください。

 

blog_subscribe

blog_comment

最新記事