DLL「地獄」の悩みから脱してプロセス情報を取得

6276 ワード

要約本稿では、システムで実行されているプロセス情報、例えば、プロセスリスト、リスト内のプロセスを列挙し、プロセスに関する詳細を取得するために、さまざまな方法について説明します.
実行プロセスのリストを取得するには、Win 32実行プロセスの情報を取得する3つの方法があります.表1を参照してください.
(表一)
方法
プラットフォーム
コメント
PSAPI
Windows NT,Windows2000,Windows XP
プロセス、ドライブ、モジュール、メモリ、ワークセット情報の取得
パフォーマンスカウンタ
Windows NT,Windows2000,Windows XP
プロセス・リスト以外のプロセスに関する詳細情報を提供し、リモート・マシンで使用できます.
TOOLHELP32
Windows 9x,Windows2000,Windows XP
プロセス、スレッド、モジュール、スタック情報の取得
本明細書では、MSDNにおいてTOOLHELP 32関数を使用する多くの例示的なコードが提供されているため、TOOLHELP 32を議論するつもりはない.パフォーマンスカウンタは、プロセスリストだけでなく、より多くの情報を提供します.リモートマシンの情報を取得したい場合は、パフォーマンスカウンタは最高のツールです.いつも別のマシンのプロセスリスト情報を入手したい場合は、パフォーマンスカウンタを使いましょう.プロセス状態API(PSAPIのフルネームはProcess Status API)はマイクロソフトSDKにおいて有用なツールであり、サンプルプログラムにはファイルProcessを実現するクラスCProcess Listがある.cppはPSAPIをパッケージ化し,このクラスでプロセスリストを取得できる.Refreshが呼び出されると、あるプロセスのIDによってプロセスの記述情報が得られ、GetFirstとGetNextで他のプロセスを簡単に列挙することができる.
   //  CProcessList        
   //          
   CProcess* pProcess = NULL;
   POSITION  Pos = 0;
   for (
         pProcess = ProcessList.GetFirst(Pos); 
         (pProcess != NULL); 
         pProcess = ProcessList.GetNext(Pos)
       )
   {
      if (pProcess != NULL)
      {
      //          
      }
   }		
Refreshの実装では、EnumProcesses関数(PSAPI)が使用されます.
//       
void CProcessList::Refresh()
{
//                 
   DefaultReset();

//         
   DWORD aProcesses[MAX_PROCESS];
   DWORD cbNeeded = 0;

//       
   if (!g_PSAPI.EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
      return;
    
//           IDs
   DWORD cProcesses = cbNeeded / sizeof(DWORD);

//  CProcess           ID
   DWORD     dwProcessID;
   CProcess* pProcess;
   for (
         DWORD dwCurrentProcess = 0; 
         dwCurrentProcess < cProcesses; 
         dwCurrentProcess++
       )
   {
      dwProcessID = aProcesses[dwCurrentProcess];

   //           
      pProcess = new CProcess(TRUE);
      if (pProcess != NULL)
      {
      //       ID     --  
         if (!pProcess->AttachProcess(aProcesses[dwCurrentProcess]))
            delete pProcess;
         else
         //       
            m_ProcessMap[(LPVOID)dwProcessID] = pProcess;
      }
   }

//                   
   SetChildrenList();
}		
今でもWindowsの16ビットコードをサポートしている場合は、タスクマネージャのntvdmなどです.exe項の下に示すアプリケーションがこの列に属する場合、列挙プロセスはさらに困難です.詳細については、このリファレンスのKnowledge Base記事、およびMatt Pietrekのコラム記事:August 1998およびSeptember 1998を参照してください.
プロセス情報の取得方法
実行プロセスのリストがあります.次に、EnumProcessesが返すプロセスIDsに基づいて、各プロセスの詳細をできるだけ多く取得し、これらの情報に基づいて有用なツールを構築します.PROCESSでQUERY_INFORMATION | PROCESS_VM_READはパラメータとしてOpenProcessを呼び出してプロセスハンドルを取得し、AttachProcess(Process.cppファイルのCProcessクラス実装を参照)メソッドでプロセス記述を作成する.表2には、プロセスの詳細情報を取得するためのCProcessの関数を示します.
(表2)
方法
説明
GetName
NULLをパラメータとしてGetModuleBaseNameを呼び出し、最後に拡張子「.EXE」を削除します.
GetFileName
NULLをパラメータとしてGetModuleFileName Exを呼び出す
GetMainWindowHandle
GetMainWindowHandleを参照
GetMainWindowTitle
GetParentProcessID
パラメータとしてProcessBasicInformationを使用してNtQueryInformationProcessを呼び出す
GetKERNELHandleCount
パラメータとしてProcessHandleCountを使用してNtQueryInformationProcessを呼び出す
GetUSERHandleCount
GRでUSEROBJECTSパラメータとしてGetGuiResourcesを呼び出す
GetGDIHandleCount
GRでGDIOBJECTSパラメータとしてGetGuiResourcesを呼び出す
GetWorkingSet
GetProcessMemoryInfoの呼び出し
GetCmdLine
GetProcessCmdLineを参照
GetOwner
GetProcessOwnerの詳細を参照
GetSessionID
ProcessIdToSessionId(Windows XPの新しい機能を参照)
GetModuleList
CModuleListは、EnumProcessModulesとGetModuleFileNameExのパッケージクラスです.
GetChildrenCountおよびサブプロセスリスト
あるプロセスのサブプロセスリストを取得するには、このようなAPIはまだ使用されていません(存在しても公開されていません).ただし、プロセスの親プロセスは既知であるため、親プロセスのサブプロセスリストにプロセスを追加するのは難しくありません(SetChildrenListの実装を参照).
ここではAttachProcessの詳細についていくつか説明する必要があります.まず、PSAPIやNTDLLのようなオペレーティングシステム特有のDLsとの静的リンクを避けるために、これらのDLsから出力関数をパッケージ化する必要があるクラスを記述することは値の得である--詳細は例コードのWrappersを参照することができる.hとWrapperscppファイル.これでは、CPSAPIWrapperオブジェクトを定義し、そのGetModuleFileName Exメソッドを呼び出すだけで、PSAPIライブラリにリンクする必要はありません.さらに、IsValidメソッドを呼び出して、これらのDLsがシステムを実行するときに使用可能であることを確認する必要があります.これにより、ある関数が定義されていないなどのリンクエラーを発生させることなく、任意のWindowsプラットフォームでコードを実行できます.注意特定のプロパティを使用する前に、WindowsのバージョンまたはIsValidの戻り結果を確認する必要があります(DllSpyインスタンスコードのDllSpyApp::InitInstanceセクションを参照).PSAPIのGetModuleFileName関数が返すファイル名は、「/system Root/System 32/smss.exe」や「/?/C:/WINNT/system 32/winlogon.exe」など、奇妙です.どういう意味か誰が知っていますか.ヘルパーでcppにはTranslateFilenameという関数があり、これらのファイル名をより分かりやすい名前に変換します.後でこの関数についてもお話しします.次に、プロセスのメインウィンドウを探す方法について説明します.EnumWindowsにはコールバック関数というパラメータがあります.このコールバック関数の役割は、最上位のウィンドウハンドルを受信することです.このコールバック関数では、GetWindowThreadProcessIdを呼び出して、対応するウィンドウを作成するプロセスIDを取得します.このウィンドウ(表示されている)が見つかったら列挙を停止します(詳細はGetMainWindowインプリメンテーションを参照).関数GetWindowTextは、異なるプロセスのウィンドウタイトルを取得するために使用できます.Windows NTおよびWindows 2000では、特定のウィンドウを作成するプロセスに対応するファイル名を取得するために、以前のようにGetWindowModuleFileName関数を使用することはできません.この関数は、現在実行されているプロセスのパス名を常に返します.プロセスメインウィンドウの詳細なプロセス記述を取得するには、MSJ Aug 99上のJeff ProsiseのWicked Codeコラムを参照してください.既知のプロセスIDを使用して、PSAPI関数を呼び出してフルパス名を取得する方法がわかりました.次に、このパス名を使用してGetWindowThreadProcessID関数を呼び出し、特定のウィンドウを作成するプロセスファイル名を取得します.ほとんどのプロセス情報を取得するためにAttachProcessでOpenProcessを呼び出す必要がありますが、アクセスを拒否するエラーが発生する可能性があります.このような場合、私はKeith Brownが与えた方法を使用して、MSJ Aug 99の「Security Briefs」コラムを参照し、プロセスハンドルを高レベルの権限で取得する方法について詳しく説明しました.詳細は例コードのHelpersを参照してください.GetProcessHandleWithEnoughRightsという関数があるcppファイルは、Keith Brownの手によるものです.あるプロセスが別のユーザーアカウント計画タスクとして実行されると、上記のアクセス拒否の問題が発生します.Windowsタスクマネージャでもこのようなプロセスを終了することはできません.次のようなダイアログボックスしか表示されません.図9:図9で終了できないプロセスプロセスプロセスプロセスプロセスプロセスプロセスプロセスプロセスプロセスのいずれかをダブルクリックすると、このプロセスが終了します.例コードのSlayProcess関数を見てみましょう.この補助関数は、プロセスハンドルを取得するためにGetProcessHandleWithEnoughRightsを呼び出しますが、アクセス権パラメータはPROCESS_です.AttachProcessで使われているPROCESS_ではなくTERMINATEQUERY_INFORMATION | PROCESS_VM_READ.最後にGetProcessOwnerでプロセス実行のユーザーアカウント(フォーマットは//Domain/User)を取得し、TokenUserをパラメータとしてGetTokenInformationを呼び出し、戻ってきたユーザーSIDをLookupAccountSidで読み取り可能なドメイン名とユーザー名に変換します.OpenProcessTokenは、Systemのようなプロセスに遭遇すると呼び出しに失敗することがある、Windows 2000のリソース開発パッケージのPULISTでさえある.EXEでは、プロセスを所有するユーザーを表示できません.ProcessExplorer(Sysinterals社が開発したツールソフトウェア)だけがこの「安全な」アプリケーションの所有者を見つけることができます.この文書では、Windows XPで、プロセスのホストを取得するためにWTS APIs(すなわち、Windows Terminal Services API-WindowsターミナルサービスAPI)を使用する方法について説明します.