Poly Polymorphic Engine API Documentation

Poly Polymorphic Engine function reference & usage examples.

Encryption function description

Poly Polymorphic Engine has only one function which both encrypts data and generates a corresponding decryption routine.

unsigned int __stdcall _poly(
  void *             lpDecryptor,
  void *             lpOutput,
  void *             lpInput,
  unsigned int       iSize,
  unsigned int       lpVA,
  unsigned int       cMaxInstr,
  unsigned int       cMinInstr,
  unsigned int       iGarbage,
  unsigned int       iForceSize,
  unsigned int       lpRelativeAddr,
  struct POLY_REGS * lpPolyOutRegs,
  unsigned int       iOptions,
  void *             lpWorkMem,
  unsigned int       iRandomSeed
);
function _poly(
  lpDecryptor    : PByteArray,
  lpOutput       : PByteArray,
  lpInput        : PByteArray,
  iSize          : DWORD,
  lpVA           : DWORD,
  cMaxInstr      : DWORD,
  cMinInstr      : DWORD,
  iGarbage       : DWORD,
  iForceSize     : DWORD,
  lpRelativeAddr : DWORD,
  prPolyOutRegs  : TPolyRegsPtr,
  iOptions       : DWORD,
  lpWorkMem      : PByteArray,
  iRandomSeed    : DWORD
): DWORD;
  push    dwRandomSeed
  push    offset lpWorkMem
  push    iOptions
  push    lpPolyOutRegs
  push    lpRelativeAddr
  push    iForceSize
  push    iGarbage
  push    cMinInstr
  push    cMaxInstr
  push    lpVA
  push    iSize
  push    lpInput
  push    lpOutput
  push    lpDecryptor
  call    _poly
lpDecryptor [out]
Output buffer for the decryptor code. Optionally encrypted data is also stored in this buffer, but only if the POLY_ATTACH_DATA flag is set.
lpOutput [out, optional]
Output buffer for the encrypted data. It can be the same as lpInput. If the POLY_ATTACH_DATA flag is set, this parameter is ignored, because encrypted data will be stored alongside the decryption code in lpDecryptor decryption code buffer.
lpInput [in]
Input data to encrypt.
iSize [in]
Input data size in bytes pointed by lpInput.
lpVA [in, optional]
An optional parameter with the virtual address of the encrypted data. This address will be hardcoded and used by a decryptor to decrypt the data. Use it only if you know where the encrypted data will be placed after encryption (e.g. non-relocatable virtual address in an executable file image).
cMaxInstr [in]
Maximal number of encryption commands used in random encryption algorithm generation. The more encryption instructions, the bigger the output decryption code, and you need to remember to adjust the size of lpDecryptor and lpWorkMem buffers.
cMinInstr [in]
Minimal number of encryption commands used in random encryption algorithm generation. The more encryption instructions, the bigger the output decryption code and you need to remember to adjust the size of lpDecryptor and lpWorkMem buffers.
iGarbage [in]
Number of junks per decryptor code instruction. This value can be set to 0. Junks / garbage instructions are used to make decryption code reverse engineering analysis extremely difficult.
iForceSize [in, optional]
An optional parameter. If this value is set, Poly engine will try to generate a decryptor body that is exactly this size; it will modify cMaxInstr, cMinInstr and iGarbage parameters on the fly if the generated code is too small or too big and re-generate the code again to meet your exact criteria (please make sure the working buffers lpDecryptor and lpWorkMem are bigger than this value).
lpRelativeAddr [in, optional]
If this parameter is set, the polymorphic decryption code will use it to calculate the address of the encrypted data in memory. This pointer is relative to the current position of the decryptor code in the memory (it can be positive or negative). It can used for example if you know the exact position of the encrypted data (as a virtual VA or RVA address) and position of the polymorphic decryptor.
lpPolyOutRegs [in, optional]

Pointer to the POLY_REGS structure with output CPU register values set after the decryption ends. To make it work, the POLY_SET_REGS flag has to be set as well as one or more register flags specifying which registers you want to set, e.g. POLY_SET_EAX.

struct POLY_REGS
{
  unsigned int regEax;
  unsigned int regEcx;
  unsigned int regEdx;
  unsigned int regEbx;
  unsigned int regEsp;
  unsigned int regEbp;
  unsigned int regEsi;
  unsigned int regEdi;
};
iOptions [in]

Additional feature flags for the polymorphic engine.

Name Value Description
POLY_SET_REGS 0x00000001 Set output registers values defined in POLY_REGS structure, after the decryption code finishes its work.
POLY_SET_EAX 0x00000002 Set EAX register output value.
POLY_SET_ECX 0x00000004 Set ECX register output value.
POLY_SET_EDX 0x00000008 Set EDX register output value.
POLY_SET_EBX 0x00000010 Set EBX register output value.
POLY_SET_ESP 0x00000020 Set ESP register output value.
POLY_SET_EBP 0x00000040 Set EBP register output value..
POLY_SET_ESI 0x00000080 Set ESI register output value.
POLY_SET_EDI 0x00000100 Set EDI register output value..
POLY_SAVE_REGS 0x00000200 Save all 32 bit register values with PUSHAD instruction (except those used with POLY_SET_REGS flag) before the call and restore them with POPAD instruction after the decryption code finishes.
POLY_SAVE_FLAGS 0x00000400 Save all CPU flags with PUSHFD instruction before the call and restore them with POPFD instruction after the decryption code finishes.
POLY_RETURN 0x00000800

If this flag is set, a return instruction (RET) is placed at the end of the decryption function, so the decryptor returns to the caller. Thanks to this flag, you can call decryption code like a regular function.

Polymorphic decryptor can be inlined between other code or some other code can be added after its body so it doesn't have to return to the caller.

POLY_ATTACH_DATA 0x00001000

Attach encrypted data to the decryptor body so the decryption code and encrypted data will be stored in a single block. The decryptor will decrypt the data and overwrite its own body so after decryption, a pointer to the decryption code will also be a pointer to the decrypted data.

If this flag is not used, the encrypted data has to be located at the static VA address declared in lpVA parameter or declared via relative address in the iRelativeDataOutputOffset parameter.

POLY_FLAGS_ALL 0xFFFFFFFF All above bit flags combined.
lpWorkMem [in]
Temporary working memory buffer. It has to be allocated with executable flags set and its size has to be as big as the size of lpDecryptor buffer.
iRandomSeed [in]
Seed value for the internal randomizer, so the encryption commands and decryption code is different for every function call.

Return value

If the data is successfully encrypted and a decryption routine is generated, this function returns the size of the decryption routine (and optionally encrypted data) stored in lpDecryptor memory buffer.

This function returns 0 on error.

C++ Example

This engine is very flexible and can be used in different scenarios, which is why the best way to show its features is to present an example.

In the below example, Poly Polymorphic Engine is used to encrypt the input block of data, generate the decryption code, and append the encrypted data to it. Although this is not shown, the resulting block can be saved to an external file, which can later be decrypted by loading its contents into an executable memory buffer and calling the memory buffer pointer like a regular function.

In this example, all of the available options are used and it runs for 10000 iterations to test the validity of encryption and decryption process.

#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

#include "poly.h"

// helper macro
#define RND_RANGE(min,max) (min + (rand() % (int)(max - min + 1)))

// number of test iterations
const int POLY_TEST_ITERATIONS = 10000;

// safe memory buffer size
const int POLY_DECRYPTOR_SIZE = 1024 * 2048 * 10;

// sample input data to encrypt
unsigned char cInputBuffer[] = { 0x11, 0x22, 0x33, 0x44 } ;

// output buffer
unsigned char cOutputBuffer[sizeof(cInputBuffer)] = { 0 };

int main(int argc, char* argv[])
{
  // allocate memory buffer for the encrypted block (it has to be executable)
  PVOID lpDecryptor = VirtualAlloc(nullptr, POLY_DECRYPTOR_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  // work memory buffer (same size, also has to be executable)
  PVOID lpWorkMem = VirtualAlloc(nullptr, POLY_DECRYPTOR_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  for (int i = 0; i < POLY_TEST_ITERATIONS; i++)
  {
    //
    // setup Poly engine parameters
    //

    // randomize parameters (remember, more means you need to increase
    // the size of the lpDecryptor & lpWorkMem buffers)

    // minimal number of encryption commands
    unsigned int iMinEncryptionCommands = RND_RANGE(10, 50);

    // maximal number of encryption commands
    unsigned int iMaxEncryptionCommands = RND_RANGE(iMinEncryptionCommands, iMinEncryptionCommands + 10);

    // number of junks per decryptor single command
    unsigned int iGarbage = RND_RANGE(10, 50);

    // force decryptor size - if this value is set, Poly engine will try to generate
    // decryptor body that is exactly this size, it will modify other parameters on
    // the fly if the generated code is too small or too big and re-generate the code
    // again to meet your exact criteria (please make sure the working buffers
    // lpDecryptor and lpWorkMem are bigger than this value)
    unsigned int iForceDecryptorSize = 0;

    // if this parameter is set, polymorphic decryption code will use it to calculate
    // the address of the encrypted data in memory, this pointer is relative to the
    // current position of the decryptor code in the memory (it can be positive or
    // negative)
    unsigned int iRelativeDataOutputOffset = 0;

    // flags
    unsigned int iOptions = 0;

    // POLY_SAVE_REGS - save all registers before the call and restore them after the call
    iOptions |= POLY_SAVE_REGS;

    // POLY_SAVE_FLAGS - save all CPU flags before the call and restore them after the call
    iOptions |= POLY_SAVE_FLAGS;

    // POLY_ATTACH_DATA - attach data to the decryptor body, decryption will overwrite
    // decryptor body and after decryption lpDecryptor will also be a pointer to the
    // decrypted buffer
    //
    // if this flag is not used, encrypted data has to be located at the static VA address
    // declared in lpVA parameter or declared via relative address in iRelativeDataOutputOffset
    // parameter
    iOptions |= POLY_ATTACH_DATA;

    // POLY_RETURN - put a return instruction (RET) at the end of the decryption function,
    // so the decryptor returns to the caller, polymorphic decryptor can be inlined
    // between other code or some other code can be added after its body so it may or
    // may not return to the caller
    iOptions |= POLY_RETURN;

    // POLY_SET_REGS - return exact values in CPU registers after decryption (it must be
    // defined in POLY_REGS structure)
    iOptions |= POLY_SET_REGS;

    // return value in EAX register
    iOptions |= POLY_SET_EAX;

    // and let's say it should return some other value in EDX register after the call
    iOptions |= POLY_SET_EDX;

    // output registers values (fill out only those marked to be returned)
    POLY_REGS prPolyRegs = { 0 };

    // fill out the EAX register with a value that should be returned after
    // the decryption, in this example let's set it to the size of the decrypted
    // buffer (you can set it to anything)
    prPolyRegs.regEax = sizeof(cInputBuffer);

    // sample value to be returned in EDX register after the call (view it
    // with a debugger)
    prPolyRegs.regEdx = 0xDEADBEEF;

    // encrypt data & generate polymorphic decryptor
    unsigned int dwOutputSize = _poly(

      lpDecryptor,                   // output buffer for polymorphic code
      cOutputBuffer,                 // output data buffer (can be the same as lpInput) (optional)
      cInputBuffer,                  // input data buffer
      sizeof(cInputBuffer),          // input data size
      (unsigned int)&cOutputBuffer,  // virtual address of data to be decrypted (optional)
      iMaxEncryptionCommands,        // max. number of real encryption instructions
      iMinEncryptionCommands,        // min. number of real encryption instructions
      iGarbage,                      // number of junks per instruction
      iForceDecryptorSize,           // force decryptor size (optional)
      iRelativeDataOutputOffset,     // relative output data offset (optional)
      &prPolyRegs,                   // output registers (optional)
      iOptions,                      // additional options
      lpWorkMem,                     // work memory
      GetTickCount()                 // random seed for the internal randomizer

    );

    // validate output size (decryptor size in bytes)
    if (dwOutputSize != 0)
    {
      PolyDecryptorFunction DecryptorFunction = reinterpret_cast<PolyDecryptorFunction>(lpDecryptor);

      // decrypt data
      unsigned int dwResult = DecryptorFunction();

      // another way to invoke decryptor function (inline assembly)
      /*
      __asm
      {
        int  3

        mov  eax, lpDecryptor;
        call  eax

        int 3
      }
      */

      // validate decrypted data (if POLY_ATTACH_DATA flag was set - lpDecryptor
      // points to the decrypted data)
      if (memcmp(reinterpret_cast<PVOID>(lpDecryptor), cInputBuffer, sizeof(cInputBuffer) != 0))
      {
        printf("Polymorphic engine failed (decrypted data is invalid)!\n");
        _getch();

        return 2;
      }
    }
    else
    {
      printf("Polymorphic engine failed!\n");
      _getch();

      return 1;
    }
  }

  printf("Polymorphic engine test success (%lu iterations)\n", POLY_TEST_ITERATIONS);

  // relese the memory
  VirtualFree(lpDecryptor, 0, MEM_RELEASE);
  VirtualFree(lpWorkMem, 0, MEM_RELEASE);

  _getch();

  return 0;
}

Requirements

Header poly.h
Library poly.lib
DLL poly.dll

Questions?

If you would like to ask me about Poly Polymorphic Engine, or something's not clear, mail me. I'll be happy to answer all of your questions.