先日とあるフォレンジックの勉強会に参加させていただいた際に SRUM(System Resource Usage Monitor) という Windows のアーティファクトの存在を知りました。
Forensic 界隈では常識的なシステムなのかもしれませんが、インサイド Windows や公式のドキュメントを検索してもほとんど情報が見つからなかったので、今回新しく知る機会を得られて非常に良かったと思います。
Forensic の観点では SRUM の情報は不審なプロセスやネットワークのアクティビティの発見に利用しているようでしたが、今回は主にパフォーマンス系のトラブルシューティングの観点で SRUM の情報を利用する方法についてまとめてみようと思います。
もくじ
SRUM とは
SRUM(System Resource Usage Monitor) は、アプリケーションやサービス等の利用状況を監視するWindowsの機能で、Windows 8 から導入されているらしいです。
SRUM が収集したデータはシステム内に一時保存された後、1 時間ごと、もしくはシャットダウン時に C:\Windows\System32\sru\SRUDB.dat
に転送されるようです。
なお、以下の記事では SRUM が収集した情報は、SRUDB.dat に転送されるまで一時的に SOFTWARE レジストリに保持されると記載されています。
参考:フォレンジックにおけるSRUMの活用: NECセキュリティブログ | NEC
しかし、以下では最新の Windows では SRUM で収集した情報は SOFTWARE レジストリではなくメモリ内の記憶領域に保存されるようになっていると説明されています。
In recent versions of Windows, SRUM no longer uses the registry to store temporarily database records. Nowadays, SRUM relies on 2 types of storage:
A Tier1 store, which is in memory and is updated every Tier1Period (60 seconds by default) with the data from the SRUM extensions. A Tier2 store, which is the SRUM database on disk and is updated every Tier2Period (1 hour by default) with the content of the Tier1 store.
参考:SRUM Analysis · WithSecureLabs/chainsaw Wiki
SRUM の動作について正式に公開されているドキュメントは確認できませんが、いずれにせよ一時保存されたデータがおよそ 1 時間ごとに C:\Windows\System32\sru\SRUDB.dat
に保存されるという点については共通しているようです。
SRUM には、ネットワークデータ通信の使用量やアプリケーションの使用状況に関する統計情報が記録されます。
参考:Beyond Windows Forensics with Built-in Microsoft Tooling
参考:SRUM: Forensic Analysis of System Resource Utilization Monitor
なお、Forensic insights about the DLLs related to the SRUM extensions の記載によると、SRUM データベースに保存される情報はいくつかの拡張機能で収集されていることが示されています。
ネットワークデータに関する情報収集は WFP に依存しており、記録される通信データのサイズには OSI モデル第 2 層のフレームのサイズが記録されるそうです。
また、端末のネットワークトラフィックが VPN プロセスを経由する場合、監査された入出力データのサイズは VPN プロセスに紐づけられるようです。
また、この SRUDB は Extensible Storage Engine(ESE) というフォーマットで保存されているらしく、あとで使用する通り libesedb などのライブラリを用いて解析を行うこともできます。
参考:拡張可能なストレージ エンジン - Win32 apps | Microsoft Learn
SRUM データベースの解析
C:\Windows\System32\sru\SRUDB.dat
に保存された情報は srum-dump や NetworkUsageView などのツールを使用して解析することができます。
参考:Displays network usage information stored in the SRUDB.dat database of Windows 10/8.
NetworkUsageView を使用する場合、アプリケーションを起動して [Options]>[Advanced Options] から調査対象のデバイスで取得した SRUDB.dat をロードすることで SRUMDB からネットワークデータの情報を参照できます。
なお、srum-dump を使用した場合にはネットワークデータだけでなくアプリケーションの利用情報なども簡単にダンプすることができたので、より多くの情報を参照したいのであれば srum-dump を使用するのがよさそうです。
ちなみに、C:\Windows\System32\sru\SRUDB.dat
のコピーは以下のようなエラーで結構頻繁に失敗します。(大体 1 分くらい待つとコピーできるようになります)
なので、例えば以下のように robocopy などを使用してコピー操作をリトライさせるようにすると良いと思います。
robocopy "C:\Windows\System32\sru" "C:\Users\Public\Downloads" /B
NetworkUsageView を使用した解析
1 GB のファイルを Microsoft Edge からアップロードした場合の SRUMDB を解析してみます。
まずは Microsoft Edge から適当なアップロードサイトに 1 GB のファイルアップロードを実施します。
Wire Shark で取得したパケットキャプチャの統計情報でも、ちょうど 1 GB のデータを外部アドレスに送信していることを確認できます。
実際にこのアップロード操作を行った端末でキャプチャされた SRUMDB を解析すると、Microsoft Edge のアプリケーションからちょうど 1 GB 程度のアップロードが行われていることがわかります。
注意が必要なのは、この時の Timestamp の値は実際に通信が発生した時刻ではなく、SRUMDB にデータが書き込まれた時間である点です。
また、この時表示されている通信データサイズなどは、前回のイベント転送以降に記録された情報に該当します。(SRUMDB にデータが転送されるのはおよそ 1 時間ごと、もしくは端末のシャットダウン時です。)
このあたりの説明は以下の記事にて非常にわかりやすく説明されており、参考になりました。
参考:フォレンジックにおけるSRUMの活用: NECセキュリティブログ | NEC
Note:App Name が空の項目について
ちなみに、1 番上の App ID が 1 で App Name が空の項目ですが、これは SRUMDB のテーブルの一つである SruDbIdMapTable の idBlob カラムの値が空であることを示しているようです。
実際に以下のスクリプトで libesedb ライブラリを使用して SRUMDB の SruDbIdMapTable テーブルをダンプしてみると、App ID が 1 となる項目の場合は idBlob が None となることを確認できます。
# sudo apt-get install libesedb-utils libesedb-dev python3-libesedb
import pyesedb
esedb_file = "SRUDB.dat"
esedb = pyesedb.file()
esedb.open(esedb_file)
table_names = [esedb.get_table(i).get_name() for i in range(esedb.number_of_tables)]
print(table_names)
table = esedb.get_table_by_name("SruDbIdMapTable")
column_names = [table.get_column(i).get_name() for i in range(table.number_of_columns)]
print(column_names)
for record_index in range(table.number_of_records):
record = table.get_record(record_index)
id_type = record.get_value_data_as_integer(0)
id_index = record.get_value_data_as_integer(1)
try:
id_blob = record.get_value_data(2).decode("utf-16le", errors="ignore").strip("\x00")
if id_type == 0:
print(f"AppID: {id_index}, AppName: {id_blob}")
except:
# AppID: 0 AppName: None
if id_type == 0:
print(f"AppID: {id_index}, AppName: {record.get_value_data(2)}")
実際のところこの App Id が 1 のエントリが何を指しているのかを示す情報を見つけることはできませんでした。
ただし、Chat GPT の回答や以下のスレッドのコメントなどを見ると、システム全体の I/O を追跡しているのではないかという情報を確認できました。
実際に Microsoft Edge から 1GB のファイルをアップロードした際には、ほぼ同程度の通信料が App ID 1 のエントリと紐づけられており、ある程度の信憑性はありそうに思えます。
この辺りは、もし詳しい方がいればぜひ教えていただけると助かります。
Note:libesedb のビルド
今回は apt で libesedb をインストールしましたが、以下のコマンドでソースコードからビルドすることもできました。
sudo apt install git autoconf automake autopoint libtool pkg-config
wget https://github.com/libyal/libesedb/releases/download/20240420/libesedb-experimental-20240420.tar.gz
targz libesedb-experimental-20240420.tar.gz
cd libesedb-20240420/
./configure --enable-python
make
sudo make install
参考:Building · libyal/libesdb Wiki
まとめ
SRUM は Forensic の用途で使用されることが多いようですが、個人的にはパフォーマンスやネットワーク系のトラブルシューティングにも非常に有用そうだと感じました(恐らくこちらが本来の用途だとは思いますが)
特に再現性の低いパフォーマンスや帯域逼迫系の問題の場合、負荷高騰を引き起こしたプロセスの特定が調査のボトルネックになることがしばしばありますが、SRUM の解析を行うことでこのような問題の原因をスムーズに調査することができるようになりそうです。