WinAPI関数IsWow64Process()の予期しない動作

このメモは、WinAPI関数IsWow64Process()の名前をいつかgoogleして、MSDNで説明されているように動作しない場合がある理由を理解しようとする人向けに書かれています。 私は1〜2年で自分自身になる可能性があります。 しかし、それは他の誰かに役立つかもしれません。



それで、それは何についてですか? Windowsオペレーティングシステムは、32ビットまたは64ビットであることが知られています。 32ビットWindowsでは、実行できるのは32ビットアプリケーションのみです。つまり、「これは32ビットアプリケーションですか、それとも64ビットですか?」という質問には意味がありません。答えは事前にわかっています。 64ビットバージョンのWindowsでの生活はもう少し楽しいです-ここでは、64ビットアプリケーション(ネイティブと見なされます)とOSネイティブではない32ビットの両方を実行でき、特別なサブシステムWoW64 (Windows-on-Windows)で実行されます64ビット) 。 このサブシステムには、32ビットコード、個別のレジストリブランチ、および64ビット環境で32ビットアプリケーションを操作するためのシステムフォルダーを起動するための手段が含まれています。



64ビットWindowsで実行されている特定のプロセスが実際にネイティブの64ビットプロセスであるか、WoW64プロセス(つまり、WoW64サブシステムで実行されている32ビットアプリケーション)であるかを知ることが重要な場合があります。 これらの目的のために、MicrosoftはIsWow64Process()関数の使用を推奨しています。 MSDNの説明は非常に詳細であり、MSDNの呼び出し方法に関する警告がいくつかありますが、一般的にはすべてが簡単です。 サンプルコードもあります。 唯一の問題は、場合によってはこの関数が嘘をつき、プロセスのアーキテクチャを誤って決定することです。





プロセスのPIDをユーザーに尋ね、そのアーキテクチャを決定するテストアプリケーションを作成しましょう。 基礎として、MSDNのコードを取り上げましょう。 エラー処理を追加します-つまり 入力にはプロセスのPIDがあり、出力には「決定できなかった」、「これは64ビットプロセス」、「これはWoW64プロセス」の3つのオプションのいずれかがあります。



#include <windows.h> #include <iostream> bool IsWow64(DWORD pid, BOOL &isWow64) { HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); if (hProcess == NULL) return false; typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process"); bool res = fnIsWow64Process != NULL && fnIsWow64Process(hProcess, &isWow64); CloseHandle(hProcess); return res; } int main(void) { for (;;) { std::cout << "Please enter PID: "; DWORD pid; std::cin >> pid; BOOL isWow64 = false; BOOL resultKnown = IsWow64(pid, isWow64); if (resultKnown == false) std::cout << "Process type is unknown"; else std::cout << "Process type is " << (isWow64 ? "x86 (wow64)" : "x64"); std::cout << std::endl << std::endl; } return 0; }
      
      





プログラムを実行し、その隣でタスクマネージャー(または私が気に入っているプロセスハッカー)を開いて、プロセスのアーキテクチャとそのPIDを確認します。 プログラムをテストします。







一見、すべてが問題ありません。存在しないPIDは検出されず、32ビットおよび64ビットのアプリケーションが正しく検出されました。



続けましょう。 Chromeを起動します。 起動されると、ページコンテンツのレンダリング、処理などを行う多数の子プロセスを開始します。 これらのプロセスのいずれかのビット数を判断してみましょう。







すべてが問題ありません。これは32ビットアプリケーションです。



そして今、私たちは耳でこのようなフェイントを行っています:テストアプリケーションに同じPIDを入力し、Process HackerでこのPIDを使用してChromeの子プロセスを強制終了し、テストアプリケーションにすばやく戻り、Enterを押します。 そして、私たちは美しい写真を見ます:







終了したばかりのプロセスは、「見つかりません」とは定義されていません(上記の例のPID 999999など)。 また、32ビットとして定義されていません(それは人生の間にあったものです)。 システムに存在する64ビットプロセスとして明確かつ明確に定義されています。 しかし、どのように!? なんで?



そして、ここに理由があります。



特定のプロセスを強制終了すると、そのプロセスはすぐには終了しません。 そのスレッドは停止し、占有されていたメモリは解放されますが、プロセスが完全に終了するかどうかは、スレッドに依存するのではなく、他のプロセスにこのプロセスのオープン記述子(HANDLE)があるかどうかに依存します。 おそらく、誰かが何らかの形で彼とやり取りしたかったのかもしれません。 たとえば、ウイルス対策、ウイルス、Process Hackerなどのシステムユーティリティ、親プロセスなどです。それらのいずれかがプロセスにオープン記述子を残している場合、「ゾンビ」状態になり、その状態になります。今のところ、何かが彼をこの世の世界に留め続けるでしょう。 この状態では、スレッドでコードを実行しなくなりますが、オペレーティングシステムのエンティティとして存在します。たとえば、「存続期間」PIDを占有し、「ゾンビ」が完全に死ぬまでプロセスは同じPIDを取得できません。 。 ここで、Chrome子プロセスの例を提案した理由を既に推測できます。親Chromeプロセスは子プロセスへのハンドルを保持し、これが「ゾンビ」プロセスでの直接パスです。



問題に戻りましょう-なぜIsWow64Process()関数がこのプロセスのアーキテクチャを誤って定義しているのですか? そして、ここではすべてが非常に単純です-プロセスが「ゾンビ」状態になると、WoW64サブシステムは停止し、その中でアンロードされます。 彼女はもう必要ありません(「ゾンビ」を再び蘇らせるオプションはありません)-なぜリソースを借りるのですか? その結果、特定のプロセスのアーキテクチャに間違ったタイミングで興味を持ち、間違った結果を得ることができます。



ちなみに、Chromeは高品質の製品であり、子プロセスの死の事実をすばやく判断し、ハンドルを解放して(「ゾンビ」に安心して休む機会を与えます)、このタイプのプロセスを再作成します。 その結果、数秒後に同じPIDに対して同じ関数を呼び出すと、一般的に次の図が表示されます。







これに対処する方法は?



はい、非常に簡単です-IsWow64Process()の呼び出しに加えて、 GetExitCodeProcess()関数も呼び出す必要があります。GetExitCodeProcess()関数は、まだ生きているプロセス(「ゾンビ」ではない)に対して常にSTILL_ACTIVEを返します。 これに基づいて、あなたはあなたの前の「ゾンビ」を理解することができます。IsWow64Process()の結果を信じる価値はあります。 もちろん、ここでは、「ゾンビ」であることがわかったときに何をすべきかという問題が発生します。つまり、そのアーキテクチャは定義上不明です。 これに対する唯一の答えは質問かもしれませんが、あなたは「ゾンビ」で何をしようとしていますか。 ほとんどの場合、反対のタスクに直面します。「ライブ」プロセスを見つけ、その情報を取得するには、GetExitCodeProcess()+ IsWow64Process()の組み合わせが正常に機能します。



IsWow64Process()関数について伝えたかったのはそれだけです。



All Articles