イントロ
PyDbgの形式で純粋なPythonデバッガーの作成と使用を検討したので、今度は完全なユーザーインターフェイスと最も強力なPythonライブラリで構成されるImmunity Debuggerを調べて、エクスプロイトを開発し、脆弱性を検出し、悪意のあるコードを分析します。 2007年にリリースされたImmunity Debuggerは、動的デバッグ機能と静的分析機能の両方をうまく組み合わせています。 さらに、完全にカスタマイズ可能なグラフィカルインターフェイスが純粋なPythonに実装されています。 この章の冒頭で、イミュニティデバッガとそのユーザーインターフェイスについて簡単に紹介します。 その後、エクスプロイトの開発とマルウェアで使用されるアンチデバッグ技術を自動的に回避するいくつかの方法の開発を徐々に深めていきます。 Immunity Debuggerをダウンロードして起動することから始めましょう。
5.1 Immunity Debuggerのインストール
Immunity Debuggerは無料で配布およびサポートされています
[1]。ダウンロードするためのリンクはこちらです:
debugger.immunityinc.comインストーラーをダウンロードして実行するだけです。 Python 2.5をインストールしていない場合(おおよそお伝えしたとおり)、これは大きな問題ではありません。ImmunityDebuggerはPython 2.5インストーラー(おおよそ、Per。 .1)必要に応じてトレーダーによってインストールされます。 Immunity Debuggerをインストールして実行すると、すぐに使用できる状態になります。
5.2イミュニティデバッガー101
Immunity Debuggerとそのインターフェイスを簡単に確認してから、immlib Pythonライブラリに移動して、デバッガ用のスクリプトを作成できるようにします。 最初の起動時に、図5-1に示すインターフェイスが表示されます。
図 5-1 :イミュニティデバッガーのメインインターフェイス
メインデバッガーインターフェースは、5つの主要部分で構成されています。 左上隅にあるCPUウィンドウには、アセンブラコードが表示されます。 右上隅には、汎用レジスターと他のプロセッサー・レジスターが表示されるレジスター・ウィンドウがあります。 左下隅には、選択したアドレススペースの16進ダンプを表示できるメモリダンプウィンドウがあります。 右下隅にはスタックウィンドウがあり、スタックへの対応する呼び出しが表示されます。 また、シンボリック情報の形式でデコードされた関数パラメーターを表示します(たとえば、Windows API関数のネイティブ呼び出し)。 5番目の要素は、一番下にある白いコマンドラインパネルで、WinDbgスタイルのコマンドを使用してデバッガーを制御するように設計されています。 ここでPyCommandsを実行できます。これについてはさらに説明します。
5.2.1 PyCommands
Immunity DebuggerでPythonスクリプトを実行する主な方法は、PyCommands
[2]を使用することです。 PyCommandは、Immunity Debugger内でさまざまなタスクを実行するために記述されたPythonスクリプトです。たとえば、さまざまなフック、静的分析、その他のデバッグ機能を実行するスクリプトです。 各PyCommandが適切に実行されるためには、特定の構造が必要です。 次のコードスニペットは、メインのPyCommand構造を示しています。これをテンプレートとして使用して、独自のPyCommandを作成できます。
from immlib import * def main(args):
各PyCommandには2つの主要なコンポーネントがあります。 最初のコンポーネントは、メイン()関数を定義している必要があります。この関数は、PyCommandに渡される引数のリストである1つのパラメーターを取る必要があります。 2番目のコンポーネントは、main()が実行を終了すると「文字列」を返すことです。 この行は、スクリプトの実行が終了すると、「デバッガーステータスバー」(約。コマンドラインの下にあります。)を更新します。
PyCommandを実行する場合は、Immunity DebuggerのメインインストールディレクトリにあるPyCommandsディレクトリにスクリプトが保存されていることを確認する必要があります。 保存したスクリプトを実行するには、次のように、デバッガーのコマンドラインで感嘆符の後にスクリプト名を入力するだけです。
!scriptname
Enterキーを押すとすぐに、スクリプトの実行が開始されます。
5.2.2 PyHooks
Immunity Debuggerには13種類のインターセプトが付属しており、それぞれ個別のスクリプトまたは内部PyCommandスクリプトとして実装できます。 次のタイプのインターセプトを使用できます。
Bphook / logbphookブレークポイントが発生すると、これらのタイプのインターセプトが機能します。 両方のフックは同じ動作をしますが、BpHookが検出されると、実際にはデバッガーが停止しますが、LogBpHookは実行を中断しません。
AllExceptHookプロセッサで発生する例外により、このタイプのインターセプトが実行されます。
PostAnalysisHookこのインターセプトは、デバッガーがロードされたモジュールの分析を完了した後にトリガーされます。 これは、モジュールの分析が完了した直後に自動的に実行する静的分析タスクがある場合に役立ちます。 immlibを使用して関数とメインブロックをデコードする前に、モジュール(メインの実行可能ファイルを含む)を分析する必要があることに注意することが重要です。
AccessViolationHookこのインターセプトは、アクセス違反が発生するたびにトリガーされます。 ファジング中に情報を傍受するのに最も役立ちます。
LoadDLLHook / UnloadDLLHookこのインターセプトは、DLLがロード/アンロードされるたびにトリガーされます。
CreateThreadHook / ExitThreadHookこのインターセプトは、スレッドが作成または破棄されるたびにトリガーされます。
CreateProcessHook / ExitProcessHookこのタイプのインターセプトは、ターゲットプロセスが開始または終了するときにトリガーされます。
FastLogHook / STDCALLFastLogHookこれらの2つのインターセプトは、スタブを使用してインターセプターのコードを小さなボディに送信します。インターセプターは、インターセプト中にレジスタまたはメモリの特定の値を記録できます。 これらのインターセプトは、頻繁に呼び出される関数をインターセプトするのに役立ちます。 第6章でそれらの使用を検討します。
PyHookを設定するには、LogBpHookを例として使用する次のテンプレートを使用できます。
from immlib import * class MyHook( LogBpHook ): def __init__( self ): LogBpHook.__init__( self ) def run( regs ):
LogBpHookクラスをオーバーロードし、run()関数が定義されていることを確認します。 インターセプトが機能すると、run()関数は単一の引数として、フックがトリガーされたときに設定されたすべてのプロセッサレジスタのリストを取得します。これにより、現在の値を適切に表示または変更できます。 regs変数は、次のように名前でレジスタにアクセスするために使用できる辞書です。
regs["ESP"]
これで、PyCommandとPyHooksを使用して、いくつかの方法でフックを定義できます。 したがって、PyCommandを使用して手動で、またはPyHooks(Immunity Debuggerのメインインストールディレクトリにある)を使用して自動的にフックを設定できます。 PyCommandの場合、インターセプトはPyCommandが実行されるたびに設定されます。 PyHooksの場合、傍受はImmunity Debuggerが起動するたびに自動的にトリガーされます。 次に、Immunity Debuggerの組み込みPythonライブラリであるimmlibの使用例に移りましょう。
5.3エクスプロイト開発
ソフトウェアの脆弱性を検出することは、信頼できる実用的なエクスプロイトを取得するための、長くて困難な道のりの始まりにすぎません。 Immunity Debuggerには、開発パスを少し簡単に行えるようにする多くの設計機能があります。 EIPを取得するための命令を見つける方法や、シェルコードでの使用に適さないバイトのフィルタリングなど、エクスプロイト開発プロセスを高速化するPyCommandを開発します。 また、PyCommand!Findatidepを使用しますが、これはImmunity Debuggerにバンドルされており、DEP(データ実行防止)
[3]のバイパスに役立ちます。 さあ始めましょう!
5.3.1エクスプロイトフレンドリーな指示の検索
EIPを制御した後、実行をシェルコードに転送する必要があります。 原則として、シェルコードを指すレジスタまたはケースオフセットがあります。 あなたのタスクは、実行可能ファイルまたはロードされたモジュールのいずれかで命令を見つけることです。これにより、制御が目的のアドレスに転送されます。 Immunity Debugger Pythonライブラリは、ダウンロードしたバイナリ全体で目的の命令を検索できる検索インターフェイスを提供することで、これを簡単にします。 命令を受け取り、この命令が発生するすべてのアドレスを返すスクリプトを膝の上に書き留めましょう。 新しい
findinstruction.pyファイルを作成し、次のコードを入力します。
findinstruction.py: from immlib import * def main(args): imm = Debugger() search_code = " ".join(args) (
最初に、受信した命令を同等のバイナリ
(#1)に変換し、Search()関数を使用して、ダウンロードしたバイナリファイルのメモリ内のすべての命令を検索します
(#2) 。 次に、返されたリストで、検出されたすべてのアドレスをソートして、命令が配置されているメモリページを取得し
(#3) 、その後、メモリが実行可能としてマークされていることを確認します
(#4) 。 次に、各命令について、メモリ実行可能ページでそのアドレスを見つけ、
「ログ」ウィンドウに表示します。 スクリプトを使用するには、探している命令を次のように引数として渡します。
!findinstruction "instruction to search for"
スクリプトを実行した後、次のパラメーターを使用します。
!findinstruction jmp esp
図のような結果が表示されます。 5-2。
図 5-2 :PyCommand!Findinstructionの出力
これで、シェルコードを実行するために使用できるアドレスのリストが作成されました。これは、ESPレジスタを介して起動できると想定しています。 アドレスのリストに加えて、関心のある命令のアドレスをすばやく見つけるための優れたツールが用意されました。
5.3.2不良文字のフィルタリング
エクスプロイトを含む文字列をターゲットシステムに送信する場合-シェルコードで使用できない文字セットがいくつかあります。 たとえば、strcpy()関数を呼び出すときにスタックオーバーフローが見つかった場合、strcpy()はNULL値に遭遇するとすぐにデータのコピーを停止するため、このエクスプロイトにNULL文字(0x00)を含めることはできません。 したがって、エクスプロイトを作成するときは、シェルコードエンコーダーが使用されます。シェルコードエンコーダーは、シェルコードを起動した後、デコードして実行します。 ただし、脆弱なソフトウェアでいくつかの文字をフィルターで除外したり、特別な方法で処理したりできる場合があり、それらを手動で識別するのは本当に悪夢です。
通常、脆弱なプログラムにシェルコードを入れて起動しない場合(完全に実行されるまでプログラムでアクセス違反またはクラッシュが発生する)、最初に正確にメモリにコピーされたことを確認する必要があります彼らはそれが欲しかった。 Immunity Debuggerはこれを簡単にします。 図を見てください。 5-3、オーバーフロー後のスタックを示します。
図 5-3 :オーバーフロー後のイミュニティデバッガウィンドウスタック
EIPレジスタが現在ESPレジスタを指していることがわかります。 4バイトの0xCCは、ブレークポイントがそこにインストールされているかのように、単にデバッガーを停止させます(0xCCはINT3命令です)。 4つのINT3命令の直後、オフセットESP + 0x4に、シェルコードが配置されています。 ここで、メモリの調査を開始して、シェルコードがターゲットシステムへの攻撃中に送信したものと正確に同じであることを確認する必要があります。 メモリ内のシェルコードを調べるには、オリジナルをASCII文字列として取得し、メモリ内にあるシェルコードと(バイト単位で)比較して、シェルコードが正しくロードされたことを確認します。 違いに気づいた場合、Logのソフトウェアフィルターを通過しなかった不良バイトを出力します。 その後、2番目の攻撃を開始する前に、このような文字の処理をシェルコードエンコーダーに追加できます! このツールの機能をテストするには、Metasploitからシェルコードを取得するか、自分の宿題を取得します。 新しい
badchar.pyファイルを作成し、次のコードを入力します。
badchar.py: from immlib import * def main(args): imm = Debugger() bad_char_found = False
このスクリプトでは、実際にはImmunity DebuggerライブラリからのreadMemory()呼び出しのみを使用し、残りのスクリプトは単純な文字列比較を行います。 これで、シェルコードをASCII文字列として取得するだけで(たとえば、バイト0xEB 0x09がある場合、文字列はEB09のようになります)、スクリプトに挿入し、次のようにスクリプトを実行します。
!badchar "Address to Begin Search"
前の例では、ESP + 0x4(絶対アドレスは0x00AEFD4C)で検索を開始するため、次のようにPyCommandを実行します。
!badchar 0x00AEFD4c
起動後、スクリプトはすぐに不良文字のフィルタリングに関する問題について警告し、シェルコードのエラーのデバッグや発生する可能性のあるフィルターの逆転にかかる時間を大幅に短縮できます。
5.3.3 DEPバイパス
DEPは、ヒープやスタックなどのメモリ領域でコードが実行されるのを防ぐために、Microsoft Windows(XP SP2、2003、およびVista)に実装されているセキュリティ対策です。 ほとんどのエクスプロイトはシェルコードをヒープまたはスタックに保存するため、これはほとんどのエクスプロイトでのシェルコードの実行を妨げる可能性があります。 ただし、スタックに保存されているかどうかに関係なく、シェルコードに制御を安全に転送できる現在のプロセスに対して、ネイティブWindows API呼び出しを使用してDEPを無効にできる既知の手法
[4]があります。またはヒープ内。 Immunity Debuggerには、findantidep.pyというPyCommandが付属しています。 DEPを無効にしてシェルコードを実行するような方法でエクスプロイトをインストールするための適切なアドレスを決定します。 DEPを無効にするための少しの理論を検討してください。 次に、PyCommandスクリプトの使用に移りましょう。これにより、関心のあるアドレスを見つけることができます。
現在のプロセスのDEPを無効にするために使用できるWindows API呼び出しは、文書化されていない関数NtSetInformationProcess()
[5]であり、次のプロトタイプがあります。
NTSTATUS NtSetInformationProcess( IN HANDLE hProcessHandle, IN PROCESS_INFORMATION_CLASS ProcessInformationClass, IN PVOID ProcessInformation, IN ULONG ProcessInformationLength );
DEPを無効にするには、ProcessInformationClassをProcessExecuteFlags(0x22)、ProcessInformationをMEM_EXECUTE_OPTION_ENABLE(0x2)に設定して、NtSetInformationProcess()関数を呼び出す必要があります。 単純なシェルコード設定の問題は、この関数の呼び出しが多くのNULLパラメーターで構成されていることです。これは、ほとんどのシェルコードで問題になります。 この制限を回避できる手法は、シェルコードを関数の中央に配置することです。これにより、既にスタック上にある必要なパラメーターでNtSetInformationProcess()が呼び出されます。
ntdll.dllには、これを行うための既知の場所があります。 Immunity Debuggerを使用して取得したWindows XP SP2の
ntdll.dll逆
アセンブラー出力を
確認してください。
7C91D3F8 . 3C 01 CMP AL,1 7C91D3FA . 6A 02 PUSH 2 7C91D3FC . 5E POP ESI 7C91D3FD . 0F84 B72A0200 JE ntdll.7C93FEBA ... 7C93FEBA > 8975 FC MOV DWORD PTR SS:[EBP-4],ESI 7C93FEBD .^E9 41D5FDFF JMP ntdll.7C91D403 ... 7C91D403 > 837D FC 00 CMP DWORD PTR SS:[EBP-4],0 7C91D407 . 0F85 60890100 JNZ ntdll.7C935D6D ... 7C935D6D > 6A 04 PUSH 4 7C935D6F . 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4] 7C935D72 . 50 PUSH EAX 7C935D73 . 6A 22 PUSH 22 7C935D75 . 6A FF PUSH -1 7C935D77 . E8 B188FDFF CALL ntdll.ZwSetInformationProcess
このコードに従って、ALと値1の比較が表示され、値2がESIに配置されますALが1の場合、0x7C93FEBAへの条件付き移行がトリガーされます。 そこで、ESIからの値がEBP-4スタック変数に移動されます(ESIがまだ2に設定されていることを覚えていますか?)。 次に、0x7C91D403の条件がチェックされ、スタック上の変数(まだ2)がゼロでないことを確認し、その後0x7C935D6Dへの条件付き遷移がトリガーされます。 ここからが楽しみです。 値4がスタックにプッシュされ、EBP-4変数(まだ2に等しい!)がEAXレジスターにロードされ、この値がスタックにプッシュされ、次に値0x22と値-1(-1、関数呼び出しを通知するプロセスハンドルがプッシュされます)これがDEPを無効にする現在のプロセスであること)に続いて、ZwSetInformationProcess(別名NtSetInformationProcess)を呼び出します。 そのため、実際には、このコードでNtSetInformationProcess()関数と呼ばれるコードで何が起こったのか、次のパラメーターがあります。
NtSetInformationProcess( -1, 0x22, 0x2, 0x4 )
パーフェクト! これにより、現在のプロセスのDEPが無効になりますが、このためにアドレス0x7C91D3F8に制御を移す必要があります。 このコードに制御を移す前に、AL(EAXの下位バイト)が1に設定されていることを確認する必要があります。これらの条件が満たされた後、他のオーバーフローと同様に、制御をシェルコードに転送できます。 JMP ESP命令。 したがって、次の3つのアドレスが必要です。
- ALを1に設定してから戻るアドレス。
- DEPを無効にするためのコードが配置されているアドレス。
- シェルコードの先頭に制御を移すためのアドレス。
通常、これらのアドレスを手動で検索する必要がありますが、Immunityのエクスプロイトの開発者は、ウィザード形式で作成された小さなPythonスクリプト
findantidep.pyを作成しました。 悪用文字列を作成して、コピーして悪用することもできます。 これにより、見つかったアドレスをまったく手間をかけずに使用できます。
findantidep.pyスクリプトを見て、試してみましょう。
findantidep.py: import immlib import immutils def tAddr(addr): buf = immutils.int2str32_swapped(addr) return "\\x%02x\\x%02x\\x%02x\\x%02x" % ( ord(buf[0]) , ord(buf[1]), ord(buf[2]), ord(buf[3]) ) DESC="""Find address to bypass software DEP""" def main(args): imm=immlib.Debugger() addylist = [] mod = imm.getModule("ntdll.dll") if not mod: return "Error: Ntdll.dll not found!"
そのため、まずALを1(#1)に設定するコマンドを見つけてから、ユーザーに適切なアドレスを選択するように依頼します。 その後、
ntdll.dllでDEP無効化コード(#2)を含む一連の命令を検索します。 3番目の手順では、シェルコード(#3)に制御を移す必要がある指示または指示を入力し、これらの指示が見つかるアドレスのリストをユーザーに提供するようにユーザーに依頼します。 スクリプトは、ログウィンドウへの結果の出力で終了します(#4)。 図5-4-5-6を見て、このプロセスがどのように進行するかを確認してください。

図 5-4:最初に、ALを1に設定するアドレスを選択します

図 5-5:次に、制御をシェルコードに転送する命令セットを導入します

図 5-6:ステップ(#2)[/ CENTER]から戻るアドレスを選択します
最後に、次のようにログウィンドウに出力が表示されます。
stack = "\x75\x24\x01\x01\xff\xff\xff\xff\x56\x31\x91\x7c\xff\xff\xff\xff" + "A" * 0x54 + "\x75\x24\x01\x01" + shellcode
これで、この出力行を単純にコピーしてエクスプロイトに貼り付け、シェルコードを追加できます。 このスクリプトを使用すると、既存のエクスプロイトをテストして、DEPが有効になっているシステムで正常に実行したり、すぐにDEPを無効にすることをサポートする新しいエクスプロイトを作成したりできます。 これは、30秒の演習になった手動検索に何時間もかかる良い例です。 これで、簡単なPythonスクリプトを使用して、短時間で信頼性の高い移植性のあるエクスプロイトを開発する方法を確認できます。 次に、immlibを使用して、マルウェアの一般的なアンチデバッグ手順をバイパスします。
5.4アンチデバッグ手法のバイパス
現在の種類のマルウェアは、感染、配布の方法、および分析から身を守る能力においてますます混乱しています。 マルウェアは、パッカーや暗号を使用するなどの一般的なコードの難読化方法に加えて、通常、デバッグを防止する手法を使用して、デバッガーによる分析を妨げ、調査を困難にします。 Immunity DebuggerとPythonを使用すると、アナリストがマルウェアサンプルを調査するのに役立つこれらのアンチデバッグトリックのいくつかを回避する簡単なスクリプトを作成できます。 これらの最も一般的なアンチデバッグメソッドのいくつかを見て、それらを回避するための適切なコードを記述しましょう。
5.4.1 IsDebuggerPresent
断然、最も一般的なデバッグ対策方法は、kernel32.dllからエクスポートされたIsDebuggerPresent()関数を使用することです。この関数はパラメータなしで呼び出され、現在のプロセスにデバッガが接続されている場合は1を返し、存在しない場合は0を返します。この関数を逆アセンブルすると、次のコードが表示されます。 7C813093 >/$ 64:A1 18000000 MOV EAX,DWORD PTR FS:[18] 7C813099 |. 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30] 7C81309C |. 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2] 7C8130A0 \. C3 RETN
このコードはTIB(スレッド情報ブロック)からアドレスをロードします。TIBは常にFSレジスタからのオフセット0x18にあります。そこから、常にTIBのオフセット0x30にあるPEB(プロセス環境ブロック)をロードします。3番目の命令は、PEBのオフセット0x2にあるBeingDebuggedパラメーターの値にEAXを設定します。プロセスにデバッガが接続されている場合、このバイトは0x1に設定されます。これに対する簡単な回避策は、ImmunityのDamian Gomez [6]によって公開されました。これは、PyCommandに含まれるか、Immunity DebuggerのPythonシェルから実行できるPython文字列の1つです。 imm.writeMemory( imm.getPEBaddress() + 0x2, "\x00" )
このコードはPEBのBeingDebuggedフラグをリセットするだけで、このチェックを使用するマルウェアは、デバッガーが接続されていないと信じ込ませます。5.4.2
悪意のある人は、コンピューターで実行中のすべてのプロセスを反復処理して、デバッガーが実行されているかどうかを判断しようとします。たとえば、Immunity Debuggerを使用してウイルスを調査すると、ImmunityDebugger.exeが実行中のプロセスとして登録されます。実行中のプロセスを反復処理するために、マルウェアはProcess32First()関数を使用してシステムプロセスのリストの最初の登録済みプロセスを取得し、その後Process32Next()を使用して残りのすべてのプロセスを反復処理します。これらの関数呼び出しは両方とも、関数が正常に完了したかどうかを呼び出し元のコードに伝えるブール値を返すので、関数が結果を返すときにEAXレジスタがゼロに設定されるようにこれらの2つの関数にパッチを適用できます。これを実現するために、強力な組み込みアセンブラーImmunity Debuggerを使用します。次のコードを見てください。 (
まず、プロセスをソートする2つの関数のアドレスを見つけて、リストに保存します(#1)。次に、いくつかのバイトを対応するオペコードに変換します。オペコードはEAXレジスタを0に設定し、関数から制御を返します。これがパッチ(#2)になります次に、Process32FirstおよびProcess32Next関数内で、10個の指示(#3)を実行します。これは、一部の高度な悪意のあるプログラムがこれらの関数の最初の数バイトを実際にチェックして、その関数がリバースエンジニアによって修正されていないことを確認するためです。以下の10の手順でパッチを当てることで、それらを欺きます。本当です、彼らが機能全体の完全性を検証すれば、彼らは私たちを見つけるでしょう。関数内のバイトにパッチを適用した後(#4)、どちらの関数が呼び出されたとしても、両方の関数は偽の結果を返します。PythonとImmunity Debuggerを使用して、接続されたデバッガーの存在を検出しようとする自動マルウェア保護方法を作成する方法の2つの例を見てきました。使用できるデバッグ防止方法は他にも多数あるため、それらを処理するために無限の数のPythonスクリプトが作成されます。この章で得られた知識は、エクスプロイトの開発時間を短縮し、マルウェアと戦うための新しいツールを楽しむのに役立ちます。次に、反転中に使用できるインターセプトメソッドに進みます。参照資料
[1]デバッガーのサポートと一般的な議論については、http://forum.immunityinc.comをご覧ください。[2] Immunity Debugger Pythonライブラリのドキュメント一式については、http://debugger.immunityinc.com/update/Documentation/ref/を参照してください。[3] DEPの詳細な説明は、http://support.microsoft.com/kb/875352/EN-US/にあります。[4] http://www.uninformed.org/?v=2&a=4&t=txtのSkape and Skywingの論文を参照してください。[5] NtSetInformationProcess()関数の定義は、http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/NtSetInformationProcess.htmlにあります。。[6]元のフォーラム投稿はhttp://forum.immunityinc.com/index.php?topic=71.0にあります。