李sir_Blog

博客园 首页 联系 订阅 管理

先解释一下远程进程,其实就是要植入你的代码的进程,相对于你的工作进程(如果叫本地进程的话)它就叫远程进程,可理解为宿主。

首先介绍一下我们的主要工具CreateRemoteThread,这里先将函数原型简单介绍以下。

CreateRemoteThread可将线程创建在远程进程中。

函数原型
HANDLE CreateRemoteThread(
  HANDLE hProcess,                          // handle to process
  LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
  SIZE_T dwStackSize,                       // initial stack size
  LPTHREAD_START_ROUTINE lpStartAddress,    // thread function
  LPVOID lpParameter,                       // thread argument
  DWORD dwCreationFlags,                    // creation option
  LPDWORD lpThreadId                        // thread identifier
);

参数说明:
hProcess
[输入] 进程句柄
lpThreadAttributes
[输入] 线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针
dwStackSize
[输入] 线程栈大小,以字节表示
lpStartAddress
[输入] 一个LPTHREAD_START_ROUTINE类型的指针,指向在远程进程中执行的函数地址
lpParameter
[输入] 传入参数
dwCreationFlags
[输入] 创建线程的其它标志

lpThreadId
[输出] 线程身份标志,如果为NULL,则不返回

返回值
成功返回新线程句柄,失败返回NULL,并且可调用GetLastError获得错误值。

接下来我们将以两种方式使用CreateRemoteThread,大家可以领略到CreateRemoteThread的神通,它使你的代码可以脱离你的进程,植入到别的进程中运行。

第一种方式,我们使用函数的形式。即我们将自己程序中的一个函数植入到远程进程中。

步骤1:首先在你的进程中创建函数MyFunc,我们将把它放在另一个进程中运行,这里以windows

计算器为目标进程。
static DWORD WINAPI MyFunc (LPVOID pData)
{
//do something
//...
//pData输入项可以是任何类型值
//这里我们会传入一个DWORD的值做示例,并且简单返回

return *(DWORD*)pData;
}
static void AfterMyFunc (void) {
}

这里有个小技巧,定义了一个static void AfterMyFunc (void);为了下面确定我们的代码大小

步骤2:定位目标进程,这里是一个计算器
HWND hStart = ::FindWindow (TEXT("SciCalc"),NULL);

步骤3:获得目标进程句柄,这里用到两个不太常用的函数(当然如果经常做线程/进程等方面的 项目的话,就很面熟了),但及有用
DWORD PID, TID;
TID = ::GetWindowThreadProcessId (hStart, &PID);

HANDLE hProcess;
hProcess = OpenProcess(PROCESS_ALL_ACCESS,false,PID);


步骤4:在目标进程中配变量地址空间,这里我们分配10个字节,并且设定为可以读

写PAGE_READWRITE,当然也可设为只读等其它标志,这里就不一一说明了。
char szBuffer[10];
*(DWORD*)szBuffer=1000;//for test
void *pDataRemote =(char*) VirtualAllocEx( hProcess, 0, sizeof(szBuffer), MEM_COMMIT,

PAGE_READWRITE );


步骤5:写内容到目标进程中分配的变量空间
::WriteProcessMemory( hProcess, pDataRemote, szBuffer,(sizeof(szBuffer),NULL);

步骤6:在目标进程中分配代码地址空间
计算代码大小
DWORD cbCodeSize=((LPBYTE) AfterMyFunc - (LPBYTE) MyFunc);
分配代码地址空间
PDWORD pCodeRemote = (PDWORD) VirtualAllocEx( hProcess, 0, cbCodeSize, MEM_COMMIT,

PAGE_EXECUTE_READWRITE );


步骤7:写内容到目标进程中分配的代码地址空间
WriteProcessMemory( hProcess, pCodeRemote, &MyFunc, cbCodeSize, NULL);

步骤8:在目标进程中执行代码

HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE) pCodeRemote,
pDataRemote, 0 , NULL);
DWORD h;
if (hThread)
{
::WaitForSingleObject( hThread, INFINITE );
::GetExitCodeThread( hThread, &h );
TRACE("run and return %d\n",h);
::CloseHandle( hThread );
}


这里有几个值得说明的地方:
使用WaitForSingleObject等待线程结束;
使用GetExitCodeThread获得返回值;
最后关闭句柄CloseHandle。

步骤9:清理现场

释放空间
::VirtualFreeEx( hProcess, pCodeRemote,
                cbCodeSize,MEM_RELEASE );

::VirtualFreeEx( hProcess, pDataRemote,
                cbParamSize,MEM_RELEASE );


关闭进程句柄
::CloseHandle( hProcess );

第二种方式,我们使用动态库的形式。即我们将自己一个动态库植入到远程进程中。

这里不再重复上面相同的步骤,只写出其中关键的地方.
关键1:
在步骤5中将动态库的路径作为变量传入变量空间.
关键2:
在步骤8中,将GetProcAddress作为目标执行函数.

hThread = ::CreateRemoteThread( hProcess, NULL, 0,
            (LPTHREAD_START_ROUTINE )::GetProcAddress(
             hModule, "LoadLibraryA"),
             pDataRemote, 0, NULL );

另外在步骤9,清理现场中首先要先进行释放我们的动态库.也即类似步骤8执行函数FreeLibrary

hThread = ::CreateRemoteThread( hProcess, NULL, 0,
(LPTHREAD_START_ROUTINE )::GetProcAddress(
hModule, "FreeLibrary"),
(void*)hLibModule, 0, NULL );


CreateRemoteThread和WriteProcessMemory技术

示例程序:WinSpy

另一种注入代码到其他进程地址空间的方法是使用WriteProcessMemory API。这次你不用编写一个独立的DLL而是直接复制你的代码到远程进程(WriteProcessMemory)并用CreateRemoteThread执行之。

让我们看一下CreateRemoteThread的声明:


HANDLE CreateRemoteThread(
   HANDLE hProcess,         // handle to process to create thread in
   LPSECURITY_ATTRIBUTES lpThreadAttributes,   // pointer to security
                                              // attributes
   DWORD dwStackSize,       // initial thread stack size, in bytes
   LPTHREAD_START_ROUTINE lpStartAddress,      // pointer to thread
                                              // function
   LPVOID lpParameter,      // argument for new thread
   DWORD dwCreationFlags,   // creation flags
   LPDWORD lpThreadId       // pointer to returned thread identifier
);


和CreateThread相比,有一下不同:
●增加了hProcess参数。这是要在其中创建线程的进程的句柄。
●CreateRemoteThread的lpStartAddress参数必须指向远程进程的地址空间中的函数。这个函数必须存在于远程进程中,所以我们不能简单地传递一个本地ThreadFucn的地址,我们必须把代码复制到远程进程。
●同样,lpParameter参数指向的数据也必须存在于远程进程中,我们也必须复制它。


现在,我们总结一下使用该技术的步骤:
1.   得到远程进程的HANDLE(OpenProcess)。
2.   在远程进程中为要注入的数据分配内存(VirtualAllocEx)、
3.   把初始化后的INJDATA结构复制到分配的内存中(WriteProcessMemory)。
4.   在远程进程中为要注入的数据分配内存(VirtualAllocEx)。
5.   把ThreadFunc复制到分配的内存中(WriteProcessMemory)。
6.   用CreateRemoteThread启动远程的ThreadFunc。
7.   等待远程线程的结束(WaitForSingleObject)。
8.   从远程进程取回指执行结果(ReadProcessMemory 或 GetExitCodeThread)。
9.   释放第2、4步分配的内存(VirtualFreeEx)。
10.   关闭第6、1步打开打开的句柄。

另外,编写ThreadFunc时必须遵守以下规则:
1.   ThreadFunc不能调用除kernel32.dll和user32.dll之外动态库中的API函数。只有kernel32.dll和user32.dll(如果被加载)可以保证在本地和目的进程中的加载地址是一样的。(注意:user32并不一定被所有的Win32进程加载!)参考附录A。如果你需要调用其他库中的函数,在注入的代码中使用LoadLibrary和GetProcessAddress强制加载。如果由于某种原因,你需要的动态库已经被映射进了目的进程,你也可以使用GetMoudleHandle代替LoadLibrary。同样,如果你想在ThreadFunc中调用你自己的函数,那么就分别复制这些函数到远程进程并通过INJDATA把地址提供给ThreadFunc。

2.   不要使用static字符串。把所有的字符串提供INJDATA传递。为什么?编译器会把所有的静态字符串放在可执行文件的“.data”段,而仅仅在代码中保留它们的引用(即指针)。这样,远程进程中的ThreadFunc就会执行不存在的内存数据(至少没有在它自己的内存空间中)。

3.   去掉编译器的/GZ编译选项。这个选项是默认的(看附录B)。
4.   要么把ThreadFunc和AfterThreadFunc声明为static,要么关闭编译器的“增量连接(incremental linking)”(看附录C)。
5.   ThreadFunc中的局部变量总大小必须小于4k字节(看附录D)。注意,当degug编译时,这4k中大约有10个字节会被事先占用。
6.   如果有多于3tch分支的case语句,必须像下面这样分割开,或用if-else if代替.

     case constant1: statement1; goto END;
     case constant2: statement2; goto END;
     case constant3: statement2; goto END;
}

     case constant4: statement4; goto END;
     case constant5: statement5; goto END;
     case constant6: statement6; goto END;
}
END:

 

#include "stdafx.h"
#include "windows.h"


// ========== 定义一个代码结构,本例为一个对话框============
struct MyData
{
 char sz[64]; // 对话框显示内容
 DWORD dwMessageBox; // 对话框的地址
};

// ========== 远程线程的函数 ==============================
DWORD __stdcall RMTFunc(MyData *pData)
{
 typedef int(__stdcall*MMessageBox)(HWND,LPCTSTR,LPCTSTR,UINT);
 MMessageBox MsgBox = (MMessageBox)pData->dwMessageBox;
 MsgBox(NULL, pData->sz, NULL, MB_OK);
 return 0;
}
int main(int argc, char* argv[])
{
// ===== 获得需要创建REMOTETHREAD的进程句柄 ===============================
 HWND hWnd = FindWindow("notepad", NULL); // 以NOTEPAD为例
 DWORD dwProcessId;
 ::GetWindowThreadProcessId(hWnd, &dwProcessId);
 HANDLE hProcess = OpenProcess(
        PROCESS_ALL_ACCESS,
        FALSE,
        dwProcessId);

// ========= 代码结构 ================================================
 MyData data;
 ZeroMemory(&data, sizeof (MyData));
 strcat(data.sz, "对话框的内容.");
 HINSTANCE hUser = LoadLibrary("user32.dll");
 if (! hUser)
 {
  printf("Can not load library. ");
  return 0;
 }
 data.dwMessageBox = (DWORD)GetProcAddress(hUser, "MessageBoxA");
 FreeLibrary(hUser);
 if (! data.dwMessageBox)
  return 0;

// ======= 分配空间 ===================================================
 void *pRemoteThread
  = VirtualAllocEx(hProcess, 0,
      1024*4, MEM_COMMIT|MEM_RESERVE,
      PAGE_EXECUTE_READWRITE);
 if (! pRemoteThread)
  return 0;
 if (! WriteProcessMemory(hProcess, pRemoteThread, &RMTFunc, 1024*4, 0))
  return 0;

 MyData *pData
  = (MyData*)VirtualAllocEx(hProcess, 0,
      sizeof (MyData), MEM_COMMIT,
      PAGE_READWRITE);
 if (!pData)
  return 0;

 if (! WriteProcessMemory(hProcess, pData, &data, sizeof (MyData), 0))
  return 0;

// =========== 创建远程线程 ===========================================
 HANDLE hThread
  = CreateRemoteThread(hProcess, 0,
       0, (LPTHREAD_START_ROUTINE)pRemoteThread,
       pData, 0, 0);
 if (! hThread)
 {
  printf("远程线程创建失败");
  return 0;
 }
 CloseHandle(hThread);
 VirtualFreeEx(hProcess, pRemoteThread, 1024*3, MEM_RELEASE);
 VirtualFreeEx(hProcess, pData, sizeof (MyData), MEM_RELEASE);
 CloseHandle(hProcess);
 printf("Hello World! ");
 return 0;
}

zhuangyu1982@hotmail.com: 我用你的程序在windows xp下运行, 弹出messagebox之后, 只要点击确定宿主进程就会崩溃。
而且不止是messagebox,调用其它的api函数也是一样,请问这是什么原因?有什么办法可以解决吗?
--------------------------------------------------------

编译成RELEASE版本就不会出错了,主要是DEBUG版本加了一个__chkesp的函数导致调用了非法地址

 

 

 

完整代码:

#include <windows.h>

typedef struct _RemotePara
{

 char pMessageBox[12];
 DWORD dwMessageBox;
}RemotePara;

//---------------------------------------------------------------------------
// 堈最盄最.
DWORD __stdcall ThreadProc (RemotePara *lpPara){
 // 隅砱MessageBox滲杅.
 typedef int (WINAPI *MMessageBoxA)(HWND,LPCTSTR,LPCTSTR,DWORD);
 MMessageBoxA myMessageBoxA;
 myMessageBoxA =(MMessageBoxA) lpPara->dwMessageBox ;// 腕善滲杅諳華硊.
 myMessageBoxA(NULL,lpPara->pMessageBox ,lpPara->pMessageBox,0);
 return 0;
}

void main()
{

 const DWORD THREADSIZE =1024;
 DWORD byte_write;
 
    // 鳳腕醴梓輛最腔輛最瘍pid,涴爵峈"calc.exe".
 HWND hWnd = FindWindow("SciCalc","數呾");
 if(!hWnd) return;

    DWORD dwProcessId =0;
 GetWindowThreadProcessId(hWnd,&dwProcessId);
    HANDLE hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);
    if(!hRemoteProcess)return;
 void *pRemoteThread = VirtualAllocEx(hRemoteProcess,0,THREADSIZE,
  MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
 if (!pRemoteThread)
 {
  return;
 }
 if (!WriteProcessMemory(hRemoteProcess,pRemoteThread,&ThreadProc,THREADSIZE,0))
  return;
 RemotePara myRemotePara;
 ZeroMemory(&myRemotePara,sizeof(RemotePara));
 HINSTANCE hUser32 = LoadLibrary ("user32.dll");
 myRemotePara.dwMessageBox =(DWORD)
  GetProcAddress (hUser32 , "MessageBoxA");
 strcat(myRemotePara.pMessageBox,"hello\0");
 // 迡輛醴梓輛最. 蛁砩扠諾潔奀腔珜醱扽俶.
 RemotePara *pRemotePara=(RemotePara *)VirtualAllocEx(hRemoteProcess,0,sizeof(RemotePara),MEM_COMMIT,PAGE_READWRITE);
 if(!pRemotePara) return;
 if (!WriteProcessMemory(hRemoteProcess,pRemotePara,&myRemotePara,sizeof myRemotePara,0)) return;
 
 HANDLE hThread = CreateRemoteThread(hRemoteProcess,0,0,(LPTHREAD_START_ROUTINE)pRemoteThread,pRemotePara,0,&byte_write);

 DWORD h;
 if (hThread)
 {
  ::WaitForSingleObject( hThread, INFINITE );
  ::GetExitCodeThread( hThread, &h );
  ::CloseHandle( hThread );
 }

 ::VirtualFreeEx( hRemoteProcess, pRemoteThread,
  THREADSIZE,MEM_RELEASE );
 
 ::VirtualFreeEx( hRemoteProcess, pRemotePara,
  sizeof(RemotePara),MEM_RELEASE );
}

 

posted on 2010-07-13 13:46  李sir  阅读(541)  评论(0)    收藏  举报