进程注入的研究与实现(上)


进程注入的研究与实现(上)


    为了对内存中的某个进程进行操作,并且获得该进程地址空间里的数据,或者修改进程的私有数据结构,必须将自己的代码放在目标进程的地址空间里运行,这时就避免不了使用进程注入方法了。

进程注入的方法分类如下:


    带DLL的注入

        利用注册表注入

        利用Windows Hooks注入

        利用远程线程注入

        利用特洛伊DLL注入

    不带DLL的注入

        直接将代码写入目标进程,并启动远程线程


1. 利用注册表注入

   在Windows NT/2000/XP/2003中,有一个注册表键值HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsHKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs。当某个进程加载User32.dll时,这里面列出的所有的DLL都将User32.dll利用LoadLibrary函数加载到该进程空间中。我们可以把自己的代码放在一个DLL中,并加入该键值,这样就可以注入到所有使用User32.dll的进程中了。

   当DLL以LoadLibrary的方式加载时,DllMain会被以DLL_PROCESS_ATTACH为原因调用,实际上我们也只需要关心DLL_PROCESS_ATTACH

Cpp代码   收藏代码
  1. BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )  
  2. {  
  3.     if (ul_reason_for_call == DLL_PROCESS_ATTACH)  
  4.     {  
  5.         HANDLE f = CreateFile(L"D:\\InjectSuccess.txt", FILE_ADD_FILE, FILE_SHARE_WRITE,  
  6.             NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);  
  7.         CloseHandle(f);  
  8.     }  
  9.     return TRUE;  
  10. }  

  

2. 利用Windows Hooks注入

   Windows系统给我们提供了一些挂钩函数,使得被挂钩的进程可以在自己处理接收到的消息之前,先执行我们的消息处理函数,而这个消息处理函数一般会放在DLL中,来让目标进程加载,这实际上已经达到了注入代码的效果。

   一般情况下,我们把挂钩函数和消息处理函数都放在dll中:

 

Cpp代码   收藏代码
  1. HHOOK g_hHook = NULL;  
  2. HINSTANCE hInst;  
  3. BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)  
  4. {  
  5.     hInst = (HINSTANCE)hModule;  
  6.     return TRUE;  
  7. }  
  8. LRESULT myKeyBrdFuncAd (int code, WPARAM wParam, LPARAM lParam)  
  9. {  
  10.     // To be nice, your rootkit should call the next-lower  
  11.     // hook, but you never know what this hook may be.  
  12.     //::MessageBoxA(NULL, "Hello Injection", "Injection", MB_OK);  
  13.     return CallNextHookEx(g_hHook, code, wParam, lParam);  
  14. }  
  15. #ifdef __cplusplus    // If used by C++ code,   
  16. extern "C" {          // we need to export the C interface  
  17. #endif  
  18.     __declspec(dllexportbool SetHook(DWORD dwThreadId)  
  19.     {  
  20.         g_hHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)myKeyBrdFuncAd, hInst, dwThreadId);  
  21.         if (g_hHook == NULL)  
  22.         {  
  23.             return false;  
  24.         }  
  25.         return true;  
  26.     }  
  27. #ifdef __cplusplus  
  28. }  
  29. #endif  
 

 

 

   注入进程要加载这个DLL,然后执行里面的SetHook函数,传入的参数为被注入线程的ID

 

Cpp代码   收藏代码
  1.  typedef bool (*MYPROC) (DWORD dwThreadId);  
  2. ......  
  3. HANDLE hLib = LoadLibraryA("D:\\source\\rootkits\\injecting\\InjectDll\\Debug\\InjectDll.dll");  
  4. if (hLib == NULL)  
  5. {  
  6.     printf("LoadLibrary Error!\n");  
  7. }  
  8. MYPROC myProc = (MYPROC)GetProcAddress((HMODULE)hLib,"SetHook");  
  9. if (myProc != NULL)  
  10. {  
  11.     if ((*myProc)((DWORD)4860) == false)  
  12.     {   
  13.         printf("loose: %d", GetLastError());  
  14.     }  
  15. }  
  16. else  
  17. {  
  18.     printf("GetProcAddress error: %d", GetLastError());  
  19. }  
 

 

 

3. 利用远程线程注入DLL

    1)、取得远程进程的进程ID; 

  2)、在远程进程空间中分配一段内存用来存放要注入的DLL完整路径; 

  3)、将要注入的DLL的路径写到刚才分配的远程进程空间; 

    4 )、从Kernel32.dll中取得LoadLibray的地址; 

  5)、调用CreateRemoteThread函数以从Kernel32.dll中取得的LoadLibrary函数的地址为线程函数的地址,以我们要注入的DLL文件名为参数,创建远程线程;

  在第二三步中,为什么要把我们要注入的DLL的文件名写到远程进程的地址空间进行操作,《WINDOWS核心编程》中是这样描述的: 

“(要注入的DLL文件名)字符串是在调用进程的地址空间中。该字符串的地址已经被赋予新创建的远程线程,该线程将它传递给L o a d L i b r a r y A。但是,当L o a d L i b r a r y A取消对内存地址的引用时, D L L路径名字符串将不再存在,远程进程的线程就可能引发访问违规”;

  至于第四步中为什么不直接对LoadLibrary进行调用,《WINDOWS核心编程》中是这样描述的: 

“如果在对C r e a t e R e m o t e T h r e a d的调用中使用一个对L o a d L i b r a r y A的直接引用,这将在你的模块的输入节中转换成L o a d L i b r a r y A的形实

替换程序的地址。将形实替换程序的地址作为远程线程的起始地址来传递,会导致远程线程开始执行一些令人莫名其妙的东西。其结果很可能造成访问违规。”

   我在DLL中只是做一些简单的处理以显示注入成功。

Cpp代码   收藏代码
  1. BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)  
  2. {  
  3.     hInst = (HINSTANCE)hModule;  
  4.     if (ul_reason_for_call == DLL_PROCESS_ATTACH)  
  5.     {  
  6.         HANDLE f = CreateFile(L"D:\\InjectSuccess.txt", FILE_ADD_FILE, FILE_SHARE_WRITE,  
  7.             NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);  
  8.         CloseHandle(f);  
  9.         hInst = (HINSTANCE)hModule;  
  10.     }  
  11.     return TRUE;  
  12. }   

    

   按照上面的4个步骤,注入进程的代码如下:

Cpp代码   收藏代码
  1. LPWSTR lpszLibName = L"D:\\source\\rootkits\\injecting\\InjectDll\\Debug\\InjectDll.dll";  
  2. HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE,FALSE, 3256);  
  3. LPWSTR lpszRemoteFile = (LPWSTR)VirtualAllocEx(hProcess, NULL, sizeof(WCHAR) * lstrlenW(lpszLibName) + 1, MEM_COMMIT, PAGE_READWRITE);  
  4. WriteProcessMemory(hProcess, lpszRemoteFile,(PVOID)lpszLibName, sizeof(WCHAR) * lstrlenW(lpszLibName) + 1, NULL);  
  5. PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryW");  
  6. HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn, // LoadLibrary地址  
  7.                                     lpszRemoteFile, // 要加载的DLL名   
  8.                                     0, NULL);  
 

    在上面的过程中,实际上我们做了一个假设,就是所有进程中的kernel32.dll和user32.dll都被映射到了同一段虚拟地址上。

             

4. 利用特洛伊DLL进行注入 

    这种方法的原理就是由自己写一个与原有进程调用的DLL具有相同接口函数的DLL,再用我们的DLL替换原有的DLL。在替换的过程中,由我们自己编写感兴趣的函数替换原有函数,而对其它不感兴趣的函数,则以函数转发的形式调用原有DLL中的函数。这里面有个前提,就是你在编写DLL时你必须知道原有DLL中的函数都有哪些,以免导至其它进程调用DLL时找不到相应的API函数,特别是在替换系统DLL文件时更要小心。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值