www.pudn.com > remote_thread.zip > RemoteRun.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 "RemoteRunLib.h" 
#include "spec_apis.h" 
#include "dll_load.h" 
#include "exe_load.h" 
#include "decimage.h" 
#include "remote.h" 
 
//--------------------------------------------------- 
// 控制加载方式 
// 指定RM_DEBUG_IMAGE将不隐藏被加载的exe文件 
// 指定RM_DEBUG_LIBRARY将不隐藏remote.dll模块 
// 如果指定RM_DEBUG_IMAGE,则相同的exe不能加载两次 
//--------------------------------------------------- 
#define RM_DEBUG_IMAGE			1 
#define RM_DEBUG_LIBRARY		2 
 
 
//--------------------------------------- 
// RemoteCall传递改结构给RemoteEntry函数 
//--------------------------------------- 
typedef struct _tagRemoteRunStruct { 
	DWORD	cbSize;			// = sizeof(RemoteRunStruct); 
	DWORD	dwFlags;		// RM_DEBUG_IMAGE和RM_DEBUG_LIBRARY的组合 
	int		nCmdShow;		// 传递给Exe的入口函数WinMain 
	CHAR	szAppPath[1];	// 被加载exe的路径 
}RemoteRunStruct, *PRemoteRunStruct; 
 
 
//------------------------------------ 
// 把exe文件所在路径设为当前路径 
//------------------------------------ 
void SetCurrentDir( LPSTR pszAppPath ) 
{ 
	LPSTR p = pszAppPath+lstrlenA(pszAppPath); 
	while( p > pszAppPath && *p != '\\' ) p--; 
	if( p >= pszAppPath ) 
	{ 
		*p = '\0'; 
		::SetCurrentDirectoryA( pszAppPath ); 
		*p = '\\'; 
	} 
} 
 
 
//----------------------------------------------------------- 
// 为VirtualFree做一个包装,提供跟FreeLibrary一致的调用接口 
//----------------------------------------------------------- 
static BOOL WINAPI FreeImage( HMODULE hModule ) 
{ 
	return VirtualFree( hModule, 0, MEM_RELEASE ); 
} 
 
 
//------------------------------------------------ 
// 完成加载目标exe文件,解决IAT和重定位表。 
// 然后调用目标exe的入口函数WinMain 
//------------------------------------------------ 
static DWORD WINAPI RealRun( PVOID pParam ) 
{ 
	//------------------------------------- 
	// 增加remote.dll锁定计数 
	//------------------------------------- 
	CRemoteRunLib::AddRef(); 
 
	DWORD retval = 0; 
	PRemoteRunStruct pRR = (PRemoteRunStruct)pParam; 
	HMODULE hImage = NULL; 
	BOOL (WINAPI *fnFreeModule)(HMODULE) = NULL; 
 
	__try{ 
	__try{ 
		LPSTR lpszAppPath = pRR->szAppPath; 
		LPSTR lpszCmdLine = (LPSTR)(lpszAppPath+lstrlenA(lpszAppPath)+sizeof(CHAR)); 
		int nCmdShow = pRR->nCmdShow; 
		BOOL fTearoffImage = TRUE; 
 
		if( pRR->dwFlags & RM_DEBUG_IMAGE ) 
		{ 
			//------------------------------------------ 
			// 用户不想隐藏exe 
			//------------------------------------------ 
			fTearoffImage = FALSE; 
		} 
 
		//------------------------------------------------------------------------- 
		// 设定起始目录为exe所在目录,有些exe需要从那里加载一些DLL,如acrobat 5.0 
		//------------------------------------------------------------------------- 
		SetCurrentDir( lpszAppPath ); 
 
 
		 
		//---------------------------------------------------------------- 
		// 加载目标exe文件 
		//---------------------------------------------------------------- 
		 
 
#define FIXUP_MANUALLY 
#ifndef FIXUP_MANUALLY 
		//---------------------------------------------------------------- 
		// 通常情况下,用LoadLibrary加载exe,OS不会解决IAT和重定位表。 
		// LoadEXEA通过把exe文件标识为dll(通过修改pe结构中的image特征), 
		// 让OS以为这是一个DLL,从而OS自己为我们解决IAT和重定位表。 
		//---------------------------------------------------------------- 
		InitializeEXELoad(); 
		hImage = LoadEXEA( lpszAppPath ); 
		fnFreeModule = FreeImage; 
 
#else 
		//------------------------------------------------------------------------------ 
		// 用LoadLibrary加载exe。其实无论指不指定DONT_RESOLVE_DLL_REFERENCES,OS都不会 
		// 为我们解决IAT和重定位表,这里加上DONT_RESOLVE_DLL_REFERENCES是为了清楚起见。 
		//------------------------------------------------------------------------------ 
		hImage = ::LoadLibraryExA( lpszAppPath, NULL, DONT_RESOLVE_DLL_REFERENCES ); 
		fnFreeModule = FreeLibrary; 
 
 
		//------------------------------------------------------- 
		// 如果没有重定位信息,且不能加载到缺省基址,则调用失败 
		//------------------------------------------------------- 
		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("RemoteCall error"), MB_OK ); 
			__leave; 
		} 
 
 
		 
		//------------------------------------------------ 
		// 既然OS不为我们解决IAT和重定位表,那就自己来吧。 
		// 注意,如果用了LoadEXE而不是LoadLibrary,就不需要 
		// 这几句了。 
		//------------------------------------------------ 
		if( !FixupImage( hImage ) ) 
			__leave; 
#endif // FIXUP_MANUALLY 
 
		 
		if( hImage == NULL ) 
			__leave; 
 
		//----------------------------------------------- 
		// 目前还不能处理存在.tls section的exe 
		//----------------------------------------------- 
//		__asm int 3; 
		if( CheckTlsSectionExist( hImage ) ) 
		{ 
			::MessageBox( NULL, _T("Sorry, this exe has a \".tls\"(Thread Local Storage) section which I can not support now."), _T("RemoteRun error"), MB_OK ); 
			__leave; 
		} 
 
 
		//----------------------------------------------------------------- 
		// 让image脱离文件运行。这时用EnumProcessModules也得不到文件名。 
		// 注意,DecorticateImage内部使用FreeLibrary卸载hImage, 
		// 如果前面加载模块用的是LoadEXE,必须把FreeLibrary替换为FreeEXE。 
		//----------------------------------------------------------------- 
		if( fTearoffImage ) 
		{ 
			DecorticateImage( hImage, UNLOAD_MODULE, fnFreeModule ); 
			fnFreeModule = FreeImage; 
		} 
 
 
		//----------------------------------------------------------------- 
		// 注册加载模块 
		// 如果本线程或从本线程派生出来的子线程以NULL或hImage值 
		// 为参数调用GetModuleFileName,必须返回该exe的文件名 
		//----------------------------------------------------------------- 
		CRemoteRunLib::RegisterEXE( hImage, lpszAppPath ); 
 
 
 
		typedef int (WINAPI *PFNWinMain)(HINSTANCE hInstance, 
										HINSTANCE hPrevInstance, 
									   PSTR szCmdLine, 
									   int iCmdShow); 
 
		poh=(PIMAGE_OPTIONAL_HEADER) OPTHDROFFSET(hImage); 
 
		//----------------------------- 
		// 计算exe入口点 
		//----------------------------- 
		PFNWinMain fnWinMain=(PFNWinMain) RVATOVA(hImage, poh->AddressOfEntryPoint); 
 
		 
		__try { 
 
			//------------------------------------------------------ 
			// all ready, let's go 
			//------------------------------------------------------ 
			retval = fnWinMain( hImage, NULL, lpszCmdLine, nCmdShow); 
 
		}__except( (GetExceptionCode() == SE_KILLTHREAD) 
       ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ){} 
	}__finally{ 
		//--------------------------------------- 
		// Make sure to clean up 
		//--------------------------------------- 
		HeapFree( GetProcessHeap(), 0, pRR ); 
 
		//--------------------------------------- 
		// 卸载exe模块 
		//--------------------------------------- 
		if( hImage != NULL && fnFreeModule != NULL ) 
			fnFreeModule( hImage ); 
 
		 
		//--------------------------------------- 
		// 减少remote.dll锁定计数 
		//--------------------------------------- 
		CRemoteRunLib::Release(); 
	} 
	}__except(EXCEPTION_EXECUTE_HANDLER){ 
		::MessageBox( NULL, _T("An fatal error occur in process!"), _T("RemoteRun error"), MB_OK ); 
	} 
 
	return retval; 
} 
 
 
//------------------------------------------------- 
// RemoteRun指定给RemoteCall的入口点 
//------------------------------------------------- 
static DWORD WINAPI RemoteEntry( PVOID pParam ) 
{ 
	PRemoteRunStruct pRR = (PRemoteRunStruct)pParam; 
 
	//-------------------------------------------------------------------- 
	// 以-1为参数调用GetModuleFileName,如果他已经被我们Detour, 
	// 则说明remote.dll已经加载过,他会返回remote.dll的base address 
	//-------------------------------------------------------------------- 
	HMODULE hRemote = (HMODULE)::GetModuleFileName( (HMODULE)-1, NULL, 0 ); 
	if( hRemote == NULL ) 
	{ 
		//------------------------------------------ 
		// remote.dll第一次在该进程中加载 
		//------------------------------------------ 
 
 
		//--------------------------------------------- 
		// for debug only 
		//--------------------------------------------- 
//		__asm int 3; 
 
		BOOL isDebugMode = (pRR->dwFlags & RM_DEBUG_LIBRARY) != 0; 
 
		//--------------------------------------------- 
		// 做一些初始化。主要是维护一个已加载exe列表, 
		// 以及截获一些必要的API 
		//--------------------------------------------- 
		if( !CRemoteRunLib::InitializeOnce( g_hInstance, ::GetModuleHandle(NULL), isDebugMode ) ) 
			return -1; 
 
 
		//------------------------------------------------------------ 
		// 在全局堆中分配一块内存,保存需要的参数信息。 
		// 内存分配方法和下面的保持一致。 
		//------------------------------------------------------------ 
		PBYTE pb = (PBYTE)HeapAlloc(GetProcessHeap(), 0, pRR->cbSize ); 
		memcpy( pb, pRR, pRR->cbSize ); 
 
		//------------------------------------------------------------------- 
		// 释放远程分配出来的代码和数据区域。通过VirtualQuery找到分配基址。 
		// 这些代码和数据不会再用到了,因为我们不会从这个函数中返回了 
		//------------------------------------------------------------------- 
		MEMORY_BASIC_INFORMATION mbi; 
		if( ::VirtualQuery( pParam, &mbi, sizeof(mbi) ) != 0 ) 
			VirtualFree( mbi.AllocationBase, 0, MEM_RELEASE ); 
 
		 
		//-------------------------- 
		// 来吧,让我们把exe跑起来 
		//-------------------------- 
		RealRun( pb ); 
 
 
		//-------------------------- 
		// 减少锁定计数 
		//-------------------------- 
		CRemoteRunLib::Release(); 
 
		//------------------------------------------------- 
		// 退出线程,不要让后面的代码卸载掉remote.dll 
		//------------------------------------------------- 
		ExitThread( 0 ); 
	} 
	else 
	{ 
		//---------------------------------------- 
		// remote.dll已经被加载过 
		//---------------------------------------- 
 
 
		//----------------------------------------- 
		// 找出第一个remote.dll模块中的RealRun地址 
		//----------------------------------------- 
		typedef DWORD (WINAPI *PFN_RealRun)(PVOID); 
		PFN_RealRun fnRealRun = (PFN_RealRun)((DWORD)RealRun - (DWORD)g_hInstance + (DWORD)hRemote); 
 
		//------------------------------------------------------------- 
		// 这里不能用new分配内存,因为后面要把这第二个remote.dll卸载掉。 
		// 那时所有new出来的内存都会被释放,但我们还要用他呢。 
		//------------------------------------------------------------- 
		PBYTE pb = (PBYTE)HeapAlloc(GetProcessHeap(), 0, pRR->cbSize ); 
		memcpy( pb, pRR, pRR->cbSize ); 
 
 
		//---------------------------------------------- 
		// 启动一个线程来运行RealRun,这个线程将被销毁 
		//---------------------------------------------- 
		DWORD tid; 
		HANDLE hThread = ::CreateThread( NULL, 0, fnRealRun, pb, 0, &tid ); 
 
		if( hThread != NULL ) 
			::CloseHandle( hThread ); 
	} 
	return 0; 
} 
 
 
//----------------------------------------------------------- 
// RemoteRunExA让一个exe在一个指定的进程里运行。 
// 这个必须可重定位。 
// 参数说明: 
// processid	-- 目标进程ID 
// lpszAppPath	-- exe的完整路径 
// lpszCmdLine	-- 这个参数将传递给exe的入口函数WinMain 
// nCmdShow		-- 这个参数将传递给exe的入口函数WinMain 
// dwFlags		-- 指定目标exe和remote.dll的加载方式,参见 
//					RM_DEBUG_IMAGE和RM_DEBUG_LIBRARY的说明 
//----------------------------------------------------------- 
extern "C" 
BOOL WINAPI RemoteRunExA( DWORD processId, LPCSTR lpszAppPath, LPCSTR lpszCmdLine, int nCmdShow, DWORD dwFlags ) 
{ 
	BOOL retval = FALSE; 
	DWORD dwParamSize; 
	RemoteRunStruct* pRR; 
 
	if( lpszAppPath == NULL || lpszAppPath[0] == '\0' ) return FALSE; 
 
	//-------------------------------------------------- 
	// 构造一个RemoteRunStruct结构,RemoteCall将把他 
	// 传递给RemoteEntry 
	//-------------------------------------------------- 
	dwParamSize = sizeof(RemoteRunStruct); 
	dwParamSize += (lstrlenA( lpszAppPath )+1); 
 
	dwParamSize++; 
	if( lpszCmdLine != NULL ) 
		dwParamSize += lstrlenA( lpszCmdLine ); 
 
	BYTE *pb = new BYTE[dwParamSize]; 
	if( pb == NULL ) return FALSE; 
 
	pRR = (PRemoteRunStruct)pb; 
	 
	pRR->cbSize = dwParamSize; 
	pRR->dwFlags = dwFlags; 
	pRR->nCmdShow = nCmdShow; 
	lstrcpyA( pRR->szAppPath, lpszAppPath ); 
 
	LPSTR psz = (LPSTR) (pRR->szAppPath+lstrlenA(lpszAppPath)+sizeof(CHAR)); 
	if( lpszCmdLine == NULL ) 
		psz[0] = '\0'; 
	else 
		lstrcpyA(psz, lpszCmdLine ); 
 
 
	//-------------------------------------------------------------------- 
	// for debug only 
	//-------------------------------------------------------------------- 
//	pRR->dwFlags |= RM_DEBUG_LIBRARY; 
//	pRR->dwFlags |= RM_DEBUG_IMAGE; 
 
	 
	//-------------------------------------------------------------------- 
	// Let's go...... 
	//-------------------------------------------------------------------- 
	retval = RemoteCall( processId, RemoteEntry, pRR, dwParamSize, FALSE ); 
 
 
	delete [] pb; 
	return retval; 
} 
 
 
 
//---------------------------------------------------------------------- 
// RemoteRunExW,RemoteRunA和RemoteRunW做一些必要的转换, 
// 最终调用RemoteRunExA 
//---------------------------------------------------------------------- 
extern "C" 
BOOL WINAPI RemoteRunExW( DWORD processId, LPCWSTR lpszAppPath, LPCWSTR lpszCmdLine, int nCmdShow, DWORD dwFlags ) 
{ 
	CHAR szAppPath[MAX_PATH], szCmdLine[MAX_PATH+100]; 
	CHAR *pszCmdLine = NULL; 
 
	if( lpszAppPath == NULL || lpszAppPath[0] == L'\0' ) return FALSE; 
 
	::WideCharToMultiByte( CP_ACP, 0, 
		lpszAppPath, -1, 
		szAppPath, sizeof(szAppPath), 
		NULL, NULL ); 
 
	if( lpszCmdLine != NULL ) 
	{ 
		::WideCharToMultiByte( CP_ACP, 0, 
			lpszCmdLine, -1, 
			szCmdLine, sizeof(szCmdLine), 
			NULL, NULL ); 
		pszCmdLine= szCmdLine; 
	} 
	return RemoteRunExA( processId, szAppPath, pszCmdLine, nCmdShow, dwFlags ); 
} 
 
extern "C" 
BOOL WINAPI RemoteRunA( DWORD processId, LPCSTR lpszAppPath, LPCSTR lpszCmdLine, int nCmdShow ) 
{ 
	return RemoteRunExA( processId, lpszAppPath, lpszCmdLine, nCmdShow, 0 ); 
} 
 
 
//---------------------------------------------- 
// RemoteRun内部采用ANSI版本,为了节省内存。 
//---------------------------------------------- 
extern "C" 
BOOL WINAPI RemoteRunW( DWORD processId, LPCWSTR lpszAppPath, LPCWSTR lpszCmdLine, int nCmdShow ) 
{ 
	return RemoteRunExW( processId, lpszAppPath, lpszCmdLine, nCmdShow, 0 ); 
}