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编译并并链接为可执行文件后执行,可以看到如下结果

img

得到

  • Kernel32.dll地址为0x75f00000
  • Loadlibrary地址为0x75f311d0
  • Getproaddress地址为0x75f18250

现在我们想要在

GAS
 invoke MessageBoxA, 0, addr temp2, addr szTitle, 0
 ; 添加代码
 invoke ExitProcess, 0

这两行代码之间添加新的汇编代码,使其能够打印我们指定的字符串,步骤如下:

  • 先通过调用代码中得到的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