hackedteam/core-packer

View on GitHub
core-packer/peasm/peasm.cpp

Summary

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

//#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES  1

#define PAGE_SIZE    4096

typedef struct base_relocation_block
{
    uint32_t PageRVA;
    uint32_t BlockSize;
} base_relocation_block_t;

typedef struct base_relocation_entry
{
    uint16_t offset : 12;
    uint16_t type : 4;
} base_relocation_entry_t;

#define relocation_block_t base_relocation_block_t
#define relocation_entry_t base_relocation_entry_t

typedef short relocation_entry;

extern struct _file_support pe_i386;
extern struct _file_support pe_amd64;

#define SUPPORTED_IMAGE_FORMAT    2

struct _file_support* _file_[SUPPORTED_IMAGE_FORMAT] =
{
    &pe_i386,
    &pe_amd64
};

static struct _file_support *get_by_image_format(DWORD ImageFormat)
{
    for(DWORD i=0; i < SUPPORTED_IMAGE_FORMAT; i++)
        if (_file_[i]->IMAGE == ImageFormat)
            return _file_[i];

    return NULL;
}
    
/**
 *    \!operator <
 *    compare two "SECTION_ITEM" objects only using "virtual address"
 *    required for stl/list/sort method
 **/
bool operator < (SECTION_ITEM &first, SECTION_ITEM &second)
{
    if (first.va < second.va)
        return true;

    return false;
}

/**
 *    \!operator ==
 *    compare two "SECTION ITEM" objects using only "virtual address"
 *    required for stl/list/remove, remove_if ...
 **/
bool operator == (const SECTION_ITEM &first, const SECTION_ITEM &second)
{
    if (first.va == second.va)
        return true;

    return false;
}

//////////////////////////////////////////////////////////////////////////
// virtual address in range?
static bool va_in_range(virtualaddress_t va, virtualaddress_t base, virtualaddress_t size)
{
    if (base <= va && va <= (base + size))
        return true;

    return false;
}

/**
*    !CPeAssembly
*    <<ctor>>
**/
CPeAssembly::CPeAssembly()
{
}

/**
*    !CPeAssembly
*    <<dtor>>
**/
CPeAssembly::~CPeAssembly()
{
    std::list<SECTION_ITEM>::iterator it = _sections.begin();

    while(it != _sections.end())
    {
        delete it->descriptor;

        it->descriptor = NULL;
        it->va = NULL;

        it++;
    }

    _sections.clear();
}

CPeAssembly::CPeAssembly(void *)
{
}

/****************************************************************************
*    Load/Save libraries!
***************************************************************************/
bool CPeAssembly::Load(char *pFileName)
{
    HANDLE hFile = CreateFileA(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);

    this->_lpBase = NULL;
    this->_lpDosHeader = NULL;
    this->_lpNtHeader = NULL;

    if (hFile == INVALID_HANDLE_VALUE)
        return NULL;

    DWORD numberOfBytesread;
    IMAGE_DOS_HEADER dos_header;
    ZeroMemory(&dos_header, sizeof(dos_header));

    if (ReadFile(hFile, &dos_header, sizeof(dos_header), &numberOfBytesread, NULL) == FALSE)
    {
        CloseHandle(hFile);
        return NULL;
    }

    if (dos_header.e_magic != IMAGE_DOS_SIGNATURE || dos_header.e_lfanew == 0)
    {
        CloseHandle(hFile);
        return NULL;
    }

    IMAGE_NT_HEADERS32 pe_header;
    ZeroMemory(&pe_header, sizeof(pe_header));

    SetFilePointer(hFile, dos_header.e_lfanew, NULL, FILE_BEGIN);

    if (ReadFile(hFile, &pe_header, sizeof(pe_header), &numberOfBytesread, NULL) == FALSE)
    {
        CloseHandle(hFile);
        return NULL;
    }

    if (pe_header.Signature != IMAGE_NT_SIGNATURE)
    {
        CloseHandle(hFile);
        return NULL;
    }

    if (pe_header.FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
    {
        DWORD sizeHeader = dos_header.e_lfanew + 
            pe_header.FileHeader.SizeOfOptionalHeader + 
            sizeof(pe_header.FileHeader) + 4;

        IMAGE_SECTION_HEADER first_section;

        ZeroMemory(&first_section, sizeof(IMAGE_SECTION_HEADER));

        SetFilePointer(hFile, sizeHeader, NULL, FILE_BEGIN);

        if (ReadFile(hFile, &first_section, sizeof(IMAGE_SECTION_HEADER), &numberOfBytesread, NULL) == NULL)
        {
            //CloseHandle(hFile);
            //VirtualFree(_lpBase, pe_header.OptionalHeader.SizeOfImage, MEM_FREE);
            return NULL;
        }

        SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
        
        if (first_section.PointerToRawData == 0)
            DebugBreak();

        this->_lpBase = VirtualAlloc(NULL, roundup(first_section.PointerToRawData, 0x1000), MEM_COMMIT, PAGE_READWRITE);

        if (this->_lpBase == NULL)
            return FALSE;    // cannot load this module for insufficient memory!

        BOOL bResult = ReadFile(hFile, _lpBase, (first_section.PointerToRawData != 0) ? first_section.PointerToRawData : 0x1000, &numberOfBytesread, NULL);

                if (bResult == FALSE)
        {
            DebugBreak();
        }

        // set pointer "DOS_HEADER" and "NT_HEADER" to our "alias"
        _lpDosHeader = (PIMAGE_DOS_HEADER) _lpBase;
        _lpNtHeader = CALC_OFFSET(PIMAGE_NT_HEADERS32, _lpDosHeader, _lpDosHeader->e_lfanew);
        
        struct _file_support *stream = get_by_image_format(_lpNtHeader->FileHeader.Machine);

        stream->read(this, hFile, &_sections);    // read from file

    }
    else if (pe_header.FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
    {
        IMAGE_NT_HEADERS64 pe64_header;
        ZeroMemory(&pe64_header, sizeof(pe_header));

        SetFilePointer(hFile, dos_header.e_lfanew, NULL, FILE_BEGIN);

        if (ReadFile(hFile, &pe64_header, sizeof(pe64_header), &numberOfBytesread, NULL) == FALSE)
        {
            CloseHandle(hFile);
            return NULL;
        }

        DWORD sizeHeader = dos_header.e_lfanew + 
            pe_header.FileHeader.SizeOfOptionalHeader + 
            sizeof(pe_header.FileHeader) + 4;

        IMAGE_SECTION_HEADER section;

        ZeroMemory(&section, sizeof(IMAGE_SECTION_HEADER));

        SetFilePointer(hFile, sizeHeader, NULL, FILE_BEGIN);

        if (ReadFile(hFile, &section, sizeof(section), &numberOfBytesread, NULL) == NULL)
        {
            CloseHandle(hFile);
            VirtualFree(_lpBase, pe_header.OptionalHeader.SizeOfImage, MEM_FREE);
            return NULL;
        }

        _lpBase = VirtualAlloc(NULL, roundup(section.PointerToRawData, 0x1000), MEM_COMMIT, PAGE_READWRITE);

        if (_lpBase == NULL)
        {
            CloseHandle(hFile);
            return NULL;
        }
        SetFilePointer(hFile, 0, NULL, SEEK_SET);

        BOOL bResult = ReadFile(hFile, _lpBase, (section.PointerToRawData != 0) ? section.PointerToRawData : 0x1000, &numberOfBytesread, NULL);

        // set pointer "DOS_HEADER" and "NT_HEADER" to our "alias"
        _lpDosHeader = (PIMAGE_DOS_HEADER) _lpBase;
        _lpNtHeader = CALC_OFFSET(PIMAGE_NT_HEADERS32, _lpDosHeader, _lpDosHeader->e_lfanew);
        _lpNtHeader64 = CALC_OFFSET(PIMAGE_NT_HEADERS64, _lpDosHeader, _lpDosHeader->e_lfanew);

        struct _file_support *stream = get_by_image_format(_lpNtHeader64->FileHeader.Machine);

        stream->read(this, hFile, &_sections);    // read from file

    }
    else
    {    // unsupported file machine
    }

    // update alias!


    CloseHandle(hFile);
    return true;
}

bool CPeAssembly::Save(char *pFileName)
{    // refactoring to standard C**
    HANDLE hOutFile = CreateFileA(pFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);

    if(hOutFile == INVALID_HANDLE_VALUE)
        return false;


    if (_lpDosHeader->e_magic != IMAGE_DOS_SIGNATURE || _lpDosHeader->e_lfanew == 0)
    {
        CloseHandle(hOutFile);
        return false;
    }

    // PE header

    if (_lpNtHeader->Signature != IMAGE_NT_SIGNATURE)
    {
        CloseHandle(hOutFile);
        return false;
    }

    DWORD dwSizeHeader = _lpNtHeader->FileHeader.SizeOfOptionalHeader  + sizeof(_lpNtHeader->FileHeader) + 4;// +
    //sizeof(IMAGE_SECTION_HEADER)*pe_header->FileHeader.NumberOfSections;

    PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)((DWORD)(_lpBase) + dwSizeHeader + _lpDosHeader->e_lfanew);

    // sort items via iterator using "va"
    _sections.sort();

    std::list<SECTION_ITEM>::iterator it = _sections.begin();

    // -- UPDATE HEADER BEFORE WRITING!!!
    update_section_header();    // transfer new code!
    /* TODO: adjust SizeOfImage and others... */
    // ++ UPDATE HEADER BEFORE WRITING!!!

    struct _file_support *stream = get_by_image_format(_lpNtHeader->FileHeader.Machine);

    if (stream == NULL)
    {
        DebugBreak();    // unsupported file format!
        CloseHandle(hOutFile);
        return FALSE;
    }

    stream->write(this, hOutFile, &_sections);    // write into file!

    return TRUE;
}


/****************************************************************************
*    query on virtual address
***************************************************************************/
virtualaddress_t CPeAssembly::getBaseAddress()
{
    return _lpNtHeader->OptionalHeader.ImageBase;
}

/**
 *     \!setBaseAddress
 *    apply a new base address to image
 **/
void CPeAssembly::setBaseAddress(virtualaddress_t newva)
{
    virtualaddress_t previous = _lpNtHeader->OptionalHeader.ImageBase;
    if (newva != previous)
    {    // 
        update_relocentries(newva, previous);
        _lpNtHeader->OptionalHeader.ImageBase = newva;    // commit change in header!
    }
}


/****************************************************************************
*    I/O in virtual address (read/write bytes)
***************************************************************************/

/// BYTE read/write members
bool CPeAssembly::ReadByte(virtualaddress_t    va, uint8_t *out)
{
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return it->descriptor->ReadByte(va, out);
    }
    return false;
}

bool CPeAssembly::PatchByte(virtualaddress_t va, uint8_t *in)
{
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return it->descriptor->PatchByte(va, in);
    }
    return false;

}

/// WORD read/write members
bool CPeAssembly::ReadWord(virtualaddress_t    va, uint16_t *out)
{
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return it->descriptor->ReadWord(va, out);
    }
    return false;

}

bool CPeAssembly::PatchWord(virtualaddress_t    va, uint16_t *in)
{
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return it->descriptor->PatchWord(va, in);
    }
    return false;

}


/// DWORD read/write members
bool CPeAssembly::ReadDword(virtualaddress_t    va, uint32_t *out)
{
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return it->descriptor->ReadDword(va, out);
    }
    return false;

}

bool CPeAssembly::PatchDword(virtualaddress_t    va, uint32_t *in)
{
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return it->descriptor->PatchDword(va, in);
    }
    return false;
}

/// QWORD read/write members
bool CPeAssembly::ReadQWord(virtualaddress_t    va, uint64_t *out)
{
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return it->descriptor->ReadQWord(va, out);
    }
    return false;
}

bool CPeAssembly::PatchQWord(virtualaddress_t    va, uint64_t *in)
{
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return it->descriptor->PatchQWord(va, in);
    }
    return false;
}

/**
 *    \!roundup
 *    round a value to "base"
 **/
virtualaddress_t CPeAssembly::roundup(virtualaddress_t value, virtualaddress_t base)
{
    virtualaddress_t tmpValue = value;

    if (base == 0)
    {
        std::cout << "[ERROR] Address " << std::hex << value << " for base " << std::hex << base;
        return value;
    }

    while((tmpValue % base) != 0)
        tmpValue++;
    return tmpValue;
}

/**
 *    \!    round_section
 *    return "value" aligned to "SectionAlignment" of PE
 **/
virtualaddress_t CPeAssembly::round_section(virtualaddress_t value)
{
    return roundup(value, _lpNtHeader->OptionalHeader.SectionAlignment);
}

/**
 *    \!    round_file
 *    return "value" aligned to "FileAlignment" of PE
 **/
virtualaddress_t CPeAssembly::round_file(virtualaddress_t value)
{
    return roundup(value, _lpNtHeader->OptionalHeader.FileAlignment);
}

void *CPeAssembly::rva2addr(virtualaddress_t address)
{
    virtualaddress_t dwImageDosHeader = (virtualaddress_t) _lpBase;    // new base address!

    if (address > _lpNtHeader->OptionalHeader.ImageBase)
        address -= _lpNtHeader->OptionalHeader.ImageBase;

    address += dwImageDosHeader;

    return (void *) address;    // warning! this is an absolute pointer, /* TODO! */ Patch in new section model!!
}


/**
*
**/
virtualaddress_t CPeAssembly::getMinva()
{
    PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(_lpNtHeader);

    return section->VirtualAddress;
}

virtualaddress_t CPeAssembly::getMaxva()
{
    PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(_lpNtHeader);

    short nSections = this->NumberOfSections();

    while((--nSections) > 0)
        section++;

    return section->VirtualAddress + section->Misc.VirtualSize;
}

/******************************************************************************
*    section management
*****************************************************************************/

/**
*    !NumberOfSection
**/
short CPeAssembly::NumberOfSections()
{
    //return _lpNtHeader->FileHeader.NumberOfSections;
    return (short) _sections.size();
}

/**
*    !getSection
*    return a CPeSection object that manage "header" and data
**/
CPeSection*    CPeAssembly::getSection(int index)
{
    if (index >= NumberOfSections())
        return NULL;

    std::list<SECTION_ITEM>::iterator it = _sections.begin();

    while(index-- > 0)
        it++;

    return it->descriptor;
}

PIMAGE_SECTION_HEADER CPeAssembly::LastSectionHeader()
{
    PIMAGE_SECTION_HEADER p = IMAGE_FIRST_SECTION(_lpNtHeader);

    short size = NumberOfSections();

    while(--size > 0)
        p++;

    return p;
}

PIMAGE_SECTION_HEADER CPeAssembly::GetSectionHeader(int index)
{
    PIMAGE_SECTION_HEADER first = IMAGE_FIRST_SECTION(_lpNtHeader);

    while(index-- > 0)
        first++;

    return first;
}

/**
 *    \!LookupSectionByName
 *    looking section by name
 **/
CPeSection *CPeAssembly::LookupSectionByName(const char *szSectionName)
{
    for(SECTION_ITERATOR it = _sections.begin(); it != _sections.end(); it++)
    {
        if (memcmp(szSectionName, it->descriptor->GetSectionHeader()->Name, strlen(szSectionName)) == 0)
            return it->descriptor;
    }
        
    return NULL;
}

/**
*    !RemoveSection
*    delete a section from header and virtual address
**/
bool CPeAssembly::RemoveSection(int index)
{
    if (index >= NumberOfSections())
        return NULL;

    PIMAGE_SECTION_HEADER first = IMAGE_FIRST_SECTION(_lpNtHeader);
    PIMAGE_SECTION_HEADER last = LastSectionHeader();

    PIMAGE_SECTION_HEADER curr = GetSectionHeader(index);

    if (curr == first)
    {    // first section deleted!
        // move descriptor to this!
        virtualaddress_t begin = first->VirtualAddress;            // "VIRTUAL ADDRESS"
        size_t size = first->Misc.VirtualSize;        // "VIRTUAL SIZE"
        size_t phys_size = first->SizeOfRawData;        // "SIZE OF RAW DATA"
        uint32_t phys_off = first->PointerToRawData;    // "POINTER TO RAW DATA"

        while(first < last)
        {

            //void *dest = rva2addr(
        }


        _lpNtHeader->FileHeader.NumberOfSections--;
        memcpy(first, first+1, sizeof(IMAGE_SECTION_HEADER) * NumberOfSections());

    }
    else if (curr == last)
    {    // zeromemory of "curr"
        _lpNtHeader->FileHeader.NumberOfSections--;
        memset(curr, 0, sizeof(IMAGE_SECTION_HEADER));
    }
    else
    {    
        memcpy(curr, curr+1, sizeof(IMAGE_SECTION_HEADER) * (NumberOfSections() - index));
        _lpNtHeader->FileHeader.NumberOfSections--;
    }

    // update header!!!

    /* TODO */ 

    // update header!!!
    return true;
}

bool CPeAssembly::RemoveSection(const char *szSectionName)
{    // lookup section by name!
    PIMAGE_SECTION_HEADER cursor = GetSectionHeader(0);
    PIMAGE_SECTION_HEADER last = LastSectionHeader();

    int index = 0;
    while(cursor <= last)
    {
        if (strncmp(szSectionName, (char *) cursor->Name, 8) == 0)
            return RemoveSection(index);

        index++;
        cursor++;
    }

    return false;
}

/**
 *    \!nextva
 *    return next virtual address!
 **/
virtualaddress_t CPeAssembly::nextva()
{
    _sections.sort();    // sort 
    
    std::list<SECTION_ITEM>::iterator it = _sections.end();
    it--;

    virtualaddress_t va = it->descriptor->VirtualAddress() + it->descriptor->VirtualSize();

    va = roundup(va, _lpNtHeader->OptionalHeader.SectionAlignment);

    return va;
}

/**
 *    \!nextva
 *    return next virtual address!
 **/
virtualaddress_t CPeAssembly::nextrawdata()
{
    _sections.sort();    // sort 
    
    std::list<SECTION_ITEM>::iterator it = _sections.end();
    it--;

    virtualaddress_t va = it->descriptor->PointerToRawData() + it->descriptor->SizeOfRawData();

    va = round_file(va); 

    return va;
}

/**
 *    \!AddSection
 *    create a new section with "rawsize" value
 **/
CPeSection*    CPeAssembly::AddSection(const char *szSectionName, virtualaddress_t newva, size_t size, size_t rawsize)
{
    // update header!!!
    IMAGE_SECTION_HEADER dummy;
    memset(&dummy, 0, sizeof(IMAGE_SECTION_HEADER));

    size_t len = strlen(szSectionName);
    if (len > 8) len = 8;
    memcpy(dummy.Name, szSectionName, len);

    if (newva == 0)    // add section in tail!
    {
        newva = nextva();
        dummy.PointerToRawData = nextrawdata();
    }

    dummy.VirtualAddress = newva;
    dummy.Misc.VirtualSize = size; //round_section(size);
    rawsize = round_file(rawsize);
    dummy.SizeOfRawData = rawsize;

    void *tmp = malloc(rawsize);
    memset(tmp, 0, rawsize);

    // before write section in list.. update next!
    _sections.sort();

    std::list<SECTION_ITEM>::iterator it = _sections.begin();

    virtualaddress_t sum = round_section(size);
    bool bFound = false;

    lock_datadir();

    virtualaddress_t _reloc_from = 0x0;
    virtualaddress_t _reloc_to = 0x0;

    for(it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t va = it->va;
        size_t size = it->descriptor->VirtualSize();

        if (va >= newva || bFound == true)
        {    // first section to move!!
            
            if (dummy.PointerToRawData == 0)
            {    // get first pointer to raw data
                dummy.PointerToRawData = it->descriptor->PointerToRawData();
            }
            
            //virtualaddress_t old = it->va;
            size_t size = it->descriptor->VirtualSize();

            it->va += sum;
            it->descriptor->SetNewVirtualAddress(it->va);

            update_header(it->va, va, size);    // reflect in header!

            update_datadirectory(it->va, va, size);    // reflect in datadirectory

            if (it->descriptor->SizeOfRawData() != 0)
            {    // adjust pointer to raw data!
                virtualaddress_t p = it->descriptor->PointerToRawData();
                p += dummy.SizeOfRawData;
                it->descriptor->SetPointerToRawData(p);
            }

            if (bFound == false)
            {    // only 1st time!
                _reloc_from = va;
                _reloc_to = it->va;
            }

            bFound = true;
        }
    }

    if (_reloc_from != 0)
        update_relocentries(_reloc_to, _reloc_from, 0);

    /* TODO */
    void *region = malloc(round_section(size));
    memset(region, 0, round_section(size));
    CPeSection *n = new CPeSection(this, &dummy, newva, size, region );
    free(region);

    SECTION_ITEM dummy1 = { newva , n };

    _sections.push_back(dummy1);    // put in list
    _sections.sort();    // re-sort!
    
    
    return n;
}

CPeSection*    CPeAssembly::AddSection(const char *szSectionName, virtualaddress_t newva, size_t size)
{
    // update header!!!
    IMAGE_SECTION_HEADER dummy;
    memset(&dummy, 0, sizeof(IMAGE_SECTION_HEADER));

    size_t len = strlen(szSectionName);
    if (len > 8) len = 8;
    memcpy(dummy.Name, szSectionName, len);

    if (newva == 0)    // add section in tail!
    {
        newva = nextva();
        dummy.PointerToRawData = nextrawdata();
    }

    dummy.VirtualAddress = newva;
    dummy.Misc.VirtualSize = size; //round_section(size);
    dummy.SizeOfRawData = round_file(size);

    void *tmp = malloc(size);
    memset(tmp, 0, size);

    // before write section in list.. update next!
    _sections.sort();

    std::list<SECTION_ITEM>::iterator it = _sections.begin();

    virtualaddress_t sum = round_section(size);
    bool bFound = false;

    lock_datadir();

    virtualaddress_t _reloc_from = 0x0;
    virtualaddress_t _reloc_to = 0x0;

    for(it = _sections.begin(); it != _sections.end(); ++it)
    {
        if (it->va >= newva || bFound == true)
        {    // first section to move!!
            if (dummy.PointerToRawData == 0)
            {    // only first time!
                dummy.PointerToRawData = it->descriptor->PointerToRawData();
            }
            
            virtualaddress_t old = it->va;
            size_t size = it->descriptor->VirtualSize();

            it->va += sum;
            it->descriptor->SetNewVirtualAddress(it->va);

            update_header(it->va, old, size);    // reflect in header!

            update_datadirectory(it->va, old, size);    // reflect in datadirectory

            if (it->descriptor->SizeOfRawData() != 0)
            {    // adjust pointer to raw data!
                virtualaddress_t p = it->descriptor->PointerToRawData();
                p += dummy.SizeOfRawData;
                it->descriptor->SetPointerToRawData(p);
            }

            if (bFound == false)
            {    // only 1st time!
                _reloc_from = old;
                _reloc_to = it->va;
            }

            bFound = true;
        }
    }

    if (_reloc_from != 0)
        update_relocentries(_reloc_to, _reloc_from, 0);

    /* TODO */
    void *region = malloc(round_section(size));
    memset(region, 0, round_section(size));
    CPeSection *n = new CPeSection(this, &dummy, newva, size, region );
    free(region);

    SECTION_ITEM dummy1 = { newva , n };

    _sections.push_back(dummy1);    // put in list
    _sections.sort();    // re-sort!
    
    
    return n;
}

//////////////////////////////////////////////////////////////////////////
// \!MergeSection
//    first section is replaced with new "big" section
CPeSection* CPeAssembly::MergeSection(CPeSection *sect0, CPeSection *sect1)
{
    IMAGE_SECTION_HEADER dummy;
    memset(&dummy, 0, sizeof(IMAGE_SECTION_HEADER));

    size_t size_first_section = sect1->VirtualAddress() - sect0->VirtualAddress();
    
    size_t virtualsize = size_first_section + sect1->VirtualSize();
    
    dummy.Misc.VirtualSize = virtualsize;
    dummy.Characteristics = sect0->GetSectionHeader()->Characteristics;
    dummy.PointerToRawData = sect0->GetSectionHeader()->PointerToRawData;

    /**
     SizeOfRawData of first section ignored... sect0->GetSectionHeader()->SizeOfRawData
     new size of first section is same of "Virtual Size" */
    dummy.SizeOfRawData = size_first_section + sect1->GetSectionHeader()->SizeOfRawData;

    void *data = malloc(virtualsize);
    
    memcpy(dummy.Name, sect1->GetSectionHeader()->Name, 8);

    dummy.VirtualAddress = sect0->VirtualAddress();
    
    //dummy.SizeOfRawData = sect0->SizeOfRawData() + sect1->SizeOfRawData();

    memset(data, 0xcc, virtualsize);    // fill section with xcc or xdd

    memcpy(data, sect0->RawData(), sect0->VirtualSize());
    memcpy(CALC_OFFSET(void *, data, sect1->VirtualAddress() - sect0->VirtualAddress()), sect1->RawData(), sect1->VirtualSize());
    
    CPeSection *newsection = new CPeSection(this, &dummy, sect0->VirtualAddress(), virtualsize, data);


    SECTION_ITEM d0 = { sect0->VirtualAddress(), NULL };
    SECTION_ITEM d1 = { sect1->VirtualAddress(), NULL };

    _sections.remove(d0);
    _sections.remove(d1);

    // update header!!!
    //PIMAGE_SECTION_HEADER pSection0 = sect0->GetSectionHeader();

    SECTION_ITEM d2 = { newsection->VirtualAddress(), newsection };

    _sections.push_back(d2);

    _sections.sort();
    
    free(data);

    DWORD sect0_RawData= sect0->SizeOfRawData();
    DWORD sect1_RawData = sect1->SizeOfRawData();
    DWORD sect0_PtrRawData = sect0->PointerToRawData();
    DWORD sect1_PtrRawData = sect0->PointerToRawData();

    delete sect0;    // remove section 0
    delete sect1;    // remove section 1

    DWORD sectN_PtrRawData = newsection->PointerToRawData();
    DWORD sectN_RawData = newsection->SizeOfRawData();

    for(SECTION_ITERATOR it = _sections.begin(); it != _sections.end(); ++it)
    {
        if (it->descriptor->PointerToRawData() > newsection->PointerToRawData())
        {
            DWORD delta = sectN_RawData - sect1_RawData - sect0_RawData;
            DWORD newptr = it->descriptor->PointerToRawData() + delta;

            it->descriptor->SetPointerToRawData(newptr);
        }

    }


    return newsection;
}

void CPeAssembly::update_section_header()
{
    PIMAGE_DOS_HEADER dos_header;
    dos_header = (PIMAGE_DOS_HEADER) this->_lpBase;

    PIMAGE_NT_HEADERS32 pe_header;   
    pe_header = (PIMAGE_NT_HEADERS32)((DWORD) _lpBase + (DWORD)dos_header->e_lfanew );

    DWORD dwSizeHeader = pe_header->FileHeader.SizeOfOptionalHeader  + sizeof(pe_header->FileHeader) + 4;// +
    PIMAGE_SECTION_HEADER sections = (PIMAGE_SECTION_HEADER)((DWORD)(_lpBase) + dwSizeHeader + dos_header->e_lfanew);
    std::list<SECTION_ITEM>::iterator it = _sections.begin();

    memset(sections, 0, pe_header->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));    // clear array before new size
    pe_header->FileHeader.NumberOfSections = _sections.size();

    size_t SizeOfCode = 0;
    size_t SizeOfImage = _sections.begin()->va;
    PIMAGE_SECTION_HEADER ptr = sections;
    
    do 
    {
        PIMAGE_SECTION_HEADER src = it->descriptor->GetSectionHeader();
        memcpy(ptr, src, sizeof(IMAGE_SECTION_HEADER));

        if (src->Characteristics & IMAGE_SCN_CNT_CODE)
        {
            SizeOfCode += round_file(src->Misc.VirtualSize);
        }
        
        SizeOfImage += round_section(src->Misc.VirtualSize);

        ptr++;
        it++;
    } while (it != _sections.end());

    pe_header->OptionalHeader.SizeOfCode = SizeOfCode;
    pe_header->OptionalHeader.SizeOfImage = SizeOfImage;
}

void* CPeAssembly::RawPointer(virtualaddress_t va)
{    // return a void* to specified virtualaddress! don't check overflow in section!!!!!
    for (std::list<SECTION_ITEM>::iterator it = _sections.begin(); it != _sections.end(); ++it)
    {
        virtualaddress_t begin = it->va;
        virtualaddress_t end = it->descriptor->VirtualSize() + begin;

        if (begin <= va && va <= end)
            return CALC_OFFSET(void *, it->descriptor->RawData(), va - begin);
    }
    return NULL;
}

/**
 *    update_datadirectory
 *    update all directory to new address!
 **/
void CPeAssembly::update_datadirectory(virtualaddress_t newbaseaddress, virtualaddress_t baseaddress, size_t size)
{
    PIMAGE_DATA_DIRECTORY pImageDataDirectory = _lpNtHeader->OptionalHeader.DataDirectory;
    
    virtualaddress_t end = baseaddress + size;

    for(int i=0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
    {
        // it's a valid virtualaddress and processed only 1 time for SECTION i/o
        if (pImageDataDirectory[i].VirtualAddress != 0 && pImageDataDirectory[i].VirtualAddress == _DATADIR[i].VirtualAddress)
        {    // valid address
            if (baseaddress <= pImageDataDirectory[i].VirtualAddress && pImageDataDirectory[i].VirtualAddress <= end)
            {
                pImageDataDirectory[i].VirtualAddress -= baseaddress;
                pImageDataDirectory[i].VirtualAddress += newbaseaddress;

                switch(i)
                {
                    case IMAGE_DIRECTORY_ENTRY_IMPORT: 
                        update_importentries(newbaseaddress, baseaddress, size);
                        break;
                    case IMAGE_DIRECTORY_ENTRY_EXPORT:
                        update_exportentries(newbaseaddress, baseaddress, size);
                        break;
                    case IMAGE_DIRECTORY_ENTRY_RESOURCE:
                        update_rsrc(newbaseaddress, baseaddress, size);
                        break;
                    case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT:
                        update_delayimportentries(newbaseaddress, baseaddress, size);
                        break;
                    case IMAGE_DIRECTORY_ENTRY_BASERELOC:
                        // don't trigger!!
                        //update_relocentries(newbaseaddress, baseaddress, size);
                        break;
                }
            }
        }
    }
}

#define UPDATE_SYMBOLADDRESS(x, base, newbase) if (x != 0) x = x - base + newbase

void CPeAssembly::update_delayimportentries(virtualaddress_t newbaseaddress, virtualaddress_t baseaddress, size_t size)
{    // process import entries!
    PImgDelayDescr pDelayDescr = 
        (PImgDelayDescr) this->RawPointer(_lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT].VirtualAddress);

    while(pDelayDescr->grAttrs != 0)
    {
        UPDATE_SYMBOLADDRESS(pDelayDescr->rvaBoundIAT, baseaddress, newbaseaddress);
        UPDATE_SYMBOLADDRESS(pDelayDescr->rvaDLLName, baseaddress, newbaseaddress);
        UPDATE_SYMBOLADDRESS(pDelayDescr->rvaHmod, baseaddress, newbaseaddress);
        UPDATE_SYMBOLADDRESS(pDelayDescr->rvaIAT, baseaddress, newbaseaddress);
        UPDATE_SYMBOLADDRESS(pDelayDescr->rvaINT, baseaddress, newbaseaddress);
        UPDATE_SYMBOLADDRESS(pDelayDescr->rvaUnloadIAT, baseaddress, newbaseaddress);

        /*iat->Name -= baseaddress;
        iat->Name += newbaseaddress;

        iat->Characteristics -= baseaddress;
        iat->Characteristics += newbaseaddress;
        iat->FirstThunk -= baseaddress;
        iat->FirstThunk += newbaseaddress;

        PULONG rvaName = (PULONG) this->RawPointer(iat->Characteristics);
        PULONG iatRVA = (PULONG) this->RawPointer(iat->FirstThunk);

        while(*rvaName != 0)
        {
            if ((*rvaName & 0x80000000) == 0)
            {    // Import by name
                *rvaName -= baseaddress;
                *rvaName += newbaseaddress;
            }

            *iatRVA -= baseaddress;
            *iatRVA += newbaseaddress;
            
            rvaName++;
            iatRVA++;
        }
        */
        pDelayDescr++;
    }

}


void CPeAssembly::update_importentries(virtualaddress_t newbaseaddress, virtualaddress_t baseaddress, size_t size)
{    // process import entries!
    PIMAGE_IMPORT_DESCRIPTOR iat = 
        (PIMAGE_IMPORT_DESCRIPTOR) this->RawPointer(_lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

    while(iat->Characteristics != 0)
    {
        iat->Name -= baseaddress;
        iat->Name += newbaseaddress;

        iat->Characteristics -= baseaddress;
        iat->Characteristics += newbaseaddress;
        iat->FirstThunk -= baseaddress;
        iat->FirstThunk += newbaseaddress;

        PULONG rvaName = (PULONG) this->RawPointer(iat->Characteristics);
        PULONG iatRVA = (PULONG) this->RawPointer(iat->FirstThunk);

        while(*rvaName != 0)
        {
            if ((*rvaName & 0x80000000) == 0)
            {    // Import by name
                *rvaName -= baseaddress;
                *rvaName += newbaseaddress;
            }

            *iatRVA -= baseaddress;
            *iatRVA += newbaseaddress;
            
            rvaName++;
            iatRVA++;
        }

        iat++;
    }

}

void CPeAssembly::update_exportentries(virtualaddress_t newbaseaddress, virtualaddress_t baseaddress, size_t size)
{    // process import entries!
    PIMAGE_EXPORT_DIRECTORY eat = 
        (PIMAGE_EXPORT_DIRECTORY) this->RawPointer(_lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

    eat->AddressOfFunctions -= baseaddress;
    eat->AddressOfFunctions += newbaseaddress;

    eat->AddressOfNameOrdinals -= baseaddress;
    eat->AddressOfNameOrdinals += newbaseaddress;

    eat->Name -= baseaddress;
    eat->Name += newbaseaddress;

    eat->AddressOfNames -= baseaddress;
    eat->AddressOfNames += newbaseaddress;

    DWORD *addressofnames = (DWORD *) this->RawPointer(eat->AddressOfNames);

    for(DWORD d = 0; d < eat->NumberOfNames; d++)
    {
        addressofnames[d] -= baseaddress;
        addressofnames[d] += newbaseaddress;
    }
    //eat->
    DWORD *AddressOfFunctions = (DWORD *) this->RawPointer(eat->AddressOfFunctions);

    for(DWORD d = 0; d < eat->NumberOfFunctions; d++)
    {
        AddressOfFunctions[d] -= baseaddress;
        AddressOfFunctions[d] += newbaseaddress;
    }

}

//////////////////////////////////////////////////////////////////////////
// create a backup of IMAGE_DATA_DIRECTORY
void CPeAssembly::update_relocentries(virtualaddress_t newimagebase, virtualaddress_t imagebase)
{
    // image base!
    uint32_t ImageBase = _lpNtHeader->OptionalHeader.ImageBase;
    void *lpRelocPointer = RawPointer(_lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
    size_t dwRelocSize = _lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

    if (dwRelocSize == 0 || lpRelocPointer == NULL)
        return;    // no reloc table here!

    base_relocation_block_t *relocation_page = (base_relocation_block_t *) lpRelocPointer;

    // for each page!
    while(relocation_page->BlockSize > 0)
    {
        typedef short relocation_entry;

        int BlockSize = relocation_page->BlockSize - 8;
        relocation_entry *entries = CALC_OFFSET(relocation_entry *, relocation_page, 8);

        while(BlockSize > 0)
        {
            short type = ((*entries & 0xf000) >> 12);
            long offset = (*entries & 0x0fff);

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

            switch(type)
            {
            case IMAGE_REL_BASED_HIGHLOW:
                value = value - imagebase;
                value = value + newimagebase;
                *ptr = value;
                break;
            case IMAGE_REL_BASED_DIR64:
                dwNewValue = value - imagebase + (ULONG) newimagebase;
                *ptr = dwNewValue;
                break;
            default:
                break;
            }
            entries++;
            BlockSize -= 2;
        }

        relocation_page = CALC_OFFSET(relocation_block_t *, relocation_page, relocation_page->BlockSize);
    }

}

//////////////////////////////////////////////////////////////////////////
// Perform an update of tree after new section!
void CPeAssembly::update_relocentries(virtualaddress_t newbaseaddress, virtualaddress_t baseaddress, size_t size)
{
    typedef struct base_relocation_block
    {
        uint32_t PageRVA;
        uint32_t BlockSize;
    } base_relocation_block_t;

    typedef struct base_relocation_entry
    {
        uint16_t offset : 12;
        uint16_t type : 4;
    } base_relocation_entry_t;

    #define relocation_block_t base_relocation_block_t
    #define relocation_entry_t base_relocation_entry_t

    typedef short relocation_entry;

    // image base!
    uint32_t ImageBase = _lpNtHeader->OptionalHeader.ImageBase;
    void *lpRelocAddress = RawPointer(_lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
    size_t dwRelocSize = _lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

    if (dwRelocSize == 0 || lpRelocAddress == NULL)
        return;    // no reloc table here!

    base_relocation_block_t *relocation_page = (base_relocation_block_t *) lpRelocAddress;

    // for each page!
    while(relocation_page->BlockSize > 0)
    {
        if (relocation_page->PageRVA >= baseaddress)
        {
            typedef short relocation_entry;

            int BlockSize = relocation_page->BlockSize - 8;
            relocation_entry *entries = CALC_OFFSET(relocation_entry *, relocation_page, 8);

            while(BlockSize > 0)
            {
                short type = ((*entries & 0xf000) >> 12);
                long offset = (*entries & 0x0fff);

                ULONG *ptr = (ULONG *) RawPointer(offset + (newbaseaddress - baseaddress) + relocation_page->PageRVA);
                ULONG value = *ptr;
                ULONG dwNewValue = 0;

                if ((value ^ this->_lpNtHeader->OptionalHeader.ImageBase) < 0x1000)
                {    // skip this block!
                    entries++;
                    BlockSize -= 2;
                    continue;
                }

                if (value >= baseaddress)
                {
                    switch(type)
                    {
                        case IMAGE_REL_BASED_HIGHLOW:
                            if (value >= 0x1000)    
                            {    // accepted values are only in first section!
                                value = value - baseaddress;
                                value = value + newbaseaddress;
                                *ptr = value;
                            }
                            break;
                        case IMAGE_REL_BASED_DIR64:
                            dwNewValue = value - baseaddress + (ULONG) newbaseaddress;
                            *ptr = dwNewValue;
                            break;
                        default:
                            break;
                    }
                }
                entries++;
                BlockSize -= 2;
            }
            relocation_page->PageRVA += (newbaseaddress - baseaddress);
        }

        relocation_page = CALC_OFFSET(base_relocation_block_t *, relocation_page, relocation_page->BlockSize);
    }

}
/**
 *    explore_rsrc
 *    recursive function to update entry:OffsetToData into RSRC tree directories
 **/
static void explore_rsrc(PIMAGE_RESOURCE_DIRECTORY root, PIMAGE_RESOURCE_DIRECTORY dir, virtualaddress_t newbaseaddress, virtualaddress_t baseaddress, size_t size)
{
    PIMAGE_RESOURCE_DIRECTORY_ENTRY entries = CALC_OFFSET(PIMAGE_RESOURCE_DIRECTORY_ENTRY, dir, sizeof(IMAGE_RESOURCE_DIRECTORY));

    for(WORD i = 0; i < dir->NumberOfIdEntries; i++)
    {
        if (entries->DataIsDirectory)
        {    // another data!!!
            PIMAGE_RESOURCE_DIRECTORY subdir =
                CALC_OFFSET(PIMAGE_RESOURCE_DIRECTORY, root, entries->OffsetToData & 0x7fffffff);

            explore_rsrc(root, subdir, newbaseaddress, baseaddress, size);
        }
        else
        {
            PIMAGE_RESOURCE_DATA_ENTRY entry = CALC_OFFSET(PIMAGE_RESOURCE_DATA_ENTRY, root, entries->OffsetToData);

            entry->OffsetToData -= baseaddress;
            entry->OffsetToData += newbaseaddress;
        }
        entries++;
    }

}
//////////////////////////////////////////////////////////////////////////
// update rsrc
void CPeAssembly::update_rsrc(virtualaddress_t newbaseaddress, virtualaddress_t baseaddress, size_t size)
{
    PIMAGE_RESOURCE_DIRECTORY dir =  
        (PIMAGE_RESOURCE_DIRECTORY) this->RawPointer(_lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);

    explore_rsrc(dir, dir, newbaseaddress, baseaddress, size);
}

void CPeAssembly::update_header(virtualaddress_t newbaseaddress, virtualaddress_t baseaddress, size_t size)
{
    if (va_in_range(_lpNtHeader->OptionalHeader.AddressOfEntryPoint, baseaddress, size))
    {    // set new AddressPoint
        _lpNtHeader->OptionalHeader.AddressOfEntryPoint -= baseaddress;
        _lpNtHeader->OptionalHeader.AddressOfEntryPoint += newbaseaddress;
    }

    
}
//////////////////////////////////////////////////////////////////////////
// perform a "backup" of PE!OPTIONALHEADER!DATADIRECTORY
void CPeAssembly::lock_datadir()
{
    memcpy(_DATADIR, _lpNtHeader->OptionalHeader.DataDirectory, sizeof(IMAGE_DATA_DIRECTORY) * IMAGE_NUMBEROF_DIRECTORY_ENTRIES);
}