最近一段时间由于使用MinHook的API挂钩不稳定,经常因为挂钩地址错误而导致宿主进程崩溃。听同事介绍了一款智能强大的挂钩引擎EasyHook。它比微软的detours好的一点是它的x64注入支持是免费开源的。不想微软的detours,想搞x64还得购买。
好了,闲话不多说,先下载EasyHook的开发库,当然有兴趣的同学可以下载源码进行学习。下载地址:http://easyhook.codeplex.com/releases/view/24401。我给的这个是2.6版本的。
EasyHook提供了两种模式的注入管理。一种是托管代码的注入,另一种是非托管代码的注入。我是学习C++的,所以直接学习了例子中的非托管项目UnmanagedHook。里面给了一个简单的挂钩MessageBeep API的示例。我需要将其改造成支持远程注入的。下面先给出钩子DLL代码:
- // dllmain.cpp : 定义 DLL 应用程序的入口点。
- #include "stdafx.h"
- #include "HookApi.h"
- #include "easyhook.h"
- #include "ntstatus.h"
- ptrCreateFileW realCreateFileW = NULL;
- ptrCreateFileA realCreateFileA = NULL;
- HMODULE hKernel32 = NULL;
- TRACED_HOOK_HANDLE hHookCreateFileW = new HOOK_TRACE_INFO();
- TRACED_HOOK_HANDLE hHookCreateFileA = new HOOK_TRACE_INFO();
- NTSTATUS statue;
- ULONG HookCreateFileW_ACLEntries[1] = {0};
- ULONG HookCreateFileA_ACLEntries[1] = {0};
- int PrepareRealApiEntry()
- {
- OutputDebugString(L"PrepareRealApiEntry()\n");
- // 获取真实函数地址
- HMODULE hKernel32 = LoadLibrary(L"Kernel32.dll");
- if (hKernel32 == NULL)
- {
- OutputDebugString(L"LoadLibrary(L\"Kernel32.dll\") Error\n");
- return -6002;
- }
- OutputDebugString(L"LoadLibrary(L\"Kernel32.dll\") OK\n");
- realCreateFileW = (ptrCreateFileW)GetProcAddress(hKernel32, "CreateFileW");
- if (realCreateFileW == NULL)
- {
- OutputDebugString(L"(ptrCreateFileW)GetProcAddress(hKernel32, \"CreateFileW\") Error\n");
- return -6007;
- }
- OutputDebugString(L"(ptrCreateFileW)GetProcAddress(hKernel32, \"CreateFileW\") OK\n");
- realCreateFileA = (ptrCreateFileA)GetProcAddress(hKernel32, "CreateFileA");
- if (realCreateFileA == NULL)
- {
- OutputDebugString(L"(ptrCreateFileA)GetProcAddress(hKernel32, \"CreateFileA\") Error\n");
- return -6007;
- }
- OutputDebugString(L"(ptrCreateFileA)GetProcAddress(hKernel32, \"CreateFileA\") OK\n");
- return 0;
- }
- void DoHook()
- {
- OutputDebugString(L"DoHook()\n");
- statue = LhInstallHook(realCreateFileW,
- MyCreateFileW,
- /*(PVOID)0x12345678*/NULL,
- hHookCreateFileW);
- if(!SUCCEEDED(statue))
- {
- switch (statue)
- {
- case STATUS_NO_MEMORY:
- OutputDebugString(L"STATUS_NO_MEMORY\n");
- break;
- case STATUS_NOT_SUPPORTED:
- OutputDebugString(L"STATUS_NOT_SUPPORTED\n");
- break;
- case STATUS_INSUFFICIENT_RESOURCES:
- OutputDebugString(L"STATUS_INSUFFICIENT_RESOURCES\n");
- break;
- default:
- WCHAR dbgstr[512] = {0};
- wsprintf(dbgstr, L"%d\n", statue);
- OutputDebugString(dbgstr);
- }
- OutputDebugString(L"LhInstallHook(GetProcAddress(hKernel32, \"CreateFileW\"),MyCreateFileW,(PVOID)0x12345678,hHookCreateFileW); Error\n");
- return;
- }
- OutputDebugString(L"Hook CreateFileW OK\n");
- statue = LhInstallHook(realCreateFileA,
- MyCreateFileA,
- /*(PVOID)0x12345678*/NULL,
- hHookCreateFileA);
- if(!SUCCEEDED(statue))
- {
- switch (statue)
- {
- case STATUS_NO_MEMORY:
- OutputDebugString(L"STATUS_NO_MEMORY\n");
- break;
- case STATUS_NOT_SUPPORTED:
- OutputDebugString(L"STATUS_NOT_SUPPORTED\n");
- break;
- case STATUS_INSUFFICIENT_RESOURCES:
- OutputDebugString(L"STATUS_INSUFFICIENT_RESOURCES\n");
- break;
- default:
- WCHAR dbgstr[512] = {0};
- wsprintf(dbgstr, L"%d\n", statue);
- OutputDebugString(dbgstr);
- }
- OutputDebugString(L"LhInstallHook(GetProcAddress(hKernel32, \"CreateFileA\"),MyCreateFileA,(PVOID)0x12345678,hHookCreateFileA); Error\n");
- return;
- }
- OutputDebugString(L"Hook CreateFileA OK\n");
- // 一定要调用这个函数,否则注入的钩子无法正常运行。
- LhSetExclusiveACL(HookCreateFileA_ACLEntries, 1, hHookCreateFileA);
- LhSetExclusiveACL(HookCreateFileW_ACLEntries, 1, hHookCreateFileW);
- }
- void DoneHook()
- {
- OutputDebugString(L"DoneHook()\n");
- // this will also invalidate "hHook", because it is a traced handle...
- LhUninstallAllHooks();
- // this will do nothing because the hook is already removed...
- LhUninstallHook(hHookCreateFileA);
- LhUninstallHook(hHookCreateFileW);
- // now we can safely release the traced handle
- delete hHookCreateFileA;
- hHookCreateFileA = NULL;
- delete hHookCreateFileW;
- hHookCreateFileW = NULL;
- // even if the hook is removed, we need to wait for memory release
- LhWaitForPendingRemovals();
- }
- BOOL APIENTRY DllMain( HMODULE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
- {
- switch (ul_reason_for_call)
- {
- case DLL_PROCESS_ATTACH:
- {
- OutputDebugString(L"DllMain::DLL_PROCESS_ATTACH\n");
- // 准备好原始地址与目的地址
- int errCode = PrepareRealApiEntry();
- if (errCode != 0)
- {
- OutputDebugString(L"PrepareRealApiEntry() Error\n");
- return FALSE;
- }
- // 开始挂钩
- DoHook();
- break;
- }
- case DLL_THREAD_ATTACH:
- {
- OutputDebugString(L"DllMain::DLL_THREAD_ATTACH\n");
- break;
- }
- case DLL_THREAD_DETACH:
- {
- OutputDebugString(L"DllMain::DLL_THREAD_DETACH\n");
- break;
- }
- case DLL_PROCESS_DETACH:
- {
- OutputDebugString(L"DllMain::DLL_PROCESS_DETACH\n");
- // 卸载钩子
- DoneHook();
- break;
- }
- }
- return TRUE;
- }
- <pre name="code" class="cpp">// HookSvr.cpp
- #include "stdafx.h"
- #include "HookApi.h"
- #include "easyhook.h"
- HANDLE WINAPI MyCreateFileW(
- __in LPCWSTR lpFileName,
- __in DWORD dwDesiredAccess,
- __in DWORD dwShareMode,
- __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- __in DWORD dwCreationDisposition,
- __in DWORD dwFlagsAndAttributes,
- __in_opt HANDLE hTemplateFile
- )
- {
- HANDLE hHandle = NULL;
- // 执行钩子
- if (realCreateFileW == NULL)
- {
- OutputDebugString(L"realCreateFileW is NULL\n");
- return INVALID_HANDLE_VALUE;
- }
- else
- {
- OutputDebugString(L"realCreateFileW is not NULL\n");
- hHandle = (realCreateFileW)(lpFileName, dwDesiredAccess, dwShareMode,
- lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
- OutputDebugString(L"MyCreateFileW : ");
- OutputDebugString(lpFileName);
- OutputDebugString(L"\n");
- }
- return hHandle;
- }
- HANDLE WINAPI MyCreateFileA(
- __in LPCSTR lpFileName,
- __in DWORD dwDesiredAccess,
- __in DWORD dwShareMode,
- __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- __in DWORD dwCreationDisposition,
- __in DWORD dwFlagsAndAttributes,
- __in_opt HANDLE hTemplateFile
- )
- {
- HANDLE hHandle = NULL;
- // 执行钩子
- if (realCreateFileA == NULL)
- {
- OutputDebugString(L"realCreateFileA is NULL\n");
- return INVALID_HANDLE_VALUE;
- }
- else
- {
- OutputDebugString(L"realCreateFileA is not NULL\n");
- hHandle = (realCreateFileA)(lpFileName, dwDesiredAccess, dwShareMode,
- lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
- OutputDebugString(L"MyCreateFileW : ");
- OutputDebugStringA(lpFileName);
- OutputDebugString(L"\n");
- }
- return hHandle;
- }</pre><br>
- 钩子这一部分我弄了比较久,主要是API不熟悉,不过好在弄好了。
- <pre></pre>
- <p><br>
- </p>
- <p></p><pre name="code" class="cpp">// HookSvr.h
- #pragma once
- #include <Windows.h>
- #ifndef _M_X64
- #pragma comment(lib, "EasyHook32.lib")
- #else
- #pragma comment(lib, "EasyHook64.lib")
- #endif
- HANDLE WINAPI MyCreateFileW(
- __in LPCWSTR lpFileName,
- __in DWORD dwDesiredAccess,
- __in DWORD dwShareMode,
- __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- __in DWORD dwCreationDisposition,
- __in DWORD dwFlagsAndAttributes,
- __in_opt HANDLE hTemplateFile
- );
- typedef HANDLE (WINAPI *ptrCreateFileW)(
- __in LPCWSTR lpFileName,
- __in DWORD dwDesiredAccess,
- __in DWORD dwShareMode,
- __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- __in DWORD dwCreationDisposition,
- __in DWORD dwFlagsAndAttributes,
- __in_opt HANDLE hTemplateFile
- );
- extern ptrCreateFileW realCreateFileW;
- HANDLE WINAPI MyCreateFileA(
- __in LPCSTR lpFileName,
- __in DWORD dwDesiredAccess,
- __in DWORD dwShareMode,
- __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- __in DWORD dwCreationDisposition,
- __in DWORD dwFlagsAndAttributes,
- __in_opt HANDLE hTemplateFile
- );
- typedef HANDLE (WINAPI *ptrCreateFileA)(
- __in LPCSTR lpFileName,
- __in DWORD dwDesiredAccess,
- __in DWORD dwShareMode,
- __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes,
- __in DWORD dwCreationDisposition,
- __in DWORD dwFlagsAndAttributes,
- __in_opt HANDLE hTemplateFile
- );
- extern ptrCreateFileA realCreateFileA;</pre><br>
- <br>
- <p></p>
- <p>接下来是注入工具,这里指提供核心代码。本来EasyHook还提供了一个叫<span style="color:black">Rh</span>InjectLibrary()方法直接注入,这种方法相当稳定,推荐使用。我本来也用它,但是发现注入会失败,所以就采用了比较通用的远程注入代码,如下:</p>
- <pre name="code" class="cpp">BOOL RtlFileExists(WCHAR* InPath)
- {
- HANDLE hFile;
- if((hFile = CreateFileW(InPath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
- return FALSE;
- CloseHandle(hFile);
- return TRUE;
- }
- BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
- {
- TOKEN_PRIVILEGES tp;
- HANDLE hToken;
- LUID luid;
- if( !OpenProcessToken(GetCurrentProcess(),
- TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
- &hToken) )
- {
- return FALSE;
- }
- if( !LookupPrivilegeValue(NULL, // lookup privilege on local system
- lpszPrivilege, // privilege to lookup
- &luid) ) // receives LUID of privilege
- {
- return FALSE;
- }
- tp.PrivilegeCount = 1;
- tp.Privileges[0].Luid = luid;
- if( bEnablePrivilege )
- tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
- else
- tp.Privileges[0].Attributes = 0;
- // Enable the privilege or disable all privileges.
- if( !AdjustTokenPrivileges(hToken,
- FALSE,
- &tp,
- sizeof(TOKEN_PRIVILEGES),
- (PTOKEN_PRIVILEGES) NULL,
- (PDWORD) NULL) )
- {
- return FALSE;
- }
- if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
- {
- //The token does not have the specified privilege.
- return FALSE;
- }
- return TRUE;
- }
- typedef DWORD (WINAPI *PFNTCREATETHREADEX)
- (
- PHANDLE ThreadHandle,
- ACCESS_MASK DesiredAccess,
- LPVOID ObjectAttributes,
- HANDLE ProcessHandle,
- LPTHREAD_START_ROUTINE lpStartAddress,
- LPVOID lpParameter,
- BOOL CreateSuspended,
- DWORD dwStackSize,
- DWORD dw1,
- DWORD dw2,
- LPVOID Unknown
- );
- BOOL MyCreateRemoteThread(HANDLE hProcess, LPTHREAD_START_ROUTINE pThreadProc, LPVOID pRemoteBuf)
- {
- HANDLE hThread = NULL;
- FARPROC pFunc = NULL;
- BOOL bHook;
- // 判断系统版本
- OSVERSIONINFO osvi;
- //BOOL bIsWindowsXPorLater;
- ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
- osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- GetVersionEx(&osvi);
- if (osvi.dwMajorVersion == 6)
- {
- bHook = TRUE;
- }
- else
- {
- bHook = FALSE;
- }
- if(bHook) // Vista, 7, Server2008
- {
- pFunc = GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx");
- if( pFunc == NULL )
- {
- //GetLastError());
- return FALSE;
- }
- OutputDebugString(L"MyCreateRemoteThread");
- ((PFNTCREATETHREADEX)pFunc)(&hThread,
- 0x1FFFFF,
- NULL,
- hProcess,
- pThreadProc,
- pRemoteBuf,
- FALSE,
- NULL,
- NULL,
- NULL,
- NULL);
- if( hThread == NULL )
- {
- return FALSE;
- }
- }
- else // 2000, XP, Server2003
- {
- hThread = CreateRemoteThread(hProcess,
- NULL,
- 0,
- pThreadProc,
- pRemoteBuf,
- 0,
- NULL);
- if( hThread == NULL )
- {
- return FALSE;
- }
- }
- if( WAIT_FAILED == WaitForSingleObject(hThread, INFINITE) )
- {
- return FALSE;
- }
- return TRUE;
- }
- BOOL InjectDll(DWORD dwPID, const wchar_t *szDllName)
- {
- HANDLE hProcess = NULL;
- LPVOID pRemoteBuf = NULL;
- FARPROC pThreadProc = NULL;
- DWORD dwBufSize = wcslen(szDllName)*sizeof(wchar_t)+2;
- if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
- {
- return FALSE;
- }
- pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize,
- MEM_COMMIT, PAGE_READWRITE);
- WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName,
- dwBufSize, NULL);
- pThreadProc = GetProcAddress(GetModuleHandle(L"kernel32.dll"),
- "LoadLibraryW");
- if( !MyCreateRemoteThread(hProcess, (LPTHREAD_START_ROUTINE)pThreadProc, pRemoteBuf) )
- {
- return FALSE;
- }
- VirtualFreeEx(hProcess, pRemoteBuf, dwBufSize, MEM_RELEASE);
- CloseHandle(hProcess);
- return TRUE;
- }
- int DoInject(DWORD aPid, const WCHAR *aFullpath)
- {
- if (wcslen(aFullpath) <= 0)
- {
- return -1;
- }
- //判断dll是否存在
- HANDLE hFile = CreateFile(aFullpath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,NULL, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
- if(hFile != INVALID_HANDLE_VALUE)
- {
- DWORD dwsize = GetFileSize(hFile, NULL);
- CloseHandle(hFile);
- if (dwsize < 10)
- {
- return -2;
- }
- }
- else
- {
- return -3;
- }
- BOOL bSuc=SetPrivilege(SE_DEBUG_NAME, TRUE);
- bSuc=InjectDll((DWORD)aPid, aFullpath);
- if (bSuc)
- {
- return -4;
- }
- return 0;
- }
- // 真实注入的时候应该这样调用
- DoInject(m_processId, L"E:\\src\\easyhook\\trunk\\Debug\\x86\\HookSvr.dll");
- </pre><br>
- 这样就能保证注入的钩子能正常工作了。
- 4楼 SpiritMFC 2013-03-26 15:43发表
- 你好~ 能提供能运行的源码嘛?
我用你的方法正常exe可以hook成功,
但是DLL注入后钩子无法工作。
困扰数天的问题了。
求帮助!
还有 C++的话这个库能实现全局钩子嘛?
- Re: baggiowangyu 2013-03-29 09:24发表
- 回复SpiritMFC:我给的例子就是源代码了哇,那时候研究到那里就没有继续往下了。应该是你注入之后挂钩写的不对导致的。
你指的全局钩子是什么概念?全局消息钩子么?目前我知道的是这个可以实现指定进程的挂钩。
- 3楼 lsssml1990 2012-10-26 12:22发表
- 这个代码可以直接用不?
- 2楼 baggiowangyu 2012-06-23 14:02发表
- 一同学习,一同学习
- 1楼 Wentasy 2012-06-19 11:46发表
- 不错,学习了。