2012-06-20 60 views
0

我已經將一個DLL注入到程序中,以在應用程序主窗口上實現聊天UI。我想我可以獲得應用程序的主窗口句柄,然後獲取它的DC,並繪製到它上面。該窗口有一個可預測的標題,這意味着我可以使用FindWindow來獲取句柄。唯一的問題是,DLL在進程啓動時被注入。那時候,這個窗口還沒有被創建。這意味着FindWindow找不到任何東西!如何從注入的DLL獲取程序窗口?

這是什麼解決方案?我可以在DLL中創建一個線程並休眠一段時間,直到我知道窗口被創建了嗎?這看起來很不穩定,所以我寧願不這樣做。

我試圖做的是在DLL中使用SetWindowsHookEx來掛鉤全局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時,它不起作用。不過,我知道我的日誌作品,因爲它可以在其他地方使用。我非常困惑與這發生了什麼。

是否有任何其他方法獲得主窗口(最好是創建時)?或者,我的鉤子方法不錯,但剛執行錯誤?感謝您的任何反饋。

回答

1

當應用程序已經啓動時,您可以隨時注入DLL。由於Windows Vista/7中的ASLR,現在很複雜,但並非不可能。您將不得不編寫一個簡短的應用程序,將選定的DLL注入給定PID的進程中。下面是爲了將DLL注入正在運行的進程而應該做的事情:

編寫一個shellcode,它將查找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. 負載編譯的shellcode到從文件存儲器。
  2. 作爲調試器附加到目標進程。停止應用中的所有線程。分配一些內存並設置「讀取,寫入,執行」權限並在其中注入shellcode。
  3. 獲取主線程句柄。打開線程,創建線程上下文備份,然後設置EIP寄存器修改後的新上下文(設置爲分配的內存 - shellcode - 地址)。
  4. 恢復一段時間的線程(例如5秒)。確保該過程已激活,並且我們的shellcode有機會執行。
  5. 再次作爲調試器附加到目標進程。讀取EAX寄存器,該寄存器現在應該在目標進程中存儲kernel32.dll基址(由於ASLR可能與注入器進程中的ASLR不同)。
  6. 從您的過程中檢查kernel32.dllLoadLibraryA函數的偏移量。
  7. 目標進程中的偏移量應該相同,因此您必須將遠程kernel32.dll基址添加到偏移量,以便計算遠程進程中的基址爲LoadLibraryA函數。
  8. 調用CreateRemoteThread函數將計算的地址LoadLibraryA作爲函數調用,並將DLL路徑作爲參數。

,我不得不對我自己前一段時間都明白這(我找不到任何說明),但最近我發現類似的東西:http://syprog.blogspot.com/2012/05/createremotethread-bypass-windows.html

快樂黑客!

+0

哇,這看起來很有前途:)我使用的DLL注入器專爲黑客遊戲而設計,因爲我還沒有自己寫過。但我現在用這個寫一個。謝謝! – smoth190

相關問題