Dokumentacja Techniczna API Silnika Polimorficznego Poly

Opis funkcji, parametrów i flag Silnika Polimorficznego Poly wykorzystywanego do polimorficznego szyfrowania danych.

Opis funkcji szyfrującej

Silnik Polimorficzny Poly posiada jedną funkcję do szyfrowania danych i jednoczesnego generowania kodu deszyfrującego.

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 : Pointer;          // output buffer for polymorphic code
  lpOutput : Pointer;             // output data buffer (can be the same as lpInput)
  lpInput : Pointer;              // input data buffer
  iSize : LongWord;               // input data size
  lpVA : Pointer;                 // virtual address of a data to be decrypted
  cMaxInstr : LongWord;           // max. number of real encryption instructions
  cMinInstr : LongWord;           // min. number of real encryption instructions
  iGarbage : LongWord;            // number of junks per instruction
  iForceSize : LongWord;          // force decryptor size (optional)
  lpRelativeAddr : Integer;       // relative output data offset (optional)
  var lpPolyOutRegs : TPOLY_REGS; // output registers (optional)
  iOptions : LongWord;            // flags
  lpWorkMem : Pointer;            // work memory
  iRandomSeed : Integer           // random seed for the internal randomizer
): LongWord;stdcall;external 'poly.dll';
  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 [wyj]
Bufor wyjściowy na kod dekryptora. Ewentualnie znajdzie się tutaj zaszyfrowany blok danych, w przypadku użycia flagi POLY_ATTACH_DATA.
lpOutput [wyj, opcjonalnie]
Bufor wyjściowy, gdzie znajdą się zaszyfrowane dane. Może być taki sam jak bufor lpInput. Jeśli ustawiona jest flaga POLY_ATTACH_DATA wartość ta jest ignorowana (gdyż zaszyfrowane dane znajdą się w buforze lpDecryptor razem z kodem deszyfrującym).
lpInput [wej]
Bufor z danymi wejściowymi do zaszyfrowania.
iSize [wej]
Rozmiar w bajtach bufora wejściowego lpInput.
lpVA [wej, opcjonalnie]
Opcjonalny parametr określający adres wirtualny, który zostanie użyty przez kod dekryptora do odszyfrowania bloku danych. Jeśli wiemy pod jakim adresem wirtualnym znajdą się zaszyfrowane dane (np. nierelokowalny adres w pliku wykonywalnym) możemy ten parametr ustawić.
cMaxInstr [wej]
Maksymalna ilość instrukcji szyfrujących, które zostaną losowo wygenerowane w procesie szyfrowania. Im więcej instrukcji szyfrujących tym większy będzie wygenerowany kod i należy odpowiednio skorygować rozmiar buforów lpDecryptor i lpWorkMem.
iGarbage [wej]
Ilość instrukcji zaśmiecających, które zostaną umieszczone co instrukcję w kodzie funkcji deszyfrującej. Wartość może być ustawiona na 0. Instrukcje zaśmiecające skutecznie utrudniają analizę kodu deszyfrującego.
iForceSize [wej, opcjonalnie]
Opcjonalny parametr określający jaki dokładnie rozmiar w bajtach ma mieć wygenerowana funkcja deszyfrująca. Jeśli ten parametr jest ustawiony, funkcja będzie tak długo generowała różne wersje kodu deszyfrującego, dopóki nie uzyska określonego rozmiaru. Jeśli rozmiar będzie zbyt mały lub zbyt duży, automatycznie skorygowane zostaną parametry cMaxInstr, cMinInstr i iGarbage, tak aby uzyskać wymagany rozmiar.
lpRelativeAddr [wej, opcjonalnie]
Opcjonalny parametr, dzięki któremu kod deszyfrujący będzie w stanie obliczyć wskaźnik do zaszyfrowanych danych, których pozycja znajduje się w pamięci relatywnie do bufora, w którym aktualnie znajduje się funkcja deszyfrująca. Parametr ten może mieć negatywną wartość (jeśli dane znajdują się przed kodem funkcji deszyfrującej). Przykładem zastosowania tego parametru jest sytuacja, w której znamy docelowe położenie kodu funkcji deszyfrującej np. w obrazie pliku wykonywalnego (jako adres wirtualny VA lub RVA) i szyfrujemy dane z innego adresu wirtualnego znajdującego się w tym samym obrazie pliku wykonywalnego w pamięci.
lpPolyOutRegs [wej, opcjonalnie]

Struktura POLY_REGS określająca wartości rejestrów CPU, które zostaną ustawione po zakończeniu działania kodu deszyfrującego. Do poprawnego działania tego mechanizmu wymagane jest ustawienie flagi POLY_SET_REGS oraz jednej lub więcej flag określających, który rejestr procesora ma mieć ustawioną wartość określoną w strukturze, np. 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 [wej]

Dodatkowe opcje bitowe dla silnika polimorficznego.

Nazwa Wartość Znaczenie
POLY_SET_REGS 0x00000001 Po wykonaniu kodu deszyfrującego do rejestrów procesora zostaną zapisane wartości określone w strukturze POLY_REGS.
POLY_SET_EAX 0x00000002 Ustaw wartość wyjściową rejestru EAX.
POLY_SET_ECX 0x00000004 Ustaw wartość wyjściową rejestru ECX.
POLY_SET_EDX 0x00000008 Ustaw wartość wyjściową rejestru EDX.
POLY_SET_EBX 0x00000010 Ustaw wartość wyjściową rejestru EBX.
POLY_SET_ESP 0x00000020 Ustaw wartość wyjściową rejestru ESP.
POLY_SET_EBP 0x00000040 Ustaw wartość wyjściową rejestru EBP.
POLY_SET_ESI 0x00000080 Ustaw wartość wyjściową rejestru ESI.
POLY_SET_EDI 0x00000100 Ustaw wartość wyjściową rejestru EDI.
POLY_SAVE_REGS 0x00000200 Zachowaj wartość wszystkich 32 bitowych rejestrów procesora używając instrukcji PUSHAD (oprócz tych ustawianych flagą POLY_SET_REGS) i przywróć ich wartość po wykonaniu kodu dekryptora korzystając z instrukcji POPAD.
POLY_SAVE_FLAGS 0x00000400 Zachowaj wartość flag procesora wykorzystując instrukcję PUSHFD i przywróć ich wartość wykonując instrukcję POPFD po wykonaniu kodu dekryptora.
POLY_RETURN 0x00000800

Jeśli ta flaga zostanie ustawiona, w kodzie dekryptora, na jego końcu umieszczona zostanie instrukcja powrotu RET. Dzięki niej można wywoływać kod dekryptora jak normalną funkcję.

Kod dekryptora może być jednak umieszczany bezpośrednio pomiędzy innymi blokami kodu lub po jego kodzie mogą być dodawane inne bloki kodu, dlatego nie jest wymagane umieszczanie na końcu jego kodu instrukcji powrotu.

POLY_ATTACH_DATA 0x00001000

Ustawienie tej flagi spowoduje, że zaszyfrowane dane znajdą się w jednym buforze z kodem dekryptora. Kod dekryptora nadpisze swój własny bufor w pamięci odszyfrowanymi danymi i po odszyfrowaniu danych, wskaźnik wskazujący na kod dekryptora będzie jednocześnie wskazywał na odszyfrowane dane. Dzięki tej fladze można tworzyć kompaktowe bloki danych zawierające jednocześnie zaszyfrowane dane jak i kod deszyfrujący.

Jeśli ta flaga nie zostanie ustawiona, kod dekryptora wykorzysta wartość parametrów lpVA lub lpRelativeAddr do pobrania wskaźnika, gdzie znajdują się zaszyfrowane dane, które należy odszyfrować.

POLY_FLAGS_ALL 0xFFFFFFFF Kombinacja wszystkich powyższych flag w postaci jednej wartości.
lpWorkMem [wej]
Pamięć na bufor roboczy. Bufor musi posiadać flagi wykonywalne, a jego rozmiar musi być przynajmniej taki jaki bufora lpDecryptor.
iRandomSeed [wej]
Wartość inicjalizująca wewnętrzny generator pseudolosowy, gwarantująca generowanie za każdym razem innych mutacji kodu deszyfrującego.

Zwracana wartość

Jeśli funkcja poprawnie zaszyfruje dane i wygeneruje kod dekryptora, wtedy zwrócony zostanie rozmiar w bajtach kodu znajdującego się w buforze wskazywanym przez lpDecryptor.

Jeśli wystąpi błąd funkcja zwróci 0.

Przykłady użycia

Silnik posiada wiele opcji pozwalających przystosować rodzaj generowanego kodu do wielu potrzeb, dlatego najlepiej jego wykorzystanie zaprezentować na przykładzie.

Na poniższym przykładzie Silnik Polimorficzny Poly wykorzystany jest do zaszyfrowania wejściowego bloku danych oraz wygenerowania kodu deszyfrującego zawierającego jednocześnie zaszyfrowany blok danych, tak, że np. po zaszyfrowaniu całość zaszyfrowanego bloku wraz z dekryptorem można zapisać do dowolnego pliku i aby odszyfrować dane, wystarczy taki blok danych odczytać z powrotem do bufora pamięci z ustawionymi flagami wykonywalnymi i uruchomić kod dekryptora.

Poniższy przykład wykorzystuje wszystkie dostępne opcje i dodatkowo wykonuje 10000 iteracji w celu sprawdzenia poprawności szyfrowania i deszyfrowania danych.

#define WIN32_LEAN_AND_MEAN

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

#include "poly.h"

// makro pomocnicze
#define RND_RANGE(min,max) (min + (rand() % (int)(max - min + 1)))

// liczba testowych iteracji
const int POLY_TEST_ITERATIONS = 10000;

// bezpieczny rozmiar bufora pamięci
const int POLY_DECRYPTOR_SIZE = 1024 * 2048 * 10;

// przykładowe dane wejściowe do zaszyfrowania
unsigned char cInputBuffer[] = { 0x11, 0x22, 0x33, 0x44 } ;

// bufor wyjściowy
unsigned char cOutputBuffer[sizeof(cInputBuffer)] = { 0 };

int main(int argc, char* argv[])
{
  // zaalokuj bufor pamięci dla zaszyfrowanego bloku (pamięć musi mieć ustawioną flagę pozwalającą na wykonywanie kodu)
  PVOID lpDecryptor = VirtualAlloc(nullptr, POLY_DECRYPTOR_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  // roboczy bufor pamięci (ten sam rozmiar i flagi wykonywalne)
  PVOID lpWorkMem = VirtualAlloc(nullptr, POLY_DECRYPTOR_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  for (int i = 0; i < POLY_TEST_ITERATIONS; i++)
  {
    //
    // przygotuj parametry silnika Poly
    //

    // ustaw losowe wartości parametrom (pamiętaj, że im więcej np. instrukcji szyfrujących, tym
    // większe muszą być rozmiary buforów pamięci lpDecryptor i lpWorkMem)

    // minimalna liczba instrukcji szyfrujących
    unsigned int iMinEncryptionCommands = RND_RANGE(10, 50);

    // maksymalna liczba instrukcji szyfrujących
    unsigned int iMaxEncryptionCommands = RND_RANGE(iMinEncryptionCommands, iMinEncryptionCommands + 10);

    // liczba instrukcji zaciemniających (garbage / junks) przypadająca na instrukcję w kodzie deszyfrującym
    unsigned int iGarbage = RND_RANGE(10, 50);

    // wymuś rozmiar kodu deszyfrującego - jeśli ten parametr jest ustawiony, silnik Poly
    // będzie tak długo generował kod deszyfrujący aż osiągnie ten rozmiar, jeśli wygenerowany
    // kod będzie zbyt mały lub zbyt duży to w locie zmodyfikuje parametry iMinEncryptionCommands,
    // iMaxEncryptionCommands oraz iGarbage i będzie do skutku próbował wygenerować kod, który
    // spełni to kryterium (proszę upewnij się, że bufory lpDecryptor i lpWorkMem są większe niż
    // ta wartość)
    unsigned int iForceDecryptorSize = 0;

    // jeśli ten parametr jest ustawiony, polimorficzny kod deszyfrujący użyje go do obliczenia
    // adresu zaszyfrowanych danych w pamięci relatywnie do bieżącego położenia kodu
    // deszyfrującego w pamięci (wartość ta może mieć negatywną wartość)
    unsigned int iRelativeDataOutputOffset = 0;

    // flagi
    unsigned int iOptions = 0;

    // POLY_SAVE_REGS - zachowaj stan wszystkich rejestrów i przywróć je po zakończeniu deszyfrowania
    iOptions |= POLY_SAVE_REGS;

    // POLY_SAVE_FLAGS - zachowaj stan wszystkich flag procesora i przywróć je po zakończeniu deszyfrowania
    iOptions |= POLY_SAVE_FLAGS;

    // POLY_ATTACH_DATA - dołącz zaszyfrowane dane do kodu dekryptora, kod deszyfrujący nadpisze
    // własny bufor pomięci odszyfrowanymi danymi, co oznacza, że wskaźnik kodu deszyfrującego
    // będzie jednocześnie wskazywał na odszyfrowane dane
    //
    // jeśli ta flaga nie jest ustawiona, zaszyfrowane dane będą musiały znajdować się pod
    // ustalonym i statycznym adresem wirtualnym określonym przez parametr lpVA lub
    // relatywnym adresem określonym przez parametr iRelativeDataOutputOffset
    iOptions |= POLY_ATTACH_DATA;

    // POLY_RETURN - umieść instrukcję powrotu (RET) na końcu funkcji deszyfrującej,
    // tak, że dekryptor wróci do kodu, który go wywołał, polimorficzny dekryptor może
    // być umieszczony również bezpośrednio pomiędzy innym kodem lub dowolny kod może być
    // dodany po nim, dlatego nie zawsze musi wracać do kodu, który go wywołał
    iOptions |= POLY_RETURN;

    // POLY_SET_REGS - zwróć określone wartości w rejestrach procesora po deszyfrowaniu
    // (muszą być one zdefiniowane w strukturze POLY_REGS)
    iOptions |= POLY_SET_REGS;

    // flaga oznacza, że rejestr EAX po deszyfrowaniu ma być ustawiony
    iOptions |= POLY_SET_EAX;

    // i przykładowo zwróćmy inną wartość w rejestrze EDX po deszyfrowaniu
    iOptions |= POLY_SET_EDX;

    // wartości wyjściowych rejestrów (wypełnij tylko te, które mają być ustawione)
    POLY_REGS prPolyRegs = { 0 };

    // ustaw wartość rejestru EAX jaka ma być ustawione po deszyfrowaniu, w tym
    // przykładzie ustawmy tą wartość na rozmiar odszyfrowanego bufora danych
    // (można tu ustawić wszystko)
    prPolyRegs.regEax = sizeof(cInputBuffer);

    // przykładowa wartość jaka ma być zwrócona w rejestrze EDX po deszyfrowaniu
    // (można to podejrzeć za pomocą debuggera)
    prPolyRegs.regEdx = 0xDEADBEEF;

    // zaszyfruj dane i wygeneruj polimorficzny kod deszyfrujący
    unsigned int dwOutputSize = _poly(

      lpDecryptor,                   // bufor wyjściowy na polimorficzny kod / dane
      cOutputBuffer,                 // bufor na zaszyfrowane dane (może być taki sam jak lpInput) (opcjonalnie)
      cInputBuffer,                  // bufor z danymi wejściowymi do zaszyfrowania
      sizeof(cInputBuffer),          // rozmiar danych do zaszyfrowania
      (unsigned int)&cOutputBuffer,  // wirtualny adres gdzie znajdą się dane do odszyfrowania (opcjonalnie)
      iMaxEncryptionCommands,        // max. liczba instrukcji szyfrujących
      iMinEncryptionCommands,        // min. liczba instrukcji szyfrujących
      iGarbage,                      // liczba instrukcji zaśmiecających na instrukcję dekryptora
      iForceDecryptorSize,           // wymuś rozmiar dekryptora (opcjonalnie)
      iRelativeDataOutputOffset,     // relatywny adres zaszyfrowanych danych (opcjonalnie)
      &prPolyRegs,                   // rejestry wyjściowe (opcjonalnie)
      iOptions,                      // dodatkowe opcje
      lpWorkMem,                     // pamięć robocza
      GetTickCount()                 // inicjalizator dla wewnętrznego generatora losowego

    );

    // sprawdź rozmiar wyjściowych danych (rozmiar dekryptora w bajtach)
    if (dwOutputSize != 0)
    {
      PolyDecryptorFunction DecryptorFunction = reinterpret_cast<PolyDecryptorFunction>(lpDecryptor);

      // wywołaj kod dekryptora i odszyfruj dane
      unsigned int dwResult = DecryptorFunction();

      // inny sposób na wywołanie kodu dekryptora (w assemblerze)
      /*
      __asm
      {
        int  3

        mov  eax, lpDecryptor;
        call  eax

        int 3
      }
      */

      // zweryfikuj odszyfrowane dane (jeśli parametr POLY_ATTACH_DATA być ustawiony, wskaźnik
      // lpDecryptor wskazuje jednocześnie na dekryptor a po jego uruchomieniu na odszyfrowane dane)
      if (memcmp(reinterpret_cast<PVOID>(lpDecryptor), cInputBuffer, sizeof(cInputBuffer) != 0))
      {
        printf("Odszyfrowane dane są inne niż powinny być!\n");
        _getch();

        return 2;
      }
    }
    else
    {
      printf("Nie udało się zaszyfrować danych!\n");
      _getch();

      return 1;
    }
  }

  printf("Test Silnika Polimorficznego Poly Polymorphic udał się (%lu iteracji testowych)\n", POLY_TEST_ITERATIONS);

  // zwolnij pamięć
  VirtualFree(lpDecryptor, 0, MEM_RELEASE);
  VirtualFree(lpWorkMem, 0, MEM_RELEASE);

  _getch();

  return 0;
}

Wymagania

Nagłówek poly.h
Biblioteka poly.lib
DLL poly.dll
program poly_test;

{$APPTYPE CONSOLE}

uses StrUtils, Classes, SysUtils, Math, Windows;

// load library from the DLL file (by default .LIB file is used)
{$DEFINE USE_POLY_DLL}

// include additional Poly constants
{$I poly.inc}

const
  // number of test iterations
  POLY_TEST_ITERATIONS = 10000;

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

  // sample input data to encrypt
  cInputBuffer : Array [1..4] of Byte = ( $11, $22, $33, $44 );

var
  // output buffer
  cOutputBuffer : Array [1..4] of Byte;

  {$IFDEF USE_POLY_DLL}

  // library handle
  hDll : THandle;

  // function pointer (if loaded dynamically)
  fnPoly : TPolyProc;

  {$ENDIF}

  i : Integer;

  iMinEncryptionCommands, iMaxEncryptionCommands : LongWord;
  iGarbage : LongWord;
  iForceDecryptorSize : LongWord;
  iRelativeDataOutputOffset : Integer;
  iOptions : LongWord;
  dwResult : LongWord;
  dwOutputSize : LongWord;

  lpDecryptor : Pointer;
  lpWorkMem : Pointer;

  // output registers values (fill out only those marked to be returned)
  prPolyRegs : TPOLY_REGS;

  DecryptorFunction : TPolyDecryptorFunction;

//
// entrypoint
//
begin
  // initialize random number generator
  Randomize;

  // clean output array
  FillChar(cOutputBuffer, SizeOf(cOutputBuffer), 0);

  // load Poly engine from the DLL library
  {$IFDEF USE_POLY_DLL}

  hDll := LoadLibrary('poly.dll');

  if hDll = 0 Then
  begin
    WriteLn('Cannot load poly.dll library!');
    ReadLn;

    ExitCode := 1;
    Halt;
  end;

  @fnPoly := GetProcAddress(hDll, '_poly');

  if not Assigned(@fnPoly) Then
  begin
    WriteLn('Cannot find "_poly" function in poly.dll library!');
    ReadLn;

    ExitCode := 1;
    Halt;
  end;

  {$ENDIF}

  // allocate memory buffer for the encrypted block (it has to be executable)
  lpDecryptor := VirtualAlloc(nil, POLY_DECRYPTOR_SIZE, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  // work memory buffer (same size, also it has to be executable)
  lpWorkMem := VirtualAlloc(nil, POLY_DECRYPTOR_SIZE, MEM_RESERVE or MEM_COMMIT, PAGE_EXECUTE_READWRITE);

  for i := 0 to POLY_TEST_ITERATIONS do
  begin
    //
    // 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
    iMinEncryptionCommands := RandomRange(10, 50);

    // maximal number of encryption commands
    iMaxEncryptionCommands := RandomRange(iMinEncryptionCommands, iMinEncryptionCommands + 10);

    // number of junks per decryptor single command
    iGarbage := RandomRange(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)
    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)
    iRelativeDataOutputOffset := 0;

    // flags
    iOptions := 0;

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

    // POLY_SAVE_FLAGS - save all CPU flags before the call and restore them after the call
    iOptions := iOptions or 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 on static VA address
    // declared in lpVA parameter or declared via relative address in iRelativeDataOutputOffset
    // parameter
    iOptions := iOptions or 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 := iOptions or POLY_RETURN;

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

    // return value in EAX register
    iOptions := iOptions or POLY_SET_EAX;

    // and lets say it should return some other value in EDX register after the call
    iOptions := iOptions or POLY_SET_EDX;

    // output registers values (fill out only those marked to be returned)

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

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

    // encrypt data & generate polymorphic decryptor
    {$IFDEF USE_POLY_DLL}
    dwOutputSize := fnPoly(
      lpDecryptor,                 // output buffer for polymorphic code
      @cOutputBuffer,              // output data buffer (can be the same as lpInput)
      @cInputBuffer,               // input data buffer
      Length(cInputBuffer),        // input data size
      @cOutputBuffer,              // virtual address of a data to be decrypted
      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
      Random(65536)                // random seed for the internal randomizer
    );
    {$ELSE}
    dwOutputSize := _poly(
      lpDecryptor,                 // output buffer for polymorphic code
      @cOutputBuffer,              // output data buffer (can be the same as lpInput)
      @cInputBuffer,               // input data buffer
      Length(cInputBuffer),        // input data size
      @cOutputBuffer,              // virtual address of a data to be decrypted
      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
      Random(65536)                // random seed for the internal randomizer
    );
    {$ENDIF}

    // validate output size (decryptor size in bytes)
    if dwOutputSize <> 0 then
    begin

      DecryptorFunction := lpDecryptor;

      // decrypt data
      dwResult := DecryptorFunction;

      // another way to invoke decryptor function (inline assembly)
      {
      asm
        int  3
        mov  eax, lpDecryptor;
        call  eax
        int 3
      end;
      }

      // validate decrypted data (if POLY_ATTACH_DATA flag was set - lpDecryptor
      // points to the decrypted data)
      if CompareMem(lpDecryptor, @cInputBuffer, Length(cInputBuffer)) <> True then
      begin
        WriteLn('Polymorphic engine failed (decrypted data is invalid)!');
        ReadLn;

        ExitCode := 2;
        Halt;
      end;
    end
    else
    begin
      WriteLn('Polymorphic engine failed!\n');
      ReadLn;

      ExitCode := 1;
      Halt;
    end;
  end;

  WriteLn(format('Polymorphic engine test success (%d iterations)', [ POLY_TEST_ITERATIONS ]));

  // zwolnij pamięć
  VirtualFree(lpDecryptor, 0, MEM_RELEASE);
  VirtualFree(lpWorkMem, 0, MEM_RELEASE);

  ReadLn;
end.

Wymagania

Nagłówek poly.inc
Biblioteka poly.lib
DLL poly.dll

Szyfrowanie pliku

Przykład szyfrowania dowolnego binarnego pliku i generowanie wyjściowego pliku, zawierającego polimorficzny dekryptor na początku pliku wraz z dołączoną zaszyfrowaną częścia pliku wejściowego.

// exclude rarely-used stuff from Windows headers
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <vector>

#include "poly.h"

// load library from the DLL file (by default .LIB file is used)
#define USE_POLY_DLL

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

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

// input file to be encrypted
#define INPUT_FILE "file.bin"

// output file with the attached x86 polymorphic decryptor and the encrypted data
#define OUTPUT_FILE "file_encrypted.bin"

int main(int argc, char* argv[])
{
    // open file to encrypt its content
    std::ifstream inputFile(INPUT_FILE, std::ios::binary);

    if (!inputFile.is_open())
    {
        printf("Cannot open %s input file!\n", INPUT_FILE);
        _getch();
        return 1;
    }

    // get file size
    inputFile.seekg(0, std::ios::end);
    std::streamsize fileSize = inputFile.tellg();
    inputFile.seekg(0, std::ios::beg);

    // allocate memory for cInputBuffer based on the file size
    std::vector<unsigned char> cInputBuffer(fileSize);

    // read the content of the file into cInputBuffer
    inputFile.read(reinterpret_cast<char*>(cInputBuffer.data()), fileSize);
    inputFile.close();

    // load Poly engine from the DLL library
    #ifdef USE_POLY_DLL

    HINSTANCE hDll = LoadLibrary("poly.dll");

    if (hDll == nullptr)
    {
        printf("Cannot load poly.dll library!\n");
        _getch();

        return 1;
    }

    POLY_PROC _poly_proc = (POLY_PROC)GetProcAddress(hDll, "_poly");

    if (hDll == nullptr)
    {
        printf("Cannot find '_poly' function in poly.dll library!\n");
        _getch();

        return 1;
    }

    #endif

    // allocate memory buffer for the encrypted block (it has to be executable)
    PVOID lpDecryptor = VirtualAlloc(nullptr, POLY_DECRYPTOR_SIZE + fileSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

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

    //
    // 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 = 0; 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 on 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 lets 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 lets set it to the size of the decrypted
    // buffer (you can set 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
    #ifdef USE_POLY_DLL
    unsigned int dwOutputSize = _poly_proc(
    #else
    unsigned int dwOutputSize = _poly(
    #endif
        lpDecryptor,              // output buffer for polymorphic code
        cInputBuffer.data(),      // output data buffer (can be the same as lpInput)
        cInputBuffer.data(),      // input data buffer
        fileSize,                 // input data size
        0,                        // virtual address of a 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)
    {
        std::ofstream outputFile(OUTPUT_FILE, std::ios::binary | std::ios::trunc);
        if (!outputFile.is_open())
        {
            printf("Error saving polymorphic loader with encrypted data to a file %s\n", OUTPUT_FILE);
            _getch();
            return 1;
        }

        outputFile.write((const char *)lpDecryptor, dwOutputSize);
        outputFile.close();
    }
    else
    {
        printf("Polymorphic engine failed!\n");
        _getch();

        return 1;
    }

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

    printf("File %s successfully encrypted to %s\n", INPUT_FILE, OUTPUT_FILE);

    _getch();

    return 0;
}

Wymagania

Nagłówek poly.h
Biblioteka poly.lib
DLL poly.dll

Masz pytania?

Jeśli masz jakieś pytania dotyczące Silnika Polimorficznego Poly, masz jakieś uwagi, coś jest niejasne, napisz do mnie, chętnie odpowiem na każde Twoje pytanie.