PowerShellでプロセスに関連するDLLを一覧表示する方法を安全に進める結論は「processを名前だけでなくIdとStartTimeで特定し、Modulesを読み取ってModuleName、FileName、FileVersionを一覧化します。Access deniedや32/64bit差を「DLLなし」と扱わず、署名とpathを追加確認します。」です。Windows processのbitness、protected process、権限差でmodule一覧が欠ける可能性を明示するという前提を外すと同じ入力でも結果が変わります。先にprocess identityとmodule一覧をread-onlyで保存し、署名確認や追加調査を分けて進めます。
対象processをPIDとStartTimeで固定する
プロセスが読み込むDLLを調べる際は、PIDだけでなくStartTimeも記録してPID再利用を防ぎます。対象プロセスとPowerShellの32-bit・64-bit差、保護プロセス、管理者権限による取得制限を確認し、ModulesのPathを重複除去して保存します。アクセス拒否を空のモジュール一覧として扱いません。
- Get-ProcessでName、Id、StartTime、Pathを確認する
- 同名processが複数ないかIdで絞る
- PowerShellと対象processのarchitecture、権限を確認する
- outputにuser pathやsecurity product情報を含むため共有範囲を決める
Modules列挙に必要な権限とarchitectureを確認する
Modulesはその時点のsnapshotで、後からload/unloadされます。ModuleMemorySizeはfile sizeやprivate memoryと同義ではありません。protected process、別user、architecture差で例外が出ることがあり、結果が少ない場合はerror streamを確認します。同名DLLでもpathとhash、signatureが違えば別物です。
module pathをsystem領域と外部領域に分ける
processを一意に選ぶ
Get-Process -Name 'notepad' -ErrorAction SilentlyContinue | Select-Object ProcessName,Id,StartTime,Path
複数Idなら対象windowやownerを別の安全な手段で特定します。
Idからmodule一覧
Get-Process -Id 1234 -Module -ErrorAction Stop |
Select-Object ModuleName,FileName,ModuleMemorySize,@{n='FileVersion';e={$_.FileVersionInfo.FileVersion}}
1234は直前に確認した実Idへ置き換えます。process再起動後のId再利用に注意します。
特定directory外を探す
$mods=Get-Process -Id 1234 -Module
$mods | Where-Object FileName -notlike 'C:\Windows\System32\*' | Select-Object ModuleName,FileName
System32外が即不正という意味ではありません。調査候補を限定するだけです。
DLL署名を確認
$mods | ForEach-Object { Get-AuthenticodeSignature -LiteralPath $_.FileName } |
Select-Object Path,Status,SignerCertificate
Unsignedが常にmalware、Validが常に安全とは断定しません。publisherとexpected install pathを照合します。
CSVへ最小列を保存
$mods | Select-Object ModuleName,FileName,ModuleMemorySize | Export-Csv '.\modules-1234.csv' -NoTypeInformation -Encoding utf8
新規fileへ保存し、機密pathの共有範囲を制限します。
各DLLのAuthenticode署名を読む
- process名だけで一件と仮定する
- Access deniedをDLLなしとする
- ModuleMemorySizeをfile sizeと読む
- System32外を即不正と断定する
- 調査中にDLLを削除する
module一覧を最小列でCSV保存する
module列挙は読み取りです。DLLを使用中のdirectoryから移動・削除したりprocessを強制終了したりしません。疑わしいmoduleはpath、hash、signature、process Id、時刻を保存してsecurity担当へ渡します。調査tool導入はpublisherを確認し、rollbackできるtest端末で行います。CSV誤出力時はCSVだけを削除しprocessやDLLを変更しません。
process終了raceを空一覧と分ける
Process Explorer等の承認済みtool、Get-Process、file signature/hashを同時刻で照合します。process再起動前後を混ぜず、IdとStartTimeを記録します。module件数、error件数、signature status別件数を分けて報告します。
identity・module件数・unique pathを検収する
同名processをIdとStartTimeで一意化し、ModulesからModuleName、FileName、FileVersion、ModuleMemorySizeを取得します。bitness差、protected process、終了競合でmodule一覧が欠けるため、成功件数とaccess errorを分けます。
一つのprocess instanceからmoduleを列挙する
$p=Get-Process -Id 1234 -ErrorAction Stop
$identity="$($p.Id)|$($p.StartTime.Ticks)"
$mods=@($p.Modules | Select-Object ModuleName,FileName,FileVersionInfo,ModuleMemorySize)
[pscustomobject]@{Identity=$identity;ModuleCount=$mods.Count;UniquePaths=@($mods.FileName|Sort-Object -Unique).Count}
process終了とmodule access拒否をidentityで見抜く
- module一覧を取得:対象PID+StartTimeが維持され、module pathを一意一覧として取得できる
- process終了:process終了はExitedDuringReadとして0 moduleと区別する
- architecture・access error:AccessDenied/bitness/protected processはPartialOrDeniedとして、見えたDLLだけで完全としない
Nameだけで別instanceを混ぜず、CSVへ署名certificate全体やuser pathの機密情報を無加工で載せません。module pathがMicrosoft directory外というだけでmalware判定せず、署名・publisher・hashを照合します。
同名processとunsigned DLLを試す
$mods | ForEach-Object { $s=Get-AuthenticodeSignature -LiteralPath $_.FileName; [pscustomobject]@{Module=$_.ModuleName;Path=$_.FileName;Signature=$s.Status;Signer=$s.SignerCertificate.Subject} }
通常process、同名複数、32/64bit差、protected、測定中終了をtestします。PID、StartTime、module総数/unique数、access error、path/hash/signatureを保存します。
PIDは再利用されるため、取得前後のStartTimeを照合します。署名なしDLLを直ちにmalwareと断定せず、path、hash、publisher、processの役割を追加調査へ引き継ぎます。

コメント