更新:MicrosoftはまだWindows8.1でそれを修正していません。
編集:これはWOW64のバグであることが判明しました-スレッドがロングモードのリング3(ユーザーモード)で中断されると、GetThreadContext()が古いコンテンツを返す場合があります。変換を実行するためにring-2を使用することをMicrosoftに提案しました。その場合、SuspendThreadはring-3のスレッドのみを一時停止し(現在のように-変更は必要ありません)、ring-2のクラッシュ/障害/エクスプロイトはカーネルに影響を与えません-ring-2とring-にのみ影響します3.3。
このような変更には、Wow64Get / SetThreadContextなどのいくつかのWinAPI関数の変更が必要になります。これにより、文書化されていない機能に依存するアプリが破損しますが、これは予想されることです。確かに、リング3からリング2に移行するのに数CPUサイクルかかるため、変換は遅くなります(CPUファミリによって異なります)が、正しい動作を保証するためには、OSの役割が何よりも重要だと思います。翻訳はすでにWOW64で実行されているアプリにオーバーヘッドを追加しているので、それも予想されます。
Microsoftがこの問題を修正することを願っています-そうでなければ、デバッガー/モノラルアプリ/ Boehm GC / WOW64でGetThreadContext()に依存するアプリは機能しません(初心者の場合、デバッガーが古いスタックトレースを表示するのを見ました)。
EDIT2:悪いニュース。MSFT(ここ)のAlexeyとの会話から、修正によって文書化されていない機能に依存するアプリが破損することを恐れて、まったく修正されない可能性があるように見えます。
元の質問
- 次のことについて混乱している人もいるようです。私は当初、カーネルモードのコードでSuspendThreadがスレッドを一時停止したことが原因だと思っていました。そうではありませんでした。以下は、実際の根本原因とは何の関係もないことが判明した私の最初の疑惑でした。これは、によって返された古いコンテンツでした
GetThreadContext()
。
MSDNから:
Suspending a thread causes the thread to stop executing user-mode (application) code.
しかし、私が見つけたのは、WOW64で実行されているWindows 7の32ビットアプリで、スレッドBでSuspendThreadを呼び出すスレッドAは、64ビットコードの実行中に一時停止できることです(ユーザーモードコードではないと思います)。EIPは、中断されたスレッドがで停止したことを示しています
wow64cpu!X86SwitchTo64BitMode:
00000000`759c31b0 ea27369c753300 jmp 0033:759C3627
ESPが変更された状態(ESPがそのスレッドのスタックと同じページを指しているときに、現在のスタックポインターよりもはるかに高いアドレスを取得しているため、これはわかっています)。上記が戻る命令にブレークポイントを設定し、スレッドを再開すると、ESPがX86SwitchTo64BitMode呼び出し(正しいスタックポインター)の前の値に戻ることがわかりました。また、同じ関数にシングルステップインすると、シングルステップのどの時点でもその高いアドレスESP値を取得できないこともわかりました。実際、シングルステップの場合、ESP値はX86SwitchTo64BitMode呼び出しの前後で変更されることはありません。
また、(DWORD)-1をチェックして、SuspendThreadが成功することを確認しました。
これらすべてから、スレッドはカーネルモードのコードで中断されていると私は信じています。
非ユーザーモードコードの実行中にOSがスレッドを一時停止する原因は何でしょうか?どうすればそれを防ぐことができますか?これは基本的に、スレッドBの実際の現在のスタックポインターを取得することを妨げています。アプリがWOW64の外部(ネイティブx86 OS上)で実行される場合、そのような問題は存在しないことに注意してください。