The Replicator

The job of the replicator is to spread the virus throughout the system of the clod who has caught the virus. How does it do this without destroying the file it infects? The easiest type of replicator infects COM files. It first saves the first few bytes of the infected file. It then copies a small portion of its code to the beginning of the file, and the rest to the end.

In the diagram, P1 is part 1 of the file, P2 is part 2 of the file, and V1 and V2 are parts 1 and 2 of the virus. Note that the size of P1 should be the same as the size of V1, but the size of P2 doesn't necessarily have to be the same size as V2. The virus first saves P1 and copies it to the
either 1) the end of the file or 2) inside the code of the virus. Let's assume it copies the code to the end of the file. The file now looks like:

Then, the virus copies the first part of itself to the beginning of the file.

Finally, the virus copies the second part of itself to the end of the file. The final, infected file looks like this:

The question is: What the fuck do V1 and V2 do? V1 transfers control of the program to V2. The code to do this is simple.

JMP FAR PTR Duh ; Takes four bytes
Duh DW V2_Start ; Takes two bytes

Duh is a far pointer (Segment:Offset) pointing to the first instruction of V2. Note that the value of Duh must be changed to reflect the length of the file that is infected. For example, if the original size of the program is 79 bytes, Duh must be changed so that the instruction at
CS:[155h] is executed. The value of Duh is obtained by adding the length of V1, the original size of the infected file, and 256 (to account for the PSP). In this case, V1 = 6 and P1 + P2 = 79, so 6 + 79 + 256 = 341 decimal (155 hex).

An alternate, albeit more difficult to understand, method follows:

DB 1101001b ; Code for JMP (2 byte-displacement)
Duh DW V2_Start - OFFSET Duh ; 2 byte displacement

This inserts the jump offset directly into the code following the jump instruction. You could also replace the second line with

DW V2_Start - $

which accomplishes the same task.

V2 contains the rest of the code, i.e. the stuff that does everything else. The last part of V2 copies P1 over V1 (in memory, not on disk) and then transfers control to the beginning of the file (in memory). The original program will then run happily as if nothing happened. The code to do this is also very simple.

MOV SI, V2_START ; V2_START is a LABEL marking where V2 starts
SUB SI, V1_LENGTH ; Go back to where P1 is stored
MOV DI, 0100h ; All COM files are loaded @ CS:[100h] in memory
MOV CX, V1_LENGTH ; Move CX bytes
REP MOVSB ; DS:[SI] -> ES:[DI]

MOV DI, 0100h
JMP DI

This code assumes that P1 is located just before V2, as in:

P1_Stored_Here:
.
.
.
V2_Start:

It also assumes ES equals CS. If these assumptions are false, change the code accordingly. Here is an example:

PUSH CS ; Store CS
POP ES ; and move it to ES
; Note MOV ES, CS is not a valid instruction
MOV SI, P1_START ; Move from whereever P1 is stored
MOV DI, 0100h ; to CS:[100h]
MOV CX, V1_LENGTH
REP MOVSB

MOV DI, 0100h
JMP DI

This code first moves CS into ES and then sets the source pointer of MOVSB to where P1 is located. Remember that this is all taking place in memory, so you need the OFFSET of P1, not just the physical location in the file. The offset of P1 is 100h higher than the physical file location, as COM files are loaded starting from CS:[100h].

So here's a summary of the parts of the virus and location labels:

V1_Start:
JMP FAR PTR Duh
Duh DW V2_Start
V1_End:

P2_Start:
P2_End:

P1_Start:
; First part of the program stored here for future use
P1_End:

V2_Start:
; Real Stuff
V2_End:

V1_Length EQU V1_End - V1_Start

Alternatively, you could store P1 in V2 as follows:

V2_Start:

P1_Start:
P1_End:

V2_End:

That's all there is to infecting a COM file without destroying it! Simple, no? EXE files, however, are a little tougher to infect without rendering them inexecutable - I will cover this topic in a later file.

Now let us turn our attention back to the replicator portion of the virus. The steps are outlined below:

1) Find a file to infect
2) Check if it is already infected
3) If so, go back to 1
4) Infect it
5) If infected enough, quit
6) Otherwise, go back to 1

Finding a file to infect is a simple matter of writing a directory traversal procedure and issuing FINDFIRST and FINDNEXT calls to find possible files to infect. Once you find the file, open it and read the first few bytes. If they are the same as the first few bytes of V1, then
the file is already infected. If the first bytes of V1 are not unique to your virus, change it so that they are. It is *extremely* important that your virus doesn't reinfect the same files, since that was how Jerusalem was first detected. If the file wasn't already infected, then infect it!
Infection should take the following steps:

1) Change the file attributes to nothing.
2) Save the file date/time stamps.
3) Close the file.
4) Open it again in read/write mode.
5) Save P1 and append it to the end of the file.
6) Copy V1 to the beginning, but change the offset which it JMPs to so
it transfers control correctly. See the previous part on infection.
7) Append V2 to the end of the file.
8) Restore file attributes/date/time.

You should keep a counter of the number of files infected during this run. If the number exceeds, say three, then stop. It is better to infect slowly then to give yourself away by infecting the entire drive at once.

You must be sure to cover your tracks when you infect a file. Save the file's original date/time/attributes and restore them when you are finished. THIS IS VERY IMPORTANT! It takes about 50 to 75 bytes of code, probably less, to do these few simple things which can do wonders for the concealment of your program.

No comments: