线程注入

今天共享一篇线程注入的知识,提到注入大家会 想到的是不好的名词对吗,其实注入是在调试工具中最常用的如 VC 自带的DLL查看器(Depends.Exe)、微点 的主动防御软件、Symbian 内存泄露检测工具HookLogger 等等。

本文适合人群为高级程序员、高级技术人员和中 级程序员等。如果对进程,线程,虚拟内存管理,DLL以及字符集编码不了解的,那就回头翻 一翻相关知识再看本文。本文用到了大量的WIN32 API,也希望大家有关于WIN32 API的储备知识来阅读本文。本文的DLL主 要是获取目标进程所加载的DLL。目标进程是我们获取的任何一个进程。

切入主题,何谓注入?注入就是把自己的DLL或者代码注入的目标进程中,来达到获取相关信息的目的。在此我想分三步介绍,第一步先介绍注入整个思 路,第二步画一个顺序图给大家一个直观的了解,第三步就是贴代码,带大家进入代码的世界。

第一步思路介绍

首先要提升本进程权限,提权代码不做重点介 绍,在第三步上会给出。其次是系统进程的查找,不做介绍,在第二步中给出流程图,在第三步中给出代码;最后还是讲我们的线程注入。

刚才介绍了线程注入,线程注入就是把自己的DLL或 者代码注入的目标进程中,来达到获取相关信息的目的。那如何让它们加载我们的代码或者DLL呢, 在WIN32 中提供了LoadLibrary这个函数,就可以提供这个功能,具体用法大家可以参考一下MSDN的介绍,在此不做介绍了。但有一点要注意,在编码使用字符集的时候要正确的引用,否则会调用失败。就 以LoadLibrary为例,看一看在代码中的定义吧:

#ifdef UNICODE

#define LoadLibrary  LoadLibraryW

#else

#define LoadLibrary  LoadLibraryA

#endif // !UNICODE

在使用UNICODE字符集时使用的是LoadLibraryW,在使用ASCII字 符集时用的是LoadLibrayA。有了LoadLibrary还是不 行,因为Windows函数只允许一个进程对它自己进程操作,这样可以防止一个进程对对另一个进程进行破坏。但是Windows也提供了一些函数允许一个进程对另外一个进程操作。如WIN 32 API CreateRemoteThread就是其中之一,查MSDN 可 知CreateRemoteThread 就可以实现我们在目标进程中创建一个新线程的目的。其原 型如下:

HANDLE CreateRemoteThread(

  HANDLE hProcess,

  LPSECURITY_ATTRIBUTES lpThreadAttributes,

  SIZE_T dwStackSize,

  LPTHREAD_START_ROUTINE lpStartAddress,

  LPVOID lpParameter,

  DWORD dwCreationFlags,

  LPDWORD lpThreadId

);

参数我简单的介绍一下吧,HANDLE hProcess 就是我们想要注入进程的句柄;LPSECURITY_ATTRIBUTES lpThreadAttributes, 设置我们线程安全属性的,可以不设置其值;SIZE_T dwStackSize,设置我们线程栈需要空间的大小;LPTHREAD_START_ROUTINE lpStartAddress 函数地址;LPVOID lpParameter 函数需要的参数;DWORD dwCreationFlags 我们创建线程的FLAGLPDWORD lpThreadId,创建线程的ID。通过这些知识我们是不是有思路了呢?是的,lpStartAddress就是我们LoadLibrary的地址,lpParameter参数就是我们的DLL的路径,或者是代码。这样就实现了注入。

或许有人会问我们的代码注入了系统进程,系统系统又是如何执行我们DLL中的代码呢?原因是:当DLL被加载到进程执行时,系统会向我们的DLL发一个DLL_PROCESS_ATTACH和一个DLL_THREAD_ATTACH消息。DLL_PROCESS_ATTACHDLL在加载到进程的地址空间进行相关的初始化发送的消息。而DLL_THREAD_ATTACH 则是系统执行线程时发送 的一个消息。所有当我们的DLL接受到如上消息时则自动 运行。

方法简单吧,但是有几个地方需要注意的。第一,我们要获取LoadLibrary的真实地址。第二,输入有效的参数。刚才讲过进程是不能直接操 作另一个进程的。所以我们的lpParameter参数,在本进程中是有 效的,但是在目标进程中是无效的。那该如何使其在目标进程中叶有效呢?我们看MSDN上的VirtualAllocEx WriteProcessMemory这两个API。他们原型如下:

LPVOID VirtualAllocEx(

  HANDLE hProcess,

  LPVOID lpAddress,

  SIZE_T dwSize,

  DWORD flAllocationType,

  DWORD flProtect

);

简单介绍一下相关参数 HANDLE hProcess 就是目标进程句柄,LPVOID lpAddress 就是指定位置要开辟的 内存,可有可无;SIZE_T dwSize要开辟的大小;DWORD flAllocationType,分配的类型;DWORD flProtect 分配内存具有的属性。 返回值就是在目标进程中分配内存的首地址。翻译的或许不好大家可有参考MSDN

BOOL WriteProcessMemory(

  HANDLE hProcess,

  LPVOID lpBaseAddress,

  LPCVOID lpBuffer,

  SIZE_T nSize,

  SIZE_T* lpNumberOfBytesWritten

);

同理。HANDLE hProcess 目标进程的句柄;LPVOID lpBaseAddress开辟的地址空间;LPCVOID lpBuffer 要写入的数据的地址;SIZE_T nSize写入的字节数;SIZE_T* lpNumberOfBytesWritten 实际写入的字节数。

通过如上两个API就可有在目标进程中开 辟有效地址,在其地址中写入有效数据了。这样目标进程就可以识别我们DLL的路径了。第三,注意 编码的字符集,不要把UNICODEASCII码编码时的函数搞混了。第四,不要忘记释放在目标进程中开辟的空间,具体应 用可参考 WIN32 API VirtualFreeEx函数,也不要忘记Dll执行完后要释放加载的Dll哦。

所需理论知识介绍完了,接下来进行我们的第二步流程图介绍。流程图介绍分两步一步是进程的操作,一步是注入的操作。 流程图如下:

进程查找

创建进程



提升进程权限



遍历进程,保存至容器中

查找要注入的进程



注入





线程注入

目标进程



开辟存储数据的空间



写入有效数据



在目标进程中创建线程



我们的DLL被调用

 

第三步主要是共享主要代码。

第一 我们DLL的代码。DLL的代码的功能就是获取目标进程加载的DLL

void GetProcessInfoFromRemoteProcess(HMODULE hModule)

{ 

   // 获取计算机名称

   WSADATA data;

   char hostName[MAX_PATH] = {0};

   int error = -1;

   // 网路环境初始化

   if ( 0 == WSAStartup(MAKEWORD(2,2), &data))

   {

      // 成功返回0

      error = gethostname(hostName,MAX_PATH);

   }

   DWORD dProcID = ::GetProcessId(hModule);

   HANDLE hModuleSnap = INVALID_HANDLE_VALUE;

   MODULEENTRY32 me32;

   // 创建文件映射文件

   hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dProcID );

   if( hModuleSnap == INVALID_HANDLE_VALUE )

   {

      return ;

   }

 

   // 初始化

   me32.dwSize = sizeof( MODULEENTRY32 );

   // 遍历开始

   if( !Module32First( hModuleSnap, &me32 ) )

   {

      return ;

   }

   char path[MAX_PATH] = {0};// 文件路径

   if (error)

   {

      sprintf(path,"%s","c:\\han_wjian.log");

   }

   else

   {

      sprintf(path,"c:\\%s.log",hostName);

   }

  

   FILE* file = fopen(path,"w+"); // 打开一个文件指针

   if (NULL == file)

   {

      return;

   }

   char buf[MAX_PATH] = {0}; // 文件缓冲区

   sprintf(buf," 进程名:     %S\r\n", me32.szModule);

   fwrite(buf,1,strlen(buf)+1,file);

      while( Module32Next( hModuleSnap, &me32 ) )

   {    

      sprintf(buf,"加载动态库    = %S\r\n", me32.szExePath);

           // 遍历到的数据写入文件中

      fwrite(buf,1,strlen(buf)+1,file);  

 

   }

   CloseHandle( hModuleSnap );

   fclose(file); // 关闭文件指针,释放内存

   WSACleanup();

}

 

第二 提升进程权限的代码

void GetSystemToken(HANDLE hSelf)

{

   // 打开当前进程的句柄

   HANDLE GetHandle;

   if (NULL != hSelf)

   {

      if (::OpenProcessToken(hSelf, TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&GetHandle))

      {

        TRACE(L"OpenProcessToken ID = %d\n",GetLastError());

        TOKEN_PRIVILEGES Token; // 指令牌

        Token.PrivilegeCount = 1;

        DWORD hLeng = sizeof(TOKEN_PRIVILEGES);

        // 遍历Debug 调 试权限的LUID

        if (::LookupPrivilegeValue(NULL, SE_DEBUG_NAME,&Token.Privileges[0].Luid))

        {

           Token.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

 

           // 赋予进程的权限

           AdjustTokenPrivileges(GetHandle, FALSE, &Token,sizeof(Token),NULL,NULL);

           TRACE(L"AdjustTokenPrivileges ID = %d\n",GetLastError());

        }

        TRACE(L"LookupPrivilegeValue ID = %d\n",GetLastError());

      }

      ::CloseHandle(GetHandle);

   }

}

 

第三 进程遍历代码

BOOL GetProcess()

{

   HANDLE hProcessSnap;

   PROCESSENTRY32 pe32; // 进程属性

   // 创建映射句柄.

   hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );

   if( hProcessSnap == INVALID_HANDLE_VALUE )

   {

      return FALSE;

   }

 

   pe32.dwSize = sizeof( PROCESSENTRY32 );

 

   // 遍历系统进程

   if( !Process32First( hProcessSnap, &pe32 ) )

   {

      CloseHandle( hProcessSnap );    

      return FALSE;

   }

   while( Process32Next( hProcessSnap, &pe32 ) )

   {

      // 把查找到的进程放入容器中

      m_mapProcess.insert(MapContinar::value_type(pe32.szExeFile, pe32.th32ProcessID));

   }

 

   CloseHandle( hProcessSnap );

   return( TRUE );

}

 

第四 是注入代码

void Inject(int iProID)

{

#define MAXMEMORY   4000

#define REMOTESAPCE 125

 

   TCHAR Buf[REMOTESAPCE] = {0} ; // 注入DLL的路径

   lstrcpy(Buf, L"GetProcessInfo.dll");

   int leng = lstrlen(Buf) + 1;

   leng = leng * sizeof(TCHAR);

 

   // 打开目标进程并赋予足够权限

   HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ, FALSE, iProID);

   TRACE(L"OpenProcess ID = %d\n",GetLastError());

   if (NULL != hProcess)

   {

      // 在目标进程中开辟足够空间存放 数据

      PWSTR pRemoteSpace = NULL; // 在目标进程里开辟的空间

      pRemoteSpace = (PWSTR)::VirtualAllocEx(hProcess, NULL,MAXMEMORY,MEM_COMMIT,PAGE_READWRITE);

      TRACE(L"VirtualAllocEx ID = %d\n",GetLastError());

      if (NULL != pRemoteSpace)

      {

        if (::WriteProcessMemory(hProcess, pRemoteSpace, (PVOID)Buf,leng,NULL))

        {

           TRACE(L"WriteProcessMemory ID = %d\n",GetLastError());

       

           // 获取LoadLibary的真实地址

           PTHREAD_START_ROUTINE pFun = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(L"Kernel32.dll"),"LoadLibraryW");

           TRACE(L"LoadLibraryW ID = %d\n",GetLastError());          

 

           if (NULL != pFun)

           {

              DWORD ThreadID = 0;

              HANDLE hThread = ::CreateRemoteThread(hProcess,NULL,0,pFun,pRemoteSpace,0,&ThreadID);

              TRACE(L"Child Thread is  =     %d Errord = %d\n",ThreadID,GetLastError());

              if (NULL != hThread)

              {

                 DWORD  hLibModule = 0;

 

                 ::WaitForSingleObject(hThread, INFINITE);

                 ::GetExitCodeThread( hThread, &hLibModule ); // 获取进程执行码

                 if (!hLibModule)

                 {

                    return;

                 }

 

                 // 释放在目标进程中开辟的空间

                 ::VirtualFreeEx(hProcess,pRemoteSpace,0,MEM_RELEASE);

 

                 // 卸载加载的dLL

                 UnInject(iProID, Buf, hProcess);

 

                 ::CloseHandle(hThread);

              }

           }

        }

      }

   }

   ::CloseHandle(hProcess);

}

 

第五是卸载代码

void UnInject(int iProID ,PCWSTR iName,HANDLE hThread)

{

   HANDLE hProcessSnap;

   MODULEENTRY32 pe32; // 进程加载模块属性

   // 创建映射句柄.

   hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, iProID);

   if( hProcessSnap == INVALID_HANDLE_VALUE )

   {

      return ;

   }

   pe32.dwSize = sizeof( MODULEENTRY32);

   // 遍历系统进程

   if( !Module32First( hProcessSnap, &pe32 ) )

   {

      CloseHandle( hProcessSnap ); 

      return ;

   }

   while( Module32Next( hProcessSnap, &pe32 ) )

   {

      TRACE(L"Remote Process had been load dll =   %s\n",pe32.szModule);

      if ( (_wcsicmp(pe32.szExePath, iName) ==  0)|| (_wcsicmp(pe32.szModule, iName) ==  0))

      {

        TRACE(L"############### Find Success #######################\n");

        // 获取系统卸载DLL api

        PTHREAD_START_ROUTINE pFun = (PTHREAD_START_ROUTINE)::GetProcAddress(::GetModuleHandle(L"Kernel32.dll"),"FreeLibrary");

        HANDLE hTmp = ::CreateRemoteThread(hThread,NULL,0,pFun,pe32.modBaseAddr,0,NULL);

        if (NULL != hTmp)

        {

           ::WaitForSingleObject(hTmp, INFINITE);

           ::CloseHandle(hTmp);

        }

        break;

      }

   }

   CloseHandle( hProcessSnap );

}

posted on 2010-06-03 00:05  carekee  阅读(3273)  评论(0编辑  收藏  举报