电子科技大学计算机学院《计算机病毒原理与防范》复习,内容主要来自于ppt。
第一章 开篇
简述计算机病毒的定义和特征
答:计算机病毒,是一种人为制造的、能够进行自我复制的、具有对计算机资源进行破坏作用的一组程序或指令集合。
特征:可执行性、传染性( 首要条件)、非授权性、隐蔽性、潜伏性、可触发性、破环性。。。
本质:都是人为制造的程序;本质特点是程序的无限重复执行或复制。
计算机病毒有哪些分类方法
答:按照破坏能力、破坏情况分类;按照攻击的操作系统分类;按照攻击的机型分类;按照特有的算法分类(伴随型、蠕虫型、寄生型);按照链接方式分类(源码型病毒、嵌入型病毒、外壳型病毒);按照传播媒介分类;按照寄生对象和驻留方式分类
为什么同一个病毒会有多个不同的名称?如何通过病毒的名称识别病毒的类型?
答:因为有许多的命名规则,比如按发作时间分类、按字节长度分类。
国际上对病毒命名的惯例:前缀+病毒名+后缀
- 前缀表示该病毒发作的操作平台或者病毒的类型,而DOS下的病毒一般是没有前缀
- 病毒名为该病毒或其家族的名称
- 后缀用于区别在该病毒在其家族中的不同
病毒名中若有PSW或者PWD之类的,一般都表示该病毒有盗取口令的功能
计算机病毒有哪些传播途径?
答:通过不可移动的计算机硬件设备;通过移动存储设备;通过有线网络系统;通过无线通讯系统
第二章 内存中的病毒
jmp指令:E9 xx xx xx xx,偏移量=目的地址-jmp下一条指令地址
问题:为什么不选择411440之前的指令,比如virusfunc的入口处作为转跳的点:
答:不执行不必要的指令,函数开始处大多是压栈操作,不执行就不用后续出栈,保护堆栈平衡。
第三章 文件系统
引导扇区最后两个字节为55 AA,0xAA55
盘的第一个扇区在启动是加载到7c00h处执行
文件分配的最小单位为簇
Fat表以3个半字节( 12bits)为元素组成一个整数数组。三个半字节表示的整数即是簇号(从0开始),一个fat元素中的整数表示下一簇的簇号,fffh代表结束。
Fat表开始3个字节没用于用户文件分配,3字节有2组12bits所以,占用了0,1两个簇号(用来代表根目录并且可以作为未分配的标记)。用户的数据从簇2开始分配。
根目录表记录字段:文件名(0,0xB),文件名8字节,扩展名3字节,没有记录'.',文件名结束用空格填充(0x20);属性(0xB,1),卷标项(0x28),文件项(0x20),目录项(0x10);首簇号(0x1A,2)。
目录项第一个字节被修改成e5代表删除;在fat表中获得簇链,将链上的每个fat项复原成00;实际文件扇区的内容不用修改。
创建文件:目录项(第一个字节为0或5e表示可用)->fat表(空簇)->填写目录项各字段
Fat16和fat12非常相似,主要区别在于fat表项不是12bits而是16bits组成,即2字节
FAT32是出于FAT16不支持大分区、单位簇容量大以至于空间急剧浪费等缺点设计的
Fat32的一个重要不同在于取消了根目录区,因为根目录区大小有限,这限制了根目录下的文件数。一般为数据区第一簇,即簇2。
fat32与fat12差异
- fat表项一个长4字节,0fffffff代表结束
- 引导记录中有一个指向根目录的首簇字段。没有专门的根目录区。一般簇为簇2
- 支持长名文件。由扩展的32字节目录项构成。属性:20h表示短文件名,代表文件;0f表示长文件名
- 目录项中的首簇号,由两个字段构成分别代表高低两字节,(0x14,0x1A)
- 引导区有保留区
硬盘数据结构
通过硬盘数据结构(分区表,扩展分区等)将硬盘划分成多个逻辑上的分区(模拟一个硬盘),再将文件系统(比如fat32)整个放入某个分区,这样就可以有多个逻辑盘了。而不同分区还可以放入不同的文件系统
分区划分以柱面为单位,一个柱面不能属于两个分区
主引导扇区:
分区表项:
主分区表只有4条,总共只有4个分区。如果要增加更多的分区。微软采用了虚拟扩展分区(Extended MBR,即EBR)的技术
硬盘的引导过程:
- 开机,cpu跳到ffff:0000处,由该处的一条jmp指令跳到Bios的自检程序,通过后加载引导程序
- 读主引导扇区MBR,将其加载到0000:7c00并跳到该处执行。扫描主分区表找到激活分区。激活分区分区表项第一个字节为0x80
- 如果有多个激活分区或没有,报错结束。否则读取激活分区引导扇区到0000:7c00
- 操作系统引导代码引导系统并读取操作系统初始化文件
因为硬盘数据结构和操作系统无关;操作系统相关的引导代码在其分区的引导扇区。所以,硬盘可以引导多系统。要引导多系统必须用专门的引导程序替换MBR。
硬盘逻辑锁:
查找盘区本身,用的是第1项-->相对扇区偏移字段;查找EBR,是分区表的第2项-->起始磁头号、扇区、柱面号。
第四章 DOS下的病毒
头部寄生
尾部插入寄生
自定位代码
call here
here:
pop ax
sub ax, here
//ax 存储了实际加载和期望加载的偏差,以后所有的地址引用都加上ax即可
在病毒尾部定义了两个变量,一个存储被jmp指令覆盖的3字节;一个存储被感染文件的大小。
逆插入感染
感染后的代码布局
病毒virus.asm的复制感染部分
引导区病毒
感染引导区,替换引导区原始的引导代码,从而获得执行。之后还原被修改的引导区,并将执行权限交给原来的引导代码,从而保持正常的工作
感染需要做的工作是:1.保留原始引导扇区的内容到簇2;2.修改fat1和fat2表将簇2的项改成坏簇,0xff7,从而防止别人使用它。
绝对扇区号和磁头,道,道内扇区号的关系
给定绝对扇区号,除以18(软盘一道有18个扇区),则余数+1是道内扇区号(道内扇区从1开始),商为总共的道数N(我称其为绝对道号)
磁道号:N/2-1
磁头号:N为奇数则为0,为偶数则为1
问题:无法识别盘
原因:因为覆盖了整个扇区,包括引导记录,导致dos无法识别盘。引导记录中有磁盘相关的各种信息。
解决:保留引导记录即可。我们的病毒代码可以只覆盖引导记录后面的部分,为了不重定位,加入了填充字节直到代码处。EB 3C,3C+2=3E
链式病毒
链式病毒只保留一份病毒拷贝,利用文件目录项,将受感染文件的头簇指向病毒。
感染算法:
- 首次感染,将病毒保存在某个空闲扇区
- 将被感染文件首簇存目录项保留段
- 修改首簇号指向病毒的首簇,大小也要修改成病毒的真实大小
启动算法:
- 病毒启动后,加载的是病毒的首簇。并执行。
- 病毒获取当前执行程序的名字,获取对应目录项。从其中保留字段获取原文件首簇号,并遍历fat簇链加载它们。
- 跳到加载的原文件内存中执行。
病毒运行后,先获取执行程序的名字,然后从a盘根目录寻找该程序,找到后从目录项的保留区获取被感染程序的首扇区号,然后读取该扇区,并将其加载到 100h执行之(注意,因为正常代码加载覆盖了100h处的病毒代码,和头感染相似,加载扇区的代码要移动到未覆盖区,因为我们约定了正常程序小于1扇区,所以代码被移动到300h处)。
识别中断向量和中断表
中断向量存放在00:00处,每个中断向量(中断例程入口地址)由4字节组成,高2字节是段地址,低2字节是段内偏移.
中断触发指令是int xxh;4 xx就是中断xx的中断向量存放的偏移量。比如int 10h其中断向量入口地址即使0x10 4 = 0x40.
非驻留式中断向量修改
中断处理程序要为所有程序服务,所以它的特点就是不退出一直驻留在内存中。
驻留式中断向量修改
关键是调用了dos中断Int 27h,请求驻留
驻留代码框架:
第五章 Windows下的病毒
PE格式
RVA:加载到内存后的偏移;文件偏移:文件上离头部的偏移
判断一个文件是否是pe格式,只要先判断文件头2字节是否是’MZ’,然后判断nt头部的signature是否是’PE’.
寄生代码在最后段,文件长度变大:
Size of Raw Data必须是FileAlign(OptionHeader的字段)的整数倍
解决入口点不在代码段的问题:
感染代码段,附着在其尾部,让入口点处于代码段
- 缺点:真正的入口一般都在代码段的前部,而我们感染的是尾部,这样入口点太靠代码段的后部,这也会成为一些检查软件的判断条件
- 不修改入口点,但将入口点所在的指令替换成一条jmp指令,跳往寄生代码
感染最后段,替换入口指令
关键数据结构(共13字节):
- 因为要恢复入口原代码的5字节。所以病毒数据区必须保留原入口绝对地址和入口5字节的原值,共9字节。
- 在数据区保留了按约定地址加载时,病毒数据区的约定首址(这样执行时用自定位代码获取病毒数据区实际首址并和保留的约定首址相减即可获得偏移)
内存不能修改可能原因:
- 内核区地址,用户区搞不定
- 该线性地址其实没有物理内存对应,是无效地址
- 该内存只读不可写
解决:修改段头属性
调用系统API——弹出对话框
谁将MessageBox的入口地址775fea71放到内存0032a2ac中的?
是编译器和操作系统的配合,那个内存0032a2ac有个名字,它属于导入表,叫导入表项。编译时,这个地址和”MessageBox”串关联,形成导入表。加载时,系统解析了导入表,然后找到MessageBox的入口,将其地址填写如导入表项。
一个系统中,进程加载的所有系统dll基址相同
OptionHeader中subsystem字段:2——GUI,3——CUI
导出表---dll对外暴露函数地址的机制
- 当按名字查找时,在函数名表索引1处找到字串func2,然后用这个索引1作为函数地址索引表的索引也找到第2项,其中存储的是2,这个2就是函数地址表的索引,找到第3项,其中存储的就是address2
- 按序号查找时,(1)按以下计算索引,i=函数序号-序号中最小的序号(2)在函数地址表中找到索引i的项即可
pe格式的设计:
Address Table RVA就是指向函数地址表的偏移RVA,而Ordinal Table RVA就是指向函数地址索引表的偏移,这里它换了个名字叫序号表而已(每项2字节)。Ordinal Base就是最小的序号,Number of names就是序号表的条数。但pe格式将函数名表做了些变化,增加了一个函数名指针表(Name Point Table),存放每个函数名首地址的RVA。
MessageBoxA,MessageBoxW等函数。A的参数是ascii码的串,W是unicode串
Patch 指令,模糊入口点
核心:(1)找到一条指令,并知道其起始边界。(2)这条指令必然执行
导入表机制:
函数调用一般采用call 相对偏移e8 xx xx xx xx,Call 相对偏移只能运用到同一个模块中,如果跨越了dll、exe调用dll的函数时,并不知道dll会加载到哪里,不可能事先算出转跳偏移,因此编译器和系统协助,利用导入表机制完成了这个工作
- 加载前后,INT内容不变,就是IAT加载前的内容。但可能INT并不存在,比如borland公司编译器就不生成INT。但如果有地址预绑定,就必须有INT
- IAT是必须的,它就是“邮箱”。在硬盘上,IAT的每项4字节指向和它相关函数名信息首部(头2字节是序号,后为0结尾的函数名串),是RVA。加载后,内容变成了函数的入口地址。
Orignialfirstthunk在预先绑定时必须存在
- 预先绑定时,因IAT中填写的是函数的入口地址!而不是import_by_name项的RVA。这里的理论是,系统dll基址是不变的,可以预先知道。那么,只要基址没变,则加载时速度块。不需要填充IAT项。只需要验证是否dll被加载到希望的地址。是则不需填充IAT项。
- 但如果引用的dll没在期望地址,必须填充IAT项为实际函数的地址。这时需要一个表保存指向import_by_name项的rva,这就该originalfirstthunk登场了。
导入表调用dll函数的指令
- call [xxx],xx就是IAT项的地址——ff 15 xx xx xx xx
- Call 相对偏移——ff 15 xx xx xx xx
...
Jmp [xx];这里xx是IAT项的地址。call到jmp [xx]指令,然后跳到函数入口
Patch api calling算法
- 查找A函数的导入表项的地址xx xx xx xx
- 查找代码段ff 15 xx xx xx xx(call [])和ff 25 xx xx xx xx(jmp [])。修改为e8 yy yy yy yy 90(call 偏移,nop)或e9 yy yy yy yy 90(jmp 偏移)
跳回原函数入口的问题,我们可以将原来的call [xxx] ,jmp[xxx]变成一条jmp 偏移到病毒,病毒的尾部再加一条jmp [xxx]指令。但该方法因为jmp[xxx]包含地址xxx,如果在exe可重定位的情况下,因为没有针对该新造指令的重定位项,所以会出错。
解决:添加对应重定位项或让exe不会重定位
Bss段——非初始化段
特性就是硬盘长度(SizeOfRawData)为0,却有内存长度(VirtualSize)
问题:认为内存长度必为有效长度,所以从硬盘读数据,都以内存长度为依据
解决:我们在从硬盘读取数据时,如内存长度小于硬盘长度则按内存长度读取有效数据;内存长度大于硬盘长度时,应该读取硬盘长度。
DEP数据执行保护
- 让数据段内存变可执行
- 关闭DEP
程序重定位
程序因加载到非编译期约定地址时,需修改指令或其它部分所包含的绝对地址,这称为程序重定位,由加载器(loader)完成。
重定位表
将被重定位的区域以4k划分,偏移是针对某个4k页面,就只需要3个半字节(12位)表示。而这个4k的区域被称为page,其首部地址用4字节记录即可。
另外半个字节为属性,这样4个半字节,2个字节构成了重定位表的项,这些项组成了数组,而数组首部是8字节的头,头4字节为本页首的RVA,后四字节是包括头和表项在内此表的字节数。
disable重定位方法:
- 把OptionHeader中的数据目录项中的重定位项的RVA改成0,而其大小不动
- 在OptionHeader的DLLCharacteristic属性中0040h表示了该重定位含义,将该属性去掉即可,即将对应4的那个bit变成0
删除指向被patch指令的重定位项
- 当patch一条指令时,指令首部偏2字节(因为call[],jmp[]操作码均为2)所指向的位置就是需要重定位的位置,假定其RVA为a,
- 遍历reloc段查找RVA=a的项,找到重定位表基址b,查找表项后三字节=a-b的项删除,填充补齐reloc段,修改小表的大小(后四字节)的值减小2,OptionHeader数据目录的reloc项的size也减小2
入口点模糊之RTL感染
特征代码——crt的初始化
特征码
判断一个数是否为地址
- 数据的地址应该在数据段,指令的地址应该在代码,看一个常数是否在这些段的地址范围内
- 重定位表中是否有指向该地址的表项
文件长度不变的寄生
- 空洞感染
- 多段插入
- 压缩感染
- 重定位段区感染
第六章 病毒检测
防治技术:预防、检测、消除、免疫
主动内核(Active K)技术,是在操作系统和网络的内核中嵌入反病毒功能,使反病毒成为系统本身的底层模块,实现各种反毒模块与操作系统和网络无缝连接,而不是一个系统外部的应用软件
诊断方法
- 比较法:用原始的正常备份与被检测的内容进行比较
校验和法:根据正常文件的信息,计算其校验和,将该校验和写入文件中或写入其他文件中保存
- 在检测病毒工具中纳入校验和法
- 在应用程序中,放入校验和法自我检查功能
- 将校验和检查程序常驻内存
- 扫描法:用每一种病毒体含有的特定病毒码对被检测的对象进行扫描
- 行为监测法:利用病毒的特有行为特性监测病毒的方法
- 感染实验法:利用了病毒的感染特性
- 软件模拟法:是一种软件分析器,用软件方法来模拟和分析程序的运行。用于检测多态型病毒
启发式代码扫描技术
启发式代码扫描技术源于人工智能技术,是基于给定的判断规则和定义的扫描技术,若发现被扫描程序中存在可疑的程序功能指令,则作出存在病毒的预警或判断
虚拟机查毒技术
查毒的虚拟机是一个软件模拟的CPU,它可以像真正CPU一样取指令、译码、执行,可以模拟一段代码在真正CPU上运行得到的结果
基本工作原理和简单流程:给定一组机器码序列,虚拟机会自动从中取出第一条指令操作码部分,判断操作码类型和寻址方式以确定该指令长度,然后在相应的函数中执行该指令,并根据执行后的结果确定下条指令的位置,如此循环反复直到某个特定情况发生以结束工作
目的是为了对抗加密变形病毒
虚拟机的设计方案:自含代码虚拟机、缓冲代码虚拟机、有限代码虚拟机
反虚拟机技术:插入特殊指令(技术)、结构化异常处理、入口点模糊、多线程
Windows 9x下的实时监控,主要采用哪些技术?Windows NT/2000下的实时监控,主要采用哪些技术?为什么会存在这些差别
Windows 9x下病毒实时监控的实现主要依赖于以下三项技术
- 虚拟设备驱动(VxD)编程
- 可安装文件系统钩子(IFSHook)
- VxD与Ring3下客户程序的通信(APC/EVENT)
Windows NT/2000下病毒实时监控的实现主要依赖于以下三项技术
- NT内核模式驱动编程
- 拦截IRP
- 驱动与Ring3下客户程序的通信(命名的事件与信号量对象)
因为Windows NT/2000下不再支持VxD
引导型病毒
硬盘主引导扇区染毒时的修复方法
- 用无毒软盘启动系统
- 寻找一台同类型、硬盘分区相同的无毒计算机,将其硬盘主引导扇区写入一张软盘中
- 将此软盘插入染毒计算机,将其中采集的主引导扇区数据写入染毒硬盘0柱面0磁头1扇区,即可修复
硬盘、软盘BOOT扇区染毒时的修复方法
用与染毒盘相同版本的无毒系统软盘启动计算机,然后执行命令“SYS C:”,用正常的引导程序覆盖硬盘的BOOT扇区