PE文件病毒实现
有一段汇编代码如下(64位机器):
GAS
.586p
.model flat,stdcall
option casemap:none;
include include\windows.inc
include include\kernel32.inc
includelib lib\kernel32.lib
include include\user32.inc
includelib lib\user32.lib
GetApiAddress PROTO:DWORD,:DWORD
.data
Kernel32Addr dd ?
ExportKernel dd ?
GetProcAddr dd ?
LoadLibraryAddr dd ?
aGetProcAddr db "GetProcAddress", 0
GetProcAddLen equ $-aGetProcAddr-1
aLoadLibrary db "LoadLibraryA" , 0
LoadLibraryLen equ $-aLoadLibrary-1
strMessageBoxA db "MessageBoxA",0
strUser32 db "User32.dll",0
strShow db "动态messagebox演示",0
szTitle db "检测结果", 0
temp1 db "Kernel32.dll 基本地址:%8x" , 0dh , 0ah
db "LoadLibrary 地址: %8x" ,0dh, 0ah
db "GetProcAddress 地址: %8x" , 0dh , 0ah , 0
temp2 db 0 dup(100)
.code
main:
Start:
mov esi, [esp] ; 补全:加载地址,动态获取
and esi, 0ffff0000h
LoopFindKernel32:
sub esi, 10000h
cmp word ptr[esi], 5a4dh ; 补全:MZ头标志
jnz short LoopFindKernel32
GetPeHeader:
mov edi, dword ptr[esi+3ch] ; 补全:DOS头偏移
add edi, esi
cmp word ptr[edi], 4550h ; 补全:PE头标志
jnz short LoopFindKernel32
mov Kernel32Addr, esi
invoke GetApiAddress, Kernel32Addr, addr aLoadLibrary
mov LoadLibraryAddr, eax
invoke GetApiAddress, Kernel32Addr, addr aGetProcAddr
mov GetProcAddr, eax
invoke wsprintf, addr temp2, addr temp1, Kernel32Addr, LoadLibraryAddr, GetProcAddr
invoke MessageBoxA, 0, addr temp2, addr szTitle, 0
invoke ExitProcess, 0
GetApiAddress proc uses ecx ebx edx esi edi hModule:DWORD, szApiName:DWORD
LOCAL dwReturn:DWORD
LOCAL dwApiLength:DWORD
mov dwReturn, 0
mov esi, szApiName
mov edx, esi
Continue_Searching_Null:
cmp byte ptr[esi], 0
jz We_Got_The_Length
inc esi
jmp Continue_Searching_Null
We_Got_The_Length:
inc esi
sub esi, edx
mov dwApiLength, esi
mov esi, hModule
add esi, dword ptr[esi+3ch]
assume esi:ptr IMAGE_NT_HEADERS
mov esi, [esi].OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress ; 补全:导出表地址
add esi, hModule
assume esi:ptr IMAGE_EXPORT_DIRECTORY
mov ebx, [esi].AddressOfNames ; 补全:函数名称地址表
add ebx, hModule
xor edx, edx
.repeat
push esi
mov edi, [ebx]
add edi, hModule
mov esi, szApiName
mov ecx, dwApiLength
cld
repz cmpsb
.if ZERO?
pop esi
jmp _Find_Index
.endif
pop esi
add ebx, 4
inc edx
.until edx >= [esi].NumberOfNames ; 补全:函数名称个数
jmp _Exit
_Find_Index:
sub ebx, [esi].AddressOfNames
sub ebx, hModule
shr ebx, 1
add ebx, [esi].AddressOfNameOrdinals ; 补全:序号表
add ebx, hModule
movzx eax, word ptr [ebx]
shl eax, 2
add eax, [esi].AddressOfFunctions ; 补全:函数地址表
add eax, hModule
mov eax, [eax]
add eax, hModule
mov dwReturn, eax
_Exit:
mov eax, dwReturn
ret
GetApiAddress endp
end main
代码的作用为打印出Kernel32.dll、LoadLibrary和GetProcAddress的内存地址
使用masm32编译并并链接为可执行文件后执行,可以看到如下结果

得到
- Kernel32.dll地址为0x75f00000
- Loadlibrary地址为0x75f311d0
- Getproaddress地址为0x75f18250
现在我们想要在
这两行代码之间添加新的汇编代码,使其能够打印我们指定的字符串,步骤如下:
- 先通过调用代码中得到的LoadLibraryA函数地址,获得user32.dll地址
- 然后在通过调用代码中获得的GetProcAddressA的地址,获取MessageBoxA函数地址后
- 动态调用该函数弹出对话框
(CALL调用时,需设置好参数,注意逆序,使用到的指令包括,lea,push,call等,函数调用的返回值送eax寄存器)。
GAS
.586p
.model flat,stdcall
option casemap:none;
include include\windows.inc
include include\kernel32.inc
includelib lib\kernel32.lib
include include\user32.inc
includelib lib\user32.lib
GetApiAddress PROTO:DWORD,:DWORD
.data
Kernel32Addr dd ?
ExportKernel dd ?
GetProcAddr dd ?
LoadLibraryAddr dd ?
aGetProcAddr db "GetProcAddress", 0
GetProcAddLen equ $-aGetProcAddr-1
aLoadLibrary db "LoadLibraryA" , 0
LoadLibraryLen equ $-aLoadLibrary-1
strMessageBoxA db "MessageBoxA",0
strUser32 db "User32.dll",0
strShow db "speacial string",0
szTitle db "检测结果", 0
temp1 db "Kernel32.dll 基本地址:%8x" , 0dh , 0ah
db "LoadLibrary 地址: %8x" ,0dh, 0ah
db "GetProcAddress 地址: %8x" , 0dh , 0ah , 0
temp2 db 0 dup(100)
.code
main:
Start:
mov esi, [esp] ; 加载地址,动态获取
and esi, 0ffff0000h
LoopFindKernel32:
sub esi, 10000h
cmp word ptr[esi], 5a4dh ; MZ头标志
jnz short LoopFindKernel32
GetPeHeader:
mov edi, dword ptr[esi+3ch] ; DOS头偏移
add edi, esi
cmp word ptr[edi], 4550h ; PE头标志
jnz short LoopFindKernel32
mov Kernel32Addr, esi
invoke GetApiAddress, Kernel32Addr, addr aLoadLibrary
mov LoadLibraryAddr, eax
invoke GetApiAddress, Kernel32Addr, addr aGetProcAddr
mov GetProcAddr, eax
invoke wsprintf, addr temp2, addr temp1, Kernel32Addr, LoadLibraryAddr, GetProcAddr
invoke MessageBoxA, 0, addr temp2, addr szTitle, 0
; 使用 LoadLibraryA 动态加载 User32.dll
lea eax, strUser32
push eax
call LoadLibraryAddr ; 调用 LoadLibraryA
mov dword ptr [ExportKernel], eax ; 保存 User32.dll 的地址
; 使用 GetProcAddress 获取 MessageBoxA 函数的地址
lea eax, strMessageBoxA
push eax
push dword ptr [ExportKernel]
call GetProcAddr ; 调用 GetProcAddress
mov dword ptr [GetProcAddr], eax ; 保存 MessageBoxA 函数的地址
; 动态调用 MessageBoxA 显示对话框
lea eax, strShow
push 0 ; uType
push eax ; lpCaption
lea eax, strShow
push eax ; lpText
push 0 ; hWnd
call dword ptr [GetProcAddr] ; 调用 MessageBoxA
invoke ExitProcess, 0
GetApiAddress proc uses ecx ebx edx esi edi hModule:DWORD, szApiName:DWORD
LOCAL dwReturn:DWORD
LOCAL dwApiLength:DWORD
mov dwReturn, 0
mov esi, szApiName
mov edx, esi
Continue_Searching_Null:
cmp byte ptr[esi], 0
jz We_Got_The_Length
inc esi
jmp Continue_Searching_Null
We_Got_The_Length:
inc esi
sub esi, edx
mov dwApiLength, esi
mov esi, hModule
add esi, dword ptr[esi+3ch]
assume esi:ptr IMAGE_NT_HEADERS
mov esi, [esi].OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress ; 导出表地址
add esi, hModule
assume esi:ptr IMAGE_EXPORT_DIRECTORY
mov ebx, [esi].AddressOfNames ; 函数名称地址表
add ebx, hModule
xor edx, edx
.repeat
push esi
mov edi, [ebx]
add edi, hModule
mov esi, szApiName
mov ecx, dwApiLength
cld
repz cmpsb
.if ZERO?
pop esi
jmp _Find_Index
.endif
pop esi
add ebx, 4
inc edx
.until edx >= [esi].NumberOfNames ; 函数名称个数
jmp _Exit
_Find_Index:
sub ebx, [esi].AddressOfNames
sub ebx, hModule
shr ebx, 1
add ebx, [esi].AddressOfNameOrdinals ; 序号表
add ebx, hModule
movzx eax, word ptr [ebx]
shl eax, 2
add eax, [esi].AddressOfFunctions ; 函数地址表
add eax, hModule
mov eax, [eax]
add eax, hModule
mov dwReturn, eax
_Exit:
mov eax, dwReturn
ret
GetApiAddress endp
end main
