リバースエンジニアリングのダーティソリューション

画像



開発者は、多くの場合、すべてを正しく行い、問題の解決に多くの時間を費やすか、実際にこれがどのように発生したかについて詳しく説明することなく動作させるという選択肢を持っています。 顧客側からは、もちろん、黄金の平均が最も魅力的です。この場合、完了したタスクをプログラマが十分に理解することと、できるだけ少ない工数を費やすことです。 開発者にとってもそう簡単ではありません-一方では、あなた自身のコードで何が起こっているかを理解することは当然のことです(特に、この製品のサポートが肩にかかっている場合)、そして他方では、アプリケーションの結果がビジュアル形式(グラフィック/サウンドまたはビデオフラグメントなど)、1回限りの開発、およびテスト部門はすべてがうまくいっていると言っています。残りの作業時間Habr、彼の最愛の人に時間を捧げてみませんか?



ポイントに到達します。 以前の記事の1つで、 「Talker」というプログラムについて既に言及しました。 名前にもかかわらず、それ自体は何も言わず、ユーザーと音声エンジンの間の接続リンクにすぎず、より便利なインターフェイスと設定を提供します。 狭い円で最も人気のあるエンジンの1つは、「Digalo 2000テキスト読み上げエンジン」(以下「Digalo」と呼びます)です。このリンクは、TalkersのWebサイトで見つけることができます。 おそらく私の以前の記事のトピックから既に推測したように、すべてが彼にとってそれほど良いわけではなく、いくつかのバグもありました。 今回、「aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa」というテキストを発声すると、問題が明らかになりました。 少し実験してみたところ、ある量の「理解できない」文字に達すると、Digaloがクラッシュし始め、そのプロセスをデバッグできるようになりました。 まあ、なぜですか?



プロセスの経緯とその結果を読んでください、カットの下で読んでください(この記事を読む前に、以前のプロセスをよく理解することを強くお勧めします。



ソースコードがDigaloに付属していないと言う必要はないと思います(さらに、公式ウェブサイトから興味のあるバージョンのバイナリをダウンロードすることさえできません)。したがって、私たちの親友は、実際には逆アセンブラ、デバッガ、および16進エディタになります。 OllyDbgのみに減らすことができます。 しかし、分解されたリストの調査に着手する前に、はい、アプリケーションをダウンロードし、トレッドで覆われていて、どんな種類のパッカーでも梱包されていないかどうかを確認する必要があります。



Digaloをダウンロードしてインストールし、インストールしたディレクトリに移動して、 DiEおよびPEiDの実行可能ファイルを調べます。



画像



画像



DIGALO_RUS.exeにPECompactがパックされていると両方のアナライザーが判断したことは簡単にわかります。原則として、それらを信じない特別な理由はありません。



PECompactとASPack以前の記事の1つで既に説明した)は完全に異なるパッカーであるにもかかわらず、アンパックの原理は同じです。 DIGALO_RUS.exeをOllyDbgにロードし、最初のJMPの直後に実行されるPUSHFD命令まで実行し、Alt-F1を使用してコマンドラインを開き、 hr esp-4コマンドを使用してESP-4にハードウェアブレークポイントを置き、F9を押しますPOPFD命令の実行後スポットに到達するまで、最も近いRETNに到達し、F8を押して0x0045B97Bに到達します。この場合はOEPです。



画像



OllyDumpプラグインを使用してダンプを削除し、「インポートの再構築」チェックボックスにチェックマークを付けて、開梱後に調査中のアプリケーションの動作を確認します...(正常に処理された行で)動作することを確認します。



さて、私たちの前に重要な疑問が生じます。この音声エンジンはどのようにデバッグできますか? 問題は、起動直後にクラッシュし、すでに実行中のプロセスへの接続の可能性を遮断することです。 ちょっとしたトリックがあります-OEPにある最初のバイトをINT3命令に変更できます。この場合(プロセスに接続されたデバッガーがないため)、現在のJITでプロセスをデバッグするための標準ダイアログボックスを表示します。デバッガ。 OllyDbgをそのようにして(オプション->ジャストインタイムデバッグ-> OllyDbgジャストインタイムデバッガーを作成)、最初のバイトを0x55PUSH EBP )から0xCCINT3 )のOEPに置き換えます。



画像



変更を保存し(CPUウィンドウを右クリック->実行可能ファイルにコピー->すべての変更->すべてをコピー->開いたウィンドウを右クリック->ファイルを保存)、元の実行可能ファイルを置き換え、「Talker」のコンソールバージョンを引数「 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa「:



画像



[プログラムのデバッグ]ボタンを押し、 INT3PUSH EBPに戻し、F9を押すと、アクセス違反を処理していることがわかります。



画像



アプリケーションを再度起動し、アクセス違反が発生するアドレス(私の場合は0x00428B9D )にブレークを設定し 、クラッシュ前にこの場所が呼び出される頻度を調べます。 ブレーカーは落下する前に2回トリガーされることがわかります(1回目以降はすべて問題ありませんが、2回目がトリガーされた時点で、 ECXレジスタの値にはアドレスが含まれており、アクセスするとこの例外が発生します)。 最初のブレークポイントがトリガーされた後、この場所からトレースを開始し、アプリケーション操作が成功した場合(たとえば、引数 "Hello"との会話を開始した場合)およびクラッシュのイベントで[トレースの実行]ウィンドウに表示される内容を確認します。



転倒の場合

Address Thread Command ; Registers and comments Flushing gathered information 00428B9D 00002410 MOV EDX,DWORD PTR DS:[ECX+64] ; EDX=0051A820 00428BA0 00002410 LEA EAX,DWORD PTR DS:[EAX+EAX*2] ; EAX=00000003 00428BA3 00002410 MOV CL,BYTE PTR DS:[ESI] ; ECX=03007BA0 [...] 004303FB 00002410 CMP EDX,ECX 004303FD 00002410 JL digalo_r.00430282 00430282 00002410 MOV EDI,1 ; EDI=00000001 00430287 00002410 MOV EAX,DWORD PTR SS:[ESP+1C] ; EAX=00000028 0043028B 00002410 MOV EDX,DWORD PTR DS:[4A8BDC] ; EDX=004A8DC8 00430291 00002410 MOVSX ECX,BYTE PTR SS:[ESP+EAX+113] ; ECX=00000074
      
      





正しく動作する場合

 Address Thread Command ; Registers and comments Flushing gathered information 00428B9D 000024D8 MOV EDX,DWORD PTR DS:[ECX+64] ; EDX=0059A880 00428BA0 000024D8 LEA EAX,DWORD PTR DS:[EAX+EAX*2] ; EAX=00000003 00428BA3 000024D8 MOV CL,BYTE PTR DS:[ESI] ; ECX=02F37BE5 [...] 004303FB 000024D8 CMP EDX,ECX 004303FD 000024D8 JL digalo_r.00430282 00430403 000024D8 MOV EDI,DWORD PTR SS:[ESP+28] ; EDI=00000001 00430407 000024D8 MOV ESI,DWORD PTR SS:[ESP+244] ; ESI=02F37B20 0043040E 000024D8 LEA EDX,DWORD PTR SS:[ESP+114] ; EDX=024CF348
      
      





最後から結論を見ると、少なくとも落下の場合、アドレス0x00430282への条件付き遷移がトリガーされます 、正しく機能していれば違いはありません。



さて、このアドレスで条件付きジャンプ命令を隠して、何が起こるか見てみましょう。 はい、Digaloは今では本当に長い「a」を発音します。 しかし、別の問題が発生しました-テキストを読んだ後、エンジンは再びアクセス違反でクラッシュしますが、まったく別の場所で:



画像



すでにアドレスで、あなたは今度はシステムライブラリの腸について話していることに気づくはずです。 Alt-Kを使用してコールスタックを調べ、WinAPI関数HeapFree内でクラッシュが発生したことを確認します。



画像



もちろん、99%の確率で、kernel32.dllのバグは見つかりませんでしたが、間違ったパラメーターを渡しました。 HeapFree呼び出しにブレークを設定すると、他のすべてのケースで、 pMemoryパラメーターとして渡される引数に、アプリケーションがクラッシュしたときに渡されたアドレスとは大幅に異なるアドレスが含まれていることがわかります。



画像



疑わしいですね。 しかし、私たちは何ができますか? 2つのオプションがあります-指定されたアドレスがここに到達する理由を長く退屈に調査するか、単にそれを叩いてメモリを解放するかです。 おそらくあなたのほとんどは、すでにわいせつな表現で私をカバーし始めていますが、あなたがそれについて考えるならば、これで実際にひどいものは何もあり得ません。 はい、あなたは正しいと聞きました。 もちろん、コードからすべてのHeapFree呼び出しを削除することは、軽度に間違っていることに同意します。プロセスでは、アプリケーションが非常に多くのメモリを割り当てることができるためです(たとえば、長いテキストなどを読むとき)。すでに新しい問題に。 しかし、アプリケーションが終了したときに空きメモリを削除することの何が問題になっていますか? なぜなら それはWindowsについてのみであり、OSはとにかくリソースを解放します(一部のプラットフォームおよびシステムでは、これが重要になる可能性があります、同意します)。



コールスタックを見てみましょう。 さて、アプリケーションを再度実行し、アドレス0x0045A2B30x0041136Cにブレークを入れます。 最初のアドレスのブレークは何度もトリガーされます。これは、この関数がHeapFreeのラッパーであり、指定されたメモリを解放する可能性が高いことを示していますが、2番目のアドレスのブレークは、スピーチエンジンが渡されたテキストを読み取った後にのみ機能しますほとんどの場合、このプロシージャはアプリケーションが終了したときにのみ呼び出されることを意味します。



画像



0x0041136Cにあるプロシージャコール0x0045A273に入力して 、問題が解決したかどうかを確認しましょう。 はい、問題は修正されました-エンジンは指定されたフレーズを発音し、正しく終了します:



画像



なぜなら 私の目標は、Digaloスピーチエンジンの助けを借りて、特定の長引く音「a」を発音する可能性を得ることでした。その後、タスクが完了したと言えます。 はい、 HeapFree関数を呼び出すときにアプリケーションクラッシュの原因を詳しく調べることはできませんでした。また、最初の問題を回避するために条件付きジャンプを埋めることができるかどうかを完全には理解していませんでしたが、結局、そのような問題の解決に多くを費やす理由たくさんの時間? 音が出ましたか? 彼らはそれを言った。 他のフレーズや音については、実行可能ファイルDigaloの元のバージョンを引き続き使用できます。これにより、アクションに予期しない結果が追加されることを心配する必要がなくなります。



あとがき



私の記事では、ここであなたの目標を達成することは、あなたが考えているほど難しくないかもしれないことを示したかったのです。 一部のプログラムが作業結果の保存を拒否したり、特定の状況で期待どおりの結果を出せなかったりした場合、それらの回答を待たずにこの問題を完全に解決できます。 使用されるアプリケーションのサポート(これは、偶然にも存在しない場合があります)。 良くも悪くも、自分で決めてください。 結局のところ、これは一般にリバースエンジニアリングにとってかなり奇妙な質問ですよね?



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



All Articles