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 );
}