First things first
This is a very experimental release. This code is only five minutes old at the time of writing and not finished, I've only tested it once and it hasn't been commented or re-factored. It's really very hackery in some places and I'll take zero responsibility for damage or errors. Next, I've cobbled together this project from many different sources, some of the code is (I admit) directly c+p'ed from other
sources. I've also used a few libraries that are available
here,
here and
here.
You'll need:
The boost C++ libraries, fasm.dll and beaengine.dll.
What does this thing do?
I realize that this is not actually a metamorphic obfuscator, since the obfuscated executable will not be able to self-modify. However lacking a name for this (since it's unlike other obfuscators out there but like techniques used in metamorphic viruses) I named it thus. A normal obfuscator will "hide" the "real" code by surrounding it by superfluous instructions or encrypting them. The real code will still execute in it's normal form however, after the decryption or in between junk instructions. More advanced obfuscators will perhaps reassemble jump and call offsets to account for the increase in executable size, but that's about it.
This code is different. This engine 'morphs' instructions to (a) different, but functionally identical, other instruction(s). This process is 100% recursive, so you could theoretically morph one instruction into thousands of others that all combine to be functionally equivalent to the original instruction.
An example:
Suppose that you start with this:
The engine can transform that to:
Which can be transformed into:
Code:
sub esp, 4
mov dword[esp], ebx
pop eax
Which can become:
Code:
xchg eax, ebx
sub esp, 4
mov dword[esp], eax
pop ebx
xchg eax, ebx
So in the end:
Code:
mov eax, ebx
; Is equivalent to:
xchg eax, ebx
sub esp, 4
mov dword[esp], eax
pop ebx
xchg eax, ebx
Now imagine doing this to an entire executable. It's size inflated a thousand times. It'll be impossible to distinguish where one functional instruction begins and where the next ends. Every new instruction will only complete 1000th of an old instruction, and yet you can't see or understand what exactly it's doing.
Sources
Like I said before, this is a very hackery/experimental build. To use it you'll need to put an executable named test.exe into the directory of the mutator. It'll also need beaengine.dll and fasm.dll in the same directory
cFasmWrapper.h:
Code:
#ifndef C_FASM_WRAPPER_H
#define C_FASM_WRAPPER_H
#include <string>
#include <vector>
#include <Windows.h>
using namespace std;
#define FASM_OK 0 //; #define FASM_STATE points to output
#define FASM_WORKING 1
#define FASM_ERROR 2 //; #define FASM_STATE contains error code
#define FASM_INVALID_PARAMETER -1
#define FASM_OUT_OF_MEMORY -2
#define FASM_STACK_OVERFLOW -3
#define FASM_SOURCE_NOT_FOUND -4
#define FASM_UNEXPECTED_END_OF_SOURCE -5
#define FASM_CANNOT_GENERATE_CODE -6
#define FASM_FORMAT_LIMITATIONS_EXCEDDED -7
#define FASM_WRITE_FAILED - 8
#define FASMERR_FILE_NOT_FOUND -101
#define FASMERR_ERROR_READING_FILE -102
#define FASMERR_INVALID_FILE_FORMAT -103
#define FASMERR_INVALID_MACRO_ARGUMENTS -104
#define FASMERR_INCOMPLETE_MACRO -105
#define FASMERR_UNEXPECTED_CHARACTERS -106
#define FASMERR_INVALID_ARGUMENT -107
#define FASMERR_ILLEGAL_INSTRUCTION -108
#define FASMERR_INVALID_OPERAND -109
#define FASMERR_INVALID_OPERAND_SIZE -110
#define FASMERR_OPERAND_SIZE_NOT_SPECIFIED -111
#define FASMERR_OPERAND_SIZES_DO_NOT_MATCH -112
#define FASMERR_INVALID_ADDRESS_SIZE -113
#define FASMERR_ADDRESS_SIZES_DO_NOT_AGREE -114
#define FASMERR_DISALLOWED_COMBINATION_OF_REGISTERS -115
#define FASMERR_LONG_IMMEDIATE_NOT_ENCODABLE -116
#define FASMERR_RELATIVE_JUMP_OUT_OF_RANGE -117
#define FASMERR_INVALID_EXPRESSION -118
#define FASMERR_INVALID_ADDRESS -119
#define FASMERR_INVALID_VALUE -120
#define FASMERR_VALUE_OUT_OF_RANGE -121
#define FASMERR_UNDEFINED_SYMBOL -122
#define FASMERR_INVALID_USE_OF_SYMBOL -123
#define FASMERR_NAME_TOO_LONG -124
#define FASMERR_INVALID_NAME -125
#define FASMERR_RESERVED_WORD_USED_AS_SYMBOL -126
#define FASMERR_SYMBOL_ALREADY_DEFINED -127
#define FASMERR_MISSING_END_QUOTE -128
#define FASMERR_MISSING_END_DIRECTIVE -129
#define FASMERR_UNEXPECTED_INSTRUCTION -130
#define FASMERR_EXTRA_CHARACTERS_ON_LINE -131
#define FASMERR_SECTION_NOT_ALIGNED_ENOUGH -132
#define FASMERR_SETTING_ALREADY_SPECIFIED -133
#define FASMERR_DATA_ALREADY_DEFINED -134
#define FASMERR_TOO_MANY_REPEATS -135
#define FASMERR_SYMBOL_OUT_OF_SCOPE -136
#define FASMERR_USER_ERROR -140
#define FASMERR_ASSERTION_FAILED -141
struct FASM_STATE
{
DWORD Condition;
union
{
DWORD OutputLength;
DWORD ErrorCode;
};
union
{
DWORD pOutput;
DWORD ErrorLine;
};
};
class cFasmWrapper
{
HANDLE _hFasm;
unsigned int (__cdecl *fAssemble)( const char * Source, const unsigned char * Buffer, int BufferSize, int NumberOfPasses);
public:
cFasmWrapper();
bool Assemble( const string &Source, const unsigned char* pBuffer, int BufferSize );
};
#endif
cFasmWrapper.cpp:
Code:
#include <string>
#include <vector>
#include <Windows.h>
#include "cFasmWrapper.h"
cFasmWrapper::cFasmWrapper()
{
_hFasm = LoadLibrary( L"FASM.dll" );
if( _hFasm == INVALID_HANDLE_VALUE )
throw "Error: cFasmWrapper::cFasmWrapper: 'FASM.dll' not found";
fAssemble = (unsigned int (__cdecl*)(const char *, const unsigned char *, int, int))GetProcAddress( (HMODULE)_hFasm, "fasm_Assemble" );
if( !fAssemble )
throw "Error: cFasmWrapper::cFasmWrapper: Symbol 'fasm_Assemble' not exported";
}
bool cFasmWrapper::Assemble( const string &Source, const unsigned char* pBuffer, int BufferSize )
{
const unsigned char* cpBuffer = pBuffer;
const char* sPointer = Source.c_str();
const unsigned int* fPointer = (const unsigned int*)fAssemble;
int iBufferSize = BufferSize;
unsigned int Result;
__asm
{
mov eax, iBufferSize
mov ecx, cpBuffer
mov edx, sPointer
mov edi, fPointer
push ebp
mov ebp, esp
push 100
push eax
push ecx
push edx
call edi
mov esp, ebp
pop ebp
mov Result, eax
}
return FASM_OK == Result;
}
cMutator.h:
Code:
#ifndef C_MUTATOR_H
#define C_MUTATOR_H
#define BEA_ENGINE_STATIC
#define BEA_USE_STDCALL
#include <fstream>
#include <vector>
#include <string>
#include <bea/BeaEngine.h>
#include <Windows.h>
#include "cFasmWrapper.h"
#pragma comment( lib, "BeaEngine.lib" )
#pragma comment( lib, "peBliss.lib" )
using namespace std;
class cDisassembler
{
DISASM _Disasm;
public:
cDisassembler();
void Disassmble( const unsigned char* pCode, const size_t Size, vector< pair< size_t, string > > &Result );
size_t LengthDisassemble( const unsigned char* pCode, const size_t Size, const char LastInstruction );
};
class cMutator
{
size_t _Index;
size_t _MutationIndex;
size_t _AppendageIndex;
vector< vector< string > > _PtrPrefix;
string GetRandomRegister( string NotOf );
vector<string> MutatePop( const vector< string > &Assembler );
vector<string> MutatePush( const vector< string > &Assembler );
vector<string> MutateMove( const vector< string > &Assembler );
void Regswap( vector< string > &SectionDisassembly, const string &Reg1, const string &Reg2 );
public:
cMutator( int MutationIndex = 3 );
void Mutate( const vector< string > &Assembler, vector< string > &Result, vector< vector< string > > &Appendages );
string VectorToFasmSource( const vector< string > &Source );
};
class cPE
{
string _Filename;
unsigned char* _File;
size_t _Size;
PIMAGE_SECTION_HEADER NewSection;
PIMAGE_DOS_HEADER dos_header;
PIMAGE_NT_HEADERS nt_headers;
DWORD AlignToBoundary(DWORD size, DWORD alignment);
PIMAGE_SECTION_HEADER AddSection(const char *section_name, size_t section_size, PIMAGE_DOS_HEADER image_addr, size_t &NewSize );
void ReloadHeaders();
public:
cPE( const string& Filename );
~cPE();
PIMAGE_SECTION_HEADER AddNewSection( unsigned int Size );
PIMAGE_SECTION_HEADER GetExecutableSection( size_t Index );
void SaveFile();
void Write( size_t Offset, unsigned char* Data, unsigned int Size );
void WriteNop( size_t Offset, size_t Size );
void WriteJump( size_t OffsetFrom, size_t OffsetTo );
const unsigned char* GetFilePointer();
DWORD GetVirtualAddress( DWORD Offset );
};
#endif
cMutator.cpp
Code:
#include <fstream>
#include <vector>
#include <string>
#include <ctime>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <bea/BeaEngine.h>
#include <Windows.h>
#include "cMutator.h"
using namespace boost;
cDisassembler::cDisassembler()
{
memset( (void*)&_Disasm, 0, sizeof( _Disasm ) );
_Disasm.Options = NasmSyntax + PrefixedNumeral + ShowSegmentRegs;
}
void cDisassembler::Disassmble( const unsigned char* pCode, const size_t Size, vector< pair< size_t, string > > &Result )
{
UInt64 EndCodeSection = (UInt64) ((DWORD) pCode + (DWORD) Size );
Result.clear();
memset( (void*)&_Disasm, 0, sizeof( _Disasm ) );
_Disasm.Options = NasmSyntax + PrefixedNumeral + ShowSegmentRegs;
_Disasm.EIP = (UInt64) pCode;
while( true )
{
_Disasm.SecurityBlock = (UIntPtr)EndCodeSection - (UIntPtr)_Disasm.EIP;
int Length = Disasm( &_Disasm );
if ( Length == OUT_OF_BLOCK)
break;
else if ( Length == UNKNOWN_OPCODE )
throw "Error: cDisassembler::Dissasmble: UNKNOWN_OPCODE";
else if( _Disasm.EIP >= EndCodeSection)
break;
else
{
char Number[16] = { NULL };
sprintf_s( Number, 16, "%.8x", _Disasm.VirtualAddr );
for( unsigned int i = 0; i < 16; i++ )
if( Number[i] )
Number[i] = (char)toupper( (int)Number[i] );
string Temp = "lbl_0x";
Temp += Number;
Temp += ": ";
Temp += _Disasm.CompleteInstr;
Result.push_back( pair< size_t, string > ( _Disasm.EIP, Temp ) );
_Disasm.EIP += Length;
_Disasm.VirtualAddr += Length;
}
}
}
size_t cDisassembler::LengthDisassemble( const unsigned char* pCode, const size_t Size, const char LastInstruction )
{
UInt64 EndCodeSection = (UInt64) ((DWORD) pCode + (DWORD) Size );
memset( (void*)&_Disasm, 0, sizeof( _Disasm ) );
_Disasm.Options = NasmSyntax + PrefixedNumeral + ShowSegmentRegs;
_Disasm.EIP = (UInt64) pCode;
size_t Result = 0;
while( true )
{
if( LastInstruction != -1 )
if( *(char*)_Disasm.EIP == LastInstruction )
return Result;
_Disasm.SecurityBlock = (UIntPtr)EndCodeSection - (UIntPtr)_Disasm.EIP;
int Length = Disasm( &_Disasm );
if ( Length == OUT_OF_BLOCK)
break;
else if ( Length == UNKNOWN_OPCODE )
throw "Error: cDisassembler::Dissasmble: UNKNOWN_OPCODE";
else if( _Disasm.EIP >= EndCodeSection)
break;
else
{
Result += Length;
_Disasm.EIP += Length;
_Disasm.VirtualAddr += Length;
}
}
return Result;
}
cMutator::cMutator( int MutationIndex ) : _MutationIndex( MutationIndex )
{
_AppendageIndex = 0;
_Index = 0;
_PtrPrefix.push_back( vector< string > () );
srand( time( NULL ) );
}
string cMutator::GetRandomRegister( string NotOf )
{
const string Registers[] =
{
"eax",
"ecx",
"edx",
"ebx",
"esi",
"edi"
};
string Choice = "";
do
{
Choice = Registers[ rand() % 5 ];
}
while( NotOf.find( Choice ) != std::string::npos );
return Choice;
}
vector<string> cMutator::MutatePop( const vector< string > &Assembler )
{
vector< string > Result;
vector< string > Line;
bool IsSrcPtr = false;
string Type = "";
split( Line, Assembler[ _Index ], is_any_of(" ,"), token_compress_on );
if( Line.size() > 3 )
{
if( Line[3][0] == '[' )
{
Type = Line[2];
IsSrcPtr = true;
Line.erase( Line.begin()+2 );
}
}
// 0:lbl_xxx 1:pop 2:dest
try
{
if( IsSrcPtr )
{
string SrcPart = (IsSrcPtr ? Type : "") + Line[2];
string RandRegister = GetRandomRegister( Line[2] );
Result.push_back( "push " + RandRegister );
Result.push_back( "mov " + RandRegister + ",dword[esp+4]" );
Result.push_back( "mov " + SrcPart + "," + RandRegister );
Result.push_back( "pop " + RandRegister );
Result.push_back( "add esp, 4 " );
}
else
{
// not an integer and not a poitner == register
Result.push_back( "mov " + Line[2] + ",dword[esp]" );
Result.push_back( "add esp, 4" );
}
}
catch( ... )
{
}
return Result;
}
vector<string> cMutator::MutatePush( const vector< string > &Assembler )
{
vector< string > Result;
vector< string > Line;
bool IsSrcPtr = false;
string Type = "";
split( Line, Assembler[ _Index ], is_any_of(" ,"), token_compress_on );
if( Line.size() > 3 )
{
if( Line[3][0] == '[' )
{
Type = Line[2];
IsSrcPtr = true;
Line.erase( Line.begin()+2 );
}
}
// 0:lbl_xxx 1:push 2:src
try
{
if( rand() % 2 )
{
int ToAchieve = boost::lexical_cast< int > ( Line[2] );
if( rand() % 2 )
{
// use +;
int rInt1 = rand() % ToAchieve;
int rInt2 = ToAchieve - rInt1;
string DestPart = "dword[esp],";
string SrcPart = (IsSrcPtr ? Type : "") + boost::lexical_cast< string > ( rInt1 );
string SrcPart2 = (IsSrcPtr ? Type : "") + boost::lexical_cast< string > ( rInt2 );
Result.push_back( "sub esp, 4" );
Result.push_back( "mov " + DestPart + SrcPart );
Result.push_back( "add " + DestPart + SrcPart2 );
}
else
{
// use -
int rInt1 = rand() % ToAchieve;
int rInt2 = ToAchieve + rInt1;
string DestPart = "dword[esp],";
string SrcPart = (IsSrcPtr ? Type : "") + boost::lexical_cast< string > ( rInt1 );
string SrcPart2 = (IsSrcPtr ? Type : "") + boost::lexical_cast< string > ( rInt2 );
Result.push_back( "sub esp, 4" );
Result.push_back( "mov " + DestPart + SrcPart2 );
Result.push_back( "sub " + DestPart + SrcPart );
}
}
else
{
if( IsSrcPtr )
{
string SrcPart = (IsSrcPtr ? Type : "") + Line[2];
Result.push_back( "sub esp, 4" );
Result.push_back( "push " + SrcPart );
Result.push_back( "pop dword[esp]" );
}
else
{
// not an integer and not a poitner == register
Result.push_back( "sub esp, 4" );
Result.push_back( "mov dword[esp], " + Line[2] );
}
}
}
catch( ... )
{
if( IsSrcPtr )
{
string SrcPart = (IsSrcPtr ? Type : "") + Line[2];
Result.push_back( "sub esp, 4" );
Result.push_back( "push " + SrcPart );
Result.push_back( "pop dword[esp]" );
}
else
{
// not an integer and not a poitner == register
Result.push_back( "sub esp, 4" );
Result.push_back( "mov dword[esp], " + Line[2] );
}
}
return Result;
}
vector<string> cMutator::MutateMove( const vector< string > &Assembler )
{
vector< string > Result;
vector< string > Line;
bool IsDestPtr = false;
bool IsSrcPtr = false;
string Type = "";
split( Line, Assembler[ _Index ], is_any_of(" ,"), token_compress_on );
if( Line.size() > 4 )
{
if( Line[3][0] == '[' )
{
Type = Line[2];
IsDestPtr = true;
Line.erase( Line.begin()+2 );
}
else if( Line[4][0] == '[' )
{
Type = Line[3];
IsSrcPtr = true;
Line.erase( Line.begin()+3 );
}
}
// 0:lbl_xxx 1:mov 2:dest 3:src == line
try
{
if( rand() % 2 )
{
int ToAchieve = boost::lexical_cast< int > ( Line[3] );
if( rand() % 2 )
{
// use +;
int rInt1 = rand() % ToAchieve;
int rInt2 = ToAchieve - rInt1;
string DestPart = (IsDestPtr ? Type : "") + Line[2] + ",";
string SrcPart = (IsSrcPtr ? Type : "") + boost::lexical_cast< string > ( rInt1 );
Result.push_back( "mov " + DestPart + SrcPart );
Result.push_back( "add " + DestPart + SrcPart );
}
else
{
// use -
int rInt1 = rand() % ToAchieve;
int rInt2 = ToAchieve + rInt1;
string DestPart = (IsDestPtr ? Type : "") + Line[2] + ",";
string SrcPart = (IsSrcPtr ? Type : "") + boost::lexical_cast< string > ( rInt2 );
string SrcPart2 = (IsSrcPtr ? Type : "") + boost::lexical_cast< string > ( rInt1 );
Result.push_back( "mov " + DestPart + SrcPart );
Result.push_back( "sub " + DestPart + SrcPart2 );
}
}
else
{
string SrcPart = (IsSrcPtr ? Type : "") + Line[3];
string DestPart = (IsDestPtr ? Type : "") + Line[2];
if( IsDestPtr && Type == "byte" )
return Assembler;
Result.push_back( "push " + SrcPart);
Result.push_back( "pop " + DestPart);
if( IsDestPtr && Type == "word" )
Result.push_back( "add esp, 2" );
}
}
catch( ... )
{
string SrcPart = (IsSrcPtr ? Type : "") + Line[3];
string DestPart = (IsDestPtr ? Type : "") + Line[2];
if( IsDestPtr && Type == "byte" )
return Assembler;
Result.push_back( "push " + SrcPart);
Result.push_back( "pop " + DestPart);
if( IsDestPtr && Type == "word" )
Result.push_back( "add esp, 2" );
}
return Result;
}
void cMutator::Regswap( vector< string > &SectionDisassembly, const string &Reg1, const string &Reg2 )
{
unsigned int Location = 0;
unsigned int PrevLocation = 0;
unsigned int NumChanges = 0;
for( unsigned int i = 0; i < SectionDisassembly.size(); i++ )
{
NumChanges = 0;
Location = 0;
string CurrLine = SectionDisassembly[ i ];
do
{
PrevLocation = Location;
Location = CurrLine.find( Reg1, Location );
if( Location != std::string::npos )
{
CurrLine.replace( Location, Reg2.size(), Reg2 );
if( CurrLine.find( Reg2 ) == Location )
Location += Reg2.size();
else
Location = 0;
NumChanges++;
}
else
{
Location = PrevLocation;
Location = CurrLine.find( Reg2, Location );
if( Location != std::string::npos )
{
CurrLine.replace( Location, Reg1.size(), Reg1 );
Location += Reg1.size();
NumChanges++;
}
}
}
while( Location != std::string::npos && NumChanges <= 1 );
SectionDisassembly[ i ] = CurrLine;
}
}
void cMutator::Mutate( const vector< string > &Assembler, vector< string > &Result, vector< vector< string > > &Appendages )
{
vector< string > NormalisedAssembler = Assembler;
int ApendageOffset = 0;
const string Registers[]
=
{
"eax",
"ecx",
"edx",
"ebx",
"esi",
"edi"
};
if( rand() % _MutationIndex )
{
int Res = rand();
string Reg1 = Registers[ Res % 5 ];
string Reg2 = Registers[ (Res + 1 * rand()) % 5 ];
Regswap( NormalisedAssembler, Reg1, Reg2 );
NormalisedAssembler.insert( NormalisedAssembler.begin(), "xchg " + Reg1 + "," + Reg2 );
NormalisedAssembler.insert( NormalisedAssembler.end(), "xchg " + Reg1 + "," + Reg2 );
}
for( _Index = 0; _Index < NormalisedAssembler.size(); _Index++ )
{
if( NormalisedAssembler[_Index].find( "mov" ) != std::string::npos && rand() % _MutationIndex )
{
Appendages.push_back( MutateMove( NormalisedAssembler ) );
Result.push_back( boost::lexical_cast< string > ( ApendageOffset++ ) );
}
else if( NormalisedAssembler[_Index].find( "push" ) != std::string::npos && rand() % _MutationIndex )
{
Appendages.push_back( MutatePush( NormalisedAssembler ) );
Result.push_back( boost::lexical_cast< string > ( ApendageOffset++ ) );
}
else if( NormalisedAssembler[_Index].find( "pop" ) != std::string::npos && rand() % _MutationIndex )
{
Appendages.push_back( MutatePop( NormalisedAssembler ) );
Result.push_back( boost::lexical_cast< string > ( ApendageOffset++ ) );
}
else
{
Result.push_back( NormalisedAssembler[ _Index ] );
}
}
}
string cMutator::VectorToFasmSource( const vector< string > &Source )
{
string Result;
for( unsigned int i = 0; i < Source.size(); i++ )
{
Result += Source[i] + "\r\n";
}
return Result;
}
cPE::cPE( const string& Filename )
: _Filename( Filename )
{
fstream f( _Filename, ios::in | ios::binary );
if( f.good() )
{
f.seekg (0, f.end);
_Size = f.tellg();
f.seekg (0, f.beg);
_File = new unsigned char [_Size];
f.read ((char*)_File, _Size);
}
f.close();
}
cPE::~cPE()
{
if( _File )
delete [] _File;
_File = NULL;
}
DWORD cPE::GetVirtualAddress( DWORD Offset )
{
for( size_t i = 0; i < nt_headers->FileHeader.NumberOfSections; i++ )
{
PIMAGE_SECTION_HEADER CurrentSection = IMAGE_FIRST_SECTION(nt_headers) + i;
if( CurrentSection->PointerToRawData <= Offset && Offset < CurrentSection->PointerToRawData + CurrentSection->SizeOfRawData )
{
return ( Offset - CurrentSection->PointerToRawData ) + CurrentSection->VirtualAddress + nt_headers->OptionalHeader.ImageBase;
}
}
return NULL;
}
DWORD cPE::AlignToBoundary(DWORD size, DWORD alignment)
{
DWORD alignedSize;
if(size % alignment == 0)
alignedSize = size;
else
{
alignedSize = size / alignment;
++alignedSize;
alignedSize = alignedSize * alignment;
}
return alignedSize;
}
PIMAGE_SECTION_HEADER cPE::AddSection(const char *section_name, size_t section_size, PIMAGE_DOS_HEADER pImageAddress, size_t &NewSize)
{
dos_header = pImageAddress;
if(dos_header->e_magic != 0x5A4D)
{
throw "ERROR: cPE::AddSection: Could not retrieve DOS header from pImageAddress";
return NULL;
}
nt_headers = (PIMAGE_NT_HEADERS)((DWORD_PTR)dos_header + dos_header->e_lfanew);
if(nt_headers->OptionalHeader.Magic != 0x010B)
{
throw "ERROR: cPE::AddSection: Could not retrieve NT header from PIMAGE_DOS_HEADER";
return NULL;
}
const int name_max_length = 8;
PIMAGE_SECTION_HEADER last_section = IMAGE_FIRST_SECTION(nt_headers) + (nt_headers->FileHeader.NumberOfSections - 1);
PIMAGE_SECTION_HEADER new_section = IMAGE_FIRST_SECTION(nt_headers) + (nt_headers->FileHeader.NumberOfSections);
memset(new_section, 0, sizeof(IMAGE_SECTION_HEADER));
new_section->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_CNT_CODE;
memcpy(new_section->Name, section_name, name_max_length);
new_section->Misc.VirtualSize = section_size;
new_section->PointerToRawData = AlignToBoundary(last_section->PointerToRawData + last_section->SizeOfRawData, nt_headers->OptionalHeader.FileAlignment);
new_section->SizeOfRawData = AlignToBoundary(section_size, nt_headers->OptionalHeader.SectionAlignment);
new_section->VirtualAddress = AlignToBoundary(last_section->VirtualAddress + last_section->Misc.VirtualSize, nt_headers->OptionalHeader.SectionAlignment);
NewSize = new_section->SizeOfRawData + ( new_section->PointerToRawData - last_section->PointerToRawData );
nt_headers->OptionalHeader.SizeOfImage = new_section->VirtualAddress + new_section->Misc.VirtualSize;
nt_headers->FileHeader.NumberOfSections++;
return new_section;
}
void cPE::ReloadHeaders()
{
dos_header = (PIMAGE_DOS_HEADER)_File;
if(dos_header->e_magic != 0x5A4D)
{
throw "ERROR: cPE::AddSection: Could not retrieve DOS header from pImageAddress";
}
nt_headers = (PIMAGE_NT_HEADERS)((DWORD_PTR)dos_header + dos_header->e_lfanew);
if(nt_headers->OptionalHeader.Magic != 0x010B)
{
throw "ERROR: cPE::AddSection: Could not retrieve NT header from PIMAGE_DOS_HEADER";
}
NewSection = IMAGE_FIRST_SECTION(nt_headers) + (nt_headers->FileHeader.NumberOfSections-1);
}
void cPE::SaveFile()
{
fstream f( _Filename +"-new.exe" , ios::out | ios::binary );
if( f.good() )
{
f.write( (char*)_File, _Size );
}
else
{
throw "ERROR: cPE::SaveFile: Failed to save new file";
}
f.close();
}
PIMAGE_SECTION_HEADER cPE::AddNewSection( unsigned int Size )
{
size_t NewSize;
NewSection = AddSection( " ", Size, (PIMAGE_DOS_HEADER)_File, NewSize );
if( NewSection == NULL )
throw "ERROR: cPE::AddNewSection: Failed to add new section!";
unsigned char* Temp = _File;
_File = new unsigned char[ (_Size+NewSize) ];
memset( _File, 0, (_Size+NewSize) );
copy( Temp, Temp+_Size, _File );
ReloadHeaders();
delete [] Temp;
_Size += NewSize;
SaveFile();
return NewSection;
}
PIMAGE_SECTION_HEADER cPE::GetExecutableSection( size_t Index )
{
PIMAGE_SECTION_HEADER Result = NULL;
size_t SectionsFound = 0;
for( size_t i = 0; i < nt_headers->FileHeader.NumberOfSections; i++ )
{
PIMAGE_SECTION_HEADER CurrentSection = IMAGE_FIRST_SECTION(nt_headers) + i;
if( ( CurrentSection->Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE) ) == (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE) )
{
SectionsFound++;
}
else
continue;
if( SectionsFound == Index+1 )
return CurrentSection;
}
return NULL;
}
void cPE::Write( size_t Offset, unsigned char* Data, unsigned int Size )
{
copy( Data, Data+Size, &_File[Offset] );
}
void cPE::WriteNop( size_t Offset, size_t Size )
{
fill_n( _File+Offset, Size, 0x90 );
}
void cPE::WriteJump( size_t OffsetFrom, size_t OffsetTo )
{
size_t vAddressFrom = GetVirtualAddress( OffsetFrom );
size_t vAddressTo = GetVirtualAddress( OffsetTo );
size_t Address = vAddressTo - vAddressFrom - 5;
_File[ OffsetFrom ] = 0xE9;
*(size_t*)&_File[ OffsetFrom + 1 ] = Address;
}
const unsigned char* cPE::GetFilePointer()
{
return _File;
}
main.cpp:
Code:
#include <iostream>
#include <string>
#include <boost\lexical_cast.hpp>
#include "cMutator.h"
using namespace std;
int main( int argc, char* argv[] )
{
size_t SizeLeft = 0x4000, Offset = 0, Index = 0, BufferSize = 100000;
cDisassembler Disassembler;
cMutator Mutator;
cFasmWrapper Reassembler;
cPE c("target.exe");
PIMAGE_SECTION_HEADER pNewSection = c.AddNewSection( SizeLeft );
PIMAGE_SECTION_HEADER pCurrentSection = NULL;
vector< pair< size_t, string > > Disassembly;
vector< string > Result;
vector< vector< string > > Appendages;
while( ( pCurrentSection = c.GetExecutableSection( Index++ ) )/* && pCurrentSection != pNewSection */ )
{
Disassembly.clear();
Result.clear();
Appendages.clear();
Disassembler.Disassmble( (const unsigned char*) c.GetFilePointer()+ pCurrentSection->PointerToRawData, pCurrentSection->SizeOfRawData, Disassembly );
for( size_t y = 0; y < 1/*rand() % 128*/; y++ )
{
vector< string > PartDissassembly;
bool Success = false;
size_t RandBase, RandSize = 0;
do
{
Success = true;
PartDissassembly.clear();
RandBase = 0; //rand() % (Disassembly.size() - 10);
RandSize = 5; //rand() % ( 10 - 6 ) + 5;
for( size_t Lines = 0; Lines < RandSize; Lines++ )
{
if( !RandSize
|| Disassembly[ RandBase + Lines ].second.find( "j" ) != std::string::npos
|| Disassembly[ RandBase + Lines ].second.find( "call" ) != std::string::npos
|| Disassembly[ RandBase + Lines ].second.find( "ret" ) != std::string::npos
)
{
Success = false;
break;
}
PartDissassembly.push_back( Disassembly[ RandBase + Lines ].second );
}
} while( !Success );
c.WriteJump( Disassembly[ RandBase ].first - (size_t)c.GetFilePointer(), pNewSection->PointerToRawData + Offset );
c.SaveFile();
Mutator.Mutate( PartDissassembly, Result, Appendages );
unsigned char* Buffer = new unsigned char[ BufferSize ];
for( size_t i = 0; i < Result.size(); i++ )
{
memset( Buffer, 0x90, BufferSize );
string Source;
try
{
int AppendageIndex = boost::lexical_cast< int > ( Result[i] );
Source = "use32\n" + Mutator.VectorToFasmSource( Appendages[ AppendageIndex ] );
}
catch( boost::bad_lexical_cast )
{
Source = "use32\n" + Result[i];
}
if( Reassembler.Assemble( Source, Buffer, BufferSize ) )
{
FASM_STATE* Ptr = (FASM_STATE*)Buffer;
size_t NewCodeSize = Ptr->OutputLength;
c.Write( pNewSection->PointerToRawData + Offset, (unsigned char*)Ptr->pOutput, NewCodeSize );
Offset += NewCodeSize;
SizeLeft -= NewCodeSize;
}
else
{
cout << "ERROR: main: Failed to assemble: " << Source << endl;
}
}
delete [] Buffer;
size_t NopSize = Disassembly[ RandBase + RandSize ].first - Disassembly[ RandBase ].first;
c.WriteJump( pNewSection->PointerToRawData + Offset, Disassembly[ RandBase ].first - (size_t)c.GetFilePointer() + NopSize );
Offset += 5;
SizeLeft -= 5;
c.SaveFile();
}
}
c.SaveFile();
return 0;
}