获取指定进程所有线程的当前执行模块

    对于可扩充架构的软件,都存在着很多的插件。这样相对于主模块,插件的工作就是不安全的。为了防止插件的不安全性影响到主模块甚至全局,很多框架都采取了好的方式隔离插件的执行,如.NET中的执行域(AppDomain)。对于那些采用Exe/DLL架构的原始代码,DLL的运行相对于EXE来说就是不安全性的。为了最大限度的隔离DLL的运行,防止其对宿主EXE造成影响。需要做以下几点:
    首先需要在主模块中采用结构化异常处理防止EXE对DLL导出函数的调用出现异常。这类问题一般比较容易解决。
    其次,主模块和插件之间的通信必须有所限制——主模块和插件都不应该假定对方和自己存在于一个地址空间,也就是说不直接使用对方的地址。
    最后,主模块需要安装未处理异常过滤器,对于常规错误,定位错误的位置,然后采取必要的修复措施,甚至终结主模块。
    第二点的目的是为了让第三点能够完好的工作。当未处理异常过滤器获取到异常时,首先定位异常的所在模块,然后检测哪些线程正位于此模块,接着终结所有位于此模块的线程,最后卸载此模块(也许会重新载入)。由于第一点已经解决了边界处的异常,第二点又屏蔽了非边界的模块调用,这样一个线程只要此时位于此模块,就肯定是此模块中的代码创建的;同样,如果一个线程当前不再此模块中,也不可能通过非边界进入此模块,通过边界进入的第一点也会保证其一致性。因此终结了这些线程以后,就可以安全的卸载此模块。
    下面说说完成这些工作的核心点——获取指定进程所有线程的当前执行模块。首先可以通过CreateToolhelp32Snapshot获取线程快照,然后根据线程信息确定其是否属于指定线程。获取到线程后,可以通过GetThreadContext获取线程当前的上下文,根据上下文中的Eip就可以获取当前调用帧的代码地址。使用VirtualQuery可以获取此地址所述的模块句柄,然后GetModuleFileName就可以获取到此模块的名称了。这里还有一点需要说明,当你获取模块名时,返回的可能是系统模块的模块名(大部分情况下都是的),因而需要根据栈帧信息回溯到上一个函数地址,一直到函数地址位于非系统模块中。函数调用过程中,EBP保存了当前栈帧的基址,并且被压入下一栈帧的首地址,因而利用EBP就可以遍历整个函数调用,而函数调用地址就保存在上一栈帧的末地址上。
    下面是一个获取当前进程所有线程的当前执行模块信息的函数:

VOID GetThreadMsg(CHAR lpszBuffer[], UINT32 iBufferSize)
{
 CHAR lpszTemp[MAX_PATH] = {0};
 DWORD dwProcessId = GetCurrentProcessId();
 CHAR lpszCurDirectory[MAX_PATH] = "C://Windows";
 UINT32 iCurDirectoryLen = (UINT32)(INT64)strlen(lpszCurDirectory)-1;
 sprintf_s(lpszBuffer, iBufferSize, "当前进程: %d, 线程信息如下:", dwProcessId);

 HANDLE hThreadSnap;
 THREADENTRY32 th32;

 if ((hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessId)) != NULL
  && hThreadSnap != INVALID_HANDLE_VALUE)
 {
  th32.dwSize = sizeof(THREADENTRY32);

  if (Thread32First(hThreadSnap, &th32))
  {
   do
   {
    if (th32.th32OwnerProcessID == dwProcessId && th32.th32ThreadID != GetCurrentThreadId())
    {
     sprintf_s(lpszTemp, MAX_PATH, "/r/nId: %8x", th32.th32ThreadID);
     strcat_s(lpszBuffer, iBufferSize, lpszTemp);
     
     HANDLE hThread = OpenThread(THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_TERMINATE
      , FALSE, th32.th32ThreadID);
     CONTEXT context = {0};
     context.ContextFlags = CONTEXT_CONTROL;

     if(hThread != NULL && hThread != INVALID_HANDLE_VALUE)
     {
      SuspendThread(hThread);
      BOOL bOK = GetThreadContext(hThread, &context);
      ResumeThread(hThread);

      if(bOK)
      {
       DWORD dwAddr = context.Eip;
       DWORD dwEbp = context.Ebp;

       do
       {
        MEMORY_BASIC_INFORMATION mbi = {0};
        if(VirtualQuery((LPCVOID)(INT64)dwAddr, &mbi, sizeof(mbi)))
        {
         UINT_PTR h_module = (UINT_PTR)mbi.AllocationBase;
         GetModuleFileNameA((HMODULE)h_module, lpszTemp, MAX_PATH);

         if(_strnicmp(lpszTemp, lpszCurDirectory, iCurDirectoryLen))
         {
          strcat_s(lpszBuffer, iBufferSize, " CurFrameBase: ");
          sprintf_s(lpszBuffer+strlen(lpszBuffer), iBufferSize-strlen(lpszBuffer), "%8x", dwAddr);

          strcat_s(lpszBuffer, iBufferSize, " CurModuleName: ");
          strcat_s(lpszBuffer, iBufferSize, lpszTemp);
          break;
         }
        }

        if((dwEbp = *(PDWORD)(INT64)dwEbp) == 0)
        {
         break;
        }

        dwAddr = ((PDWORD)(INT64)dwEbp)[1];
       } while (TRUE);
      }

      CloseHandle(hThread);
     }     
    }
   } while(Thread32Next(hThreadSnap, &th32));
  }
  
  CloseHandle(hThreadSnap);
 }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值