Opis funkcji, parametrów i flag Silnika Polimorficznego Poly wykorzystywanego do polimorficznego szyfrowania danych.
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
POLY_ATTACH_DATA
.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
.lpDecryptor
i lpWorkMem
.cMaxInstr
, cMinInstr
i iGarbage
, tak aby uzyskać
wymagany rozmiar.VA
lub RVA
) i szyfrujemy dane z innego
adresu wirtualnego znajdującego się w tym samym obrazie pliku wykonywalnego w pamięci.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;
};
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 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 |
POLY_FLAGS_ALL | 0xFFFFFFFF |
Kombinacja wszystkich powyższych flag w postaci jednej wartości. |
lpDecryptor
.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
.
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;
}
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.
Nagłówek | poly.inc |
Biblioteka | poly.lib |
DLL | poly.dll |
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;
}
Nagłówek | poly.h |
Biblioteka | poly.lib |
DLL | poly.dll |
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.