www.gusucode.com > VC++写的APIHook实例源代码-源码程序 > VC++写的APIHook实例源代码-源码程序\code\HookTool\Injector.cpp

    //Download by http://www.NewXing.com
//---------------------------------------------------------------------------
//
// Injector.cpp
//
// SUBSYSTEM: 
//				API hooking system
// MODULE:    
//              Implements injection mechanism
//
// DESCRIPTION:
//              
//
// AUTHOR:		Ivo Ivanov (ivopi@hotmail.com)
// DATE:		2001 November 13,  version 1.0 
//                                                                         
// FIXES:
//              - 2002 November 20
//                Added a mechanism for handling reference count of the HookTool.dll 
//                instances. This allows us to fix a problem caused by the asynchronous 
//                behavior of ::UnhookWindowsHookEx() API                
//
//---------------------------------------------------------------------------

#include "Injector.h"
#include "..\Common\ModuleInstance.h"
#include "..\Common\SysUtils.h"
#include "..\Common\IniFile.h"
#include "NtInjectorThread.h"

//---------------------------------------------------------------------------
//
// Thread function prototype
//
//---------------------------------------------------------------------------
typedef unsigned (__stdcall *PTHREAD_START)(void *);

//---------------------------------------------------------------------------
//
// External declarations
//
//---------------------------------------------------------------------------

extern LRESULT CALLBACK GetMsgProc(
	int code,       // hook code
	WPARAM wParam,  // removal option
	LPARAM lParam   // message
	);


//---------------------------------------------------------------------------
//
// class CInjector  
//
//---------------------------------------------------------------------------

CInjector::CInjector(BOOL bServerInstance):
	m_bServerInstance(bServerInstance),
	m_bHookAllEnabledInitialized(FALSE),
	m_bHookAllEnabled(FALSE)
{

}

CInjector::~CInjector()
{

}

//
// examines whether a process should be hooked up by the DLL
//
BOOL CInjector::IsProcessForHooking(PSTR pszExaminedProcessName)
{
	BOOL  bHoolAll = GetHookAllEnabled();
	BOOL  bProcessProtected = FALSE;
	char  szProcessName[MAX_PATH];
	DWORD dwStartPos = 0;
	long  nCommaPos;
	if (bHoolAll)
	{
		while ( (dwStartPos < strlen(m_szProcessesForHooking)) &&
		        GetNextCommaSeparatedString(
					&m_szProcessesForHooking[dwStartPos],
					szProcessName,
					sizeof(szProcessName),
					&nCommaPos
					) 
		      )
		{
			strcat(szProcessName, ".exe");
			if (0 == stricmp(szProcessName, pszExaminedProcessName))
			{
				bProcessProtected = TRUE;
				return FALSE;
			} // if
			dwStartPos += nCommaPos + 1;
		} // while
	} // if
	if (!bHoolAll)
	{
		dwStartPos = 0;
		while ( (dwStartPos < strlen(m_szProcessesForHooking)) &&
		        GetNextCommaSeparatedString(
					&m_szProcessesForHooking[dwStartPos],
					szProcessName,
					sizeof(szProcessName),
					&nCommaPos
					) 
		      )
		{
			strcat(szProcessName, ".exe");
			if (0 == stricmp(szProcessName, pszExaminedProcessName))
				return TRUE;
			dwStartPos += nCommaPos + 1;
		} // while
		return FALSE;
	} // if

	return TRUE;
}

//
// Return the name of the INI file
//
void CInjector::GetIniFile(char* pszIniFile)
{
	char  *pdest;
	::GetModuleFileName(
		ModuleFromAddress(GetMsgProc), 
		pszIniFile, 
		MAX_PATH
		);
	pdest = &pszIniFile[strlen(pszIniFile) - 4];
	strcpy(pdest, ".ini");
}


//
// Get the value of [Scope] / HookAll from the INI file
//
BOOL CInjector::GetHookAllEnabled()
{
	if (!m_bHookAllEnabledInitialized)
	{
		char szIniFile[MAX_PATH];
		GetIniFile(szIniFile);
		CIniFile iniFile(szIniFile);
		m_bHookAllEnabled = iniFile.ReadBool(
			"Scope",
			"HookAll",
			TRUE
			);
		m_bHookAllEnabledInitialized = TRUE;
		strcpy(m_szProcessesForHooking, "\0");
		strcpy(m_szProtectedProcesses, "\0");
		//
		// Should we process the two lists with processes for hooking
		// and ones that shouldn't be hooked up at all
		//
		if (!m_bHookAllEnabled)
			iniFile.ReadString(
				"Scope",
				"Hook",
				"\0",
				m_szProcessesForHooking
				);
		else
			iniFile.ReadString(
				"Scope",
				"Protect",
				"\0",
				m_szProtectedProcesses
				);
	} // if

	return m_bHookAllEnabled;
}


//---------------------------------------------------------------------------
//
// class CWinHookInjector  
//
//---------------------------------------------------------------------------

HHOOK* CWinHookInjector::sm_pHook = NULL;

CWinHookInjector::CWinHookInjector(BOOL bServerInstance, HHOOK* pHook):
	CInjector(bServerInstance)
{
	sm_pHook = pHook;
}


//
// Inject the DLL into all running processes
//
BOOL CWinHookInjector::InjectModuleIntoAllProcesses()
{
	*sm_pHook = ::SetWindowsHookEx(
		WH_GETMESSAGE,
		(HOOKPROC)(GetMsgProc),
		ModuleFromAddress(GetMsgProc), 
		0
		);
		
	return (NULL != *sm_pHook);
}

//
// Eject the DLL from all processes if it has been injected before 
//
BOOL CWinHookInjector::EjectModuleFromAllProcesses(HANDLE hWaitOn)
{
	BOOL bResult;
	if (NULL != *sm_pHook)
	{
		bResult = ::UnhookWindowsHookEx(*sm_pHook);
		// Wait on the reference counter event handle 
		// until all the instances of the DLL get unloaded
		::WaitForSingleObject(hWaitOn, INFINITE);
	}
	else 
		bResult = TRUE;
	return bResult;
}


//---------------------------------------------------------------------------
//
// class CRemThreadInjector  
//
//---------------------------------------------------------------------------

CCSWrapper  CRemThreadInjector::sm_CritSecInjector;


//
//
//
CRemThreadInjector::CRemThreadInjector(BOOL bServerInstance):
	CInjector(bServerInstance),
	m_pNtInjectorThread(NULL)
{
	if (bServerInstance)
		m_pNtInjectorThread = new CNtInjectorThread(this);
}

//
//
//
CRemThreadInjector::~CRemThreadInjector()
{
	if (m_bServerInstance)
	{
		m_pNtInjectorThread->SetActive(FALSE);
		delete m_pNtInjectorThread;
	} // if
}

//
// Attempts to enable SeDebugPrivilege. This is required by use of
// CreateRemoteThread() under NT/2K
//
BOOL CRemThreadInjector::EnableDebugPrivilege()
{
	HANDLE           hToken;
	LUID             sedebugnameValue;
	TOKEN_PRIVILEGES tp;

	if ( !::OpenProcessToken( 
		GetCurrentProcess(),
		TOKEN_ADJUST_PRIVILEGES | // to adjust privileges
		TOKEN_QUERY,              // to get old privileges setting
		&hToken 
		) )
		//
		// OpenProcessToken() failed
		//
		return FALSE;
	//
	// Given a privilege's name SeDebugPrivilege, we should locate its local LUID mapping.
	//
	if ( !::LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue ) )
	{
		//
		// LookupPrivilegeValue() failed
		//
		::CloseHandle( hToken );
		return FALSE;
	}

	tp.PrivilegeCount = 1;
	tp.Privileges[0].Luid = sedebugnameValue;
	tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

	if ( !::AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(tp), NULL, NULL ) )
	{
		//
		// AdjustTokenPrivileges() failed
		//
		::CloseHandle( hToken );
		return FALSE;
	}

	::CloseHandle( hToken );
	return TRUE;
}



//
// Inject the DLL into all running processes
//
BOOL CRemThreadInjector::InjectModuleIntoAllProcesses()
{
	BOOL                bResult = FALSE;

	if (m_bServerInstance)
	{
		CTaskManager        taskManager;
		CExeModuleInstance* pProcess; 

		taskManager.Populate(FALSE);
		EnableDebugPrivilege();
		for (long i = 0; i < taskManager.GetProcessCount(); i++)
		{
			pProcess = taskManager.GetProcessByIndex(i);
			bResult = InjectModuleInto( pProcess->Get_ProcessId() ) || bResult; 
		} // for

		m_pNtInjectorThread->SetActive(TRUE);
	} // if

	return bResult;
}

//
// Eject the DLL from all processes if it has been injected before 
//
BOOL CRemThreadInjector::EjectModuleFromAllProcesses(HANDLE hWaitOn)
{
	BOOL                bResult = FALSE;

	if (m_bServerInstance)
	{
		CTaskManager        taskManager;
		CExeModuleInstance* pProcess; 

		m_pNtInjectorThread->SetActive(FALSE);
		taskManager.Populate(FALSE);

		for (long i = 0; i < taskManager.GetProcessCount(); i++)
		{
			pProcess = taskManager.GetProcessByIndex(i);
			bResult = EjectModuleFrom( pProcess->Get_ProcessId() ) || bResult; 
		} // for
	} // if

	return bResult;
}

//
// Inject the DLL into address space of a specific external process
//
BOOL CRemThreadInjector::InjectModuleInto(DWORD dwProcessId)
{
	CLockMgr<CCSWrapper> lockMgr(sm_CritSecInjector, TRUE);	
	BOOL                 bResult  = FALSE; 
	//
	// We shouldn't inject the dll into the address space of the
	// server
	//
	if (dwProcessId != ::GetCurrentProcessId())
	{
		CTaskManager         taskManager;
		taskManager.PopulateProcess(dwProcessId, TRUE);
		CExeModuleInstance  *pProcess = taskManager.GetProcessById(dwProcessId);
		if (TRUE == IsProcessForHooking(pProcess->GetBaseName()))
			bResult = DoInjectModuleInto(pProcess);
	} // if

	return bResult;
}

//
// Eject the DLL from the address space of an external process
//
BOOL CRemThreadInjector::EjectModuleFrom(DWORD dwProcessId)
{
	CLockMgr<CCSWrapper> lockMgr(sm_CritSecInjector, TRUE);	
	BOOL   bResult  = FALSE;
	//
	// We shouldn't eject the dll into the address space of the
	// server
	//
	if (dwProcessId != ::GetCurrentProcessId())
	{
		CTaskManager         taskManager;
		taskManager.PopulateProcess(dwProcessId, TRUE);
		CExeModuleInstance  *pProcess = taskManager.GetProcessById(dwProcessId);
		if (pProcess)
			bResult  = DoEjectModuleFrom(*pProcess);
	} // if
	return bResult;
}

//
// Execute injection mechanism for NT/2K systems
//
BOOL CRemThreadInjector::DoInjectModuleInto(CExeModuleInstance *pProcess)
{
	BOOL   bResult          = FALSE; 
	HANDLE hProcess         = NULL;
	HANDLE hThread          = NULL;
	PSTR   pszLibFileRemote = NULL;
	__try 
	{
		if (TRUE == IsWindows9x())
			__leave;
		if (NULL == pProcess)
			__leave;

		char szLibFile[MAX_PATH];
		::GetModuleFileNameA(
			ModuleFromAddress(GetMsgProc), 
			szLibFile, 
			MAX_PATH
			);

		BOOL bFound = FALSE;
		CModuleInstance* pModuleInstance;
		for (long i = 0; i < pProcess->GetModuleCount(); i++)
		{
			pModuleInstance = pProcess->GetModuleByIndex(i);
			if ( 0 == stricmp(pModuleInstance->Get_Name(), szLibFile) )
			{
				bFound = TRUE;
				break;
			} // if
		} // for
		if (bFound) 
			__leave;

		// Get a handle for the process we want to inject into
		hProcess = ::OpenProcess(
			PROCESS_ALL_ACCESS, // Specifies all possible access flags
			FALSE, 
			pProcess->Get_ProcessId()
			);
		if (hProcess == NULL) 
			__leave;
		
		// Calculate the number of bytes needed for the DLL's pathname
		int cch = 1 + strlen(szLibFile);

		// Allocate space in the remote process for the pathname
		pszLibFileRemote = (PSTR)::VirtualAllocEx(
			hProcess, 
			NULL, 
			cch, 
			MEM_COMMIT, 
			PAGE_READWRITE
			);
		if (pszLibFileRemote == NULL) 
			__leave;
		// Copy the DLL's pathname to the remote process's address space
		if (!::WriteProcessMemory(
			hProcess, 
			(PVOID)pszLibFileRemote, 
			(PVOID)szLibFile, 
			cch, 
			NULL)) 
			__leave;
		// Get the real address of LoadLibraryW in Kernel32.dll
		PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
			::GetProcAddress(::GetModuleHandle("Kernel32"), "LoadLibraryA");
		if (pfnThreadRtn == NULL) 
			__leave;
		// Create a remote thread that calls LoadLibraryW(DLLPathname)
		hThread = ::CreateRemoteThread(
			hProcess, 
			NULL, 
			0, 
			pfnThreadRtn, 
			(PVOID)pszLibFileRemote, 
			0, 
			NULL
			);
		if (hThread == NULL) 
			__leave;
		// Wait for the remote thread to terminate
		::WaitForSingleObject(hThread, INFINITE);

		bResult = TRUE; 
	}
	__finally 
	{ 
		// Free the remote memory that contained the DLL's pathname
		if (pszLibFileRemote != NULL) 
			::VirtualFreeEx(hProcess, (PVOID)pszLibFileRemote, 0, MEM_RELEASE);

		if (hThread  != NULL) 
			::CloseHandle(hThread);

		if (hProcess != NULL) 
			::CloseHandle(hProcess);
	}
	return bResult;
}

//
// Perform actual ejection of the DLL from the address space of an external process
//
BOOL CRemThreadInjector::DoEjectModuleFrom(CExeModuleInstance& process)
{
	BOOL   bResult  = FALSE; 
	HANDLE hProcess = NULL; 
	HANDLE hThread  = NULL;
	__try 
	{
		if (TRUE == IsWindows9x())
			__leave;
		//
		// Do not force the server to eject the DLL
		//
		if (process.Get_ProcessId() == ::GetCurrentProcessId())
			__leave;

		char szLibFile[MAX_PATH];
		::GetModuleFileNameA(
			ModuleFromAddress(GetMsgProc), 
			szLibFile, 
			MAX_PATH
			);

		BOOL bFound = FALSE;
		CModuleInstance* pModuleInstance;
		for (long i = 0; i < process.GetModuleCount(); i++)
		{
			pModuleInstance = process.GetModuleByIndex(i);
			if ( 0 == stricmp(pModuleInstance->Get_Name(), szLibFile) )
			{
				bFound = TRUE;
				break;
			} // if
		} // for

		if (!bFound) 
			__leave;

		// Get a handle for the target process.
		hProcess = ::OpenProcess(
			PROCESS_ALL_ACCESS, // Specifies all possible access flags
			FALSE, 
			process.Get_ProcessId()
			);
		if (hProcess == NULL) 
			__leave;
		// Get the real address of LoadLibraryW in Kernel32.dll
		PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
			::GetProcAddress(::GetModuleHandle("Kernel32"), "FreeLibrary");
		if (pfnThreadRtn == NULL) 
			__leave;
		// Create a remote thread that calls LoadLibraryW(DLLPathname)
		hThread = ::CreateRemoteThread(
			hProcess, 
			NULL, 
			0, 
			pfnThreadRtn, 
			pModuleInstance->Get_Module(), 
			0, 
			NULL
			);
		if (hThread == NULL) 
			__leave;
		// Wait for the remote thread to terminate
		::WaitForSingleObject(hThread, INFINITE);

		bResult = TRUE; 
	}
	__finally 
	{ 
		if (hThread != NULL) 
			::CloseHandle(hThread);
		if (hProcess != NULL) 
			::CloseHandle(hProcess);
	}
	return bResult;
}

//----------------------------End of the file -------------------------------