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