基于c++进程注入的实现

示范工程:http://download.csdn.net/user/kissyfish

要实现对一个程序的进程注入,然后对被注入的进程进行控制,首先需要查找到要注入的进程ID。如何获取的进程ID呢?windows提供了一个API只要知道了这个进程里面的一个窗口句柄,就可以找到找到该进程ID。函数形式如下:DWORD GetWindowThreadProcessId(
  HWND hWnd,
  LPDWORD lpdwProcessId
);

那如何获取这个窗口的句柄呢?很自然我们可以想到这么一个函数,函数形式如下:

HWND FindWindowEx(     
    HWND hwndParent,
    HWND hwndChildAfter,
    LPCTSTR lpszClass,
    LPCTSTR lpszWindow
);

hwndParent:指向一个待搜索窗口的父窗。

hwndChildAfter:子窗口的句柄。

lpszClass:窗口的类名。

lpszWindow:窗口的标题名。

例如,本示例工程要找到一个对话框里的编辑框,并对这个编辑框进行注入。查找过程如下:

  1.     HWND hWndChild = NULL;
  2.     while(1)
  3.     {
  4.         // 查找对话框窗口,且这个对话框窗口的标题名为“TestDlg”
  5.         HWND hDlg = FindWindowEx(0, NULL, "#32770""TestDlg");
  6.         if(hDlg == NULL)
  7.         {
  8.             printf("没有找到该对话框窗口!/n");
  9.             exit(1);
  10.         }
  11.         else
  12.         {
  13.             // 查找这个对话框窗口中的edit控件
  14.             hWndChild = FindWindowEx(hDlg, NULL, "Edit",NULL);
  15.             if(hWndChild != NULL)
  16.             {
  17.                 break// 找到这个编辑框,现在要对这个编辑框进行注入
  18.             }
  19.         }
  20.     }
  21.     // 根据查找出的窗口,查询进程
  22.     DWORD dwQQGameId;
  23.     HANDLE hProcessQQGame;
  24.     if(!GetWindowThreadProcessId(hWndChild,&dwQQGameId))
  25.     {
  26.         printf("Error in GetWindowThreadProcessId():%d/n",GetLastError());
  27.         exit(1);
  28.     }

找到这个进程后,然后就要对这个进程进行注入。但是,你别忘记了,当你在其他进程中获取另外进程的窗口句柄,你是没有办法操作这个句柄的。为什么呢?每个进程都被赋予它自己的虚拟地址空间。对于3 2位进程来说,这个地址空间是4 G B,因
为3 2位指针可以拥有从0 x 0 0 0 0 0 0 0 0至0 x F F F F F F F F之间的任何一个值。这使得一个指针能够拥有4 294 967 296个值中的一个值,它覆盖了一个进程的4 G B虚拟空间的范围。对于6 4位进程来说,这个地址空间是1 6 E B(1 01 8字节),因为6 4位指针可以拥有从0 x 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0至0 x F F F F F F F F F F F F F F F F之间的任何值。这使得一个指针可以拥有18 446 744 073 709 551 616个值中的一个值,它覆盖了一个进程的1 6 E B虚拟空间的范围。这是相当大的一个范围。由于每个进程可以接收它自己的私有的地址空间,因此当进程中的一个线程正在运行时,该线程可以访问只属于它的进程的内存。属于所有其他进程的内存则隐藏着,并且不能被正在运行的线程访问。注意在Windows 2000中,属于操作系统本身的内存也是隐藏的,正在运行的线程无法访问。这意味着线程常常不能访问操作系统的数据。Windows 98中,属于操作系统的内存是不隐藏的,正在运行的线程可以访问。因此,正在运行的线程常常可以访问操作系统的数据,也可以破坏操作系统(从而有可能导致操作系统崩溃)。在Windows 98中,一个进程的线程不可能访问属于另一个进程的内存。前面说过,每个进程有它自己的私有地址空间。进程A可能有一个存放在它的地址空间中的数据结构,地址是0 x 1 2 3 4 5 6 7 8,而进程B则有一个完全不同的数据结构存放在它的地址空间中,地址是0 x 1 2 3 4 5 6 7 8。当进程A中运行的线程访问地址为0 x 1 2 3 4 5 6 7 8的内存时,这些线程访问的是进程A的数据结构。当进程B中运行的线程访问地址为0 x 1 2 3 4 5 6 7 8的内存时,这些线程访问的是进程B的数据结构。进程A中运行的线程不能访问进程B的地址空间中的数据结构,反之亦然。

这样看来,若想对这个窗口句柄进行操作,得想方设法使我们能够进入到原宿主进程中,然后执行我们的操作。这个就好象一个寄生虫想要破坏人体的机能,必须得进入我们的体内,寄生在我们的组织上才能够产生作用。现在关键是如何寄生到宿主中呢?

通常,任何进程都可以通过LoadLibrary动态地加载DLL,但是我们如何强制一个外部进程调用该函数呢?答案是CreateRemoteThread。
让我们先来看看LoadLibrary和FreeLibrary的函数声明:

HINSTANCE LoadLibrary(
  LPCTSTR lpLibFileName   // address of filename of library module
);

BOOL FreeLibrary(
  HMODULE hLibModule      // handle to loaded library module
);

再和CreateRemoteThread的线程过程(thread procedure)ThreadProc比较一下:
DWORD WINAPI ThreadProc(
  LPVOID lpParameter   // thread data
);

    你会发现所有的函数都有同样的调用约定(calling convention)、都接受一个32位的参数并且返回值类型的大小也一样。也就是说,我们可以把LoadLibrary/FreeLibrary的指针作为参数传递给CrateRemoteThread。

    然而,还有两个问题(参考下面对CreateRemoteThread的说明)

    1. 传递给ThreadProc的lpStartAddress 参数必须为远程进程中的线程过程的起始地址。
    2. 如果把ThreadProc的lpParameter参数当做一个普通的32位整数(FreeLibrary把它当做HMODULE)那么没有如何问题,但是如果把它当做一个指针(LoadLibrary把它当做一个char*),它就必须指向远程进程中的内存数据。

    第一个问题其实已经迎刃而解了,因为LoadLibrary和FreeLibrary都是存在于kernel32.dll中的函数,而kernel32可以保证任何“正常”进程中都存在,且其加载地址都是一样的。(参看附录A)于是LoadLibrary/FreeLibrary在任何进程中的地址都是一样的,这就保证了传递给远程进程的指针是个有效的指针。

    第二个问题也很简单:把DLL的文件名(LodLibrary的参数)用WriteProcessMemory复制到远程进程。

    所以,使用CreateRemoteThread和LoadLibrary技术的步骤如下:
    1. 得到远程进程的HANDLE(使用OpenProcess)。
    2. 在远程进程中为DLL文件名分配内存(VirtualAllocEx)。
    3. 把DLL的文件名(全路径)写到分配的内存中(WriteProcessMemory)
    4. 使用CreateRemoteThread和LoadLibrary把你的DLL映射近远程进程。
    5. 等待远程线程结束(WaitForSingleObject),即等待LoadLibrary返回。也就是说当我们的DllMain(是以DLL_PROCESS_ATTACH为参数调用的)返回时远程线程也就立即结束了。
    6. 取回远程线程的结束码(GetExitCodeThtread),即LoadLibrary的返回值――我们DLL加载后的基地址(HMODULE)。
    7. 释放第2步分配的内存(VirtualFreeEx)。
    8. 用CreateRemoteThread和FreeLibrary把DLL从远程进程中卸载。调用时传递第6步取得的HMODULE给FreeLibrary(通过CreateRemoteThread的lpParameter参数)。
    9. 等待线程的结束(WaitSingleObject)。

主要代码如下:

  1. #include<stdio.h>
  2. #include<conio.h>
  3. #include<windows.h>
  4. void main()
  5. {
  6.     HWND hWndChild = NULL;
  7.     while(1)
  8.     {
  9.         // 查找对话框窗口,且这个对话框窗口的标题名为“TestDlg”
  10.         HWND hDlg = FindWindowEx(0, NULL, "#32770""TestDlg");
  11.         if(hDlg == NULL)
  12.         {
  13.             printf("没有找到该对话框窗口!/n");
  14.             exit(1);
  15.         }
  16.         else
  17.         {
  18.             // 查找这个对话框窗口中的edit控件
  19.             hWndChild = FindWindowEx(hDlg, NULL, "Edit",NULL);
  20.             if(hWndChild != NULL)
  21.             {
  22.                 break// 找到这个编辑框,现在要对这个编辑框进行注入
  23.             }
  24.         }
  25.     }
  26.     // 根据查找出的窗口,查询进程
  27.     DWORD dwQQGameId;
  28.     HANDLE hProcessQQGame;
  29.     if(!GetWindowThreadProcessId(hWndChild,&dwQQGameId))
  30.     {
  31.         printf("Error in GetWindowThreadProcessId():%d/n",GetLastError());
  32.         exit(1);
  33.     }
  34.     HINSTANCE hDll = NULL;
  35.     typedef void(*LP_SET_HEDIT_FUN)(HWND);
  36.     LP_SET_HEDIT_FUN m_SethEdit = NULL;
  37.     char DllPath[MAX_PATH] = "D://进程注入测试工程//Bin//automessagedll.dll";
  38.     hDll = LoadLibrary(DllPath);
  39.     if(hDll)
  40.     {
  41.         m_SethEdit = (LP_SET_HEDIT_FUN)GetProcAddress(hDll,"SethEdit");
  42.     }
  43.     if(m_SethEdit)
  44.     {
  45.         m_SethEdit(hWndChild);
  46.     }
  47.     else
  48.     {
  49.         printf("Can not load SethEdit in the dll(%d)./n",GetLastError());
  50.     }
  51.     if( (hProcessQQGame = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwQQGameId)) == NULL)
  52.     {
  53.         printf("Error in OpenProcess():%d/n",GetLastError());
  54.         getch();
  55.         exit(1);
  56.     }
  57.     int cb = (1+lstrlen(DllPath))* sizeof(char);
  58.     char* RemoteLibFile = (char*)VirtualAllocEx(hProcessQQGame,NULL,cb,MEM_COMMIT,PAGE_READWRITE);
  59.     if(RemoteLibFile == NULL)
  60.     {
  61.         printf("Error in VirtualAllocEx():%d/n",GetLastError());
  62.         getch();
  63.         exit(1);
  64.     }
  65.     if( (WriteProcessMemory(hProcessQQGame,RemoteLibFile,(LPVOID)DllPath,cb,NULL)) == 0)
  66.     {
  67.         printf("Error in WriteProcessMemory():%d/n",GetLastError());
  68.         getch();
  69.         exit(1);
  70.     }
  71.     PTHREAD_START_ROUTINE pfnStartAddr;
  72.     pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"),"LoadLibraryA");
  73.     HANDLE hThread = CreateRemoteThread(hProcessQQGame,NULL,0,pfnStartAddr,RemoteLibFile,0,NULL);
  74.     if(hThread == NULL)
  75.     {
  76.         printf("Error in CreateRemoteThread():%d",GetLastError());
  77.         getch();
  78.         exit(1);
  79.     }
  80.     WaitForSingleObject(hThread,INFINITE);
  81.     CloseHandle(hThread);
  82.     VirtualFreeEx(hProcessQQGame,RemoteLibFile,0,MEM_RELEASE);
  83.     CloseHandle(hProcessQQGame);
  84. }

 

没有更多推荐了,返回首页