Windows 10 April 2018 Updateをデバッグしてみた
2018/05/10追記
Windows 10 April 2018 Update (RS4)でマウスカーソルが画面端に飛ぶ不具合が修正されたかと思われましたが、完全には修正されていませんでした。私が使っているソフトだと、Dependency WalkerとKanjiTranslatorで不具合が発生します。comctl32.dllはバージョンが複数あり、6.10の方は修正されていましたが、5.82の方は修正されていないようです。この記事は公開当初5.82の検証しかしていませんでしたが、6.10の検証結果も踏まえて記事を修正しました。
昨年のWindows 10 Fall Creators Update (RS3)ではマウスカーソルが画面端に飛んでいく不具合がありました。
https://silight.hatenablog.jp/entry/win10fixPatch
今回のWindows 10 April 2018 Update (RS4)で、その不具合が発生しなくなったのですが、本当に修正されたのか念のためcomctl32.dllをOllyDbgで逆アセンブルして確認してみました。
まずはRS3の不具合の箇所から見てみます。(32bit版で検証しています。)
RS3 comctl32.dll 5.82
RS3 comctl32.dll 6.10
重要なのはPeekMessageWのところです。
PeekMessageWはPeekMessageのUnicode版で、ウィンドウメッセージを取得する関数です。この関数が成功すると引数のpMsgにMSG構造体が書き込まれます。その後、PeekMessageの戻り値がEAXに格納されます。
次にTEST EAX, EAXでEAX同士のAND演算(EAX & EAX)を計算しています。この結果が0になるのはEAXが0の時だけです。
その直後にJNZで先ほどのAND演算の結果が0でなかったときにジャンプしています(JNZ = Jump if Not Zero)。結果的にPeekMessageの戻り値が0の時だけジャンプせずに次の命令に行きます。
その後、PtInRectなどが実行された後にSetCursorPosが呼ばれます。
SetCursorPosはマウスカーソルを引数で指定した座標に移動させる関数です。SetCursorPosの引数に渡されているのは先ほどのPeekMessageで取得したMSG構造体のpt.xとpt.yになります。
MSG構造体
https://msdn.microsoft.com/ja-jp/library/windows/apps/xaml/900ks98t(v=vs.120).aspx
これらの処理をC言語のコードにするとこうなります。
MSG msg;
if (!PeekMessageW(&msg, ...))
{
// PtInRectなど
...
SetCursorPos(msg.pt.x, msg.pt.y);
}
さて、ここでPeekMessageのリファレンスを読むとこう書いてあります。
メッセージを取得した場合、0 以外の値が返ります。
メッセージを取得しなかった場合、0 が返ります。PeekMessage
https://msdn.microsoft.com/ja-jp/library/cc410948.aspx
ということは、PeekMessageでメッセージを取得できなかった場合に未初期化のMSG構造体を参照し、SetCursorPosに未初期化の座標を引数として渡して実行します。逆にPeekMessageでメッセージを取得できた場合は何もしません。
マウスカーソルを未初期化の座標に移動していたために画面端に飛んでいったわけです。
さて、RS3にバグがあることが確認できたところで、RS4の方も見てみましょう。
RS4 comctl32.dll 5.82
RS4 comctl32.dll 6.10
5.82の方はアドレスが変わっているだけでロジックは変わっていません。6.10の方はGetCursorPosが追加されたことで未初期化のpt.xとpt.yを参照することはなくなりました。
よって、Windows 10 April 2018 Update (RS4)でcomctl32.dllの不具合が6.10だけ修正され、5.82は放置されました。
6.10が修正されたにもかかわらず5.82が修正されなかったのは不自然ですね。もしかしたらMicrosoftが意図的にサポート対象外としているのかもしれません。