mirror of
https://github.com/e-fin/ADWS-BOF
synced 2026-06-23 09:33:17 +00:00
initialcommit
This commit is contained in:
+398
@@ -0,0 +1,398 @@
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Ww][Ii][Nn]32/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# ASP.NET Scaffolding
|
||||
ScaffoldingReadMe.txt
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.tlog
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Coverlet is a free, cross platform Code Coverage Tool
|
||||
coverage*.json
|
||||
coverage*.xml
|
||||
coverage*.info
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||
*.vbp
|
||||
|
||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||
*.dsw
|
||||
*.dsp
|
||||
|
||||
# Visual Studio 6 technical files
|
||||
*.ncb
|
||||
*.aps
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# Visual Studio History (VSHistory) files
|
||||
.vshistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
|
||||
# VS Code files for those working on multiple tools
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
*.code-workspace
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Windows Installer files from build outputs
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# JetBrains Rider
|
||||
*.sln.iml
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 18
|
||||
VisualStudioVersion = 18.6.11819.183 stable
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ADWS-BOF", "ADWS-BOF\ADWS-BOF.vcxproj", "{20D4C7A1-5B0E-489D-8871-DE3C9961663E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
UnitTest|x64 = UnitTest|x64
|
||||
UnitTest|x86 = UnitTest|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.Debug|x64.Build.0 = Debug|x64
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.Debug|x86.Build.0 = Debug|Win32
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.Release|x64.ActiveCfg = Release|x64
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.Release|x64.Build.0 = Release|x64
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.Release|x86.ActiveCfg = Release|Win32
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.Release|x86.Build.0 = Release|Win32
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.UnitTest|x64.ActiveCfg = UnitTest|x64
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.UnitTest|x64.Build.0 = UnitTest|x64
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.UnitTest|x86.ActiveCfg = UnitTest|Win32
|
||||
{20D4C7A1-5B0E-489D-8871-DE3C9961663E}.UnitTest|x86.Build.0 = UnitTest|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {05614438-CD94-4CA7-899A-327C067C1C3B}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -0,0 +1,256 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="UnitTest|Win32">
|
||||
<Configuration>UnitTest</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="UnitTest|x64">
|
||||
<Configuration>UnitTest</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{20D4C7A1-5B0E-489D-8871-DE3C9961663E}</ProjectGuid>
|
||||
<RootNamespace>BOFTemplate</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>ADWS-BOF</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<LocalDebuggerCommand>Debug\bof.exe</LocalDebuggerCommand>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UnitTest|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<LocalDebuggerCommand>x64\Debug\bof.exe</LocalDebuggerCommand>
|
||||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UnitTest|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Makefile</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v145</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='UnitTest|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='UnitTest|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<NMakeBuildCommandLine>nmake /A all</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>nmake /A all</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>nmake clean</NMakeCleanCommandLine>
|
||||
<AdditionalOptions>/std:c++20</AdditionalOptions>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<NMakeBuildCommandLine>nmake /A all</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>nmake /A all</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>nmake clean</NMakeCleanCommandLine>
|
||||
<AdditionalOptions>/std:c++20</AdditionalOptions>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<NMakeBuildCommandLine>nmake /A all-debug</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>nmake /A all-debug</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>nmake clean</NMakeCleanCommandLine>
|
||||
<AdditionalOptions>/std:c++20</AdditionalOptions>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UnitTest|Win32'">
|
||||
<NMakeBuildCommandLine>nmake /A all-debug</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>nmake /A all-debug</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>nmake clean</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<NMakeBuildCommandLine>nmake /A all-debug</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>nmake /A all-debug</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>nmake clean</NMakeCleanCommandLine>
|
||||
<IncludePath>$(MSBuildProjectDirectory)\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\build\native\include;$(IncludePath)</IncludePath>
|
||||
<AdditionalOptions>/std:c++20</AdditionalOptions>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='UnitTest|x64'">
|
||||
<NMakeBuildCommandLine>nmake /A all-debug</NMakeBuildCommandLine>
|
||||
<NMakeReBuildCommandLine>nmake /A all-debug</NMakeReBuildCommandLine>
|
||||
<NMakeCleanCommandLine>nmake clean</NMakeCleanCommandLine>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='UnitTest|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_GTEST;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\build\native\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(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)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='UnitTest|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_GTEST;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)packages\Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static.1.8.1.7\build\native\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>$(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)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="base\mock.cpp" />
|
||||
<ClCompile Include="base\mock_syscalls.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='UnitTest|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='UnitTest|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="adwsldap.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">C:\Users\User\source\repos\BOF-Template\x64\Debug\bof.exe</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="base\helpers.h" />
|
||||
<ClInclude Include="base\mock.h" />
|
||||
<ClInclude Include="beacon.h" />
|
||||
<ClInclude Include="beacon_gate.h" />
|
||||
<ClInclude Include="sleepmask.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Makefile" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="utils\boflint.py" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\base">
|
||||
<UniqueIdentifier>{b74d73f4-acc9-410d-a627-90481555d13a}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files\base">
|
||||
<UniqueIdentifier>{8fa0224c-f4ab-4998-991b-bf2b56db2848}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="adwsldap.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="base\mock.cpp">
|
||||
<Filter>Source Files\base</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="base\mock_syscalls.cpp">
|
||||
<Filter>Source Files\base</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="beacon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="base\helpers.h">
|
||||
<Filter>Header Files\base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="base\mock.h">
|
||||
<Filter>Header Files\base</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="beacon_gate.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="sleepmask.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Makefile" />
|
||||
<None Include="packages.config" />
|
||||
<None Include="utils\boflint.py" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,51 @@
|
||||
CFLAGS=/c /GS- /std:c++20 /D_HAS_EXCEPTIONS=0 /GR- /Gs9999999
|
||||
DEBUGCFLAGS=/Zi /MTd /D_DEBUG /EHsc /std:c++20
|
||||
PYTHON=python
|
||||
|
||||
!IF "$(PROCESSOR_ARCHITECTURE)" != "x86" && "$(PROCESSOR_ARCHITECTURE)" != "AMD64"
|
||||
!ERROR Only x86 and AMD64 architectures are supported or the PROCESSOR_ARCHITECTURE environment variable is not set.
|
||||
!ELSEIF "$(PROCESSOR_ARCHITECTURE)" == "AMD64"
|
||||
OUTDIR=..\x64\Release\
|
||||
IMDIR=x64\Release\
|
||||
DOUTDIR=..\x64\Debug\
|
||||
DIMDIR=x64\Debug\
|
||||
OUTEXT=.x64.o
|
||||
!ELSE
|
||||
OUTDIR=..\Release\
|
||||
IMDIR=Release\
|
||||
DIMDIR=Debug\
|
||||
DOUTDIR=..\Debug\
|
||||
OUTEXT=.x86.o
|
||||
!ENDIF
|
||||
|
||||
|
||||
all: *.cpp
|
||||
@$(MAKE) /A $(patsubst %.c,%.obj, $(patsubst %.cpp, %.obj, $(patsubsti %, $(IMDIR)\%, $**)))
|
||||
|
||||
@if not exist "$(OUTDIR)" mkdir "$(OUTDIR)"
|
||||
copy "$(IMDIR)\*.obj" "$(OUTDIR)"
|
||||
del /F "$(OUTDIR)\*$(OUTEXT)"
|
||||
ren "$(OUTDIR)*.obj" "*$(OUTEXT)"
|
||||
|
||||
all-debug: *.cpp
|
||||
@$(MAKE) /A $(patsubst %.c,%.exe, $(patsubst %.cpp, %.exe, $(patsubsti %, $(DOUTDIR)\%, $**)))
|
||||
|
||||
.cpp{$(IMDIR)}.obj:
|
||||
@if not exist "$(IMDIR)" mkdir "$(IMDIR)"
|
||||
$(CPP) $(CFLAGS) /Fo"$@" $<
|
||||
|
||||
@(where $(PYTHON) >nul 2>nul \
|
||||
&& $(PYTHON) --version >nul 2>nul \
|
||||
|| (echo [*] Install Python to enable boflint && exit /b 0) \
|
||||
) && $(PYTHON) utils\boflint.py --logformat vs --loader cs "$@"
|
||||
|
||||
.cpp{$(DOUTDIR)}.exe:
|
||||
@if not exist "$(DIMDIR)" mkdir "$(DIMDIR)"
|
||||
@if not exist "$(DOUTDIR)" mkdir "$(DOUTDIR)"
|
||||
$(CPP) $(DEBUGCFLAGS) /Fo$(DIMDIR) /Fd$(DIMDIR) /Fe"$@" $< base/mock.cpp
|
||||
|
||||
clean:
|
||||
@if exist "$(DIMDIR)" rmdir /Q /S "$(DIMDIR)"
|
||||
@if exist "$(IMDIR)" rmdir /Q /S "$(IMDIR)"
|
||||
@if exist "$(OUTDIR)" rmdir /Q /S "$(OUTDIR)"
|
||||
@if exist "$(DOUTDIR)" rmdir /Q /S "$(DOUTDIR)"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
#ifdef __cplusplus
|
||||
#ifndef _DEBUG
|
||||
#define DFR(module, function) \
|
||||
DECLSPEC_IMPORT decltype(function) module##$##function;
|
||||
|
||||
#define DFR_LOCAL(module, function) \
|
||||
DECLSPEC_IMPORT decltype(function) module##$##function; \
|
||||
decltype(module##$##function) * function = module##$##function;
|
||||
#else
|
||||
#define DFR_LOCAL(module, function)
|
||||
#define DFR(module, function) \
|
||||
decltype(function) *module##$##function = function;
|
||||
#endif // end of _DEBUG
|
||||
#endif // end of __cplusplus
|
||||
@@ -0,0 +1,724 @@
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
#include <cassert>
|
||||
#include <windows.h>
|
||||
|
||||
extern "C" {
|
||||
#ifdef _DEBUG
|
||||
#undef DECLSPEC_IMPORT
|
||||
#define DECLSPEC_IMPORT
|
||||
#endif
|
||||
#include "..\beacon.h"
|
||||
}
|
||||
|
||||
#include "mock.h"
|
||||
|
||||
namespace bof {
|
||||
namespace utils {
|
||||
template <typename T>
|
||||
T swapEndianness(T value) {
|
||||
char *ptr = reinterpret_cast<char *>(&value);
|
||||
std::reverse(ptr, ptr + sizeof(T));
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<char> toBytes(T input) {
|
||||
char *ptr = reinterpret_cast<char *>(&input);
|
||||
return std::vector<char>(ptr, ptr + sizeof(T));
|
||||
}
|
||||
|
||||
const char* typeToStr(int callbackType) {
|
||||
switch (callbackType) {
|
||||
case CALLBACK_OUTPUT: return "CALLBACK_OUTPUT";
|
||||
case CALLBACK_OUTPUT_OEM: return "CALLBACK_OUTPUT_OEM";
|
||||
case CALLBACK_ERROR: return "CALLBACK_ERROR";
|
||||
case CALLBACK_OUTPUT_UTF8: return "CALLBACK_OUTPUT_UTF8";
|
||||
default: return "CALLBACK_UNKOWN";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace mock {
|
||||
static BEACON_INFO beaconInfo = { 0 };
|
||||
|
||||
char *BofData::get() {
|
||||
return size() > 0 ? reinterpret_cast<char *>(&data[0]) : nullptr;
|
||||
}
|
||||
|
||||
int BofData::size() {
|
||||
return data.size();
|
||||
}
|
||||
|
||||
void BofData::addData(const char *buf, std::size_t len) {
|
||||
std::vector<char> bytes;
|
||||
bytes.assign(buf, buf + len);
|
||||
insert(static_cast<int>(len));
|
||||
append(bytes);
|
||||
}
|
||||
|
||||
void BofData::append(const std::vector<char> &data) {
|
||||
this->data.insert(std::end(this->data), std::begin(data), std::end(data));
|
||||
}
|
||||
|
||||
void BofData::insert(int v) {
|
||||
append(bof::utils::toBytes(bof::utils::swapEndianness(v)));
|
||||
}
|
||||
|
||||
void BofData::insert(short v) {
|
||||
append(bof::utils::toBytes(bof::utils::swapEndianness(v)));
|
||||
}
|
||||
|
||||
void BofData::insert(unsigned int v) {
|
||||
insert(static_cast<int>(v));
|
||||
}
|
||||
|
||||
void BofData::insert(unsigned short v) {
|
||||
insert(static_cast<short>(v));
|
||||
}
|
||||
|
||||
void BofData::insert(const char *v) {
|
||||
addData(v, std::strlen(v) + 1);
|
||||
}
|
||||
|
||||
void BofData::insert(const wchar_t *v) {
|
||||
addData((const char *)v, (std::wcslen(v) + 1) * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
void BofData::insert(const std::vector<char>& data) {
|
||||
pack<int32_t>(data.size());
|
||||
append(data);
|
||||
}
|
||||
|
||||
void setBeaconInfo(BEACON_INFO& info) {
|
||||
std::memcpy(&bof::mock::beaconInfo, &info, sizeof(BEACON_INFO));
|
||||
}
|
||||
|
||||
void setSection(PALLOCATED_MEMORY_SECTION info, PVOID baseAddress, SIZE_T size, DWORD finalProtection, DWORD allocationType, ALLOCATED_MEMORY_LABEL label) {
|
||||
info->BaseAddress = baseAddress;
|
||||
info->VirtualSize = size;
|
||||
info->PreviousProtect = finalProtection;
|
||||
info->CurrentProtect = finalProtection;
|
||||
info->MaskSection = TRUE;
|
||||
info->Label = label;
|
||||
}
|
||||
|
||||
ALLOCATED_MEMORY_REGION allocateBeaconMemory(const bof::profile::Stage& stage, size_t size, DWORD* initialPermission) {
|
||||
ALLOCATED_MEMORY_REGION info;
|
||||
std::memset(&info, 0, sizeof(info));
|
||||
|
||||
/* Allocate the base memory for Beacon. The following .stage options affect how the memory is allocated:
|
||||
* - .stage.allocator: Set how Beacon's Reflective Loader allocates memory for the agent. Options are: HeapAlloc, MapViewOfFile, and VirtualAlloc.
|
||||
* - .stage.module_x64/x86: Ask the ReflectiveLoader to load the specified library and overwrite its space instead of allocating memory with the .stage.allocator.
|
||||
*/
|
||||
*initialPermission = stage.useRWX == bof::profile::UseRWX::True ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
|
||||
if (stage.module.empty()) {
|
||||
HANDLE hHeap = INVALID_HANDLE_VALUE;
|
||||
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||||
|
||||
switch (stage.allocator) {
|
||||
case bof::profile::Allocator::HeapAlloc: {
|
||||
// For heap allocator we don't honor the .stage.userwx flag
|
||||
*initialPermission = PAGE_EXECUTE_READWRITE;
|
||||
hHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);
|
||||
assert(hHeap != NULL && "Could not create the heap");
|
||||
info.AllocationBase = HeapAlloc(hHeap, 0, size);
|
||||
info.Type = MEM_PRIVATE;
|
||||
info.CleanupInformation.AllocationMethod = METHOD_HEAPALLOC;
|
||||
info.CleanupInformation.AdditionalCleanupInformation.HeapAllocInfo.HeapHandle = hHeap;
|
||||
info.CleanupInformation.AdditionalCleanupInformation.HeapAllocInfo.DestroyHeap = TRUE;
|
||||
break;
|
||||
}
|
||||
case bof::profile::Allocator::MapViewOfFile: {
|
||||
hFile = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, size, NULL);
|
||||
assert(hFile != NULL && "Could not create file mapping");
|
||||
info.AllocationBase = MapViewOfFile(hFile, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0, 0, 0);
|
||||
info.Type = MEM_MAPPED;
|
||||
info.CleanupInformation.AllocationMethod = METHOD_NTMAPVIEW;
|
||||
CloseHandle(hFile);
|
||||
|
||||
DWORD old = 0;
|
||||
if (!VirtualProtect(info.AllocationBase, size, *initialPermission, &old)) {
|
||||
assert(false && "Could not set the initial memory permission for the Beacon memory");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case bof::profile::Allocator::VirtualAlloc: {
|
||||
info.AllocationBase = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, *initialPermission);
|
||||
info.Type = MEM_PRIVATE;
|
||||
info.CleanupInformation.AllocationMethod = METHOD_VIRTUALALLOC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// For simplicity we use module base here instead of resolving a function
|
||||
info.AllocationBase = (PVOID)LoadLibraryExA(stage.module.c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES);
|
||||
info.Type = MEM_IMAGE;
|
||||
info.CleanupInformation.AllocationMethod = METHOD_MODULESTOMP;
|
||||
|
||||
DWORD old = 0;
|
||||
if (!VirtualProtect(info.AllocationBase, size, *initialPermission, &old)) {
|
||||
assert(false && "Could not set the initial memory permission for the Beacon memory");
|
||||
}
|
||||
}
|
||||
assert(info.AllocationBase != NULL && "Could not allocate the base memory for Beacon");
|
||||
|
||||
// Set the necessary fields for the Beacon memory
|
||||
info.Purpose = PURPOSE_BEACON_MEMORY;
|
||||
info.RegionSize = size;
|
||||
info.CleanupInformation.Cleanup = TRUE;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
BEACON_INFO setupMockBeacon(const bof::profile::Stage& stage) {
|
||||
const std::vector<std::tuple<ALLOCATED_MEMORY_LABEL, std::string>> sections{
|
||||
{ LABEL_PEHEADER, "PE" },
|
||||
{ LABEL_TEXT, ".text" },
|
||||
{ LABEL_RDATA, ".rdata" },
|
||||
{ LABEL_DATA, ".data" },
|
||||
#ifdef _M_X64
|
||||
{ LABEL_PDATA, ".pdata" },
|
||||
#endif
|
||||
{ LABEL_RELOC, ".reloc" },
|
||||
};
|
||||
const size_t sectionSize = 0x1000;
|
||||
|
||||
BEACON_INFO info = { 0 };
|
||||
info.version = bof::CsVersion;
|
||||
|
||||
// Set the static mask key
|
||||
for (size_t i = 0; i < MASK_SIZE; ++i) {
|
||||
info.mask[i] = 0xAB;
|
||||
}
|
||||
|
||||
// Allocate the memory for Beacon
|
||||
DWORD initialProtection = 0;
|
||||
ALLOCATED_MEMORY_REGION beaconMemory = allocateBeaconMemory(stage, sections.size() * sectionSize, &initialProtection);
|
||||
info.allocatedMemory.AllocatedMemoryRegions[0] = beaconMemory;
|
||||
info.beacon_ptr = stage.obfuscate == bof::profile::Obfuscate::False
|
||||
? reinterpret_cast<char*>(beaconMemory.AllocationBase)
|
||||
: reinterpret_cast<char*>(beaconMemory.AllocationBase) - 0x1000;
|
||||
|
||||
// Build the section list
|
||||
PALLOCATED_MEMORY_SECTION nextSection = info.allocatedMemory.AllocatedMemoryRegions[0].Sections;
|
||||
char* sectionBase = reinterpret_cast<char*>(beaconMemory.AllocationBase);
|
||||
for (auto [section, name] : sections) {
|
||||
// Skip the PE header if .stage.obfuscate = true
|
||||
if (stage.obfuscate == bof::profile::Obfuscate::True && section == LABEL_PEHEADER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fill the memory
|
||||
for (size_t i = 0; i < sectionSize; ++i) {
|
||||
sectionBase[i] = name[i % name.length()];
|
||||
}
|
||||
|
||||
DWORD finalProtection = initialProtection;
|
||||
|
||||
// Fix the .code section permission if the allocator is not heap alloc and .stage.userwx is false
|
||||
if (stage.allocator != bof::profile::Allocator::HeapAlloc || !stage.module.empty()) {
|
||||
if (section == LABEL_TEXT && stage.useRWX == bof::profile::UseRWX::False) {
|
||||
finalProtection = PAGE_EXECUTE_READ;
|
||||
|
||||
// Fix the permissions
|
||||
DWORD old = 0;
|
||||
if (!VirtualProtect(sectionBase, sectionSize, finalProtection, &old)) {
|
||||
assert(false && "Could not set the final memory protection");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set the section memory information
|
||||
setSection(nextSection, sectionBase, sectionSize, finalProtection, beaconMemory.Type, section);
|
||||
|
||||
// Next section
|
||||
++nextSection;
|
||||
sectionBase += sectionSize;
|
||||
}
|
||||
|
||||
// Add few mock heap records
|
||||
const size_t numberOfHeapRecords = 2;
|
||||
info.heap_records = new HEAP_RECORD[numberOfHeapRecords + 1];
|
||||
assert(info.heap_records != nullptr && "Could not the allocate heap records array");
|
||||
for (size_t i = 0; i < numberOfHeapRecords; ++i) {
|
||||
info.heap_records[i].ptr = new char[512];
|
||||
info.heap_records[i].size = 512;
|
||||
assert(info.heap_records[i].ptr != nullptr && "Could not allocate a heap record");
|
||||
}
|
||||
info.heap_records[numberOfHeapRecords].ptr = NULL;
|
||||
info.heap_records[numberOfHeapRecords].size = 0;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
void resolveMockUpSleepmaskLocation(BEACON_INFO& info) {
|
||||
// Get the base address of the debug exe
|
||||
char* exeBase = (char*)GetModuleHandleA(NULL);
|
||||
// Find the start of the section headers
|
||||
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)exeBase;
|
||||
PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)(exeBase + dosHeader->e_lfanew);
|
||||
PIMAGE_SECTION_HEADER sectionHeader = (PIMAGE_SECTION_HEADER)(reinterpret_cast<char*>(&ntHeader->OptionalHeader) + ntHeader->FileHeader.SizeOfOptionalHeader);
|
||||
// Find the .text section which will be our "mock" sleepmask code
|
||||
DWORD numberOfSections = ntHeader->FileHeader.NumberOfSections;
|
||||
while (numberOfSections--) {
|
||||
if (strcmp(reinterpret_cast<char*>(sectionHeader->Name), ".text") == 0) {
|
||||
info.sleep_mask_ptr = exeBase + sectionHeader->VirtualAddress;
|
||||
info.sleep_mask_text_size = sectionHeader->Misc.VirtualSize;
|
||||
// In theory, the total size would contains also the data part, but we cannot guarantee the section order.
|
||||
info.sleep_mask_total_size = sectionHeader->Misc.VirtualSize;
|
||||
return;
|
||||
}
|
||||
++sectionHeader;
|
||||
}
|
||||
assert(false && "Could not find the text section of the debug image");
|
||||
}
|
||||
|
||||
FUNCTION_CALL createFunctionCallStructure(PVOID targetFunction, WinApi targetFunctionName, BOOL bMask, int numOfArgs, ...) {
|
||||
FUNCTION_CALL functionCall = { 0 };
|
||||
va_list valist = NULL;
|
||||
|
||||
/* Set basic info for encapsulated function call */
|
||||
functionCall.functionPtr = targetFunction;
|
||||
functionCall.function = targetFunctionName;
|
||||
functionCall.bMask = bMask;
|
||||
functionCall.numOfArgs = numOfArgs;
|
||||
|
||||
/* Start parsing valist and copy over variadic arguments. */
|
||||
va_start(valist, numOfArgs);
|
||||
for (int i = 0; i < numOfArgs; i++)
|
||||
{
|
||||
functionCall.args[i] = va_arg(valist, ULONG_PTR);
|
||||
}
|
||||
va_end(valist);
|
||||
|
||||
return functionCall;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace output {
|
||||
std::vector<OutputEntry> outputs;
|
||||
|
||||
void addEntry(int type, const char* data, int len) {
|
||||
OutputEntry output = {
|
||||
type,
|
||||
std::string(data, data + len)
|
||||
};
|
||||
outputs.push_back(output);
|
||||
}
|
||||
|
||||
const std::vector<OutputEntry>& getOutputs() {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
outputs.clear();
|
||||
}
|
||||
|
||||
void PrintTo(const OutputEntry& o, std::ostream* os) {
|
||||
*os << "{ callbackType: " << bof::utils::typeToStr(o.callbackType) << ", output: " << o.output << " }";
|
||||
}
|
||||
}
|
||||
|
||||
namespace valuestore {
|
||||
std::map<std::string, void*> values;
|
||||
|
||||
void reset() {
|
||||
values.clear();
|
||||
}
|
||||
}
|
||||
|
||||
namespace bud {
|
||||
char custom[BEACON_USER_DATA_CUSTOM_SIZE] = { 0 };
|
||||
|
||||
void reset() {
|
||||
std::memset(custom, 0, BEACON_USER_DATA_CUSTOM_SIZE);
|
||||
}
|
||||
|
||||
void set(const char* data) {
|
||||
if (data) {
|
||||
std::memcpy(custom, data, BEACON_USER_DATA_CUSTOM_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<bof::output::OutputEntry> runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, PBEACON_INFO beaconInfo, PFUNCTION_CALL functionCall) {
|
||||
// Reset the global output container
|
||||
bof::output::reset();
|
||||
// Execute the entrypoint
|
||||
sleepMaskFunc(beaconInfo, functionCall);
|
||||
// Return the stored outputs
|
||||
return bof::output::getOutputs();
|
||||
}
|
||||
|
||||
std::vector<bof::output::OutputEntry> runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, const bof::profile::Stage& stage, const bof::mock::MockSleepMaskConfig& config) {
|
||||
BEACON_INFO beaconInfo = bof::mock::setupMockBeacon(stage);
|
||||
FUNCTION_CALL functionCall = bof::mock::createFunctionCallStructure(Sleep, SLEEP, TRUE, 1, config.sleepTimeMs);
|
||||
bof::mock::resolveMockUpSleepmaskLocation(beaconInfo);
|
||||
bof::mock::setBeaconInfo(beaconInfo);
|
||||
|
||||
std::vector<bof::output::OutputEntry> output;
|
||||
do {
|
||||
output = runMockedSleepMask(sleepMaskFunc, &beaconInfo, &functionCall);
|
||||
} while (config.runForever);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
std::vector<bof::output::OutputEntry> runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, const bof::profile::Stage& stage) {
|
||||
// Set the default config for the mock sleepmask runner
|
||||
const bof::mock::MockSleepMaskConfig config = {
|
||||
.sleepTimeMs = 5000,
|
||||
.runForever = false,
|
||||
};
|
||||
return runMockedSleepMask(sleepMaskFunc, stage, config);
|
||||
}
|
||||
|
||||
std::vector<bof::output::OutputEntry> runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc) {
|
||||
return runMockedSleepMask(sleepMaskFunc, bof::profile::defaultStage);
|
||||
}
|
||||
|
||||
std::vector<bof::output::OutputEntry> runMockedBeaconGate(SLEEPMASK_FUNC sleepMaskFunc, PFUNCTION_CALL functionCall, const bof::profile::Stage& stage) {
|
||||
BEACON_INFO beaconInfo = bof::mock::setupMockBeacon(stage);
|
||||
bof::mock::resolveMockUpSleepmaskLocation(beaconInfo);
|
||||
bof::mock::setBeaconInfo(beaconInfo);
|
||||
return runMockedSleepMask(sleepMaskFunc, &beaconInfo, functionCall);
|
||||
}
|
||||
|
||||
std::vector<bof::output::OutputEntry> runMockedBeaconGate(SLEEPMASK_FUNC sleepMaskFunc, PFUNCTION_CALL functionCall) {
|
||||
return runMockedBeaconGate(sleepMaskFunc, functionCall, bof::profile::defaultStage);
|
||||
}
|
||||
}
|
||||
|
||||
#include "mock_syscalls.cpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
// Print API
|
||||
void BeaconPrintf(int type, const char *fmt, ...) {
|
||||
printf("[Output Callback: %s (0x%X)]\n", bof::utils::typeToStr(type), type);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int size = vsnprintf(nullptr, 0, fmt, args);
|
||||
if (size >= 0) {
|
||||
char* buffer = new char[size + 1];
|
||||
vsnprintf(buffer, size + 1, fmt, args);
|
||||
bof::output::addEntry(type, buffer, size);
|
||||
delete[] buffer;
|
||||
}
|
||||
vprintf(fmt, args);
|
||||
printf("\n");
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
BOOL BeaconDownload(const char* filename, const char* buffer, unsigned int length) {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void BeaconOutput(int type, const char *data, int len) {
|
||||
bof::output::addEntry(type, data, len);
|
||||
printf("[Output Callback: %s (0x%X)]\n%.*s", bof::utils::typeToStr(type), type, len, data);
|
||||
}
|
||||
|
||||
// Parser API
|
||||
void BeaconDataParse(datap *parser, char *buffer, int size) {
|
||||
parser->buffer = buffer;
|
||||
parser->original = buffer;
|
||||
parser->size = size;
|
||||
parser->length = size;
|
||||
}
|
||||
|
||||
char* BeaconDataPtr(datap* parser, int size) {
|
||||
if (parser->length < size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* data = parser->buffer;
|
||||
parser->buffer += size;
|
||||
parser->length -= size;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int BeaconDataInt(datap *parser) {
|
||||
int value = *(int *)(parser->buffer);
|
||||
parser->buffer += sizeof(int);
|
||||
parser->length -= sizeof(int);
|
||||
return bof::utils::swapEndianness(value);
|
||||
}
|
||||
|
||||
short BeaconDataShort(datap *parser) {
|
||||
short value = *(short *)(parser->buffer);
|
||||
parser->buffer += sizeof(short);
|
||||
parser->length -= sizeof(short);
|
||||
return bof::utils::swapEndianness(value);
|
||||
}
|
||||
|
||||
int BeaconDataLength(datap *parser) {
|
||||
return parser->length;
|
||||
}
|
||||
|
||||
char *BeaconDataExtract(datap *parser, int *size) {
|
||||
int size_im = BeaconDataInt(parser);
|
||||
char *buff = parser->buffer;
|
||||
parser->buffer += size_im;
|
||||
if (size)
|
||||
{
|
||||
*size = size_im;
|
||||
}
|
||||
return buff;
|
||||
}
|
||||
|
||||
// Format API
|
||||
void BeaconFormatAlloc(formatp *format, int maxsz) {
|
||||
format->original = new char[maxsz];
|
||||
format->buffer = format->original;
|
||||
format->length = maxsz;
|
||||
format->size = maxsz;
|
||||
}
|
||||
|
||||
void BeaconFormatReset(formatp *format) {
|
||||
format->buffer = format->original;
|
||||
format->length = format->size;
|
||||
}
|
||||
|
||||
void BeaconFormatFree(formatp *format) {
|
||||
delete[] format->original;
|
||||
}
|
||||
|
||||
void BeaconFormatAppend(formatp *format, const char *text, int len) {
|
||||
memcpy(format->buffer, text, len);
|
||||
format->buffer += len;
|
||||
format->length -= len;
|
||||
}
|
||||
|
||||
void BeaconFormatPrintf(formatp *format, const char *fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
int len = vsprintf_s(format->buffer, format->length, fmt, args);
|
||||
format->buffer += len;
|
||||
format->length -= len;
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
char *BeaconFormatToString(formatp *format, int *size) {
|
||||
if (size)
|
||||
{
|
||||
*size = format->size - format->length;
|
||||
}
|
||||
return format->original;
|
||||
}
|
||||
|
||||
void BeaconFormatInt(formatp *format, int value) {
|
||||
value = bof::utils::swapEndianness(value);
|
||||
BeaconFormatAppend(format, (char *)&value, 4);
|
||||
}
|
||||
|
||||
// Internal API
|
||||
BOOL BeaconUseToken(HANDLE token) {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void BeaconRevertToken() {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
BOOL BeaconIsAdmin() {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void BeaconGetSpawnTo(BOOL x86, char *buffer, int length) {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
void BeaconInjectProcess(HANDLE hProc, int pid, char *payload,
|
||||
int p_len, int p_offset, char *arg,
|
||||
int a_len)
|
||||
{
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
void BeaconInjectTemporaryProcess(PROCESS_INFORMATION *pInfo,
|
||||
char *payload, int p_len,
|
||||
int p_offset, char *arg,
|
||||
int a_len)
|
||||
{
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
|
||||
BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO* si, PROCESS_INFORMATION* pInfo) {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
void BeaconCleanupProcess(PROCESS_INFORMATION *pInfo) {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
BOOL toWideChar(char *src, wchar_t *dst, int max) {
|
||||
std::string str = src;
|
||||
std::wstring wstr(str.begin(), str.end());
|
||||
|
||||
size_t bytes = min(wstr.length() * sizeof(wchar_t), max);
|
||||
std::memcpy(dst, wstr.c_str(), bytes);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL BeaconInformation(PBEACON_INFO info) {
|
||||
std::memcpy(info, &bof::mock::beaconInfo, sizeof(BEACON_INFO));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL BeaconAddValue(const char* key, void* ptr) {
|
||||
auto item = bof::valuestore::values.find(std::string(key));
|
||||
if (ptr && item == bof::valuestore::values.end()) {
|
||||
bof::valuestore::values[std::string(key)] = ptr;
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void* BeaconGetValue(const char* key) {
|
||||
auto item = bof::valuestore::values.find(std::string(key));
|
||||
if (item != bof::valuestore::values.end()) {
|
||||
return item->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL BeaconRemoveValue(const char* key) {
|
||||
auto item = bof::valuestore::values.find(std::string(key));
|
||||
if (item != bof::valuestore::values.end()) {
|
||||
bof::valuestore::values.erase(item);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index) {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void BeaconDataStoreProtectItem(size_t index) {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
void BeaconDataStoreUnprotectItem(size_t index) {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
size_t BeaconDataStoreMaxEntries() {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* BeaconGetCustomUserData() {
|
||||
return bof::bud::custom;
|
||||
}
|
||||
|
||||
BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized) {
|
||||
if (infoSize != sizeof(BEACON_SYSCALLS)) {
|
||||
return FALSE;
|
||||
}
|
||||
if (info) {
|
||||
bof::mock::syscall::ResolveSyscalls(&info->syscalls);
|
||||
bof::mock::syscall::ResolveRtls(&info->rtls);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Beacon System Call API
|
||||
LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
|
||||
return VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
|
||||
}
|
||||
|
||||
LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
|
||||
return VirtualAllocEx(processHandle, lpAddress, dwSize, flAllocationType, flProtect);
|
||||
}
|
||||
|
||||
BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) {
|
||||
return VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
|
||||
}
|
||||
|
||||
BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) {
|
||||
return VirtualProtectEx(processHandle, lpAddress, dwSize, flNewProtect, lpflOldProtect);
|
||||
}
|
||||
|
||||
BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) {
|
||||
return VirtualFree(lpAddress, dwSize, dwFreeType);
|
||||
}
|
||||
|
||||
BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext) {
|
||||
return GetThreadContext(threadHandle, threadContext);
|
||||
}
|
||||
|
||||
BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext) {
|
||||
return SetThreadContext(threadHandle, threadContext);
|
||||
}
|
||||
|
||||
DWORD BeaconResumeThread(HANDLE threadHandle) {
|
||||
return ResumeThread(threadHandle);
|
||||
}
|
||||
|
||||
HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId) {
|
||||
return OpenProcess(desiredAccess, inheritHandle, processId);
|
||||
}
|
||||
|
||||
HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId) {
|
||||
return OpenThread(desiredAccess, inheritHandle, threadId);
|
||||
}
|
||||
|
||||
BOOL BeaconCloseHandle(HANDLE object) {
|
||||
return CloseHandle(object);
|
||||
}
|
||||
|
||||
BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress) {
|
||||
return UnmapViewOfFile(baseAddress);
|
||||
}
|
||||
|
||||
SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length) {
|
||||
return VirtualQuery(address, buffer, length);
|
||||
}
|
||||
|
||||
BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions) {
|
||||
return DuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions);
|
||||
}
|
||||
|
||||
BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesRead) {
|
||||
return ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead);
|
||||
}
|
||||
|
||||
BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T* lpNumberOfBytesWritten) {
|
||||
return WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten);
|
||||
}
|
||||
|
||||
VOID BeaconDisableBeaconGate() {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
VOID BeaconEnableBeaconGate() {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
VOID BeaconDisableBeaconGateMasking() {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
|
||||
VOID BeaconEnableBeaconGateMasking() {
|
||||
std::cerr << "Not implemented: " << __FUNCTION__ << std::endl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,299 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "../sleepmask.h"
|
||||
|
||||
namespace bof {
|
||||
const DWORD CsVersion = 0x041200;
|
||||
|
||||
namespace profile {
|
||||
/**
|
||||
* Enum classes to mimic the stage block in the C2 profile.
|
||||
*/
|
||||
enum class Allocator {
|
||||
VirtualAlloc,
|
||||
HeapAlloc,
|
||||
MapViewOfFile
|
||||
};
|
||||
enum class Obfuscate {
|
||||
False,
|
||||
True
|
||||
};
|
||||
enum class UseRWX {
|
||||
False,
|
||||
True
|
||||
};
|
||||
|
||||
struct Stage {
|
||||
Allocator allocator;
|
||||
Obfuscate obfuscate;
|
||||
UseRWX useRWX;
|
||||
std::string module;
|
||||
};
|
||||
|
||||
const Stage defaultStage = {
|
||||
.allocator = bof::profile::Allocator::VirtualAlloc,
|
||||
.obfuscate = bof::profile::Obfuscate::False,
|
||||
.useRWX = bof::profile::UseRWX::True,
|
||||
.module = "",
|
||||
};
|
||||
}
|
||||
|
||||
namespace mock {
|
||||
/**
|
||||
* Data container class used for packing BOF arguments.
|
||||
*/
|
||||
class BofData {
|
||||
public:
|
||||
/**
|
||||
* Pack a variadic number of arguments.
|
||||
* Equivalent to the bof_pack function.
|
||||
*
|
||||
* For example, bof_pack("isz", 1, 2, "hello")
|
||||
* -> pack<int, short, const char*>(1, 2, "hello")
|
||||
*
|
||||
* @param ... arguments
|
||||
*/
|
||||
template <typename... T>
|
||||
void pack(T &&...v)
|
||||
{
|
||||
((insert(std::forward<T>(v))), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add binary data to the argument buffer.
|
||||
* Equivalent to bof_pack("b", $data).
|
||||
*
|
||||
* @param buf A char pointer to the data
|
||||
* @param len A length to the data
|
||||
*/
|
||||
void addData(const char *buf, std::size_t len);
|
||||
|
||||
|
||||
/**
|
||||
* << operator to allow an alternative way to build the argument buffer.
|
||||
*
|
||||
* For example: args << 123 << 32;
|
||||
*
|
||||
* @param container A BofData object
|
||||
* @param arg An argument
|
||||
*/
|
||||
template <typename T>
|
||||
friend BofData &operator<<(BofData &container, T arg)
|
||||
{
|
||||
container.pack(arg);
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a raw argument buffer.
|
||||
*
|
||||
* @return A char pointer of raw argument buffer
|
||||
*/
|
||||
char* get();
|
||||
|
||||
/**
|
||||
* Get the size of the argument buffer.
|
||||
*
|
||||
* @return A size of the argument buffer
|
||||
*/
|
||||
int size();
|
||||
private:
|
||||
void append(const std::vector<char> &data);
|
||||
void insert(int v);
|
||||
void insert(short v);
|
||||
void insert(unsigned int v);
|
||||
void insert(unsigned short v);
|
||||
void insert(const char *v);
|
||||
void insert(const wchar_t *v);
|
||||
void insert(const std::vector<char>& data);
|
||||
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
/**
|
||||
* This structure holds the information about how the mock runner should
|
||||
* execute the sleepmask.
|
||||
*/
|
||||
typedef struct {
|
||||
DWORD sleepTimeMs;
|
||||
bool runForever;
|
||||
} MockSleepMaskConfig;
|
||||
|
||||
/**
|
||||
* Setup memory for a mock Beacon
|
||||
*
|
||||
* The memory layout is created by mimicking Beacon's default reflective loader,
|
||||
* and the related C2 options
|
||||
*
|
||||
* @param stage The applicable stage{} options
|
||||
* @return the mock Beacon memory structure
|
||||
*/
|
||||
BEACON_INFO setupMockBeacon(const bof::profile::Stage& stage);
|
||||
|
||||
/**
|
||||
* Setup a FUNCTION_CALL structure
|
||||
*
|
||||
* @param targetFunction The WINAPI function pointer
|
||||
* @param targetFunctionName Human readable WinApi enum
|
||||
* @param bMask Mask beacon
|
||||
* @param numOfArgs number of arguments for the WINAPI function
|
||||
* @param ... the arguments for the WINAPI function
|
||||
*/
|
||||
FUNCTION_CALL createFunctionCallStructure(PVOID targetFunction, WinApi targetFunctionName, BOOL bMask, int numOfArgs, ...);
|
||||
|
||||
/**
|
||||
* Change the mock BEACON_INFO strucutre
|
||||
*
|
||||
* @param info The new beacon inforamation
|
||||
*/
|
||||
void setBeaconInfo(BEACON_INFO& info);
|
||||
}
|
||||
|
||||
namespace output {
|
||||
/**
|
||||
* Data structure to store a output from BOF
|
||||
*/
|
||||
struct OutputEntry {
|
||||
/**
|
||||
* The callback type. E.g. CALLBACK_OUTPUT
|
||||
*/
|
||||
int callbackType;
|
||||
|
||||
/**
|
||||
* The output data
|
||||
*/
|
||||
std::string output;
|
||||
|
||||
/**
|
||||
* Equivalence overloading.
|
||||
*
|
||||
* param other Another OutputEntry object
|
||||
*/
|
||||
bool operator==(const OutputEntry& other) const {
|
||||
return callbackType == other.callbackType && output == other.output;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the list of BOF outputs
|
||||
*
|
||||
* @return A vector of OutputEntry objects
|
||||
*/
|
||||
const std::vector<OutputEntry>& getOutputs();
|
||||
|
||||
/**
|
||||
* Clear the currently stored BOF outputs
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Pretty print an OutputEntry object.
|
||||
* Required by the GoogleTest.
|
||||
*
|
||||
* @param o An OutputEntry object
|
||||
* @param os An output stream
|
||||
*/
|
||||
void PrintTo(const OutputEntry& o, std::ostream* os);
|
||||
}
|
||||
|
||||
namespace valuestore {
|
||||
/**
|
||||
* Clear items in BOF Key/Value store
|
||||
*/
|
||||
void reset();
|
||||
}
|
||||
|
||||
namespace bud {
|
||||
/**
|
||||
* Clear the custom data buffer in Beacon User Data
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Set the custom data buffer in Beacon User Data
|
||||
*
|
||||
* @param data A pointer to custom data buffer
|
||||
*/
|
||||
void set(const char* data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a BOF with arguments
|
||||
*
|
||||
* @param entry BOF's entry point
|
||||
* @param ... arguments
|
||||
* @return A vector of OutputEntry objects
|
||||
*/
|
||||
template <typename... T>
|
||||
std::vector<bof::output::OutputEntry> runMocked(void (*entry)(char*, int), T &&...v) {
|
||||
// Setup mock Beacon memory
|
||||
BEACON_INFO beaconInfo = bof::mock::setupMockBeacon(bof::profile::defaultStage);
|
||||
bof::mock::setBeaconInfo(beaconInfo);
|
||||
// Reset the global output container
|
||||
bof::output::reset();
|
||||
// Pack the arguments
|
||||
bof::mock::BofData args;
|
||||
args.pack(std::forward<T>(v)...);
|
||||
// Execute the entrypoint
|
||||
entry(args.get(), args.size());
|
||||
// Return the stored outputs
|
||||
return bof::output::getOutputs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a mock-up Beacon and execute the sleepmask function once with the default .stage options and mock-up config.
|
||||
*
|
||||
* @param sleepMaskFunc the function pointer for the sleepmask
|
||||
* @return A vector of OutputEntry objects
|
||||
*/
|
||||
std::vector<bof::output::OutputEntry> runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc);
|
||||
|
||||
/**
|
||||
* Setup a mock-up Beacon and execute the sleepmask function using a custom stage profile, and the default mock-up config.
|
||||
*
|
||||
* @param sleepMaskFunc the function pointer for the sleepmask
|
||||
* @param stage the stage options
|
||||
* @return A vector of OutputEntry objects
|
||||
*/
|
||||
std::vector<bof::output::OutputEntry> runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, const bof::profile::Stage& stage);
|
||||
|
||||
/**
|
||||
* Setup a mock-up Beacon and execute the sleepmask function using a custom stage profile and mock-up config.
|
||||
*
|
||||
* @param sleepMaskFunc the function pointer for the sleepmask
|
||||
* @param stage the applicable stage{} options
|
||||
* @param config the mockup config
|
||||
* @return A vector of OutputEntry objects
|
||||
*/
|
||||
std::vector<bof::output::OutputEntry> runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, const bof::profile::Stage& stage, const bof::mock::MockSleepMaskConfig& config);
|
||||
|
||||
/**
|
||||
* Execute the sleepmask.
|
||||
*
|
||||
* @param sleepMaskFunc the function pointer for the sleepmask
|
||||
* @param sleepMaskInfo the pointer to the SLEEPMASK_INFO structure
|
||||
* @param functionCall the pointer to the FUNCTION_CALL structure
|
||||
* @return A vector of OutputEntry objects
|
||||
*/
|
||||
std::vector<bof::output::OutputEntry> runMockedSleepMask(SLEEPMASK_FUNC sleepMaskFunc, PBEACON_INFO beaconInfo, PFUNCTION_CALL functionCall);
|
||||
|
||||
/**
|
||||
* Setup a mock-up Beacon and execute the sleepmask function as Beacon Gate with the default stage block.
|
||||
*
|
||||
* @param sleepMaskFunc the function pointer for the sleepmask
|
||||
* @param functionCall the pointer to FUNCTION_CALL structure
|
||||
* @return A vector of OutputEntry objects
|
||||
*/
|
||||
std::vector<bof::output::OutputEntry> runMockedBeaconGate(SLEEPMASK_FUNC sleepMaskFunc, PFUNCTION_CALL functionCall);
|
||||
|
||||
/**
|
||||
* Setup a mock-up Beacon and execute the sleepmask function as Beacon Gate with a custom stage profile.
|
||||
*
|
||||
* @param sleepMaskFunc the function pointer for the sleepmask
|
||||
* @param functionCall the pointer to FUNCTION_CALL structure
|
||||
* @param stage the stage options
|
||||
* @return A vector of OutputEntry objects
|
||||
*/
|
||||
std::vector<bof::output::OutputEntry> runMockedBeaconGate(SLEEPMASK_FUNC sleepMaskFunc, PFUNCTION_CALL functionCall, const bof::profile::Stage& stage);
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
#include <windows.h>
|
||||
#include <cassert>
|
||||
|
||||
extern "C" {
|
||||
#ifdef _DEBUG
|
||||
#undef DECLSPEC_IMPORT
|
||||
#define DECLSPEC_IMPORT
|
||||
#endif
|
||||
#include "..\beacon.h"
|
||||
}
|
||||
|
||||
namespace bof {
|
||||
namespace mock {
|
||||
namespace syscall {
|
||||
/**
|
||||
* @brief A function to find the first occurrence of a system call instruction in memory.
|
||||
*
|
||||
* This function searches for a specific system call instruction pattern within the
|
||||
* memory block starting at the given address 'addr'. The pattern to search for
|
||||
* depends on the target architecture:
|
||||
*
|
||||
* - On x64, the assembly pattern is: 'syscall; ret;' (0x0f, 0x05, 0xc3).
|
||||
* - On x86, the assembly pattern is: 'sysenter; ret;' (0x0f, 0x34, 0xc3).
|
||||
*
|
||||
* @param addr A pointer to the starting address of the memory block.
|
||||
* @return A pointer to the first occurrence of the system call instruction pattern
|
||||
* if found, or NULL if the pattern is not found within the first 32 bytes.
|
||||
*
|
||||
*/
|
||||
|
||||
PBYTE FindSyscallInstruction(PBYTE addr) {
|
||||
#if _M_X64
|
||||
char syscallPattern[] = { '\x0f', '\x05', '\xc3' }; // syscall; ret;
|
||||
#else
|
||||
char syscallPattern[] = { '\x0f', '\x34', '\xc3' }; // sysenter; ret;
|
||||
#endif
|
||||
for (int offset = 0; offset < 32; ++offset) {
|
||||
if (!memcmp(syscallPattern, (char*)addr + offset, sizeof(syscallPattern))) {
|
||||
return addr + offset;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find the system call number in the memory block
|
||||
*
|
||||
* This function searches for a specific pattern within the memory block starting at the
|
||||
* given address 'addr' to identify the system call number. The pattern to search for
|
||||
* depends on the target architecture:
|
||||
*
|
||||
* - On x64, the assembly pattern is: 'mov r10, rcx; mov eax, <syscall num>' (0x4c, 0x8b, 0xd1, 0xb8).
|
||||
* - On x86, the assembly pattern is: 'mov eax, <syscall num>' (0xb8).
|
||||
*
|
||||
* @param addr A pointer to the starting address of the memory block.
|
||||
* @return The system call number found in memory following the pattern, or 0 if the
|
||||
* pattern is not found within the first 32 bytes.
|
||||
*
|
||||
*/
|
||||
DWORD FindSyscallNumber(PBYTE addr) {
|
||||
#if _M_X64
|
||||
char syscallPattern[] = { '\x4c', '\x8b', '\xd1', '\xb8' };
|
||||
#else
|
||||
char syscallPattern[] = { '\xb8' };
|
||||
#endif
|
||||
for (int offset = 0; offset < 32; ++offset) {
|
||||
if (!memcmp(syscallPattern, (char*)addr + offset, sizeof(syscallPattern))) {
|
||||
DWORD* numAddress = (DWORD*)(addr + offset + sizeof(syscallPattern));
|
||||
return *numAddress;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to resolve the a system call number and function address.
|
||||
*
|
||||
* @param entry A pointer to a SYSCALL_API_ENTRY structure where resolved information will be stored.
|
||||
* @param funcHash Hash value representing the target function to resolve.
|
||||
* @return Returns TRUE if the resolution is successful; otherwise, returns FALSE.
|
||||
*
|
||||
*/
|
||||
BOOL ResolveSyscallEntry(PSYSCALL_API_ENTRY entry, const char* funcName) {
|
||||
// Resolve the NT function address
|
||||
static HMODULE ntdll = LoadLibraryA("ntdll");
|
||||
PVOID fnAddr = GetProcAddress(ntdll, funcName);
|
||||
|
||||
if (!fnAddr) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Find the syscall number
|
||||
DWORD sysnum = FindSyscallNumber((PBYTE)fnAddr);
|
||||
|
||||
// Find the address of the syscall instruction
|
||||
PVOID jmpAddr = FindSyscallInstruction((PBYTE)fnAddr);
|
||||
|
||||
#ifdef _M_IX86
|
||||
if (!jmpAddr) {
|
||||
jmpAddr = (PVOID)__readfsdword(0xc0); // If WoW64, this returns wow64cpu!X86SwitchTo64BitMode
|
||||
}
|
||||
#endif
|
||||
|
||||
// We did not find the syscall
|
||||
if (sysnum == 0 || jmpAddr == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Fill the entry
|
||||
entry->fnAddr = fnAddr;
|
||||
entry->sysnum = sysnum;
|
||||
entry->jmpAddr = jmpAddr;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper macro for resolving an SYSCALL_API_ENTRY.
|
||||
*
|
||||
* @param field The field in the SYSCALL_API structure in which the resolved entry will be stored.
|
||||
* @param name The function name used to generate a compile-time hash for entry lookup.
|
||||
*/
|
||||
#define RESOLVE_ENTRY(field, name) { \
|
||||
if(!ResolveSyscallEntry(&field, name)) { assert(false && "Could not resolve the syscall entry"); } \
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve system call function addresses and syscall numbers.
|
||||
*
|
||||
* @param syscalls A pointer to a SYSCALL_API structure.
|
||||
* @return TRUE if all system call entries are successfully resolved, FALSE otherwise.
|
||||
*
|
||||
*/
|
||||
void ResolveSyscalls(PSYSCALL_API syscalls) {
|
||||
RESOLVE_ENTRY(syscalls->ntAllocateVirtualMemory, "NtAllocateVirtualMemory");
|
||||
RESOLVE_ENTRY(syscalls->ntAllocateVirtualMemory, "NtAllocateVirtualMemory");
|
||||
RESOLVE_ENTRY(syscalls->ntProtectVirtualMemory, "NtProtectVirtualMemory");
|
||||
RESOLVE_ENTRY(syscalls->ntFreeVirtualMemory, "NtFreeVirtualMemory");
|
||||
RESOLVE_ENTRY(syscalls->ntGetContextThread, "NtGetContextThread");
|
||||
RESOLVE_ENTRY(syscalls->ntSetContextThread, "NtSetContextThread");
|
||||
RESOLVE_ENTRY(syscalls->ntResumeThread, "NtResumeThread");
|
||||
RESOLVE_ENTRY(syscalls->ntCreateThreadEx, "NtCreateThreadEx");
|
||||
RESOLVE_ENTRY(syscalls->ntOpenProcess, "NtOpenProcess");
|
||||
RESOLVE_ENTRY(syscalls->ntOpenThread, "NtOpenThread");
|
||||
RESOLVE_ENTRY(syscalls->ntClose, "NtClose");
|
||||
RESOLVE_ENTRY(syscalls->ntCreateSection, "NtCreateSection");
|
||||
RESOLVE_ENTRY(syscalls->ntMapViewOfSection, "NtMapViewOfSection");
|
||||
RESOLVE_ENTRY(syscalls->ntUnmapViewOfSection, "NtUnmapViewOfSection");
|
||||
RESOLVE_ENTRY(syscalls->ntQueryVirtualMemory, "NtQueryVirtualMemory");
|
||||
RESOLVE_ENTRY(syscalls->ntDuplicateObject, "NtDuplicateObject");
|
||||
RESOLVE_ENTRY(syscalls->ntReadVirtualMemory, "NtReadVirtualMemory");
|
||||
RESOLVE_ENTRY(syscalls->ntWriteVirtualMemory, "NtWriteVirtualMemory");
|
||||
RESOLVE_ENTRY(syscalls->ntReadFile, "NtReadFile");
|
||||
RESOLVE_ENTRY(syscalls->ntWriteFile, "NtWriteFile");
|
||||
RESOLVE_ENTRY(syscalls->ntCreateFile, "NtCreateFile");
|
||||
|
||||
/* Added in 411 */
|
||||
RESOLVE_ENTRY(syscalls->ntQueueApcThread, "NtQueueApcThread");
|
||||
RESOLVE_ENTRY( syscalls->ntCreateProcess, "NtCreateProcess");
|
||||
RESOLVE_ENTRY(syscalls->ntOpenProcessToken, "NtOpenProcessToken");
|
||||
RESOLVE_ENTRY(syscalls->ntQueryDirectoryFile, "NtQueryDirectoryFile");
|
||||
RESOLVE_ENTRY(syscalls->ntTestAlert, "NtTestAlert");
|
||||
RESOLVE_ENTRY(syscalls->ntSuspendProcess, "NtSuspendProcess");
|
||||
RESOLVE_ENTRY(syscalls->ntResumeProcess, "NtResumeProcess");
|
||||
RESOLVE_ENTRY(syscalls->ntQuerySystemInformation, "NtQuerySystemInformation");
|
||||
RESOLVE_ENTRY(syscalls->ntSetInformationProcess, "NtSetInformationProcess");
|
||||
RESOLVE_ENTRY(syscalls->ntSetInformationThread, "NtSetInformationThread");
|
||||
RESOLVE_ENTRY(syscalls->ntQueryInformationProcess, "NtQueryInformationProcess");
|
||||
RESOLVE_ENTRY(syscalls->ntQueryInformationThread, "NtQueryInformationThread");
|
||||
RESOLVE_ENTRY(syscalls->ntOpenSection, "NtOpenSection");
|
||||
RESOLVE_ENTRY(syscalls->ntAdjustPrivilegesToken, "NtAdjustPrivilegesToken");
|
||||
RESOLVE_ENTRY(syscalls->ntDeviceIoControlFile, "NtDeviceIoControlFile");
|
||||
RESOLVE_ENTRY(syscalls->ntWaitForMultipleObjects, "NtWaitForMultipleObjects");
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to resolve the RTL function address.
|
||||
*
|
||||
* @param address A pointer to where the resolved information will be stored.
|
||||
* @param funcHash Hash value representing the target function to resolve.
|
||||
*
|
||||
* @return Returns TRUE if the resolution is successful; otherwise, returns FALSE.
|
||||
*
|
||||
*/
|
||||
BOOL ResolveNtdllFunc(PVOID* address, const char* funcName) {
|
||||
static const HMODULE ntdll = LoadLibraryA("ntdll");
|
||||
*address = GetProcAddress(ntdll, funcName);
|
||||
return *address != NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper macro for resolving a RTL function address.
|
||||
*
|
||||
* @param field The field in the RTL_API structure in which the resolved function address will be stored.
|
||||
* @param name The function name used to generate a compile-time hash for entry lookup.
|
||||
*/
|
||||
#define RESOLVE_RTL_ENTRY(field, name) { \
|
||||
if(!ResolveNtdllFunc(&field, name)) { assert(field && "Could not resolve RTL entry"); } \
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve RTL function addresses.
|
||||
*
|
||||
* @param rtls A pointer to a RTL_API structure.
|
||||
*
|
||||
*/
|
||||
void ResolveRtls(PRTL_API rtls) {
|
||||
/* Resolve the RTL function addresses */
|
||||
RESOLVE_RTL_ENTRY(rtls->rtlDosPathNameToNtPathNameUWithStatusAddr, "RtlDosPathNameToNtPathName_U_WithStatus");
|
||||
RESOLVE_RTL_ENTRY(rtls->rtlFreeHeapAddr, "RtlFreeHeap");
|
||||
rtls->rtlGetProcessHeapAddr = GetProcessHeap();
|
||||
assert(rtls->rtlGetProcessHeapAddr && "Could not get the process heap address");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* Beacon Object Files (BOF)
|
||||
* -------------------------
|
||||
* A Beacon Object File is a light-weight post exploitation tool that runs
|
||||
* with Beacon's inline-execute command.
|
||||
*
|
||||
* Additional BOF resources are available here:
|
||||
* - https://github.com/Cobalt-Strike/bof_template
|
||||
*
|
||||
* Cobalt Strike 4.x
|
||||
* ChangeLog:
|
||||
* 1/25/2022: updated for 4.5
|
||||
* 7/18/2023: Added BeaconInformation API for 4.9
|
||||
* 7/31/2023: Added Key/Value store APIs for 4.9
|
||||
* BeaconAddValue, BeaconGetValue, and BeaconRemoveValue
|
||||
* 8/31/2023: Added Data store APIs for 4.9
|
||||
* BeaconDataStoreGetItem, BeaconDataStoreProtectItem,
|
||||
* BeaconDataStoreUnprotectItem, and BeaconDataStoreMaxEntries
|
||||
* 9/01/2023: Added BeaconGetCustomUserData API for 4.9
|
||||
* 3/21/2024: Updated BeaconInformation API for 4.10 to return a BOOL
|
||||
* Updated the BEACON_INFO data structure to add new parameters
|
||||
* 4/19/2024: Added BeaconGetSyscallInformation API for 4.10
|
||||
* 4/25/2024: Added APIs to call Beacon's system call implementation
|
||||
* 12/18/2024: Updated BeaconGetSyscallInformation API for 4.11 (Breaking changes)
|
||||
* 2/13/2025: Updated SYSCALL_API structure with more ntAPIs for 4.11
|
||||
* 3/20/2025: Updated ALLOCATED_MEMORY_SECTION structure with driploader page size for 4.12
|
||||
* 4/7/2025: Updated ALLOCATED_MEMORY_REGION structure with driploader allocation granularity for 4.12
|
||||
* 7/16/2025: Updated ALLOCATED_MEMORY_PURPOSE structure with PURPOSE_UDC2_MEMORY for 4.12
|
||||
*/
|
||||
#ifndef _BEACON_H_
|
||||
#define _BEACON_H_
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
/* data API */
|
||||
typedef struct {
|
||||
char * original; /* the original buffer [so we can free it] */
|
||||
char * buffer; /* current pointer into our buffer */
|
||||
int length; /* remaining length of data */
|
||||
int size; /* total size of this buffer */
|
||||
} datap;
|
||||
|
||||
DECLSPEC_IMPORT void BeaconDataParse(datap * parser, char * buffer, int size);
|
||||
DECLSPEC_IMPORT char * BeaconDataPtr(datap * parser, int size);
|
||||
DECLSPEC_IMPORT int BeaconDataInt(datap * parser);
|
||||
DECLSPEC_IMPORT short BeaconDataShort(datap * parser);
|
||||
DECLSPEC_IMPORT int BeaconDataLength(datap * parser);
|
||||
DECLSPEC_IMPORT char * BeaconDataExtract(datap * parser, int * size);
|
||||
|
||||
/* format API */
|
||||
typedef struct {
|
||||
char * original; /* the original buffer [so we can free it] */
|
||||
char * buffer; /* current pointer into our buffer */
|
||||
int length; /* remaining length of data */
|
||||
int size; /* total size of this buffer */
|
||||
} formatp;
|
||||
|
||||
DECLSPEC_IMPORT void BeaconFormatAlloc(formatp * format, int maxsz);
|
||||
DECLSPEC_IMPORT void BeaconFormatReset(formatp * format);
|
||||
DECLSPEC_IMPORT void BeaconFormatAppend(formatp * format, const char * text, int len);
|
||||
DECLSPEC_IMPORT void BeaconFormatPrintf(formatp * format, const char * fmt, ...);
|
||||
DECLSPEC_IMPORT char * BeaconFormatToString(formatp * format, int * size);
|
||||
DECLSPEC_IMPORT void BeaconFormatFree(formatp * format);
|
||||
DECLSPEC_IMPORT void BeaconFormatInt(formatp * format, int value);
|
||||
|
||||
/* Output Functions */
|
||||
#define CALLBACK_OUTPUT 0x0
|
||||
#define CALLBACK_OUTPUT_OEM 0x1e
|
||||
#define CALLBACK_OUTPUT_UTF8 0x20
|
||||
#define CALLBACK_ERROR 0x0d
|
||||
#define CALLBACK_CUSTOM 0x1000
|
||||
#define CALLBACK_CUSTOM_LAST 0x13ff
|
||||
|
||||
|
||||
DECLSPEC_IMPORT void BeaconOutput(int type, const char * data, int len);
|
||||
DECLSPEC_IMPORT void BeaconPrintf(int type, const char * fmt, ...);
|
||||
DECLSPEC_IMPORT BOOL BeaconDownload(const char * filename, const char* buffer, unsigned int length);
|
||||
|
||||
|
||||
/* Token Functions */
|
||||
DECLSPEC_IMPORT BOOL BeaconUseToken(HANDLE token);
|
||||
DECLSPEC_IMPORT void BeaconRevertToken();
|
||||
DECLSPEC_IMPORT BOOL BeaconIsAdmin();
|
||||
|
||||
/* Spawn+Inject Functions */
|
||||
DECLSPEC_IMPORT void BeaconGetSpawnTo(BOOL x86, char * buffer, int length);
|
||||
DECLSPEC_IMPORT void BeaconInjectProcess(HANDLE hProc, int pid, char * payload, int p_len, int p_offset, char * arg, int a_len);
|
||||
DECLSPEC_IMPORT void BeaconInjectTemporaryProcess(PROCESS_INFORMATION * pInfo, char * payload, int p_len, int p_offset, char * arg, int a_len);
|
||||
DECLSPEC_IMPORT BOOL BeaconSpawnTemporaryProcess(BOOL x86, BOOL ignoreToken, STARTUPINFO * si, PROCESS_INFORMATION * pInfo);
|
||||
DECLSPEC_IMPORT void BeaconCleanupProcess(PROCESS_INFORMATION * pInfo);
|
||||
|
||||
/* Utility Functions */
|
||||
DECLSPEC_IMPORT BOOL toWideChar(char * src, wchar_t * dst, int max);
|
||||
|
||||
/* Beacon Information */
|
||||
/*
|
||||
* ptr - pointer to the base address of the allocated memory.
|
||||
* size - the number of bytes allocated for the ptr.
|
||||
*/
|
||||
typedef struct {
|
||||
char * ptr;
|
||||
size_t size;
|
||||
} HEAP_RECORD;
|
||||
#define MASK_SIZE 13
|
||||
|
||||
/* Information the user can set in the USER_DATA via a UDRL */
|
||||
typedef enum {
|
||||
PURPOSE_EMPTY,
|
||||
PURPOSE_GENERIC_BUFFER,
|
||||
PURPOSE_BEACON_MEMORY,
|
||||
PURPOSE_SLEEPMASK_MEMORY,
|
||||
PURPOSE_BOF_MEMORY,
|
||||
PURPOSE_UDC2_MEMORY,
|
||||
PURPOSE_USER_DEFINED_MEMORY = 1000
|
||||
} ALLOCATED_MEMORY_PURPOSE;
|
||||
|
||||
typedef enum {
|
||||
LABEL_EMPTY,
|
||||
LABEL_BUFFER,
|
||||
LABEL_PEHEADER,
|
||||
LABEL_TEXT,
|
||||
LABEL_RDATA,
|
||||
LABEL_DATA,
|
||||
LABEL_PDATA,
|
||||
LABEL_RELOC,
|
||||
LABEL_USER_DEFINED = 1000
|
||||
} ALLOCATED_MEMORY_LABEL;
|
||||
|
||||
typedef enum {
|
||||
METHOD_UNKNOWN,
|
||||
METHOD_VIRTUALALLOC,
|
||||
METHOD_HEAPALLOC,
|
||||
METHOD_MODULESTOMP,
|
||||
METHOD_NTMAPVIEW,
|
||||
METHOD_USER_DEFINED = 1000,
|
||||
} ALLOCATED_MEMORY_ALLOCATION_METHOD;
|
||||
|
||||
/**
|
||||
* This structure allows the user to provide additional information
|
||||
* about the allocated heap for cleanup. It is mandatory to provide
|
||||
* the HeapHandle but the DestroyHeap Boolean can be used to indicate
|
||||
* whether the clean up code should destroy the heap or simply free the pages.
|
||||
* This is useful in situations where a loader allocates memory in the
|
||||
* processes current heap.
|
||||
*/
|
||||
typedef struct _HEAPALLOC_INFO {
|
||||
PVOID HeapHandle;
|
||||
BOOL DestroyHeap;
|
||||
} HEAPALLOC_INFO, *PHEAPALLOC_INFO;
|
||||
|
||||
typedef struct _MODULESTOMP_INFO {
|
||||
HMODULE ModuleHandle;
|
||||
} MODULESTOMP_INFO, *PMODULESTOMP_INFO;
|
||||
|
||||
typedef union _ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION {
|
||||
HEAPALLOC_INFO HeapAllocInfo;
|
||||
MODULESTOMP_INFO ModuleStompInfo;
|
||||
PVOID Custom;
|
||||
} ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_CLEANUP_INFORMATION {
|
||||
BOOL Cleanup;
|
||||
ALLOCATED_MEMORY_ALLOCATION_METHOD AllocationMethod;
|
||||
ALLOCATED_MEMORY_ADDITIONAL_CLEANUP_INFORMATION AdditionalCleanupInformation;
|
||||
} ALLOCATED_MEMORY_CLEANUP_INFORMATION, *PALLOCATED_MEMORY_CLEANUP_INFORMATION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_SECTION {
|
||||
ALLOCATED_MEMORY_LABEL Label; // A label to simplify Sleepmask development
|
||||
PVOID BaseAddress; // Pointer to virtual address of section
|
||||
SIZE_T VirtualSize; // Virtual size of the section
|
||||
DWORD CurrentProtect; // Current memory protection of the section
|
||||
DWORD PreviousProtect; // The previous memory protection of the section (prior to masking/unmasking)
|
||||
BOOL MaskSection; // A boolean to indicate whether the section should be masked
|
||||
DWORD DripLoadPageSize; // The page size used when committing memory during drip-loading
|
||||
} ALLOCATED_MEMORY_SECTION, *PALLOCATED_MEMORY_SECTION;
|
||||
|
||||
typedef struct _ALLOCATED_MEMORY_REGION {
|
||||
ALLOCATED_MEMORY_PURPOSE Purpose; // A label to indicate the purpose of the allocated memory
|
||||
PVOID AllocationBase; // The base address of the allocated memory block
|
||||
SIZE_T RegionSize; // The size of the allocated memory block
|
||||
DWORD Type; // The type of memory allocated
|
||||
DWORD DripLoadAllocationGranularity; // The allocation granularity used when reserving memory for drip-loading
|
||||
ALLOCATED_MEMORY_SECTION Sections[8]; // An array of section information structures
|
||||
ALLOCATED_MEMORY_CLEANUP_INFORMATION CleanupInformation; // Information required to cleanup the allocation
|
||||
} ALLOCATED_MEMORY_REGION, *PALLOCATED_MEMORY_REGION;
|
||||
|
||||
typedef struct {
|
||||
ALLOCATED_MEMORY_REGION AllocatedMemoryRegions[6];
|
||||
} ALLOCATED_MEMORY, *PALLOCATED_MEMORY;
|
||||
|
||||
/*
|
||||
* version - The version of the beacon dll was added for release 4.10
|
||||
* version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch
|
||||
* e.g. 0x040900 -> CS 4.9
|
||||
* 0x041000 -> CS 4.10
|
||||
*
|
||||
* sleep_mask_ptr - pointer to the sleep mask base address
|
||||
* sleep_mask_text_size - the sleep mask text section size
|
||||
* sleep_mask_total_size - the sleep mask total memory size
|
||||
*
|
||||
* beacon_ptr - pointer to beacon's base address
|
||||
* The stage.obfuscate flag affects this value when using CS default loader.
|
||||
* true: beacon_ptr = allocated_buffer - 0x1000 (Not a valid address)
|
||||
* false: beacon_ptr = allocated_buffer (A valid address)
|
||||
* For a UDRL the beacon_ptr will be set to the 1st argument to DllMain
|
||||
* when the 2nd argument is set to DLL_PROCESS_ATTACH.
|
||||
* heap_records - list of memory addresses on the heap beacon wants to mask.
|
||||
* The list is terminated by the HEAP_RECORD.ptr set to NULL.
|
||||
* mask - the mask that beacon randomly generated to apply
|
||||
*
|
||||
* Added in version 4.10
|
||||
* allocatedMemory - An ALLOCATED_MEMORY structure that can be set in the USER_DATA
|
||||
* via a UDRL.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int version;
|
||||
char * sleep_mask_ptr;
|
||||
DWORD sleep_mask_text_size;
|
||||
DWORD sleep_mask_total_size;
|
||||
|
||||
char * beacon_ptr;
|
||||
HEAP_RECORD * heap_records;
|
||||
char mask[MASK_SIZE];
|
||||
|
||||
ALLOCATED_MEMORY allocatedMemory;
|
||||
} BEACON_INFO, *PBEACON_INFO;
|
||||
|
||||
DECLSPEC_IMPORT BOOL BeaconInformation(PBEACON_INFO info);
|
||||
|
||||
/* Key/Value store functions
|
||||
* These functions are used to associate a key to a memory address and save
|
||||
* that information into beacon. These memory addresses can then be
|
||||
* retrieved in a subsequent execution of a BOF.
|
||||
*
|
||||
* key - the key will be converted to a hash which is used to locate the
|
||||
* memory address.
|
||||
*
|
||||
* ptr - a memory address to save.
|
||||
*
|
||||
* Considerations:
|
||||
* - The contents at the memory address is not masked by beacon.
|
||||
* - The contents at the memory address is not released by beacon.
|
||||
*
|
||||
*/
|
||||
DECLSPEC_IMPORT BOOL BeaconAddValue(const char * key, void * ptr);
|
||||
DECLSPEC_IMPORT void * BeaconGetValue(const char * key);
|
||||
DECLSPEC_IMPORT BOOL BeaconRemoveValue(const char * key);
|
||||
|
||||
/* Beacon Data Store functions
|
||||
* These functions are used to access items in Beacon's Data Store.
|
||||
* BeaconDataStoreGetItem returns NULL if the index does not exist.
|
||||
*
|
||||
* The contents are masked by default, and BOFs must unprotect the entry
|
||||
* before accessing the data buffer. BOFs must also protect the entry
|
||||
* after the data is not used anymore.
|
||||
*
|
||||
*/
|
||||
|
||||
#define DATA_STORE_TYPE_EMPTY 0
|
||||
#define DATA_STORE_TYPE_GENERAL_FILE 1
|
||||
|
||||
typedef struct {
|
||||
int type;
|
||||
DWORD64 hash;
|
||||
BOOL masked;
|
||||
char* buffer;
|
||||
size_t length;
|
||||
} DATA_STORE_OBJECT, *PDATA_STORE_OBJECT;
|
||||
|
||||
DECLSPEC_IMPORT PDATA_STORE_OBJECT BeaconDataStoreGetItem(size_t index);
|
||||
DECLSPEC_IMPORT void BeaconDataStoreProtectItem(size_t index);
|
||||
DECLSPEC_IMPORT void BeaconDataStoreUnprotectItem(size_t index);
|
||||
DECLSPEC_IMPORT size_t BeaconDataStoreMaxEntries();
|
||||
|
||||
/* Beacon User Data functions */
|
||||
DECLSPEC_IMPORT char * BeaconGetCustomUserData();
|
||||
|
||||
/* Beacon System call */
|
||||
/* Syscalls API */
|
||||
typedef struct
|
||||
{
|
||||
PVOID fnAddr;
|
||||
PVOID jmpAddr;
|
||||
DWORD sysnum;
|
||||
} SYSCALL_API_ENTRY, *PSYSCALL_API_ENTRY;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SYSCALL_API_ENTRY ntAllocateVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntProtectVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntFreeVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntGetContextThread;
|
||||
SYSCALL_API_ENTRY ntSetContextThread;
|
||||
SYSCALL_API_ENTRY ntResumeThread;
|
||||
SYSCALL_API_ENTRY ntCreateThreadEx;
|
||||
SYSCALL_API_ENTRY ntOpenProcess;
|
||||
SYSCALL_API_ENTRY ntOpenThread;
|
||||
SYSCALL_API_ENTRY ntClose;
|
||||
SYSCALL_API_ENTRY ntCreateSection;
|
||||
SYSCALL_API_ENTRY ntMapViewOfSection;
|
||||
SYSCALL_API_ENTRY ntUnmapViewOfSection;
|
||||
SYSCALL_API_ENTRY ntQueryVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntDuplicateObject;
|
||||
SYSCALL_API_ENTRY ntReadVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntWriteVirtualMemory;
|
||||
SYSCALL_API_ENTRY ntReadFile;
|
||||
SYSCALL_API_ENTRY ntWriteFile;
|
||||
SYSCALL_API_ENTRY ntCreateFile;
|
||||
SYSCALL_API_ENTRY ntQueueApcThread;
|
||||
SYSCALL_API_ENTRY ntCreateProcess;
|
||||
SYSCALL_API_ENTRY ntOpenProcessToken;
|
||||
SYSCALL_API_ENTRY ntTestAlert;
|
||||
SYSCALL_API_ENTRY ntSuspendProcess;
|
||||
SYSCALL_API_ENTRY ntResumeProcess;
|
||||
SYSCALL_API_ENTRY ntQuerySystemInformation;
|
||||
SYSCALL_API_ENTRY ntQueryDirectoryFile;
|
||||
SYSCALL_API_ENTRY ntSetInformationProcess;
|
||||
SYSCALL_API_ENTRY ntSetInformationThread;
|
||||
SYSCALL_API_ENTRY ntQueryInformationProcess;
|
||||
SYSCALL_API_ENTRY ntQueryInformationThread;
|
||||
SYSCALL_API_ENTRY ntOpenSection;
|
||||
SYSCALL_API_ENTRY ntAdjustPrivilegesToken;
|
||||
SYSCALL_API_ENTRY ntDeviceIoControlFile;
|
||||
SYSCALL_API_ENTRY ntWaitForMultipleObjects;
|
||||
} SYSCALL_API, *PSYSCALL_API;
|
||||
|
||||
/* Additional Run Time Library (RTL) addresses used to support system calls.
|
||||
* If they are not set then system calls that require them will fall back
|
||||
* to the Standard Windows API.
|
||||
*
|
||||
* Required to support the following system calls:
|
||||
* ntCreateFile
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
PVOID rtlDosPathNameToNtPathNameUWithStatusAddr;
|
||||
PVOID rtlFreeHeapAddr;
|
||||
PVOID rtlGetProcessHeapAddr;
|
||||
} RTL_API, *PRTL_API;
|
||||
|
||||
/* Updated in version 4.11 to use the entire structure instead of pointers to the structure.
|
||||
* This allows for retrieving a copy of the information which would be under the BOF's
|
||||
* control instead of a reference pointer which may be obfuscated when beacon is sleeping.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
SYSCALL_API syscalls;
|
||||
RTL_API rtls;
|
||||
} BEACON_SYSCALLS, *PBEACON_SYSCALLS;
|
||||
|
||||
/* Updated in version 4.11 to include the size of the info pointer, which equals sizeof(BEACON_SYSCALLS) */
|
||||
DECLSPEC_IMPORT BOOL BeaconGetSyscallInformation(PBEACON_SYSCALLS info, SIZE_T infoSize, BOOL resolveIfNotInitialized);
|
||||
|
||||
/* Beacon System call functions which will use the current system call method */
|
||||
DECLSPEC_IMPORT LPVOID BeaconVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
DECLSPEC_IMPORT LPVOID BeaconVirtualAllocEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
|
||||
DECLSPEC_IMPORT BOOL BeaconVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
DECLSPEC_IMPORT BOOL BeaconVirtualProtectEx(HANDLE processHandle, LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect);
|
||||
DECLSPEC_IMPORT BOOL BeaconVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType);
|
||||
DECLSPEC_IMPORT BOOL BeaconGetThreadContext(HANDLE threadHandle, PCONTEXT threadContext);
|
||||
DECLSPEC_IMPORT BOOL BeaconSetThreadContext(HANDLE threadHandle, PCONTEXT threadContext);
|
||||
DECLSPEC_IMPORT DWORD BeaconResumeThread(HANDLE threadHandle);
|
||||
DECLSPEC_IMPORT HANDLE BeaconOpenProcess(DWORD desiredAccess, BOOL inheritHandle, DWORD processId);
|
||||
DECLSPEC_IMPORT HANDLE BeaconOpenThread(DWORD desiredAccess, BOOL inheritHandle, DWORD threadId);
|
||||
DECLSPEC_IMPORT BOOL BeaconCloseHandle(HANDLE object);
|
||||
DECLSPEC_IMPORT BOOL BeaconUnmapViewOfFile(LPCVOID baseAddress);
|
||||
DECLSPEC_IMPORT SIZE_T BeaconVirtualQuery(LPCVOID address, PMEMORY_BASIC_INFORMATION buffer, SIZE_T length);
|
||||
DECLSPEC_IMPORT BOOL BeaconDuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
|
||||
DECLSPEC_IMPORT BOOL BeaconReadProcessMemory(HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead);
|
||||
DECLSPEC_IMPORT BOOL BeaconWriteProcessMemory(HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten);
|
||||
|
||||
/* Beacon Gate APIs */
|
||||
DECLSPEC_IMPORT VOID BeaconDisableBeaconGate();
|
||||
DECLSPEC_IMPORT VOID BeaconEnableBeaconGate();
|
||||
|
||||
DECLSPEC_IMPORT VOID BeaconDisableBeaconGateMasking();
|
||||
DECLSPEC_IMPORT VOID BeaconEnableBeaconGateMasking();
|
||||
|
||||
/* Beacon User Data
|
||||
*
|
||||
* version format: 0xMMmmPP, where MM = Major, mm = Minor, and PP = Patch
|
||||
* e.g. 0x040900 -> CS 4.9
|
||||
* 0x041000 -> CS 4.10
|
||||
*/
|
||||
|
||||
#define DLL_BEACON_USER_DATA 0x0d
|
||||
#define BEACON_USER_DATA_CUSTOM_SIZE 32
|
||||
typedef struct
|
||||
{
|
||||
unsigned int version;
|
||||
PSYSCALL_API syscalls;
|
||||
char custom[BEACON_USER_DATA_CUSTOM_SIZE];
|
||||
PRTL_API rtls;
|
||||
PALLOCATED_MEMORY allocatedMemory;
|
||||
} USER_DATA, * PUSER_DATA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif // __cplusplus
|
||||
#endif // _BEACON_H_
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifndef _BEACON_GATE_H
|
||||
#define _BEACON_GATE_H
|
||||
#include <windows.h>
|
||||
|
||||
/* Beacon gate defines. */
|
||||
#define MAX_BEACON_GATE_ARGUMENTS 10
|
||||
#define beaconGate(i) ((BEACON_GATE_##i)gateFunction->functionPtr)
|
||||
#define arg(i) (ULONG_PTR)gateFunction->args[i]
|
||||
|
||||
/* Enum to specify what WinAPI is being called. */
|
||||
typedef enum _WinApi {
|
||||
INTERNETOPENA,
|
||||
INTERNETCONNECTA,
|
||||
VIRTUALALLOC,
|
||||
VIRTUALALLOCEX,
|
||||
VIRTUALPROTECT,
|
||||
VIRTUALPROTECTEX,
|
||||
VIRTUALFREE,
|
||||
GETTHREADCONTEXT,
|
||||
SETTHREADCONTEXT,
|
||||
RESUMETHREAD,
|
||||
CREATETHREAD,
|
||||
CREATEREMOTETHREAD,
|
||||
OPENPROCESS,
|
||||
OPENTHREAD,
|
||||
CLOSEHANDLE,
|
||||
CREATEFILEMAPPING,
|
||||
MAPVIEWOFFILE,
|
||||
UNMAPVIEWOFFILE,
|
||||
VIRTUALQUERY,
|
||||
DUPLICATEHANDLE,
|
||||
READPROCESSMEMORY,
|
||||
WRITEPROCESSMEMORY,
|
||||
EXITTHREAD,
|
||||
VIRTUALFREEEX,
|
||||
VIRTUALQUERYEX,
|
||||
WAITFORSINGLEOBJECT,
|
||||
SLEEP
|
||||
} WinApi;
|
||||
|
||||
/**
|
||||
* FUNCTION_CALL struct which encapsulates atomic function call.
|
||||
*
|
||||
* functionPtr - target function to call
|
||||
* function - Enum representing target WinApi
|
||||
* numOfArgs - number of arguments
|
||||
* args - array of ULONG_PTRs containing the passed arguments (e.g. rcx, rdx, ...)
|
||||
* bMask - BOOL indicating whether Beacon should be masked during the call
|
||||
* ULONG_PTR - retValue of the atomic function call
|
||||
*/
|
||||
typedef struct {
|
||||
PVOID functionPtr;
|
||||
WinApi function;
|
||||
int numOfArgs;
|
||||
ULONG_PTR args[MAX_BEACON_GATE_ARGUMENTS];
|
||||
BOOL bMask;
|
||||
ULONG_PTR retValue;
|
||||
} FUNCTION_CALL, * PFUNCTION_CALL;
|
||||
|
||||
/* Currently support max 10 arguments. */
|
||||
/* NB For x86 we only support std call convention as this is what Windows uses for most Win32 APIs. */
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_00)(VOID);
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_01)(ULONG_PTR);
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_02)(ULONG_PTR, ULONG_PTR);
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_03)(ULONG_PTR, ULONG_PTR, ULONG_PTR);
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_04)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR);
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_05)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR);
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_06)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR);
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_07)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR);
|
||||
typedef ULONG_PTR(__stdcall* BEACON_GATE_08)(ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR, ULONG_PTR);
|
||||
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);
|
||||
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);
|
||||
#endif // _BEACON_GATE_H
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static" version="1.8.1.7" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -0,0 +1,9 @@
|
||||
#ifndef _SLEEPMASK_H_
|
||||
#define _SLEEPMASK_H_
|
||||
|
||||
#include <windows.h>
|
||||
#include "beacon.h"
|
||||
#include "beacon_gate.h"
|
||||
|
||||
typedef void(* SLEEPMASK_FUNC)(PBEACON_INFO, PFUNCTION_CALL);
|
||||
#endif // _SLEEPMASK_H_
|
||||
@@ -0,0 +1,799 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import struct
|
||||
from enum import Enum, auto, IntFlag
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
|
||||
###############################################################################
|
||||
# Constants and Struct Formats
|
||||
###############################################################################
|
||||
|
||||
COFF_HEADER_FORMAT = "<HHLLLHH" # 20 bytes
|
||||
COFF_HEADER_SIZE = struct.calcsize(COFF_HEADER_FORMAT)
|
||||
|
||||
SECTION_HEADER_FORMAT = "<8sLLLLLLHHL" # 40 bytes
|
||||
SECTION_HEADER_SIZE = struct.calcsize(SECTION_HEADER_FORMAT)
|
||||
|
||||
SYMBOL_FORMAT = "<8sLHHBB" # 18 bytes
|
||||
SYMBOL_SIZE = struct.calcsize(SYMBOL_FORMAT)
|
||||
|
||||
RELOCATION_FORMAT = "<LLH" # 10 bytes
|
||||
RELOCATION_SIZE = struct.calcsize(RELOCATION_FORMAT)
|
||||
|
||||
# Machine constants
|
||||
MACHINE_X86 = 0x14C # IMAGE_FILE_MACHINE_I386
|
||||
MACHINE_AMD64 = 0x8664 # IMAGE_FILE_MACHINE_AMD64
|
||||
|
||||
# Define relocation types
|
||||
class ImageRelocationType(Enum):
|
||||
IMAGE_REL_I386_ABSOLUTE = 0x0000
|
||||
IMAGE_REL_I386_DIR16 = 0x0001
|
||||
IMAGE_REL_I386_REL16 = 0x0002
|
||||
IMAGE_REL_I386_DIR32 = 0x0006
|
||||
IMAGE_REL_I386_DIR32NB = 0x0007
|
||||
IMAGE_REL_I386_SEG12 = 0x0009
|
||||
IMAGE_REL_I386_SECTION = 0x000A
|
||||
IMAGE_REL_I386_SECREL = 0x000B
|
||||
IMAGE_REL_I386_TOKEN = 0x000C
|
||||
IMAGE_REL_I386_SECREL7 = 0x000D
|
||||
IMAGE_REL_I386_REL32 = 0x0014
|
||||
|
||||
IMAGE_REL_AMD64_ABSOLUTE = 0x0000
|
||||
IMAGE_REL_AMD64_ADDR64 = 0x0001
|
||||
IMAGE_REL_AMD64_ADDR32 = 0x0002
|
||||
IMAGE_REL_AMD64_ADDR32NB = 0x0003
|
||||
IMAGE_REL_AMD64_REL32 = 0x0004
|
||||
IMAGE_REL_AMD64_REL32_1 = 0x0005
|
||||
IMAGE_REL_AMD64_REL32_2 = 0x0006
|
||||
IMAGE_REL_AMD64_REL32_3 = 0x0007
|
||||
IMAGE_REL_AMD64_REL32_4 = 0x0008
|
||||
IMAGE_REL_AMD64_REL32_5 = 0x0009
|
||||
IMAGE_REL_AMD64_SECTION = 0x000A
|
||||
IMAGE_REL_AMD64_SECREL = 0x000B
|
||||
IMAGE_REL_AMD64_SECREL7 = 0x000C
|
||||
IMAGE_REL_AMD64_TOKEN = 0x000D
|
||||
IMAGE_REL_AMD64_SREL32 = 0x000E
|
||||
IMAGE_REL_AMD64_PAIR = 0x000F
|
||||
IMAGE_REL_AMD64_SSPAN32 = 0x0010
|
||||
IMAGE_REL_AMD64_EHANDLER = 0x0011
|
||||
IMAGE_REL_AMD64_IMPORT_BR = 0x0012
|
||||
IMAGE_REL_AMD64_IMPORT_CALL = 0x0013
|
||||
IMAGE_REL_AMD64_CFG_BR = 0x0014
|
||||
IMAGE_REL_AMD64_CFG_BR_REX = 0x0015
|
||||
IMAGE_REL_AMD64_CFG_CALL = 0x0016
|
||||
IMAGE_REL_AMD64_INDIR_BR = 0x0017
|
||||
IMAGE_REL_AMD64_INDIR_BR_REX = 0x0018
|
||||
IMAGE_REL_AMD64_INDIR_CALL = 0x0019
|
||||
IMAGE_REL_AMD64_INDIR_BR_SWITCHTABLE_FIRST = 0x0020
|
||||
IMAGE_REL_AMD64_INDIR_BR_SWITCHTABLE_LAST = 0x002F
|
||||
|
||||
list_of_implant_functions_cs = [
|
||||
"BeaconDataParse",
|
||||
"BeaconDataPtr",
|
||||
"BeaconDataInt",
|
||||
"BeaconDataShort",
|
||||
"BeaconDataLength",
|
||||
"BeaconDataExtract",
|
||||
"BeaconFormatAlloc",
|
||||
"BeaconFormatReset",
|
||||
"BeaconFormatAppend",
|
||||
"BeaconFormatPrintf",
|
||||
"BeaconFormatToString",
|
||||
"BeaconFormatFree",
|
||||
"BeaconFormatInt",
|
||||
"BeaconOutput",
|
||||
"BeaconPrintf",
|
||||
"BeaconUseToken",
|
||||
"BeaconRevertToken",
|
||||
"BeaconIsAdmin",
|
||||
"BeaconGetSpawnTo",
|
||||
"BeaconInjectProcess",
|
||||
"BeaconInjectTemporaryProcess",
|
||||
"BeaconSpawnTemporaryProcess",
|
||||
"BeaconCleanupProcess",
|
||||
"toWideChar",
|
||||
"BeaconInformation",
|
||||
"BeaconAddValue",
|
||||
"BeaconGetValue",
|
||||
"BeaconRemoveValue",
|
||||
"BeaconDataStoreGetItem",
|
||||
"BeaconDataStoreProtectItem",
|
||||
"BeaconDataStoreUnprotectItem",
|
||||
"BeaconDataStoreMaxEntries",
|
||||
"BeaconGetCustomUserData",
|
||||
"BeaconGetSyscallInformation",
|
||||
"BeaconVirtualAlloc",
|
||||
"BeaconVirtualAllocEx",
|
||||
"BeaconVirtualProtect",
|
||||
"BeaconVirtualProtectEx",
|
||||
"BeaconVirtualFree",
|
||||
"BeaconGetThreadContext",
|
||||
"BeaconSetThreadContext",
|
||||
"BeaconResumeThread",
|
||||
"BeaconOpenProcess",
|
||||
"BeaconOpenThread",
|
||||
"BeaconCloseHandle",
|
||||
"BeaconUnmapViewOfFile",
|
||||
"BeaconVirtualQuery",
|
||||
"BeaconDuplicateHandle",
|
||||
"BeaconReadProcessMemory",
|
||||
"BeaconWriteProcessMemory",
|
||||
"BeaconDisableBeaconGate",
|
||||
"BeaconEnableBeaconGate",
|
||||
"GetProcAddress",
|
||||
"GetModuleHandleA",
|
||||
"GetModuleHandleW",
|
||||
"LoadLibraryA",
|
||||
"LoadLibraryW",
|
||||
"FreeLibrary"
|
||||
]
|
||||
|
||||
list_of_implant_functions_oc2 = [
|
||||
"BeaconDataParse",
|
||||
"BeaconDataInt",
|
||||
"BeaconDataShort",
|
||||
"BeaconDataLength",
|
||||
"BeaconDataExtract",
|
||||
"BeaconFormatAlloc",
|
||||
"BeaconFormatFree",
|
||||
"BeaconFormatReset",
|
||||
"BeaconFormatAppend",
|
||||
"BeaconFormatPrintf",
|
||||
"BeaconFormatToString",
|
||||
"BeaconFormatInt",
|
||||
"BeaconPrintf",
|
||||
"BeaconOutput",
|
||||
"BeaconUseToken",
|
||||
"BeaconRevertToken",
|
||||
"BeaconIsAdmin",
|
||||
"BeaconThrow",
|
||||
"BeaconInjectProcess",
|
||||
"GetProcAddress",
|
||||
"GetModuleHandleA",
|
||||
"GetModuleHandleW",
|
||||
"LoadLibraryA",
|
||||
"LoadLibraryW",
|
||||
"FreeLibrary"
|
||||
]
|
||||
|
||||
list_of_implant_functions_ci = [
|
||||
"BeaconDataParse",
|
||||
"BeaconDataPtr",
|
||||
"BeaconDataInt",
|
||||
"BeaconDataShort",
|
||||
"BeaconDataLength",
|
||||
"BeaconDataExtract",
|
||||
"BeaconFormatAlloc",
|
||||
"BeaconFormatReset",
|
||||
"BeaconFormatAppend",
|
||||
"BeaconFormatPrintf",
|
||||
"BeaconFormatToString",
|
||||
"BeaconFormatFree",
|
||||
"BeaconFormatInt",
|
||||
"BeaconOutput",
|
||||
"BeaconPrintf",
|
||||
"GetProcAddress",
|
||||
"GetModuleHandleA",
|
||||
"LoadLibraryA",
|
||||
"FreeLibrary"
|
||||
]
|
||||
|
||||
|
||||
###############################################################################
|
||||
# Classes Representing COFF Structures
|
||||
###############################################################################
|
||||
|
||||
class COFFFileHeader:
|
||||
"""
|
||||
Represents the 20-byte COFF File Header.
|
||||
"""
|
||||
__slots__ = (
|
||||
"machine", "number_of_sections", "time_date_stamp",
|
||||
"pointer_to_symbol_table", "number_of_symbols",
|
||||
"size_of_optional_header", "characteristics",
|
||||
)
|
||||
|
||||
def __init__(self, machine, number_of_sections, time_date_stamp,
|
||||
pointer_to_symbol_table, number_of_symbols,
|
||||
size_of_optional_header, characteristics):
|
||||
self.machine = machine
|
||||
self.number_of_sections = number_of_sections
|
||||
self.time_date_stamp = time_date_stamp
|
||||
self.pointer_to_symbol_table = pointer_to_symbol_table
|
||||
self.number_of_symbols = number_of_symbols
|
||||
self.size_of_optional_header = size_of_optional_header
|
||||
self.characteristics = characteristics
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, f):
|
||||
data = f.read(COFF_HEADER_SIZE)
|
||||
if len(data) < COFF_HEADER_SIZE:
|
||||
raise ValueError("File too small to contain a valid COFF header.")
|
||||
|
||||
(
|
||||
machine,
|
||||
number_of_sections,
|
||||
time_date_stamp,
|
||||
pointer_to_symbol_table,
|
||||
number_of_symbols,
|
||||
size_of_opt_header,
|
||||
characteristics
|
||||
) = struct.unpack(COFF_HEADER_FORMAT, data)
|
||||
|
||||
return cls(
|
||||
machine,
|
||||
number_of_sections,
|
||||
time_date_stamp,
|
||||
pointer_to_symbol_table,
|
||||
number_of_symbols,
|
||||
size_of_opt_header,
|
||||
characteristics
|
||||
)
|
||||
|
||||
class COFFSectionHeader:
|
||||
"""
|
||||
Represents a 40-byte COFF Section Header.
|
||||
"""
|
||||
__slots__ = (
|
||||
"name", "virtual_size", "virtual_address", "size_of_raw_data",
|
||||
"pointer_to_raw_data", "pointer_to_relocations", "pointer_to_line_numbers",
|
||||
"number_of_relocations", "number_of_line_numbers", "characteristics",
|
||||
)
|
||||
|
||||
def __init__(self, name, virtual_size, virtual_address, size_of_raw_data,
|
||||
pointer_to_raw_data, pointer_to_relocations, pointer_to_line_numbers,
|
||||
number_of_relocations, number_of_line_numbers, characteristics):
|
||||
self.name = name
|
||||
self.virtual_size = virtual_size
|
||||
self.virtual_address = virtual_address
|
||||
self.size_of_raw_data = size_of_raw_data
|
||||
self.pointer_to_raw_data = pointer_to_raw_data
|
||||
self.pointer_to_relocations = pointer_to_relocations
|
||||
self.pointer_to_line_numbers = pointer_to_line_numbers
|
||||
self.number_of_relocations = number_of_relocations
|
||||
self.number_of_line_numbers = number_of_line_numbers
|
||||
self.characteristics = characteristics
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, f):
|
||||
data = f.read(SECTION_HEADER_SIZE)
|
||||
if len(data) < SECTION_HEADER_SIZE:
|
||||
raise ValueError("File ended while reading section header.")
|
||||
|
||||
(
|
||||
name_bytes,
|
||||
virtual_size,
|
||||
virtual_address,
|
||||
size_of_raw_data,
|
||||
ptr_raw_data,
|
||||
ptr_relocs,
|
||||
ptr_linenums,
|
||||
num_relocs,
|
||||
num_linenums,
|
||||
characteristics
|
||||
) = struct.unpack(SECTION_HEADER_FORMAT, data)
|
||||
|
||||
# decode section name (null-terminated within 8 bytes)
|
||||
name = name_bytes.split(b'\x00', 1)[0].decode(errors='ignore')
|
||||
|
||||
return cls(
|
||||
name,
|
||||
virtual_size,
|
||||
virtual_address,
|
||||
size_of_raw_data,
|
||||
ptr_raw_data,
|
||||
ptr_relocs,
|
||||
ptr_linenums,
|
||||
num_relocs,
|
||||
num_linenums,
|
||||
characteristics
|
||||
)
|
||||
|
||||
class COFFSymbol:
|
||||
"""
|
||||
Represents an 18-byte COFF Symbol Table entry.
|
||||
"""
|
||||
__slots__ = (
|
||||
"name_raw", "value", "section_number", "type_", "storage_class",
|
||||
"number_of_aux_symbols"
|
||||
)
|
||||
|
||||
def __init__(self, name_raw, value, section_number, type_, storage_class, number_of_aux_symbols):
|
||||
self.name_raw = name_raw
|
||||
self.value = value
|
||||
self.section_number = section_number
|
||||
self.type_ = type_
|
||||
self.storage_class = storage_class
|
||||
self.number_of_aux_symbols = number_of_aux_symbols
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, f):
|
||||
data = f.read(SYMBOL_SIZE)
|
||||
if len(data) < SYMBOL_SIZE:
|
||||
raise ValueError("Unexpected end of file while reading symbol.")
|
||||
|
||||
(name_bytes, value, section_number, type_, storage_class, num_aux) = \
|
||||
struct.unpack(SYMBOL_FORMAT, data)
|
||||
|
||||
return cls(
|
||||
name_raw=name_bytes,
|
||||
value=value,
|
||||
section_number=section_number,
|
||||
type_=type_,
|
||||
storage_class=storage_class,
|
||||
number_of_aux_symbols=num_aux
|
||||
)
|
||||
|
||||
class COFFRelocation:
|
||||
"""
|
||||
Represents a 10-byte COFF Relocation entry.
|
||||
"""
|
||||
__slots__ = ("virtual_address", "symbol_table_index", "type_")
|
||||
|
||||
def __init__(self, virtual_address, symbol_table_index, type_):
|
||||
self.virtual_address = virtual_address
|
||||
self.symbol_table_index = symbol_table_index
|
||||
self.type_ = type_
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, f):
|
||||
data = f.read(RELOCATION_SIZE)
|
||||
if len(data) < RELOCATION_SIZE:
|
||||
raise ValueError("Unexpected end of file while reading relocation.")
|
||||
|
||||
virt_addr, sym_idx, typ = struct.unpack(RELOCATION_FORMAT, data)
|
||||
return cls(virt_addr, sym_idx, typ)
|
||||
|
||||
###############################################################################
|
||||
# Main COFF Object Container
|
||||
###############################################################################
|
||||
|
||||
class COFFObject:
|
||||
"""
|
||||
Collects a parsed COFF object file:
|
||||
- file_header (COFFFileHeader)
|
||||
- section_headers (list[COFFSectionHeader])
|
||||
- symbols (list[COFFSymbol])
|
||||
- relocations (dict[int -> list[COFFRelocation]]) indexed by section index
|
||||
- raw string table
|
||||
Provides a .lint() method to do basic checks.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.file_header = None
|
||||
self.section_headers = []
|
||||
self.symbols = []
|
||||
self.relocations = {} # section_index -> list of relocations
|
||||
self.string_table = b''
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, path):
|
||||
obj = cls()
|
||||
with open(path, "rb") as f:
|
||||
# 1) Parse COFF header
|
||||
obj.file_header = COFFFileHeader.from_file(f)
|
||||
|
||||
# 2) Skip optional header if present
|
||||
sz_opt = obj.file_header.size_of_optional_header
|
||||
if sz_opt > 0:
|
||||
f.seek(sz_opt, os.SEEK_CUR)
|
||||
|
||||
# 3) Parse section headers
|
||||
for _ in range(obj.file_header.number_of_sections):
|
||||
sec = COFFSectionHeader.from_file(f)
|
||||
obj.section_headers.append(sec)
|
||||
|
||||
# 4) Parse symbols and string table
|
||||
obj._parse_symbols_and_strings(f)
|
||||
|
||||
# 5) Read relocations from each section in a second pass
|
||||
with open(path, "rb") as f:
|
||||
for i, sec in enumerate(obj.section_headers):
|
||||
rels = obj._read_relocations_for_section(f, sec)
|
||||
obj.relocations[i] = rels
|
||||
|
||||
return obj
|
||||
|
||||
def _parse_symbols_and_strings(self, f):
|
||||
"""
|
||||
Read the symbol table (18 bytes each, plus any auxiliary records),
|
||||
then read the string table if present.
|
||||
"""
|
||||
hdr = self.file_header
|
||||
if hdr.pointer_to_symbol_table == 0 or hdr.number_of_symbols == 0:
|
||||
return # no symbols
|
||||
|
||||
# Seek to the start of the symbol table
|
||||
f.seek(hdr.pointer_to_symbol_table, os.SEEK_SET)
|
||||
symbol_count = hdr.number_of_symbols
|
||||
|
||||
i = 0
|
||||
while i < symbol_count:
|
||||
sym = COFFSymbol.from_file(f)
|
||||
self.symbols.append(sym)
|
||||
|
||||
# If the symbol has auxiliary entries, skip them
|
||||
if sym.number_of_aux_symbols > 0:
|
||||
skip_count = sym.number_of_aux_symbols * SYMBOL_SIZE
|
||||
f.seek(skip_count, os.SEEK_CUR)
|
||||
i += sym.number_of_aux_symbols
|
||||
self.symbols.extend([None] * sym.number_of_aux_symbols)
|
||||
|
||||
i += 1
|
||||
|
||||
# Next 4 bytes should be the string table size
|
||||
size_data = f.read(4)
|
||||
if len(size_data) == 4:
|
||||
string_table_size = struct.unpack("<L", size_data)[0]
|
||||
if string_table_size > 4:
|
||||
remaining = string_table_size - 4
|
||||
self.string_table = f.read(remaining)
|
||||
else:
|
||||
self.string_table = b''
|
||||
|
||||
def _read_relocations_for_section(self, f, section):
|
||||
"""
|
||||
Reads relocations for a given section.
|
||||
"""
|
||||
relocs = []
|
||||
if section.pointer_to_relocations == 0 or section.number_of_relocations == 0:
|
||||
return relocs
|
||||
|
||||
f.seek(section.pointer_to_relocations, os.SEEK_SET)
|
||||
for _ in range(section.number_of_relocations):
|
||||
rel = COFFRelocation.from_file(f)
|
||||
relocs.append(rel)
|
||||
return relocs
|
||||
|
||||
def get_symbol_name(self, sym: COFFSymbol) -> str:
|
||||
"""
|
||||
Extracts the symbol name from the raw bytes.
|
||||
If the first 4 bytes are zero, the last 4 are an offset into the string table.
|
||||
Otherwise, it is an inline name (null-terminated).
|
||||
"""
|
||||
raw = sym.name_raw
|
||||
# check if first four bytes are zero
|
||||
if raw[:4] == b'\x00\x00\x00\x00':
|
||||
offset = struct.unpack("<L", raw[4:8])[0]
|
||||
# typical approach: offset - 4 from the start of self.string_table
|
||||
real_offset = offset - 4
|
||||
if real_offset < 0 or real_offset >= len(self.string_table):
|
||||
return "<invalid-offset>"
|
||||
# read until null
|
||||
sub = self.string_table[real_offset:]
|
||||
null_pos = sub.find(b'\x00')
|
||||
return sub[:null_pos].decode(errors='ignore') if null_pos != -1 else sub.decode(errors='ignore')
|
||||
else:
|
||||
# inline name
|
||||
null_pos = raw.find(b'\x00')
|
||||
return raw[:null_pos].decode(errors='ignore') if null_pos != -1 else raw.decode(errors='ignore')
|
||||
|
||||
def is_64bit(self) -> bool:
|
||||
return self.file_header.machine == MACHINE_AMD64
|
||||
|
||||
def lint(self):
|
||||
"""Perform basic lint checks and print results."""
|
||||
hdr = self.file_header
|
||||
machine = hdr.machine
|
||||
|
||||
# Print header info
|
||||
log_message(LogLevel.INFO, "=== COFF File Header ===")
|
||||
log_message(LogLevel.INFO, f" Machine: 0x{machine:04X}")
|
||||
log_message(LogLevel.INFO, f" NumberOfSections: {hdr.number_of_sections}")
|
||||
log_message(LogLevel.INFO, f" TimeDateStamp: 0x{hdr.time_date_stamp:08X}")
|
||||
log_message(LogLevel.INFO, f" PointerToSymbolTable:0x{hdr.pointer_to_symbol_table:08X}")
|
||||
log_message(LogLevel.INFO, f" NumberOfSymbols: {hdr.number_of_symbols}")
|
||||
log_message(LogLevel.INFO, f" SizeOfOptionalHeader:{hdr.size_of_optional_header}")
|
||||
log_message(LogLevel.INFO, f" Characteristics: 0x{hdr.characteristics:04X}")
|
||||
|
||||
if machine == MACHINE_X86:
|
||||
log_message(LogLevel.INFO, " -> x86 (32-bit) COFF object.n")
|
||||
elif machine == MACHINE_AMD64:
|
||||
log_message(LogLevel.INFO, " -> AMD64 (x64) COFF object.\n")
|
||||
else:
|
||||
log_message(LogLevel.ERROR, " -> Unknown or non-standard machine type.\n")
|
||||
sys.exit(1)
|
||||
|
||||
# Check sections
|
||||
log_message(LogLevel.INFO, "=== Sections ===")
|
||||
|
||||
for i, sec in enumerate(self.section_headers):
|
||||
log_message(LogLevel.INFO, f" Section[{i}]: {sec.name}")
|
||||
log_message(LogLevel.INFO, f" PointerToRelocations: 0x{sec.pointer_to_relocations:08X}")
|
||||
log_message(LogLevel.INFO, f" NumberOfRelocations: {sec.number_of_relocations}")
|
||||
log_message(LogLevel.INFO, f" Characteristics: 0x{sec.characteristics:08X}")
|
||||
log_message(LogLevel.INFO, f" VirtualSize: 0x{sec.virtual_size:08X}")
|
||||
log_message(LogLevel.INFO, f" SizeOfRawData: 0x{sec.size_of_raw_data:08X}")
|
||||
|
||||
if sec.name == ".bss" and (sec.size_of_raw_data > 0 or sec.virtual_size > 0) and loader_type not in (LoaderType.CS, LoaderType.OC2, LoaderType.CI):
|
||||
log_message(LogLevel.WARN, f"Section '{sec.name}' is present! Not all loaders support uninitialized data in a BOF.")
|
||||
elif sec.name == ".rdata" and sec.size_of_raw_data > 0 and loader_type not in (LoaderType.CS, LoaderType.OC2, LoaderType.CI):
|
||||
log_message(LogLevel.WARN, f"Section '{sec.name}' is present! Not all loaders support read-only/const data in a BOF.")
|
||||
|
||||
#elif sec.name == ".pdata" and sec.size_of_raw_data > 0 and loader_type not in (LoaderType.CS, LoaderType.OC2):
|
||||
# log_message(LogLevel.WARN, f"Section '{sec.name}' is present! This may indicate that your BOF is using exception handling, which is not supported.")
|
||||
|
||||
log_message(LogLevel.INFO, "")
|
||||
|
||||
# Symbols
|
||||
log_message(LogLevel.INFO, "=== Symbols ===")
|
||||
|
||||
allowed_entry_names = {
|
||||
LoaderType.CS: { "go", "sleep_mask", "_go", "_sleep_mask" },
|
||||
LoaderType.OC2: { "go", "_go" },
|
||||
LoaderType.CI: {"go", "_go"},
|
||||
}
|
||||
|
||||
allowed_entry_names[LoaderType.ANY] = allowed_entry_names[LoaderType.CS] | allowed_entry_names[LoaderType.OC2] | allowed_entry_names[LoaderType.CI]
|
||||
|
||||
found_entry = False
|
||||
|
||||
for i, sym in enumerate(self.symbols):
|
||||
if sym is None:
|
||||
continue # skip aux symbols
|
||||
sym_name = self.get_symbol_name(sym)
|
||||
|
||||
log_message(LogLevel.INFO, f" [{i}] Name='{sym_name}' Value=0x{sym.value:08X} "
|
||||
f"Section={sym.section_number} StorageClass={sym.storage_class}")
|
||||
|
||||
if sym_name in allowed_entry_names[loader_type] and sym.section_number > 0:
|
||||
found_entry = True
|
||||
|
||||
if "@" in sym_name:
|
||||
sym_name = sym_name.split("@", 1)[0]
|
||||
|
||||
if sym_name.startswith("__imp_"):
|
||||
sym_without_prefix = sym_name[6:] # remove __imp_
|
||||
if not self.is_64bit():
|
||||
if sym_without_prefix[0] == "_":
|
||||
sym_without_prefix = sym_without_prefix[1:]
|
||||
|
||||
if "$" in sym_without_prefix: # DFR
|
||||
# split in exact two parts, not more
|
||||
import_lib, import_func = sym_without_prefix.split("$", 1)
|
||||
if "@" in import_func:
|
||||
import_func = import_func.split("@", 1)[0]
|
||||
import_func = import_func.strip("$")
|
||||
|
||||
log_message(LogLevel.INFO, f"Imported symbol '{sym_without_prefix}' from library '{import_lib}' and function '{import_func}'")
|
||||
else:
|
||||
if loader_type == LoaderType.CS:
|
||||
if sym_without_prefix not in list_of_implant_functions_cs:
|
||||
log_message(LogLevel.ERROR, f"Imported symbol '{sym_without_prefix}' is not a recognized implant function.", LogDestination.CS)
|
||||
elif loader_type == LoaderType.OC2:
|
||||
if sym_without_prefix not in list_of_implant_functions_oc2:
|
||||
log_message(LogLevel.ERROR, f"Imported symbol '{sym_without_prefix}' is not a recognized implant function.", LogDestination.OC2)
|
||||
elif loader_type == LoaderType.CI:
|
||||
if sym_without_prefix not in list_of_implant_functions_ci:
|
||||
log_message(LogLevel.ERROR, f"Imported symbol '{sym_without_prefix}' is not a recognized implant function.", LogDestination.CI)
|
||||
else:
|
||||
if sym_without_prefix not in (list_of_implant_functions_cs + list_of_implant_functions_oc2 + list_of_implant_functions_ci):
|
||||
log_message(LogLevel.ERROR, f"Imported symbol '{sym_without_prefix}' is not a recognized implant function.")
|
||||
else:
|
||||
if sym.section_number == 0:
|
||||
unhandled = True
|
||||
|
||||
if unhandled and sym_name in ["___chkstk_ms", "__chkstk"]:
|
||||
if loader_type != LoaderType.OC2:
|
||||
log_message(LogLevel.ERROR, f"Symbol '{sym_name}' is a stack check function. You may have a stack variable that is too large.")
|
||||
unhandled = False
|
||||
|
||||
if unhandled and sym_name in ["__stack_chk_fail", "__security_init_cookie"] or "security_check_cookie" in sym_name:
|
||||
log_message(LogLevel.ERROR, f"Symbol '{sym_name}' is a stack check function. Disable stack protections.")
|
||||
unhandled = False
|
||||
|
||||
if unhandled and sym_name in ["__C_specific_handler", "__cxa_begin_catch"] or "except_handler" in sym_name or "CxxFrameHandler" in sym_name:
|
||||
log_message(LogLevel.WARN, f"Symbol '{sym_name}' is an exception handling function. This is not supported.")
|
||||
unhandled = False
|
||||
|
||||
if unhandled and sym_name in ["memset", "memmove", "memcpy"]:
|
||||
if loader_type == LoaderType.OC2:
|
||||
# OC2 shims these functions in the loader
|
||||
unhandled = False
|
||||
|
||||
if unhandled:
|
||||
log_message(LogLevel.ERROR, f"Symbol '{sym_name}' is an undefined symbol.")
|
||||
|
||||
log_message(LogLevel.INFO, "")
|
||||
|
||||
if not found_entry:
|
||||
entry_names = ', '.join(f"'{name}'" for name in allowed_entry_names[loader_type])
|
||||
log_message(LogLevel.ERROR, f"No {entry_names} entry point found in object file (required for BOF)")
|
||||
|
||||
# Relocations
|
||||
log_message(LogLevel.INFO, "=== Relocations ===")
|
||||
for i, sec in enumerate(self.section_headers):
|
||||
rels = self.relocations.get(i, [])
|
||||
if rels:
|
||||
log_message(LogLevel.INFO, f" Section[{i}] '{sec.name}' has {len(rels)} relocation(s):")
|
||||
for r in rels:
|
||||
# Symbol reference name
|
||||
sym_name = "<invalid-symbol>"
|
||||
if r.symbol_table_index < len(self.symbols):
|
||||
sym_name = self.get_symbol_name(self.symbols[r.symbol_table_index])
|
||||
|
||||
try:
|
||||
r_type = ImageRelocationType(r.type_)
|
||||
r_type_str = r_type.name
|
||||
except ValueError:
|
||||
r_type_str = f"0x{r.type_:04X}"
|
||||
|
||||
# Map relocation type to a name
|
||||
if self.is_64bit():
|
||||
# Check for allowed relocation types
|
||||
allowed_reloc_types = {
|
||||
ImageRelocationType.IMAGE_REL_AMD64_ADDR64,
|
||||
ImageRelocationType.IMAGE_REL_AMD64_ADDR32NB,
|
||||
ImageRelocationType.IMAGE_REL_AMD64_REL32,
|
||||
ImageRelocationType.IMAGE_REL_AMD64_REL32_1,
|
||||
ImageRelocationType.IMAGE_REL_AMD64_REL32_2,
|
||||
ImageRelocationType.IMAGE_REL_AMD64_REL32_3,
|
||||
ImageRelocationType.IMAGE_REL_AMD64_REL32_4,
|
||||
ImageRelocationType.IMAGE_REL_AMD64_REL32_5
|
||||
}
|
||||
if r_type not in allowed_reloc_types:
|
||||
log_message(LogLevel.WARN, f"Warning: Unexpected relocation type {r_type_str} at VA=0x{r.virtual_address:08X}")
|
||||
else:
|
||||
# Check for allowed relocation types
|
||||
allowed_reloc_types = {
|
||||
ImageRelocationType.IMAGE_REL_I386_DIR32,
|
||||
ImageRelocationType.IMAGE_REL_I386_REL32
|
||||
}
|
||||
if r_type not in allowed_reloc_types:
|
||||
log_message(LogLevel.WARN, f"Warning: Unexpected relocation type {r_type_str} at VA=0x{r.virtual_address:08X}")
|
||||
|
||||
|
||||
log_message(LogLevel.INFO, f" VA=0x{r.virtual_address:08X}, "
|
||||
f"SymIdx={r.symbol_table_index} ({sym_name}), "
|
||||
f"Type={r_type_str}")
|
||||
log_message(LogLevel.INFO, "")
|
||||
|
||||
###############################################################################
|
||||
# Command-line Entry Point
|
||||
###############################################################################
|
||||
|
||||
class LogLevel(Enum):
|
||||
INFO = auto()
|
||||
WARN = auto()
|
||||
ERROR = auto()
|
||||
|
||||
class LogDestination(IntFlag):
|
||||
NONE = 0
|
||||
CS = auto() # Cobalt Strike BOFs
|
||||
OC2 = auto() # OC2 BOFs
|
||||
CI = auto() # Core Impact BOFs
|
||||
ALL = CS | OC2 | CI # All BOF types
|
||||
|
||||
class LogFormat(Enum):
|
||||
DEFAULT = "default" # Default format
|
||||
VS = "vs" # Visual Studio
|
||||
|
||||
class LoaderType(Enum):
|
||||
ANY = "any" # Any loader
|
||||
CS = "cs" # Cobalt Strike
|
||||
OC2 = "oc2" # OC2
|
||||
CI = "ci" # Core Impact
|
||||
|
||||
loader_type = LoaderType.ANY
|
||||
verbose_logging = False
|
||||
has_errors = False
|
||||
enable_colors = True
|
||||
log_format = LogFormat.DEFAULT
|
||||
input_file = ""
|
||||
|
||||
def log_message(level: LogLevel, message: str, dest: LogDestination = LogDestination.ALL):
|
||||
if not verbose_logging and level == LogLevel.INFO:
|
||||
return
|
||||
|
||||
# Skip messages not meant for current loader
|
||||
if dest != LogDestination.ALL:
|
||||
if loader_type == LoaderType.CS and not (dest & LogDestination.CS):
|
||||
return
|
||||
if loader_type == LoaderType.OC2 and not (dest & LogDestination.OC2):
|
||||
return
|
||||
if loader_type == LoaderType.CI and not (dest & LogDestination.CI):
|
||||
return
|
||||
|
||||
if level == LogLevel.ERROR:
|
||||
global has_errors
|
||||
has_errors = True
|
||||
|
||||
format = {
|
||||
LogFormat.DEFAULT: log_format_default,
|
||||
LogFormat.VS: log_format_vs,
|
||||
}
|
||||
|
||||
global log_format
|
||||
print(format[log_format](level, message, dest))
|
||||
|
||||
def log_format_default(level: LogLevel, message: str, dest: LogDestination):
|
||||
# ANSI color codes
|
||||
COLORS = {
|
||||
LogLevel.INFO: "", # default
|
||||
LogLevel.WARN: "\033[93m", # yellow
|
||||
LogLevel.ERROR: "\033[91m", # red
|
||||
}
|
||||
|
||||
global enable_colors
|
||||
RESET = "\033[0m" if enable_colors else ""
|
||||
COLOR = COLORS[level] if enable_colors else ""
|
||||
|
||||
prefix = {
|
||||
LogLevel.INFO: "INFO",
|
||||
LogLevel.WARN: "WARN",
|
||||
LogLevel.ERROR: "ERROR"
|
||||
}
|
||||
|
||||
destinations = format_destinations(dest)
|
||||
if dest == LogDestination.ALL:
|
||||
return f"{COLOR}[{prefix[level]}] {message}{RESET}"
|
||||
else:
|
||||
return f"{COLOR}[{prefix[level]}] {message}{RESET} [{destinations}]"
|
||||
|
||||
def log_format_vs(level: LogLevel, message: str, dest: LogDestination):
|
||||
global input_file
|
||||
bof_name = Path(input_file).name
|
||||
|
||||
prefix = {
|
||||
LogLevel.INFO: "",
|
||||
LogLevel.WARN: "warning",
|
||||
LogLevel.ERROR: "error"
|
||||
}
|
||||
|
||||
destinations = f" [{format_destinations(dest)}]" if dest != LogDestination.ALL else ""
|
||||
return f"{bof_name}: {prefix[level]} BOFLINT: {message}{destinations}"
|
||||
|
||||
def format_destinations(dest: LogDestination):
|
||||
# append the log destinations to a string, formatted like this [CS|OC2], properly checking it as an intflag, also properly expanding ALL to CS|OC2
|
||||
return "|".join([d.name for d in LogDestination if d & dest and d != LogDestination.ALL])
|
||||
|
||||
def is_valid_bof(filepath: str) -> bool:
|
||||
"""Quick check if file is a valid BOF by checking machine type."""
|
||||
try:
|
||||
with open(filepath, 'rb') as f:
|
||||
machine_data = f.read(2)
|
||||
if len(machine_data) < 2:
|
||||
return False
|
||||
|
||||
machine = struct.unpack("<H", machine_data)[0]
|
||||
return machine in (MACHINE_AMD64, MACHINE_X86)
|
||||
except:
|
||||
return False
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='BOF Linter - Validates COFF object files')
|
||||
parser.add_argument('file', help='Path to COFF object file')
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='Show INFO messages')
|
||||
parser.add_argument('--loader', choices=['cs', 'oc2', 'ci', 'any'], default='any',
|
||||
help='BOF loader type (cs=Cobalt Strike, oc2=OC2, ci=Core Impact, any=Any)')
|
||||
parser.add_argument('--nocolor', action='store_true', help='Disable color output')
|
||||
parser.add_argument('--logformat', choices=['default', 'vs'], default='default',
|
||||
help='Output format (default=Normal output, vs=Visual Studio diagnostic)')
|
||||
|
||||
args = parser.parse_args()
|
||||
global verbose_logging, loader_type, enable_colors, log_format, input_file
|
||||
verbose_logging = args.verbose
|
||||
loader_type = LoaderType(args.loader)
|
||||
enable_colors = not args.nocolor
|
||||
log_format = LogFormat(args.logformat)
|
||||
input_file = args.file
|
||||
|
||||
if not os.path.isfile(args.file):
|
||||
log_message(LogLevel.ERROR, "File does not exist")
|
||||
sys.exit(1)
|
||||
|
||||
if not is_valid_bof(args.file):
|
||||
log_message(LogLevel.ERROR, "Not a valid BOF COFF file (or unsupported machine type)")
|
||||
sys.exit(1)
|
||||
|
||||
coff = COFFObject.from_file(args.file)
|
||||
coff.lint()
|
||||
|
||||
if has_errors:
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,262 @@
|
||||
# Beacon Object File Visual Studio Template
|
||||
|
||||
[This repository](https://github.com/Cobalt-Strike/bof-vs) contains the Beacon Object File Visual Studio (BOF-VS) template project.
|
||||
You can read more about rationale and design decisions from this blog [post](https://www.cobaltstrike.com/blog/simplifying-bof-development).
|
||||
|
||||
## Quick Start Guide
|
||||
|
||||
To get started, use the instructions provided below.
|
||||
|
||||
### Video Walkthrough
|
||||
|
||||
<center><video width="720" heigth="480" crossorigin="anonymous" aria-label="BOF-VS setup" x-webkit-airplay="allow" playsinline="" controls controlslist="nodownload"><source src="https://raw.githubusercontent.com/Cobalt-Strike/bof-vs/main/media/Setup%20BOF-VS.mp4" type="video/mp4"></video></center>
|
||||
|
||||
https://github.com/user-attachments/assets/256fec31-bf25-4c10-9a10-0ab50751ca6d
|
||||
|
||||
### Prerequisites:
|
||||
|
||||
* An x64 Windows 10/11 development machine (without a security solution)
|
||||
* Visual Studio Community/Pro/Enterprise 2022 (Desktop Development with C++ installed)
|
||||
* Python 3 for the BOF linter (optional)
|
||||
|
||||
### Template Installation
|
||||
|
||||
Download the latest [release](https://github.com/Cobalt-Strike/bof-vs/releases/latest/download/bof-vs.zip),
|
||||
and copy the `bof-vs.zip` archive under the
|
||||
`%USERPROFILE%\Documents\Visual Studio 2022\Templates\ProjectTemplates` folder.
|
||||
The template is accessible through Visual Studio's new project dialog,
|
||||
where you can locate it by searching with the keyword `BOF`. Be certain
|
||||
to have `All languages` chosen as the language filter.
|
||||
|
||||
If Visual Studio does not recognize the template, then reset the project template cache by
|
||||
deleting the following file: `%localappdata%\Microsoft\VisualStudio\<VS vesrion>\ProjectTemplatesCache_{<GUID>}\cache.bin`
|
||||
|
||||
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).
|
||||
|
||||
### Debug Build
|
||||
|
||||
The `Debug` target builds your BOF to an executable, which allows
|
||||
you to benefit from the convenience of debugging your BOF code directly within
|
||||
Visual Studio's built-in debugger. This will enable you to work at the source
|
||||
code level without running the BOF through a Beacon.
|
||||
|
||||
### Release Build
|
||||
|
||||
The Release target compiles a release object file of your BOF,
|
||||
which is designed to be used directly with Cobalt Strike.
|
||||
|
||||
## Dynamic Function Resolution
|
||||
|
||||
The project template includes two macro definitions to facilitate Dynamic
|
||||
Function Resolution (DFR) declarations. These macros provide a robust mechanism
|
||||
for efficiently resolving Win32 API functions in BOFs and simplify the
|
||||
development process significantly.
|
||||
|
||||
### DFR Macro
|
||||
|
||||
The `DFR` macro can automatically extract the function type and generate
|
||||
the required function declaration.
|
||||
|
||||
```cpp
|
||||
|
||||
DFR(KERNEL32, OpenProcess)
|
||||
```
|
||||
|
||||
The above `DFR` macro statement expands to the following declaration.
|
||||
|
||||
```cpp
|
||||
|
||||
DECLSPEC_IMPORT decltype(OpenProcess) KERNEL32$OpenProcess;
|
||||
```
|
||||
|
||||
A common practice is to map the `KERNEL32$OpenProcess` function to OpenProcess
|
||||
using the following macro definition. This mapping enables you to call the
|
||||
OpenProcess function directly, eliminating the need for the `KERNEL32$` prefix.
|
||||
|
||||
```cpp
|
||||
#define OpenProcess KERNEL32$OpenProcess
|
||||
```
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```cpp
|
||||
DFR(KERNEL32, OpenProcess)
|
||||
#define OpenProcess KERNEL32$OpenProcess
|
||||
|
||||
void func1() {
|
||||
OpenProcess(...);
|
||||
}
|
||||
|
||||
void func2() {
|
||||
OpenProcess(...);
|
||||
}
|
||||
```
|
||||
|
||||
### DFR_LOCAL Macro
|
||||
|
||||
The `DFR_LOCAL(module, function)` macro allows you to define a local function
|
||||
pointer variable that directly references the `module$function` function. One
|
||||
of the main advantages of using this macro compared to the `DFR` macro is
|
||||
the elimination of the need for the additional `OpenProcess` -> `KERNEL32$OpenProcess`
|
||||
mapping. This streamlines the code and makes it more concise. However, it's
|
||||
important to note that the function pointer created with the `DFR_LOCAL` macro
|
||||
has a limited scope and can only be accessed within the function where it is defined.
|
||||
Consequently, if you plan to use the required WINAPI functions in multiple
|
||||
functions throughout your BOF, you will need to define the function pointer
|
||||
using the DFR_LOCAL macro in each of those functions.
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```cpp
|
||||
void func1() {
|
||||
DFR_LOCAL(KERNEL32, OpenProcess);
|
||||
OpenProcess(...);
|
||||
}
|
||||
|
||||
void func2() {
|
||||
DFR_LOCAL(KERNEL32, OpenProcess);
|
||||
OpenProcess(...);
|
||||
}
|
||||
```
|
||||
|
||||
## Mocked APIs
|
||||
|
||||
The template includes a mocked version of the Beacon API for Debug builds,
|
||||
enabling BOF debugging without a running Beacon instance. When you select
|
||||
either the Debug or UnitTest configuration, the mocked API is automatically
|
||||
included into the project.
|
||||
|
||||
### Argument Packer
|
||||
|
||||
The BofData class implements an argument packer to replicate the argument
|
||||
packing behavior of the bof_pack aggressor function. This enables us to
|
||||
call BOF's entry point with custom arguments without Beacon.
|
||||
|
||||
```cpp
|
||||
bof::mock::BofData data;
|
||||
// the pack function takes one or more arguments
|
||||
data.pack<int, short, int, const char*>(6502, 80, 68010, "Hello World");
|
||||
|
||||
// alternatively, the << operator can be used to construct the arguments buffer
|
||||
data << 0xdeadface << L"Hello World";
|
||||
|
||||
// raw buffers can be added too
|
||||
const char buf[] = { 0x41, 0x42, 0x43, 0x44 };
|
||||
data.addData(buf, sizeof(buf));
|
||||
|
||||
go(data.get(), data.size());
|
||||
```
|
||||
|
||||
### Beacon API
|
||||
|
||||
The template also provides a mocked implementation of the Data Parser, Output,
|
||||
and Format APIs. The mocked functions within the Output API print the output
|
||||
to the standard output, ensuring the results are visible. Moreover, all returned
|
||||
output is stored for future examination and analysis.
|
||||
|
||||
Furthermore, the Internal API functions (such as BeaconUseToken,
|
||||
BeaconInjectProcess, etc.) are declared. However, it is important to note that
|
||||
these functions lack the real implementation and only display an error message
|
||||
on the standard error if called.
|
||||
|
||||
## Unit Tests
|
||||
|
||||
The project template offers an additional build target called `UnitTest`,
|
||||
specifically designed to build BOFs with the GoogleTest framework. Furthermore,
|
||||
the mock library provides a convenient `runMocked` function that handles
|
||||
the argument packing, execution of the BOF's entry point, and capturing all
|
||||
generated outputs.
|
||||
|
||||
Install the GoogleTest framework:
|
||||
|
||||
1. Right-click the project name in Solution Explorer.
|
||||
2. Select `Manage NuGet Packages`.
|
||||
3. Ensure that the `Microsoft.googletest.v140.windesktop.msvcstl.static.rt-static` package is installed.
|
||||
|
||||
### Example Usage
|
||||
```cpp
|
||||
extern "C" {
|
||||
#include "beacon.h"
|
||||
|
||||
void go(char* args, int len) {
|
||||
datap parser;
|
||||
BeaconDataParse(&parser, args, len);
|
||||
int number = BeaconDataInt(&parser);
|
||||
BeaconPrintf(CALLBACK_OUTPUT, "Hello: %i", number);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ExampleBofTest, TestCase1) {
|
||||
std::vector<bof::output::OutputEntry> actual =
|
||||
bof::runMocked<int>(go, 6502);
|
||||
|
||||
std::vector<bof::output::OutputEntry> expected = {
|
||||
{CALLBACK_OUTPUT, "Hello: 6502"}
|
||||
};
|
||||
|
||||
ASSERT_EQ(expected, actual);
|
||||
}
|
||||
```
|
||||
|
||||
## Sleepmask
|
||||
In addition to supporting standard Beacon Object Files, the template also includes
|
||||
functionality for developing Sleepmask BOFs. Beacon's Sleepmask can be used to apply
|
||||
runtime masking to its PE sections and Heap allocations. Therefore, this template
|
||||
creates a "mock Beacon" as part of the call to runMockedSleepMask() to replicate
|
||||
the layout of Beacon in memory during debugging. This function also makes it possible
|
||||
to apply malleable C2 settings to the "mock Beacon".
|
||||
|
||||
|
||||
```c
|
||||
// Mock up Beacon and run the sleep mask once
|
||||
bof::runMockedSleepMask(sleep_mask);
|
||||
|
||||
// Mock up Beacon with the specific .stage C2 profile
|
||||
bof::runMockedSleepMask(sleep_mask,
|
||||
{
|
||||
.allocator = bof::profile::Allocator::VirtualAlloc,
|
||||
.obfuscate = bof::profile::Obfuscate::False,
|
||||
.useRWX = bof::profile::UseRWX::True,
|
||||
.module = "",
|
||||
},
|
||||
{
|
||||
.sleepTimeMs = 5000,
|
||||
.runForever = false,
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Multiple BOFs
|
||||
|
||||
The template supports multiple BOFs within separate files, enabling each .cpp
|
||||
file to be compiled into an individual BOF. This approach eliminates the need
|
||||
for multiple projects and allows grouping similar BOFs under one Visual Studio
|
||||
project. When it comes to debugging, since each BOF is compiled into its own
|
||||
debug executable, it is important to adjust the debug command accordingly.
|
||||
This can be done through the project properties: `Configuration Properties
|
||||
-> Debugging -> Command`.
|
||||
|
||||
## BOF Linter
|
||||
|
||||
To help identify potential BOF issues, the template integrates the `boflint`
|
||||
tool, which runs during the release build to analyze the BOF for common errors
|
||||
and shows them in the Error List of Visual Studio. This linter detects several
|
||||
key issues, including:
|
||||
|
||||
- **Relocation types**: Ensures only supported relocation types are used.
|
||||
- **Entry point validation**: Verifies the presence of the mandatory `go` or `sleep_mask` function.
|
||||
- **Import resolution**: Flags undefined or unresolvable imports.
|
||||
- **Large stack variables**: Detects large stack variable usage that results in an unresolvable import for stack probing.
|
||||
- **Exception handling**: Warns against the use of exception handling, which is unsupported in BOFs.
|
||||
|
||||
`boflint` is only executed if Python 3 is detected on the system. If Python 3
|
||||
is not installed, you can get it from:
|
||||
|
||||
- [Microsoft Store](https://apps.microsoft.com/detail/9ncvdn91xzqp?hl=en-us&gl=US)
|
||||
- [Official Python release](https://www.python.org/downloads/windows/)
|
||||
|
||||
You can also run the linter manually:
|
||||
|
||||
```
|
||||
python ./utils/boflint.py --loader cs x64/Release/bof.x64.o
|
||||
```
|
||||
Reference in New Issue
Block a user