├── .gitignore ├── BOF-Template.sln ├── BOF-Template ├── BOF-Template.vcxproj ├── BOF-Template.vcxproj.filters ├── Makefile ├── base │ ├── helpers.h │ ├── mock.cpp │ ├── mock.h │ └── mock_syscalls.cpp ├── beacon.h ├── beacon_gate.h ├── bof.cpp ├── packages.config └── sleepmask.h ├── LICENSE └── README.md /.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-Template.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33414.496 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BOF-Template", "BOF-Template\BOF-Template.vcxproj", "{58EBEE7C-B0CF-49E3-AE82-E60750C03613}" 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 | UnitTest|x64 = UnitTest|x64 15 | UnitTest|x86 = UnitTest|x86 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.Debug|x64.ActiveCfg = Debug|x64 19 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.Debug|x64.Build.0 = Debug|x64 20 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.Debug|x86.ActiveCfg = Debug|Win32 21 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.Debug|x86.Build.0 = Debug|Win32 22 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.Release|x64.ActiveCfg = Release|x64 23 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.Release|x64.Build.0 = Release|x64 24 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.Release|x86.ActiveCfg = Release|Win32 25 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.Release|x86.Build.0 = Release|Win32 26 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.UnitTest|x64.ActiveCfg = UnitTest|x64 27 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.UnitTest|x64.Build.0 = UnitTest|x64 28 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.UnitTest|x86.ActiveCfg = UnitTest|Win32 29 | {58EBEE7C-B0CF-49E3-AE82-E60750C03613}.UnitTest|x86.Build.0 = UnitTest|Win32 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {05614438-CD94-4CA7-899A-327C067C1C3B} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /BOF-Template/BOF-Template.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 | UnitTest 22 | Win32 23 | 24 | 25 | UnitTest 26 | x64 27 | 28 | 29 | 30 | 16.0 31 | Win32Proj 32 | {58ebee7c-b0cf-49e3-ae82-e60750c03613} 33 | BOFTemplate 34 | 10.0 35 | 36 | 37 | 38 | Makefile 39 | true 40 | v143 41 | Unicode 42 | Debug\bof.exe 43 | WindowsLocalDebugger 44 | 45 | 46 | Application 47 | true 48 | v143 49 | Unicode 50 | 51 | 52 | Makefile 53 | false 54 | v143 55 | true 56 | Unicode 57 | 58 | 59 | Makefile 60 | true 61 | v143 62 | Unicode 63 | x64\Debug\bof.exe 64 | WindowsLocalDebugger 65 | 66 | 67 | Application 68 | true 69 | v143 70 | Unicode 71 | 72 | 73 | Makefile 74 | false 75 | v143 76 | true 77 | Unicode 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | nmake /A all 105 | nmake /A all 106 | nmake clean 107 | /std:c++20 108 | 109 | 110 | nmake /A all 111 | nmake /A all 112 | nmake clean 113 | /std:c++20 114 | 115 | 116 | nmake /A all-debug 117 | nmake /A all-debug 118 | nmake clean 119 | /std:c++20 120 | 121 | 122 | nmake all-debug 123 | nmake /B all-debug 124 | nmake clean 125 | 126 | 127 | nmake /A all-debug 128 | nmake /A all-debug 129 | nmake clean 130 | $(MSBuildProjectDirectory)\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\build\native\include;$(IncludePath) 131 | /std:c++20 132 | 133 | 134 | nmake all-debug 135 | nmake /B all-debug 136 | nmake clean 137 | 138 | 139 | 140 | Level3 141 | true 142 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 143 | false 144 | stdcpp17 145 | 146 | 147 | Console 148 | true 149 | 150 | 151 | 152 | 153 | Level3 154 | true 155 | WIN32;_DEBUG;_CONSOLE;_GTEST;%(PreprocessorDefinitions) 156 | false 157 | stdcpp20 158 | MultiThreadedDebug 159 | $(SolutionDir)\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\build\native\include;%(AdditionalIncludeDirectories) 160 | 161 | 162 | Console 163 | true 164 | $(SolutionDir)\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\lib\native\v140\windesktop\msvcstl\static\rt-static\$(PlatformTarget)\Debug\gtest_maind.lib;$(SolutionDir)\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\lib\native\v140\windesktop\msvcstl\static\rt-static\$(PlatformTarget)\Debug\gtestd.lib;%(AdditionalDependencies) 165 | 166 | 167 | 168 | 169 | Level3 170 | true 171 | true 172 | true 173 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 174 | false 175 | stdcpp17 176 | 177 | 178 | Console 179 | true 180 | true 181 | true 182 | 183 | 184 | 185 | 186 | Level3 187 | true 188 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 189 | false 190 | stdcpp17 191 | 192 | 193 | Console 194 | true 195 | 196 | 197 | 198 | 199 | Level3 200 | true 201 | _DEBUG;_CONSOLE;_GTEST;%(PreprocessorDefinitions) 202 | false 203 | stdcpp20 204 | $(SolutionDir)packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\build\native\include;%(AdditionalIncludeDirectories) 205 | MultiThreadedDebug 206 | 207 | 208 | Console 209 | true 210 | $(SolutionDir)\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\lib\native\v140\windesktop\msvcstl\static\rt-static\$(PlatformTarget)\Debug\gtest_maind.lib;$(SolutionDir)\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\lib\native\v140\windesktop\msvcstl\static\rt-static\$(PlatformTarget)\Debug\gtestd.lib;%(AdditionalDependencies) 211 | 212 | 213 | 214 | 215 | Level3 216 | true 217 | true 218 | true 219 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 220 | false 221 | stdcpp17 222 | 223 | 224 | Console 225 | true 226 | true 227 | true 228 | 229 | 230 | 231 | 232 | 233 | true 234 | true 235 | 236 | 237 | C:\Users\User\source\repos\BOF-Template\x64\Debug\bof.exe 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | -------------------------------------------------------------------------------- /BOF-Template/BOF-Template.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 | {b74d73f4-acc9-410d-a627-90481555d13a} 18 | 19 | 20 | {8fa0224c-f4ab-4998-991b-bf2b56db2848} 21 | 22 | 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files\base 29 | 30 | 31 | Source Files\base 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files\base 40 | 41 | 42 | Header Files\base 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /BOF-Template/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS=/c /GS- /std:c++20 2 | DEBUGCFLAGS=/Zi /MTd /D_DEBUG /EHsc /std:c++20 3 | 4 | !IF "$(PROCESSOR_ARCHITECTURE)" != "x86" && "$(PROCESSOR_ARCHITECTURE)" != "AMD64" 5 | !ERROR Only x86 and AMD64 architectures are supported or the PROCESSOR_ARCHITECTURE environment variable is not set. 6 | !ELSEIF "$(PROCESSOR_ARCHITECTURE)" == "AMD64" 7 | OUTDIR=..\x64\Release\ 8 | IMDIR=x64\Release\ 9 | DOUTDIR=..\x64\Debug\ 10 | DIMDIR=x64\Debug\ 11 | OUTEXT=.x64.o 12 | !ELSE 13 | OUTDIR=..\Release\ 14 | IMDIR=Release\ 15 | DIMDIR=Debug\ 16 | DOUTDIR=..\Debug\ 17 | OUTEXT=.x86.o 18 | !ENDIF 19 | 20 | all: *.cpp 21 | @$(MAKE) /A $(patsubst %.c,%.obj, $(patsubst %.cpp, %.obj, $(patsubsti %, $(IMDIR)\%, $**))) 22 | @if not exist "$(OUTDIR)" mkdir "$(OUTDIR)" 23 | copy "$(IMDIR)\*.obj" "$(OUTDIR)" 24 | del /F "$(OUTDIR)\*$(OUTEXT)" 25 | ren "$(OUTDIR)*.obj" "*$(OUTEXT)" 26 | 27 | all-debug: *.cpp 28 | @$(MAKE) /A $(patsubst %.c,%.exe, $(patsubst %.cpp, %.exe, $(patsubsti %, $(DOUTDIR)\%, $**))) 29 | 30 | .cpp{$(IMDIR)}.obj: 31 | @if not exist "$(IMDIR)" mkdir "$(IMDIR)" 32 | $(CPP) $(CFLAGS) /Fo"$@" $< 33 | 34 | .cpp{$(DOUTDIR)}.exe: 35 | @if not exist "$(DIMDIR)" mkdir "$(DIMDIR)" 36 | @if not exist "$(DOUTDIR)" mkdir "$(DOUTDIR)" 37 | $(CPP) $(DEBUGCFLAGS) /Fo$(DIMDIR) /Fd$(DIMDIR) /Fe"$@" $< base/mock.cpp 38 | 39 | clean: 40 | @if exist "$(DIMDIR)" rmdir /Q /S "$(DIMDIR)" 41 | @if exist "$(IMDIR)" rmdir /Q /S "$(IMDIR)" 42 | @if exist "$(OUTDIR)" rmdir /Q /S "$(OUTDIR)" 43 | @if exist "$(DOUTDIR)" rmdir /Q /S "$(DOUTDIR)" 44 | -------------------------------------------------------------------------------- /BOF-Template/base/helpers.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | #ifndef _DEBUG 3 | #define DFR(module, function) \ 4 | DECLSPEC_IMPORT decltype(function) module##$##function; 5 | 6 | #define DFR_LOCAL(module, function) \ 7 | DECLSPEC_IMPORT decltype(function) module##$##function; \ 8 | decltype(module##$##function) * function = module##$##function; 9 | #else 10 | #define DFR_LOCAL(module, function) 11 | #define DFR(module, function) \ 12 | decltype(function) *module##$##function = function; 13 | #endif // end of _DEBUG 14 | #endif // end of __cplusplus -------------------------------------------------------------------------------- /BOF-Template/base/mock.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | extern "C" { 13 | #ifdef _DEBUG 14 | #undef DECLSPEC_IMPORT 15 | #define DECLSPEC_IMPORT 16 | #endif 17 | #include "..\beacon.h" 18 | } 19 | 20 | #include "mock.h" 21 | 22 | namespace bof { 23 | namespace utils { 24 | template 25 | T swapEndianness(T value) { 26 | char *ptr = reinterpret_cast(&value); 27 | std::reverse(ptr, ptr + sizeof(T)); 28 | return value; 29 | } 30 | 31 | template 32 | std::vector toBytes(T input) { 33 | char *ptr = reinterpret_cast(&input); 34 | return std::vector(ptr, ptr + sizeof(T)); 35 | } 36 | 37 | const char* typeToStr(int callbackType) { 38 | switch (callbackType) { 39 | case CALLBACK_OUTPUT: return "CALLBACK_OUTPUT"; 40 | case CALLBACK_OUTPUT_OEM: return "CALLBACK_OUTPUT_OEM"; 41 | case CALLBACK_ERROR: return "CALLBACK_ERROR"; 42 | case CALLBACK_OUTPUT_UTF8: return "CALLBACK_OUTPUT_UTF8"; 43 | default: return "CALLBACK_UNKOWN"; 44 | } 45 | } 46 | } 47 | 48 | namespace mock { 49 | static BEACON_INFO beaconInfo = { 0 }; 50 | 51 | char *BofData::get() { 52 | return size() > 0 ? reinterpret_cast(&data[0]) : nullptr; 53 | } 54 | 55 | int BofData::size() { 56 | return data.size(); 57 | } 58 | 59 | void BofData::addData(const char *buf, std::size_t len) { 60 | std::vector bytes; 61 | bytes.assign(buf, buf + len); 62 | insert(static_cast(len)); 63 | append(bytes); 64 | } 65 | 66 | void BofData::append(const std::vector &data) { 67 | this->data.insert(std::end(this->data), std::begin(data), std::end(data)); 68 | } 69 | 70 | void BofData::insert(int v) { 71 | append(bof::utils::toBytes(bof::utils::swapEndianness(v))); 72 | } 73 | 74 | void BofData::insert(short v) { 75 | append(bof::utils::toBytes(bof::utils::swapEndianness(v))); 76 | } 77 | 78 | void BofData::insert(unsigned int v) { 79 | insert(static_cast(v)); 80 | } 81 | 82 | void BofData::insert(unsigned short v) { 83 | insert(static_cast(v)); 84 | } 85 | 86 | void BofData::insert(const char *v) { 87 | addData(v, std::strlen(v) + 1); 88 | } 89 | 90 | void BofData::insert(const wchar_t *v) { 91 | addData((const char *)v, (std::wcslen(v) + 1) * sizeof(wchar_t)); 92 | } 93 | 94 | void BofData::insert(const std::vector& data) { 95 | pack(data.size()); 96 | append(data); 97 | } 98 | 99 | void setBeaconInfo(BEACON_INFO& info) { 100 | std::memcpy(&bof::mock::beaconInfo, &info, sizeof(BEACON_INFO)); 101 | } 102 | 103 | void setSection(PALLOCATED_MEMORY_SECTION info, PVOID baseAddress, SIZE_T size, DWORD finalProtection, DWORD allocationType, ALLOCATED_MEMORY_LABEL label) { 104 | info->BaseAddress = baseAddress; 105 | info->VirtualSize = size; 106 | info->PreviousProtect = finalProtection; 107 | info->CurrentProtect = finalProtection; 108 | info->MaskSection = TRUE; 109 | info->Label = label; 110 | } 111 | 112 | ALLOCATED_MEMORY_REGION allocateBeaconMemory(const bof::profile::Stage& stage, size_t size, DWORD* initialPermission) { 113 | ALLOCATED_MEMORY_REGION info; 114 | std::memset(&info, 0, sizeof(info)); 115 | 116 | /* Allocate the base memory for Beacon. The following .stage options affect how the memory is allocated: 117 | * - .stage.allocator: Set how Beacon's Reflective Loader allocates memory for the agent. Options are: HeapAlloc, MapViewOfFile, and VirtualAlloc. 118 | * - .stage.module_x64/x86: Ask the ReflectiveLoader to load the specified library and overwrite its space instead of allocating memory with the .stage.allocator. 119 | */ 120 | *initialPermission = stage.useRWX == bof::profile::UseRWX::True ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; 121 | if (stage.module.empty()) { 122 | HANDLE hHeap = INVALID_HANDLE_VALUE; 123 | HANDLE hFile = INVALID_HANDLE_VALUE; 124 | 125 | switch (stage.allocator) { 126 | case bof::profile::Allocator::HeapAlloc: { 127 | // For heap allocator we don't honor the .stage.userwx flag 128 | *initialPermission = PAGE_EXECUTE_READWRITE; 129 | hHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0); 130 | assert(hHeap != NULL && "Could not create the heap"); 131 | info.AllocationBase = HeapAlloc(hHeap, 0, size); 132 | info.Type = MEM_PRIVATE; 133 | info.CleanupInformation.AllocationMethod = METHOD_HEAPALLOC; 134 | info.CleanupInformation.AdditionalCleanupInformation.HeapAllocInfo.HeapHandle = hHeap; 135 | info.CleanupInformation.AdditionalCleanupInformation.HeapAllocInfo.DestroyHeap = TRUE; 136 | break; 137 | } 138 | case bof::profile::Allocator::MapViewOfFile: { 139 | hFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, size, NULL); 140 | assert(hFile != NULL && "Could not create file mapping"); 141 | info.AllocationBase = MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0); 142 | info.Type = MEM_MAPPED; 143 | info.CleanupInformation.AllocationMethod = METHOD_NTMAPVIEW; 144 | CloseHandle(hFile); 145 | 146 | DWORD old = 0; 147 | if (!VirtualProtect(info.AllocationBase, size, *initialPermission, &old)) { 148 | assert(false && "Could not set the initial memory permission for the Beacon memory"); 149 | } 150 | break; 151 | } 152 | case bof::profile::Allocator::VirtualAlloc: { 153 | info.AllocationBase = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, *initialPermission); 154 | info.Type = MEM_PRIVATE; 155 | info.CleanupInformation.AllocationMethod = METHOD_VIRTUALALLOC; 156 | break; 157 | } 158 | } 159 | } 160 | else { 161 | // For simplicity we use module base here instead of resolving a function 162 | info.AllocationBase = (PVOID)LoadLibraryExA(stage.module.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES); 163 | info.Type = MEM_IMAGE; 164 | info.CleanupInformation.AllocationMethod = METHOD_MODULESTOMP; 165 | 166 | DWORD old = 0; 167 | if (!VirtualProtect(info.AllocationBase, size, *initialPermission, &old)) { 168 | assert(false && "Could not set the initial memory permission for the Beacon memory"); 169 | } 170 | } 171 | assert(info.AllocationBase != NULL && "Could not allocate the base memory for Beacon"); 172 | 173 | // Set the necessary fields for the Beacon memory 174 | info.Purpose = PURPOSE_BEACON_MEMORY; 175 | info.RegionSize = size; 176 | info.CleanupInformation.Cleanup = TRUE; 177 | 178 | return info; 179 | } 180 | 181 | BEACON_INFO setupMockBeacon(const bof::profile::Stage& stage) { 182 | const std::vector> sections{ 183 | { LABEL_PEHEADER, "PE" }, 184 | { LABEL_TEXT, ".text" }, 185 | { LABEL_RDATA, ".rdata" }, 186 | { LABEL_DATA, ".data" }, 187 | #ifdef _M_X64 188 | { LABEL_PDATA, ".pdata" }, 189 | #endif 190 | { LABEL_RELOC, ".reloc" }, 191 | }; 192 | const size_t sectionSize = 0x1000; 193 | 194 | BEACON_INFO info = { 0 }; 195 | info.version = bof::CsVersion; 196 | 197 | // Set the static mask key 198 | for (size_t i = 0; i < MASK_SIZE; ++i) { 199 | info.mask[i] = 0xAB; 200 | } 201 | 202 | // Allocate the memory for Beacon 203 | DWORD initialProtection = 0; 204 | ALLOCATED_MEMORY_REGION beaconMemory = allocateBeaconMemory(stage, sections.size() * sectionSize, &initialProtection); 205 | info.allocatedMemory.AllocatedMemoryRegions[0] = beaconMemory; 206 | info.beacon_ptr = stage.obfuscate == bof::profile::Obfuscate::False 207 | ? reinterpret_cast(beaconMemory.AllocationBase) 208 | : reinterpret_cast(beaconMemory.AllocationBase) - 0x1000; 209 | 210 | // Build the section list 211 | PALLOCATED_MEMORY_SECTION nextSection = info.allocatedMemory.AllocatedMemoryRegions[0].Sections; 212 | char* sectionBase = reinterpret_cast(beaconMemory.AllocationBase); 213 | for (auto [section, name] : sections) { 214 | // Skip the PE header if .stage.obfuscate = true 215 | if (stage.obfuscate == bof::profile::Obfuscate::True && section == LABEL_PEHEADER) { 216 | continue; 217 | } 218 | 219 | // Fill the memory 220 | for (size_t i = 0; i < sectionSize; ++i) { 221 | sectionBase[i] = name[i % name.length()]; 222 | } 223 | 224 | DWORD finalProtection = initialProtection; 225 | 226 | // Fix the .code section permission if the allocator is not heap alloc and .stage.userwx is false 227 | if (stage.allocator != bof::profile::Allocator::HeapAlloc || !stage.module.empty()) { 228 | if (section == LABEL_TEXT && stage.useRWX == bof::profile::UseRWX::False) { 229 | finalProtection = PAGE_EXECUTE_READ; 230 | 231 | // Fix the permissions 232 | DWORD old = 0; 233 | if (!VirtualProtect(sectionBase, sectionSize, finalProtection, &old)) { 234 | assert(false && "Could not set the final memory protection"); 235 | } 236 | } 237 | } 238 | 239 | // Set the section memory information 240 | setSection(nextSection, sectionBase, sectionSize, finalProtection, beaconMemory.Type, section); 241 | 242 | // Next section 243 | ++nextSection; 244 | sectionBase += sectionSize; 245 | } 246 | 247 | // Add few mock heap records 248 | const size_t numberOfHeapRecords = 2; 249 | info.heap_records = new HEAP_RECORD[numberOfHeapRecords + 1]; 250 | assert(info.heap_records != nullptr && "Could not the allocate heap records array"); 251 | for (size_t i = 0; i < numberOfHeapRecords; ++i) { 252 | info.heap_records[i].ptr = new char[512]; 253 | info.heap_records[i].size = 512; 254 | assert(info.heap_records[i].ptr != nullptr && "Could not allocate a heap record"); 255 | } 256 | info.heap_records[numberOfHeapRecords].ptr = NULL; 257 | info.heap_records[numberOfHeapRecords].size = 0; 258 | 259 | return info; 260 | } 261 | 262 | void resolveMockUpSleepmaskLocation(BEACON_INFO& info) { 263 | // Get the base address of the debug exe 264 | char* exeBase = (char*)GetModuleHandleA(NULL); 265 | // Find the start of the section headers 266 | PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)exeBase; 267 | PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(exeBase + dosHeader->e_lfanew); 268 | PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)(reinterpret_cast(&ntHeader->OptionalHeader) + ntHeader->FileHeader.SizeOfOptionalHeader); 269 | // Find the .text section which will be our "mock" sleepmask code 270 | DWORD numberOfSections = ntHeader->FileHeader.NumberOfSections; 271 | while (numberOfSections--) { 272 | if (strcmp(reinterpret_cast(sectionHeader->Name), ".text") == 0) { 273 | info.sleep_mask_ptr = exeBase + sectionHeader->VirtualAddress; 274 | info.sleep_mask_text_size = sectionHeader->Misc.VirtualSize; 275 | // In theory, the total size would contains also the data part, but we cannot guarantee the section order. 276 | info.sleep_mask_total_size = sectionHeader->Misc.VirtualSize; 277 | return; 278 | } 279 | ++sectionHeader; 280 | } 281 | assert(false && "Could not find the text section of the debug image"); 282 | } 283 | 284 | FUNCTION_CALL createFunctionCallStructure(PVOID targetFunction, WinApi targetFunctionName, BOOL bMask, int numOfArgs, ...) { 285 | FUNCTION_CALL functionCall = { 0 }; 286 | va_list valist = NULL; 287 | 288 | /* Set basic info for encapsulated function call */ 289 | functionCall.functionPtr = targetFunction; 290 | functionCall.function = targetFunctionName; 291 | functionCall.bMask = bMask; 292 | functionCall.numOfArgs = numOfArgs; 293 | 294 | /* Start parsing valist and copy over variadic arguments. */ 295 | va_start(valist, numOfArgs); 296 | for (int i = 0; i < numOfArgs; i++) 297 | { 298 | functionCall.args[i] = va_arg(valist, ULONG_PTR); 299 | } 300 | va_end(valist); 301 | 302 | return functionCall; 303 | } 304 | 305 | } 306 | 307 | namespace output { 308 | std::vector outputs; 309 | 310 | void addEntry(int type, const char* data, int len) { 311 | OutputEntry output = { 312 | type, 313 | std::string(data, data + len) 314 | }; 315 | outputs.push_back(output); 316 | } 317 | 318 | const std::vector& getOutputs() { 319 | return outputs; 320 | } 321 | 322 | void reset() { 323 | outputs.clear(); 324 | } 325 | 326 | void PrintTo(const OutputEntry& o, std::ostream* os) { 327 | *os << "{ callbackType: " << bof::utils::typeToStr(o.callbackType) << ", output: " << o.output << " }"; 328 | } 329 | } 330 | 331 | namespace valuestore { 332 | std::map values; 333 | 334 | void reset() { 335 | values.clear(); 336 | } 337 | } 338 | 339 | namespace bud { 340 | char custom[BEACON_USER_DATA_CUSTOM_SIZE] = { 0 }; 341 | 342 | void reset() { 343 | std::memset(custom, 0, BEACON_USER_DATA_CUSTOM_SIZE); 344 | } 345 | 346 | void set(const char* data) { 347 | if (data) { 348 | std::memcpy(custom, data, BEACON_USER_DATA_CUSTOM_SIZE); 349 | } 350 | } 351 | } 352 | 353 | std::vector runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, PSLEEPMASK_INFO sleepMaskInfo, PFUNCTION_CALL functionCall) { 354 | // Reset the global output container 355 | bof::output::reset(); 356 | // Execute the entrypoint 357 | sleepMaskFunc(sleepMaskInfo, functionCall); 358 | // Return the stored outputs 359 | return bof::output::getOutputs(); 360 | } 361 | 362 | std::vector runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, const bof::profile::Stage& stage, const bof::mock::MockSleepMaskConfig& config) { 363 | BEACON_INFO beaconInfo = bof::mock::setupMockBeacon(stage); 364 | SLEEPMASK_INFO sleepmaskInfo = { 365 | .version = bof::CsVersion, 366 | .reason = DEFAULT_SLEEP, 367 | .sleep_time = config.sleepTimeMs, 368 | .beacon_info = beaconInfo, 369 | }; 370 | bof::mock::resolveMockUpSleepmaskLocation(sleepmaskInfo.beacon_info); 371 | bof::mock::setBeaconInfo(beaconInfo); 372 | 373 | std::vector output; 374 | do { 375 | output = runMockedSleepMask(sleepMaskFunc, &sleepmaskInfo, NULL); 376 | } while (config.runForever); 377 | 378 | return output; 379 | } 380 | 381 | std::vector runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, const bof::profile::Stage& stage) { 382 | // Set the default config for the mock sleepmask runner 383 | const bof::mock::MockSleepMaskConfig config = { 384 | .sleepTimeMs = 5000, 385 | .runForever = false, 386 | }; 387 | return runMockedSleepMask(sleepMaskFunc, stage, config); 388 | } 389 | 390 | std::vector runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc) { 391 | return runMockedSleepMask(sleepMaskFunc, bof::profile::defaultStage); 392 | } 393 | 394 | std::vector runMockedBeaconGate(SLEEPMASK_FUNC sleepMaskFunc, PFUNCTION_CALL functionCall, const bof::profile::Stage& stage) { 395 | BEACON_INFO beaconInfo = bof::mock::setupMockBeacon(stage); 396 | SLEEPMASK_INFO sleepmaskInfo = { 397 | .version = bof::CsVersion, 398 | .reason = BEACON_GATE, 399 | .sleep_time = 0, 400 | .beacon_info = beaconInfo, 401 | }; 402 | bof::mock::resolveMockUpSleepmaskLocation(sleepmaskInfo.beacon_info); 403 | bof::mock::setBeaconInfo(beaconInfo); 404 | return runMockedSleepMask(sleepMaskFunc, &sleepmaskInfo, functionCall); 405 | } 406 | 407 | std::vector runMockedBeaconGate(SLEEPMASK_FUNC sleepMaskFunc, PFUNCTION_CALL functionCall) { 408 | return runMockedBeaconGate(sleepMaskFunc, functionCall, bof::profile::defaultStage); 409 | } 410 | } 411 | 412 | #include "mock_syscalls.cpp" 413 | 414 | extern "C" 415 | { 416 | // Print API 417 | void BeaconPrintf(int type, const char *fmt, ...) { 418 | printf("[Output Callback: %s (0x%X)]\n", bof::utils::typeToStr(type), type); 419 | va_list args; 420 | va_start(args, fmt); 421 | int size = vsnprintf(nullptr, 0, fmt, args); 422 | if (size >= 0) { 423 | char* buffer = new char[size + 1]; 424 | vsnprintf(buffer, size + 1, fmt, args); 425 | bof::output::addEntry(type, buffer, size); 426 | delete[] buffer; 427 | } 428 | vprintf(fmt, args); 429 | printf("\n"); 430 | va_end(args); 431 | } 432 | 433 | void BeaconOutput(int type, const char *data, int len) { 434 | bof::output::addEntry(type, data, len); 435 | printf("[Output Callback: %s (0x%X)]\n%.*s", bof::utils::typeToStr(type), type, len, data); 436 | } 437 | 438 | // Parser API 439 | void BeaconDataParse(datap *parser, char *buffer, int size) { 440 | parser->buffer = buffer; 441 | parser->original = buffer; 442 | parser->size = size; 443 | parser->length = size; 444 | } 445 | 446 | char* BeaconDataPtr(datap* parser, int size) { 447 | if (parser->length < size) { 448 | return NULL; 449 | } 450 | 451 | char* data = parser->buffer; 452 | parser->buffer += size; 453 | parser->length -= size; 454 | 455 | return data; 456 | } 457 | 458 | int BeaconDataInt(datap *parser) { 459 | int value = *(int *)(parser->buffer); 460 | parser->buffer += sizeof(int); 461 | parser->length -= sizeof(int); 462 | return bof::utils::swapEndianness(value); 463 | } 464 | 465 | short BeaconDataShort(datap *parser) { 466 | short value = *(short *)(parser->buffer); 467 | parser->buffer += sizeof(short); 468 | parser->length -= sizeof(short); 469 | return bof::utils::swapEndianness(value); 470 | } 471 | 472 | int BeaconDataLength(datap *parser) { 473 | return parser->length; 474 | } 475 | 476 | char *BeaconDataExtract(datap *parser, int *size) { 477 | int size_im = BeaconDataInt(parser); 478 | char *buff = parser->buffer; 479 | parser->buffer += size_im; 480 | if (size) 481 | { 482 | *size = size_im; 483 | } 484 | return buff; 485 | } 486 | 487 | // Format API 488 | void BeaconFormatAlloc(formatp *format, int maxsz) { 489 | format->original = new char[maxsz]; 490 | format->buffer = format->original; 491 | format->length = maxsz; 492 | format->size = maxsz; 493 | } 494 | 495 | void BeaconFormatReset(formatp *format) { 496 | format->buffer = format->original; 497 | format->length = format->size; 498 | } 499 | 500 | void BeaconFormatFree(formatp *format) { 501 | delete[] format->original; 502 | } 503 | 504 | void BeaconFormatAppend(formatp *format, const char *text, int len) { 505 | memcpy(format->buffer, text, len); 506 | format->buffer += len; 507 | format->length -= len; 508 | } 509 | 510 | void BeaconFormatPrintf(formatp *format, const char *fmt, ...) { 511 | va_list args; 512 | va_start(args, fmt); 513 | int len = vsprintf_s(format->buffer, format->length, fmt, args); 514 | format->buffer += len; 515 | format->length -= len; 516 | va_end(args); 517 | } 518 | 519 | char *BeaconFormatToString(formatp *format, int *size) { 520 | if (size) 521 | { 522 | *size = format->size - format->length; 523 | } 524 | return format->original; 525 | } 526 | 527 | void BeaconFormatInt(formatp *format, int value) { 528 | value = bof::utils::swapEndianness(value); 529 | BeaconFormatAppend(format, (char *)&value, 4); 530 | } 531 | 532 | // Internal API 533 | BOOL BeaconUseToken(HANDLE token) { 534 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 535 | return TRUE; 536 | } 537 | 538 | void BeaconRevertToken() { 539 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 540 | } 541 | 542 | BOOL BeaconIsAdmin() { 543 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 544 | return FALSE; 545 | } 546 | 547 | void BeaconGetSpawnTo(BOOL x86, char *buffer, int length) { 548 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 549 | } 550 | 551 | void BeaconInjectProcess(HANDLE hProc, int pid, char *payload, 552 | int p_len, int p_offset, char *arg, 553 | int a_len) 554 | { 555 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 556 | } 557 | 558 | void BeaconInjectTemporaryProcess(PROCESS_INFORMATION *pInfo, 559 | char *payload, int p_len, 560 | int p_offset, char *arg, 561 | int a_len) 562 | { 563 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 564 | } 565 | 566 | 567 | BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* si, PROCESS_INFORMATION* pInfo) { 568 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 569 | return false; 570 | } 571 | 572 | void BeaconCleanupProcess(PROCESS_INFORMATION *pInfo) { 573 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 574 | } 575 | 576 | BOOL toWideChar(char *src, wchar_t *dst, int max) { 577 | std::string str = src; 578 | std::wstring wstr(str.begin(), str.end()); 579 | 580 | size_t bytes = min(wstr.length() * sizeof(wchar_t), max); 581 | std::memcpy(dst, wstr.c_str(), bytes); 582 | return TRUE; 583 | } 584 | 585 | BOOL BeaconInformation(PBEACON_INFO info) { 586 | std::memcpy(info, &bof::mock::beaconInfo, sizeof(BEACON_INFO)); 587 | return TRUE; 588 | } 589 | 590 | BOOL BeaconAddValue(const char* key, void* ptr) { 591 | auto item = bof::valuestore::values.find(std::string(key)); 592 | if (ptr && item == bof::valuestore::values.end()) { 593 | bof::valuestore::values[std::string(key)] = ptr; 594 | return TRUE; 595 | } 596 | return FALSE; 597 | } 598 | 599 | void* BeaconGetValue(const char* key) { 600 | auto item = bof::valuestore::values.find(std::string(key)); 601 | if (item != bof::valuestore::values.end()) { 602 | return item->second; 603 | } 604 | return NULL; 605 | } 606 | 607 | BOOL BeaconRemoveValue(const char* key) { 608 | auto item = bof::valuestore::values.find(std::string(key)); 609 | if (item != bof::valuestore::values.end()) { 610 | bof::valuestore::values.erase(item); 611 | return TRUE; 612 | } 613 | return FALSE; 614 | } 615 | 616 | PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index) { 617 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 618 | return NULL; 619 | } 620 | 621 | void BeaconDataStoreProtectItem(size_t index) { 622 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 623 | } 624 | 625 | void BeaconDataStoreUnprotectItem(size_t index) { 626 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 627 | } 628 | 629 | size_t BeaconDataStoreMaxEntries() { 630 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 631 | return 0; 632 | } 633 | 634 | char* BeaconGetCustomUserData() { 635 | return bof::bud::custom; 636 | } 637 | 638 | BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized) { 639 | if (infoSize != sizeof(BEACON_SYSCALLS)) { 640 | return FALSE; 641 | } 642 | if (info) { 643 | bof::mock::syscall::ResolveSyscalls(&info->syscalls); 644 | bof::mock::syscall::ResolveRtls(&info->rtls); 645 | return TRUE; 646 | } 647 | return FALSE; 648 | } 649 | 650 | // Beacon System Call API 651 | LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) { 652 | return VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); 653 | } 654 | 655 | LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) { 656 | return VirtualAllocEx(processHandle, lpAddress, dwSize, flAllocationType, flProtect); 657 | } 658 | 659 | BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) { 660 | return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); 661 | } 662 | 663 | BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) { 664 | return VirtualProtectEx(processHandle, lpAddress, dwSize, flNewProtect, lpflOldProtect); 665 | } 666 | 667 | BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) { 668 | return VirtualFree(lpAddress, dwSize, dwFreeType); 669 | } 670 | 671 | BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext) { 672 | return GetThreadContext(threadHandle, threadContext); 673 | } 674 | 675 | BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext) { 676 | return SetThreadContext(threadHandle, threadContext); 677 | } 678 | 679 | DWORD BeaconResumeThread(HANDLE threadHandle) { 680 | return ResumeThread(threadHandle); 681 | } 682 | 683 | HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId) { 684 | return OpenProcess(desiredAccess, inheritHandle, processId); 685 | } 686 | 687 | HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId) { 688 | return OpenThread(desiredAccess, inheritHandle, threadId); 689 | } 690 | 691 | BOOL BeaconCloseHandle(HANDLE object) { 692 | return CloseHandle(object); 693 | } 694 | 695 | BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress) { 696 | return UnmapViewOfFile(baseAddress); 697 | } 698 | 699 | SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length) { 700 | return VirtualQuery(address, buffer, length); 701 | } 702 | 703 | BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) { 704 | return DuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions); 705 | } 706 | 707 | BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead) { 708 | return ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead); 709 | } 710 | 711 | BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten) { 712 | return WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten); 713 | } 714 | 715 | VOID BeaconDisableBeaconGate() { 716 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 717 | } 718 | 719 | VOID BeaconEnableBeaconGate() { 720 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 721 | } 722 | 723 | VOID BeaconDisableBeaconGateMasking() { 724 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 725 | } 726 | 727 | VOID BeaconEnableBeaconGateMasking() { 728 | std::cerr << "Not implemented: " << __FUNCTION__ << std::endl; 729 | } 730 | } 731 | -------------------------------------------------------------------------------- /BOF-Template/base/mock.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../sleepmask.h" 5 | 6 | namespace bof { 7 | const DWORD CsVersion = 0x041000; 8 | 9 | namespace profile { 10 | /** 11 | * Enum classes to mimic the stage block in the C2 profile. 12 | */ 13 | enum class Allocator { 14 | VirtualAlloc, 15 | HeapAlloc, 16 | MapViewOfFile 17 | }; 18 | enum class Obfuscate { 19 | False, 20 | True 21 | }; 22 | enum class UseRWX { 23 | False, 24 | True 25 | }; 26 | 27 | struct Stage { 28 | Allocator allocator; 29 | Obfuscate obfuscate; 30 | UseRWX useRWX; 31 | std::string module; 32 | }; 33 | 34 | const Stage defaultStage = { 35 | .allocator = bof::profile::Allocator::VirtualAlloc, 36 | .obfuscate = bof::profile::Obfuscate::False, 37 | .useRWX = bof::profile::UseRWX::True, 38 | .module = "", 39 | }; 40 | } 41 | 42 | namespace mock { 43 | /** 44 | * Data container class used for packing BOF arguments. 45 | */ 46 | class BofData { 47 | public: 48 | /** 49 | * Pack a variadic number of arguments. 50 | * Equivalent to the bof_pack function. 51 | * 52 | * For example, bof_pack("isz", 1, 2, "hello") 53 | * -> pack(1, 2, "hello") 54 | * 55 | * @param ... arguments 56 | */ 57 | template 58 | void pack(T &&...v) 59 | { 60 | ((insert(std::forward(v))), ...); 61 | } 62 | 63 | /** 64 | * Add binary data to the argument buffer. 65 | * Equivalent to bof_pack("b", $data). 66 | * 67 | * @param buf A char pointer to the data 68 | * @param len A length to the data 69 | */ 70 | void addData(const char *buf, std::size_t len); 71 | 72 | 73 | /** 74 | * << operator to allow an alternative way to build the argument buffer. 75 | * 76 | * For example: args << 123 << 32; 77 | * 78 | * @param container A BofData object 79 | * @param arg An argument 80 | */ 81 | template 82 | friend BofData &operator<<(BofData &container, T arg) 83 | { 84 | container.pack(arg); 85 | return container; 86 | } 87 | 88 | /** 89 | * Return a raw argument buffer. 90 | * 91 | * @return A char pointer of raw argument buffer 92 | */ 93 | char* get(); 94 | 95 | /** 96 | * Get the size of the argument buffer. 97 | * 98 | * @return A size of the argument buffer 99 | */ 100 | int size(); 101 | private: 102 | void append(const std::vector &data); 103 | void insert(int v); 104 | void insert(short v); 105 | void insert(unsigned int v); 106 | void insert(unsigned short v); 107 | void insert(const char *v); 108 | void insert(const wchar_t *v); 109 | void insert(const std::vector& data); 110 | 111 | std::vector data; 112 | }; 113 | 114 | /** 115 | * This structure holds the information about how the mock runner should 116 | * execute the sleepmask. 117 | */ 118 | typedef struct { 119 | DWORD sleepTimeMs; 120 | bool runForever; 121 | } MockSleepMaskConfig; 122 | 123 | /** 124 | * Setup memory for a mock Beacon 125 | * 126 | * The memory layout is created by mimicking Beacon's default reflective loader, 127 | * and the related C2 options 128 | * 129 | * @param stage The applicable stage{} options 130 | * @return the mock Beacon memory structure 131 | */ 132 | BEACON_INFO setupMockBeacon(const bof::profile::Stage& stage); 133 | 134 | /** 135 | * Setup a FUNCTION_CALL structure 136 | * 137 | * @param targetFunction The WINAPI function pointer 138 | * @param targetFunctionName Human readable WinApi enum 139 | * @param bMask Mask beacon 140 | * @param numOfArgs number of arguments for the WINAPI function 141 | * @param ... the arguments for the WINAPI function 142 | */ 143 | FUNCTION_CALL createFunctionCallStructure(PVOID targetFunction, WinApi targetFunctionName, BOOL bMask, int numOfArgs, ...); 144 | 145 | /** 146 | * Change the mock BEACON_INFO strucutre 147 | * 148 | * @param info The new beacon inforamation 149 | */ 150 | void setBeaconInfo(BEACON_INFO& info); 151 | } 152 | 153 | namespace output { 154 | /** 155 | * Data structure to store a output from BOF 156 | */ 157 | struct OutputEntry { 158 | /** 159 | * The callback type. E.g. CALLBACK_OUTPUT 160 | */ 161 | int callbackType; 162 | 163 | /** 164 | * The output data 165 | */ 166 | std::string output; 167 | 168 | /** 169 | * Equivalence overloading. 170 | * 171 | * param other Another OutputEntry object 172 | */ 173 | bool operator==(const OutputEntry& other) const { 174 | return callbackType == other.callbackType && output == other.output; 175 | } 176 | }; 177 | 178 | /** 179 | * Returns the list of BOF outputs 180 | * 181 | * @return A vector of OutputEntry objects 182 | */ 183 | const std::vector& getOutputs(); 184 | 185 | /** 186 | * Clear the currently stored BOF outputs 187 | */ 188 | void reset(); 189 | 190 | /** 191 | * Pretty print an OutputEntry object. 192 | * Required by the GoogleTest. 193 | * 194 | * @param o An OutputEntry object 195 | * @param os An output stream 196 | */ 197 | void PrintTo(const OutputEntry& o, std::ostream* os); 198 | } 199 | 200 | namespace valuestore { 201 | /** 202 | * Clear items in BOF Key/Value store 203 | */ 204 | void reset(); 205 | } 206 | 207 | namespace bud { 208 | /** 209 | * Clear the custom data buffer in Beacon User Data 210 | */ 211 | void reset(); 212 | 213 | /** 214 | * Set the custom data buffer in Beacon User Data 215 | * 216 | * @param data A pointer to custom data buffer 217 | */ 218 | void set(const char* data); 219 | } 220 | 221 | /** 222 | * Execute a BOF with arguments 223 | * 224 | * @param entry BOF's entry point 225 | * @param ... arguments 226 | * @return A vector of OutputEntry objects 227 | */ 228 | template 229 | std::vector runMocked(void (*entry)(char*, int), T &&...v) { 230 | // Setup mock Beacon memory 231 | BEACON_INFO beaconInfo = bof::mock::setupMockBeacon(bof::profile::defaultStage); 232 | bof::mock::setBeaconInfo(beaconInfo); 233 | // Reset the global output container 234 | bof::output::reset(); 235 | // Pack the arguments 236 | bof::mock::BofData args; 237 | args.pack(std::forward(v)...); 238 | // Execute the entrypoint 239 | entry(args.get(), args.size()); 240 | // Return the stored outputs 241 | return bof::output::getOutputs(); 242 | } 243 | 244 | /** 245 | * Setup a mock-up Beacon and execute the sleepmask function once with the default .stage options and mock-up config. 246 | * 247 | * @param sleepMaskFunc the function pointer for the sleepmask 248 | * @return A vector of OutputEntry objects 249 | */ 250 | std::vector runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc); 251 | 252 | /** 253 | * Setup a mock-up Beacon and execute the sleepmask function using a custom stage profile, and the default mock-up config. 254 | * 255 | * @param sleepMaskFunc the function pointer for the sleepmask 256 | * @param stage the stage options 257 | * @return A vector of OutputEntry objects 258 | */ 259 | std::vector runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, const bof::profile::Stage& stage); 260 | 261 | /** 262 | * Setup a mock-up Beacon and execute the sleepmask function using a custom stage profile and mock-up config. 263 | * 264 | * @param sleepMaskFunc the function pointer for the sleepmask 265 | * @param stage the applicable stage{} options 266 | * @param config the mockup config 267 | * @return A vector of OutputEntry objects 268 | */ 269 | std::vector runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, const bof::profile::Stage& stage, const bof::mock::MockSleepMaskConfig& config); 270 | 271 | /** 272 | * Execute the sleepmask. 273 | * 274 | * @param sleepMaskFunc the function pointer for the sleepmask 275 | * @param sleepMaskInfo the pointer to the SLEEPMASK_INFO structure 276 | * @param functionCall the pointer to the FUNCTION_CALL structure 277 | * @return A vector of OutputEntry objects 278 | */ 279 | std::vector runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, PSLEEPMASK_INFO sleepMaskInfo, PFUNCTION_CALL functionCall); 280 | 281 | /** 282 | * Setup a mock-up Beacon and execute the sleepmask function as Beacon Gate with the default stage block. 283 | * 284 | * @param sleepMaskFunc the function pointer for the sleepmask 285 | * @param functionCall the pointer to FUNCTION_CALL structure 286 | * @return A vector of OutputEntry objects 287 | */ 288 | std::vector runMockedBeaconGate(SLEEPMASK_FUNC sleepMaskFunc, PFUNCTION_CALL functionCall); 289 | 290 | /** 291 | * Setup a mock-up Beacon and execute the sleepmask function as Beacon Gate with a custom stage profile. 292 | * 293 | * @param sleepMaskFunc the function pointer for the sleepmask 294 | * @param functionCall the pointer to FUNCTION_CALL structure 295 | * @param stage the stage options 296 | * @return A vector of OutputEntry objects 297 | */ 298 | std::vector runMockedBeaconGate(SLEEPMASK_FUNC sleepMaskFunc, PFUNCTION_CALL functionCall, const bof::profile::Stage& stage); 299 | } -------------------------------------------------------------------------------- /BOF-Template/base/mock_syscalls.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" { 5 | #ifdef _DEBUG 6 | #undef DECLSPEC_IMPORT 7 | #define DECLSPEC_IMPORT 8 | #endif 9 | #include "..\beacon.h" 10 | } 11 | 12 | namespace bof { 13 | namespace mock { 14 | namespace syscall { 15 | /** 16 | * @brief A function to find the first occurrence of a system call instruction in memory. 17 | * 18 | * This function searches for a specific system call instruction pattern within the 19 | * memory block starting at the given address 'addr'. The pattern to search for 20 | * depends on the target architecture: 21 | * 22 | * - On x64, the assembly pattern is: 'syscall; ret;' (0x0f, 0x05, 0xc3). 23 | * - On x86, the assembly pattern is: 'sysenter; ret;' (0x0f, 0x34, 0xc3). 24 | * 25 | * @param addr A pointer to the starting address of the memory block. 26 | * @return A pointer to the first occurrence of the system call instruction pattern 27 | * if found, or NULL if the pattern is not found within the first 32 bytes. 28 | * 29 | */ 30 | 31 | PBYTE FindSyscallInstruction(PBYTE addr) { 32 | #if _M_X64 33 | char syscallPattern[] = { '\x0f', '\x05', '\xc3' }; // syscall; ret; 34 | #else 35 | char syscallPattern[] = { '\x0f', '\x34', '\xc3' }; // sysenter; ret; 36 | #endif 37 | for (int offset = 0; offset < 32; ++offset) { 38 | if (!memcmp(syscallPattern, (char*)addr + offset, sizeof(syscallPattern))) { 39 | return addr + offset; 40 | } 41 | } 42 | return NULL; 43 | } 44 | 45 | /** 46 | * @brief Find the system call number in the memory block 47 | * 48 | * This function searches for a specific pattern within the memory block starting at the 49 | * given address 'addr' to identify the system call number. The pattern to search for 50 | * depends on the target architecture: 51 | * 52 | * - On x64, the assembly pattern is: 'mov r10, rcx; mov eax, ' (0x4c, 0x8b, 0xd1, 0xb8). 53 | * - On x86, the assembly pattern is: 'mov eax, ' (0xb8). 54 | * 55 | * @param addr A pointer to the starting address of the memory block. 56 | * @return The system call number found in memory following the pattern, or 0 if the 57 | * pattern is not found within the first 32 bytes. 58 | * 59 | */ 60 | DWORD FindSyscallNumber(PBYTE addr) { 61 | #if _M_X64 62 | char syscallPattern[] = { '\x4c', '\x8b', '\xd1', '\xb8' }; 63 | #else 64 | char syscallPattern[] = { '\xb8' }; 65 | #endif 66 | for (int offset = 0; offset < 32; ++offset) { 67 | if (!memcmp(syscallPattern, (char*)addr + offset, sizeof(syscallPattern))) { 68 | DWORD* numAddress = (DWORD*)(addr + offset + sizeof(syscallPattern)); 69 | return *numAddress; 70 | } 71 | } 72 | return 0; 73 | } 74 | 75 | /** 76 | * A function to resolve the a system call number and function address. 77 | * 78 | * @param entry A pointer to a SYSCALL_API_ENTRY structure where resolved information will be stored. 79 | * @param funcHash Hash value representing the target function to resolve. 80 | * @return Returns TRUE if the resolution is successful; otherwise, returns FALSE. 81 | * 82 | */ 83 | BOOL ResolveSyscallEntry(PSYSCALL_API_ENTRY entry, const char* funcName) { 84 | // Resolve the NT function address 85 | static HMODULE ntdll = LoadLibraryA("ntdll"); 86 | PVOID fnAddr = GetProcAddress(ntdll, funcName); 87 | 88 | if (!fnAddr) { 89 | return FALSE; 90 | } 91 | 92 | // Find the syscall number 93 | DWORD sysnum = FindSyscallNumber((PBYTE)fnAddr); 94 | 95 | // Find the address of the syscall instruction 96 | PVOID jmpAddr = FindSyscallInstruction((PBYTE)fnAddr); 97 | 98 | #ifdef _M_IX86 99 | if (!jmpAddr) { 100 | jmpAddr = (PVOID)__readfsdword(0xc0); // If WoW64, this returns wow64cpu!X86SwitchTo64BitMode 101 | } 102 | #endif 103 | 104 | // We did not find the syscall 105 | if (sysnum == 0 || jmpAddr == NULL) { 106 | return FALSE; 107 | } 108 | 109 | // Fill the entry 110 | entry->fnAddr = fnAddr; 111 | entry->sysnum = sysnum; 112 | entry->jmpAddr = jmpAddr; 113 | 114 | return TRUE; 115 | } 116 | 117 | /** 118 | * A helper macro for resolving an SYSCALL_API_ENTRY. 119 | * 120 | * @param field The field in the SYSCALL_API structure in which the resolved entry will be stored. 121 | * @param name The function name used to generate a compile-time hash for entry lookup. 122 | */ 123 | #define RESOLVE_ENTRY(field, name) { \ 124 | if(!ResolveSyscallEntry(&field, name)) { assert(false && "Could not resolve the syscall entry"); } \ 125 | } 126 | 127 | /** 128 | * Resolve system call function addresses and syscall numbers. 129 | * 130 | * @param syscalls A pointer to a SYSCALL_API structure. 131 | * @return TRUE if all system call entries are successfully resolved, FALSE otherwise. 132 | * 133 | */ 134 | void ResolveSyscalls(PSYSCALL_API syscalls) { 135 | RESOLVE_ENTRY(syscalls->ntAllocateVirtualMemory, "NtAllocateVirtualMemory"); 136 | RESOLVE_ENTRY(syscalls->ntAllocateVirtualMemory, "NtAllocateVirtualMemory"); 137 | RESOLVE_ENTRY(syscalls->ntProtectVirtualMemory, "NtProtectVirtualMemory"); 138 | RESOLVE_ENTRY(syscalls->ntFreeVirtualMemory, "NtFreeVirtualMemory"); 139 | RESOLVE_ENTRY(syscalls->ntGetContextThread, "NtGetContextThread"); 140 | RESOLVE_ENTRY(syscalls->ntSetContextThread, "NtSetContextThread"); 141 | RESOLVE_ENTRY(syscalls->ntResumeThread, "NtResumeThread"); 142 | RESOLVE_ENTRY(syscalls->ntCreateThreadEx, "NtCreateThreadEx"); 143 | RESOLVE_ENTRY(syscalls->ntOpenProcess, "NtOpenProcess"); 144 | RESOLVE_ENTRY(syscalls->ntOpenThread, "NtOpenThread"); 145 | RESOLVE_ENTRY(syscalls->ntClose, "NtClose"); 146 | RESOLVE_ENTRY(syscalls->ntCreateSection, "NtCreateSection"); 147 | RESOLVE_ENTRY(syscalls->ntMapViewOfSection, "NtMapViewOfSection"); 148 | RESOLVE_ENTRY(syscalls->ntUnmapViewOfSection, "NtUnmapViewOfSection"); 149 | RESOLVE_ENTRY(syscalls->ntQueryVirtualMemory, "NtQueryVirtualMemory"); 150 | RESOLVE_ENTRY(syscalls->ntDuplicateObject, "NtDuplicateObject"); 151 | RESOLVE_ENTRY(syscalls->ntReadVirtualMemory, "NtReadVirtualMemory"); 152 | RESOLVE_ENTRY(syscalls->ntWriteVirtualMemory, "NtWriteVirtualMemory"); 153 | RESOLVE_ENTRY(syscalls->ntReadFile, "NtReadFile"); 154 | RESOLVE_ENTRY(syscalls->ntWriteFile, "NtWriteFile"); 155 | RESOLVE_ENTRY(syscalls->ntCreateFile, "NtCreateFile"); 156 | 157 | /* Added in 411 */ 158 | RESOLVE_ENTRY(syscalls->ntQueueApcThread, "NtQueueApcThread"); 159 | RESOLVE_ENTRY( syscalls->ntCreateProcess, "NtCreateProcess"); 160 | RESOLVE_ENTRY(syscalls->ntOpenProcessToken, "NtOpenProcessToken"); 161 | RESOLVE_ENTRY(syscalls->ntQueryDirectoryFile, "NtQueryDirectoryFile"); 162 | RESOLVE_ENTRY(syscalls->ntTestAlert, "NtTestAlert"); 163 | RESOLVE_ENTRY(syscalls->ntSuspendProcess, "NtSuspendProcess"); 164 | RESOLVE_ENTRY(syscalls->ntResumeProcess, "NtResumeProcess"); 165 | RESOLVE_ENTRY(syscalls->ntQuerySystemInformation, "NtQuerySystemInformation"); 166 | RESOLVE_ENTRY(syscalls->ntSetInformationProcess, "NtSetInformationProcess"); 167 | RESOLVE_ENTRY(syscalls->ntSetInformationThread, "NtSetInformationThread"); 168 | RESOLVE_ENTRY(syscalls->ntQueryInformationProcess, "NtQueryInformationProcess"); 169 | RESOLVE_ENTRY(syscalls->ntQueryInformationThread, "NtQueryInformationThread"); 170 | RESOLVE_ENTRY(syscalls->ntOpenSection, "NtOpenSection"); 171 | RESOLVE_ENTRY(syscalls->ntAdjustPrivilegesToken, "NtAdjustPrivilegesToken"); 172 | RESOLVE_ENTRY(syscalls->ntDeviceIoControlFile, "NtDeviceIoControlFile"); 173 | RESOLVE_ENTRY(syscalls->ntWaitForMultipleObjects, "NtWaitForMultipleObjects"); 174 | } 175 | 176 | /** 177 | * A function to resolve the RTL function address. 178 | * 179 | * @param address A pointer to where the resolved information will be stored. 180 | * @param funcHash Hash value representing the target function to resolve. 181 | * 182 | * @return Returns TRUE if the resolution is successful; otherwise, returns FALSE. 183 | * 184 | */ 185 | BOOL ResolveNtdllFunc(PVOID* address, const char* funcName) { 186 | static const HMODULE ntdll = LoadLibraryA("ntdll"); 187 | *address = GetProcAddress(ntdll, funcName); 188 | return *address != NULL; 189 | } 190 | 191 | /** 192 | * A helper macro for resolving a RTL function address. 193 | * 194 | * @param field The field in the RTL_API structure in which the resolved function address will be stored. 195 | * @param name The function name used to generate a compile-time hash for entry lookup. 196 | */ 197 | #define RESOLVE_RTL_ENTRY(field, name) { \ 198 | if(!ResolveNtdllFunc(&field, name)) { assert(field && "Could not resolve RTL entry"); } \ 199 | } 200 | 201 | /** 202 | * Resolve RTL function addresses. 203 | * 204 | * @param rtls A pointer to a RTL_API structure. 205 | * 206 | */ 207 | void ResolveRtls(PRTL_API rtls) { 208 | /* Resolve the RTL function addresses */ 209 | RESOLVE_RTL_ENTRY(rtls->rtlDosPathNameToNtPathNameUWithStatusAddr, "RtlDosPathNameToNtPathName_U_WithStatus"); 210 | RESOLVE_RTL_ENTRY(rtls->rtlFreeHeapAddr, "RtlFreeHeap"); 211 | rtls->rtlGetProcessHeapAddr = GetProcessHeap(); 212 | assert(rtls->rtlGetProcessHeapAddr && "Could not get the process heap address"); 213 | } 214 | } 215 | } 216 | } -------------------------------------------------------------------------------- /BOF-Template/beacon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Beacon Object Files (BOF) 3 | * ------------------------- 4 | * A Beacon Object File is a light-weight post exploitation tool that runs 5 | * with Beacon's inline-execute command. 6 | * 7 | * Additional BOF resources are available here: 8 | * - https://github.com/Cobalt-Strike/bof_template 9 | * 10 | * Cobalt Strike 4.x 11 | * ChangeLog: 12 | * 1/25/2022: updated for 4.5 13 | * 7/18/2023: Added BeaconInformation API for 4.9 14 | * 7/31/2023: Added Key/Value store APIs for 4.9 15 | * BeaconAddValue, BeaconGetValue, and BeaconRemoveValue 16 | * 8/31/2023: Added Data store APIs for 4.9 17 | * BeaconDataStoreGetItem, BeaconDataStoreProtectItem, 18 | * BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries 19 | * 9/01/2023: Added BeaconGetCustomUserData API for 4.9 20 | * 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL 21 | * Updated the BEACON_INFO data structure to add new parameters 22 | * 4/19/2024: Added BeaconGetSyscallInformation API for 4.10 23 | * 4/25/2024: Added APIs to call Beacon's system call implementation 24 | * 12/18/2024: Updated BeaconGetSyscallInformation API for 4.11 (Breaking changes) 25 | * 2/13/2025: Updated SYSCALL_API structure with more ntAPIs 26 | */ 27 | #ifndef _BEACON_H_ 28 | #define _BEACON_H_ 29 | #include 30 | 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif // __cplusplus 34 | 35 | /* data API */ 36 | typedef struct { 37 | char * original; /* the original buffer [so we can free it] */ 38 | char * buffer; /* current pointer into our buffer */ 39 | int length; /* remaining length of data */ 40 | int size; /* total size of this buffer */ 41 | } datap; 42 | 43 | DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size); 44 | DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size); 45 | DECLSPEC_IMPORT int BeaconDataInt(datap * parser); 46 | DECLSPEC_IMPORT short BeaconDataShort(datap * parser); 47 | DECLSPEC_IMPORT int BeaconDataLength(datap * parser); 48 | DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size); 49 | 50 | /* format API */ 51 | typedef struct { 52 | char * original; /* the original buffer [so we can free it] */ 53 | char * buffer; /* current pointer into our buffer */ 54 | int length; /* remaining length of data */ 55 | int size; /* total size of this buffer */ 56 | } formatp; 57 | 58 | DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz); 59 | DECLSPEC_IMPORT void BeaconFormatReset(formatp * format); 60 | DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, const char * text, int len); 61 | DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, const char * fmt, ...); 62 | DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size); 63 | DECLSPEC_IMPORT void BeaconFormatFree(formatp * format); 64 | DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value); 65 | 66 | /* Output Functions */ 67 | #define CALLBACK_OUTPUT 0x0 68 | #define CALLBACK_OUTPUT_OEM 0x1e 69 | #define CALLBACK_OUTPUT_UTF8 0x20 70 | #define CALLBACK_ERROR 0x0d 71 | #define CALLBACK_CUSTOM 0x1000 72 | #define CALLBACK_CUSTOM_LAST 0x13ff 73 | 74 | 75 | DECLSPEC_IMPORT void BeaconOutput(int type, const char * data, int len); 76 | DECLSPEC_IMPORT void BeaconPrintf(int type, const char * fmt, ...); 77 | 78 | 79 | /* Token Functions */ 80 | DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token); 81 | DECLSPEC_IMPORT void BeaconRevertToken(); 82 | DECLSPEC_IMPORT BOOL BeaconIsAdmin(); 83 | 84 | /* Spawn+Inject Functions */ 85 | DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length); 86 | DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len); 87 | DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len); 88 | DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo); 89 | DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo); 90 | 91 | /* Utility Functions */ 92 | DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max); 93 | 94 | /* Beacon Information */ 95 | /* 96 | * ptr - pointer to the base address of the allocated memory. 97 | * size - the number of bytes allocated for the ptr. 98 | */ 99 | typedef struct { 100 | char * ptr; 101 | size_t size; 102 | } HEAP_RECORD; 103 | #define MASK_SIZE 13 104 | 105 | /* Information the user can set in the USER_DATA via a UDRL */ 106 | typedef enum { 107 | PURPOSE_EMPTY, 108 | PURPOSE_GENERIC_BUFFER, 109 | PURPOSE_BEACON_MEMORY, 110 | PURPOSE_SLEEPMASK_MEMORY, 111 | PURPOSE_BOF_MEMORY, 112 | PURPOSE_USER_DEFINED_MEMORY = 1000 113 | } ALLOCATED_MEMORY_PURPOSE; 114 | 115 | typedef enum { 116 | LABEL_EMPTY, 117 | LABEL_BUFFER, 118 | LABEL_PEHEADER, 119 | LABEL_TEXT, 120 | LABEL_RDATA, 121 | LABEL_DATA, 122 | LABEL_PDATA, 123 | LABEL_RELOC, 124 | LABEL_USER_DEFINED = 1000 125 | } ALLOCATED_MEMORY_LABEL; 126 | 127 | typedef enum { 128 | METHOD_UNKNOWN, 129 | METHOD_VIRTUALALLOC, 130 | METHOD_HEAPALLOC, 131 | METHOD_MODULESTOMP, 132 | METHOD_NTMAPVIEW, 133 | METHOD_USER_DEFINED = 1000, 134 | } ALLOCATED_MEMORY_ALLOCATION_METHOD; 135 | 136 | /** 137 | * This structure allows the user to provide additional information 138 | * about the allocated heap for cleanup. It is mandatory to provide 139 | * the HeapHandle but the DestroyHeap Boolean can be used to indicate 140 | * whether the clean up code should destroy the heap or simply free the pages. 141 | * This is useful in situations where a loader allocates memory in the 142 | * processes current heap. 143 | */ 144 | typedef struct _HEAPALLOC_INFO { 145 | PVOID HeapHandle; 146 | BOOL DestroyHeap; 147 | } HEAPALLOC_INFO, *PHEAPALLOC_INFO; 148 | 149 | typedef struct _MODULESTOMP_INFO { 150 | HMODULE ModuleHandle; 151 | } MODULESTOMP_INFO, *PMODULESTOMP_INFO; 152 | 153 | typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION { 154 | HEAPALLOC_INFO HeapAllocInfo; 155 | MODULESTOMP_INFO ModuleStompInfo; 156 | PVOID Custom; 157 | } ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION; 158 | 159 | typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION { 160 | BOOL Cleanup; 161 | ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod; 162 | ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation; 163 | } ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION; 164 | 165 | typedef struct _ALLOCATED_MEMORY_SECTION { 166 | ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development 167 | PVOID BaseAddress; // Pointer to virtual address of section 168 | SIZE_T VirtualSize; // Virtual size of the section 169 | DWORD CurrentProtect; // Current memory protection of the section 170 | DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking) 171 | BOOL MaskSection; // A boolean to indicate whether the section should be masked 172 | } ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION; 173 | 174 | typedef struct _ALLOCATED_MEMORY_REGION { 175 | ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory 176 | PVOID AllocationBase; // The base address of the allocated memory block 177 | SIZE_T RegionSize; // The size of the allocated memory block 178 | DWORD Type; // The type of memory allocated 179 | ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures 180 | ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation 181 | } ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION; 182 | 183 | typedef struct { 184 | ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6]; 185 | } ALLOCATED_MEMORY, *PALLOCATED_MEMORY; 186 | 187 | /* 188 | * version - The version of the beacon dll was added for release 4.10 189 | * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch 190 | * e.g. 0x040900 -> CS 4.9 191 | * 0x041000 -> CS 4.10 192 | * 193 | * sleep_mask_ptr - pointer to the sleep mask base address 194 | * sleep_mask_text_size - the sleep mask text section size 195 | * sleep_mask_total_size - the sleep mask total memory size 196 | * 197 | * beacon_ptr - pointer to beacon's base address 198 | * The stage.obfuscate flag affects this value when using CS default loader. 199 | * true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address) 200 | * false: beacon_ptr = allocated_buffer (A valid address) 201 | * For a UDRL the beacon_ptr will be set to the 1st argument to DllMain 202 | * when the 2nd argument is set to DLL_PROCESS_ATTACH. 203 | * heap_records - list of memory addresses on the heap beacon wants to mask. 204 | * The list is terminated by the HEAP_RECORD.ptr set to NULL. 205 | * mask - the mask that beacon randomly generated to apply 206 | * 207 | * Added in version 4.10 208 | * allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA 209 | * via a UDRL. 210 | */ 211 | typedef struct { 212 | unsigned int version; 213 | char * sleep_mask_ptr; 214 | DWORD sleep_mask_text_size; 215 | DWORD sleep_mask_total_size; 216 | 217 | char * beacon_ptr; 218 | HEAP_RECORD * heap_records; 219 | char mask[MASK_SIZE]; 220 | 221 | ALLOCATED_MEMORY allocatedMemory; 222 | } BEACON_INFO, *PBEACON_INFO; 223 | 224 | DECLSPEC_IMPORT BOOL BeaconInformation(PBEACON_INFO info); 225 | 226 | /* Key/Value store functions 227 | * These functions are used to associate a key to a memory address and save 228 | * that information into beacon. These memory addresses can then be 229 | * retrieved in a subsequent execution of a BOF. 230 | * 231 | * key - the key will be converted to a hash which is used to locate the 232 | * memory address. 233 | * 234 | * ptr - a memory address to save. 235 | * 236 | * Considerations: 237 | * - The contents at the memory address is not masked by beacon. 238 | * - The contents at the memory address is not released by beacon. 239 | * 240 | */ 241 | DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr); 242 | DECLSPEC_IMPORT void * BeaconGetValue(const char * key); 243 | DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key); 244 | 245 | /* Beacon Data Store functions 246 | * These functions are used to access items in Beacon's Data Store. 247 | * BeaconDataStoreGetItem returns NULL if the index does not exist. 248 | * 249 | * The contents are masked by default, and BOFs must unprotect the entry 250 | * before accessing the data buffer. BOFs must also protect the entry 251 | * after the data is not used anymore. 252 | * 253 | */ 254 | 255 | #define DATA_STORE_TYPE_EMPTY 0 256 | #define DATA_STORE_TYPE_GENERAL_FILE 1 257 | 258 | typedef struct { 259 | int type; 260 | DWORD64 hash; 261 | BOOL masked; 262 | char* buffer; 263 | size_t length; 264 | } DATA_STORE_OBJECT, *PDATA_STORE_OBJECT; 265 | 266 | DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index); 267 | DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index); 268 | DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index); 269 | DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries(); 270 | 271 | /* Beacon User Data functions */ 272 | DECLSPEC_IMPORT char * BeaconGetCustomUserData(); 273 | 274 | /* Beacon System call */ 275 | /* Syscalls API */ 276 | typedef struct 277 | { 278 | PVOID fnAddr; 279 | PVOID jmpAddr; 280 | DWORD sysnum; 281 | } SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY; 282 | 283 | typedef struct 284 | { 285 | SYSCALL_API_ENTRY ntAllocateVirtualMemory; 286 | SYSCALL_API_ENTRY ntProtectVirtualMemory; 287 | SYSCALL_API_ENTRY ntFreeVirtualMemory; 288 | SYSCALL_API_ENTRY ntGetContextThread; 289 | SYSCALL_API_ENTRY ntSetContextThread; 290 | SYSCALL_API_ENTRY ntResumeThread; 291 | SYSCALL_API_ENTRY ntCreateThreadEx; 292 | SYSCALL_API_ENTRY ntOpenProcess; 293 | SYSCALL_API_ENTRY ntOpenThread; 294 | SYSCALL_API_ENTRY ntClose; 295 | SYSCALL_API_ENTRY ntCreateSection; 296 | SYSCALL_API_ENTRY ntMapViewOfSection; 297 | SYSCALL_API_ENTRY ntUnmapViewOfSection; 298 | SYSCALL_API_ENTRY ntQueryVirtualMemory; 299 | SYSCALL_API_ENTRY ntDuplicateObject; 300 | SYSCALL_API_ENTRY ntReadVirtualMemory; 301 | SYSCALL_API_ENTRY ntWriteVirtualMemory; 302 | SYSCALL_API_ENTRY ntReadFile; 303 | SYSCALL_API_ENTRY ntWriteFile; 304 | SYSCALL_API_ENTRY ntCreateFile; 305 | SYSCALL_API_ENTRY ntQueueApcThread; 306 | SYSCALL_API_ENTRY ntCreateProcess; 307 | SYSCALL_API_ENTRY ntOpenProcessToken; 308 | SYSCALL_API_ENTRY ntTestAlert; 309 | SYSCALL_API_ENTRY ntSuspendProcess; 310 | SYSCALL_API_ENTRY ntResumeProcess; 311 | SYSCALL_API_ENTRY ntQuerySystemInformation; 312 | SYSCALL_API_ENTRY ntQueryDirectoryFile; 313 | SYSCALL_API_ENTRY ntSetInformationProcess; 314 | SYSCALL_API_ENTRY ntSetInformationThread; 315 | SYSCALL_API_ENTRY ntQueryInformationProcess; 316 | SYSCALL_API_ENTRY ntQueryInformationThread; 317 | SYSCALL_API_ENTRY ntOpenSection; 318 | SYSCALL_API_ENTRY ntAdjustPrivilegesToken; 319 | SYSCALL_API_ENTRY ntDeviceIoControlFile; 320 | SYSCALL_API_ENTRY ntWaitForMultipleObjects; 321 | } SYSCALL_API, *PSYSCALL_API; 322 | 323 | /* Additional Run Time Library (RTL) addresses used to support system calls. 324 | * If they are not set then system calls that require them will fall back 325 | * to the Standard Windows API. 326 | * 327 | * Required to support the following system calls: 328 | * ntCreateFile 329 | */ 330 | typedef struct 331 | { 332 | PVOID rtlDosPathNameToNtPathNameUWithStatusAddr; 333 | PVOID rtlFreeHeapAddr; 334 | PVOID rtlGetProcessHeapAddr; 335 | } RTL_API, *PRTL_API; 336 | 337 | /* Updated in version 4.11 to use the entire structure instead of pointers to the structure. 338 | * This allows for retrieving a copy of the information which would be under the BOF's 339 | * control instead of a reference pointer which may be obfuscated when beacon is sleeping. 340 | */ 341 | typedef struct 342 | { 343 | SYSCALL_API syscalls; 344 | RTL_API rtls; 345 | } BEACON_SYSCALLS, *PBEACON_SYSCALLS; 346 | 347 | /* Updated in version 4.11 to include the size of the info pointer, which equals sizeof(BEACON_SYSCALLS) */ 348 | DECLSPEC_IMPORT BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized); 349 | 350 | /* Beacon System call functions which will use the current system call method */ 351 | DECLSPEC_IMPORT LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); 352 | DECLSPEC_IMPORT LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect); 353 | DECLSPEC_IMPORT BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); 354 | DECLSPEC_IMPORT BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect); 355 | DECLSPEC_IMPORT BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType); 356 | DECLSPEC_IMPORT BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); 357 | DECLSPEC_IMPORT BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext); 358 | DECLSPEC_IMPORT DWORD BeaconResumeThread(HANDLE threadHandle); 359 | DECLSPEC_IMPORT HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId); 360 | DECLSPEC_IMPORT HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId); 361 | DECLSPEC_IMPORT BOOL BeaconCloseHandle(HANDLE object); 362 | DECLSPEC_IMPORT BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress); 363 | DECLSPEC_IMPORT SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length); 364 | DECLSPEC_IMPORT BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions); 365 | DECLSPEC_IMPORT BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead); 366 | DECLSPEC_IMPORT BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten); 367 | 368 | /* Beacon Gate APIs */ 369 | DECLSPEC_IMPORT VOID BeaconDisableBeaconGate(); 370 | DECLSPEC_IMPORT VOID BeaconEnableBeaconGate(); 371 | 372 | DECLSPEC_IMPORT VOID BeaconDisableBeaconGateMasking(); 373 | DECLSPEC_IMPORT VOID BeaconEnableBeaconGateMasking(); 374 | 375 | /* Beacon User Data 376 | * 377 | * version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch 378 | * e.g. 0x040900 -> CS 4.9 379 | * 0x041000 -> CS 4.10 380 | */ 381 | 382 | #define DLL_BEACON_USER_DATA 0x0d 383 | #define BEACON_USER_DATA_CUSTOM_SIZE 32 384 | typedef struct 385 | { 386 | unsigned int version; 387 | PSYSCALL_API syscalls; 388 | char custom[BEACON_USER_DATA_CUSTOM_SIZE]; 389 | PRTL_API rtls; 390 | PALLOCATED_MEMORY allocatedMemory; 391 | } USER_DATA, * PUSER_DATA; 392 | 393 | #ifdef __cplusplus 394 | } 395 | #endif // __cplusplus 396 | #endif // _BEACON_H_ 397 | -------------------------------------------------------------------------------- /BOF-Template/beacon_gate.h: -------------------------------------------------------------------------------- 1 | #ifndef _BEACON_GATE_H 2 | #define _BEACON_GATE_H 3 | #include 4 | 5 | /* Beacon gate defines. */ 6 | #define MAX_BEACON_GATE_ARGUMENTS 10 7 | #define beaconGate(i) ((BEACON_GATE_##i)gateFunction->functionPtr) 8 | #define arg(i) (ULONG_PTR)gateFunction->args[i] 9 | 10 | /* Enum to specify what WinAPI is being called. */ 11 | typedef enum _WinApi { 12 | INTERNETOPENA, 13 | INTERNETCONNECTA, 14 | VIRTUALALLOC, 15 | VIRTUALALLOCEX, 16 | VIRTUALPROTECT, 17 | VIRTUALPROTECTEX, 18 | VIRTUALFREE, 19 | GETTHREADCONTEXT, 20 | SETTHREADCONTEXT, 21 | RESUMETHREAD, 22 | CREATETHREAD, 23 | CREATEREMOTETHREAD, 24 | OPENPROCESS, 25 | OPENTHREAD, 26 | CLOSEHANDLE, 27 | CREATEFILEMAPPING, 28 | MAPVIEWOFFILE, 29 | UNMAPVIEWOFFILE, 30 | VIRTUALQUERY, 31 | DUPLICATEHANDLE, 32 | READPROCESSMEMORY, 33 | WRITEPROCESSMEMORY, 34 | EXITTHREAD, 35 | } WinApi; 36 | 37 | /** 38 | * FUNCTION_CALL struct which encapsulates atomic function call. 39 | * 40 | * functionPtr - target function to call 41 | * function - Enum representing target WinApi 42 | * numOfArgs - number of arguments 43 | * args - array of ULONG_PTRs containing the passed arguments (e.g. rcx, rdx, ...) 44 | * bMask - BOOL indicating whether Beacon should be masked during the call 45 | * ULONG_PTR - retValue of the atomic function call 46 | */ 47 | typedef struct { 48 | PVOID functionPtr; 49 | WinApi function; 50 | int numOfArgs; 51 | ULONG_PTR args[MAX_BEACON_GATE_ARGUMENTS]; 52 | BOOL bMask; 53 | ULONG_PTR retValue; 54 | } FUNCTION_CALL, * PFUNCTION_CALL; 55 | 56 | /* Currently support max 10 arguments. */ 57 | /* NB For x86 we only support std call convention as this is what Windows uses for most Win32 APIs. */ 58 | typedef ULONG_PTR(__stdcall* BEACON_GATE_00)(VOID); 59 | typedef ULONG_PTR(__stdcall* BEACON_GATE_01)(ULONG_PTR); 60 | typedef ULONG_PTR(__stdcall* BEACON_GATE_02)(ULONG_PTR, ULONG_PTR); 61 | typedef ULONG_PTR(__stdcall* BEACON_GATE_03)(ULONG_PTR, ULONG_PTR, ULONG_PTR); 62 | typedef ULONG_PTR(__stdcall* BEACON_GATE_04)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 63 | typedef ULONG_PTR(__stdcall* BEACON_GATE_05)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 64 | typedef ULONG_PTR(__stdcall* BEACON_GATE_06)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 65 | typedef ULONG_PTR(__stdcall* BEACON_GATE_07)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 66 | typedef ULONG_PTR(__stdcall* BEACON_GATE_08)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 67 | typedef ULONG_PTR(__stdcall* BEACON_GATE_09)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 68 | typedef ULONG_PTR(__stdcall* BEACON_GATE_10)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR); 69 | #endif // _BEACON_GATE_H 70 | -------------------------------------------------------------------------------- /BOF-Template/bof.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "base\helpers.h" 3 | 4 | /** 5 | * For the debug build we want: 6 | * a) Include the mock-up layer 7 | * b) Undefine DECLSPEC_IMPORT since the mocked Beacon API 8 | * is linked against the the debug build. 9 | */ 10 | #ifdef _DEBUG 11 | #undef DECLSPEC_IMPORT 12 | #define DECLSPEC_IMPORT 13 | #include "base\mock.h" 14 | #endif 15 | 16 | extern "C" { 17 | #include "beacon.h" 18 | #include "sleepmask.h" 19 | 20 | // Define the Dynamic Function Resolution declaration for the GetLastError function 21 | DFR(KERNEL32, GetLastError); 22 | // Map GetLastError to KERNEL32$GetLastError 23 | #define GetLastError KERNEL32$GetLastError 24 | 25 | void go(char* args, int len) { 26 | /** 27 | * Define the Dynamic Function Resolution declaration for the GetSystemDirectoryA function 28 | * This time we use the DFR_LOCAL macro which create a local function pointer variable that 29 | * points to GetSystemDirectoryA. Therefore, we do have to map GetSystemDirectoryA to 30 | * KERNEL32$GetSystemDirectoryA 31 | */ 32 | DFR_LOCAL(KERNEL32, GetSystemDirectoryA); 33 | char path[MAX_PATH + 1]; 34 | 35 | UINT bytesCopied = GetSystemDirectoryA(path, sizeof(path)); 36 | if (bytesCopied == 0) { 37 | BeaconPrintf(CALLBACK_ERROR, "Error: %i", GetLastError()); 38 | } 39 | else if (bytesCopied <= sizeof(path)) { 40 | BeaconPrintf(CALLBACK_OUTPUT, "System Directory: %s", path); 41 | } 42 | } 43 | 44 | /* 45 | void sleep_mask(PSLEEPMASK_INFO info, PFUNCTION_CALL funcCall) { 46 | } 47 | */ 48 | } 49 | 50 | // Define a main function for the bebug build 51 | #if defined(_DEBUG) && !defined(_GTEST) 52 | 53 | int main(int argc, char* argv[]) { 54 | // Run BOF's entrypoint 55 | // To pack arguments for the bof use e.g.: bof::runMocked(go, 6502, 42, "foobar"); 56 | bof::runMocked<>(go); 57 | 58 | /* To test a sleepmask BOF, the following mockup executors can be used 59 | // Mock up Beacon and run the sleep mask once 60 | bof::runMockedSleepMask(sleep_mask); 61 | 62 | // Mock up Beacon with the specific .stage C2 profile 63 | bof::runMockedSleepMask(sleep_mask, 64 | { 65 | .allocator = bof::profile::Allocator::VirtualAlloc, 66 | .obfuscate = bof::profile::Obfuscate::False, 67 | .useRWX = bof::profile::UseRWX::True, 68 | .module = "", 69 | }, 70 | { 71 | .sleepTimeMs = 5000, 72 | .runForever = false, 73 | } 74 | ); 75 | */ 76 | 77 | return 0; 78 | } 79 | 80 | // Define unit tests 81 | #elif defined(_GTEST) 82 | #include 83 | 84 | TEST(BofTest, Test1) { 85 | std::vector got = 86 | bof::runMocked<>(go); 87 | std::vector expected = { 88 | {CALLBACK_OUTPUT, "System Directory: C:\\Windows\\system32"} 89 | }; 90 | // It is possible to compare the OutputEntry vectors, like directly 91 | // ASSERT_EQ(expected, got); 92 | // However, in this case, we want to compare the output, ignoring the case. 93 | ASSERT_EQ(expected.size(), got.size()); 94 | ASSERT_STRCASEEQ(expected[0].output.c_str(), got[0].output.c_str()); 95 | } 96 | #endif -------------------------------------------------------------------------------- /BOF-Template/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /BOF-Template/sleepmask.h: -------------------------------------------------------------------------------- 1 | #ifndef _SLEEPMASK_H_ 2 | #define _SLEEPMASK_H_ 3 | 4 | #include 5 | #include "beacon.h" 6 | #include "beacon_gate.h" 7 | 8 | /* Define the supported action types for the pivot beacons */ 9 | typedef enum _PIVOT_ACTION { 10 | ACTION_UNKNOWN, 11 | ACTION_TCP_RECV, 12 | ACTION_TCP_ACCEPT, 13 | ACTION_PIPE_WAIT, 14 | ACTION_PIPE_PEEK 15 | } PIVOT_ACTION; 16 | 17 | /* 18 | * action - defines which ACTION_ type to use in the pivot_sleep 19 | * in - defines the in socket for the ACTION_TCP_ types 20 | * out - defines the out socket for the ACTION_TCP_ types 21 | * pipe - defines the pipe for the ACTION_PIPE_ types 22 | */ 23 | typedef struct _PIVOT_ARGS { 24 | PIVOT_ACTION action; 25 | SOCKET in; 26 | SOCKET out; 27 | HANDLE pipe; 28 | } PIVOT_ARGS, * PPIVOT_ARGS; 29 | 30 | typedef enum _REASON_FOR_CALL { 31 | DEFAULT_SLEEP, 32 | PIVOT_SLEEP, 33 | BEACON_GATE 34 | } REASON_FOR_CALL; 35 | 36 | /* 37 | * version - version of the structure. format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch 38 | * reason - reason for the call (default sleep, pivot sleep, beacon gate) 39 | * sleep_time - the time to sleep in milliseconds 40 | * beacon_info - the BEACON_INFO structure 41 | * pivot_args - the PIVOT_ARGS structure 42 | */ 43 | typedef struct _SLEEPMASK_INFO { 44 | unsigned int version; 45 | REASON_FOR_CALL reason; 46 | DWORD sleep_time; 47 | BEACON_INFO beacon_info; 48 | PIVOT_ARGS pivot_args; 49 | } SLEEPMASK_INFO, * PSLEEPMASK_INFO; 50 | 51 | typedef void(* SLEEPMASK_FUNC)(PSLEEPMASK_INFO, PFUNCTION_CALL); 52 | #endif // _SLEEPMASK_H_ 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Beacon Object File Visual Studio Template 2 | 3 | This repository contains the Beacon Object File Visual Studio (BOF-VS) template project. 4 | You can read more about rationale and design decisions from this blog [post](https://www.cobaltstrike.com/blog/simplifying-bof-development). 5 | 6 | ## Quick Start Guide 7 | 8 | To get started, use the instructions provided below. 9 | 10 | ### Prerequisites: 11 | 12 | * An x64 Windows 10/11 development machine (without a security solution) 13 | * Visual Studio Community/Pro/Enterprise 2022 (Desktop Development with C++ installed) 14 | 15 | ### Template Installation 16 | 17 | Download the latest [release](https://github.com/Cobalt-Strike/bof-vs/releases/latest/download/bof-vs.zip), 18 | and copy the `bof-vs.zip` archive under the 19 | `%USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates` folder. 20 | The template is accessible through Visual Studio's new project dialog, 21 | where you can locate it by searching with the keyword `BOF`. Be certain 22 | to have `All languages` chosen as the language filter. 23 | 24 | If Visual Studio does not recognize the template, then reset the project template cache by 25 | deleting the following file: `%localappdata%\Microsoft\VisualStudio\\ProjectTemplatesCache_{}\cache.bin` 26 | 27 | If you need a BOF-VS template for a previous version of Cobalt Strike, you can find it under the [tags](https://github.com/Cobalt-Strike/bof-vs/tags). 28 | 29 | ### Debug Build 30 | 31 | The `Debug` target builds your BOF to an executable, which allows 32 | you to benefit from the convenience of debugging your BOF code directly within 33 | Visual Studio's built-in debugger. This will enable you to work at the source 34 | code level without running the BOF through a Beacon. 35 | 36 | ### Release Build 37 | 38 | The Release target compiles a release object file of your BOF, 39 | which is designed to be used directly with Cobalt Strike. 40 | 41 | ## Dynamic Function Resolution 42 | 43 | The project template includes two macro definitions to facilitate Dynamic 44 | Function Resolution (DFR) declarations. These macros provide a robust mechanism 45 | for efficiently resolving Win32 API functions in BOFs and simplify the 46 | development process significantly. 47 | 48 | ### DFR Macro 49 | 50 | The `DFR` macro can automatically extract the function type and generate 51 | the required function declaration. 52 | 53 | ```cpp 54 | 55 | DFR(KERNEL32, OpenProcess) 56 | ``` 57 | 58 | The above `DFR` macro statement expands to the following declaration. 59 | 60 | ```cpp 61 | 62 | DECLSPEC_IMPORT decltype(OpenProcess) KERNEL32$OpenProcess; 63 | ``` 64 | 65 | A common practice is to map the `KERNEL32$OpenProcess` function to OpenProcess 66 | using the following macro definition. This mapping enables you to call the 67 | OpenProcess function directly, eliminating the need for the `KERNEL32$` prefix. 68 | 69 | ```cpp 70 | #define OpenProcess KERNEL32$OpenProcess 71 | ``` 72 | 73 | #### Example Usage 74 | 75 | ```cpp 76 | DFR(KERNEL32, OpenProcess) 77 | #define OpenProcess KERNEL32$OpenProcess 78 | 79 | void func1() { 80 | OpenProcess(...); 81 | } 82 | 83 | void func2() { 84 | OpenProcess(...); 85 | } 86 | ``` 87 | 88 | ### DFR_LOCAL Macro 89 | 90 | The `DFR_LOCAL(module, function)` macro allows you to define a local function 91 | pointer variable that directly references the `module$function` function. One 92 | of the main advantages of using this macro compared to the `DFR` macro is 93 | the elimination of the need for the additional `OpenProcess` -> `KERNEL32$OpenProcess` 94 | mapping. This streamlines the code and makes it more concise. However, it's 95 | important to note that the function pointer created with the `DFR_LOCAL` macro 96 | has a limited scope and can only be accessed within the function where it is defined. 97 | Consequently, if you plan to use the required WINAPI functions in multiple 98 | functions throughout your BOF, you will need to define the function pointer 99 | using the DFR_LOCAL macro in each of those functions. 100 | 101 | #### Example Usage 102 | 103 | ```cpp 104 | void func1() { 105 | DFR_LOCAL(KERNEL32, OpenProcess); 106 | OpenProcess(...); 107 | } 108 | 109 | void func2() { 110 | DFR_LOCAL(KERNEL32, OpenProcess); 111 | OpenProcess(...); 112 | } 113 | ``` 114 | 115 | ## Mocked APIs 116 | 117 | The template includes a mocked version of the Beacon API for Debug builds, 118 | enabling BOF debugging without a running Beacon instance. When you select 119 | either the Debug or UnitTest configuration, the mocked API is automatically 120 | included into the project. 121 | 122 | ### Argument Packer 123 | 124 | The BofData class implements an argument packer to replicate the argument 125 | packing behavior of the bof_pack aggressor function. This enables us to 126 | call BOF's entry point with custom arguments without Beacon. 127 | 128 | ```cpp 129 | bof::mock::BofData data; 130 | // the pack function takes one or more arguments 131 | data.pack(6502, 80, 68010, "Hello World"); 132 | 133 | // alternatively, the << operator can be used to construct the arguments buffer 134 | data << 0xdeadface << L"Hello World"; 135 | 136 | // raw buffers can be added too 137 | const char buf[] = { 0x41, 0x42, 0x43, 0x44 }; 138 | data.addData(buf, sizeof(buf)); 139 | 140 | go(data.get(), data.size()); 141 | ``` 142 | 143 | ### Beacon API 144 | 145 | The template also provides a mocked implementation of the Data Parser, Output, 146 | and Format APIs. The mocked functions within the Output API print the output 147 | to the standard output, ensuring the results are visible. Moreover, all returned 148 | output is stored for future examination and analysis. 149 | 150 | Furthermore, the Internal API functions (such as BeaconUseToken, 151 | BeaconInjectProcess, etc.) are declared. However, it is important to note that 152 | these functions lack the real implementation and only display an error message 153 | on the standard error if called. 154 | 155 | ## Unit Tests 156 | 157 | The project template offers an additional build target called `UnitTest`, 158 | specifically designed to build BOFs with the GoogleTest framework. Furthermore, 159 | the mock library provides a convenient `runMocked` function that handles 160 | the argument packing, execution of the BOF's entry point, and capturing all 161 | generated outputs. 162 | 163 | Install the GoogleTest framework: 164 | 165 | 1. Right-click the project name in Solution Explorer. 166 | 2. Select `Manage NuGet Packages`. 167 | 3. Ensure that the `Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static` package is installed. 168 | 169 | ### Example Usage 170 | ```cpp 171 | extern "C" { 172 | #include "beacon.h" 173 | 174 | void go(char* args, int len) { 175 | datap parser; 176 | BeaconDataParse(&parser, args, len); 177 | int number = BeaconDataInt(&parser); 178 | BeaconPrintf(CALLBACK_OUTPUT, "Hello: %i", number); 179 | } 180 | } 181 | 182 | TEST(ExampleBofTest, TestCase1) { 183 | std::vector actual = 184 | bof::runMocked(go, 6502); 185 | 186 | std::vector expected = { 187 | {CALLBACK_OUTPUT, "Hello: 6502"} 188 | }; 189 | 190 | ASSERT_EQ(expected, actual); 191 | } 192 | ``` 193 | 194 | ## Sleepmask 195 | In addition to supporting standard Beacon Object Files, the template also includes 196 | functionality for developing Sleepmask BOFs. Beacon's Sleepmask can be used to apply 197 | runtime masking to its PE sections and Heap allocations. Therefore, this template 198 | creates a "mock Beacon" as part of the call to runMockedSleepMask() to replicate 199 | the layout of Beacon in memory during debugging. This function also makes it possible 200 | to apply malleable C2 settings to the "mock Beacon". 201 | 202 | 203 | ```c 204 | // Mock up Beacon and run the sleep mask once 205 | bof::runMockedSleepMask(sleep_mask); 206 | 207 | // Mock up Beacon with the specific .stage C2 profile 208 | bof::runMockedSleepMask(sleep_mask, 209 | { 210 | .allocator = bof::profile::Allocator::VirtualAlloc, 211 | .obfuscate = bof::profile::Obfuscate::False, 212 | .useRWX = bof::profile::UseRWX::True, 213 | .module = "", 214 | }, 215 | { 216 | .sleepTimeMs = 5000, 217 | .runForever = false, 218 | } 219 | ); 220 | ``` 221 | 222 | 223 | ## Multiple BOFs 224 | 225 | The template supports multiple BOFs within separate files, enabling each .cpp 226 | file to be compiled into an individual BOF. This approach eliminates the need 227 | for multiple projects and allows grouping similar BOFs under one Visual Studio 228 | project. When it comes to debugging, since each BOF is compiled into its own 229 | debug executable, it is important to adjust the debug command accordingly. 230 | This can be done through the project properties: `Configuration Properties 231 | -> Debugging -> Command`. 232 | --------------------------------------------------------------------------------