VC下提前注入进程的一些方法1——远线程不带参数

来源:互联网 发布:直播间送礼物 知乎 编辑:程序博客网 时间:2024/05/22 15:16

前些天一直在研究Ring3层的提前注入问题。所谓提前注入,就是在程序代码逻辑还没执行前就注入,这样做一般用于Hook API。(转载请指明出处)自己写了个demo,在此记下。

        我的demo使用了两种注入方式:1 远线程; 2 修改代码入口点。

        先说下我代码风格,因为要处理很多异常逻辑,我比较喜欢do{}while(0);这样的结构,一旦出错,就break出来。于是下面代码中可能会出现“莫名其妙”的break,再次说明下,那不是语法错误,因为我只是将部分代码提出来。

1 远线程

        在处理远线程注入问题时,往往会遇到两种情况:1 执行注入的进程需要传信息给被注入进程 ;2 执行注入的进程需要传信息给被注入进程。

  1.1 执行注入的进程不需要传信息给被注入进程

        最简单的方案就是不需要传信息给被注入进程。实现的相关原理就是使用远线程执行LoadLibrary函数,Load我们的注入DLL。DLL在载入过程或者其他会被执行到的地方执行相关逻辑(如Hook API)。根据需要,可以考虑在远线程执行完毕后,再使用远线程把被注入进程加载的DLL卸载掉。

        首先说LoadLibrary ,这个函数只有一个参数,需要传递Load的DLL路径。那么什么地方保存这个参数呢?于是我们应该找一个注入进程可以访问(因为我们要写入这个数据),被注入进程也可以访问的路径(因为他们要读取)。我们可以考虑申请一个系统全局的空间来保存这个数据,也可以采用非常常见的方案——申请被注入进程的内存空间。我采用的是后者。

[cpp] viewplaincopy
  1.         void *pBufferRemote = NULL;  
  2.         DWORD dwMemSize =  ( (DWORD) wcslen( lpDllPath ) ) * sizeof(WCHAR);  
  3.         HANDLE pRemoteLoadLibraryThread = NULL;  
  4.         BOOL bRun = TRUR;  
  5.         do {  
  6.   
  7.             pBufferRemote = ::VirtualAllocEx( hProcess, NULL, dwMemSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );  
  8.             if ( NULL == pBufferRemote ) {  
  9.                 break;  
  10.             }  
  11.             if ( FALSE == WriteProcessMemory( hProcess, pBufferRemote, lpDllPath, dwMemSize, NULL ) ) {  
  12.                 break;  
  13.             }  
  14. ……  

        dwMemSize目前保存的是DLL路径的长度(in bytes)。pBufferRemote指向被注入进程申请的内存块首地址。hProcess是被注入进程的句柄。我们使用VirtualAllocEx在被注入进程中申请一块内存,注意这块内存是PAGE_READWRITE,因为我们要读写(不用执行)这块内存。当你这儿写成PAGE_EXECUTE_READWRITE也没啥问题。在申请空间成功后,使用WriteProcessMemory向刚申请的空间中写入DLL的路径。这儿说一下DLL路径问题,因为Windows在寻找路径的问题上存在一定策略。如果采用的相对路径,windows会优先在本进程所在的目录下寻找这个文件,最后回去到系统相关文件夹下去找。但是它肯定不会全盘去搜索这个文件的。于是我们这儿要写入被注入进程的是DLL的绝对路径,因为我们这个路径被访问的进程(被注入的进程)是谁可能我们自己都不知道,其所在的路径和我们进程路径之间的关系更不知道,化繁为简,于是这儿应该用绝对路径。

        DLL地址已经写好了,我们就要让远线程去Load这个地址所指向的DLL。我们Load DLL文件的函数是LoadLibrary,这个函数是Kernel32.dll中的导出函数。像Kernel32.dll、NtDll.dll等这些系统关键DLL是被映射到系统的0x7FFFFFFF~0xFFFFFFFF(32位系统)地址空间,说到这里不得不介绍windows系统的内存管理问题,32位系统下进程内存地址上限是4G,而程序自身只能在低2G内,高2G是系统文件的映射。而且一般情况下各个程序加载LoadLibaray所在的kernel32.DLL的基地址是一样的,于是我们可以直接指定它的值。这个特性将方便我们执行远线程,否则我们就得编写ShellCode去执行我们的逻辑了(下节会介绍)。

[cpp] view plaincopy
  1. do {  
  2.                 typedef HMODULE (WINAPI *LPLoadLibrary)(LPCWSTR);  
  3.                 LPLoadLibrary pfnLoadLibraryAddr = LoadLibraryW;  
  4.                 if ( NULL == pfnLoadLibraryAddr ) {  
  5.                     break;  
  6.                 }  
  7.                 pRemoteLoadLibraryThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnLoadLibraryAddr, pBufferRemote, 0, NULL );  
  8.                 if ( NULL == pRemoteLoadLibraryThread ) {  
  9.                     break;  
  10.                 }  
  11. ……  
        typedef HMODULE (WINAPI *LPLoadLibrary)(LPCWSTR);这个用于定义函数指针,其对象指向LoadLibraryW(指向A版也可以,但是之前写入的DLL路径也要是A版的)。之后CreateRemoteThread将在被注入进程中创建一个线程会去调用LoadLibrary,我们的DLL就被载入了,可以为所欲为了。哈哈~

       如果想尽量抹掉我们注入的一些痕迹,我们可以把这个逻辑完善些——从被注入进程中卸载远线程载入的DLL。我们要卸载这个DLL,我们就得很耐心的等,等它执行完了。

[cpp] view plaincopy
  1. if ( WAIT_OBJECT_0 != WaitForSingleObject( pRemoteLoadLibraryThread, INFINITE ) ) {  
  2.                     // 远线程出问题了,关闭进程  
  3.                     ::TerminateProcess( hProcess, 0 );  
  4.                     bRun = FALSE;  
  5.                     break;  
  6.                 }  
        因为我不急,所以我使用INFINITE等到底。此处的break是跳出这段代码之外的do{}while(0);*(以后不说明了)。
        终于等到远线程执行完毕,那么我们就开始FreeLibrary吧。别急!FreeLibrary有个参数,是要被卸载的DLL的句柄。这下犯难了。其实没关系,有一个函数GetExitCodeThread。我之前一直没有重视过这个函数,但是这个函数在此场景下发挥了重要作用。我们远线程执行的函数是LoadLibrary,这个函数的返回值是我们加载的DLL的句柄,于是看到GetExitCodeThread这个函数的字面意思,应该就可以想到这个函数应该可以获得我们远线程加载的DLL的句柄,实际也是如此。

[cpp] view plaincopy
  1. // 获取加载模块的句柄  
  2.                 HMODULE hLibModule = 0;  
  3.                 // LoadLibrary的返回值就是这个句柄,所以GetExitCodeThread返回的就是这个句柄  
  4.                 if ( FALSE == GetExitCodeThread( pRemoteLoadLibraryThread, (LPDWORD)&hLibModule ) ) {  
  5.                     break;  
  6.                 }  
        现在不要高兴太早,因为有个疑虑。远线程是在被注入进程中执行的,那么远线程Load的DLL文件的文件句柄应该在被注入进程的地址空间中,我们在注入进程中获得它也不能操作啊?是的,比如我此时调试时,获得的hLibModule = 0x10000000。其实这也不是问题,我们在我们进程中不对这个值做什么操作,我们只是获取到它,然后再把它塞回到被注入进程中,让FreeLibrary远线程在被注入进程中去操作它。

[cpp] view plaincopy
  1. // 获取FreeLibrary函数地址  
  2.                 LPFreeLibrary pfnFreeLibraryAddr = FreeLibrary;  
  3.                 if ( NULL  == pfnFreeLibraryAddr ) {  
  4.                     break;  
  5.                 }  
  6.                 // 远线程卸载DLL  
  7.                 HANDLE pRemoteFreeLibraryThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnFreeLibraryAddr, &hLibModule, 0, NULL );  
        之后是等待FreeLibrary远线程结束和判断其返回值,这儿不再赘述。但是有一个非常关键的操作——恢复主线程(ResumeThread( hThread );)。我想很多做这块的人都发现过,在xp中以挂起方式创建的进程,在被执行完远线程并线程退出后,恢复主线程会导致主进程退出。我也找过相关原因,也没找到足够的理论支持,只是找到两个方法:让远线程一直活着;或者远线程做完事后激发一个事件A并等待另一个事件B,注入进程等到A事件后恢复主线程,主线程执行一段时间后注入进程激发B事件,通知远线程结束。之前的一个方案简单,当然像我们做技术的,总是不能满足于简单。于是我探索了下后一种方案,后一种方案引入一个问题:事件是什么样的?全局命名的事件?如果是全局命名的也太简单了,不讨论。没有名字的?是的,就是使用没有名字的非全局事件。于是这儿又遇到一个问题:如何将这个句柄给远线程呢?现在抛出这个问题,下节我们会讲到使用ShellCode加载我们的DLL,调用DLL中的导出函数并传入参数。最后贴一下之上的完整代码

[cpp] view plaincopy
  1. // 不传参数过去,并且会卸载DLL  
  2.     BOOL HookProcessByCreateRemoteThread( HANDLE hProcess, HANDLE hThread, LPCWSTR lpDllPath )  
  3.     {  
  4.         if ( NULL == hProcess || NULL == hThread || NULL == lpDllPath ) {  
  5.             return FALSE;  
  6.         }  
  7.   
  8.         BOOL bSuc = FALSE;  
  9.   
  10.         void *pBufferRemote = NULL;  
  11.         DWORD dwMemSize =  ( (DWORD) wcslen( lpDllPath ) ) * sizeof(WCHAR);  
  12.         HANDLE pRemoteLoadLibraryThread = NULL;  
  13.         BOOL bRun = TRUE;  
  14.   
  15.         do {  
  16.   
  17.             pBufferRemote = ::VirtualAllocEx( hProcess, NULL, dwMemSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE );  
  18.             if ( NULL == pBufferRemote ) {  
  19.                 break;  
  20.             }  
  21.             if ( FALSE == WriteProcessMemory( hProcess, pBufferRemote, lpDllPath, dwMemSize, NULL ) ) {  
  22.                 break;  
  23.             }  
  24.                   
  25.             do {  
  26.   
  27.                 LPLoadLibrary pfnLoadLibraryAddr = LoadLibraryW;  
  28.                 if ( NULL == pfnLoadLibraryAddr ) {  
  29.                     break;  
  30.                 }  
  31.                 pRemoteLoadLibraryThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnLoadLibraryAddr, pBufferRemote, 0, NULL );  
  32.                 if ( NULL == pRemoteLoadLibraryThread ) {  
  33.                     break;  
  34.                 }  
  35.   
  36.                 if ( WAIT_OBJECT_0 != WaitForSingleObject( pRemoteLoadLibraryThread, INFINITE ) ) {  
  37.                     // 远线程出问题了,关闭进程  
  38.                     ::TerminateProcess( hProcess, 0 );  
  39.                     bRun = FALSE;  
  40.                     break;  
  41.                 }  
  42.   
  43.                 // 获取加载模块的句柄  
  44.                 HMODULE hLibModule = 0;  
  45.                 // LoadLibrary的返回值就是这个句柄,所以GetExitCodeThread返回的就是这个句柄  
  46.                 if ( FALSE == GetExitCodeThread( pRemoteLoadLibraryThread, (LPDWORD)&hLibModule ) ) {  
  47.                     break;  
  48.                 }  
  49.   
  50.                 // 获取FreeLibrary函数地址  
  51.                 LPFreeLibrary pfnFreeLibraryAddr = FreeLibrary;  
  52.                 if ( NULL  == pfnFreeLibraryAddr ) {  
  53.                     break;  
  54.                 }  
  55.                 // 远线程卸载DLL  
  56.                 HANDLE pRemoteFreeLibraryThread = CreateRemoteThread( hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnFreeLibraryAddr, &hLibModule, 0, NULL );  
  57.   
  58.                 do {  
  59.                     if ( NULL == pRemoteFreeLibraryThread ) {  
  60.                         break;  
  61.                     }  
  62.                      
  63.                     if ( WAIT_OBJECT_0 != WaitForSingleObject( pRemoteFreeLibraryThread, INFINITE ) ) {  
  64.                         break;  
  65.                     }  
  66.                     else {  
  67.                         // 查看卸载DLL结果  
  68.                         if ( FALSE == GetExitCodeThread( pRemoteFreeLibraryThread, (LPDWORD)&bSuc ) ) {  
  69.                             break;  
  70.                         }  
  71.                     }  
  72.   
  73.                 } while (0);  
  74.   
  75.                 if ( NULL != pRemoteFreeLibraryThread ) {  
  76.                     CloseHandle( pRemoteFreeLibraryThread );  
  77.                     pRemoteFreeLibraryThread = NULL;  
  78.                 }  
  79.   
  80.             }while (0);  
  81.   
  82.             if ( NULL != pRemoteLoadLibraryThread ) {  
  83.                 CloseHandle( pRemoteLoadLibraryThread );  
  84.                 pRemoteLoadLibraryThread = NULL;  
  85.             }  
  86.   
  87.         }while(0);  
  88.           
  89.         if ( bRun ) {  
  90.             ResumeThread( hThread );  
  91.         }  
  92.   
  93.         return bSuc;  
  94.     }  

(转载请指明出处)
原创粉丝点击