1. 程式人生 > >EasyHook遠端程式碼注入

EasyHook遠端程式碼注入

    最近一段時間由於使用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");



這樣就能保證注入的鉤子能正常工作了。