EasyHook遠端程式碼注入
阿新 • • 發佈:2019-02-05
最近一段時間由於使用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; }
// 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; }
鉤子這一部分我弄了比較久,主要是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");
這樣就能保證注入的鉤子能正常工作了。