hackedteam/core-packer

View on GitHub
core-packer/main64.cpp

Summary

Maintainability
Test Coverage
#include <Windows.h>
#include <iostream>
#include "peasm/peasm.h"
#include "peasm/pesection.h"

#include "library.h"
#include "macro.h"
#include "rva.h"
#include "rc4.h"
#include "symbols.h"
#include "reloc.h"
#include "patchutils.h"

#ifdef _BUILD64

#include "dll64.h"

extern BOOL WINAPI DllEntryPoint(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
extern DWORD NextPointerToRawData64(PIMAGE_NT_HEADERS64 pHeader);
extern DWORD NextVirtualAddress(PIMAGE_NT_HEADERS pHeader);
extern DWORD NextVirtualAddress64(PIMAGE_NT_HEADERS64 pHeader);

#define DIFF(b, a) (DWORD)((ULONG64) b - (ULONG64) a)

#define MAGIC_EXPORT_SYMBOL 0x0BABECAFEBAD00000

DWORD OffsetMagic(LPVOID lpBlock, DWORD dwSignature, ULONG64 magic)
{
    LPVOID x = FindBlockMem((LPBYTE) lpBlock, dwSignature, &magic, sizeof(ULONG64));

    if (x != NULL)
    {    // block found
        return DIFF(x, lpBlock);        
    }

    return -1;
}

void Patch_EXPORT_SYMBOL(LPVOID lpBaseBlock, LPBYTE lpInitialMem, DWORD dwSize, LPVOID lpSignature, DWORD dwSignatureSize, DWORD newOffset, DWORD oldOffset)
{
    LPVOID lpInitialByte = FindBlockMem((LPBYTE) lpInitialMem, dwSize, lpSignature, dwSignatureSize);

    if (lpInitialByte != NULL)
    {
        DWORD MagicJumpOffset = OffsetMagic(lpInitialByte, dwSignatureSize, MAGIC_EXPORT_SYMBOL) - 1;
        LPBYTE c = CALC_OFFSET(LPBYTE, lpInitialByte, MagicJumpOffset);
        DWORD dwNewValue = diff_rva32(NULL, NULL, oldOffset, newOffset+MagicJumpOffset+5);
        Patch_JMP(c, dwNewValue);
    }
}

#define SECTION_RANDOM_NAME    15

static char *szSectionNames[SECTION_RANDOM_NAME] = 
{
    ".textbss",
    ".pages",
    ".visical",
    ".inferno",
    ".calc",
    ".notepad",
    ".word",
    ".viper0",
    ".venom",
    ".text0",
    ".uspack0",
    ".hermit",
    ".locals",
    ".stack1",
    ".GLOBAL"
};

/**
 *    Return size required for relocation
 **/
size_t SizeOfRelocSection(LPVOID hProcessModule, PIMAGE_NT_HEADERS64 pSelf, PIMAGE_SECTION_HEADER pSection)
{
    size_t size = 0;

    relocation_block_t *reloc = CALC_OFFSET(relocation_block_t *, hProcessModule, pSelf->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

    ULONGLONG dwImageBase = pSelf->OptionalHeader.ImageBase;

    if (dwImageBase != (DWORD) hProcessModule)
        dwImageBase = (DWORD) hProcessModule;


    while(reloc != NULL)
    {
        if (reloc->PageRVA >= pSection->VirtualAddress && reloc->PageRVA < (pSection->VirtualAddress + pSection->Misc.VirtualSize))
        {    // good! add this page!
            size+= reloc->BlockSize;
        }

        reloc = CALC_OFFSET(relocation_block_t *, reloc, reloc->BlockSize);
        if (reloc->BlockSize == 0) reloc = NULL;
    }
    return size;
}

char *szSectionName[] = { ".hermit\0", ".pedll32\0", ".pedll64\0", ".peexe32\0", ".peexe64\0" };

PIMAGE_SECTION_HEADER lookup_core_section(PIMAGE_DOS_HEADER pImageDosHeader, PIMAGE_NT_HEADERS64 pImageNtHeaders64, BOOL dllTARGET)
{
    short NumberOfSections = pImageNtHeaders64->FileHeader.NumberOfSections;

    char *szHermitName = szSectionName[2];

    if (dllTARGET == FALSE)
        szHermitName = szSectionName[4];

    PIMAGE_SECTION_HEADER pResult = NULL;

    for(PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pImageNtHeaders64); NumberOfSections > 0; NumberOfSections--, pSection++)
    {
        if (memcmp(szHermitName, pSection->Name, 8) == 0)
        {
            std::cout << szHermitName << "/64bit section found in code" << std::endl;
            
            std::cout << "\tSize of raw data: " << std::hex << pSection->SizeOfRawData << std::endl;
            std::cout << "\t    Virtual size: " << std::hex << pSection->Misc.VirtualSize << std::endl;
            std::cout << "\t             RVA: " << std::hex << pSection->VirtualAddress << std::endl;
            std::cout << "\t Virtual Address: " << std::hex << rva2addr(pImageDosHeader, pImageNtHeaders64, CALC_OFFSET(LPVOID, pImageDosHeader, pSection->VirtualAddress)) << std::endl;

            pResult = pSection;
            break;
        }
    }

    return pResult;
}

BOOL check_blacklist(PWIN32_FIND_DATA lpFindData)
{
    char szToLower[MAX_PATH];
    if (strcmpi(lpFindData->cFileName, "compobj.dll") == 0)
        return TRUE;

    return FALSE;
}


BOOL lookup_rand_file(char *szOutFile, int maxsize)
{
    memset(szOutFile, 0, maxsize);
    
    char szWindirPath[MAX_PATH];

    DWORD dwIgnore = GetEnvironmentVariableA("windir", szWindirPath, MAX_PATH);

    if (dwIgnore == 0)
    {    // try default c:\windows
        strcpy_s(szWindirPath, "C:\\windows\\");
    }
    else
    {
        int i = (int) strlen(szWindirPath);

        if (szWindirPath[i-1] != '\\')
            strcat_s(szWindirPath, "\\");
    }

    typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
    LPFN_ISWOW64PROCESS fnIsWow64Process;

    fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress(GetModuleHandle("kernel32"),"IsWow64Process");

    strcat_s(szWindirPath, "system32\\");

    char szFindPath[MAX_PATH];
    sprintf_s(szFindPath, "%s*.dll", szWindirPath);

    WIN32_FIND_DATA findfiledata;
    WIN32_FIND_DATA _previous_findfiledata;
    HANDLE hLook = FindFirstFileA(szFindPath, &findfiledata);

    int l = rand() % 256;

    if (hLook == INVALID_HANDLE_VALUE)
        return FALSE;

    do
    {    // perform a backup!
        if (check_blacklist(&findfiledata) == TRUE)    // file in blacklist
            continue;

        memcpy(&_previous_findfiledata, &findfiledata, sizeof(WIN32_FIND_DATA));
        if (l  == 0)
            break;

        l--;
    } while(FindNextFileA(hLook, &findfiledata));


    FindClose(hLook);

    strcat_s(szWindirPath, _previous_findfiledata.cFileName);

    strcpy(szOutFile, szWindirPath);

    return TRUE;
}

CPeAssembly *load_random(char *param)
{
    if (param != NULL)
    {
        CPeAssembly *obj = new CPeAssembly();
        obj->Load(param);
        return obj;
    }
    else
    {
        char randfile[MAX_PATH];

        CPeAssembly *obj = NULL;

        while(obj == NULL)
        {
            lookup_rand_file(randfile, MAX_PATH);

            obj = new CPeAssembly();

            if (obj->Load(randfile) == false)
            {    // failed!
                std::cout << "Error loading " << randfile << std::endl;
                delete obj;
                obj = NULL;
            }

            if (obj->NumberOfSections() == 0)
            {    // failed!
                std::cout << "Error loading " << randfile << std::endl;
                delete obj;
                obj = NULL;
            }
        }

        return obj;
    }

    return NULL;
}

DWORD Transfer_Reloc_Table(LPVOID hProcessModule, PIMAGE_NT_HEADERS64 pSelf, PIMAGE_SECTION_HEADER pSection, LPVOID lpOutput, DWORD dwNewVirtualAddress, CPeAssembly *destination, PIMAGE_NT_HEADERS64 pNewFile)
{
    DWORD dwSize = 0;

    relocation_block_t *reloc = CALC_OFFSET(relocation_block_t *, hProcessModule, pSelf->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

    ULONGLONG dwImageBase = pSelf->OptionalHeader.ImageBase;

    if (dwImageBase != (DWORD) hProcessModule)
        dwImageBase = (DWORD) hProcessModule;

    while(reloc != NULL)
    {
        if (reloc->PageRVA >= pSection->VirtualAddress && reloc->PageRVA < (pSection->VirtualAddress + pSection->Misc.VirtualSize))
        {    // good! add this page!
            memcpy(lpOutput, reloc, reloc->BlockSize);
            
            relocation_block_t *newReloc= CALC_OFFSET(relocation_block_t *, lpOutput, 0);

            newReloc->PageRVA = reloc->PageRVA - pSection->VirtualAddress + dwNewVirtualAddress;
            
            DWORD blocksize = newReloc->BlockSize - 8;
            relocation_entry *entry = CALC_OFFSET(relocation_entry *, newReloc, 8);
            
            while(blocksize > 0)
            {    // fetch instruction and patch!
                short type = ((*entry & 0xf000) >> 12);
                long offset = (*entry & 0x0fff);

                ULONG *ptr = (PULONG) destination->RawPointer(offset + newReloc->PageRVA);
                ULONG value = *ptr;
                ULONG dwNewValue = 0;

                switch(type)
                {
                    case 0x03:
                        value = value - dwImageBase - reloc->PageRVA;
                        value = value + pNewFile->OptionalHeader.ImageBase + newReloc->PageRVA;
                        *ptr = value;
                        break;
                    case 0x0a:
                        //dwNewValue = value - pImageNtHeader->OptionalHeader.ImageBase + (ULONG64) pModule;
                        //*ptr = dwNewValue;
                        break;
                }
                entry++;
                blocksize -= 2;
            }

            lpOutput = CALC_OFFSET(LPVOID, lpOutput, reloc->BlockSize);

            dwSize += reloc->BlockSize;
        }

        reloc = CALC_OFFSET(relocation_block_t *, reloc, reloc->BlockSize);
        if (reloc->BlockSize == 0) reloc = NULL;
    }
    return dwSize;
}


int main64(int argc, char *argv[])
{
    srand(GetTickCount());    // initialize for (rand)

    if (argc == 1)
    {
        std::cout << "packer32 infile outfile" << std::endl;
        std::cout << "packer32 in/outfile" << std::endl;
    }

    HMODULE hModule = GetModuleHandle(NULL);

    PIMAGE_DOS_HEADER pImageDosHeader = (PIMAGE_DOS_HEADER) hModule;
    PIMAGE_NT_HEADERS64 pImageNtHeaders64 = CALC_OFFSET(PIMAGE_NT_HEADERS64, pImageDosHeader, pImageDosHeader->e_lfanew);

    if (pImageNtHeaders64->Signature != IMAGE_NT_SIGNATURE)
    {    
        std::cout << "Sorry! I can't check myself!";
        return FALSE;    
    }
    
    char szHermitName[] = ".hermit";

    short NumberOfSections = pImageNtHeaders64->FileHeader.NumberOfSections;

    CPeAssembly *pTarget = new CPeAssembly();

    pTarget->Load(argv[1]);

    CPeAssembly *morph = NULL;

    if (argc > 3)
        morph = load_random(argv[3]);
    else
        morph = load_random(NULL);

    PIMAGE_SECTION_HEADER pUnpackerCode = NULL;
    
    if (pTarget->IsDLL())
    {    // it's a DLL?!?!?
        std::cout << "Input file is DLL!" << std::endl;

        pUnpackerCode = lookup_core_section(pImageDosHeader, pImageNtHeaders64, TRUE);
    }
    else if (pTarget->IsEXE())
    {
        std::cout << "Input file is EXECUTABLE!" << std::endl;
        pUnpackerCode = lookup_core_section(pImageDosHeader, pImageNtHeaders64, FALSE);
    }
    else
    {
        std::cout << "Unsupported input file!" << std::endl;
        return 0;
    }

    if (pUnpackerCode == NULL)
    {    //  break!
        std::cout << "Cannot find <PACKER> in sections" << std::endl;
        return 0;
    }

    size_t required_reloc_space = SizeOfRelocSection(pImageDosHeader, pImageNtHeaders64, pUnpackerCode);
    CPeSection *relocSection = pTarget->LookupSectionByName(".reloc");

    size_t relocsize = relocSection->VirtualSize();
        
    if (relocsize + required_reloc_space > relocSection->SizeOfRawData())
    {    // expande
        relocSection->AddSize(required_reloc_space);
    }


    char passKey[16];

    for(int i =0; i < sizeof(passKey); i++)
        passKey[i] = rand() % 256;

    BYTE rc4sbox[256];
    
    PIMAGE_DATA_DIRECTORY DataDir = pTarget->DataDirectory64();

    PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR) pTarget->RawPointer(DataDir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
    
    PIMAGE_SECTION_HEADER pDestSection = NULL;

    char *szSectionName = szSectionNames[rand() % 15];
    
    int newVirtualSize = RoundUp(pUnpackerCode->Misc.VirtualSize + ((rand() % 16) * 1024), 1024);

    CPeSection *pTargetSection = NULL;

    if (pTarget->IsDLL())
    {
        pTargetSection = pTarget->AddSection(szSectionName, 0x0, newVirtualSize);    // move section in "head"
    }
    else if (pTarget->IsEXE())
    {
        pTargetSection = pTarget->AddSection(szSectionName, 0x1000, newVirtualSize);    // move section in "head"

        CPeSection *pMorphSection = morph->getSection(0);

        memset(pTargetSection->RawData(), 0x90, newVirtualSize);
        memcpy(pTargetSection->RawData(), pMorphSection->RawData(), (pMorphSection->SizeOfRawData() < newVirtualSize) ? pMorphSection->SizeOfRawData() : newVirtualSize);
    }

    DWORD dwOffset = RoundUp(pUnpackerCode->SizeOfRawData, 16);

    ULONG offsetEntryPoint = (ULONG) (DllEntryPoint);

    ULONG rvaEntryPoint = offsetEntryPoint - ((ULONG) pImageDosHeader) - pUnpackerCode->VirtualAddress; // - pImageNtHeaders64->OptionalHeader.SectionAlignment); // 
    
    DWORD AddressOfEntryPoint = pTarget->NtHeader64()->OptionalHeader.AddressOfEntryPoint;
    
    pTargetSection->GetSectionHeader()->Characteristics = 0xE0000020;
    
    PIMAGE_NT_HEADERS64 pTargetNtHeader = pTarget->NtHeader64();

    pTargetNtHeader->OptionalHeader.AddressOfEntryPoint = pTargetSection->VirtualAddress() + rvaEntryPoint; // - pInfectMeNtHeader->OptionalHeader.SectionAlignment;

    LPVOID lpRawSource = rva2addr(pImageDosHeader, pImageNtHeaders64, CALC_OFFSET(LPVOID, pImageDosHeader, pUnpackerCode->VirtualAddress));

    memcpy(pTargetSection->RawData(), lpRawSource, pUnpackerCode->SizeOfRawData);

    ULONG64 *passKeyPtr = (ULONG64*) &passKey;

    
    // Process export table
    std::cout << "IAT Size: " << std::hex << pTargetNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size << std::endl;
    std::cout << "IAT Addr.: " << std::hex << pTargetNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress << std::endl;

    DWORD kernel32LoadLibraryA_Offset = 0;
    DWORD kernel32GetProcAddress_Offset = 0;

    PIMAGE_IMPORT_DESCRIPTOR ImportAddressTable = (PIMAGE_IMPORT_DESCRIPTOR) pTarget->RawPointer(DataDir[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

    while(ImportAddressTable->Characteristics != 0)
    {
        std::cout << "Name " << pTarget->RawPointer(ImportAddressTable->Name) << std::endl;
        
        std::cout << "\tEntries: " << std::endl;

        PULONG64 rvaName = (PULONG64) pTarget->RawPointer(ImportAddressTable->Characteristics);
        PULONG64 iatRVA = (PULONG64) pTarget->RawPointer(ImportAddressTable->FirstThunk);
        PULONG64 iat = (PULONG64) ImportAddressTable->FirstThunk;

        while(*rvaName != 0)
        {
            char *name = (char *) pTarget->RawPointer((*rvaName & 0x7fffffff) + 2);

            std::cout << "\t " << std::hex << CALC_DISP(LPVOID, iatRVA, pTarget) << " " << std::hex << *iatRVA << " " << name << std::endl;

            if (strcmp(name, "LoadLibraryA") == 0) 
                kernel32LoadLibraryA_Offset = (DWORD) iat;
            else if (strcmp(name, "GetProcAddress") == 0)
                kernel32GetProcAddress_Offset = (DWORD) iat;

            rvaName++;
            iatRVA++;
            iat++;
        }

        ImportAddressTable++;
    }

    for(int i = 0; i < pTarget->NumberOfSections(); i++)
    {    // each section must be packed
        if (pTarget->IsDLL())
        {
            init_sbox(rc4sbox);
            init_sbox_key(rc4sbox, (BYTE *) passKey, 16);
        }
        else
        {
            uint32_t *key = (uint32_t *) rc4sbox;
            memcpy(key, passKey, 16);
        }

        CPeSection *pProcessSection = pTarget->getSection(i);
        PIMAGE_SECTION_HEADER pSectionHeader = pProcessSection->GetSectionHeader();

        if ((pSectionHeader->Characteristics & IMAGE_SCN_MEM_SHARED) == IMAGE_SCN_MEM_SHARED)
        {    // skip current section
        }
        else if ((pSectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE) == IMAGE_SCN_MEM_EXECUTE)
        {
            pSectionHeader->Characteristics |= 0x02;
            
            if (pTarget->IsDLL())
                cypher_msg(rc4sbox, (PBYTE) pProcessSection->RawData(), pProcessSection->SizeOfRawData());
            else
            {
                /*uint32_t *key = (uint32_t *) rc4sbox;
                LPDWORD encptr = (LPDWORD) pProcessSection->RawData();

                for(DWORD dwPtr = 0; dwPtr < pProcessSection->SizeOfRawData(); dwPtr += 8, encptr += 2)
                    tea_encrypt((uint32_t *) encptr, key);*/
            }

            //pSectionHeader->Characteristics ^= IMAGE_SCN_MEM_EXECUTE;
            if (strcmp((char *) pSectionHeader->Name, ".text") == 0)
            {    // text section!
                //pSectionHeader->Misc.VirtualSize = pSectionHeader->SizeOfRawData;
            }

        }
        else if (memcmp(pSectionHeader->Name, ".data", 5) == 0)
        {
            pSectionHeader->Characteristics |= 0x02;

            if (pTarget->IsDLL())
                cypher_msg(rc4sbox, (PBYTE) pProcessSection->RawData(), pProcessSection->SizeOfRawData());
            else
            {
                /*uint32_t *key = (uint32_t *) rc4sbox;
                LPDWORD encptr = (LPDWORD) pProcessSection->RawData();

                for(DWORD dwPtr = 0; dwPtr < pProcessSection->SizeOfRawData(); dwPtr += 8, encptr += 2)
                    tea_encrypt((uint32_t *) encptr, key);*/
            }

        }
    }

    if (pTarget->IsDLL())
    {    // DLL stub .. SECTION RWX
        pTargetSection->GetSectionHeader()->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE;
    }
    else
    {    // EXE STUB ... SECTION RX
        pTargetSection->GetSectionHeader()->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE;
    }

    // Laod Config Data <-> REMOVE!!!
    if (DataDir[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress != 0 && pTarget->IsDLL() == FALSE)
    {
        std::cout << "\t**WARNING**\tLOAD_CONFIG Data Directory isn't NULL! Removing! " << std::endl;

        DataDir[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].VirtualAddress = 0;
        DataDir[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG].Size = 0;
    }

    PIMAGE_EXPORT_DIRECTORY ExportDirectory =  
        (PIMAGE_EXPORT_DIRECTORY) pTarget->RawPointer(DataDir[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
        
    LPDWORD AddressOfFunctions = (LPDWORD) pTarget->RawPointer(ExportDirectory->AddressOfFunctions);

    ULONG64 table[10] =
    {
        (ULONG64) _FakeEntryPoint0,
        (ULONG64) _FakeEntryPoint1,
        (ULONG64) _FakeEntryPoint2,
        (ULONG64) _FakeEntryPoint3,
        (ULONG64) _FakeEntryPoint4,
        (ULONG64) _FakeEntryPoint5,
        (ULONG64) _FakeEntryPoint6,
        (ULONG64) _FakeEntryPoint7,
        (ULONG64) _FakeEntryPoint8,
        (ULONG64) _FakeEntryPoint9
    };
            
    int maxoffset = (pTargetSection->VirtualSize() - pUnpackerCode->SizeOfRawData);
    
    int basesize  = 0;

    if (maxoffset == 0)
        basesize = 0;
    else
        basesize = rand() % maxoffset;    // offset

    LPVOID lpRawDestin = CALC_OFFSET(LPVOID, pTargetSection->RawData(), basesize);    // an offset inside acceptable range


    /*******************************************************************************************
     * WARNING!!!
     *    The next memcpy transfer section from our binary into target!
     *    All patch/modification must be done after next line!
     ******************************************************************************************/
    memcpy(lpRawDestin, lpRawSource, pUnpackerCode->SizeOfRawData);

    /**
     *    Decryption routine
     **/
    if (pTarget->IsDLL())
    {    // process code for encryption of "RC4"
    
    }
    else
    {    // process code for encryption of TEA
        
    }

    //////////////////////
    // write new entry point!
    pTarget->NtHeader64()->OptionalHeader.AddressOfEntryPoint = pTargetSection->VirtualAddress() + basesize + rvaEntryPoint; // - pInfectMeNtHeader->OptionalHeader.SectionAlignment;
    ///////////////////////
    
    if (kernel32GetProcAddress_Offset == 0 || kernel32LoadLibraryA_Offset == 0)
    {
        std::cout << "Error! KERNEL32!GetProcAddress/LoadLibraryA not found in IAT" << std::endl;
        return 0;
    }

    Patch_MARKER_QWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &_rc4key0, passKeyPtr[0]);
    Patch_MARKER_QWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &_rc4key1, passKeyPtr[1]);
    Patch_MARKER_DWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &dwRelocSize, DataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
    Patch_MARKER_DWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &lpRelocAddress, DataDir[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
    
    if (pTarget->IsDLL())
    {    // nothing!!!
    }
    else
    {    // save preferred image base
        Patch_MARKER_DWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &_baseAddress, pTarget->NtHeader64()->OptionalHeader.ImageBase);
    }

    // Transfer our .reloc into .reloc of target
    
    DWORD dwNewRelocSize = 0;
    DWORD dwNewRelocOffset = 0;
    
    if (relocSection != NULL)
    {    // there are a relocation
        // space available into section!
        dwOffset = RoundUp(relocSection->VirtualSize(), 0x10);
        dwNewRelocOffset = relocSection->VirtualAddress() + dwOffset;
        LPVOID lpWriteInto = CALC_OFFSET(LPVOID, relocSection->RawData(), dwOffset);
        dwNewRelocSize = Transfer_Reloc_Table(pImageDosHeader, pImageNtHeaders64, pUnpackerCode, lpWriteInto, pTargetSection->VirtualAddress() + basesize, pTarget, (PIMAGE_NT_HEADERS64)  pTarget->NtHeader64());
        relocSection->GetSectionHeader()->Misc.VirtualSize = dwOffset + dwNewRelocSize;
    }
    else
    {    // allocate new section inside ".text" section
        dwOffset = RoundUp(pUnpackerCode->Misc.VirtualSize, 16);
        dwNewRelocSize = Transfer_Reloc_Table(pImageDosHeader, pImageNtHeaders64, pUnpackerCode, CALC_OFFSET(LPVOID, lpRawDestin, dwOffset + basesize ), pTargetSection->VirtualAddress(), pTarget, (PIMAGE_NT_HEADERS64) pTarget->NtHeader64());
        dwNewRelocOffset = pTargetSection->VirtualAddress() + dwOffset;
    }

    pTarget->NtHeader64()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = dwNewRelocSize;
    pTarget->NtHeader64()->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = dwNewRelocOffset;

    /**
     *    EXPORT SYMBOLS
     **/
    if (pTarget->IsDLL())
    {    // DLL - Patch 
        DWORD dwSignatureSize = table[1] - table[0];

        for(int i=0; i < ExportDirectory->NumberOfFunctions; i++)
        {
            ULONG64 exportRVA = table[i];
            ULONG64 exportSymbolEntryPoint = exportRVA - ((ULONG64) pImageDosHeader) - pUnpackerCode->VirtualAddress; // - pImageNtHeaders64->OptionalHeader.SectionAlignment); // 
            exportSymbolEntryPoint = pTargetSection->VirtualAddress() + basesize + exportSymbolEntryPoint; // - pInfectMeNtHeader->OptionalHeader.SectionAlignment;
            DWORD dwOldValue = AddressOfFunctions[i];
            AddressOfFunctions[i] = exportSymbolEntryPoint;
            Patch_EXPORT_SYMBOL(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, (LPVOID) table[i], dwSignatureSize, exportSymbolEntryPoint, dwOldValue);

        }
    }

    
    // lpRawDestination PATCH!

    // Patch Entry point
    
    if (kernel32GetProcAddress_Offset == 0 || kernel32LoadLibraryA_Offset == 0)
    {
        std::cout << "Error! KERNEL32!GetProcAddress/LoadLibraryA not found in IAT" << std::endl;
        return 0;
    }

    Patch_MARKER((LPVOID)(pTargetSection->VirtualAddress()+basesize), (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &_EntryPoint, 9, AddressOfEntryPoint);

    Patch_MARKER(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &_LoadLibraryA, 0x0F, kernel32LoadLibraryA_Offset);
    Patch_MARKER(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &_GetProcAddress, 0x0F, kernel32GetProcAddress_Offset);

    Patch_MARKER_QWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &_rc4key0, passKeyPtr[0]);
    Patch_MARKER_QWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &_rc4key1, passKeyPtr[1]);

    Patch_MARKER_DWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &dwRelocSize, pTargetNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
    Patch_MARKER_DWORD(pTarget, (LPBYTE) lpRawDestin, pUnpackerCode->SizeOfRawData, &lpRelocAddress, pTargetNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

    relocation_block_t *ImageRelocation = (relocation_block_t *)pTarget->RawPointer(pTargetNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);

    while(ImageRelocation != NULL)
    {
        std::cout << "Base Address: " << std::hex << ImageRelocation->PageRVA << " " << std::hex << ImageRelocation->BlockSize << std::endl;
        
        int BlockSize = ImageRelocation->BlockSize - 8;
        relocation_entry *entries = CALC_OFFSET(relocation_entry *, ImageRelocation, 8);

        while(BlockSize > 0)
        {
            std::cout << "\t" << std::hex << "Type " << ((*entries & 0xf000) >> 12) << " " << std::hex << (*entries & 0x0fff) << std::endl;
            entries++;
            BlockSize -= 2;
        }

        ImageRelocation = CALC_OFFSET(relocation_block_t *, ImageRelocation, ImageRelocation->BlockSize);
        if (ImageRelocation->BlockSize == 0) ImageRelocation = NULL;
    }


    if (argc > 2)
    {
        char tmpName[MAX_PATH];

        strcpy(tmpName, argv[2]);
        int lentmp = strlen(tmpName);

        if (tmpName[lentmp-1] == '\\')
        {    // random name!
            SYSTEMTIME time;
            GetSystemTime(&time);
            sprintf_s(tmpName, "%s%04i%02i%02i_%02i%02i.exe", argv[2], time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute);
        }
        pTarget->Save(tmpName);
    }
    else
        pTarget->Save(argv[1]);

    delete pTarget;    // destroy and release memory!
    delete morph;        // destroy and release memory!

    return 0;
}

#endif