我已将DLL注入程序,以在应用程序主窗口上实现聊天UI。我想我可以获得应用程序主窗口句柄,然后得到它的DC,然后绘制它。该窗口具有可预测的标题,这意味着我可以使用FindWindow
来获取句柄。唯一的问题是,在进程启动时注入DLL。那时,窗口尚未创建。这意味着FindWindow
找不到任何东西!
这有什么解决方案?我可以在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
而不是日志,看看它是否有效,这结果是一个糟糕的主意。所有的程序都冻结了,因为他们在等我单击“确定”,我不得不进行硬重置...当我重新打开计算机并用日志命令替换MessageBox
时,它没有'工作。但我知道我的日志有效,因为它可以在其他任何地方使用。我对此发生的事情非常困惑。
是否有其他获取主窗口的方法(最好是在创建时)?或者我的钩子方法好,但只是执行错了?感谢您的任何反馈。
答案 0 :(得分: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
kernel32.dll
基址(由于ASLR,它可能与您的注入过程不同)。LoadLibraryA
中kernel32.dll
函数与流程的偏移量。 kernel32.dll
基址,以便计算远程进程中LoadLibraryA
函数的基址。CreateRemoteThread
函数,将计算出的LoadLibraryA
地址作为调用函数,将DLL路径作为参数。前一段时间我不得不自己解决这个问题(我找不到任何描述),但最近我发现了类似的东西:http://syprog.blogspot.com/2012/05/createremotethread-bypass-windows.html
快乐的黑客攻击!