「ポンピング」notepad.exe

画像



F5キーとどのような関連がありますか? ブラウザでページを更新しますか? あるディレクトリから別のディレクトリにファイルをコピーしますか? Visual Studioからアプリケーションを実行していますか? しかし、notepad.exeの作成者は、元々この質問にアプローチしていました。F5キーを押すと、現在の日付と時刻が、カーソルがこの時点で指している場所に追加されます。 notepad.exeに、現在のファイルの内容を再読み込みするなど、ほとんどのテキストエディターにとって非常に自然な機能があり、F5 / Ctrl-Rまたは他の一般的に受け入れられている機能がある場合、すべてがクールになりますホットキー。



Microsoftがそれを実装するまで待つことができ、別のテキストエディターを選択する(結局、これが標準のnotepad.exeの機能に対する唯一の制限ではない)または...逆アセンブラー、デバッガー、およびPEファイルエディターを手にします。



プロセスがどのように進み、どのようになったのか、カットの下で読んでください(慎重に、 多くのスクリーンショット )。 この記事を読む前に、以前の記事をよく理解することを強くお勧めします。



前の記事で遭遇したのと同じ不便さを処理しないために、最初にASLRの使用をオフにしましょう。 Wikiによると、ASLR(アドレス空間レイアウトランダム化)は、使用するとプロセスアドレス空間の重要な構造の場所、つまり実行可能ファイルのイメージ、ロードされたライブラリ、ヒープ、スタックをランダムに変更する技術です。 彼女がアプリケーションを最後に再起動したときに、以前に見つけたアドレスが変更されたためです。 Windows XPまたは古いOSを使用している場合、ASLRは当時存在しなかったため、次のいくつかの段落で説明する内容を簡単にスキップできます。



ASLRの無効化は、グローバルに(このために、レジストリに「HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Session Manager \ Memory Management」に格納されている「MoveImages」オプションの値を追加/編集して、ゼロに等しくする必要があります) 、つまり 特定の実行可能ファイル用。 後者のオプションは、特に仮想マシンではなく、実際のシステムである場合に、より魅力的に見えるので、止めましょう。



notepad.exeを「%WINDIR%\ System32」以外のディレクトリにコピーし、 PE Toolsをダウンロード、解凍して実行し、Alt-1を押して、以前にコピーしたnotepad.exeを選択します。



画像



[オプションのヘッダー]ボタンをクリックして、DLLフラグフィールドを確認します。このフィールドは、0x8140です。



画像



このフィールドの値は、 公式の MSDN ドキュメントにリストされている定数のビット単位の「OR」演算の結果です。 バイナリには次の特性があることに気付くのは簡単です。



IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE

0x8000

イメージはターミナルサーバーに対応しています



IMAGE_DLLCHARACTERISTICS_NX_COMPAT

0x0100

イメージはデータ実行防止(DEP)と互換性があります



IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE

0x0040

DLLはロード時に再配置できます


最後の意味に注意してください? まあ、これはまさに私たちが興味を持っているものです。 0x8140を0x8100に変更し、両方のウィンドウで[OK]をクリックして、デバッグを続行します。



notepad.exeパッチを条件付きでどのステップに分割できますか?





OllyDbgでnotepad.exeを開き、最初の手順に進みます。



現在のファイルへのパスが保存されているアドレスの検索にいくつかの側面からアプローチできます。 たとえば、ファイルを開く手順を見つけることができます(成功した場合、ファイルへのパスを特定のアドレスに保存します)、またはファイルを保存するアルゴリズムの実装を確認できます(明らかに、現在のファイルのハンドルを知っている必要があります、またはそれへの道)。 2番目のオプションについて説明します。



ファイルが保存されるたびにファイルが再度開かれることを期待して、WinAPI関数CreateFileWの呼び出しにブレークを設定します。



画像



Ctrl-Sを押し、ファイル名(私の場合は「C:\ helper.txt」)を選択し、次の場所で停止します。



画像



彼らが私たちを呼んだ場所と引数を見てみましょう:



画像



2番目の引数として渡されたアドレスが指すものを見ると(この引数のある行を右クリック->スタック内のアドレスに従う)、パスだけが表示されます。



画像



このアドレスがどこにどのように届いたかを理解するために、調査する手順の前にコードを見てみましょう。



画像



ご覧のとおり、ファイルへのパスが保存されているアドレスはEBP-8に含まれています。 もう一度Ctrl-Sを押して、今度はどこに到達するかを確認しましょう(プログラムは既にファイルへのパスを知っているため、アプリケーションの進行状況を変更できます)。



画像



そのため、以前と同じ休憩で終わりましたが、別の場所から呼び出されました。



画像



今回は、ファイルへのパスを含むアドレスがEBXレジスタに保存されます。 現在のcaseブロックの開始(割り当てられたスペースの前にいくつかの命令があるコメントに注意してください)から、このレジスタの値は変更されません。つまり、元のアドレスをどこかで探す必要があります。 このケースブロックの先頭を参照する指示を確認します( 0x01004D5Dで左クリック-> Ctrl-R)。



画像



そのようなアピールが1つだけになったら、Enterキーを押してジャンプし、 EBXでこのアドレスが表示される場所をすぐに確認します。



画像



そのため、現在のファイルへのパスはアドレス0x0100CAE0に格納されていることがわかりました 。 次は? そして、ファイルの内容を読み取るための手順を見つける必要があります。



もちろん、 CreateFileWも呼び出します(代わりに、 GetOpenFileName関数の呼び出しをインターセプトできますが、モジュール間呼び出しのリストには含まれていません-明らかに、MSDNで推奨されているCommon Item Dialog APIが代わりに使用されます)。 Ctrl-Oを押して任意のファイルを選択し(同じファイルを選択しました)、マウスをダブルクリックする時間がなくても、アドレス0x01006E8Cの亀裂を見つけます:



画像



このジャンクを取り除き、残りを期待する前に、同じことを数回行います。 実際、以前に指定したアドレスでブレークが削除された後も、目的のファイルをダブルクリックすることができました。その結果、ブレークポイントは完全に別の場所で既に機能していました。



画像



そのため、目的のファイルを正常に再読み込みするために、どのプロシージャをどのように呼び出す必要があるかを見つけることがタスクです。 呼び出し元のアドレスにブレークを入れます



画像



、F9キーを押すと...すぐに動作します! 何も、もう一度F9キーを押して、notepad.exeウィンドウにフォーカスを移動して、ブレーカーが再び機能することを確認してください。 なんてこった! このCALLが呼び出す手順の始まりを見てみましょう。



画像



唯一のコメントに注意してください-処理された値の数と実際に観察したものによって判断すると、この手順は、フォーカスがnotepad.exeウィンドウに送信されるか、ファイルが開かれるかに関係なく、ユーザーが実行するアクションに反応します。 どうやら、Ctrl-Oを押した後、プログラムはCALLを実行せず、条件付きジャンプ操作を使用して対応するケースブロックに切り替えるだけです。 この内訳を削除して、もう一度ファイルを開いて、コードに呼び出しがあるCreateFileW呼び出しの場所にある、破損に最も近い命令を見つけましょう。 彼女はアドレス0x01004DF5の命令であることが判明しました。



画像



両方の呼び出しにブレークを設定し、同じアクションを実行して、ここで自分自身を見つけます。



画像



このケースの最初にブレークを置き、再び同じファイルを開いて、ここで何が起こっているのかを理解しようとします。



;     EDI 01003ECC > \33FF XOR EDI,EDI ; Case 2 of switch 01001824 ;        ;   ,          01003ECE . 57 PUSH EDI 01003ECF . E8 90D7FFFF CALL notepad.01001664 ;    ; EAX == 1,     /    Save / Don't Save, EAX == 0,     Cancel 01003ED4 . 85C0 TEST EAX,EAX ;   Cancel,      ,    case 01003ED6 .^ 0F84 8ED9FFFF JE notepad.0100186A ;     0x100C00C  EAX    EBP-10 01003EDC . A1 0CC00001 MOV EAX,DWORD PTR DS:[100C00C] 01003EE1 . 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX ;          01003EE4 . 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8] 01003EE7 . 50 PUSH EAX ; /Arg2 01003EE8 . FF75 F4 PUSH DWORD PTR SS:[EBP-C] ; |Arg1 01003EEB . E8 31000000 CALL notepad.01003F21 ; \notepad.01003F21 ;       EBP-8       ; EAX == 0     0x800704C7     Cancel 01003EF0 . 8BF0 MOV ESI,EAX 01003EF2 . 3BF7 CMP ESI,EDI ;        01003EF4 . 0F8D FB0E0000 JGE notepad.01004DF5 01003EFA . 81FE C7040780 CMP ESI,800704C7 01003F00 . 0F85 DC0E0000 JNZ notepad.01004DE2 01003F06 > 3BF7 CMP ESI,EDI 01003F08 . 0F8D E70E0000 JGE notepad.01004DF5 01003F0E > 8B45 F0 MOV EAX,DWORD PTR SS:[EBP-10] 01003F11 . A3 0CC00001 MOV DWORD PTR DS:[100C00C],EAX 01003F16 . 56 PUSH ESI 01003F17 .^ E9 A2FCFFFF JMP notepad.01003BBE
      
      





ここで、正しい動作に必要な「環境」の種類を理解するために、 0x01004DF5のアドレスでコードを登録およびアドレス指定するものを見てみましょう。



画像



もちろん、このコードはEBP-8を参照しています。これは、思い出すと、開くファイルへのパスを格納しています。 さらに、 hTemplateFileおよびpSecurityパラメーターの引数として使用されるEDIレジスタの値も重要です。 最初はアドレス0x0100CAE0から取得でき、指定されたパラメーターにゼロを渡すだけです。



ここで、F5キーの押下を処理するコードを見つけましょう。 これを行うには、現在の時刻を取得する機能の呼び出しを中断することを提案します。 最も人気のあるものはGetSystemTimeGetLocalTimeです。 最初は相互モジュラー呼び出しのリストにはありませんが、2番目は一度に2つの場所から呼び出されます。



画像



休憩を入れて、F5を押して、ここで見つけてください。



画像



現在のプロシージャコールの場所にジャンプして、別のケースブロックのほぼ最初に到達します。これは明らかに、F5プレスの処理を担当します。



画像



素晴らしい。 コードの洞窟の場所を探して記述します(もちろん、アドレスは異なる場合があります)。



 0100BEB3 33FF XOR EDI,EDI 0100BEB5 C745 F8 E0CA0>MOV DWORD PTR SS:[EBP-8],notepad.0100CAE0 ; UNICODE "C:\helper.txt" 0100BEBC A1 0CC00001 MOV EAX,DWORD PTR DS:[100C00C] 0100BEC1 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX 0100BEC4 ^ E9 2C8FFFFF JMP notepad.01004DF5
      
      





アドレス0x0100447Bのコード洞窟にジャンプを貼り付けます。



画像



F9を押し、もう一度F5を押して、次の図を確認します。



画像



ご覧のとおり、 CoTaskMemFree関数の腸のどこかに落ちました。 この関数に渡される引数に注意してください-はい、はい、これはファイルへのパスを持つ行のアドレスです。 これは、そのためのメモリをCoTaskMemAllocを使用して割り当てる必要があることを意味します。 SHStrDup関数はこれに役立ちます。これは、渡された文字列の複製を作成し、 CoTaskMemAllocを使用してメモリを割り当てます。



notepad.exeを再起動し、IATでSHStrDupW関数のアドレスを探します。 これを行うには、モジュール内の他のWinAPI関数の呼び出しを見てください。



画像



したがって、IATのGetDlgItemTextW関数のアドレスは0x010012A4です。 飛びついてSHStrDupWを探します。



画像



その呼び出しは、命令CALL DWORD PTR DS:[010013B4]の形式で発行で​​きることがわかりました 。 次に、次のコードを記述します(エラーチェックは省略されます)。



 0100BFA5 . 33FF XOR EDI,EDI 0100BFA7 . 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8] 0100BFAA . 50 PUSH EAX ; /pTarget 0100BFAB . 68 E0CA0001 PUSH notepad.0100CAE0 ; |Source = "C:\helper.txt" 0100BFB0 . FF15 B4130001 CALL DWORD PTR DS:[<&SHLWAPI.SHStrDupW>] ; \SHStrDupW 0100BFB6 . A1 0CC00001 MOV EAX,DWORD PTR DS:[100C00C] 0100BFBB . 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX 0100BFBE .^ E9 328EFFFF JMP notepad.01004DF5
      
      





ファイル「C:\ helper.txt」を開き、空であることを確認し、それを編集してnotepad.exeの別のコピーに保存し、デバッグしているバージョンでF5を押すと、ファイルが更新されます。



変更を実行可能ファイルに保存しましょう。 CPUウィンドウを右クリック->実行可能ファイルにコピー->すべての変更->すべてをコピーして以下を参照:



画像



実行可能ファイルの物理的な境界を超えてクロールしたことがわかりました。 PE Toolsのセクションの境界を見てみましょう(「セクション」ボタン)



画像



コードケーブを他の場所に配置します。 「痛みのない」パッチの領域の上部の「境界線」を取得するには、仮想オフセット.textセクションを追加する必要があります。ここには、パッチ、そのRawサイズおよびイメージベースを配置します。 仮想オフセット( 0x00001000 )+生サイズ( 0x0000A800 )+画像ベース( 0x01000000 )= 0x0100B800 たとえば、 0x0100B6CFに配置して、変更を再度保存します(CPUウィンドウを右クリック->実行可能ファイルにコピー->すべての変更->すべてをコピー->表示されるウィンドウを右クリック->ファイルを保存)。



結果の実行可能ファイルのパフォーマンスをチェックし、すべてが期待どおりに動作することを確認します。



あとがき



この記事の目的は、ソースコードを手元に置かずに、既存のプログラムに独自の機能を追加する可能性をもう一度実証することです。 ここで、vim / emacs / Notepad ++ /などに戻りますが、バグに遭遇したり、クローズドソースエディタの機能が不足していることに気付いた場合は、どうすればよいかがわかります。



ご清聴ありがとうございました。また、この記事が誰かに役立つことを願っています。



All Articles