辛抱強いメモ帳:13年間修正されていないバグ





2001年頃から始まるWindowsのすべてのバージョンの標準メモ帳には、ほとんどの人が知っているエラーがありますが、誰も修正するつもりはありません。 また、これは重大な脆弱性ではないため、誰のセキュリティを脅かすこともないため、理解できます。 誰もがノートブックを使用しますか?



それでも、事実自体はかなり奇妙なので、Windows 7の64ビットおよび32ビットのnotepad.exeのコードでこのエラーを見つけて修正し、最終的にその原因を見つけます。 エラーは次のとおりです。



ノートブックで「ワードラップ」オプションが有効になっている場合、ファイルを保存した後、すべての種類のグリッチが始まります。行が離れ始め、カーソルが飛び去り、予期しない場所にテキストが入力されます。



まず、何が起きているかをより正確に調べてみましょう。 長い行を含むテキストを開いたり、入力して、折り返します。 ファイルを保存します。 たとえば、「blue」という単語を追加するなど、編集しようとすると、行が誤って折り返され、フォーマットが崩れます。







メモ帳ウィンドウを縮小すると、ラインはカットされ(これはタイトル画像に表示されます)、引き伸ばされると、拡大ウィンドウを埋めることなく所定の位置に残ります。 保存時に終了した場所の各行に、ハードな「改行」が表示されるように。 どうやら、テキストは何らかの形でメモリ内で劣化します。







ここでファイルを再度保存すると、さらに悪化します。 すべての行が再フォーマットされますが、ウィンドウは更新されません。 したがって、カーソルは別の場所に移動でき、テキストの入力を開始すると、カーソルがある場所ではなく、まったく別の場所にテキストを入力していることがわかります。 メモ帳を書いたプログラマーは論理的に推論しました。ファイルを保存するとき、ウィンドウ内の何も変更すべきではないので、それを更新しても意味がありません。 しかし、このエラーを考慮すると、テキスト全体が変更されます。 Windowsの各ユーザーは状況を再現できます。これは、このエラーが存在しなかった最新バージョンがWindows'98であり、まだ誰もそれを持っている可能性が低いためです。



そのため、明らかに、ファイルを保存するときに何かがおかしくなり、テキストがおかしくなります。 コードでこの場所を見つける方法は? デバッガーでnotepad.exeを開きます。 ご存知のように、互換性のための64ビットシステムには、32ビットと64ビットの2つのノートブックがあり、それらを混同しないでください。



行を折り返すときにどのように劣化するかが簡単にわかるテキストを紹介します。 1行目に「最初のテキスト行2番目のテキスト行」と入力してから、ウィンドウを縮小して中央で切り取りましょう。







WriteFile関数を使用して記録が行われると想定するのは合理的です。 コードで6回も呼び出されていることがわかります。 考え直すことなく、6つの呼び出しすべてにブレークポイントを設定します。 ノートブックを起動し、「保存」をクリックします。 実行はここで停止します。







呼び出しパラメータが含まれるすべてのレジスタを見てみましょう。 rcxには104がありますが、それは明確ではありません。 rdx = 002D45E0、メモリ内のアドレスのように見えます。 何があるか見てみましょう。







素晴らしい。 ここから録音しています。 コードをさらに実行して、どこが悪いかを見てみましょう。 ただし、ほとんどすぐにデータが上書きされます。つまり、データは単なる一時バッファーであり、テキスト自体は別の場所に保存されます。 上記のプログラムを見てみましょう。







ええ、保存する前に、テキストは明らかにマルチバイトエンコーディングからシングルバイトに変換されます。 前回同様、パラメーターを見てみましょう。 rax = 002D45E0、ここまでゼロがあります。 これはまさに結果がどこに行くかです。 esi = 20、これはテキストの長さです。 exx = 4eZ、コメントなし。 edx = 400、同じこと。 そして、ここにr8 = 002D6780があります。







このメモリ領域の内容を観察しながら、再び実行を続けます。 数十のコマンドの後、サブルーチンを終了し、いくつかの遷移、呼び出しが行われますが、それに注意を払わずに、「ステップオーバー」に圧力をかけ続け、コードをステップで実行し、テキストのあるウィンドウのみを監視します。 そして、ある時点で変化します。 ご覧のとおり、コード1dと2dの間にコード0d、0d、0aが表示されています。







いつものように、私たちは目的のコマンドを滑らせ、ボタンを絶えず押していたので、あなたはそれがどこで起こったかを覚えて、すべてをもう一度繰り返す必要があります。 コードの適切な場所に近づくにつれて、この呼び出しでテキストが破損したことを正確に判断します。







この呼び出しを行わないと、何が起こるか試してみることができます。 この場所に再び行き、ここでデバッグする際に、RIP(現在実行されているコードのアドレスが格納されているレジスタ)を00000000FFA38EE1に変更します。この呼び出しを見逃したかのように、すべてが台無しになりました。 驚くべきことに、すべてが機能し、テキストは壊れません!



ここで、そのような場合、彼らは通常、それがどんな種類のサブルーチンで、何をし、何をするのかを理解せず、単にEXEファイルからそれを捨てるということを言わなければなりません。 これはさまざまな方法で行うことができます。たとえば、すべてのNOPをハンマーで叩くか、等号「je」の条件付き遷移を、その直前にある無条件の「jmp」に変更します。



しかし、今ではこのエラーを修正する必要はあまりありません。どこから来たのかを知るのは興味深いことです。 したがって、私たちは中に入って見てみましょう:







これは素晴らしいすばらしい小さなルーチンです。 手順を実行します。 最初に、いくつかの2つの変数がゼロと比較されます。その結果、最初の呼び出しでは何が行われていないかがわかりませんが、SendMessageを呼び出すために連続して行われます。 つまり、発生するすべてのメッセージが2種類のWindowsメッセージで送信され、最初のテキストの直後にテキストが無効になります(緑色で強調表示されます)。 コードがEDXに送信される(赤で強調表示されている)ことを肉眼で確認できます。 コード0C8hを探しましょう。



これは、EM_FMTLINESメッセージであることが判明しました。 文字列をフォーマットするためのメッセージを送信しているように見えるため、フォーマットされました。 ドキュメントを読む時間です。 MSDNは次のことを教えています。



このメッセージは、複数行編集コントロールにソフトラインフィードを含めるかどうかを決定します。 ソフトラインフィードは、2つの文字[CR]と1つの[LF]で構成され、ワー​​ドラップ中にカットされる行に挿入されます。



パラメータwParam:true-文字を挿入し、false-文字を削除します。



メッセージは、EM_GETHANDLEおよびWM_GETTEXTメッセージによって返されるバッファーにのみ影響し、編集コントロールに表示されるテキストには影響しません。 また、1つの[CR]と1つの[LF]で構成される「ハード」ラインフィードには影響しません。


さらに、このメッセージはWindows 95よりも早く入力されたことがわかります。それですべてです。 95年には影響を及ぼさないと想定されていましたが、今では影響があり、どのように影響するかがわかります。 コードを少し調べてみると、同様の課題がいくつか見つかりました。次の図が私たちの心の目で見られます。



昔、90年代前半に、MicrosoftプログラマーはWindows 95用のメモ帳を書きました。すばらしい行折り返し機能を実装するために、ウィンドウ(またはその要素)に送信して特殊文字を挿入して再フォーマットするメッセージを思いつきました。 これらの文字を通常のラインフィードと区別するために、シーケンス0d、0d、0aを考え出しました。 それがファイルに落ちないようにするために、保存する前にそのようなコードはすべて削除され、保存後に追加されました。



後に、Windows XPを作成したとき、要素は必要に応じてすべてを転送し始め、彼はこのメッセージを必要としなくなりました。 しかし、なぜそれが必要なのか誰も覚えていなかったので、万が一のために残しておくことにしました。 さらに、すべてが機能しているように見えましたが、保存後に問題に気づいた人はいませんでした。 それ以降、このコードは残っており、Windows 7および8の最新バージョンに到達しています。



エラーの修正に進みます。 メッセージ08hOB1hも送信された後、これはEM_SETSEL-割り当てを設定します。 このルーチン全体を投げることはまだ間違っているようで、最初に奇妙な呼び出しがあります。 したがって、最初のSendMessage呼び出しのみを削除するか、パラメーターを1から0に変更するか、別のアドレスへの遷移を変更して、変数[0FFA40054h]を確認した後、すぐに2番目の呼び出しに進むことをお勧めします。 多くのオプションがありますが、結果は同じになります。







ここで、パラメーターは1に等しいですか? すべてが非常にシンプルです-r8レジスタにあります。 コードを削減するために、コンパイラーはゼロをレジスターに直接転送することはありません。 このようなコマンドは、2バイトのオペコード、4バイト-32ビットのゼロのbバイトを取ります。 代わりに、XORレジスタはそれ自体でゼロになり、3バイトしかかかりません。 その後、ゼロであるr9は、1を追加してr8に送信されます(緑色で強調表示されます)。 この操作にも4バイトしかかかりません。 この緑の1は0に変更する必要があり、テキストは損なわれません。



そして今、32ビット版のノートブックでも同じ手順を見つけています。 デバッグですべての同じ操作を繰り返したくない場合は、番号0C8hを検索するだけで見つけることができます。







ご覧のとおり、完全に同様のコードで、32ビットのみです。 さて、エラーを修正するために、exe-shnikでこの場所を見つけ、目的のバイトを変更するだけです。 その前に、ファイルの所有者になることを忘れずに、自分にファイルを変更する権利を与えてください。



64ビットnotepad.exe(193536バイト)は、アドレス[80FC]のバイトを1から0に変更します

32ビットnotepad.exe(179712バイト)アドレス[6FC8]のバイトを1から0に変更



マイクロソフトのコードのどこかに、古代のバグが眠る場所がまだたくさんあることは間違いありません。 これらがすべてこれほど無害であり、世界中のユーザーが喜んでインストールする次のオペレーティングシステムに移行しても、悪いことは何もないことが期待できます。



All Articles