前段时间写的代码,写完后一运行就出错,又因为手头有点事,就放在那了.今天中午没事拿出来和"雪"牛一起看了看,终于能跑起来了.这种被ken戏称称为"吸星大法"的技术,确实没啥新意,只当练手吧.顺便温习下IAT HOOK和PE结构.高手飘过!
大体说下思路:首先利用远程线程注入到系统的explorer.exe进程,注入后利用IAT HOOK,勾住explorer.exe的CreateProcessA,因为应用程序都是这个桌面进程拉起来的.所以后面创建的进程,都会走进我们挂钩后的函数.在我们的函数中做下过滤,根据进程名判断是否是我们感兴趣的进程.如果是,以CREATE_SUSPENDED标志创建进程,此时该进程虽然被创建但已被挂起.这时,我们可以将进程对应的可执行文件映射进内存,根据PE结构知道该进程的EP,然后修改EP处的代码.我这里选择的方式是,从EP开始搜索第一个call xxxx,然后修改xxxx处的地址,直接跳转到我们的代码处(当然必须先将我们的代码写入目标进程中),执行完我们的代码,再JMP到call对应的xxxx处.至于我们的代码中要干什么,想干什么就干什么了,我这里只是向进程load了一个dll.有人可能会问,这样不是闲着蛋疼,没事找事,直接为目标进程创建一个LoadlibraryA的远程线程不就load一个dll了,问题是有些进程自己做了保护,程序运行起来后,就不那么轻易让你注入了.废话不多说,直接上代码吧!
代码1:向系统的explorer.exe进程注入DLL
这段代码没什么好解释的,就是在系统进程快照中查找名叫explorer.exe的进程id,然后通过OpenProcess将进程映射到本进程的句柄表中,再远程分配空间,写内存,创建远程线程.

代码:
void main()
{
  char lpDllName[100] = {0};
  GetCurrentDirectory( 100, lpDllName );
  strcat( lpDllName, "\\InjectProcessDll.dll" );
  cout<<"Dll当前路径:"<<lpDllName<<endl;

  HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
  if( hSnapshot == NULL )
  {
    cout<<"Create Snapshot false."<<endl;
    cin.get();
    return;
  }
  PROCESSENTRY32 stProcessEntry32 = {0};
  stProcessEntry32.dwSize = sizeof(PROCESSENTRY32);
  Process32First( hSnapshot, &stProcessEntry32 );
  bool bFind = false;
  do
  {
    if( strncmp( stProcessEntry32.szExeFile, "explorer.exe", strlen("explorer.exe") ) == 0 )
    {
      bFind = true;
      break;
    }
  }while( Process32Next( hSnapshot, &stProcessEntry32 ) );
  CloseHandle( hSnapshot );
  if( !bFind )
  {
    cout<<"查找explorer进程失败."<<endl;
    cin.get();
    return;
  }

  DWORD dwPId = stProcessEntry32.th32ProcessID;
  HANDLE hProcess = OpenProcess(  PROCESS_ALL_ACCESS, false, dwPId );
  if( hProcess == NULL )
  {
    cout<<"打开explorer进程失败."<<endl;
    cin.get();
    return;
  }
  LPVOID lpDllNameAddr = VirtualAllocEx( hProcess, NULL, strlen(lpDllName)+1, MEM_COMMIT, PAGE_READWRITE );
  if( lpDllNameAddr == NULL )
  {
    cout<<"explorer进程中申请内存失败."<<endl;
    CloseHandle(hProcess);
    cin.get();
    return;
  }
  cout<<"在目标进程:"<<stProcessEntry32.szExeFile<<"中申请的空间地址:"<<hex<<lpDllNameAddr<<endl;
  DWORD dwRes = 0;
  bool bRet = WriteProcessMemory( hProcess, lpDllNameAddr, lpDllName, strlen(lpDllName), &dwRes );
  if( !bRet )
  {
    cout<<"explorer进程写信息失败."<<endl;
    VirtualFreeEx( hProcess, lpDllNameAddr, strlen(lpDllName)+1, MEM_DECOMMIT );
    CloseHandle(hProcess);
    cin.get();
    return;

  }
  HMODULE hModuleKernel32 = GetModuleHandle("kernel32.dll");
  LPTHREAD_START_ROUTINE lpLoadLibraryAddr = (LPTHREAD_START_ROUTINE)GetProcAddress( hModuleKernel32, "LoadLibraryA" );
  if( lpLoadLibraryAddr != NULL )
  {
    cout<<"获得函数地址:"<<hex<<lpLoadLibraryAddr<<endl;
    HANDLE hRemote = CreateRemoteThread( hProcess, NULL, 0, lpLoadLibraryAddr, lpDllNameAddr, 0, NULL );
    if( hRemote != NULL )
    {
      cout<<"创建远程线程成功,句柄:"<<hex<<hRemote<<endl;
      WaitForSingleObject( hRemote, INFINITE );
      CloseHandle( hRemote );
      cout<<"远程线程运行结束"<<endl;
      cin.get();

    }
    else
      cout<<"创建远程线程失败."<<endl;
  }
  else
    cout<<"获取LoadLibrary地址失败"<<endl;

  
  VirtualFreeEx( hProcess, lpDllNameAddr, strlen(lpDllName)+1, MEM_DECOMMIT );
  CloseHandle(hProcess);
  cin.get();
  return;


}
代码2:现在已经在explorer.exe的空间,IAT Hook该进程的CreateProcessA函数
该段代码从功能上分两个部分:(1).IAT HOOK CreateProcessA函数,刚开始我也以为直接搜索explorer.exe的IAT,如下图:
但不是想象的那样,用OD在CreateProcessW处下段,居然断在Shell32.dll空间,于是只好IAT HOOK Shell32.dll中IAT结构中的CreateProcessW,将其入口指向我们自己的函数MyCreateProcess.如下图:

详细见代码:
代码:
BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
           )
{
  switch( ul_reason_for_call )
  {
  case DLL_PROCESS_ATTACH:
    {  
      
      OutLog("dll attach.");
      char pModule[50] = {0};
      sprintf( pModule, "Module Base:%x", hModule );
      OutLog(pModule);
      MessageBox( NULL, pModule, NULL, MB_OK );
      hInstance = (HINSTANCE)hModule;
      HookCreateProcess(true);
      break;
    }
  case DLL_PROCESS_DETACH:
    {      
    HookCreateProcess(false);
    break;
    }
  case DLL_THREAD_ATTACH:
  case DLL_THREAD_DETACH:  
    break;
  }

    return TRUE;
}


void HookCreateProcess( bool bHook)
{
  HMODULE hModule = GetModuleHandle( "SHELL32.DLL" );
  if( hModule != NULL )
  {
    PIMAGE_DOS_HEADER pstDosHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pstNtHeaders = (PIMAGE_NT_HEADERS)((ULONG)hModule + (ULONG)(pstDosHeader->e_lfanew));
    IMAGE_OPTIONAL_HEADER stOptionalHeader = pstNtHeaders->OptionalHeader;
    IMAGE_DATA_DIRECTORY stImportDataDirectory = stOptionalHeader.DataDirectory[1];//导入表结构
    PIMAGE_IMPORT_DESCRIPTOR pstImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)( (ULONG)hModule + (ULONG)(stImportDataDirectory.VirtualAddress) );
  
    PIMAGE_THUNK_DATA pThunkData = NULL;
    while( pstImportDescriptor->FirstThunk != 0 )
    {
      char *pDllName = (char*)((ULONG)hModule + pstImportDescriptor->Name );
      if( 0 == strnicmp( pDllName, "kernel32.dll", strlen("kernel32.dll") ) )
      {
        OutLog("找到导入表中kernel32.dll结构.");
        pThunkData = (PIMAGE_THUNK_DATA)((ULONG)hModule + pstImportDescriptor->FirstThunk);
        break;
      }
      pstImportDescriptor++;
    }
    if( pThunkData != NULL )
    {
      while( pThunkData->u1.Function != 0 )
      {
        ULONG *lpFuncAddr = (PULONG)&(pThunkData->u1.Function);
        ULONG ulCreateProcess = (ULONG)GetProcAddress( GetModuleHandle("kernel32.dll"), "CreateProcessW" );

        char p[50] = {0};
        sprintf( p, "%x-%x-MyCreateProcessA:%x-%x",*lpFuncAddr,lpFuncAddr,MyCreateProcess, ulCreateProcess );
        OutLog( p );

        if( *lpFuncAddr == ulCreateProcess )
        {
          OutLog("找到kernel32.dll结构中CreateProcessW函数地址.");
          dwOrigCreateProcessAddr = *lpFuncAddr;
          DWORD dwOldProtect = 0;

          MEMORY_BASIC_INFORMATION stMemBasicInfo = {0};
          VirtualQuery( &lpFuncAddr, &stMemBasicInfo, sizeof(MEMORY_BASIC_INFORMATION) );
          VirtualProtect( stMemBasicInfo.BaseAddress, stMemBasicInfo.RegionSize, PAGE_READWRITE, &dwOldProtect );
          if( bHook )
          {
            DWORD lpDw = (DWORD)MyCreateProcess;
            HANDLE h = ::GetCurrentProcess();
            
            bool bOk = ::WriteProcessMemory( h, lpFuncAddr, &lpDw, sizeof(DWORD), NULL );
            //*lpFuncAddr = (ULONG)MyCreateProcess;
            if(bOk == false )
              OutLog("改写kernel32.dll结构中CreateProcessW函数地址失败");

          }
          else
            ::WriteProcessMemory( ::GetCurrentProcess(), lpFuncAddr, &dwOrigCreateProcessAddr, sizeof(DWORD), NULL );
          VirtualProtect( stMemBasicInfo.BaseAddress, stMemBasicInfo.RegionSize, dwOldProtect, NULL );
          break;
        }
        pThunkData++;
      }
    }
    else
      OutLog("没有找到导入表中kernel32.dll结构.");


  }
  else
    OutLog("获取Shell32.dll失败.");
}
(2).现在运行的应用程序会先走到下面这个函数,如果是自己感兴趣的应用,则通过修改EP注入.

代码:
BOOL WINAPI MyCreateProcess(
  LPCTSTR lpApplicationName,
  LPTSTR lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL bInheritHandles,
  DWORD dwCreationFlags,
  LPVOID lpEnvironment,
  LPCTSTR lpCurrentDirectory,
  LPSTARTUPINFO lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
)
{
  DWORD dwNum = WideCharToMultiByte( CP_OEMCP, 0, (const unsigned short*)lpApplicationName, -1, NULL, 0, NULL, false );
  if( dwNum != 0 )
  {
    char *lpAnsiName = new char[dwNum+1];
    dwNum = WideCharToMultiByte( CP_OEMCP, NULL, (const unsigned short*)lpApplicationName, -1, lpAnsiName, dwNum+1, NULL, false );

    OutLog( lpAnsiName );
    char lpExePath[100] = {0};
    strncpy( lpExePath, lpAnsiName, strlen(lpAnsiName) );
    
    char lpExeName[50] = {0};
    char *p = strrchr( lpAnsiName, '\\' ); 
    strncpy( lpExeName, p+1, strlen(p+1) );
    OutLog( lpExeName );
    delete lpAnsiName;
    //如果是感兴趣的进程被创建,将通过EPO技术挂关键函数,这里只是修改进程的EP入口,对目标进程实现dll注入
    if( 0 == strncmp( lpExeName, "notepad.exe", strlen("notepad.exe") ) )
    {

      dwCreationFlags |= CREATE_SUSPENDED;
      bool bRes = ((LPCREATEPROCESS)dwOrigCreateProcessAddr)( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes,
              bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation );

      //重新映射可执行文件,寻找EP点
      HANDLE hFile = CreateFile( lpExePath, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
      if( hFile == NULL )
      {
        OutLog( "打开目标文件失败" );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        return bRes;
      }
      HANDLE hMapping = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0, 0, NULL );
      CloseHandle( hFile );
      if( hMapping == NULL )
      {
        OutLog( "打开目标文件映射失败" );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        return bRes;
      }
      LPVOID lpMapFile = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
      if( lpMapFile == NULL )
      {
        OutLog( "映射目标文件失败" );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        CloseHandle( hMapping );
        return bRes;

      }
      CloseHandle( hMapping );

      //已经完成文件映射
      PIMAGE_DOS_HEADER lpstDosHeader = (PIMAGE_DOS_HEADER)lpMapFile;
      PIMAGE_NT_HEADERS lpstNtHeaders = (PIMAGE_NT_HEADERS)( (ULONG)lpMapFile + lpstDosHeader->e_lfanew );
      DWORD dwPeEntry = lpstNtHeaders->OptionalHeader.AddressOfEntryPoint+lpstNtHeaders->OptionalHeader.ImageBase;
      DWORD dwSizeOfImage = lpstNtHeaders->OptionalHeader.SizeOfImage;
      char lpEntry[50] = {0};
      sprintf( lpEntry, "EntryAddr:%x", dwPeEntry );
      OutLog( lpEntry );

      //加载dll方式:从程序的入口点,寻找第一个call,然后替换这个call地址,从而转到我们的shellcode中,执行完shellcode
      //再继续走原来的函数流程
      //1.查找第一个call地址
      BYTE bCode = 0;
      DWORD dwReadAddr = dwPeEntry;
      while( bCode != 0xe8 )
      {
        bool bOk = ReadProcessMemory( lpProcessInformation->hProcess, (LPVOID)dwReadAddr, &bCode, 1, NULL );
        if( !bOk )
        {
          OutLog( "查找第一个call地址出错" );
          if( bRes )
            ResumeThread( lpProcessInformation->hThread );
          return bRes;
        }
        dwReadAddr++;

      }
      //计算被替换函数的入口地址
      DWORD dwCallAddr  = 0;
      bool bOk = ReadProcessMemory( lpProcessInformation->hProcess, (LPVOID)dwReadAddr, &dwCallAddr, 4, NULL );
      if( !bOk )
      {
        OutLog( "查找第一个call地址出错" );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        return bRes;
      }
      //计算call的目标函数地址
      DWORD dwChangedFuncAddr = dwReadAddr+4+dwCallAddr;
      char s[50] = {0};
      sprintf( s, "CallAddr:%x", dwChangedFuncAddr );
      OutLog( s );
      //1.编写shellcode
      //shellcode格式
/*
      
$ ==>    >  60              pushad
$+1      >  9C              pushfd
$+2      >  68 11111111     push    11111111  //加载的dll名称
$+7      >  E8 444288A5     call    22222222  //LoadLibraryA地址
$+C      >  9D              popfd
$+D      >  61              popad
$+13     >- E9 495399B6     jmp     33333333  //跳转到第一个call函数开始
*/
      char lpShellCode[] = {
          0x60,
          0x9c,
          0x68,0x90,0x90,0x90,0x90,
          0xe8,0x90,0x90,0x90,0x90,
          0x9d,
          0x61,
          0xe9,0x90,0x90,0x90,0x90};
          
      char lpCurrentDirectory [100] = {0};
      GetCurrentDirectory( 100, lpCurrentDirectory );
      strncat( lpCurrentDirectory, "\\xx.dll", strlen("\\xx.dll") );//构建你想注入到目标进程的dll完成路径
      LPVOID lpDllPathAddr = VirtualAllocEx( lpProcessInformation->hProcess, NULL, strlen(lpCurrentDirectory)+1, MEM_COMMIT, PAGE_READWRITE );
      DWORD dwErr = GetLastError();
      char lpErr[50] = {0};
      
      if( lpDllPathAddr == NULL )
      {
        sprintf( lpErr, "ErrorCode:%d", dwErr );
        OutLog( lpErr );
        OutLog( "映射目标进程申请空间失败" );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        return bRes;
      }
      char tp[50] = {0};
      sprintf( tp, "申请的dll路径空间:%x", lpDllPathAddr );
      OutLog( tp );
      bOk = WriteProcessMemory( lpProcessInformation->hProcess, lpDllPathAddr, lpCurrentDirectory, strlen(lpCurrentDirectory), NULL);
      if( !bOk )
      {
        OutLog( "写目标进程空间dll路径失败" );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        VirtualFree( lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT );
        return bRes;
      }
      OutLog( "写dll路径到目标地址空间成功" );
      
      
      LPVOID lpFuncAddr = GetProcAddress( GetModuleHandle("kernel32.dll"), "LoadLibraryA" );
      memset( tp, 0, 50 );
      sprintf( tp, "获取的目标进程中LoadLibraryA地址:%x", lpFuncAddr );
      OutLog( tp );
      
      LPVOID lpShellCodeAddr = VirtualAllocEx( lpProcessInformation->hProcess, NULL, strlen(lpShellCode)+1, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
      if( lpShellCodeAddr == NULL )
      {
        OutLog( "申请目标进程空间shellcode失败" );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        VirtualFree( lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT );
        return bRes;
      }
      memset( tp, 0, 50 );
      sprintf( tp, "获取的目标进程中ShellCode地址:%x", lpShellCodeAddr );
      OutLog( tp );

      //组合shellcode
      memcpy( lpShellCode+3, (char*)&lpDllPathAddr, 4 );
      DWORD dwFuncAddr = (DWORD)lpFuncAddr - ((DWORD)lpShellCodeAddr+7)-5;
      memcpy( lpShellCode+8, (char*)&dwFuncAddr, 4 );
      DWORD dwJmpEnd = dwChangedFuncAddr - ((DWORD)lpShellCodeAddr+14) -5;
      memcpy( lpShellCode+15, (char*)&dwJmpEnd, 4 );

      //3.写入shellcode,及改变原来函数入口
      bOk = WriteProcessMemory( lpProcessInformation->hProcess, lpShellCodeAddr, lpShellCode, 30, NULL );
      if( !bOk )
      {
        OutLog( "写目标进程shellcode失败" );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        VirtualFreeEx( lpProcessInformation->hProcess, lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT );
        VirtualFreeEx( lpProcessInformation->hProcess, lpShellCodeAddr, strlen(lpShellCode)+1, MEM_DECOMMIT );
        return bRes;
      }
      //计算call的新地址
      DWORD dwNewCallAddr = (DWORD)lpShellCodeAddr - (dwReadAddr+4);
      //写入写call地址
      
      MEMORY_BASIC_INFORMATION  stMemBasicInfor = {0};
      VirtualQueryEx( lpProcessInformation->hProcess, (PVOID)dwReadAddr, &stMemBasicInfor, sizeof(MEMORY_BASIC_INFORMATION) );
      DWORD dwOldProtect = 0;
      VirtualProtectEx( lpProcessInformation->hProcess, stMemBasicInfor.BaseAddress, stMemBasicInfor.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect );
      bOk = WriteProcessMemory( lpProcessInformation->hProcess, (PVOID)dwReadAddr, &dwNewCallAddr, 4, NULL );
      dwErr = GetLastError();
      VirtualProtectEx( lpProcessInformation->hProcess, stMemBasicInfor.BaseAddress, stMemBasicInfor.RegionSize, dwOldProtect, NULL );
      if( !bOk )
      {
        OutLog( "写目标进程第一个call地址失败" );
        memset( lpErr, 0, 10 );
        sprintf( lpErr, "ErrorCode:%d", dwErr );
        OutLog( lpErr );
        if( bRes )
          ResumeThread( lpProcessInformation->hThread );
        VirtualFreeEx( lpProcessInformation->hProcess, lpDllPathAddr, strlen(lpCurrentDirectory)+1, MEM_DECOMMIT );
        VirtualFreeEx( lpProcessInformation->hProcess, lpShellCodeAddr, strlen(lpShellCode)+1, MEM_DECOMMIT );
        return bRes;
      }
      
      //至此目标进程的入口已经改造完毕,
      ResumeThread( lpProcessInformation->hThread );
      OutLog( "成功" );

      
      return bRes;
    }
    else
    {
      return ((LPCREATEPROCESS)dwOrigCreateProcessAddr)( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes,
    bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation );
    }

  }
  else
  {
  return ((LPCREATEPROCESS)dwOrigCreateProcessAddr)( lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes,
    bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation );
  }
}

void OutLog( char *pStr )
{  
  //判断日志文件是否存在
  bool bFileExist = false;
  FILE *fp = NULL;
    fp = fopen( "log.txt", "r" );
  if( fp != NULL )
  {
    bFileExist = true;
    fclose(fp);
    fp = NULL;
  }

  //文件存在,写日志
  if( bFileExist )
  {
    ofstream out("log.txt", ios::app );
    if( out != NULL )
    {
      out<<pStr<<endl;
    }
    out.close();
  }
}
运行前后记事本的入口代码对比:


EPO记事本后的入口代码:


1。挂钩前,记事本的入口代码:

上面代码已经测试并能实现注入.这个框架能干什么?就各显神通了.希望多认识些有共同爱好的朋友.
上传的附件 注入.rar