遇到一个棘手的问题,求各位帮助解答。同样的远程注入线程的代码(注入到Notepad当中),如果我的程序是MFC或者WTL窗体程序,在注入线程的时候,目标进程就会CRASH,如果我的程序是SDK的窗体程序,目标进程就不会CRASH。
代码如下:(Notepad会在弹出MessageBox之后CRASH在KERNEL32.DLL当中,Message的内容只有"T"而不是"Test")。typedef int (WINAPI *MESSAGEBOX)(HWND,LPCTSTR,LPCTSTR,UINT);typedef struct {
MESSAGEBOX fnMessageBox; // pointer to user32!SendMessage
TCHAR pbText[128 * sizeof(TCHAR)];
} INJDATA, *PINJDATA;static DWORD WINAPI ThreadFunc (INJDATA *pData)
{
int nXferred  = 0; // number of chars retrieved by WM_GETTEXT
nXferred = pData->fnMessageBox( NULL, pData->pbText, pData->pbText, MB_OK);
return nXferred;
}static void AfterThreadFunc (void) {
}
DWORD InjectCodeToRemoteThread(DWORD processID)
{
HINSTANCE hUser32;
INJDATA *pDataRemote; // the address (in the remote process) where INJDATA will be copied to;
DWORD *pCodeRemote; // the address (in the remote process) where ThreadFunc will be copied to;
HANDLE hThread = NULL; // the handle to the thread executing the remote copy of ThreadFunc;
DWORD dwThreadId = 0;
DWORD dwNumBytesXferred = 0; // number of bytes written/read to/from the remote process; HANDLE hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |
PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, processID );
if(hProcess == NULL)
return -1;

__try {
hUser32 = GetModuleHandle(__TEXT("user32"));
if (hUser32 == NULL)
__leave; // Initialize INJDATA and then 
// copy it to the remote process
INJDATA DataLocal = {
(MESSAGEBOX) GetProcAddress(hUser32, "MessageBoxA"),
_T("Test")
};

if( DataLocal.fnMessageBox == NULL )
__leave;

// 1. Allocate memory in the remote process for INJDATA
// 2. Write a copy of DataLocal to the allocated memory
pDataRemote = (INJDATA*) VirtualAllocEx( hProcess, 0, sizeof(INJDATA), MEM_COMMIT, PAGE_READWRITE );
if (pDataRemote == NULL)
__leave;
WriteProcessMemory( hProcess, pDataRemote, &DataLocal, sizeof(INJDATA), &dwNumBytesXferred );
// Calculate the number of bytes that ThreadFunc occupies
const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc); // 1. Allocate memory in the remote process for the injected ThreadFunc
// 2. Write a copy of ThreadFunc to the allocated memory
pCodeRemote = (PDWORD) VirtualAllocEx( hProcess, 0, cbCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if (pCodeRemote == NULL)
__leave;
WriteProcessMemory( hProcess, pCodeRemote, &ThreadFunc, cbCodeSize, &dwNumBytesXferred );
// Start execution of remote ThreadFunc
hThread = CreateRemoteThread(hProcess, NULL, 0, 
(LPTHREAD_START_ROUTINE) pCodeRemote,
pDataRemote, 0 , &dwThreadId);
if (hThread == NULL)
__leave; WaitForSingleObject(hThread, INFINITE); }
__finally {
if ( pDataRemote != 0 )
VirtualFreeEx( hProcess, pDataRemote, 0, MEM_RELEASE ); if ( pCodeRemote != 0 )
VirtualFreeEx( hProcess, pCodeRemote, 0, MEM_RELEASE ); if ( hThread != NULL ) {
CloseHandle(hThread);
}
} return 0;
}

解决方案 »

  1.   

    你的程序使用的是Unicode字符集,而调用的是MessageBoxA,所以只显示一个字母T。
      

  2.   

    谢谢,用MessageBoxW就可以显示全了,但是为什么Notepad会CRASH呢?
      

  3.   

    简单看了一下代码,没有发现明显的问题。你对两种情况分别调试一下,看看获取的MessageBoxW的地址是否相同,cbCodeSize的值是否相同。
      

  4.   

    const int cbCodeSize = ((LPBYTE) AfterThreadFunc - (LPBYTE) ThreadFunc); 这种方法计算大小, 不怎么正确, 编译器连接函数的顺序并不是一定按代码的顺序
    所以有时 CRASH, 你固定cbCodeSize 比如 cbCodeSize = 1024; 试试
      

  5.   

    谢谢,固定大小我也试过了,还是会CRASH。关键是为什么MFC和WTL结构的会CRASH掉,SDK的却不会CRASH。我昨天又用vs2005做了一个sdk的工程,奇怪的是这个也会crash。而我用vs6.0做的工程用同样的代码却没有问题。太奇怪了。
      

  6.   

    只显示T是因为你的字符串版本和你的函数版本不一致。
    第一种
    typedef struct { 
    MESSAGEBOX fnMessageBox; // pointer to user32!SendMessage 
    CHAR pbText[128 * sizeof(CHAR)]; 
    } INJDATA, *PINJDATA;INJDATA DataLocal = { 
    (MESSAGEBOX) GetProcAddress(hUser32, "MessageBoxA"), 
    "Test";
    };第二种,最简单,都不需要GetModuleHandleINJDATA DataLocal = { 
    MessageBox, 
    _T("Test") 
    };编译器会根据你当前的字符集选择合适的Call,另外直接使用函数名可以得到函数地址而非跳转地址,所以这样做是安全的。
      

  7.   


    第二种方式会有问题吧,这个结构会被插入到另一个process中,如果直接使用函数名,调用应该会失败的。
      

  8.   

    user32在不同进程会映射到同一个地址么?不是说只有kernel32才会这样映射?
    楼主你要不试试把获取MessageBox地址的过程放到ThreadFunc中?
      

  9.   


    看过了,地址与size都相同。
      

  10.   

    crash是在关闭MessageBox之后还是一显示MessageBox就crash?
    取到的MessageBoxW的地址是0x7开头吗?
      

  11.   

    crash 发生在关闭messagebox之后。如果不点ok就不会crash。
    MessageBoxW的地址是0x77d960f2
      

  12.   

    也许是我的问题错误引导了大家,所以没有能在这里获得正确答案。最终我发现crash与工程是否使MFC无关。在一篇文章里面我找到了答案:
    Three Ways to Inject Your Code into Another Process --- By Robert Kuster
    ...
    The /GZ compiler switch In Debug builds, the /GZ compiler feature is turned on by default. You can use it to catch some errors (see the documentation for details). But what does it mean to our executable? 
    When /GZ is turned on, the compiler will add some additional code to every function residing in the executable, including a function call (added at the very end of every function) that verifies the ESP stack pointer hasn't changed through our function. But wait, a function call is added to ThreadFunc? That's the road to disaster. Now the remote copy of ThreadFunc will call a function that doesn't exist in the remote process (at least not at the same address).
    ...答案就是关掉GZ,我的代码也是来源于这片文章,只是学习过程中不仔细,忽略了这一段。还是要谢谢大家的帮助。
      

  13.   

    也许是我的问题错误引导了大家,所以没有能在这里获得正确答案。最终我发现crash与工程是否使MFC无关。在一篇文章里面我找到了答案:
    Three Ways to Inject Your Code into Another Process --- By Robert Kuster
    ...
    The /GZ compiler switch In Debug builds, the /GZ compiler feature is turned on by default. You can use it to catch some errors (see the documentation for details). But what does it mean to our executable? 
    When /GZ is turned on, the compiler will add some additional code to every function residing in the executable, including a function call (added at the very end of every function) that verifies the ESP stack pointer hasn't changed through our function. But wait, a function call is added to ThreadFunc? That's the road to disaster. Now the remote copy of ThreadFunc will call a function that doesn't exist in the remote process (at least not at the same address).
    ...答案就是关掉GZ,我的代码也是来源于这片文章,只是学习过程中不仔细,忽略了这一段。还是要谢谢大家的帮助。