www.pudn.com > remote_thread.zip > Remotexec.cpp


/*  Remote Run Library 
    Copyright (C) 2001, Sting Feng(冯越) 
 
    This program is free software; you can redistribute it and/or modify 
    it under the terms of the GNU General Public License as published by 
    the Free Software Foundation; either version 2 of the License, or 
    (at your option) any later version. 
 
    This program is distributed in the hope that it will be useful, 
    but WITHOUT ANY WARRANTY; without even the implied warranty of 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    GNU General Public License for more details. 
 
    You should have received a copy of the GNU General Public License 
    along with this program; if not, write to the Free Software 
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 
	The author of this program may be contacted at fenny@163.net. */ 
 
 
#include  
#include  
#include "remote.h" 
#include "dll_load.h" 
 
typedef HMODULE (WINAPI *PFNLoadLibraryA)(LPSTR); 
typedef BOOL    (WINAPI *PFNFreeLibrary)(HMODULE); 
typedef VOID	(WINAPI *PFNExitThread)(DWORD); 
typedef BOOL	(WINAPI *PFNVirtualFree)(LPVOID,SIZE_T,DWORD); 
 
 
enum { MAXINJECTSIZE = 1024 }; 
 
 
//--------------------------------------- 
// RemoteThreadProc要用到API,在这里提供 
//--------------------------------------- 
typedef struct _tagKernel32Lib { 
	PFNLoadLibraryA		fnLoadLibraryA; 
	PFNFreeLibrary		fnFreeLibrary; 
	PFNExitThread		fnExitThread; 
	PFNVirtualFree		fnVirtualFree; 
}Kernel32Lib, *PKernel32Lib; 
 
 
typedef struct _tagFunctionBlock { 
	// kernel32.dll基址 
	Kernel32Lib			Kernel32Lib; 
	 
	HMODULE				hInstance; 
	 
	// 用户指定函数的返回值 
	DWORD				dwReturnCode; 
	 
	// 如果有错误,在这里存放 
	DWORD				dwLastError; 
	 
	// remote.dll的全路径 
	char				szLibPath[MAX_PATH]; 
	 
	// 远程运行函数所在文件的全路径 
	char				szImagePath[MAX_PATH]; 
 
	// 远程运行函数的相对地址 
	DWORD				rvaFunction; 
	 
	// 同步标记,指示RemoteCall的调用者是否应该等待远程调用结束再返回 
	BOOL				fSyncronize; 
 
	// 远程运行函数需要的参数 
	DWORD				dwData; 
}FUNCTIONBLOCK, *PFUNCTIONBLOCK; 
 
HMODULE g_hInstance = NULL; 
 
 
//----------------------------------------------------- 
// 获取目标函数的真正入口地址。 
// 在debug版中,有些函数的地址实际上是相应的跳转表地址 
// 我们用这个跳转表地址得到真正的入口地址 
//----------------------------------------------------- 
static PVOID GetFuncAddress(PVOID addr) 
{ 
#ifdef _DEBUG 
	//check if instruction is relative jump (E9) 
	if (0xE9 != *((UCHAR*)addr)) 
		return addr; 
	// calculate base of relative jump 
	ULONG base = (ULONG)((UCHAR*)addr + 5); 
	// calculate offset  
	ULONG *offset = (ULONG*)((UCHAR*)addr + 1); 
	return (PVOID)(base + *offset); 
#else 
	// in release, don't have to mess with jumps 
	return addr; 
#endif 
} 
 
 
//------------------------------------------------------ 
// 解决掉IAT(Import Address Table)和重定位表 
//------------------------------------------------------ 
BOOL FixupImage( HMODULE hImageBase ) 
{ 
	if( hImageBase == NULL ) 
		return FALSE; 
 
	PIMAGE_OPTIONAL_HEADER poh; 
	poh=(PIMAGE_OPTIONAL_HEADER) OPTHDROFFSET(hImageBase); 
	 
	 
	//------------------------------------------------------ 
	// 在模块中做一点记号,防止IAT和重定位表被再次修正 
	//------------------------------------------------------ 
	DWORD newProtect, oldProtect; 
	newProtect = PAGE_EXECUTE_WRITECOPY; 
	BOOL fHeaderWritable = VirtualProtect(hImageBase,poh->SizeOfImage,newProtect,&oldProtect); 
	if( !fHeaderWritable ) 
		ProtectDLLImage( hImageBase, TRUE ); 
	 
	 
	//----------------------------------- 
	// 来吧,让我们修理修理他 
	//----------------------------------- 
	if(	PrepareDLLImage( hImageBase, poh->SizeOfImage, TRUE, TRUE ) ) 
	{ 
		if( fHeaderWritable || 
			VirtualProtect(hImageBase,(DWORD)&poh->ImageBase-(DWORD)poh+1,newProtect,&oldProtect) ) 
		{ 
			poh->ImageBase = (DWORD)hImageBase; 
		} 
 
		//----------------------------------- 
		// 对DLL实行保护 
		//----------------------------------- 
		ProtectDLLImage( hImageBase, FALSE ); 
		return TRUE; 
	} 
 
	::MessageBox( NULL, _T("fixup image fail!"), _T("error"), MB_OK ); 
	return FALSE; 
} 
 
 
//------------------------------------------------- 
// 进入remote.dll环境。在这里,我们不再受限制, 
// 可以为所欲为 
//------------------------------------------------- 
static DWORD WINAPI RealCall( PFUNCTIONBLOCK pblk ) 
{ 
	HMODULE hImage = NULL; 
	__try { 
		hImage = ::LoadLibraryA( pblk->szImagePath ); 
		if( hImage == NULL ) __leave; 
		__try { 
			PIMAGE_FILE_HEADER pfh; 
			PIMAGE_OPTIONAL_HEADER poh; 
			pfh = (PIMAGE_FILE_HEADER)PEFHDROFFSET(hImage); 
			poh=(PIMAGE_OPTIONAL_HEADER) OPTHDROFFSET(hImage); 
 
			 
			//------------------------------------------------------- 
			// 如果没有重定位信息,且不能加载到缺省基址,则调用失败 
			//------------------------------------------------------- 
			if( (pfh->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) 
			&& (DWORD)hImage != poh->ImageBase ) 
			{ 
				::MessageBox( NULL, _T("Sorry, this image cann't be rebased(lack of \".reloc\"(relocation information table) section.)"), _T("RemoteRun error"), MB_OK ); 
				__leave; 
			} 
 
			//------------------------------------------------------- 
			// 修正image,包括导入表和重定位信息 
			//------------------------------------------------------- 
			if( ( pfh->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) 
			&&	!(pfh->Characteristics & IMAGE_FILE_DLL) ) 
			{if( !FixupImage( hImage ) ) __leave;} 
 
 
			//------------------------------------------------------- 
			// 计算目标函数的虚拟地址 
			//------------------------------------------------------- 
			typedef DWORD	(WINAPI *PFNUserCall)(PVOID); 
			PFNUserCall fnUser = (PFNUserCall)( (DWORD)hImage + pblk->rvaFunction ); 
 
 
			//------------------------------------------------------- 
			// call user defined function 
			//------------------------------------------------------- 
			pblk->dwReturnCode = fnUser( &pblk->dwData ); 
 
 
			//------------------------------------------------------- 
			// 如果有错,保存错误号 
			//------------------------------------------------------- 
			pblk->dwLastError = GetLastError(); 
		}__finally {::FreeLibrary( hImage );} // 确保卸载 
	}__except(EXCEPTION_EXECUTE_HANDLER ){} 
 
	return 0; 
} 
 
static DWORD RemoteThreadProc( PFUNCTIONBLOCK pblk ) 
{ 
	HMODULE hLib; 
 
	//-------------------------------------------------------- 
	// 加载remote.dll 
	//-------------------------------------------------------- 
	hLib = pblk->Kernel32Lib.fnLoadLibraryA( pblk->szLibPath ); 
	if( hLib != NULL ) 
	{ 
		//--------------------------------- 
		// 计算remote.dll中RealCall的地址 
		//--------------------------------- 
		typedef (WINAPI *PFN_RealCall)(PFUNCTIONBLOCK); 
		PFN_RealCall fnRealCall = (PFN_RealCall)( (DWORD)RealCall - (DWORD)pblk->hInstance + (DWORD)hLib); 
 
		//--------------------------------- 
		// 走吧,自由就在前面 
		//--------------------------------- 
		fnRealCall( pblk ); 
 
 
		//--------------------------------- 
		// Game over,卸掉remote.dll 
		//--------------------------------- 
		pblk->Kernel32Lib.fnFreeLibrary( hLib ); 
	} 
	else 
		pblk->dwLastError = -1; 
 
	if( !pblk->fSyncronize ) 
	{ 
		//---------------------------------------------------- 
		// 用户选择异步方式调用 
		//---------------------------------------------------- 
 
		 
		PFNExitThread fnExitThread = pblk->Kernel32Lib.fnExitThread; 
		PFNVirtualFree fnVirtualFree = pblk->Kernel32Lib.fnVirtualFree; 
 
 
		//---------------------------------------------------- 
		// 释放自己后调用ExitThread结束线程。 
		// 下面的汇编代码相当于: 
		// VirtualFree( pblk, 0, MEM_RELEASE ); 
		// ExitThread( 0 ); 
		// 想想看,我们显然不能用常规写法。 
		//---------------------------------------------------- 
		__asm { 
			push 0;				// parameter of ExitThread 
			push 0;				// return address of ExitThread 
			push MEM_RELEASE;	// parameter of VirtualFree 
			push 0;				// parameter of VirtualFree 
			push pblk;			// parameter of VirtualFree 
			push fnExitThread;	// return address of VirtualFree 
			push fnVirtualFree; 
			ret;				// call VirtualFree 
		} 
	} 
	return 0; 
} 
 
 
//------------------------------------------------------------------------------------- 
//	RemoteCall用于在远程进程执行自己指定的代码。支持同步、异步调用。 
//	可以使用全局变量,字符串常量等等。和在本地调用几乎没有区别。 
//	参数说明: 
//	processId -- target process ID 
//	pfnAddr		-- function entrypoint you want to execute in target process 
//	pParam		-- parameter pass to function 
//	cbParamSize	-- size of parameter block 
//	fSyncronize	-- syncronize flag 
//	注意:pfnAddr必须声明为__stdcall调用,不能是__cdecl,否则会导致函数返回后堆栈错误 
//------------------------------------------------------------------------------------- 
extern "C" 
BOOL WINAPI RemoteCall( DWORD processId, DWORD (__stdcall *pfnAddr)(PVOID), PVOID pParam, DWORD cbParamSize, BOOL fSyncronize ) 
{ 
	// open the process 
	HANDLE hProcess = ::OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | 
			PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, processId ); 
	 
	if ( hProcess == NULL ) 
		return FALSE; 
 
	HANDLE ht = 0; 
	PBYTE p, c, pLocalCopy = NULL; 
	DWORD result = -1; 
	DWORD rc, cbFrameCodeSize, cbDataBuffer; 
 
 
	p = c = NULL; 
	if( pParam == NULL ) 
		cbParamSize = 0; 
 
	__try 
	{ 
		cbFrameCodeSize = MAXINJECTSIZE; // (PBYTE)AfterRemoteThreadProc - (PBYTE)RemoteThreadProc; 
		cbDataBuffer = sizeof(FUNCTIONBLOCK) + cbParamSize; 
 
		 
		//----------------------------------------------------------- 
		// 在目标进程分配一段内存,供我们写入启动代码和必要的参数 
		//----------------------------------------------------------- 
		p = (PBYTE)::VirtualAllocEx( hProcess, 0, cbFrameCodeSize+cbDataBuffer, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); 
		c = p+cbDataBuffer; 
 
		pLocalCopy = new BYTE[ cbDataBuffer ]; 
		if( pLocalCopy == NULL ) 
			__leave; 
 
		PFUNCTIONBLOCK pblk = (PFUNCTIONBLOCK)pLocalCopy; 
 
		//-------------------------------------------------------- 
		// 为远程启动代码准备一些必要的API 
		// 这里有一个非常重要的假定:在所有进程中,Kernel32.dll 
		// 的加载基址都一样。 
		//-------------------------------------------------------- 
		HMODULE hKernel32 = ::GetModuleHandleW( L"Kernel32.dll" ); 
		pblk->Kernel32Lib.fnLoadLibraryA = (PFNLoadLibraryA)::GetProcAddress( hKernel32, 
													"LoadLibraryA" ); 
		pblk->Kernel32Lib.fnFreeLibrary = (PFNFreeLibrary)::GetProcAddress( hKernel32, 
													"FreeLibrary" ); 
 
		MEMORY_BASIC_INFORMATION mbi; 
		if( ::VirtualQuery( pfnAddr, &mbi, sizeof(mbi) ) == 0 ) 
			__leave; 
 
		HMODULE hModule = (HMODULE)mbi.AllocationBase; 
 
		//----------------------------------------- 
		// 远程运行函数所在模块的全路径名 
		//----------------------------------------- 
		::GetModuleFileNameA( hModule, pblk->szImagePath, sizeof(pblk->szImagePath)/sizeof(pblk->szImagePath[0]) ); 
 
		//----------------------------------------- 
		// Remote.dll的全路径名 
		//----------------------------------------- 
		::GetModuleFileNameA( g_hInstance, pblk->szLibPath, sizeof(pblk->szLibPath)/sizeof(pblk->szLibPath[0]) ); 
 
 
		//----------------------------------------- 
		// 函数的相对地址 
		//----------------------------------------- 
		pblk->rvaFunction = (DWORD)pfnAddr - (DWORD)hModule; 
 
		pblk->hInstance = g_hInstance; 
 
		if( pParam != NULL ) 
			memcpy( &pblk->dwData, pParam, cbParamSize ); 
 
 
		pblk->fSyncronize = fSyncronize; 
		if( !fSyncronize ) 
		{ 
			//------------------------------------- 
			// 对于异步调用,我们还需要这两个API 
			//------------------------------------- 
			pblk->Kernel32Lib.fnExitThread = (PFNExitThread)::GetProcAddress( hKernel32, 
													"ExitThread" ); 
			pblk->Kernel32Lib.fnVirtualFree =  (PFNVirtualFree)::GetProcAddress( hKernel32, 
													"VirtualFree" ); 
		} 
 
		//------------------------------------------------------------------ 
		// copy parameterblock to the other process adress space 
		//------------------------------------------------------------------ 
		if ( ! ::WriteProcessMemory( hProcess, p, pblk, cbDataBuffer, 0 ) ) 
			__leave; 
 
		//------------------------------------------------------------------ 
		// copy function there, we will execute this piece of code 
		//------------------------------------------------------------------ 
		if ( ! ::WriteProcessMemory( hProcess, c, GetFuncAddress(RemoteThreadProc), cbFrameCodeSize, 0 ) ) 
			__leave; 
 
 
		//------------------------------------------------------------------ 
		// all ready, let's go.................. 
		//------------------------------------------------------------------ 
		ht = ::CreateRemoteThread( hProcess, 0, 0, (DWORD (WINAPI *)( PVOID))c, p, 0, &rc ); 
		if ( ht == NULL ) 
			__leave; 
 
		if( fSyncronize ) 
		{ 
			//------------------------------ 
			// 同步调用方式 
			//------------------------------ 
 
			 
			//------------------------------ 
			// 等待远程线程结束 
			//------------------------------ 
			rc = ::WaitForSingleObject( ht, INFINITE ); 
 
			 
			//------------------------------ 
			// 读取调用结果 
			//------------------------------ 
			if ( ::ReadProcessMemory( hProcess, p, pblk, cbDataBuffer, 0 ) ) 
			{ 
				::SetLastError( pblk->dwLastError ); 
				result = pblk->dwReturnCode; // Get return code 
				memcpy( pParam, &pblk->dwData, cbParamSize ); 
			} 
		} 
	} 
	__finally 
	{ 
		//--------------------------------- 
		// 释放所有资源 
		//--------------------------------- 
 
		 
		if( pLocalCopy != NULL ) 
			delete [] pLocalCopy; 
		if( ht != NULL ) 
			::CloseHandle( ht ); 
		if( hProcess != NULL ) 
			::CloseHandle( hProcess ); 
 
		if( fSyncronize ) 
		{ 
			//-------------------------------------------- 
			// open the process again 
			// make sure that target process still exists. 
			//-------------------------------------------- 
			hProcess = ::OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | 
					PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, processId ); 
			if( hProcess != NULL ) 
			{ 
				//----------------- 
				// Let's clean 
				//----------------- 
				if ( p != NULL ) 
					::VirtualFreeEx( hProcess, p, 0, MEM_RELEASE ); 
			} 
		} 
	} 
 
	return result == 0; 
}