做金融培训的网站,电脑做系统教学网站,linux网站建设技术指南,网站未及时续费这次#xff0c;作者将和大家一起讨论病毒的感染技术。另外#xff0c;从本文开始#xff0c;我们将陆续接触到一些病毒的高级编码技术。例如#xff0c;内存驻留、EPO#xff08;入口点模糊#xff09;技术、加密技术、多态和变形等。通过这些高级技巧#xff0c;你将进…这次作者将和大家一起讨论病毒的感染技术。另外从本文开始我们将陆续接触到一些病毒的高级编码技术。例如内存驻留、EPO入口点模糊技术、加密技术、多态和变形等。通过这些高级技巧你将进一步感受到病毒技术的精华从而更好的享受其中精妙的思想与编程技艺。 在解决了起始目录的问题之后就可以从这些起始目录开始使用FindFirstFile和FindNextFile开始遍历其下以及其子目录下的所有文件和目录了遍历方法可采用深度优先或广度优先搜索算法较常用的还是深度优先算法。具体实现方式可采用递归搜索或非递归搜索两种实现方式。递归搜索需要占用栈空间有可能造成栈空间耗竭而产生异常不过在现实应用中这种情况很少出现而非递归搜索则不存在此问题但代码实现略复杂。在现实应用中应用最多的还是递归遍历搜索。搜索时可指定FindFirstFile的第一形参为*.*以搜索所有文件根据搜索结果WIN32_FIND_DATA结构的dwFileAttributes成员判断是否为目录若为目录则需要继续遍历该子目录根据WIN32_FIND_DATA的cFileName中的文件名成员判断是否具有要感染的文件后缀以采取修改感染动作以下代码实现了递归搜索某个目录及其下所有子目录的功能
void enum_path(char *cpath){ WIN32_FIND_DATA wfd; HANDLE hfd; char cdir[MAX_PATH]; char subdir[MAX_PATH]; int r; GetCurrentDirectory(MAX_PATH,cdir); SetCurrentDirectory(cpath); hfd FindFirstFile(*.*,wfd); if(hfd!INVALID_HANDLE_VALUE) { do{ if(wfd.dwFileAttributes FILE_ATTRIBUTE_DIRECTORY) { if(wfd.cFileName[0] ! .) { // 合成完整路径名 sprintf(subdir,%s\\%s,cpath,wfd.cFileName); // 递归枚举子目录 enum_path(subdir); } }else{ printf(%s\\%s\n,cpath,wfd.cFileName); // 病毒可根据后缀名判断是 // 否要感染相应的文件 } }while(rFindNextFile(hfd,wfd),r!0); } SetCurrentDirectory(cdir); } 短短20 多行C 代码就实现了文件遍历的功能Win32 API的强大功能不仅为开发者提供了便利同时也为病毒敞开了方便之门。用汇编实现则稍微复杂一些感兴趣的读者可参阅Elkern 中的enum_path部分原理是一样的限于篇幅这里不再给出相应的汇编代码。 非递归搜索不使用堆栈存储相关的信息而使用显式分配的链表或栈等结构存储相关的信息应用一个迭代循环完成递归遍历同样的功能下面是使用链表以栈方式处理子目录列表的一个简单实现
void nr_enum_path(char *cpath){ liststring dir_list; string cdir,subdir; WIN32_FIND_DATA wfd; HANDLE hfd; int r; dir_list.push_back(string(cpath)); while(dir_list.size()) { cdir dir_list.back(); dir_list.pop_back(); SetCurrentDirectory(cdir.c_str()); hfd FindFirstFile(*.*,wfd); if(hfd!INVALID_HANDLE_VALUE) { do{ if(wfd.dwFileAttributes FILE_ATTRIBUTE_DIRECTORY) { if(wfd.cFileName[0] ! .) { // 合成完整路径名 subdircdir\\wfd.cFileName; coutpush subdir: subdirendl; // 递归枚举子目录 dir_list.push_back(string(subdir)); } }else{ printf(%s\\%s\n,cpath,wfd.cFileName); // 病毒可根据后缀名判断 // 是否要感染相应的文件 } }while(rFindNextFile(hfd,wfd),r!0); } }//end while } 在以汇编语言实现时需要自己管理链表以及分配和释放相应的结构因此较为烦琐代码量也稍大因此病毒多采用递归的方式进行搜索。值得注意的是搜索深层次的目录是很费时的因此大部分病毒为避免CPU占用率过高搜索一定数量的文件之后都会调用Sleep 休眠一会以避免被敏感的用户发觉。文件搜索和感染模块通常是以单独的线程运行的在病毒获得控制权后创建相应的搜索和感染线程而将主现成的控制权交给原程序。 PE 文件的修改和感染策略 既然已经能够搜索磁盘及网络共享文件中的所有文件要实现寄生那么自然下一步就是对搜索到的PE文件进行感染了。感染PE的很重要的一个考虑就是将病毒代码写入到PE 文件的哪个位置。读写文件一般利用Win32 API CreateFile、CreateFileMapping、MapViewOfFile等API以内存映射文件的方式进行这样可以避免自己管理缓冲的麻烦因而为较多病毒所采用。为了能够读写具有只读属性的文 件病毒在操作前首先利用GetFileAttributes 获取其属性并保存然后用SetFileAttributes将文件的属性修改为可写在 感染完毕后再恢复其属性值。 一般说来有如下几种感染PE文件的方案供选择 a)添加一个新的节。将病毒代码写入到新的节中相应修改节表文件头中文件大小等属性值。由于在PE尾部增加了一个节因此较容易被用户察觉。在某些情况下由于原PE头部没有足够的空间存放新增节的节表信息因此还要对其它数据进行搬移等操作。鉴于上述问 题PE 病毒使用该方法的并不多。 b)附加在最后一个节上。修改最后一个节节表的大小和属性以及文件头中文件大小等属性值。由于越来越多的杀毒软件采用了一种尾部扫描的方式因此很多病毒还要在病毒代码之后附加随机数据以逃避该种扫描。现代PE 病毒大量使用该种方式。 c)写入到PE文件头部未用空间各个节所保留的空隙之中。PE 头部大小一般为1024 字节有5-6 个节的普通PE文件实际被占用部分一般仅为600 字节左右尚有400 多个字节的剩余空间可以利用。PE文件各个节之间一般都是按照512 字节对齐的但节中的实际数据常常未完全使用全部的512字节PE文件的对齐设计本来是出于效率的考虑但其留下的空隙却给病毒留下了栖身之地。这种感染方式感染后原PE 文的总长度可能并不会增加因此自CIH 病毒首次使用该技术以来备受病毒作者的青睐。 d)覆盖某些非常用数据。如一般exe文件的重定位表由于exe一般不需要重定位因此可以覆盖重定位数据而不会造成问题为保险起见可将文件头中指示重定位项的DataDirectory 数组中的相应项清空这种方式一般也不会造成被感染文件长度的增加。因此很多病毒也广泛使用该种方法。 e)压缩某些数据或代码以节约出存放病毒代码的空间然后将病毒代码写入这些空间在程序代码运行前病毒首先解压缩相应的数据或代码然后再将控制权交给原程序。该种方式一般不会增加被感染文件的大小但需考虑的因素较多实现起来难度也比较大。用的还不多。 不论何种方式都涉及到对PE头部相关信息以及节表的相关操作我们首先研究一下PE的修改即如何在添加了病毒代码后使得PE文件仍然是合法的PE文件仍然能够被系统加载器加载执行。PE文件的每个节的属性都是由节表中的一个表项描述的节表紧跟在IMAGE_NT_HEADERS后面因此从文件偏移0x3C 处的双字找到IMAGE_NT_HEADERS 的起始偏移再加上IMAGE_NT_HEADERS的大小248字节就定位了节表的起始位置每个表项是一个IMAGE_SECTION_HEADER结构 typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节的名字 union { DWORD PhysicalAddress; DWORD VirtualSize; // 字节计算的实际大小 } Misc; DWORD VirtualAddress; // 节的起始虚拟地址 DWORD SizeOfRawData; // 按照文件头FileAlignment // 对齐后的大小 DWORD PointerToRawData; // 文件中指向该节起始的偏移 DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; // 节的属性 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; 节表项的数目由IMAGE_NT_HEADERS的NumberOfSections成员确定。由节表中的起始虚拟地址以及该节在文件中的位置就可以换算加载后内存虚拟地址和文件中地址之间的映射关系。添加一个节则需要修改该节表数组 在其中增加一个表项 然后相应修改 NumberOfSections 的数目。值得注意的是某些PE文件现存节表后面可能紧跟着其它数据如bound import 数据这时就不能简单地增加一个节表项需要先移动这些数据并修改相应的结构后才能增加节否则PE文件将不能正常执行。由于很多病毒是自我修改的因此节属性通常设置为E000XXXX表示该节可读写执行否则就需要在病毒的开始处调用VirtualProtect之类的API动态修改内存页的属性了。 由上述节表的定义还可以看到每个节的实际数据都是按照文件头中FileAlignment 对齐的这个大小一般是512因此每个节可能有不超过512字节的未用空间SizeOfRawData-VirtualSize这恰好给病毒以可乘之机著名的CIH病毒首先采用了这种技术不过问题是每个节的空隙大小是不定的因此就需要将病毒代码分成若干部分存放运行时再通过一段代码组合起来优点是如果病毒代码较小则无需增加PE的大小隐蔽性较强。如果所有节的未用空间仍不足以容纳病毒代码则可新增节或附加到最后一个节上。 附加到最后一个节上是比较简单的只要修改节表中最后一个节的VirtualSize 以及按FileAlignment 对齐后的SizeOfRawData成员即可。当然在上述所有修改节的情况中如果改变了文件的大小都要修正文件头中SizeOfImage这个值的大小该值是所有节和头按照SectionAlignment 对齐后的大小。 这里有两个问题值得注意第一问题就是对WFP(Windows File Protection)文件的处理WFP机制是从Windows 2000 开始新增的保护系统文件的机制若系统发现重要的系统文件被改变则弹出一个对话框警告用户该文件已被替换。当然有多种方法绕过WFP 保护但对病毒而言更简单的方法就是不感染在WFP 列表中的系统文件。 可使用sfc.dll的导出函数SfcIsFileProtected判断一个文件是否在该列表中该API 的第一个问 匦胛?第二个参数是要判断的文件名若在列表中返回非0值否则返回0。另外一个问题就是关于PE文件的校验。大部分PE文件都不使用文件头中的CheckSum域的校验和值不过有些PE文件如关键的系统服务程序文件以及驱动程序文件则该值必须正确否则系统加载器将拒绝加载。PE 头部的CheckSum 可以使用Imagehlp.dll的导出函数 CheckSumMappedFile计算也可以在将该域清0后按照如下简单的等价算法计算 如果PE文件大小是奇数字节则以0补足使之按偶数字节。将PE文件头的CheckSum 域清0然后以两个字节为单位进行adc运算最后和将该累加和同文件实际大小进行adc运算即得到校验和的值。下面的cal_checksum过程假设esi 已经指向PE文件头文件头部CheckSum域已经被清0CF 标志位已经被复位 ;调用示例 ;clc ;push pe_fileseize ;call cal_checksum cal_checksum: adc bp,word [esi] ;初始esi指向文件头ebx 中保 存的是文件大小 inc esi inc esi loop cal_checksum mov ebx,[esp4] adc ebp,ebx ;ebp 中存放的就是PE 的校验和 ret 4 除了PE头部的校验和之外很多程序自身也有校验模块如Winzip 和Winrar 的自解压文件如果被感染将造成无法正常解压缩。因此对于类似的PE文件病毒应尽量不予感染。 Elkern 中感染文件修改文件相关的代码在infect.asm中该病毒首先尽可能地利用PE 的头部和节的间隙存储自身代码若所有间隙仍不足以存放病毒代码则附加到最后一个节上限于篇幅相关代码从略感兴趣的读者请自行参阅。 事实上除了在上边提到的病毒重定位、API地址的获取、文件搜索、修改感染PE等基本技术之外关于病毒技术还有很重要的几个方面没有提及病毒的内存驻留感染技术、内核模式病毒技术、抗分析以及隐藏技术EPO、多态和变形技术等。 内存驻留感染是前述主动全盘搜索技术的变形病毒代码驻留内存被动地等待用户事件或等待程序代码执行到指定的路径被唤醒以执行感染操作。内核态病毒也称ring0病毒是指那些运行在ring0特权级内核模式下的病毒这类病毒相当特殊需要调用内核驱动接口实现感染和传播等操作由于NT内核的复杂性这类病毒非常难于编写另外由于不同版本的NT系统之间内核的差异欲令病毒稳定运行编写者需要付出额外的努力最后的结果可能还是会由于测试的不充分而很快因蓝屏事故而被发现这将严重影响病毒的传播速度和传播范围。ring0 病毒比较少其中最著名的ring0 病毒当非CIH莫属了但由于其巨大的破坏性格外引人注目。由于ring0病毒数量不多而且非常复 杂本文篇幅所限不做深入介绍。对抗杀毒软件、抗分析以及病毒自身的隐藏技术可以说是病毒技术近年来除利用社会工程学借助网络快速传播之外的又一个重要发展方向其目的在于对抗或逃避杀毒软件的扫描最大限度地延长其生存期主要包括EPO入口点模糊技术、加密技术、多态和变形技术等。作者将在后续的文章中陆续向读者进行介绍。 内存驻留感染技术 如果读者曾经使用过MS-DOS的话对驻留内存、截获中断以执行特定操作的程序TSR一定不会陌生。在MS-DOS时代不仅正常的应用程序大量使用TSR技术病毒同样也利用TSR 技术驻留内存监视文件读写操作并伺机进行感染。在Windows NT下各个进程的地址空间被隔离了不同进程之间不能自由地相互访问内存而且对于用户态代码有了访问限制ring3程序代码只能读写其进程空间中应用专属的部分在进程空间为4GB 的情况下通常是低2GB对系统内核部分占用的空间是没有读写权限的。这使得内存驻留感染变得困难不过类似的想法和技术仍然是可能实现的需要做的不过是一点变通既然每个进程有其专属的进程空间尽管不能做到永久驻留但病毒代码至少在进程的生命期内仍然是可以驻留的既然Windows下仍然有作用和DOS下中断相同的API那么病毒自然可以截获API从而监视文件读写伺机进行感染。 截获API 的技术通常被称为Hook 技术实现起来也比较简单修改API 的入口点代码将其修改为指向病毒代码的跳转指令在病毒代码开始处保存传递给API的参数待病毒代码执行完毕后再恢复API 的入口代码和保存的参数重新跳转到API 的入口使得程序继续执行。HOOK API还有其它的几种变形比如修改Import 表的API地址指针修改调用API点的CALL指令或者是为了防止API Hook检查修改API 函数的尾部或中间部分的某条指令而获取控制权思路都是类似的。 既然前面介绍了全盘搜索感染技术那么为什么病毒还大量使用内存驻留感染技术呢 让我们回头思考一下随着存储媒质价格的降低用户配备的可存储媒质的容量也越来越大但是磁盘读写速度并没有大幅提高普通的全磁盘搜索是很耗时的。试想一下用户在双击了某个程序后10分钟之后才出现界面的情况即使是初次使用计算机的用户也会暗生疑窦这对于病毒的隐藏和传播是极端不利的。因此那些流行的病毒并不在获得控制权后直接进行全盘搜索感染而是采用了如下的变通 A) 仅搜索当前目录并对其中的PE 文件进行感染然后迅速将控制权交给宿主程序。 B) 创建单独执行的线程后马上把控制权交给宿主程序。在用户进行操作的同时进行当前目录或全盘的搜索感染。 C) 采用Hook 技术监视文件或目录相关的API操作对用户操作的文件或其所在的目录下的可执行文件进行感染。 搜索当前目录的技术已经在前面进行了讨论。下面主要讨论B和C中涉及到线程技术和内存驻留Hook感染技术。 1.创建独立的线程 如果读者熟悉Win32 API 程序设计对CreateThread这个API一定不会陌生。每个线程都是一个独立的执行单元也是Windows内核进行调度以及时间片分配的最基本的单位同一进程内所有线程共享同一地址空间内的资源。病毒可将真正的功能部分放到该线程部分去执行。病毒代码可以在宿主程序运行的同时得以执行普通用户通常难以察觉。 2.内存驻留Hook感染技术 上面提到的线程模型还是有缺陷的试想一下如果宿主程序一执行马上就退出了怎么办呢病毒的感染线程可能还没有开始运行呢。值得注意的是Win32线程模型中如果一个线程执行了ExitProcess调用其它正在运行的线程不会自动得到通知。病毒代码可能还尚未执行完成就退出了试想一下感染线程在感染某个PE文件时写入自身代码时只写入了一半的情况。尽管类似的情况在现实中极少发生但要想提高病毒的传染能力和隐蔽性这却又是必须要考虑的问题。此外用单独线程进行全盘搜索和感染仍然会造成计算机CPU 资源被大量占用用户可能会注意到程序运行缓慢、硬盘指示灯狂闪等现象。当然可以采用感染少量文件后休眠一会继续感染或者仅在计算机空闲时进行感染来解决。 让我们换个角度重新思考一下真的有必要搜索全盘然后感染所有的PE 文件吗或者全盘搜索感染模型效率高吗事实上用户计算机硬盘上的PE 文件有很大一部分是很少有机会执行的除系统程序外经常运行的程序只有很少的几个可能是几个游戏程序也可能是用户的业务程序。如果占用资源去感染那些很少有机会被执行的PE 文件除演示病毒的感染性的概念之外意义是不大的。相反如果优先感染那些用户经常执行的程序即使每次只感染少量的文件病毒的传播速度也会得到大幅提升。鉴于此现代病毒作者经常使用一条简单的启发式规则优先感染那些经常被用户或用户程序访问的PE 文件或经常访问的文件夹下的PE 文件。这样病毒就从被感染文件开始逐渐扩散感染与该程序相关或在逻辑位置目录层次比较接近的PE文件最后直至整个磁盘上的文件都被感染。 无论是想要在进程结束时得到通知还是实现上述的文件感染模型都可借助于Hook 技术来解决。通过Hook 文件或目录操作相关的API病毒就能获取正在操作的PE文件路径或目录从而对正在操作的PE文件或正在操作的目录下的PE文件进行感染。有关文件或目录操作的相关API包括
CopyFile CopyFileEx CreateFile FindFirstFile FindFirstFileEx FindNextFile GetCurrentDirectory SetCurrentDirectory GetFileAttributes SetFileAttributes GetFileSize GetFileType GetFullPathName LockFile LockFileEx MoveFile MoveFileEx SearchPath UnlockFile UnlockFileEx 如果想在进程或线程结束时获得通知或进行感染操作可Hook 如下API
ExitProcess TerminateProcess ExitThread TerminateThread 如果不考虑9X 系统Hook API 还可以考虑Hook Native API。Windows系统内核代码运行在CPU 保护模式下的ring0特权级普通应用程序运行在ring3特权级普通应用程序执行IO 操作、访问内存资源等都受到严格的限制加上Windows NT系统严格的用户权限审查机制不同用户对资源的访问权限不同使得病毒在运行时要考虑的因素越来越多。但反观ring0 特权级的程序执行时没有任何限制因此ring0 病毒对病毒编写者有着独特的吸引力。Windows 9X 下普通的Ring3应用程序切入ring0 模式非常容易但在Windows NT系统下则困难得多但也不是不可能的最通用的方法就是采用内核模式驱动的加载机制将病毒自身代码写入驱动文件然后加载另外的一些技巧还包括感染 Windows 内核驱动文件或修改NTLDR 的IDT、GDT表项的访问限制位使得在ring3 可以访问从而使得病毒代码切入ring0除此之外还可以利用\Device\PhysicalMemory对象的漏洞切入ring0。 最成功的ring0 病毒就是大名鼎鼎的CIH不过该病毒只能运行在Win9X 系统下。但在Windows NT系统下尽管仍然可能切入ring0但很少有大规模流行的ring0 病毒这是由于NT系统内核处理流程非常复杂编写一个在各个版本的NT系统上都能稳定运行的ring0病毒难度不小需要更加高超的编程技巧并经过大量的测试。因此ring0病毒数量较少鉴于此本文将不再作深入介绍。 病毒抗分析技术 当今反病毒已经成为一个产业病毒在被发现之后为数众多的杀毒厂商会迅速分析该病毒并升级其病毒库或推出专杀工具颇有“老鼠过街人人喊打”之势。但这一切都是建立在对病毒代码和病毒行为分析的基础上的。 从病毒的角度来讲对抗病毒分析和动态静态查杀还是有意义的尽管再狡猾的狐狸也难逃有经验猎手的追捕但抗分析和查杀技术至少可以在一定程度上延缓反病毒厂商推出杀毒方案的时间从而延长病毒的生命周期、扩大传播范围。