0

プログラムにDLLを挿入して、アプリケーションのメインウィンドウにチャットUIを実装しました。アプリケーションのメインウィンドウハンドルを取得し、次にDCを取得して、そこに描画できると考えました。ウィンドウには予測可能なタイトルがあります。これはFindWindow、ハンドルを取得するために使用できることを意味します。唯一の問題は、プロセスの開始時にDLLが挿入されることです。その時点では、ウィンドウは作成されていません。つまりFindWindow、何も見つかりません!

これに対するいくつかの解決策は何ですか?DLLにスレッドを作成し、ウィンドウが作成されたことがわかるまでしばらくスリープできますか?これは非常に不安定なようですので、やりたくありません。

私がやろうとしたSetWindowsHookExのは、DLLでグローバルWndProcをフックするために使用することでした。ウィンドウからメッセージが見つかるまで(つまり、メッセージが作成されるまで)メッセージをスキャンできました。次に、ハンドルを保存してプログラムを続行できます。一度に同じ名前のウィンドウが複数あることについてはあまり心配していません。唯一の問題は、私のフックが呼び出されないことです。

私は次のようにフックを作成します:

m_hWndProcHook = SetWindowsHookEx(WH_CALLWNDPROC, (HOOKPROC)WndProc, m_hModule, 0);
if(!m_hWndProcHook)
{
    oss << "Failed to set wndproc hook. Error code: " << GetLastError();
    Log(oss.str().c_str());
    return false;
} 

これは有効なフックを返します。WndProcは次のようになります。

LRESULT CALLBACK CChatLibrary::WndProc(int code, WPARAM wParam, LPARAM lParam)
{
    CWPSTRUCT* pData;
    ostringstream oss;
    char wndName[256];

    gChatLib->Log("WNDPROC");

    if(code < 0)
        return CallNextHookEx(gChatLib->GetWndProcHookHandle(), code, wParam, lParam);
    else
    {
        //Get the data for the wndproc
        pData = (CWPSTRUCT*)lParam;

        //Log the message
        GetWindowText(pData->hwnd, wndName, 256);
        oss << "Message from window \"" << wndName << "\"";
        gChatLib->Log(oss.str().c_str());

        return CallNextHookEx(gChatLib->GetWndProcHookHandle(), code, wParam, lParam);
    }
}

しかし、「WNDPROC」メッセージはログファイルに記録されません...以前は、ログのMessageBox代わりにログが機能するかどうかを確認していましたが、これはひどい考えでした。「OK」をクリックするのを待っていたため、すべてのプログラムがフリーズし、ハードリセットを実行する必要がありました...コンピュータの電源を入れ直してMessageBoxログコマンドに置き換えたところ、機能しませんでした。ただし、ログは他のすべての場所で機能するため、ログが機能することはわかっています。私はこれで何が起こっているのか非常に混乱しています。

メインウィンドウを取得する他の方法はありますか(できれば作成時に)?または、私のフックメソッドは良いのですが、実行が間違っていますか?フィードバックありがとうございます。

4

1 に答える 1

1

アプリケーションがすでに開始されている場合は、いつでも DLL を挿入できます。Windows Vista/7 の ASLR のせいで、今日では非常に複雑ですが、不可能ではありません。選択した DLL を特定の PID でプロセスに挿入する短いアプリケーションを作成する必要があります。実行中のプロセスに DLL を挿入するには、次の手順を実行する必要があります。

kernel32.dllライブラリのアドレスを見つけるシェルコードを書きます。NASM の私の古いコードは次のとおりです。

[BITS 32]

_main:
    xor     eax,    eax
    mov     esi,    [FS:eax+0x30]   ; ESI points at PEB
    mov     esi,    [esi+0x0C]  ; ESI points at PEB->Ldr
    mov     esi,    [esi+0x1C]  ; ESI points at PEB->Ldr.InInitOrder
    mov     edx,    -1          ; EDX is now the current letter pointer

check_dll:
    mov     ebp,    [esi+0x08]  ; EBP points at base address InInitOrder[i]
    mov     edi,    [esi+0x20]  ; EDI points at InInitOrder[X] name
    mov     esi,    [esi]       ; ESI points at flink
    mov     edx,    -1      ; set letter pointer at InInitOrder name
    mov     ebx,    0       ; set pattern letter pointer to null

check_small_name:
    inc     edx             ; go to the next letter in InInitOrder name
    cmp     ebx,    0x7     ; check if we have checked all letters
    je      library_found       ; if so and no error kernel32.dll found
    mov     al, BYTE[edi+edx]   ; load byte to EAX from InInitOrder name
    cmp     al,     0x0 ; check if unicode complement
    je      check_small_name    ; ignore if so
    jmp     s_kernel32

back1:
    pop     ecx
    cmp     BYTE[ecx+ebx],  al  ; compare characters
    jne     check_big_name      ; if not equal check upper size
    inc     ebx         ; if equal then go to the next letter in pattern
    jmp     check_small_name    ; loop  

check_big_name:
    jmp     b_kernel32

back2:
    pop     ecx
    cmp     BYTE[ecx+ebx],  al  ; check characters
    jne     check_dll       ; if not equal then go to the next module
    inc     ebx         ; if equal go increment the pattern pointer
    jmp     check_small_name    ; loop

library_found:
    mov     eax,    ebp         ; move kernel32 base address into ECX

loop:
    jmp loop    

s_kernel32:
    call    back1
    db      "kernel32",10,0 

b_kernel32:
    call    back2
    db      "KERNEL32",10,0
  1. コンパイルされたシェルコードをファイルからメモリにロードします。
  2. ターゲット プロセスにデバッガーとしてアタッチします。アプリケーション内のすべてのスレッドを停止します。メモリを割り当て、「読み取り、書き込み、実行」権限を設定し、そこにシェルコードを挿入します。
  3. メイン スレッド ハンドルを取得します。スレッドを開き、スレッド コンテキストのバックアップを作成してから、EIP レジスタを変更して新しいコンテキストを設定します (割り当てられたメモリ - シェルコード - アドレスに設定)。
  4. スレッドをしばらく (5 秒など) 再開します。プロセスがアクティブ化され、シェルコードが実行される可能性があることを確認してください。
  5. 再びデバッガーとしてターゲット プロセスにアタッチします。ベースアドレスをターゲットプロセスに保存する必要があるEAXレジスタを読み取りkernel32.dllます(ASLRのおかげで、インジェクタプロセスとは異なる場合があります)。
  6. LoadLibraryAプロセスからの関数のオフセットを確認しkernel32.dllてください。
  7. オフセットはターゲット プロセスで同じである必要があるため、リモート プロセスで関数のkernel32.dllベース アドレスを計算するには、オフセットにリモート ベース アドレスを追加する必要があります。LoadLibraryA
  8. 呼び出すCreateRemoteThread関数として計算されたアドレスを指定LoadLibraryAして関数を呼び出し、パラメーターとして DLL パスを指定します。

少し前にこれをすべて自分で理解しなければなりませんでした(説明が見つかりませんでした)が、最近、似たようなものを見つけました:http://syprog.blogspot.com/2012/05/createremotethread-bypass-windows.html

ハッピーハッキング!

于 2012-06-20T18:33:24.667 に答える