参考:http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces
SetWindowsHookEx
1.在成功调用 SetWindowsHookEx 后,系统自动映射 DLL 到钩子作用的线程地址空间,但不会立即发生映射,因为 Windows 钩子都是消息,DLL 在消息事件发生前并没有产生实际的映射。
2.为了强制进行映射,在调用 SetWindowsHookEx 之后马上发送一个事件到相关的线程。
SendMessage(hWnd, WM_HOOKEX, 0, 1);
WM_HOOKEX可以在DLL加载时注册,DLLMain:DLL_PROCESS_ATTACH
WM_HOOKEX = ::RegisterWindowMessage("WM_HOOKEX_RK");
3.钩子会影响性能,所以最好DLL被实际映射的第一时间,也就是在HOOK处理函数中,及时UnhookWindowsHookEx,并LoadLibrary我们的dll,同时SubClass消息
LRESULT HookProc (
int code,
WPARAM wParam,
LPARAM lParam
)
{
if ((pCW->message == WM_HOOKEX)
&& pCW->lParam)
{
::UnhookWindowsHookEx(g_hHook);
if (g_bSubclassed)
{
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}
char lib_name[MAX_PATH];
::GetModuleFileName( hDll, lib_name, MAX_PATH );
if (!::LoadLibrary( lib_name ))
{
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}
OldProc = (WNDPROC)
::SetWindowLong(g_hWnd, GWL_WNDPROC, (long)NewProc);
if (OldProc==NULL)
{
::FreeLibrary( hDll );
}
else
{
::MessageBeep(MB_OK);
g_bSubclassed = true;
}
}
4.上述三点可以实现挂钩后及时脱离,但怎么释放自己的DLL呢,这时就需要再次
SetWindowsHookEx了,同时发送释放自己dll的消息通知,接上述代码
else if (WM_HOOKEX ==pCW->message)
{
::UnhookWindowsHookEx(g_hHook);
if(!SetWindowLong( g_hWnd, GWL_WNDPROC, (long)OldProc))
{
return ::CallNextHookEx(g_hHook, code, wParam, lParam);
}
::FreeLibrary( hDll );
::MessageBeep(MB_OK);
g_bSubclassed = false;
}
CreateRemoteThread
HMODULE WINAPI LoadLibrary(
__in LPCTSTR lpFileName
);
BOOL WINAPI FreeLibrary(
__in HMODULE hModule
);
HANDLE WINAPI CreateRemoteThread(
__in HANDLE hProcess,
__in LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out LPDWORD lpThreadId
);
typedef DWORD (__stdcall *LPTHREAD_START_ROUTINE) (
[in] LPVOID lpThreadParameter
);
关键点,
LoadLibrary和FreeLibrary都是LPTHREAD_START_ROUTINE函数类型
也就是说,我们可以把LoadLibrary/FreeLibrary 作为到CreateRemoteThread 的线程参数
1.
lpStartAddress 参数必须表示远程进程中线程函数的开始地址,这个没问题,因为一般来说kernel32在不同进程中加载位置是相同的,所以LoadLibrary/FreeLibrary 地址可以直接在本进程获取再传给远程进程
2.如果传递到 ThreadFunc 的参数lpParameter 被解释为一个指针。它必须指向远程进程的内存数据,如果被解释成常规的32位数据当然就不考虑了
这里就会有个新问题,LoadLibrary传入的肯定是要注入的dll的路径,所以就是向被注入的进程写数据了
HMODULE hKernel32 = ::GetModuleHandle("Kernel32");
void* pLibRemote = ::VirtualAllocEx(hProcess, NULL, nLen, MEM_COMMIT, PAGE_READWRITE );
if (NULL == pLibRemote)
{
return false;
}
if (!::WriteProcessMemory(hProcess, pLibRemote, (void*)szDllPath,nLen,NULL))
{
return false;
}
HANDLE hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"LoadLibraryA"),
pLibRemote, 0, NULL );
if (NULL == hThread)
{
goto JUMP;
}
::WaitForSingleObject( hThread, INFINITE );
// Get handle of loaded module
::GetExitCodeThread(hThread, &g_hLibModule);
::CloseHandle(hThread);
//
JUMP:
::VirtualFreeEx(hProcess, pLibRemote, sizeof(szDllPath), MEM_RELEASE );
if (NULL == g_hLibModule)
{
return false;
}
3.退出HOOK
HMODULE hKernel32 = ::GetModuleHandle("Kernel32");
HANDLE hThread = ::CreateRemoteThread(hProcess,
NULL, 0,
(LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"FreeLibrary"),
(void*)g_hLibModule,
0, NULL );