9X 环境中Windows提供了想光的API函数用于隐藏系统进程。但是到了 2000 以上系统,已经无法真正的做到对于进程的隐藏,除非编写底层驱动。但是我们可以通过一些变通的办法来达到隐藏进程的目的,其中一个就是远程注入。简单的说就是先编写一个 API的DLL,然后将这个DLL库注入到一个系统进程中,作为它的一个线程去执行。
要实现DLL注入,首先需要打开目标进程。
hRemoteProcess 
OpenProcess ( PROCESS_CREATE_THREAD  //允许远程创建线程
    PROCESS_VM_OPERATION  //允许远程VM操作
    PROCESS_VM_WRITE ,   //允许远程VM写
    FALSE dwRemoteProcessId  )
由于我们后面需要写入远程进程的内存地址空间并建立远程线程,所以需要申请足够的权限(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。
如果进程打不开,以后的操作就别想了。进程打开后,就可以建立远线程了,不过别急,先想想这个远线程的线程函数是什么?我们的目的是注入一个DLL。而且我们知道用LoadLibrary可以加载一个DLL到本进程的地址空间。于是,自然会想到如果可以在目标进程中调用LoadLibrary,不就可以把 DLL加载到目标进程的地址空间了吗?对!就是这样。远线程就在这儿用了一次,建立的远线程的线程函数就是LoadLibrary,而参数就是要注入的 DLL的文件名。
( 这里需要自己想一想,注意到了吗,线程函数ThreadProc和LoadLibrary函数非常相似,返回值,参数个数都一样 还有一个问题,LoadLibrary这个函数的地址在哪儿?也许你会说,这个简单,GetProcAddress就可以得出。于是代码就出来了。
char  * pszLibFileRemote = "my.dll" ;
PTHREAD_START_ROUTINE pfnStartAddr  = ( PTHREAD_START_ROUTINE ) GetProcAddress ( GetModuleHandle ( "Kernel32" ),  "LoadLibraryA" );
CreateRemoteThread hRemoteProcess NULL 0 pfnStartAddr pszLibFileRemote 0 NULL );
    
但是不对!不要忘了,这是远线程,不是在你的进程里,而pszLibFileRemote指向的是你的进程里的数据,到了目标进程,这个指针都不知道指向哪儿去了,同样pfnStartAddr这个地址上的代码到了目标进程里也不知道是什么了,不知道是不是你想要的LoadLibraryA了。但是,问题总是可以解决的,Windows有些很强大的API函数,他们可以在目标进程里分配内存,可以将你的进程中的数据拷贝到目标进程中。因此 pszLibFileRemote的问题可以解决了。
char  * pszLibFileName = "my.dll" ; //注意,这个一定要是全路径文件名,除非它在系统目录里;原因大家自己想想。
//计算DLL路径名需要的内存空间
int  cb  = ( lstrlenA ( pszLibFileName )) *  sizeof ( char );
//使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名缓冲区
pszLibFileRemote  = ( char  *)  VirtualAllocEx hRemoteProcess NULL cb MEM_COMMIT PAGE_READWRITE );
//使用WriteProcessMemory函数将DLL的路径名复制到远程进程的内存空间
iReturnCode  WriteProcessMemory ( hRemoteProcess pszLibFileRemote , ( PVOID pszLibFileName cb NULL );
    
OK,现在目标进程也认识pszLibFileRemote了,但是pfnStartAddr好像不好办,我怎么可能知道LoadLibraryA在目标进程中的地址呢?其实Windows为我们解决了这个问题,LoadLibraryA这个函数是在Kernel32 . dll这个核心DLL里的,而这个 DLL很特殊,不管对于哪个进程,Windows总是把它加载到相同的地址上去。因此你的进程中LoadLibraryA的地址和目标进程中 LoadLibraryA的地址是相同的 ( 其实,这个DLL里的所有函数都是如此 ) 。至此,DLL注入结束了。
 
但是目前还有一个问题,上面的方法是无法将DLL注入到系统进程中去的,原因是进程级别不够。那么我们就要提升注入程序的进程级别。使用下面的函数:
void  EnableDebugPriv void  )
{
 
HANDLE hToken ;
 
LUID sedebugnameValue ;
 
TOKEN_PRIVILEGES tkp ;
 
if  ( !  OpenProcessToken GetCurrentProcess (),
  
TOKEN_ADJUST_PRIVILEGES  TOKEN_QUERY , & hToken  ) )
  
return ;
 
if  ( !  LookupPrivilegeValue NULL SE_DEBUG_NAME , & sedebugnameValue  ) ){
  
CloseHandle hToken  );
  
return ;
 }
 
tkp . PrivilegeCount  1 ;
 
tkp . Privileges [ 0 ]. Luid  sedebugnameValue ;
 
tkp . Privileges [ 0 ]. Attributes  SE_PRIVILEGE_ENABLED ;
 
if  ( !  AdjustTokenPrivileges hToken FALSE , & tkp sizeof  tkp NULL NULL  ) )
  
CloseHandle hToken  );
}
 
 
 
最后我们来做一个简单的例子:
首先编写注入程序的代码
// DLLAdd.cpp : Defines the entry point for the application.
//
#include  "stdafx.h"
#include  "winnt.h"
void  EnableDebugPriv ();
int  APIENTRY WinMain ( HINSTANCE hInstance ,
                     
HINSTANCE hPrevInstance ,
                     
LPSTR     lpCmdLine ,
                     
int        nCmdShow )
{
 
EnableDebugPriv ();
  
// TODO: Place code here.
 
HANDLE hRemoteProcess ;
 
HANDLE hRemoteThread ;
 
//PWSTR pszLibFileRemote;
 //LPCWSTR pszLibFileName;
 
BOOL iReturnCode ;
 
char  * pszLibFileRemote = "RemoteDLL.dll" ;
 
char  * pszLibFileName = "C:\\RemoteDLL.dll" ; //注意,这个一定要是全路径文件名,除非它在系统目录里
 
hRemoteProcess  OpenProcess ( PROCESS_CREATE_THREAD
        
| PROCESS_VM_OPERATION
        
| PROCESS_VM_WRITE ,
        
FALSE , 0x3E0 ); //0x3E0是进程的id,测试时是explorer的进程id,可以用spy++去查找。
 //计算DLL路径名需要的内存空间
 
int  cb  = ( lstrlenA ( pszLibFileName )) *  sizeof ( char );
 
//使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名缓冲区
 
pszLibFileRemote  = ( char  *)  VirtualAllocEx ( hRemoteProcess NULL cb ,
            
MEM_COMMIT PAGE_READWRITE );
 
//使用WriteProcessMemory函数将DLL的路径名复制到远程进程的内存空间
 
iReturnCode  WriteProcessMemory ( hRemoteProcess ,
    
pszLibFileRemote , ( PVOID pszLibFileName cb NULL );
 
//计算LoadLibraryW的入口地址
 
PTHREAD_START_ROUTINE pfnStartAddr  = ( PTHREAD_START_ROUTINE )
          
GetProcAddress ( GetModuleHandle ( TEXT ( "Kernel32" )),
          
"LoadLibraryA" );
 
//启动远程线程LoadLibraryW,通过远程线程调用用户的DLL文件
 
hRemoteThread  CreateRemoteThread hRemoteProcess NULL 0 ,
          
pfnStartAddr pszLibFileRemote 0 NULL );

 
return  0 ;
}
 
//提升权限
void  EnableDebugPriv void  )
{
 
HANDLE hToken ;
 
LUID sedebugnameValue ;
 
TOKEN_PRIVILEGES tkp ;
 
if  ( !  OpenProcessToken GetCurrentProcess (),
  
TOKEN_ADJUST_PRIVILEGES  TOKEN_QUERY , & hToken  ) )
  
return ;
 
if  ( !  LookupPrivilegeValue NULL SE_DEBUG_NAME , & sedebugnameValue  ) ){
  
CloseHandle hToken  );
  
return ;
 }
 
tkp . PrivilegeCount  1 ;
 
tkp . Privileges [ 0 ]. Luid  sedebugnameValue ;
 
tkp . Privileges [ 0 ]. Attributes  SE_PRIVILEGE_ENABLED ;
 
if  ( !  AdjustTokenPrivileges hToken FALSE , & tkp sizeof  tkp NULL NULL  ) )
  
CloseHandle hToken  );
}
 
 

然后编写需要注入的DLL的代码

#include  "stdafx.h"
#include  "winnt.h"
#include  < stdlib . h >
BOOL APIENTRY DllMain HANDLE hModule ,
                       
DWORD  ul_reason_for_call ,
                       
LPVOID lpReserved
      
)
{
 
char  szProcessId [ 64 ];
 
int  i = 1 ;
 
switch ( ul_reason_for_call )
 {
 
case  DLL_PROCESS_ATTACH :
  {
   
_itoa ( GetCurrentProcessId (), szProcessId , 10 );
   
MessageBox ( NULL , szProcessId , "RemoteDLL" , MB_OK );
  }
 
default :
  
return  TRUE ;
 }
}
 
将编译好的dll放到C盘根目录下面运行注入程序。我们可以发现弹出了一个标示了被注入进程id的对话框。
如上,只要我们再dll中编写我们需要的代码,就可以隐秘的在电脑里执行我们需要的事情。