呕心沥血写就(试验代码写了一大坨),修改及转载请标明出处,谢谢!
感谢[jozu][ahakin][辟邪马甲]等高手指点。
GetWindowText等函数在win2k及以后系统上不能获得其它进程内的EDIT BOX内容,本文使用CreateRemoteThread来解决这个问题。
* 这不是唯一的解决方法,使用钩子等方法也可以解决这个问题。
* 即使使用CreateRemoteThread,那么注入自己的DLL会简单一箩筐,但我这里没有使用自己的DLL,所以这是一种很差的方法,代码移植性差、代码编写复杂、代码不容易看懂,等等,但它也有一个优点:不需要制作自己的DLL ^_^。
(不需要制作自己的DLL有另外一个大优点,不说)
平台:IBM兼容PC指令, Windows 2K pro
编译器:VC++6.0
说明:
1. 欲在目标进程内使用GetWindowText等函数,必先加载User32.dll。
* 如果目标进程早已自己加载了User32的话,再加载一次也无所妨 ^_^
2. 载入User32.dll需要用到LoadLibrary等,其存在于KERNEL32.DLL中。
* KERNEL32.DLL是内核模块,无需显式载入。
3. CreateRemoteThread可以自带一个参数,而LoadLibrary也只需要一个参数,因此可以使用一点点技巧令目标进程载入User32.dll。
* CreateRemoteThread( , , , &LoadLibraryA, 动态库名称, , );
4. CALL和JMP等指令后面的是相对偏移,因此代码中存在CALL和LONG JMP时需要重新调整相对偏移。
* CALL指令格式:
* Y : ……
* X : CALL Y
* 生成的指令就是
* E8, Y-X-5
* 可见CALL指令跟其所处地址是有关系的,因此将CALL Y移到新地址后要重新计算Y-X-5。
5. 因为要调整CALL后的相对便宜,因为我使用汇编来编写线程函数。
* 编译器不对汇编语句进行任何优化和调整
6. 在DEBUG下,自己定义的函数其函数地址处是个jmp,跳到真正的函数体处,所以不能简单的使用 &函数名称 来得到地址,但如果定义这个全局函数为static可以不生成jmp,可惜这里的代码写在类中,因为还是需要自己去计算真正的地址。
7. 此处使用"USER32"来令目标进程载入USER32.DLL,这不是一种保稳的做法。保稳的做法是使用全路径,令目标进程无法"作弊" ^_^
代码如下,没有过多测试
#include <windows.h>
#include <cassert>
class CRemoteEditBox
{
public:
static DWORD GetPIdFromCtrl( HWND hCtrl );
CRemoteEditBox( DWORD dwRemoteProcessId = 0 );
~CRemoteEditBox();
bool Open( DWORD dwRemoteProcessId );
void Close();
bool IsOpen() const;
int GetTextLength( HWND hEdit ) const;
bool GetText( HWND hEdit, char* pStr, int nLen ) const;
bool SetText( HWND hEdit, const char* pStr );
private:
static DWORD __stdcall _GetTextLength( LPVOID lpParam );
static DWORD __stdcall _GetText( LPVOID lpParam );
static DWORD __stdcall _SetText( LPVOID lpParam );
static VOID __stdcall _EndText( LPVOID lpParam );
private:
CRemoteEditBox( const CRemoteEditBox& );
CRemoteEditBox& operator=( const CRemoteEditBox& );
private:
DWORD PId;
HANDLE hProcess;
mutable void* pData;
void* pCode;
HINSTANCE hInst;
mutable size_t DataLen;
};
DWORD CRemoteEditBox::GetPIdFromCtrl( HWND hCtrl )
{
DWORD PId = 0;
GetWindowThreadProcessId( hCtrl, &PId );
return PId;
}
CRemoteEditBox::CRemoteEditBox( DWORD dwRemoteProcessId )
{
PId = 0;
hProcess = 0;
pData = 0;
pCode = 0;
hInst = 0;
DataLen = 0;
Open( dwRemoteProcessId );
}
CRemoteEditBox::~CRemoteEditBox()
{
Close();
}
__declspec(naked) DWORD __stdcall CRemoteEditBox::_GetTextLength( LPVOID lpParam )
{
__asm mov eax, [esp+4]
__asm push eax
__asm call GetWindowTextLengthA
__asm ret 4
}
__declspec(naked) DWORD __stdcall CRemoteEditBox::_GetText( LPVOID lpParam )
{
__asm mov eax, [esp+4]
__asm push [eax+4]
__asm add eax, 8
__asm push eax
__asm sub eax, 8
__asm push [eax]
__asm call GetWindowTextA
__asm ret 4
}
__declspec(naked) DWORD __stdcall CRemoteEditBox::_SetText( LPVOID lpParam )
{
__asm mov eax, [esp+4]
__asm add eax, 4
__asm push eax
__asm sub eax, 4
__asm push [eax]
__asm call SetWindowTextA
__asm ret 4
}
void __stdcall CRemoteEditBox::_EndText( LPVOID lpParam )
{
}
bool CRemoteEditBox::Open( DWORD dwRemoteProcessId )
{
Close();
if( 0 == dwRemoteProcessId ) return false;
PId = dwRemoteProcessId;
hProcess = ::OpenProcess( PROCESS_ALL_ACCESS, TRUE, PId );
if( 0 != hProcess )
{
char USER32[] = "USER32";
DataLen = 65536;
pData = VirtualAllocEx( hProcess, 0, DataLen, MEM_COMMIT, PAGE_READWRITE );
if( 0 != pData )
{
WriteProcessMemory( hProcess, pData, USER32, sizeof(USER32), NULL );
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)&LoadLibraryA, pData, 0, NULL );
if( 0 != hThread )
{
WaitForSingleObject( hThread, INFINITE );
GetExitCodeThread( hThread, (DWORD*)&hInst );
::CloseHandle( hThread );
if( 0 != hInst )
{
#ifdef _DEBUG // DEBUG 下函数多了一个jmp
const DWORD __GetTextLength = *(LPDWORD)((LPBYTE)(&_GetTextLength)+1) + (DWORD)(&_GetTextLength) + 5;
const DWORD __GetText = *(LPDWORD)((LPBYTE)(&_GetText)+1) + (DWORD)(&_GetText) + 5;
const DWORD __SetText = *(LPDWORD)((LPBYTE)(&_SetText)+1) + (DWORD)(&_SetText) + 5;
const DWORD __EndText = *(LPDWORD)((LPBYTE)(&_EndText)+1) + (DWORD)(&_EndText) + 5;
#else
const DWORD __GetTextLength = (DWORD)(&_GetTextLength);
const DWORD __GetText = (DWORD)(&_GetText);
const DWORD __SetText = (DWORD)(&_SetText);
const DWORD __EndText = (DWORD)(&_EndText);
#endif
const size_t CODELEN1 = __GetText - __GetTextLength;
const size_t CODELEN2 = __SetText - __GetText;
const size_t CODELEN3 = __EndText - __SetText;
const size_t CODELEN = __EndText - __GetTextLength;
pCode = VirtualAllocEx( hProcess, 0, CODELEN, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( 0 != pCode )
{
// 重定向
char CODE[512];
memcpy( CODE, (const void*)__GetTextLength, CODELEN );
LPBYTE p = 0;
for( p=(LPBYTE)CODE; (BYTE)0xE8!=*p; ++p ); // E8为CALL指令
*(DWORD*)(p+1) = (DWORD)&GetWindowTextLength - (DWORD)(p-(LPBYTE)CODE+(LPBYTE)pCode) - 5;
for( p=(LPBYTE)CODE+CODELEN1; (BYTE)0xE8!=*p; ++p );
for( ++p; (BYTE)0xE8!=*p; ++p );
*(DWORD*)(p+1) = (DWORD)&GetWindowText - (DWORD)(p-(LPBYTE)CODE+(LPBYTE)pCode) - 5;
for( p=(LPBYTE)CODE+CODELEN1+CODELEN2; (BYTE)0xE8!=*p; ++p );
for( ++p; (BYTE)0xE8!=*p; ++p );
*(DWORD*)(p+1) = (DWORD)&SetWindowText - (DWORD)(p-(LPBYTE)CODE+(LPBYTE)pCode) - 5;
WriteProcessMemory( hProcess, pCode, CODE, CODELEN, NULL);
return true;
VirtualFreeEx( hProcess, pCode, 0, MEM_RELEASE );
pCode = 0;
}
hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)&FreeLibrary, hInst, 0, NULL );
if( 0 != hThread )
{
WaitForSingleObject( hThread, INFINITE );
::CloseHandle( hThread );
}
hInst = 0;
}
}
VirtualFreeEx( hProcess, pData, 0, MEM_RELEASE );
pData = 0;
}
DataLen = 0;
CloseHandle( hProcess );
hProcess = 0;
}
PId = 0;
return false;
}
void CRemoteEditBox::Close()
{
if( 0 != PId )
{
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)&FreeLibrary, hInst, 0, NULL );
if( 0 != hThread )
{
WaitForSingleObject( hThread, INFINITE );
::CloseHandle( hThread );
}
hInst = 0;
VirtualFreeEx( hProcess, pCode, 0, MEM_RELEASE );
VirtualFreeEx( hProcess, pData, 0, MEM_RELEASE );
CloseHandle( hProcess );
PId = 0;
hProcess = 0;
pData = 0;
pCode = 0;
DataLen = 0;
}
}
bool CRemoteEditBox::IsOpen() const
{
return ( 0 != hProcess );
}
int CRemoteEditBox::GetTextLength( HWND hEdit ) const
{
assert( IsOpen() );
assert( GetPIdFromCtrl(hEdit) == PId );
int ret = 0;
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pCode, hEdit, 0, NULL );
if( 0 != hThread )
{
WaitForSingleObject( hThread, INFINITE );
GetExitCodeThread( hThread, (DWORD*)&ret );
::CloseHandle( hThread );
}
return ret;
}
bool CRemoteEditBox::GetText( HWND hEdit, char* pStr, int nLen ) const
{
assert( IsOpen() );
assert( GetPIdFromCtrl(hEdit) == PId );
if( sizeof(hEdit)+sizeof(nLen)+nLen+1 >= DataLen )
{
DataLen = ( sizeof(hEdit)+sizeof(nLen)+nLen+1 + 65535 )/65536 * 65536;
VirtualFreeEx( hProcess, pData, 0, MEM_RELEASE );
pData = VirtualAllocEx( hProcess, 0, DataLen, MEM_COMMIT, PAGE_READWRITE );
if( 0 == pData )
{
DataLen = 0;
return false;
}
}
WriteProcessMemory( hProcess, pData, &hEdit, sizeof(hEdit), NULL );
WriteProcessMemory( hProcess, (LPBYTE)pData+sizeof(hEdit), &nLen, sizeof(nLen), NULL );
#ifdef _DEBUG
const DWORD __GetTextLength = *(LPDWORD)((LPBYTE)(&_GetTextLength)+1) + (DWORD)(&_GetTextLength) + 5;
const DWORD __GetText = *(LPDWORD)((LPBYTE)(&_GetText)+1) + (DWORD)(&_GetText) + 5;
#else
const DWORD __GetTextLength = (DWORD)(&_GetTextLength);
const DWORD __GetText = (DWORD)(&_GetText);
#endif
int ret = 0;
LPTHREAD_START_ROUTINE p = (LPTHREAD_START_ROUTINE)( (LPBYTE)pCode + (__GetText-__GetTextLength) );
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, p, pData, 0, NULL );
if( 0 != hThread )
{
WaitForSingleObject( hThread, INFINITE );
GetExitCodeThread( hThread, (DWORD*)&ret );
::CloseHandle( hThread );
ReadProcessMemory( hProcess, (char*)pData+sizeof(hEdit)+sizeof(nLen), pStr, nLen, NULL );
return true;
}
return false;
}
bool CRemoteEditBox::SetText( HWND hEdit, const char* pStr )
{
assert( IsOpen() );
assert( GetPIdFromCtrl(hEdit) == PId );
size_t nLen = strlen(pStr)+1;
if( sizeof(hEdit)+nLen+1 >= DataLen )
{
DataLen = ( sizeof(hEdit)+nLen+1 + 65535 )/65536 * 65536;
VirtualFreeEx( hProcess, pData, 0, MEM_RELEASE );
pData = VirtualAllocEx( hProcess, 0, DataLen, MEM_COMMIT, PAGE_READWRITE );
if( 0 == pData )
{
DataLen = 0;
return false;
}
}
WriteProcessMemory( hProcess, pData, &hEdit, sizeof(hEdit), NULL );
WriteProcessMemory( hProcess, (LPBYTE)pData+sizeof(hEdit), (void*)pStr, nLen, NULL );
#ifdef _DEBUG
const DWORD __GetTextLength = *(LPDWORD)((LPBYTE)(&_GetTextLength)+1) + (DWORD)(&_GetTextLength) + 5;
const DWORD __SetText = *(LPDWORD)((LPBYTE)(&_SetText)+1) + (DWORD)(&_SetText) + 5;
#else
const DWORD __GetTextLength = (DWORD)(&_GetTextLength);
const DWORD __SetText = (DWORD)(&_SetText);
#endif
int ret = 0;
LPTHREAD_START_ROUTINE p = (LPTHREAD_START_ROUTINE)( (LPBYTE)pCode + (__SetText-__GetTextLength) );
HANDLE hThread = CreateRemoteThread( hProcess, NULL, 0, p, pData, 0, NULL );
if( 0 != hThread )
{
WaitForSingleObject( hThread, INFINITE );
GetExitCodeThread( hThread, (DWORD*)&ret );
::CloseHandle( hThread );
return true;
}
return false;
}
// 测试代码
#include <stdio.h>
int main( void )
{
HWND hEdit = (HWND)0x0013047A; // 自己找个EDIT BOX的HANDLE
CRemoteEditBox a;
if( a.Open( CRemoteEditBox::GetPIdFromCtrl(hEdit) ) )
{
char tmp[260];
printf( "length = %u\n", a.GetTextLength(hEdit) );
a.GetText( hEdit, tmp, 260 );
printf( "str = %s\n\n", tmp );
a.SetText( hEdit, "A1B2C3" );
printf( "length = %u\n", a.GetTextLength(hEdit) );
a.GetText( hEdit, tmp, 260 );
printf( "str = %s\n\n", tmp );
}
}