BarryServer : Projects

A set of pages about projects I do
// BarryServer : Projects / PolymorphicEngine /

// Related

Polymorphic Engine

A polymorphic engine, also known as a mutation engine, is a program that transforms a program into a subsequent version with different code, but the same functionality. My polymorphic engine will modify its own code. This project will support other functionality being written into the code, which will also get modified by the engine. Polymorphic behaviour will be achieved by encrypting the main body of the code with a random key.

Theory

The engine's flow is pretty simple, and is easily imagined as a flat binary. Since I want this code to run on multiple platforms and operating systems with minimal or no modification, I intend not to have to read any file metadata specific to an executable format, e.g. ELF or EXE. Finding the correct sections in a file is a matter for later, but assuming we can find them, the theory behind our engine is simple:
  1. The program starts
  2. Run a decryption routine, decrypt main body
  3. Jump to the decrypted code
  4. Run some arbitrary payload
  5. Duplicate our file to a child file
  6. Open the child's file
  7. Generate a new key
  8. Read our main body from memory, encrypt it, and write to the file over the old body
  9. Write the key to the child file
  10. Halt execution
The child file can follow this exact same flow, but with the new decryption key. Thanks to encryption, we ensure that the majority of the program's machine code is different to iterations before it, so it should go undetected. Since it's just getting encrypted, then decrypted, the final code that is run is identical to the original.

We will have a few issues with this due to memory protections that are in place and, as mentioned before, finding the correct parts of the file. The former issue can be solved by just changing the memory protections on the region of memory. The latter issue is a bit harder to solve. Breaking it down, we have to determine the size of our program's body, the offset in the file of our program's body, and the location in the file of the encryption key.

Finding the code is fairly easy, we can just parse the ELF file headers to find the polymorphic section. This can be done in a linker script:
SECTIONS {
	.polymorphic : { *(.polymorphic); }
}
INSERT AFTER .text;
We just insert a custom section .polymorphic after .text. We can now use the address and size of that section in our code, by looking for the named section. This section can encompass all functions we want to encrypt. We now know the size and offset. We can just get the position of the key from memory, and locate the correct offset in the .rodata section. The code works correctly on both 32-bit and 64-bit ELF files by switching the type of the structures used based on the word-size.

Encryption

A simple method of encryption is just to process each source byte with a key byte, using an operation such as XOR, ADD or SUB. Each of these processes can be reversed if you know the key byte. These are not processor intensive, and can be run for large amounts of data quickly. A key of multiple bytes can be used, and just looped so it matches the length of the source data. The processes can be picked randomly too, and embedded in the key. If we wanted an 8 byte key, we'd need an 8 byte array for the key bytes, and an 8 byte array for the operations, each byte being a number that represents an operation.

The processes, and their reverse operations are as follows (B is our key byte, A is the source data, C is the output):
A xor B = C
C xor B = A

A add B = C
C sub B = A

A sub B = C
C add B = A
The key can be randomly generated on the fly, and then looped to fit the data:
This is our data.
keykeykeykeykeyke
This applies to both the processes and the key bytes.

A key where all the key bytes are zero will not modify the data, as adding, subtracting, and xoring by zero yeild the original number. This is useful because our first version of the program (the one we compile) is not encrypted. An all-zero key means it will not change when decrypted, which is good, as we don't want to change it from the already functional code.

Technical

Some more technical things must be accounted for. Firstly, memory protection. We have to ensure that the memory that our code is stored in is writable, so we can decrypt it, and overwrite the new version. This is going to be operating system specific, sadly. Since I'm writing this on Linux, I'll make use of mprotect() in the sys/mman.h header. Secondly, we can't write to our own file. The OS doesn't allow it. Instead, we can create another file, copied from our file, and modify that. This child file can be given a random name.

Modifying the program to add functionality is very simple. You can add code to the polymorphic_main() function. Alternatively, if you wished to create your own functions, you could just add them to the .polymorphic section by adding POLYMORPHIC to the function return type. This does not have to be done on function prototypes.

For both compatibility and for security, the binaries generated will be statically linked. This makes them more difficult to reverse engineer, and also reduces any reliance on the host system providing the libraries required.