Click here to Skip to main content
11,412,516 members (73,227 online)
Click here to Skip to main content

Real Self-Replicating Program

, 19 Nov 2007 CPOL
Rate this:
Please Sign up or sign in to vote.
A self-reproducing, mutable, compiling, and executing computer program.

Screenshot - SelfReplication.png

Introduction

From Wiki:

Self-replication is any process by which a thing might make a copy of itself.

Background

At one point, I wanted a small program to compile some Notepad edited scripts and run them on the fly. There is this nice project called "C# Script: The Missing Puzzle Piece". But, that is for professionals. And then, one night I went to do some coding. And came up with a code compiler. But, this was not enough. I wanted to store the source-code for this program into the program itself, and a final spec was to generate this same source code out of the program.

In short:

  1. There is only one executable.
  2. When starting the executable, it generates its own source code.
  3. When starting the executable again, it compiles this source code and executes it, showing the same user interface!

    A nice test is to delete the executable and compile the generated source code by using Visual Studio or the command line C# compiler:

    > del SelfReplication.exe
    > csc SelfReplication.cs
    > move SelfReplication.cs SelfReplication-old.cs
    > SelfReplication.exe
  4. The last statement generates a SelfReplication.cs file.
  5. The old and the new generated files are exactly the same!!

A feature of the program is you can change (mutate) the source code, adding new functionality and generating a totally new executable. The new program will be able to replicate itself, including your mutation, in the same way as the original one.

Using the code

The code has four sections:

The compiler

The compiler is a straightforward C# compiler which uses a file path to a C# file and produces an assembly of the source file. A nice feature is to get the names for the ReferencedAssemblies by calling GetReferencedAssemblies on the assembly itself.

private Assembly CompileCSharp(string strFilePath)
{
    if (strFilePath == null)
        return null;
    StreamReader sr = new StreamReader(strFilePath);
    string strSource = sr.ReadToEnd();
    sr.Close();
    CodeDomProvider cc = new CSharpCodeProvider();
    CompilerParameters cp = new CompilerParameters();
    foreach (AssemblyName assemblyName in 
    Assembly.GetEntryAssembly().GetReferencedAssemblies())
        cp.ReferencedAssemblies.Add(assemblyName.Name + ".dll");
    cp.GenerateInMemory = true;
    CompilerResults cr = cc.CompileAssemblyFromSource(cp, strSource);

    StringBuilder sb = new StringBuilder();

    if (cr.Errors.HasErrors || cr.Errors.HasWarnings)
    {
        foreach (CompilerError err in cr.Errors)
            sb.AppendLine(err.ToString());
        MessageBox.Show(sb.ToString(), "Error", 
        MessageBoxButtons.OK,
        MessageBoxIcon.Error);
        return null;
    }
    return cr.CompiledAssembly;
}

Compiling and executing an assembly

This function is also straightforward. It searches for any Main method on the compiled assembly, and invokes (executes) this Main method using the FilePath as an argument.

private void Execute(object FilePath)
{
    Assembly assembly = CompileCSharp(FilePath as string);
    if (assembly == null)
        return;

    foreach (Type t in assembly.GetTypes())
    {
        MethodInfo info = t.GetMethod("Main",
            BindingFlags.Public |
            BindingFlags.NonPublic |
            BindingFlags.Static);

        if (info == null)
            continue;

        object[] parameters = new object[]
        { new string[] { FilePath as string } };
        info.Invoke(null, parameters);
    }
}

The main program

This is a tricky one. It checks for the existence of SeflReplication.cs. If it does not exist, it writes the contents of MYSELF to disk to clean up the code (not shown in this source listing for brevity). After writing it to disk, it is read back again and the MYSELF string is replaced by itself. It took me about 2 or 3 hours to get this right.

static class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        string strFilePath;
        if (args.Length == 0)
        {
            string strDirectory =
                Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            strFilePath = Path.Combine(strDirectory, "SelfReplication.cs");
            if (!File.Exists(strFilePath))
            {
                StreamWriter sw = new StreamWriter(strFilePath);
                sw.Write(Program.MYSELF);
                sw.Close();

                StreamReader sr = new StreamReader(strFilePath);
                string strCode = sr.ReadToEnd();
                sr.Close();

                int intI = strCode.IndexOf(" " + "MYSELF");
                if (intI > 0)
                    strCode = strCode.Substring(0, intI + 7) + 
                              ";\r\n\t}\r\n}\r\n";

                string strInsertCode = "MYSELF=@\"" + 
                       strCode.Replace("\"", "\"\"") + "\";";

                strCode = strCode.Replace("MYSELF" + ";", strInsertCode);

                sw = new StreamWriter(strFilePath);
                sw.Write(strCode);
                sw.Close();

                return;
            }
        }
        else
        {
            strFilePath = args[0];
        }
        Application.Run(new SelfReplication(strFilePath));
    }
    public static string MYSELF=@".......";
}

Now, the basics are in place. But in this era, there is not much place for Console programs, so I added a simple user interface to the program. You have to press a button for starting the replication (once). This prevents us from making some kind of runaway virus.

Adding a GUI

The GUI has a button on a small window. An event handler starts a new thread, having the FilePath as an argument. When the parent of all parents dies, it kills all the children.

private string strFilePath;
public SelfReplication(string strFilePath)
{
    this.strFilePath = strFilePath;
    this.button1 = new Button();
    this.button1.Location = new Point(75, 25);
    this.button1.Size = new Size(100, 25);
    this.button1.Text = "Replicate";
    this.button1.Click += new System.EventHandler(this.button1_Click);
    this.ClientSize = new Size(250, 75);
    this.Controls.Add(this.button1);
    this.Text = "SelfReplication";
}

private void button1_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(new ParameterizedThreadStart(Execute));
    thread.Name = "Execute";
    thread.IsBackground = true;
    thread.Start(strFilePath);
}

By writing this article, new ideas popped up. I am thinking of a contest to mutate the source-code, doing all kinds of weird stuff. There are only a few rules for this contest, which are mentioned in the head of this article.

Another idea is to compile to disk rather than compiling in-memory, leaving all other 'external' programs untouched.

Have fun with the program, and leave some comments when doing awful awesome things inspired by my article.

Points of interest

If you are interested in self-replication, you should definitely read the Wiki article about it.

And for this project, the Quin article will get you moving.

Acknowledgments

Thanks to Stewart Roberts for the Visual Basic .NET version of the program.

History

As of writing, the presented source code is version 1.0.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Alphons van der Heijden
Retired Van der Heijden Holding BV
Netherlands Netherlands
I'm Alphons van der Heijden, living in Lelystad, Netherlands, Europa, Earth. And currently I'm retiring from hard working ( ;- ), owning my own company. Because I'm full of energy, and a little to young to relax ...., I don't sit down, but create and recreate software solutions, that I like. Reinventing the wheel is my second nature. My interest is in the area of Internet technologies, .NET etc. I was there in 1992 when Mosaic came out, and from that point, my life changed dramatically, and so did the world, in fact. (Y)

Comments and Discussions

 
GeneralMy vote of 5 Pin
Kanasz Robert at 6-Nov-12 3:32
mvpKanasz Robert6-Nov-12 3:32 
Generalanother possible usage Pin
Roey C at 21-Nov-07 2:52
memberRoey C21-Nov-07 2:52 
GeneralRe: another possible usage Pin
dawmail333 at 23-Dec-07 16:05
memberdawmail33323-Dec-07 16:05 
GeneralRe: another possible usage Pin
Roey C at 24-Dec-07 6:33
memberRoey C24-Dec-07 6:33 
GeneralMalware source on CP Pin
jonty2 at 19-Nov-07 14:51
memberjonty219-Nov-07 14:51 
GeneralRe: Malware source on CP Pin
alphons at 20-Nov-07 12:03
memberalphons20-Nov-07 12:03 
GeneralVB Version Pin
Stewart Roberts at 18-Nov-07 14:35
memberStewart Roberts18-Nov-07 14:35 
Very awesome stuff! Big Grin | :-D
After a few tedious hours playing with quotes... here is the VB version of your code.
(Use vbc instead of csc to compile).
Why must VB hate whitespace so much?


Imports System
Imports System.IO
Imports System.Text
Imports System.Drawing
Imports System.Threading
Imports System.Reflection
Imports Microsoft.VisualBasic
Imports System.Windows.Forms
Imports System.ComponentModel
Imports System.CodeDom.Compiler
Namespace SelfReplication
Public Class SelfReplication
Inherits Form
Private button1 As Button
Private Function CompileVisualBasic(ByVal strFilePath As String) As Assembly
If strFilePath Is Nothing Then
Return Nothing
End If
Dim sr As New StreamReader(strFilePath)
Dim strSource As String = sr.ReadToEnd()
sr.Close()
Dim cc As CodeDomProvider = New VBCodeProvider()
Dim cp As New CompilerParameters()
For Each assemblyName As AssemblyName In Assembly.GetEntryAssembly().GetReferencedAssemblies()
cp.ReferencedAssemblies.Add(assemblyName.Name + ".dll")
Next
cp.GenerateInMemory = True
Dim cr As CompilerResults = cc.CompileAssemblyFromSource(cp, strSource)
Dim sb As New StringBuilder()
If cr.Errors.HasErrors OrElse cr.Errors.HasWarnings Then
For Each err As CompilerError In cr.Errors
sb.AppendLine(err.ToString())
Next
MessageBox.Show(sb.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.[Error])
Return Nothing
End If
Return cr.CompiledAssembly
End Function
Private Sub Execute(ByVal FilePath As Object)
Dim assembly As Assembly = CompileVisualBasic(TryCast(FilePath, String))
If assembly Is Nothing Then
Return
End If
For Each t As Type In assembly.GetTypes()
Dim info As MethodInfo = t.GetMethod("Main", BindingFlags.[Public] Or BindingFlags.NonPublic Or BindingFlags.[Static])
If info Is Nothing Then
Continue For
End If
Dim parameters As Object() = New Object() {New String() {TryCast(FilePath, String)}}
info.Invoke(Nothing, parameters)
Next
End Sub
Private strFilePath As String
Public Sub New(ByVal strFilePath As String)
Me.strFilePath = strFilePath
Me.button1 = New Button()
Me.button1.Location = New Point(75, 25)
Me.button1.Size = New Size(100, 25)
Me.button1.Text = "Replicate"
AddHandler Me.button1.Click, AddressOf button1_Click
Me.ClientSize = New Size(250, 75)
Me.Controls.Add(Me.button1)
Me.Text = "SelfReplication"
End Sub
Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)
Dim thread As New Thread(New ParameterizedThreadStart(AddressOf Execute))
thread.Name = "Execute"
thread.IsBackground = True
thread.Start(strFilePath)
End Sub
End Class
NotInheritable Class Program
Private Sub New()
End Sub
Public Shared Sub Main(ByVal args As String())
Dim strFilePath As String
If args.Length = 0 Then
Dim strDirectory As String = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
strFilePath = Path.Combine(strDirectory, "SelfReplication.vb")
If Not File.Exists(strFilePath) Then
Dim sw As New StreamWriter(strFilePath)
sw.Write(Program.MYSELF)
sw.Close()
Dim sr As New StreamReader(strFilePath)
Dim strCode As String = String.Empty
Dim line As String = String.Empty
While sr.Peek <> -1
line = sr.ReadLine()
line = line.Replace(Chr(34), Chr(34) & " & Chr(34) & " & Chr(34))
strCode &= Chr(34) & line & Chr(34) & " & Environment.NewLine _" & Environment.NewLine & "& "
End While
sr.Close()
strCode = strCode.Substring(0, strCode.Length - 27)
sr = New StreamReader(strFilePath)
line = sr.ReadToEnd()
sr.Close()
line = line.Replace(line.Substring(line.Length - 26), " = _" & Environment.NewLine & strCode & Environment.NewLine & "End Class" & Environment.NewLine & "End Namespace")
sw = New StreamWriter(strFilePath)
sw.Write(line)
sw.Close()
Return
End If
Else
strFilePath = args(0)
End If
Application.Run(New SelfReplication(strFilePath))
End Sub
Public Shared MYSELF As String = _
"Imports System" & Environment.NewLine _
& "Imports System.IO" & Environment.NewLine _
& "Imports System.Text" & Environment.NewLine _
& "Imports System.Drawing" & Environment.NewLine _
& "Imports System.Threading" & Environment.NewLine _
& "Imports System.Reflection" & Environment.NewLine _
& "Imports Microsoft.VisualBasic" & Environment.NewLine _
& "Imports System.Windows.Forms" & Environment.NewLine _
& "Imports System.ComponentModel" & Environment.NewLine _
& "Imports System.CodeDom.Compiler" & Environment.NewLine _
& "Namespace SelfReplication" & Environment.NewLine _
& "Public Class SelfReplication" & Environment.NewLine _
& "Inherits Form" & Environment.NewLine _
& "Private button1 As Button" & Environment.NewLine _
& "Private Function CompileVisualBasic(ByVal strFilePath As String) As Assembly" & Environment.NewLine _
& "If strFilePath Is Nothing Then" & Environment.NewLine _
& "Return Nothing" & Environment.NewLine _
& "End If" & Environment.NewLine _
& "Dim sr As New StreamReader(strFilePath)" & Environment.NewLine _
& "Dim strSource As String = sr.ReadToEnd()" & Environment.NewLine _
& "sr.Close()" & Environment.NewLine _
& "Dim cc As CodeDomProvider = New VBCodeProvider()" & Environment.NewLine _
& "Dim cp As New CompilerParameters()" & Environment.NewLine _
& "For Each assemblyName As AssemblyName In Assembly.GetEntryAssembly().GetReferencedAssemblies()" & Environment.NewLine _
& "cp.ReferencedAssemblies.Add(assemblyName.Name + " & Chr(34) & ".dll" & Chr(34) & ")" & Environment.NewLine _
& "Next" & Environment.NewLine & "cp.GenerateInMemory = True" & Environment.NewLine _
& "Dim cr As CompilerResults = cc.CompileAssemblyFromSource(cp, strSource)" & Environment.NewLine _
& "Dim sb As New StringBuilder()" & Environment.NewLine _
& "If cr.Errors.HasErrors OrElse cr.Errors.HasWarnings Then" & Environment.NewLine _
& "For Each err As CompilerError In cr.Errors" & Environment.NewLine _
& "sb.AppendLine(err.ToString())" & Environment.NewLine _
& "Next" & Environment.NewLine _
& "MessageBox.Show(sb.ToString(), " & Chr(34) & "Error" & Chr(34) & ", MessageBoxButtons.OK, MessageBoxIcon.[Error])" & Environment.NewLine _
& "Return Nothing" & Environment.NewLine _
& "End If" & Environment.NewLine _
& "Return cr.CompiledAssembly" & Environment.NewLine _
& "End Function" & Environment.NewLine _
& "Private Sub Execute(ByVal FilePath As Object)" & Environment.NewLine _
& "Dim assembly As Assembly = CompileVisualBasic(TryCast(FilePath, String))" & Environment.NewLine _
& "If assembly Is Nothing Then" & Environment.NewLine _
& "Return" & Environment.NewLine _
& "End If" & Environment.NewLine _
& "For Each t As Type In assembly.GetTypes()" & Environment.NewLine _
& "Dim info As MethodInfo = t.GetMethod(" & Chr(34) & "Main" & Chr(34) & ", BindingFlags.[Public] Or BindingFlags.NonPublic Or BindingFlags.[Static])" & Environment.NewLine _
& "If info Is Nothing Then" & Environment.NewLine _
& "Continue For" & Environment.NewLine _
& "End If" & Environment.NewLine _
& "Dim parameters As Object() = New Object() {New String() {TryCast(FilePath, String)}}" & Environment.NewLine _
& "info.Invoke(Nothing, parameters)" & Environment.NewLine _
& "Next" & Environment.NewLine _
& "End Sub" & Environment.NewLine _
& "Private strFilePath As String" & Environment.NewLine _
& "Public Sub New(ByVal strFilePath As String)" & Environment.NewLine _
& "Me.strFilePath = strFilePath" & Environment.NewLine _
& "Me.button1 = New Button()" & Environment.NewLine _
& "Me.button1.Location = New Point(75, 25)" & Environment.NewLine _
& "Me.button1.Size = New Size(100, 25)" & Environment.NewLine _
& "Me.button1.Text = " & Chr(34) & "Replicate" & Chr(34) & Environment.NewLine _
& "AddHandler Me.button1.Click, AddressOf button1_Click" & Environment.NewLine _
& "Me.ClientSize = New Size(250, 75)" & Environment.NewLine _
& "Me.Controls.Add(Me.button1)" & Environment.NewLine _
& "Me.Text = " & Chr(34) & "SelfReplication" & Chr(34) & Environment.NewLine _
& "End Sub" & Environment.NewLine _
& "Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)" & Environment.NewLine _
& "Dim thread As New Thread(New ParameterizedThreadStart(AddressOf Execute))" & Environment.NewLine _
& "thread.Name = " & Chr(34) & "Execute" & Chr(34) & Environment.NewLine _
& "thread.IsBackground = True" & Environment.NewLine _
& "thread.Start(strFilePath)" & Environment.NewLine _
& "End Sub" & Environment.NewLine _
& "End Class" & Environment.NewLine _
& "NotInheritable Class Program" & Environment.NewLine _
& "Private Sub New()" & Environment.NewLine _
& "End Sub" & Environment.NewLine _
& "Public Shared Sub Main(ByVal args As String())" & Environment.NewLine _
& "Dim strFilePath As String" & Environment.NewLine _
& "If args.Length = 0 Then" & Environment.NewLine _
& "Dim strDirectory As String = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)" & Environment.NewLine _
& "strFilePath = Path.Combine(strDirectory, " & Chr(34) & "SelfReplication.vb" & Chr(34) & ")" & Environment.NewLine _
& "If Not File.Exists(strFilePath) Then" & Environment.NewLine _
& "Dim sw As New StreamWriter(strFilePath)" & Environment.NewLine _
& "sw.Write(Program.MYSELF)" & Environment.NewLine _
& "sw.Close()" & Environment.NewLine _
& "Dim sr As New StreamReader(strFilePath)" & Environment.NewLine _
& "Dim strCode As String = String.Empty" & Environment.NewLine _
& "Dim line As String = String.Empty" & Environment.NewLine _
& "While sr.Peek <> -1" & Environment.NewLine _
& "line = sr.ReadLine()" & Environment.NewLine _
& "line = line.Replace(Chr(34), Chr(34) & " & Chr(34) & " & Chr(34) & " & Chr(34) & " & Chr(34))" & Environment.NewLine _
& "strCode &= Chr(34) & line & Chr(34) & " & Chr(34) & " & Environment.NewLine _" & Chr(34) & " & Environment.NewLine & " & Chr(34) & "& " & Chr(34) & Environment.NewLine _
& "End While" & Environment.NewLine _
& "sr.Close()" & Environment.NewLine _
& "strCode = strCode.Substring(0, strCode.Length - 27)" & Environment.NewLine _
& "sr = New StreamReader(strFilePath)" & Environment.NewLine _
& "line = sr.ReadToEnd()" & Environment.NewLine _
& "sr.Close()" & Environment.NewLine _
& "line = line.Replace(line.Substring(line.Length - 26), " & Chr(34) & " = _" & Chr(34) & " & Environment.NewLine & strCode & Environment.NewLine & " & Chr(34) & "End Class" & Chr(34) & " & Environment.NewLine & " & Chr(34) & "End Namespace" & Chr(34) & ")" & Environment.NewLine _
& "sw = New StreamWriter(strFilePath)" & Environment.NewLine _
& "sw.Write(line)" & Environment.NewLine _
& "sw.Close()" & Environment.NewLine _
& "Return" & Environment.NewLine _
& "End If" & Environment.NewLine _
& "Else" & Environment.NewLine _
& "strFilePath = args(0)" & Environment.NewLine _
& "End If" & Environment.NewLine _
& "Application.Run(New SelfReplication(strFilePath))" & Environment.NewLine _
& "End Sub" & Environment.NewLine _
& "Public Shared MYSELF As String" & Environment.NewLine _
& "End Class" & Environment.NewLine _
& "End Namespace"
End Class
End Namespace

GeneralRe: VB Version Pin
alphons at 18-Nov-07 15:36
memberalphons18-Nov-07 15:36 
QuestionRe: VB Version Pin
Stewart Roberts at 19-Nov-07 6:12
memberStewart Roberts19-Nov-07 6:12 
AnswerRe: VB Version [modified] Pin
alphons at 20-Nov-07 12:00
memberalphons20-Nov-07 12:00 
GeneralRe: VB Version Pin
Stewart Roberts at 20-Nov-07 13:46
memberStewart Roberts20-Nov-07 13:46 
GeneralRe: VB Version Pin
dawmail333 at 23-Dec-07 16:20
memberdawmail33323-Dec-07 16:20 
GeneralRe: VB Version Pin
Stewart Roberts at 27-Dec-07 8:52
memberStewart Roberts27-Dec-07 8:52 
GeneralMutation Pin
Daniel Vaughan at 15-Nov-07 1:09
memberDaniel Vaughan15-Nov-07 1:09 
GeneralRe: Mutation Pin
alphons at 18-Nov-07 15:32
memberalphons18-Nov-07 15:32 
QuestionWhy in memory? Pin
Giorgi Dalakishvili at 14-Nov-07 10:25
memberGiorgi Dalakishvili14-Nov-07 10:25 
AnswerRe: Why in memory? Pin
alphons at 14-Nov-07 12:08
memberalphons14-Nov-07 12:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150427.1 | Last Updated 20 Nov 2007
Article Copyright 2007 by Alphons van der Heijden
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid