最近一段时间由于使用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;
-
}
[cpp] view plain copy
- <code class="language-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;
- }</code>
钩子这一部分我弄了比较久,主要是API不熟悉,不过好在弄好了。
-
// 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;
接下来是注入工具,这里指提供核心代码。本来EasyHook还提供了一个叫RhInjectLibrary()方法直接注入,这种方法相当稳定,推荐使用。我本来也用它,但是发现注入会失败,所以就采用了比较通用的远程注入代码,如下:
-
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");
这样就能保证注入的钩子能正常工作了。