名前付きカーネルオブジェクトの一覧を取得

任意のプロセスが使用している名前付きカーネルオブジェクトの一覧を取得したいのですが。


それ、Win32でできるよ!


ということで書いてみた*1。よければどうぞー(named_kernel_object.cab)。
動作確認は 2008x64 のみだけど、適当に修正すれば大抵の環境で動くと思う。 x86の2000/2003/Vistaでも動作するようにしました。

概略

プログラムはオブジェクトタイプを列挙するEnumObjectType関数、ハンドルテーブルを列挙するEnumHandleTable関数、オブジェクトディレクトリを再帰的に列挙するEnumObjectDirectory関数で構成されている。

表題の実装はEnumHandleTable関数にあり、次の処理によってプロセスが持つハンドル一覧が列挙され、名前を持つものについてはそれも出力される。

  1. NtQuerySystemInformation(SystemHandleInformation によりシステムのハンドルテーブル(SYSTEM_HANDLE_INFORMATION)を取得する
  2. ハンドルテーブルから、指定したプロセスが所有するハンドルを検索する
  3. 自プロセス以外を指定した場合は、ハンドルテーブルのハンドル値をDuplicateHandleで複製して扱う
  4. 取得したハンドルによる NtQueryObject(Handle, ObjectNameInformation でオブジェクトの名前(OBJECT_NAME_INFORMATION)を取得する


これで名前があるオブジェクトに関しては名前が取得できる。ただしDuplicateHandleはすべてのオブジェクトハンドルを複製できるわけではないことと、システムプロセスが持つ一部のハンドルに対してNtQueryObjectを実行するとAPIのバグによりスレッドがデッドロックすることに注意する。

技術メモ

wsprintfやwprintfでも書式指定子 %wZ を受け付けるようだ。DbgPrint だけかと思っていた。 ユーザーモードではUNICODE_STRINGの出番はほとんどないのだけど。

12/29 追記

書式指定子 %wZ 等にまつわるバグについては、別の記事を参照のこと。
あと、NtQuerySystemInformationやNtQueryObjectのバッファにNULLとsize 0を与えると、WindowsのバージョンによってはReturnLength(必要なバッファサイズ)に正しい値が設定されないので、これも対応した。
機能追加として、コマンドラインに0を渡すとすべてのハンドルについてNtQueryObjectを実行する(そしてデッドロックする)ようにした。

*1:正直、ユーザーモードだけでできるとは思ってなかった。このフレーズも一度書いてみたかった。特に反省はしてない。