// http://www.exploit-monday.com/2013/08/writing-optimized-windows-shellcode-in-c.html private static byte[] CompileToShellcode(TargetArch targetArch, IShellcodeCCxxSourceIParameterlessCFunction ccxxSource, IEnumerable <ObjectFile> additionalShellcodeObjectFiles, bool optimize) { // __chkstk will be linked if large portion of the stack used. Still POC if we statically link crt additionalShellcodeObjectFiles = additionalShellcodeObjectFiles ?? new List <ObjectFile>(); var source = new CCxxSource(((IShellcodeCCxxSource)ccxxSource).SourceFiles); var shellcodeEntryFunction = ccxxSource.Name; // Compile Compiler.Config compilerConfig; if (optimize) { compilerConfig = new Compiler.Config( GL: true, O1: true, Os: true, // Optimizations that differ O2: false, Og: false, Ot: false, Od: false, Oy: true, Oi: true, // Optimizations that are the same GF: true, Gy: true, GS: false, sdl: null, Zl: false, FA: true, runtime: Compiler.Config.RunTimeLibrary.MT, // Necessary for PIE callingConvention: null, EHa: null, EHs: null, EHc: null, EHr: null // unsure of the effect of these, so leaving as unspecified ); } else { compilerConfig = new Compiler.Config( GL: false, O1: false, Os: false, // Optimizations that differ O2: false, Og: false, Ot: false, Od: false, Oy: true, Oi: true, // Optimizations that are the same GF: true, Gy: true, GS: false, sdl: null, Zl: false, FA: true, runtime: Compiler.Config.RunTimeLibrary.MT, // Necessary for PIE callingConvention: null, EHa: null, EHs: null, EHc: null, EHr: null // unsure of the effect of these, so leaving as unspecified ); } Linker.Config linkerConfig; if (optimize) { linkerConfig = new Linker.Config( LTCG: Linker.Config.Ltcg.ON, // Optimizations that differ OPTREF: true, OPTICF: 1, // Optimizations that are the same ENTRY: "Begin", SUBSYSTEM: Linker.Config.Subsystem.CONSOLE, MAP: true, SAFESEH: false, NODEFAULTLIB: true, ORDER: new List <string>() { "Begin", shellcodeEntryFunction }, // Necessary for PIE additionalDependencies: new List <string> { "libucrt.lib", "libvcruntime.lib", "libcmt.lib" } ); } else { linkerConfig = new Linker.Config( LTCG: Linker.Config.Ltcg.OFF, // Optimizations that differ OPTREF: true, OPTICF: 1, // Optimizations that are the same ENTRY: "Begin", SUBSYSTEM: Linker.Config.Subsystem.CONSOLE, MAP: true, SAFESEH: false, NODEFAULTLIB: true, ORDER: new List <string>() { "Begin", shellcodeEntryFunction }, // Necessary for PIE additionalDependencies: new List <string> { "libucrt.lib", "libvcruntime.lib", "libcmt.lib" } ); } Executable exe; switch (targetArch) { case TargetArch.x64: { var additionalShellcodeObjectFiles64 = new StaticLibrary <Win64ObjectFile>(additionalShellcodeObjectFiles.Cast <Win64ObjectFile>()); var staticLibrary = ((ICCxxCompiler <Win64ObjectFile>) new Compiler(compilerConfig)).Compile(source); // Since this is x64, we need to ensure that the stack is aligned before we execute our C/C++'s entry function var alignStackAsm = new AlignRSPMasmAssemblySource(shellcodeEntryFunction); var alignStackSL = ((IAssembler <MASM, Win64ObjectFile>) new Masm()).Assemble(alignStackAsm); var linkerInput = new List <StaticLibrary <Win64ObjectFile> >() { staticLibrary, additionalShellcodeObjectFiles64, alignStackSL }; exe = ((ILinker <Win64ObjectFile, Executable>) new Linker(linkerConfig)).Link(linkerInput); break; } case TargetArch.x86: { var additionalShellcodeObjectFiles32 = new StaticLibrary <Win32ObjectFile>(additionalShellcodeObjectFiles.Cast <Win32ObjectFile>()); source.FindAndReplace("BeginExecutePayload", shellcodeEntryFunction); var staticLibrary = ((ICCxxCompiler <Win32ObjectFile>) new Compiler(compilerConfig)).Compile(source); var linkerInput = new List <StaticLibrary <Win32ObjectFile> >() { staticLibrary, additionalShellcodeObjectFiles32 }; exe = ((ILinker <Win32ObjectFile, Executable>) new Linker(linkerConfig)).Link(linkerInput); break; } default: throw new ArgumentException("Invalid TargetArch valid"); } // Check if Position Independent Code was generate // Sometimes when building for 32-bit, char[] initialzied with an arrary of a certain length gets placed in the data section ... // Seems to be when the strlen >= 15 (>= 16 with null byte) // Possible solution, split up array into multiple smalle rarrarys? if (exe.Map.Split("Publics by Value")[1].Split("entry point")[0].Contains("0002:") || exe.Map.Split("Publics by Value")[1].Split("entry point")[0].Contains("0003:")) { Console.Error.Write(exe.Map); throw new Exception("Failed to generate shellcode: code dependent on data section. See Map file for more info"); } // 'Begin' is the entry point to our shellcode if (!exe.Map.Split("Publics by Value")[1].Split("0001:")[1].Split("\n")[0].Contains("Begin")) { Console.Error.Write(exe.Map); throw new Exception("Failed to generate shellcode: function 'Begin' is not the first function in the CODE section. See Map file for more info"); } // Extract shellcoode var shellcodeBytes = PeCode.Extract(exe); var codeLengthHexString = exe.Map.Split("CODE")[0].Split("H ")[0].Split(" ")[^ 1];
// Merges input source into this object public MutexSingletonShellcodeCCxxSource(IShellcodeCCxxSourceIParameterlessCFunction functionSource, string mutexName = @"Global\MutexSingleton") : base(MergeSourceFiles(MutexSingletonCCxxSource.CreateSource((IParameterlessCFunction)functionSource, mutexName), new List <ICCxxSource>() { functionSource })) { FindAndReplace(SourceFiles, FunctionNamePlaceholder, ((ICFunction)this).Name); }
// Merges input source into this object public CreateThreadShellcodeCCxxSource(IShellcodeCCxxSourceIParameterlessCFunction functionSource) : base(MergeSourceFiles(CreateThreadCCxxSource.CreateSource((IParameterlessCFunction)functionSource), new List <ICCxxSource>() { functionSource })) { FindAndReplace(SourceFiles, FunctionNamePlaceholder, ((ICFunction)this).Name); }