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

要实现对一个程序的进程注入,然后对被注入的进程进行控制,首先需要查找到要注入的进程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:窗口的标题名。

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

    HWND hWndChild = NULL;
    while(1)
    {
        // 查找对话框窗口,且这个对话框窗口的标题名为“TestDlg”
        HWND hDlg = FindWindowEx(0, NULL, "#32770", "TestDlg");
        if(hDlg == NULL)
        {
            printf("没有找到该对话框窗口!\n");
            exit(1);
        }
        else
        {
            // 查找这个对话框窗口中的edit控件
            hWndChild = FindWindowEx(hDlg, NULL, "Edit",NULL);
            if(hWndChild != NULL)
            {
                break; // 找到这个编辑框,现在要对这个编辑框进行注入
            }
        }
    }

    // 根据查找出的窗口,查询进程
    DWORD dwQQGameId;
    HANDLE hProcessQQGame;
    if(!GetWindowThreadProcessId(hWndChild,&dwQQGameId))
    {
        printf("Error in GetWindowThreadProcessId():%d\n",GetLastError());
        exit(1);
    }
找到这个进程后,然后就要对这个进程进行注入。但是,你别忘记了,当你在其他进程中获取另外进程的窗口句柄,你是没有办法操作这个句柄的。为什么呢?每个进程都被赋予它自己的虚拟地址空间。对于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)。

主要代码如下:

#include<stdio.h>
#include<conio.h>
#include<windows.h>

void main()
{
    HWND hWndChild = NULL;
    while(1)
    {
        // 查找对话框窗口,且这个对话框窗口的标题名为“TestDlg”
        HWND hDlg = FindWindowEx(0, NULL, "#32770", "TestDlg");
        if(hDlg == NULL)
        {
            printf("没有找到该对话框窗口!\n");
            exit(1);
        }
        else
        {
            // 查找这个对话框窗口中的edit控件
            hWndChild = FindWindowEx(hDlg, NULL, "Edit",NULL);
            if(hWndChild != NULL)
            {
                break; // 找到这个编辑框,现在要对这个编辑框进行注入
            }
        }
    }

    // 根据查找出的窗口,查询进程
    DWORD dwQQGameId;
    HANDLE hProcessQQGame;
    if(!GetWindowThreadProcessId(hWndChild,&dwQQGameId))
    {
        printf("Error in GetWindowThreadProcessId():%d\n",GetLastError());
        exit(1);
    }


    HINSTANCE hDll = NULL;
    typedef void(*LP_SET_HEDIT_FUN)(HWND);
    LP_SET_HEDIT_FUN m_SethEdit = NULL;
    char DllPath[MAX_PATH] = "D:\\进程注入测试工程\\Bin\\automessagedll.dll";
    hDll = LoadLibrary(DllPath);
    if(hDll)
    {
        m_SethEdit = (LP_SET_HEDIT_FUN)GetProcAddress(hDll,"SethEdit");
    }
    if(m_SethEdit)
    {
        m_SethEdit(hWndChild);
    }
    else
    {
        printf("Can not load SethEdit in the dll(%d).\n",GetLastError());
    }

 

    if( (hProcessQQGame = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwQQGameId)) == NULL)
    {
        printf("Error in OpenProcess():%d\n",GetLastError());
        getch();
        exit(1);
    }

    int cb = (1+lstrlen(DllPath))* sizeof(char);

    char* RemoteLibFile = (char*)VirtualAllocEx(hProcessQQGame,NULL,cb,MEM_COMMIT,PAGE_READWRITE);
    if(RemoteLibFile == NULL)
    {
        printf("Error in VirtualAllocEx():%d\n",GetLastError());
        getch();
        exit(1);
    }
    if( (WriteProcessMemory(hProcessQQGame,RemoteLibFile,(LPVOID)DllPath,cb,NULL)) == 0)
    {
        printf("Error in WriteProcessMemory():%d\n",GetLastError());
        getch();
        exit(1);
    }


    PTHREAD_START_ROUTINE pfnStartAddr;
    pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"),"LoadLibraryA");


    HANDLE hThread = CreateRemoteThread(hProcessQQGame,NULL,0,pfnStartAddr,RemoteLibFile,0,NULL);
    if(hThread == NULL)
    {
        printf("Error in CreateRemoteThread():%d",GetLastError());
        getch();
        exit(1);
    }

    WaitForSingleObject(hThread,INFINITE);


    CloseHandle(hThread);
    VirtualFreeEx(hProcessQQGame,RemoteLibFile,0,MEM_RELEASE);
    CloseHandle(hProcessQQGame);
}

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/kissyfish/archive/2008/12/07/3462055.aspx

posted on 2010-06-23 15:33 lhking 阅读(1183) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


导航

<2010年5月>
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345

统计

常用链接

留言簿

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜