├── .gitignore ├── BOF ├── Makefile ├── test.c └── test64.out ├── COFFLoader.sln ├── COFFLoader.vcxproj ├── COFFLoader.vcxproj.filters ├── Images └── demo.gif ├── Include ├── beacon_compatibility.h ├── coff_loader.h └── utils.h ├── LICENSE ├── README.md └── Src ├── beacon_compatibility.c ├── coff_loader.cpp ├── main.cpp └── utils.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | -------------------------------------------------------------------------------- /BOF/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: bof 3 | 4 | bof: 5 | x86_64-w64-mingw32-gcc -c test.c -o test64.out -------------------------------------------------------------------------------- /BOF/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "beacon.h" 6 | 7 | DECLSPEC_IMPORT DWORD WINAPI KERNEL32$GetComputerNameA(LPSTR, LPDWORD); 8 | WINBASEAPI int __cdecl MSVCRT$printf(const char * __restrict__ _Format,...); 9 | WINBASEAPI int __cdecl MSVCRT$wcscpy(wchar_t* dest, const wchar_t* src); 10 | 11 | char* TestGlobalString = "This is a global string"; 12 | /* Can't do stuff like "int testvalue;" in a coff file, because it assumes that 13 | * the symbol is like any function, so you would need to allocate a section of bss 14 | * (without knowing the size of it), and then resolve the symbol to that. So safer 15 | * to just not support that */ 16 | int testvalue = 0; 17 | 18 | int function1(void){ 19 | MSVCRT$printf("Test String from function1\n"); 20 | testvalue = 1; 21 | return 0; 22 | } 23 | 24 | int function2(void){ 25 | MSVCRT$printf("Test String from function2\n"); 26 | return 0; 27 | } 28 | 29 | 30 | void go(char * args, unsigned long alen) { 31 | BOOL compNameStatus = 0; 32 | PDOMAIN_CONTROLLER_INFO pdcInfo; 33 | BeaconPrintf(1, "Global CHAR* \"%s\"\n", TestGlobalString); 34 | MSVCRT$printf("Global Test INT: %d\n", testvalue); 35 | (void)function1(); 36 | MSVCRT$printf("Test ValueBack: %d\n", testvalue); 37 | 38 | datap parser; 39 | BeaconDataParse(&parser, args, alen); 40 | /* Arguments should be unpacked in the same order they were packed */ 41 | int number = BeaconDataShort(&parser); 42 | 43 | BeaconPrintf(CALLBACK_OUTPUT, "Number passed: %i\n", number); 44 | (void)function2(); 45 | 46 | char computerName[MAX_COMPUTERNAME_LENGTH + 1]; 47 | DWORD size = MAX_COMPUTERNAME_LENGTH + 1; 48 | compNameStatus = KERNEL32$GetComputerNameA(computerName, &size); 49 | if (compNameStatus) { 50 | BeaconPrintf(1,"The computer name is: %s\n", computerName); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /BOF/test64.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ap3x/COFF-Loader/44a8ea807bbb981a42ea53db24e07ccb3257158c/BOF/test64.out -------------------------------------------------------------------------------- /COFFLoader.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32228.430 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "COFFLoader", "COFFLoader.vcxproj", "{6F5E83F5-7263-4F8B-88FF-F6667685D2B2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {6F5E83F5-7263-4F8B-88FF-F6667685D2B2}.Debug|x64.ActiveCfg = Debug|x64 17 | {6F5E83F5-7263-4F8B-88FF-F6667685D2B2}.Debug|x64.Build.0 = Debug|x64 18 | {6F5E83F5-7263-4F8B-88FF-F6667685D2B2}.Debug|x86.ActiveCfg = Debug|Win32 19 | {6F5E83F5-7263-4F8B-88FF-F6667685D2B2}.Debug|x86.Build.0 = Debug|Win32 20 | {6F5E83F5-7263-4F8B-88FF-F6667685D2B2}.Release|x64.ActiveCfg = Release|x64 21 | {6F5E83F5-7263-4F8B-88FF-F6667685D2B2}.Release|x64.Build.0 = Release|x64 22 | {6F5E83F5-7263-4F8B-88FF-F6667685D2B2}.Release|x86.ActiveCfg = Release|Win32 23 | {6F5E83F5-7263-4F8B-88FF-F6667685D2B2}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {F3C909F8-9595-4E74-B701-75D3A5BCFAE7} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /COFFLoader.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {6f5e83f5-7263-4f8b-88ff-f6667685d2b2} 25 | CustomCOFFLoader 26 | 10.0 27 | COFFLoader 28 | 29 | 30 | 31 | Application 32 | true 33 | v143 34 | Unicode 35 | 36 | 37 | Application 38 | false 39 | v143 40 | true 41 | Unicode 42 | 43 | 44 | Application 45 | true 46 | v143 47 | Unicode 48 | 49 | 50 | Application 51 | false 52 | v143 53 | true 54 | Unicode 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | true 76 | 77 | 78 | false 79 | 80 | 81 | true 82 | $(SolutionDir)Bin 83 | 84 | 85 | false 86 | $(SolutionDir)Bin 87 | 88 | 89 | 90 | Level3 91 | true 92 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 93 | true 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | Level3 103 | true 104 | true 105 | true 106 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | true 108 | 109 | 110 | Console 111 | true 112 | true 113 | true 114 | 115 | 116 | 117 | 118 | Level3 119 | true 120 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 121 | true 122 | 123 | 124 | Console 125 | true 126 | 127 | 128 | 129 | 130 | Level3 131 | true 132 | true 133 | true 134 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 135 | true 136 | 137 | 138 | Console 139 | true 140 | true 141 | true 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /COFFLoader.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | 32 | 33 | Header Files 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | -------------------------------------------------------------------------------- /Images/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ap3x/COFF-Loader/44a8ea807bbb981a42ea53db24e07ccb3257158c/Images/demo.gif -------------------------------------------------------------------------------- /Include/beacon_compatibility.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Cobalt Strike 4.X BOF compatibility layer 3 | * ----------------------------------------- 4 | * The whole point of these files are to allow beacon object files built for CS 5 | * to run fine inside of other tools without recompiling. 6 | * 7 | * Built off of the beacon.h file provided to build for CS. 8 | */ 9 | #ifndef BEACON_COMPATIBILITY_H_ 10 | /* Structures as is in beacon.h */ 11 | extern unsigned char* InternalFunctions[30][2]; 12 | 13 | typedef struct { 14 | char* original; /* the original buffer [so we can free it] */ 15 | char* buffer; /* current pointer into our buffer */ 16 | int length; /* remaining length of data */ 17 | int size; /* total size of this buffer */ 18 | } datap; 19 | 20 | typedef struct { 21 | char* original; /* the original buffer [so we can free it] */ 22 | char* buffer; /* current pointer into our buffer */ 23 | int length; /* remaining length of data */ 24 | int size; /* total size of this buffer */ 25 | } formatp; 26 | 27 | void BeaconDataParse(datap* parser, char* buffer, int size); 28 | int BeaconDataInt(datap* parser); 29 | short BeaconDataShort(datap* parser); 30 | int BeaconDataLength(datap* parser); 31 | char* BeaconDataExtract(datap* parser, int* size); 32 | 33 | void BeaconFormatAlloc(formatp* format, int maxsz); 34 | void BeaconFormatReset(formatp* format); 35 | void BeaconFormatFree(formatp* format); 36 | void BeaconFormatAppend(formatp* format, char* text, int len); 37 | void BeaconFormatPrintf(formatp* format, char* fmt, ...); 38 | char* BeaconFormatToString(formatp* format, int* size); 39 | void BeaconFormatInt(formatp* format, int value); 40 | 41 | #define CALLBACK_OUTPUT 0x0 42 | #define CALLBACK_OUTPUT_OEM 0x1e 43 | #define CALLBACK_ERROR 0x0d 44 | #define CALLBACK_OUTPUT_UTF8 0x20 45 | 46 | 47 | void BeaconPrintf(int type, char* fmt, ...); 48 | void BeaconOutput(int type, char* data, int len); 49 | 50 | /* Token Functions */ 51 | BOOL BeaconUseToken(HANDLE token); 52 | void BeaconRevertToken(); 53 | BOOL BeaconIsAdmin(); 54 | 55 | /* Spawn+Inject Functions */ 56 | void BeaconGetSpawnTo(BOOL x86, char* buffer, int length); 57 | BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* sInfo, PROCESS_INFORMATION* pInfo); 58 | void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len); 59 | void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len); 60 | void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo); 61 | 62 | /* Utility Functions */ 63 | BOOL toWideChar(char* src, wchar_t* dst, int max); 64 | uint32_t swap_endianess(uint32_t indata); 65 | 66 | char* BeaconGetOutputData(int* outsize); 67 | #endif -------------------------------------------------------------------------------- /Include/coff_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | extern "C" { 7 | #include "beacon_compatibility.h" 8 | } 9 | 10 | #ifdef _DEBUG 11 | #define DEBUG_PRINT(x, ...) printf(x, ##__VA_ARGS__) 12 | #else 13 | #define DEBUG_PRINT(x, ...) 14 | #endif 15 | 16 | #if defined(__x86_64__) || defined(_WIN64) 17 | #define PREPENDSYMBOLVALUE "__imp_" 18 | #else 19 | #define PREPENDSYMBOLVALUE "__imp__" 20 | #endif 21 | 22 | #define STR_EQUALS(str, substr) (strncmp(str, substr, strlen(substr)) == 0) 23 | 24 | //https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-file-header-object-and-image 25 | typedef struct coff_file_header { 26 | uint16_t Machine; 27 | uint16_t NumberOfSections; 28 | uint32_t TimeDateStamp; 29 | uint32_t PointerToSymbolTable; 30 | uint32_t NumberOfSymbols; 31 | uint16_t SizeOfOptionalHeader; 32 | uint16_t Characteristics; 33 | } FileHeader_t; 34 | 35 | #pragma pack(push,1) 36 | 37 | //https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers 38 | typedef struct coff_sections_table { 39 | char Name[8]; 40 | uint32_t VirtualSize; 41 | uint32_t VirtualAddress; 42 | uint32_t SizeOfRawData; 43 | uint32_t PointerToRawData; 44 | uint32_t PointerToRelocations; 45 | uint32_t PointerToLineNumbers; 46 | uint16_t NumberOfRelocations; 47 | uint16_t NumberOfLinenumbers; 48 | uint32_t Characteristics; 49 | } Section_t; 50 | 51 | //https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-relocations-object-only 52 | typedef struct coff_relocations { 53 | uint32_t VirtualAddress; 54 | uint32_t SymbolTableIndex; 55 | uint16_t Type; 56 | } Relocation_t; 57 | 58 | //https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#coff-symbol-table 59 | typedef struct coff_symbols_table { 60 | union { 61 | char Name[8]; 62 | uint32_t value[2]; 63 | } first; 64 | uint32_t Value; 65 | uint16_t SectionNumber; 66 | uint16_t Type; 67 | uint8_t StorageClass; 68 | uint8_t NumberOfAuxSymbols; 69 | } Symbol_t; 70 | 71 | 72 | typedef struct COFF { 73 | char* FileBase; 74 | FileHeader_t* FileHeader; 75 | Relocation_t* Relocation; 76 | Symbol_t* SymbolTable; 77 | 78 | void* RawTextData; 79 | char* RelocationsTextPTR; 80 | int RelocationsCount; 81 | int FunctionMappingCount; 82 | } COFF_t; 83 | 84 | bool RunCOFF(char* FileData, DWORD* DataSize, char* EntryName, char* ArgumentData, unsigned long ArgumentSize); 85 | -------------------------------------------------------------------------------- /Include/utils.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef _DEBUG 7 | #define DEBUG_PRINT(x, ...) printf(x, ##__VA_ARGS__) 8 | #else 9 | #define DEBUG_PRINT(x, ...) 10 | #endif 11 | 12 | typedef struct _Arg { 13 | char* value; 14 | size_t size; 15 | BOOL includeSize; 16 | } Arg; 17 | 18 | 19 | void PrintBanner(); 20 | void PrintUsage(char* ExecutableName); 21 | void PackData(Arg* args, size_t numberOfArgs, char** output, size_t* size); 22 | char* ReadFile(char* FilePath, DWORD* FileSize); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ap3x 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # COFF-Loader 2 | 3 | This is a reimplementation of [TrustedSec COFF Loader](https://github.com/trustedsec/COFFLoader). I decided to create this repo to challenge my understanding of the Windows PE Format. This technique was originally used in [Cobalt Strike](https://www.cobaltstrike.com/). This project utilizes Visual Studio 2022 for those who wish to employ the VS Debugger and trace the execution of its memory operations loader. 4 | 5 | While coding this I mainly used the following resources: 6 | - [Microsoft PE Format](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format) 7 | - [Otterhacker's COFF Loader Article](https://otterhacker.github.io/Malware/CoffLoader.html) 8 | 9 | ## Usage 10 | 11 | ``` 12 | COFFLoader.exe 13 | ``` 14 | 15 | The `function name` is the name BOF function entry name. This is typically `"go"` 16 | 17 | Example: 18 | ```shell 19 | COFFLoader.exe "go" C:\Github\COFF-Loader\BOF\test64.out 20 | ``` 21 | 22 | ### BOF Arguments 23 | In order to pass arguments to the BOF I used the exact same code that [Otterhacker's COFF Loader](https://github.com/OtterHacker/CoffLoader) uses. The struct is as follows: 24 | 25 | ```C 26 | typedef struct _Arg { 27 | char* value; 28 | size_t size; 29 | BOOL includeSize; 30 | } Arg; 31 | ``` 32 | 33 | You can see an example of this used [here](./Src/main.cpp) on line 25. 34 | 35 | ## Example 36 | 37 | In the example below I used the BOF file that I modified from [TrustedSec BOF example](https://github.com/trustedsec/COFFLoader/blob/main/test.c). This simple BOF shows that the COFF loader was able to successfully was able to relocate all the symbols in different formats. Included in this I included the modified BOF in `BOF/test.c` directory. You can use the make file to compile it with gcc or run the following command: 38 | 39 | ```shell 40 | x86_64-w64-mingw32-gcc -c test.c -o test64.out 41 | ``` 42 | 43 | ![demo](Images/demo.gif) 44 | 45 | ### References 46 | - [Microsoft PE Format](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format) 47 | - THIS IS AN AMAZING BLOG WRITE UP --> [Otterhacker COFF Loader Article](https://otterhacker.github.io/Malware/CoffLoader.html) 48 | - [TrustedSec COFFLoader](https://github.com/trustedsec/COFFLoader) 49 | - [TrustedSec Situational Awareness BOF Repo](https://github.com/trustedsec/CS-Situational-Awareness-BOF) 50 | - [Otterhacker COFF Loader Repo](https://github.com/OtterHacker/CoffLoader) 51 | - [Cobalt Strike BOF C API](https://hstechdocs.helpsystems.com/manuals/cobaltstrike/current/userguide/content/topics/beacon-object-files_bof-c-api.htm) 52 | - [Video to GIF](https://ezgif.com/video-to-gif) 53 | 54 | -------------------------------------------------------------------------------- /Src/beacon_compatibility.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Cobalt Strike 4.X BOF compatibility layer 3 | * ----------------------------------------- 4 | * The whole point of these files are to allow beacon object files built for CS 5 | * to run fine inside of other tools without recompiling. 6 | * 7 | * Built off of the beacon.h file provided to build for CS. 8 | */ 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef _WIN32 15 | #include 16 | 17 | #include "../Include/beacon_compatibility.h" 18 | 19 | #define DEFAULTPROCESSNAME "rundll32.exe" 20 | #ifdef _WIN64 21 | #define X86PATH "SysWOW64" 22 | #define X64PATH "System32" 23 | #else 24 | #define X86PATH "System32" 25 | #define X64PATH "sysnative" 26 | #endif 27 | 28 | 29 | /* Data Parsing */ 30 | unsigned char* InternalFunctions[30][2] = { 31 | {(unsigned char*)"BeaconDataParse", (unsigned char*)BeaconDataParse}, 32 | {(unsigned char*)"BeaconDataInt", (unsigned char*)BeaconDataInt}, 33 | {(unsigned char*)"BeaconDataShort", (unsigned char*)BeaconDataShort}, 34 | {(unsigned char*)"BeaconDataLength", (unsigned char*)BeaconDataLength}, 35 | {(unsigned char*)"BeaconDataExtract", (unsigned char*)BeaconDataExtract}, 36 | {(unsigned char*)"BeaconFormatAlloc", (unsigned char*)BeaconFormatAlloc}, 37 | {(unsigned char*)"BeaconFormatReset", (unsigned char*)BeaconFormatReset}, 38 | {(unsigned char*)"BeaconFormatFree", (unsigned char*)BeaconFormatFree}, 39 | {(unsigned char*)"BeaconFormatAppend", (unsigned char*)BeaconFormatAppend}, 40 | {(unsigned char*)"BeaconFormatPrintf", (unsigned char*)BeaconFormatPrintf}, 41 | {(unsigned char*)"BeaconFormatToString", (unsigned char*)BeaconFormatToString}, 42 | {(unsigned char*)"BeaconFormatInt", (unsigned char*)BeaconFormatInt}, 43 | {(unsigned char*)"BeaconPrintf", (unsigned char*)BeaconPrintf}, 44 | {(unsigned char*)"BeaconOutput", (unsigned char*)BeaconOutput}, 45 | {(unsigned char*)"BeaconUseToken", (unsigned char*)BeaconUseToken}, 46 | {(unsigned char*)"BeaconRevertToken", (unsigned char*)BeaconRevertToken}, 47 | {(unsigned char*)"BeaconIsAdmin", (unsigned char*)BeaconIsAdmin}, 48 | {(unsigned char*)"BeaconGetSpawnTo", (unsigned char*)BeaconGetSpawnTo}, 49 | {(unsigned char*)"BeaconSpawnTemporaryProcess", (unsigned char*)BeaconSpawnTemporaryProcess}, 50 | {(unsigned char*)"BeaconInjectProcess", (unsigned char*)BeaconInjectProcess}, 51 | {(unsigned char*)"BeaconInjectTemporaryProcess", (unsigned char*)BeaconInjectTemporaryProcess}, 52 | {(unsigned char*)"BeaconCleanupProcess", (unsigned char*)BeaconCleanupProcess}, 53 | {(unsigned char*)"toWideChar", (unsigned char*)toWideChar}, 54 | {(unsigned char*)"LoadLibraryA", (unsigned char*)LoadLibraryA}, 55 | {(unsigned char*)"GetProcAddress", (unsigned char*)GetProcAddress}, 56 | {(unsigned char*)"GetModuleHandleA", (unsigned char*)GetModuleHandleA}, 57 | {(unsigned char*)"FreeLibrary", (unsigned char*)FreeLibrary}, 58 | {(unsigned char*)"__C_specific_handler", NULL} 59 | 60 | }; 61 | 62 | uint32_t swap_endianess(uint32_t indata) { 63 | uint32_t testint = 0xaabbccdd; 64 | uint32_t outint = indata; 65 | if (((unsigned char*)&testint)[0] == 0xdd) { 66 | ((unsigned char*)&outint)[0] = ((unsigned char*)&indata)[3]; 67 | ((unsigned char*)&outint)[1] = ((unsigned char*)&indata)[2]; 68 | ((unsigned char*)&outint)[2] = ((unsigned char*)&indata)[1]; 69 | ((unsigned char*)&outint)[3] = ((unsigned char*)&indata)[0]; 70 | } 71 | return outint; 72 | } 73 | 74 | char* beacon_compatibility_output = NULL; 75 | int beacon_compatibility_size = 0; 76 | int beacon_compatibility_offset = 0; 77 | 78 | void BeaconDataParse(datap* parser, char* buffer, int size) { 79 | if (parser == NULL) { 80 | return; 81 | } 82 | parser->original = buffer; 83 | parser->buffer = buffer; 84 | parser->length = size - 4; 85 | parser->size = size - 4; 86 | parser->buffer += 4; 87 | return; 88 | } 89 | 90 | int BeaconDataInt(datap* parser) { 91 | int32_t fourbyteint = 0; 92 | if (parser->length < 4) { 93 | return 0; 94 | } 95 | memcpy(&fourbyteint, parser->buffer, 4); 96 | parser->buffer += 4; 97 | parser->length -= 4; 98 | return (int)fourbyteint; 99 | } 100 | 101 | short BeaconDataShort(datap* parser) { 102 | int16_t retvalue = 0; 103 | if (parser->length < 2) { 104 | return 0; 105 | } 106 | memcpy(&retvalue, parser->buffer, 2); 107 | parser->buffer += 2; 108 | parser->length -= 2; 109 | return (short)retvalue; 110 | } 111 | 112 | int BeaconDataLength(datap* parser) { 113 | return parser->length; 114 | } 115 | 116 | char* BeaconDataExtract(datap* parser, int* size) { 117 | uint32_t length = 0; 118 | char* outdata = NULL; 119 | /*Length prefixed binary blob, going to assume uint32_t for this.*/ 120 | if (parser->length < 4) { 121 | return NULL; 122 | } 123 | memcpy(&length, parser->buffer, 4); 124 | parser->buffer += 4; 125 | 126 | outdata = parser->buffer; 127 | if (outdata == NULL) { 128 | return NULL; 129 | } 130 | parser->length -= 4; 131 | parser->length -= length; 132 | parser->buffer += length; 133 | if (size != NULL && outdata != NULL) { 134 | *size = length; 135 | } 136 | return outdata; 137 | } 138 | 139 | /* format API */ 140 | 141 | void BeaconFormatAlloc(formatp* format, int maxsz) { 142 | if (format == NULL) { 143 | return; 144 | } 145 | format->original = calloc(maxsz, 1); 146 | format->buffer = format->original; 147 | format->length = 0; 148 | format->size = maxsz; 149 | return; 150 | } 151 | 152 | void BeaconFormatReset(formatp* format) { 153 | memset(format->original, 0, format->size); 154 | format->buffer = format->original; 155 | format->length = format->size; 156 | return; 157 | } 158 | 159 | void BeaconFormatFree(formatp* format) { 160 | if (format == NULL) { 161 | return; 162 | } 163 | if (format->original) { 164 | free(format->original); 165 | format->original = NULL; 166 | } 167 | format->buffer = NULL; 168 | format->length = 0; 169 | format->size = 0; 170 | return; 171 | } 172 | 173 | void BeaconFormatAppend(formatp* format, char* text, int len) { 174 | memcpy(format->buffer, text, len); 175 | format->buffer += len; 176 | format->length += len; 177 | return; 178 | } 179 | 180 | void BeaconFormatPrintf(formatp* format, char* fmt, ...) { 181 | /*Take format string, and sprintf it into here*/ 182 | va_list args; 183 | int length = 0; 184 | 185 | va_start(args, fmt); 186 | length = vsnprintf(NULL, 0, fmt, args); 187 | va_end(args); 188 | if (format->length + length > format->size) { 189 | return; 190 | } 191 | 192 | va_start(args, fmt); 193 | (void)vsnprintf(format->buffer, length, fmt, args); 194 | va_end(args); 195 | format->length += length; 196 | format->buffer += length; 197 | return; 198 | } 199 | 200 | 201 | char* BeaconFormatToString(formatp* format, int* size) { 202 | *size = format->length; 203 | return format->original; 204 | } 205 | 206 | void BeaconFormatInt(formatp* format, int value) { 207 | uint32_t indata = value; 208 | uint32_t outdata = 0; 209 | if (format->length + 4 > format->size) { 210 | return; 211 | } 212 | outdata = swap_endianess(indata); 213 | memcpy(format->buffer, &outdata, 4); 214 | format->length += 4; 215 | format->buffer += 4; 216 | return; 217 | } 218 | 219 | /* Main output functions */ 220 | 221 | void BeaconPrintf(int type, char* fmt, ...) { 222 | /* Change to maintain internal buffer, and return after done running. */ 223 | int length = 0; 224 | char* tempptr = NULL; 225 | va_list args; 226 | va_start(args, fmt); 227 | vprintf(fmt, args); 228 | va_end(args); 229 | 230 | va_start(args, fmt); 231 | length = vsnprintf(NULL, 0, fmt, args); 232 | va_end(args); 233 | tempptr = realloc(beacon_compatibility_output, beacon_compatibility_size + length + 1); 234 | if (tempptr == NULL) { 235 | return; 236 | } 237 | beacon_compatibility_output = tempptr; 238 | memset(beacon_compatibility_output + beacon_compatibility_offset, 0, length + 1); 239 | va_start(args, fmt); 240 | length = vsnprintf(beacon_compatibility_output + beacon_compatibility_offset, length + 1, fmt, args); 241 | beacon_compatibility_size += length; 242 | beacon_compatibility_offset += length; 243 | va_end(args); 244 | return; 245 | } 246 | 247 | void BeaconOutput(int type, char* data, int len) { 248 | char* tempptr = NULL; 249 | tempptr = realloc(beacon_compatibility_output, beacon_compatibility_size + len + 1); 250 | beacon_compatibility_output = tempptr; 251 | if (tempptr == NULL) { 252 | return; 253 | } 254 | memset(beacon_compatibility_output + beacon_compatibility_offset, 0, len + 1); 255 | memcpy(beacon_compatibility_output + beacon_compatibility_offset, data, len); 256 | beacon_compatibility_size += len; 257 | beacon_compatibility_offset += len; 258 | return; 259 | } 260 | 261 | /* Token Functions */ 262 | 263 | BOOL BeaconUseToken(HANDLE token) { 264 | /* Probably needs to handle DuplicateTokenEx too */ 265 | SetThreadToken(NULL, token); 266 | return TRUE; 267 | } 268 | 269 | void BeaconRevertToken(void) { 270 | if (!RevertToSelf()) { 271 | #ifdef DEBUG 272 | printf("RevertToSelf Failed!\n"); 273 | #endif 274 | } 275 | return; 276 | } 277 | 278 | BOOL BeaconIsAdmin(void) { 279 | /* Leaving this to be implemented by people needing it */ 280 | #ifdef DEBUG 281 | printf("BeaconIsAdmin Called\n"); 282 | #endif 283 | return FALSE; 284 | } 285 | 286 | /* Injection/spawning related stuffs 287 | * 288 | * These functions are basic place holders, and if implemented into something 289 | * real should be just calling internal functions for your tools. */ 290 | void BeaconGetSpawnTo(BOOL x86, char* buffer, int length) { 291 | char* tempBufferPath = NULL; 292 | if (buffer == NULL) { 293 | return; 294 | } 295 | if (x86) { 296 | tempBufferPath = "C:\\Windows\\"X86PATH"\\"DEFAULTPROCESSNAME; 297 | } 298 | else { 299 | tempBufferPath = "C:\\Windows\\"X64PATH"\\"DEFAULTPROCESSNAME; 300 | } 301 | 302 | if ((int)strlen(tempBufferPath) > length) { 303 | return; 304 | } 305 | memcpy(buffer, tempBufferPath, strlen(tempBufferPath)); 306 | return; 307 | } 308 | 309 | BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* sInfo, PROCESS_INFORMATION* pInfo) { 310 | BOOL bSuccess = FALSE; 311 | if (x86) { 312 | bSuccess = CreateProcessA(NULL, (char*)"C:\\Windows\\"X86PATH"\\"DEFAULTPROCESSNAME, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, sInfo, pInfo); 313 | } 314 | else { 315 | bSuccess = CreateProcessA(NULL, (char*)"C:\\Windows\\"X64PATH"\\"DEFAULTPROCESSNAME, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, sInfo, pInfo); 316 | } 317 | return bSuccess; 318 | } 319 | 320 | void BeaconInjectProcess(HANDLE hProc, int pid, char* payload, int p_len, int p_offset, char* arg, int a_len) { 321 | /* Leaving this to be implemented by people needing/wanting it */ 322 | return; 323 | } 324 | 325 | void BeaconInjectTemporaryProcess(PROCESS_INFORMATION* pInfo, char* payload, int p_len, int p_offset, char* arg, int a_len) { 326 | /* Leaving this to be implemented by people needing/wanting it */ 327 | return; 328 | } 329 | 330 | void BeaconCleanupProcess(PROCESS_INFORMATION* pInfo) { 331 | (void)CloseHandle(pInfo->hThread); 332 | (void)CloseHandle(pInfo->hProcess); 333 | return; 334 | } 335 | 336 | BOOL toWideChar(char* src, wchar_t* dst, int max) { 337 | if (max < sizeof(wchar_t)) 338 | return FALSE; 339 | return MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, src, -1, dst, max / sizeof(wchar_t)); 340 | } 341 | 342 | char* BeaconGetOutputData(int* outsize) { 343 | char* outdata = beacon_compatibility_output; 344 | *outsize = beacon_compatibility_size; 345 | beacon_compatibility_output = NULL; 346 | beacon_compatibility_size = 0; 347 | beacon_compatibility_offset = 0; 348 | return outdata; 349 | } 350 | 351 | #endif -------------------------------------------------------------------------------- /Src/coff_loader.cpp: -------------------------------------------------------------------------------- 1 | #include "../Include/coff_loader.h" 2 | 3 | bool InternalFunctionMatch(char* StrippedSymbolName) { 4 | if (STR_EQUALS(StrippedSymbolName, "Beacon") || 5 | STR_EQUALS(StrippedSymbolName, "GetProcAddress") || 6 | STR_EQUALS(StrippedSymbolName, "GetModuleHandleA") || 7 | STR_EQUALS(StrippedSymbolName, "toWideChar") || 8 | STR_EQUALS(StrippedSymbolName, "LoadLibraryA") || 9 | STR_EQUALS(StrippedSymbolName, "FreeLibrary")) 10 | { 11 | return true; 12 | } 13 | return false; 14 | } 15 | 16 | void* ProcessBeaconSymbols(char* SymbolName, bool InternalFunction) { 17 | void* functionaddress = NULL; 18 | char localSymbolNameCopy[1024] = { 0 }; 19 | InternalFunction = false; 20 | char* locallib = NULL; 21 | char* localfunc = SymbolName + sizeof(PREPENDSYMBOLVALUE) - 1; 22 | HMODULE llHandle = NULL; 23 | strncpy_s(localSymbolNameCopy, SymbolName, sizeof(localSymbolNameCopy) - 1); 24 | char* context = NULL; 25 | 26 | if (InternalFunctionMatch(SymbolName + sizeof(PREPENDSYMBOLVALUE) - 1)) { 27 | InternalFunction = true; 28 | 29 | localfunc = SymbolName + strlen(PREPENDSYMBOLVALUE); 30 | 31 | DEBUG_PRINT("\t\tInternalFunction: %s\n", localfunc); 32 | 33 | for (int tempcounter = 0; tempcounter < 30; tempcounter++) { 34 | if (InternalFunctions[tempcounter][0] != NULL) { 35 | if (STR_EQUALS(localfunc, (char*)(InternalFunctions[tempcounter][0]))) { 36 | functionaddress = (void*)InternalFunctions[tempcounter][1]; 37 | return functionaddress; 38 | } 39 | } 40 | } 41 | } 42 | else { 43 | DEBUG_PRINT("\t\tExternal Symbol\n"); 44 | locallib = strtok_s(localSymbolNameCopy + sizeof(PREPENDSYMBOLVALUE) - 1, "$", &context); 45 | llHandle = LoadLibraryA(locallib); 46 | 47 | DEBUG_PRINT("\t\tHandle: 0x%lx\n", llHandle); 48 | localfunc = strtok_s(NULL, "$", &context); 49 | localfunc = strtok_s(localfunc, "@", &context); 50 | functionaddress = GetProcAddress(llHandle, localfunc); 51 | DEBUG_PRINT("\t\tProcAddress: 0x%p\n", functionaddress); 52 | return functionaddress; 53 | } 54 | } 55 | 56 | BOOL ExecuteEntry(COFF_t* COFF, char* func, char* args, unsigned long argSize) { 57 | VOID(*foo)(char* in, uint32_t datalen) = NULL; 58 | 59 | if (!func || !COFF->FileBase) 60 | DEBUG_PRINT("No entry provided"); 61 | 62 | for (UINT32 counter = 0; counter < COFF->FileHeader->NumberOfSymbols; counter++) 63 | { 64 | if (strcmp(COFF->SymbolTable[counter].first.Name, func) == 0) { 65 | foo = (void(*)(char*, uint32_t))((char*)COFF->RawTextData + COFF->SymbolTable[counter].Value); 66 | DEBUG_PRINT("Trying to run: 0x%p\n\n", foo); 67 | } 68 | } 69 | 70 | if (!foo) 71 | DEBUG_PRINT("Couldn't find entry point"); 72 | 73 | foo((char*)args, argSize); 74 | return TRUE; 75 | } 76 | 77 | void RelocationTypeParse(COFF_t* COFF, void** SectionMapped, int SectionNumber, bool InternalFunction, void* FunctionAddrPTR, char* FunctionMapping) { 78 | uint32_t offsetAddr = 0; 79 | uint64_t longOffsetAddr = 0; 80 | unsigned int Type = COFF->Relocation->Type; 81 | 82 | if (Type == IMAGE_REL_AMD64_ADDR64) 83 | { 84 | memcpy(&longOffsetAddr, (char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, sizeof(uint64_t)); 85 | DEBUG_PRINT("\tReadin longOffsetValue : 0x%llX\n", longOffsetAddr); 86 | longOffsetAddr = (uint64_t)((char*)SectionMapped[COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber - 1] + (uint64_t)longOffsetAddr); 87 | longOffsetAddr += COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value; 88 | DEBUG_PRINT("\tModified longOffsetValue : 0x%llX Base Address: %p\n", longOffsetAddr, SectionMapped[COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber - 1]); 89 | memcpy((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, &longOffsetAddr, sizeof(uint64_t)); 90 | } 91 | else if (COFF->Relocation->Type == IMAGE_REL_AMD64_ADDR32NB) { 92 | memcpy(&offsetAddr, (char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, sizeof(int32_t)); 93 | DEBUG_PRINT("\tReadin OffsetValue : 0x%0X\n", offsetAddr); 94 | DEBUG_PRINT("\t\tReferenced Section: 0x%X\n", (char*)SectionMapped[COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber - 1] + offsetAddr); 95 | DEBUG_PRINT("\t\tEnd of Relocation Bytes: 0x%X\n", (char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress + 4); 96 | offsetAddr = ((char*)((char*)SectionMapped[COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber - 1] + offsetAddr) - ((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress + 4)); 97 | offsetAddr += COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value; 98 | DEBUG_PRINT("\tSetting 0x%p to OffsetValue: 0x%X\n", (char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, offsetAddr); 99 | memcpy((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, &offsetAddr, sizeof(uint32_t)); 100 | } 101 | else if (Type == IMAGE_REL_AMD64_REL32) { 102 | if (FunctionAddrPTR != NULL) { 103 | memcpy(FunctionMapping + (COFF->FunctionMappingCount * 8), &FunctionAddrPTR, sizeof(uint64_t)); 104 | offsetAddr = (int32_t)((FunctionMapping + (COFF->FunctionMappingCount * 8) ) - ((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress + 4)); 105 | offsetAddr += COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value; 106 | DEBUG_PRINT("\t\tSetting internal function at 0x%p to relative address: 0x%X\n", (char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, offsetAddr); 107 | memcpy((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, &offsetAddr, sizeof(uint32_t)); 108 | InternalFunction = false; 109 | COFF->FunctionMappingCount++; 110 | } 111 | else { 112 | // This should copy the relative offset for the specified data section into offsetAddr 113 | memcpy(&offsetAddr, (void*)((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress), sizeof(uint32_t)); 114 | DEBUG_PRINT("\tReadin Offset Value : 0x%llX\n", offsetAddr); 115 | // Getting the symbols section then adding the offset to get the value stored. 116 | offsetAddr += (uint32_t)((char*)SectionMapped[COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber - 1] - ((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress + 4)); 117 | // Since the StorageClass is going to be IMAGE_SYM_CLASS_STATIC or IMAGE_SYM_CLASS_EXTERNAL with a non-zero SymbolTableIndex 118 | offsetAddr += COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value; 119 | DEBUG_PRINT("\t\tSetting 0x%p to relative address: 0x%X\n", (char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, offsetAddr); 120 | memcpy((char*)SectionMapped[SectionNumber] + COFF->Relocation->VirtualAddress, &offsetAddr, sizeof(uint32_t)); 121 | } 122 | } 123 | else 124 | { 125 | DEBUG_PRINT("[!] Relocation Type Not Implemented\n"); 126 | } 127 | DEBUG_PRINT("\tValueNumber: 0x%X\n", COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].Value); 128 | DEBUG_PRINT("\tSectionNumber: 0x%X\n", COFF->SymbolTable[COFF->Relocation->SymbolTableIndex].SectionNumber); 129 | 130 | } 131 | 132 | bool RunCOFF(char* FileData, DWORD* DataSize, char* EntryName, char* argumentdata, unsigned long argumentsize) { 133 | 134 | COFF_t COFF; 135 | COFF.FileBase = FileData; 136 | COFF.FileHeader = (FileHeader_t*)COFF.FileBase; 137 | COFF.SymbolTable = (Symbol_t*)(COFF.FileBase + COFF.FileHeader->PointerToSymbolTable); 138 | COFF.FunctionMappingCount = 0; 139 | COFF.RelocationsCount = 0; 140 | 141 | char* functionMapping = NULL; 142 | void** sectionMapped = (void**)calloc(sizeof(char*) * (COFF.FileHeader->NumberOfSections + 1), 1);; 143 | 144 | if ((int)COFF.FileHeader->Machine != IMAGE_FILE_MACHINE_AMD64) { 145 | DEBUG_PRINT("[!] This common object file format is not supported yet :)"); 146 | delete FileData; 147 | return false; 148 | } 149 | 150 | #pragma region "Print COFF Header" 151 | DEBUG_PRINT("********* COFF File Header *********\n"); 152 | DEBUG_PRINT("Machine 0x%X\n", COFF.FileHeader->Machine); 153 | DEBUG_PRINT("Number of sections: %d\n", COFF.FileHeader->NumberOfSections); 154 | DEBUG_PRINT("TimeDateStamp : %X\n", COFF.FileHeader->TimeDateStamp); 155 | DEBUG_PRINT("PointerToSymbolTable : 0x%X\n", COFF.FileHeader->PointerToSymbolTable); 156 | DEBUG_PRINT("NumberOfSymbols: %u\n", COFF.FileHeader->NumberOfSymbols); 157 | DEBUG_PRINT("OptionalHeaderSize: %d\n", COFF.FileHeader->SizeOfOptionalHeader); 158 | DEBUG_PRINT("Characteristics: %d\n", COFF.FileHeader->Characteristics); 159 | DEBUG_PRINT("\n"); 160 | #pragma endregion 161 | 162 | for (byte i = 0; i < COFF.FileHeader->NumberOfSections; i++) { 163 | Section_t* section = (Section_t*)(COFF.FileBase + sizeof(FileHeader_t) + (i * sizeof(Section_t))); 164 | DEBUG_PRINT("********* COFF Section %d: \"%s\" *********\n", i, section->Name); 165 | 166 | sectionMapped[i] = (char*)VirtualAlloc(NULL, section->SizeOfRawData, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); 167 | DEBUG_PRINT("Allocated section %d at 0x%p\n", i, sectionMapped[i]); 168 | 169 | if (section->PointerToRawData != 0) { 170 | memcpy(sectionMapped[i], COFF.FileBase + section->PointerToRawData, section->SizeOfRawData); 171 | } 172 | else { 173 | memset(sectionMapped[i], 0, section->SizeOfRawData); 174 | } 175 | 176 | if (!strcmp(section->Name, ".text")) { 177 | COFF.RawTextData = sectionMapped[i]; 178 | } 179 | 180 | COFF.RelocationsCount += section->NumberOfRelocations; 181 | 182 | #pragma region "Print Section Table" 183 | DEBUG_PRINT("Name: %s\n", section->Name); 184 | DEBUG_PRINT("VirtualSize: 0x%X\n", section->VirtualSize); 185 | DEBUG_PRINT("VirtualAddress: 0x%X\n", section->VirtualAddress); 186 | DEBUG_PRINT("SizeOfRawData: 0x%X\n", section->SizeOfRawData); 187 | DEBUG_PRINT("PointerToRelocations: 0x%X\n", section->PointerToRelocations); 188 | DEBUG_PRINT("PointerToRawData: 0x%X\n", section->PointerToRawData); 189 | DEBUG_PRINT("NumberOfRelocations: %d\n", section->NumberOfRelocations); 190 | DEBUG_PRINT("Characteristics: %x\n\n", section->Characteristics); 191 | #pragma endregion 192 | 193 | } 194 | 195 | DEBUG_PRINT("Total Relocations: %d\n", COFF.RelocationsCount); 196 | 197 | functionMapping = (char*)VirtualAlloc(NULL, COFF.RelocationsCount * 8, MEM_COMMIT | MEM_RESERVE | MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE); 198 | int currentSection = 0; 199 | for (int s = 0; s < COFF.FileHeader->NumberOfSections; s++) { 200 | Section_t* section = (Section_t*)(COFF.FileBase + sizeof(FileHeader_t) + (s * sizeof(Section_t))); 201 | COFF.RelocationsTextPTR = COFF.FileBase + section->PointerToRelocations; 202 | DEBUG_PRINT("********* Performing Relocations for \"%s\" Section *********\n", section->Name); 203 | 204 | for (int i = 0; i < section->NumberOfRelocations; i++) { 205 | 206 | uint32_t symbolOffset = 0; 207 | void* funcptrlocation = NULL; 208 | COFF.Relocation = (Relocation_t*)(COFF.RelocationsTextPTR + (i * sizeof(Relocation_t))); 209 | 210 | #pragma region "Print Relocation Struct" 211 | DEBUG_PRINT("********* COFF Relocation %d / %d *********\n", i, section->NumberOfRelocations); 212 | DEBUG_PRINT("VirtualAddress: 0x%X\n", COFF.Relocation->VirtualAddress); 213 | DEBUG_PRINT("SymbolTableIndex: 0x%X\n", COFF.Relocation->SymbolTableIndex); 214 | DEBUG_PRINT("Type: 0x%X\n\n", COFF.Relocation->Type); 215 | #pragma endregion 216 | 217 | symbolOffset = COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].first.value[1]; 218 | 219 | // Check if the symbol name is more that 8 bytes. If so then the name is stored at the .first.value address 220 | // We can assume that if the name is longer than 8 bytes then it is probably an internal function and starts with "__imp_" and needs to be processed. 221 | // So if the name is 8 bytes then it points to a specific section. 222 | if (COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].first.Name[0] != 0) { 223 | 224 | #pragma region "Print Mapped Relocation Symbol" 225 | DEBUG_PRINT("\tSymPtr: 0x%X\n", symbolOffset); 226 | DEBUG_PRINT("\tSymName: %s\n", COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].first.Name); 227 | DEBUG_PRINT("\tSectionNumber: 0x%X\n", COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].SectionNumber); 228 | #pragma endregion 229 | 230 | RelocationTypeParse(&COFF, sectionMapped, s, false, NULL, NULL); 231 | } 232 | else { 233 | #pragma region "Print Mapped Relocation Symbol" 234 | DEBUG_PRINT("[!] Symbol Name is longer than 8 bytes\n"); 235 | DEBUG_PRINT("\tSymPtr: 0x%X\n", symbolOffset); 236 | DEBUG_PRINT("\tSymName: %s\n", ((char*)(COFF.SymbolTable + COFF.FileHeader->NumberOfSymbols)) + symbolOffset); 237 | DEBUG_PRINT("\tSectionNumber: 0x%X\n", COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].SectionNumber); 238 | #pragma endregion 239 | 240 | bool internalFunctionCheck = false; 241 | funcptrlocation = ProcessBeaconSymbols(((char*)(COFF.SymbolTable + COFF.FileHeader->NumberOfSymbols)) + symbolOffset, &internalFunctionCheck); 242 | if (funcptrlocation == NULL && COFF.SymbolTable[COFF.Relocation->SymbolTableIndex].SectionNumber == 0) { 243 | DEBUG_PRINT("[!] Failed to resolve symbol\n"); 244 | } 245 | 246 | RelocationTypeParse(&COFF, sectionMapped, s, &internalFunctionCheck, funcptrlocation, functionMapping); 247 | } 248 | } 249 | } 250 | 251 | ExecuteEntry(&COFF, EntryName, argumentdata, argumentsize); 252 | 253 | return true; 254 | } -------------------------------------------------------------------------------- /Src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../Include/coff_loader.h" 2 | #include "../Include/utils.h" 3 | 4 | int main(int argc, char* argv[]){ 5 | DWORD coffSize = 0; 6 | char* coffData = NULL; 7 | char* outdata = NULL; 8 | int outdataSize = 0; 9 | 10 | PrintBanner(); 11 | 12 | if (argc < 3) 13 | { 14 | PrintUsage(argv[0]); 15 | return 1; 16 | } 17 | 18 | coffData = ReadFile((char*)argv[2], &coffSize); 19 | if (coffData == NULL) 20 | { 21 | printf(""); 22 | return 1; 23 | } 24 | 25 | Arg arg1; 26 | short testNum = 8; 27 | arg1.value = (char*)&testNum; 28 | arg1.size = sizeof(short); 29 | arg1.includeSize = FALSE; 30 | 31 | Arg args[1] = {arg1}; 32 | 33 | char* argumentsString = NULL; 34 | size_t argumentsSize; 35 | PackData(args,1, &argumentsString, &argumentsSize); 36 | 37 | printf("\n[+] Executing COFF File\n"); 38 | 39 | if (RunCOFF(coffData, &coffSize, (char*)argv[1], argumentsString, argumentsSize)) { 40 | printf("\n[+] SUCCESS - Executed COFF File\n"); 41 | } 42 | 43 | outdata = BeaconGetOutputData(&outdataSize); 44 | if (outdata != NULL) { 45 | 46 | printf("\n[+] Outdata Below:\n\n%s\n", outdata); 47 | } 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /Src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "../Include/utils.h" 2 | using namespace std; 3 | 4 | void PrintBanner() { 5 | #pragma region Banner 6 | printf(" .o88b. .d88b. d88888b d88888b db .d88b. .d8b. d8888b. d88888b d8888b.\n"); 7 | printf(" d8P Y8 .8P Y8. 88' 88' 88 .8P Y8. d8' `8b 88 `8D 88' 88 `8D\n"); 8 | printf(" 8P 88 88 88ooo 88ooo 88 88 88 88ooo88 88 88 88ooooo 88oobY' \n"); 9 | printf(" 8b 88 88 88~~~ 88~~~ 88 88 88 88~~~88 88 88 88~~~~~ 88`8b\n"); 10 | printf(" Y8b d8 `8b d8' 88 88 88booo. `8b d8' 88 88 88 .8D 88. 88 `88.\n"); 11 | printf(" `Y88P' `Y88P' YP YP Y88888P `Y88P' YP YP Y8888D' Y88888P 88 YD \n"); 12 | printf(" Written by: Ap3x (https://github.com/Ap3x)\n\n"); 13 | #pragma endregion 14 | } 15 | 16 | void PrintUsage(char* ExecutableName) { 17 | printf("\nUsage:\n%s \n", ExecutableName); 18 | } 19 | 20 | void PackData(Arg* args, size_t numberOfArgs, char** output, size_t* size) { 21 | uint32_t fullSize = 0; 22 | for (size_t i = 0; i < numberOfArgs; i++) { 23 | Arg arg = args[i]; 24 | fullSize += sizeof(uint32_t) + arg.size; 25 | } 26 | *output = (char*)malloc(sizeof(uint32_t) + fullSize); 27 | fullSize = 4; 28 | for (size_t i = 0; i < numberOfArgs; i++) { 29 | Arg arg = args[i]; 30 | if (arg.includeSize == TRUE) { 31 | memcpy(*output + fullSize, &arg.size, sizeof(uint32_t)); 32 | fullSize += sizeof(uint32_t); 33 | } 34 | memcpy(*output + fullSize, arg.value, arg.size); 35 | fullSize += arg.size; 36 | } 37 | memcpy(*output, &fullSize, sizeof(uint32_t)); 38 | *size = fullSize; 39 | } 40 | 41 | char* ReadFile(char* FilePath, DWORD* FileSize) { 42 | HANDLE hFile = CreateFileA(FilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 43 | if (hFile == INVALID_HANDLE_VALUE) { 44 | DEBUG_PRINT("[!] Failed to open the file"); 45 | return nullptr; 46 | } 47 | *FileSize = GetFileSize(hFile, NULL); 48 | if (*FileSize == INVALID_FILE_SIZE) { 49 | CloseHandle(hFile); 50 | return nullptr; 51 | } 52 | 53 | char* coffData = new char[*FileSize]; 54 | 55 | DWORD bytesRead; 56 | if (!ReadFile(hFile, coffData, *FileSize, &bytesRead, NULL)) { 57 | CloseHandle(hFile); 58 | delete[] coffData; 59 | return nullptr; 60 | } 61 | 62 | CloseHandle(hFile); 63 | 64 | return coffData; 65 | } --------------------------------------------------------------------------------