init commit

master
_xeroxz 3 years ago
parent 83c05915ef
commit 07f7fdeab5

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 xerox
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,3 +1,7 @@
# nasa-injector
inserts a PML4E where a prior PML4E is set to not present, pointing at the PDPT that contains the dll.
- 0 bytes allocated in the kernel.
- 0 bytes allocated in the process.
- 0 VAD entries created inside of the game.
<img src="https://githacks.org/nasa-tech/nasa-injector/raw/c5694cf86f627b55196e9badb1fbb1357e9c89b5/unknown.png"/>

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "injector", "injector\injector.vcxproj", "{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}"
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
{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}.Debug|x64.ActiveCfg = Debug|x64
{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}.Debug|x64.Build.0 = Debug|x64
{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}.Debug|x86.ActiveCfg = Debug|Win32
{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}.Debug|x86.Build.0 = Debug|Win32
{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}.Release|x64.ActiveCfg = Release|x64
{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}.Release|x64.Build.0 = Release|x64
{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}.Release|x86.ActiveCfg = Release|Win32
{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {46DAEC7A-391F-4347-8C20-E11B52609A1B}
EndGlobalSection
EndGlobal

@ -0,0 +1,91 @@
#pragma once
#include <windows.h>
#include <cstdint>
#include <cstddef>
extern "C" NTSTATUS __protect_virtual_memory(
HANDLE p_handle,
void** base_addr,
std::size_t* bytes_to_protect,
std::uint32_t new_protect,
std::uint32_t* old_protect
);
extern "C" NTSTATUS __write_virtual_memory(
HANDLE p_handle,
void* base_addr,
void* buffer,
std::size_t size,
std::size_t* bytes_written
);
extern "C" NTSTATUS __read_virtual_memory(
HANDLE p_handle,
void* base_addr,
void* buffer,
std::size_t size,
std::size_t* bytes_written
);
extern "C" NTSTATUS __alloc_virtual_memory(
HANDLE p_handle,
void** base_addr,
std::uint32_t zero_bits,
std::size_t* size,
std::uint32_t alloc_type,
std::uint32_t protect
);
namespace direct
{
__forceinline bool protect_virtual_memory(
HANDLE p_handle,
void* base_addr,
std::size_t size,
std::uint32_t protect,
std::uint32_t* old_protect
)
{
return ERROR_SUCCESS == ::__protect_virtual_memory(p_handle, &base_addr, &size, protect, old_protect);
}
__forceinline bool write_virtual_memory(
HANDLE p_handle,
void* base_addr,
void* buffer,
std::size_t size
)
{
std::size_t bytes_written;
return ERROR_SUCCESS == __write_virtual_memory(p_handle, base_addr, buffer, size, &bytes_written);
}
__forceinline bool read_virtual_memory(
HANDLE p_handle,
void* addr,
void* buffer,
std::size_t size
)
{
std::size_t bytes_written;
return ERROR_SUCCESS == ::__read_virtual_memory(p_handle, addr, buffer, size, &bytes_written);
}
__forceinline void* alloc_virtual_memory(
HANDLE p_handle,
std::size_t size,
std::uint32_t protect
)
{
void* base_addr = NULL;
::__alloc_virtual_memory(
p_handle,
&base_addr,
NULL,
&size,
MEM_COMMIT | MEM_RESERVE,
protect
);
return base_addr;
}
}

Binary file not shown.

@ -0,0 +1,93 @@
#include <iostream>
#include "kernel_ctx/kernel_ctx.h"
#include "mem_ctx/mem_ctx.hpp"
#include "injector_ctx/injector_ctx.hpp"
#include "util/util.hpp"
int __cdecl main(int argc, char** argv)
{
if (argc < 2)
{
MessageBoxA(NULL, "ERROR", "please provide dll", NULL);
return -1;
}
std::vector<std::uint8_t> raw_image;
util::open_binary_file(argv[1], raw_image);
nasa::load_drv();
nasa::kernel_ctx kernel_ctx;
nasa::mem_ctx syscall_fix(kernel_ctx, GetCurrentProcessId());
kernel_ctx.fix_syscall(syscall_fix);
//
// shoot the tires off piddb cache entry.
//
{
const auto drv_timestamp = util::get_file_header((void*)raw_driver)->TimeDateStamp;
printf("[+] clearing piddb cache for driver: %s, with timestamp 0x%x\n", nasa::drv_key.c_str(), drv_timestamp);
if (!kernel_ctx.clear_piddb_cache(nasa::drv_key, drv_timestamp))
{
// this is because the signature might be broken on these versions of windows.
perror("[-] failed to clear PiDDBCacheTable.\n");
return -1;
}
printf("[+] cleared piddb cache...\n");
}
nasa::unload_drv();
//
// load the dll but dont actually call dll main
//
auto temp_module_base = LoadLibraryEx(argv[1], NULL, DONT_RESOLVE_DLL_REFERENCES);
std::cout << "[+] temp_module_base: " << temp_module_base << std::endl;
if (!temp_module_base)
return -1;
//
// get dll export RVA's
//
auto present_rva = (uint64_t)GetProcAddress(temp_module_base, "?event_handler@@YAKPEAXPEAW4event_type@@IPEAPEAPEAUIDXGISwapChain@@@Z") - (uint64_t)temp_module_base;
std::cout << "[+] present_rva: " << present_rva << std::endl;
if (!present_rva)
return -1;
auto pid = util::get_pid("RainbowSix.exe");
auto base_addr = util::get_module_base(pid, "RainbowSix.exe");
std::cout << "[+] pid: " << pid << " base address: " << base_addr << std::endl;
if (!pid || !base_addr)
return -1;
//
// create memory contexts for the game and this process
//
nasa::mem_ctx explorer(kernel_ctx, util::get_pid("explorer.exe"));
nasa::mem_ctx r6(kernel_ctx, pid);
//
// inject into the game
//
nasa::injector_ctx inject(r6, explorer);
const auto [inject_addr, inject_from_addr] = inject.inject(raw_image);
std::cout << "[+] injected into address: " << inject_addr << std::endl;
std::cout << "[+] (module base inside of injector) inject_from_addr: " << inject_from_addr << std::endl;
std::cin.get();
//
// install hooks on the game
//
auto result = inject.hook(
(void*)(((uint64_t)inject_addr) + present_rva)
);
std::cout << "[+] hook result: " << result << std::endl;
std::cin.get();
explorer.~mem_ctx();
r6.~mem_ctx();
syscall_fix.~mem_ctx();
}

@ -0,0 +1,187 @@
<?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>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{CAF2DBF5-95EA-49ED-95C8-43F7550E6B70}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>injector</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>MultiByte</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</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|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<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|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<Optimization>Disabled</Optimization>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>false</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<Optimization>Disabled</Optimization>
<BufferSecurityCheck>false</BufferSecurityCheck>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>direct.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="injector.cpp" />
<ClCompile Include="injector_ctx\injector_ctx.cpp" />
<ClCompile Include="kernel_ctx\kernel_ctx.cpp" />
<ClCompile Include="mem_ctx\mem_ctx.cpp" />
<ClCompile Include="pe_image\pe_image.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="direct.h" />
<ClInclude Include="injector_ctx\injector_ctx.hpp" />
<ClInclude Include="kernel_ctx\kernel_ctx.h" />
<ClInclude Include="loadup.hpp" />
<ClInclude Include="mem_ctx\mem_ctx.hpp" />
<ClInclude Include="pe_image\pe_image.h" />
<ClInclude Include="physmeme\physmeme.hpp" />
<ClInclude Include="raw_driver.hpp" />
<ClInclude Include="util\hook.hpp" />
<ClInclude Include="util\nt.hpp" />
<ClInclude Include="util\util.hpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Header Files\physmeme">
<UniqueIdentifier>{1ac5eac2-540f-4299-b247-6e474f0663c5}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\kernel_ctx">
<UniqueIdentifier>{c8a6138a-6610-4ee7-b784-31950a4b2510}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\util">
<UniqueIdentifier>{8f28f4f1-a8df-4c81-b593-10894d062d77}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\pe_image">
<UniqueIdentifier>{59ffe31b-8d86-44de-b8b8-d9e625fcf14d}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\mem_ctx">
<UniqueIdentifier>{adc0b7b7-1d99-4ac9-a713-e60536555241}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\mem_ctx">
<UniqueIdentifier>{afd76729-9a8c-4513-aa69-4128b87324b7}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\kernel_ctx">
<UniqueIdentifier>{76959bd1-9765-439c-8c3e-92a8147e8345}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\injector_ctx">
<UniqueIdentifier>{18c7fd24-0e1f-4dd5-96c8-c1173a7f1981}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\injector_ctx">
<UniqueIdentifier>{ec029218-ad17-41b5-995a-7422a1c9a7d5}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="injector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="kernel_ctx\kernel_ctx.cpp">
<Filter>Source Files\kernel_ctx</Filter>
</ClCompile>
<ClCompile Include="pe_image\pe_image.cpp">
<Filter>Source Files\pe_image</Filter>
</ClCompile>
<ClCompile Include="mem_ctx\mem_ctx.cpp">
<Filter>Source Files\mem_ctx</Filter>
</ClCompile>
<ClCompile Include="injector_ctx\injector_ctx.cpp">
<Filter>Source Files\injector_ctx</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="physmeme\physmeme.hpp">
<Filter>Header Files\physmeme</Filter>
</ClInclude>
<ClInclude Include="util\hook.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="util\nt.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="util\util.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="pe_image\pe_image.h">
<Filter>Source Files\pe_image</Filter>
</ClInclude>
<ClInclude Include="mem_ctx\mem_ctx.hpp">
<Filter>Header Files\mem_ctx</Filter>
</ClInclude>
<ClInclude Include="kernel_ctx\kernel_ctx.h">
<Filter>Header Files\kernel_ctx</Filter>
</ClInclude>
<ClInclude Include="injector_ctx\injector_ctx.hpp">
<Filter>Header Files\injector_ctx</Filter>
</ClInclude>
<ClInclude Include="loadup.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="raw_driver.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="direct.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

@ -0,0 +1,124 @@
#include "injector_ctx.hpp"
#include "../direct.h"
#include <iostream>
namespace nasa
{
injector_ctx::injector_ctx(
mem_ctx& inject_into,
mem_ctx& inject_from
)
:
inject_into(inject_into),
inject_from(inject_from)
{}
std::pair<void*, void*> injector_ctx::inject(
std::vector<std::uint8_t>& raw_image,
const unsigned pml4_index
)
{
nasa::pe_image image(raw_image);
const auto p_handle = OpenProcess(
PROCESS_ALL_ACCESS,
FALSE,
inject_from.get_pid()
);
//
// allocate rwx memory for dll inside of explorer.exe
//
const auto module_base = direct::alloc_virtual_memory(
p_handle,
image.size() + 0x1000 * 512, //image size + 2mb (creates a new pde/pt)
PAGE_EXECUTE_READWRITE
);
image.fix_imports(NULL, NULL); // no imports
image.map();
image.relocate(reinterpret_cast<std::uintptr_t>(module_base));
//
// write dll into explorer.exe
//
auto result = direct::write_virtual_memory(
p_handle,
module_base,
image.data(),
image.size()
);
if (!result)
return {};
const auto [ppml4e, pml4e] = inject_from.get_pml4e(module_base);
if (!ppml4e || !pml4e.value)
return {};
const auto inject_into_dirbase = reinterpret_cast<::ppml4e>(inject_into.get_dirbase());
if (!inject_into_dirbase)
return {};
//
// write in pml4e containing dll at desired index
//
inject_into.write_phys<::pml4e>(
inject_into_dirbase + pml4_index,
pml4e
);
CloseHandle(p_handle);
virt_addr_t new_addr{ module_base };
new_addr.pml4_index = pml4_index;
return { new_addr.value, module_base };
}
bool injector_ctx::hook(void* present_hook)
{
if (!present_hook)
return false;
//
// get dxgi.dll base address
//
const auto dxgi = (std::uintptr_t)util::get_module_base(
inject_into.get_pid(),
"dxgi.dll"
);
std::cout << "[+] dxgi.dll base address: " << std::hex << dxgi << std::endl;
std::cin.get();
//
// IAT hook EtwEventWrite to our function
//
inject_into.write_virtual<void*>(
reinterpret_cast<void*>(dxgi + 0x9F000),
present_hook
);
std::cout << "[+] iat hooked: " << std::hex << dxgi + 0x9F000 << " to: " << present_hook << std::endl;
std::cin.get();
//
// enable EtwEventWrite to be called
//
{
inject_into.write_virtual<std::uint32_t>(
reinterpret_cast<void*>(dxgi + 0xCB024),
1
);
inject_into.write_virtual<std::uint64_t>(
reinterpret_cast<void*>(dxgi + 0xCB010),
0xFFFFFFFFFFFFFFFF
);
inject_into.write_virtual<std::uint64_t>(
reinterpret_cast<void*>(dxgi + 0xCB018),
0x4000000000000000
);
}
return true;
}
}

@ -0,0 +1,21 @@
#include <map>
#include "../mem_ctx/mem_ctx.hpp"
#include "../pe_image/pe_image.h"
namespace nasa
{
class injector_ctx
{
public:
injector_ctx(
nasa::mem_ctx& inject_into,
nasa::mem_ctx& inject_from
);
std::pair<void*, void*> inject(std::vector<std::uint8_t>& raw_image, const unsigned pml4_index = 179);
bool hook(void* present_hook);
private:
nasa::mem_ctx inject_into;
nasa::mem_ctx inject_from;
};
}

@ -0,0 +1,306 @@
#include "kernel_ctx.h"
#include "../mem_ctx/mem_ctx.hpp"
namespace nasa
{
kernel_ctx::kernel_ctx()
{
if (psyscall_func.load() || nt_page_offset || ntoskrnl_buffer)
return;
nt_rva = reinterpret_cast<std::uint32_t>(
util::get_kernel_export(
"ntoskrnl.exe",
syscall_hook.first.data(),
true
));
nt_page_offset = nt_rva % PAGE_SIZE;
ntoskrnl_buffer = reinterpret_cast<std::uint8_t*>(LoadLibraryA(ntoskrnl_path));
DBG_PRINT("[+] page offset of %s is 0x%llx\n", syscall_hook.first.data(), nt_page_offset);
DBG_PRINT("[+] ntoskrnl_buffer: 0x%p\n", ntoskrnl_buffer);
DBG_PRINT("[!] ntoskrnl_buffer was 0x%p, nt_rva was 0x%p\n", ntoskrnl_buffer, nt_rva);
std::vector<std::thread> search_threads;
//--- for each physical memory range, make a thread to search it
for (auto ranges : util::pmem_ranges)
search_threads.emplace_back(std::thread(
&kernel_ctx::map_syscall,
this,
ranges.first,
ranges.second
));
for (std::thread& search_thread : search_threads)
search_thread.join();
DBG_PRINT("[+] psyscall_func: 0x%p\n", psyscall_func.load());
}
void kernel_ctx::map_syscall(std::uintptr_t begin, std::uintptr_t end) const
{
//if the physical memory range is less then or equal to 2mb
if (begin + end <= 0x1000 * 512)
{
auto page_va = nasa::map_phys(begin + nt_page_offset, end);
if (page_va)
{
// scan every page of the physical memory range
for (auto page = page_va; page < page_va + end; page += 0x1000)
if (!psyscall_func.load()) // keep scanning until its found
if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
{
psyscall_func.store((void*)page);
return;
}
nasa::unmap_phys(page_va, end);
}
}
else // else the range is bigger then 2mb
{
auto remainder = (begin + end) % (0x1000 * 512);
// loop over 2m chunks
for (auto range = begin; range < begin + end; range += 0x1000 * 512)
{
auto page_va = nasa::map_phys(range + nt_page_offset, 0x1000 * 512);
if (page_va)
{
// loop every page of 2mbs (512)
for (auto page = page_va; page < page_va + 0x1000 * 512; page += 0x1000)
{
if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
{
psyscall_func.store((void*)page);
return;
}
}
nasa::unmap_phys(page_va, 0x1000 * 512);
}
}
// map the remainder and check each page of it
auto page_va = nasa::map_phys(begin + end - remainder + nt_page_offset, remainder);
if (page_va)
{
for (auto page = page_va; page < page_va + remainder; page += 0x1000)
{
if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
{
psyscall_func.store((void*)page);
return;
}
}
nasa::unmap_phys(page_va, remainder);
}
}
}
PEPROCESS kernel_ctx::get_peprocess(DWORD pid)
{
if (!pid)
return NULL;
PEPROCESS proc;
static auto get_peprocess_from_pid =
util::get_kernel_export(
"ntoskrnl.exe",
"PsLookupProcessByProcessId"
);
syscall<PsLookupProcessByProcessId>(
get_peprocess_from_pid,
(HANDLE)pid,
&proc
);
return proc;
}
void kernel_ctx::rkm(void* buffer, void* address, std::size_t size)
{
if (!buffer || !address || !size)
return;
size_t amount_copied;
static auto mm_copy_memory =
util::get_kernel_export(
"ntoskrnl.exe",
"memcpy"
);
if (mm_copy_memory)
syscall<decltype(&memcpy)>(
mm_copy_memory,
buffer,
address,
size
);
}
void kernel_ctx::wkm(void* buffer, void* address, std::size_t size)
{
if (!buffer || !address || !size)
return;
size_t amount_copied;
static auto mm_copy_memory =
util::get_kernel_export(
"ntoskrnl.exe",
"memcpy"
);
if (mm_copy_memory)
syscall<decltype(&memcpy)>(
mm_copy_memory,
address,
buffer,
size
);
}
void* kernel_ctx::get_physical(void* virt_addr)
{
if (!virt_addr)
return NULL;
static auto mm_get_physical =
util::get_kernel_export(
"ntoskrnl.exe",
"MmGetPhysicalAddress"
);
return syscall<MmGetPhysicalAddress>(
mm_get_physical,
virt_addr
);
}
void* kernel_ctx::get_virtual(void* addr)
{
if (!addr)
return NULL;
static auto mm_get_virtual =
util::get_kernel_export(
"ntoskrnl.exe",
"MmGetVirtualForPhysical"
);
PHYSICAL_ADDRESS phys_addr;
memcpy(&phys_addr, &addr, sizeof(addr));
return syscall<MmGetVirtualForPhysical>(
mm_get_virtual,
phys_addr
);
}
bool kernel_ctx::clear_piddb_cache(const std::string& file_name, const std::uint32_t timestamp)
{
static const auto piddb_lock =
util::memory::get_piddb_lock();
static const auto piddb_table =
util::memory::get_piddb_table();
if (!piddb_lock || !piddb_table)
return false;
static const auto ex_acquire_resource =
util::get_kernel_export(
"ntoskrnl.exe",
"ExAcquireResourceExclusiveLite"
);
static const auto lookup_element_table =
util::get_kernel_export(
"ntoskrnl.exe",
"RtlLookupElementGenericTableAvl"
);
static const auto release_resource =
util::get_kernel_export(
"ntoskrnl.exe",
"ExReleaseResourceLite"
);
static const auto delete_table_entry =
util::get_kernel_export(
"ntoskrnl.exe",
"RtlDeleteElementGenericTableAvl"
);
if (!ex_acquire_resource || !lookup_element_table || !release_resource)
return false;
PiDDBCacheEntry cache_entry;
const auto drv_name = std::wstring(file_name.begin(), file_name.end());
cache_entry.time_stamp = timestamp;
RtlInitUnicodeString(&cache_entry.driver_name, drv_name.data());
//
// ExAcquireResourceExclusiveLite
//
if (!syscall<ExAcquireResourceExclusiveLite>(ex_acquire_resource, piddb_lock, true))
return false;
//
// RtlLookupElementGenericTableAvl
//
PIDCacheobj* found_entry_ptr =
syscall<RtlLookupElementGenericTableAvl>(
lookup_element_table,
piddb_table,
reinterpret_cast<void*>(&cache_entry)
);
if (found_entry_ptr)
{
//
// unlink entry.
//
PIDCacheobj found_entry = rkm<PIDCacheobj>(found_entry_ptr);
LIST_ENTRY NextEntry = rkm<LIST_ENTRY>(found_entry.list.Flink);
LIST_ENTRY PrevEntry = rkm<LIST_ENTRY>(found_entry.list.Blink);
PrevEntry.Flink = found_entry.list.Flink;
NextEntry.Blink = found_entry.list.Blink;
wkm<LIST_ENTRY>(found_entry.list.Blink, PrevEntry);
wkm<LIST_ENTRY>(found_entry.list.Flink, NextEntry);
//
// delete entry.
//
syscall<RtlDeleteElementGenericTableAvl>(delete_table_entry, piddb_table, found_entry_ptr);
//
// ensure the entry is 0
//
auto result = syscall<RtlLookupElementGenericTableAvl>(
lookup_element_table,
piddb_table,
reinterpret_cast<void*>(&cache_entry)
);
syscall<ExReleaseResourceLite>(release_resource, piddb_lock);
return !result;
}
syscall<ExReleaseResourceLite>(release_resource, piddb_lock);
return false;
}
void kernel_ctx::fix_syscall(nasa::mem_ctx& ctx)
{
pt_entries syscall_entries; // not used.
nasa::psyscall_func =
ctx.set_page(
ctx.virt_to_phys(
syscall_entries,
nasa::psyscall_func
));
nasa::unmap_all();
}
}

@ -0,0 +1,120 @@
#pragma once
#include "../util/util.hpp"
#include "../physmeme/physmeme.hpp"
#include "../util/hook.hpp"
namespace nasa
{
//
// offset of function into a physical page
// used for comparing bytes when searching
//
inline std::uint16_t nt_page_offset{};
//
// rva of nt function we are going to hook
//
inline std::uint32_t nt_rva{};
//
// base address of ntoskrnl (inside of this process)
//
inline const std::uint8_t* ntoskrnl_buffer{};
//
// mapping of a syscalls physical memory (for installing hooks)
//
inline std::atomic<void*> psyscall_func{};
//
// you can edit this how you choose, im hooking NtShutdownSystem.
//
inline const std::pair<std::string_view, std::string_view> syscall_hook = { "NtShutdownSystem", "ntdll.dll" };
class kernel_ctx
{
friend class mem_ctx;
public:
kernel_ctx();
//
// read kernel memory into buffer
//
void rkm(void* buffer, void* address, std::size_t size);
//
// write kernel memory from buffer
//
void wkm(void* buffer, void* address, std::size_t size);
template <class T>
T rkm(void* addr)
{
if (!addr)
return {};
T buffer;
rkm((void*)&buffer, addr, sizeof(T));
return buffer;
}
template <class T>
void wkm(void* addr, const T& data)
{
if (!addr)
return;
wkm((void*)&data, addr, sizeof(T));
}
//
// gets physical address from virtual
//
void* get_physical(void* virt_addr);
//
// uses the pfn database to get the virtual address
//
void* get_virtual(void* virt_addr);
//
// use this to call any function in the kernel
//
template <class T, class ... Ts>
std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args)
{
static const auto proc =
GetProcAddress(
GetModuleHandleA(syscall_hook.second.data()),
syscall_hook.first.data()
);
if (!proc || !psyscall_func || !addr)
return {};
hook::make_hook(psyscall_func, addr);
auto result = reinterpret_cast<T>(proc)(args ...);
hook::remove(psyscall_func);
return result;
}
bool clear_piddb_cache(
const std::string& drv_name,
const std::uint32_t epoch_time
);
//
// fix syscall to use new page.
//
static void fix_syscall(nasa::mem_ctx& ctx);
private:
//
// find and map the physical page of a syscall into this process
//
void map_syscall(std::uintptr_t begin, std::uintptr_t end) const;
//
// get a pointer to an eprocess given process id.
//
PEPROCESS get_peprocess(DWORD pid);
};
}

@ -0,0 +1,278 @@
/*
MIT License
Copyright (c) 2020 xerox
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <Windows.h>
#include <Winternl.h>
#include <string>
#include <fstream>
#include <filesystem>
#pragma comment(lib, "ntdll.lib")
using nt_load_driver_t = NTSTATUS(__fastcall*)(PUNICODE_STRING);
using nt_unload_driver_t = NTSTATUS(__fastcall*)(PUNICODE_STRING);
namespace driver
{
namespace util
{
inline bool delete_service_entry(const std::string& service_name)
{
HKEY reg_handle;
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
auto result = RegOpenKeyA(
HKEY_LOCAL_MACHINE,
reg_key.c_str(),
&reg_handle
);
return ERROR_SUCCESS == RegDeleteKeyA(reg_handle, service_name.data()) && ERROR_SUCCESS == RegCloseKey(reg_handle);;
}
inline bool create_service_entry(const std::string& drv_path, const std::string& service_name)
{
HKEY reg_handle;
std::string reg_key("System\\CurrentControlSet\\Services\\");
reg_key += service_name;
auto result = RegCreateKeyA(
HKEY_LOCAL_MACHINE,
reg_key.c_str(),
&reg_handle
);
if (result != ERROR_SUCCESS)
return false;
//
// set type to 1 (kernel)
//
constexpr std::uint8_t type_value = 1;
result = RegSetValueExA(
reg_handle,
"Type",
NULL,
REG_DWORD,
&type_value,
4u
);
if (result != ERROR_SUCCESS)
return false;
//
// set error control to 3
//
constexpr std::uint8_t error_control_value = 3;
result = RegSetValueExA(
reg_handle,
"ErrorControl",
NULL,
REG_DWORD,
&error_control_value,
4u
);
if (result != ERROR_SUCCESS)
return false;
//
// set start to 3
//
constexpr std::uint8_t start_value = 3;
result = RegSetValueExA(
reg_handle,
"Start",
NULL,
REG_DWORD,
&start_value,
4u
);
if (result != ERROR_SUCCESS)
return false;
//
// set image path to the driver on disk
//
result = RegSetValueExA(
reg_handle,
"ImagePath",
NULL,
REG_SZ,
(std::uint8_t*) drv_path.c_str(),
drv_path.size()
);
if (result != ERROR_SUCCESS)
return false;
return ERROR_SUCCESS == RegCloseKey(reg_handle);
}
// this function was coded by paracord: https://githacks.org/snippets/4#L94
inline bool enable_privilege(const std::wstring& privilege_name)
{
HANDLE token_handle = nullptr;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token_handle))
return false;
LUID luid{};
if (!LookupPrivilegeValueW(nullptr, privilege_name.data(), &luid))
return false;
TOKEN_PRIVILEGES token_state{};
token_state.PrivilegeCount = 1;
token_state.Privileges[0].Luid = luid;
token_state.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(token_handle, FALSE, &token_state, sizeof(TOKEN_PRIVILEGES), nullptr, nullptr))
return false;
CloseHandle(token_handle);
return true;
}
inline std::string get_service_image_path(const std::string& service_name)
{
HKEY reg_handle;
DWORD bytes_read;
char image_path[0xFF];
static const std::string reg_key("System\\CurrentControlSet\\Services\\");
auto result = RegOpenKeyA(
HKEY_LOCAL_MACHINE,
reg_key.c_str(),
&reg_handle
);
result = RegGetValueA(
reg_handle,
service_name.c_str(),
"ImagePath",
REG_SZ,
NULL,
image_path,
&bytes_read
);
RegCloseKey(reg_handle);
return std::string(image_path);
}
}
inline bool load(const std::string& drv_path, const std::string& service_name)
{
if (!util::enable_privilege(L"SeLoadDriverPrivilege"))
return false;
if (!util::create_service_entry("\\??\\" + std::filesystem::absolute(std::filesystem::path(drv_path)).string(), service_name))
return false;
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
reg_path += service_name;
static const auto lp_nt_load_drv =
::GetProcAddress(
GetModuleHandleA("ntdll.dll"),
"NtLoadDriver"
);
if (lp_nt_load_drv)
{
ANSI_STRING driver_rep_path_cstr;
UNICODE_STRING driver_reg_path_unicode;
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true);
return ERROR_SUCCESS == reinterpret_cast<nt_load_driver_t>(lp_nt_load_drv)(&driver_reg_path_unicode);
}
return false;
}
inline std::tuple<bool, std::string> load(const std::vector<std::uint8_t>& drv_buffer)
{
static const auto random_file_name = [](std::size_t length) -> std::string
{
static const auto randchar = []() -> char
{
const char charset[] =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const std::size_t max_index = (sizeof(charset) - 1);
return charset[rand() % max_index];
};
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return str;
};
const auto service_name = random_file_name(16);
const auto file_path = std::filesystem::temp_directory_path().string() + service_name;
std::ofstream output_file(file_path.c_str(), std::ios::binary);
output_file.write((char*)drv_buffer.data(), drv_buffer.size());
output_file.close();
return { load(file_path, service_name), service_name };
}
inline std::tuple<bool, std::string> load(const std::uint8_t* buffer, const std::size_t size)
{
std::vector<std::uint8_t> image(buffer, buffer + size);
return load(image);
}
inline bool unload(const std::string& service_name)
{
std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
reg_path += service_name;
static const auto lp_nt_unload_drv =
::GetProcAddress(
GetModuleHandleA("ntdll.dll"),
"NtUnloadDriver"
);
if (lp_nt_unload_drv)
{
ANSI_STRING driver_rep_path_cstr;
UNICODE_STRING driver_reg_path_unicode;
RtlInitAnsiString(&driver_rep_path_cstr, reg_path.c_str());
RtlAnsiStringToUnicodeString(&driver_reg_path_unicode, &driver_rep_path_cstr, true);
const bool unload_drv = !reinterpret_cast<nt_unload_driver_t>(lp_nt_unload_drv)(&driver_reg_path_unicode);
const auto image_path = util::get_service_image_path(service_name);
const bool delete_drv = std::filesystem::remove(image_path);
const bool delete_reg = util::delete_service_entry(service_name);
return unload_drv && delete_drv && delete_reg;
}
return false;
}
}

@ -0,0 +1,461 @@
#include "mem_ctx.hpp"
#include <cassert>
namespace nasa
{
mem_ctx::mem_ctx(kernel_ctx& krnl_ctx, DWORD pid)
:
k_ctx(&krnl_ctx),
dirbase(get_dirbase(krnl_ctx, pid)),
pid(pid)
{
genesis_page.first = VirtualAlloc(
NULL,
PAGE_SIZE,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
//
// page in the page, do not remove this makes the entries.
//
*(std::uintptr_t*)genesis_page.first = 0xC0FFEE;
//
// flush tlb since we just accessed the page
//
page_accessed();
//
// get the ppte and pte of the page we allocated
//
auto [page_ppte, page_pte] = get_pte(genesis_page.first, true);
genesis_page.second = page_pte;
//
// allocate a page that will get the mapping of the first pages PT
//
genesis_cursor.first = reinterpret_cast<::ppte>(
VirtualAlloc(
NULL,
0x1000,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
));
//
// page it in
//
*(std::uintptr_t*)genesis_cursor.first = 0xC0FFEE;
//
// flush tlb since we just accessed the page
//
page_accessed();
//
// get ppte and pte of the cursor page.
//
auto [cursor_ppte, cursor_pte] = get_pte(genesis_cursor.first, true);
genesis_cursor.second = cursor_pte;
//
// change the page to the PT of the first page we allocated.
//
cursor_pte.pfn = reinterpret_cast<std::uint64_t>(page_ppte) >> 12;
set_pte(genesis_cursor.first, cursor_pte, true);
//
// change the offset of genesis cursor page to genesis pages pt_index since the page is now a PT
// WARNING: pointer arithmetic, do not add pt_index * 8
//
genesis_cursor.first += +virt_addr_t{ genesis_page.first }.pt_index;
//
// since the page has been accessed we need to reset this bit in the PTE.
//
page_accessed();
}
mem_ctx::~mem_ctx()
{
set_pte(genesis_page.first, genesis_page.second, true);
set_pte(genesis_cursor.first, genesis_cursor.second, true);
}
void* mem_ctx::set_page(void* addr)
{
page_offset = virt_addr_t{ addr }.offset;
genesis_cursor.first->pfn = reinterpret_cast<uint64_t>(addr) >> 12;
page_accessed();
return get_page();
}
void* mem_ctx::get_page() const
{
return reinterpret_cast<void*>((reinterpret_cast<std::uint64_t>(genesis_page.first)) + page_offset);
}
void mem_ctx::page_accessed() const
{
Sleep(5);
}
void* mem_ctx::get_dirbase(kernel_ctx& k_ctx, DWORD pid)
{
if (!pid)
return NULL;
const auto peproc =
reinterpret_cast<std::uint64_t>(k_ctx.get_peprocess(pid));
if (!peproc)
return NULL;
pte dirbase = k_ctx.rkm<pte>(
reinterpret_cast<void*>(peproc + 0x28)
);
return reinterpret_cast<void*>(dirbase.pfn << 12);
}
bool mem_ctx::hyperspace_entries(pt_entries& entries, void* addr)
{
if (!addr || !dirbase)
return false;
virt_addr_t virt_addr{ addr };
entries.pml4.first = reinterpret_cast<ppml4e>(dirbase) + virt_addr.pml4_index;
entries.pml4.second = k_ctx->rkm<pml4e>(
k_ctx->get_virtual(entries.pml4.first));
if (!entries.pml4.second.value)
return false;
entries.pdpt.first = reinterpret_cast<ppdpte>(entries.pml4.second.pfn << 12) + virt_addr.pdpt_index;
entries.pdpt.second = k_ctx->rkm<pdpte>(
k_ctx->get_virtual(entries.pdpt.first));
if (!entries.pdpt.second.value)
return false;
entries.pd.first = reinterpret_cast<ppde>(entries.pdpt.second.pfn << 12) + virt_addr.pd_index;
entries.pd.second = k_ctx->rkm<pde>(
k_ctx->get_virtual(entries.pd.first));
// if its a 2mb page
if (entries.pd.second.page_size)
{
memcpy(
&entries.pt.second,
&entries.pd.second,
sizeof(pte)
);
entries.pt.first = reinterpret_cast<ppte>(entries.pd.second.value);
return true;
}
entries.pt.first = reinterpret_cast<ppte>(entries.pd.second.pfn << 12) + virt_addr.pt_index;
entries.pt.second = k_ctx->rkm<pte>(
k_ctx->get_virtual(entries.pt.first));
if (!entries.pt.second.value)
return false;
return true;
}
std::pair<ppte, pte> mem_ctx::get_pte(void* addr, bool use_hyperspace)
{
if (!dirbase || !addr)
return {};
pt_entries entries;
if (use_hyperspace ? hyperspace_entries(entries, addr) : virt_to_phys(entries, addr))
{
::pte pte;
memcpy(&pte, &entries.pt.second, sizeof(pte));
return { entries.pt.first, pte };
}
return {};
}
void mem_ctx::set_pte(void* addr, const ::pte& pte, bool use_hyperspace)
{
if (!dirbase || !addr)
return;
pt_entries entries;
if (use_hyperspace)
if (hyperspace_entries(entries, addr))
k_ctx->wkm(
k_ctx->get_virtual(entries.pt.first),
pte
);
else
if (virt_to_phys(entries, addr))
write_phys(entries.pt.first, pte);
}
std::pair<ppde, pde> mem_ctx::get_pde(void* addr, bool use_hyperspace)
{
if (!dirbase || !addr)
return {};
pt_entries entries;
if (use_hyperspace ? hyperspace_entries(entries, addr) : virt_to_phys(entries, addr))
{
::pde pde;
memcpy(
&pde,
&entries.pd.second,
sizeof(pde)
);
return { entries.pd.first, pde };
}
return {};
}
void mem_ctx::set_pde(void* addr, const ::pde& pde, bool use_hyperspace)
{
if (!dirbase || !addr)
return;
pt_entries entries;
if (use_hyperspace)
if (hyperspace_entries(entries, addr))
k_ctx->wkm(
k_ctx->get_virtual(entries.pd.first),
pde
);
else
if (virt_to_phys(entries, addr))
write_phys(entries.pd.first, pde);
}
std::pair<ppdpte, pdpte> mem_ctx::get_pdpte(void* addr, bool use_hyperspace)
{
if (!dirbase || !addr)
return {};
pt_entries entries;
if (use_hyperspace ? hyperspace_entries(entries, addr) : virt_to_phys(entries, addr))
{
::pdpte pdpte;
memcpy(
&pdpte,
&entries.pdpt.second,
sizeof(pdpte)
);
return { entries.pdpt.first, pdpte };
}
return {};
}
void mem_ctx::set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace)
{
if (!dirbase || !addr)
return;
pt_entries entries;
if (use_hyperspace)
if (hyperspace_entries(entries, addr))
k_ctx->wkm(
k_ctx->get_virtual(entries.pdpt.first),
pdpte
);
else
if (virt_to_phys(entries, addr))
write_phys(entries.pdpt.first, pdpte);
}
std::pair<ppml4e, pml4e> mem_ctx::get_pml4e(void* addr, bool use_hyperspace)
{
if (!dirbase || !addr)
return {};
pt_entries entries;
if (use_hyperspace ? hyperspace_entries(entries, addr) : virt_to_phys(entries, addr))
{
::pml4e pml4e;
memcpy(
&pml4e,
&entries.pml4.second,
sizeof(pml4e)
);
return { entries.pml4.first, pml4e };
}
return {};
}
void mem_ctx::set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace)
{
if (!dirbase || !addr)
return;
pt_entries entries;
if (use_hyperspace)
if (hyperspace_entries(entries, addr))
k_ctx->wkm(
k_ctx->get_virtual(entries.pml4.first),
pml4e
);
else
if (virt_to_phys(entries, addr))
write_phys(entries.pml4.first, pml4e);
}
std::pair<void*, void*> mem_ctx::read_virtual(void* buffer, void* addr, std::size_t size)
{
if (!buffer || !addr || !size || !dirbase)
return {};
virt_addr_t virt_addr{ addr };
if (size <= PAGE_SIZE - virt_addr.offset)
{
pt_entries entries;
read_phys(
buffer,
virt_to_phys(entries, addr),
size
);
return {
reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(buffer) + size),
reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(addr) + size)
};
}
else
{
// cut remainder
const auto [new_buffer_addr, new_addr] = read_virtual(
buffer,
addr,
PAGE_SIZE - virt_addr.offset
);
// forward work load
return read_virtual(
new_buffer_addr,
new_addr,
size - (PAGE_SIZE - virt_addr.offset)
);
}
}
std::pair<void*, void*> mem_ctx::write_virtual(void* buffer, void* addr, std::size_t size)
{
if (!buffer || !addr || !size || !dirbase)
return {};
virt_addr_t virt_addr{ addr };
if (size <= PAGE_SIZE - virt_addr.offset)
{
pt_entries entries;
write_phys(
buffer,
virt_to_phys(entries, addr),
size
);
return {
reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(buffer) + size),
reinterpret_cast<void*>(reinterpret_cast<std::uintptr_t>(addr) + size)
};
}
else
{
// cut remainder
const auto [new_buffer_addr, new_addr] = write_virtual(
buffer,
addr,
PAGE_SIZE - virt_addr.offset
);
// forward work load
return write_virtual(
new_buffer_addr,
new_addr,
size - (PAGE_SIZE - virt_addr.offset)
);
}
}
void mem_ctx::read_phys(void* buffer, void* addr, std::size_t size)
{
if (!buffer || !addr || !size)
return;
auto temp_page = set_page(addr);
if (temp_page)
memcpy(buffer, temp_page, size);
}
void mem_ctx::write_phys(void* buffer, void* addr, std::size_t size)
{
if (!buffer || !addr || !size)
return;
auto temp_page = set_page(addr);
if (temp_page)
memcpy(temp_page, buffer, size);
}
void* mem_ctx::virt_to_phys(pt_entries& entries, void* addr)
{
if (!addr || !dirbase)
return {};
virt_addr_t virt_addr{ addr };
//
// traverse paging tables
//
auto pml4e = read_phys<::pml4e>(
reinterpret_cast<ppml4e>(dirbase) + virt_addr.pml4_index);
entries.pml4.first = reinterpret_cast<ppml4e>(dirbase) + virt_addr.pml4_index;
entries.pml4.second = pml4e;
if (!pml4e.value)
return NULL;
auto pdpte = read_phys<::pdpte>(
reinterpret_cast<ppdpte>(pml4e.pfn << 12) + virt_addr.pdpt_index);
entries.pdpt.first = reinterpret_cast<ppdpte>(pml4e.pfn << 12) + virt_addr.pdpt_index;
entries.pdpt.second = pdpte;
if (!pdpte.value)
return NULL;
auto pde = read_phys<::pde>(
reinterpret_cast<ppde>(pdpte.pfn << 12) + virt_addr.pd_index);
entries.pd.first = reinterpret_cast<ppde>(pdpte.pfn << 12) + virt_addr.pd_index;
entries.pd.second = pde;
if (!pde.value)
return NULL;
auto pte = read_phys<::pte>(
reinterpret_cast<ppte>(pde.pfn << 12) + virt_addr.pt_index);
entries.pt.first = reinterpret_cast<ppte>(pde.pfn << 12) + virt_addr.pt_index;
entries.pt.second = pte;
if (!pte.value)
return NULL;
return reinterpret_cast<void*>((pte.pfn << 12) + virt_addr.offset);
}
unsigned mem_ctx::get_pid() const
{
return pid;
}
void* mem_ctx::get_dirbase() const
{
return dirbase;
}
}

@ -0,0 +1,120 @@
#pragma once
#include "../util/nt.hpp"
#include "../kernel_ctx/kernel_ctx.h"
#define FLUSH_TLB Sleep(1)
struct pt_entries
{
std::pair<ppml4e, pml4e> pml4;
std::pair<ppdpte, pdpte> pdpt;
std::pair<ppde, pde> pd;
std::pair<ppte, pte> pt;
};
namespace nasa
{
class mem_ctx
{
public:
explicit mem_ctx(kernel_ctx& k_ctx, DWORD pid);
~mem_ctx();
//
// PTE manipulation
//
std::pair<ppte, pte> get_pte(void* addr, bool use_hyperspace = false);
void set_pte(void* addr, const ::pte& pte, bool use_hyperspace = false);
//
// PDE manipulation
//
std::pair<ppde, pde> get_pde(void* addr, bool use_hyperspace = false);
void set_pde(void* addr, const ::pde& pde, bool use_hyperspace = false);
//
// PDPTE manipulation
//
std::pair<ppdpte, pdpte> get_pdpte(void* addr, bool use_hyperspace = false);
void set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace = false);
//
// PML4E manipulation
//
std::pair<ppml4e, pml4e> get_pml4e(void* addr, bool use_hyperspace = false);
void set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace = false);
//
// gets dirbase (not the PTE or PFN but actual physical address)
//
void* get_dirbase() const;
static void* get_dirbase(kernel_ctx& k_ctx, DWORD pid);
void read_phys(void* buffer, void* addr, std::size_t size);
void write_phys(void* buffer, void* addr, std::size_t size);
template <class T>
T read_phys(void* addr)
{
if (!addr)
return {};
T buffer;
read_phys((void*)&buffer, addr, sizeof(T));
return buffer;
}
template <class T>
void write_phys(void* addr, const T& data)
{
if (!addr)
return;
write_phys((void*)&data, addr, sizeof(T));
}
std::pair<void*, void*> read_virtual(void* buffer, void* addr, std::size_t size);
std::pair<void*, void*> write_virtual(void* buffer, void* addr, std::size_t size);
template <class T>
T read_virtual(void* addr)
{
if (!addr)
return {};
T buffer;
read_virtual((void*)&buffer, addr, sizeof(T));
return buffer;
}
template <class T>
void write_virtual(void* addr, const T& data)
{
write_virtual((void*)&data, addr, sizeof(T));
}
//
// linear address translation (not done by hyperspace mappings)
//
void* virt_to_phys(pt_entries& entries, void* addr);
//
// these are used for the pfn backdoor, this will be removed soon
//
void* set_page(void* addr);
void* get_page() const;
void page_accessed() const;
unsigned get_pid() const;
kernel_ctx* k_ctx;
private:
//
// given an address fill pt entries with physical addresses and entry values.
//
bool hyperspace_entries(pt_entries& entries, void* addr);
std::pair<void*, pte> genesis_page;
std::pair<ppte, pte> genesis_cursor;
void* dirbase;
unsigned pid;
unsigned short page_offset;
};
}

@ -0,0 +1,224 @@
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
!!!!!!!!!!!!!!!!!!!!!!!!!!! This code was created by not-wlan (wlan). all credit for this header and source file goes to him !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
#include "pe_image.h"
#include <cassert>
#include <fstream>
namespace nasa
{
pe_image::pe_image(std::vector<uint8_t>& image) : m_image(image)
{
m_dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(m_image.data());
m_nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS64>((uintptr_t)m_dos_header + m_dos_header->e_lfanew);
m_section_header = reinterpret_cast<IMAGE_SECTION_HEADER*>((uintptr_t)(&m_nt_headers->OptionalHeader) + m_nt_headers->FileHeader.SizeOfOptionalHeader);
}
size_t pe_image::size() const
{
return m_nt_headers->OptionalHeader.SizeOfImage;
}
uintptr_t pe_image::entry_point() const
{
return m_nt_headers->OptionalHeader.AddressOfEntryPoint;
}
void pe_image::map()
{
m_image_mapped.clear();
m_image_mapped.resize(m_nt_headers->OptionalHeader.SizeOfImage);
std::copy_n(m_image.begin(), m_nt_headers->OptionalHeader.SizeOfHeaders, m_image_mapped.begin());
for (size_t i = 0; i < m_nt_headers->FileHeader.NumberOfSections; ++i)
{
const auto& section = m_section_header[i];
const auto target = (uintptr_t)m_image_mapped.data() + section.VirtualAddress;
const auto source = (uintptr_t)m_dos_header + section.PointerToRawData;
std::copy_n(m_image.begin() + section.PointerToRawData, section.SizeOfRawData, m_image_mapped.begin() + section.VirtualAddress);
printf("copying [%s] 0x%p -> 0x%p [0x%04X]\n", &section.Name[0], (void*)source, (void*)target, section.SizeOfRawData);
}
}
bool pe_image::process_relocation(uintptr_t image_base_delta, uint16_t data, uint8_t* relocation_base)
{
#define IMR_RELOFFSET(x) (x & 0xFFF)
switch (data >> 12 & 0xF)
{
case IMAGE_REL_BASED_HIGH:
{
const auto raw_address = reinterpret_cast<int16_t*>(relocation_base + IMR_RELOFFSET(data));
*raw_address += static_cast<unsigned long>(HIWORD(image_base_delta));
break;
}
case IMAGE_REL_BASED_LOW:
{
const auto raw_address = reinterpret_cast<int16_t*>(relocation_base + IMR_RELOFFSET(data));
*raw_address += static_cast<unsigned long>(LOWORD(image_base_delta));
break;
}
case IMAGE_REL_BASED_HIGHLOW:
{
const auto raw_address = reinterpret_cast<size_t*>(relocation_base + IMR_RELOFFSET(data));
*raw_address += static_cast<size_t>(image_base_delta);
break;
}
case IMAGE_REL_BASED_DIR64:
{
auto UNALIGNED raw_address = reinterpret_cast<DWORD_PTR UNALIGNED*>(relocation_base + IMR_RELOFFSET(data));
*raw_address += image_base_delta;
break;
}
case IMAGE_REL_BASED_ABSOLUTE: // No action required
case IMAGE_REL_BASED_HIGHADJ: // no action required
{
break;
}
default:
{
throw std::runtime_error("gay relocation!"); // :flushed: ??
return false;
}
}
#undef IMR_RELOFFSET
return true;
}
void pe_image::relocate(uintptr_t base) const
{
if (m_nt_headers->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
return;
ULONG total_count_bytes;
const auto nt_headers = ImageNtHeader((void*)m_image_mapped.data());
auto relocation_directory = (PIMAGE_BASE_RELOCATION)::ImageDirectoryEntryToData(nt_headers, TRUE, IMAGE_DIRECTORY_ENTRY_BASERELOC, &total_count_bytes);
auto image_base_delta = static_cast<uintptr_t>(static_cast<uintptr_t>(base) - (nt_headers->OptionalHeader.ImageBase));
auto relocation_size = total_count_bytes;
// This should check (DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) too but lots of drivers do not have it set due to WDK defaults
const bool doRelocations = image_base_delta != 0 && relocation_size > 0;
if (!doRelocations)
{
printf("no relocations needed\n");
return;
}
assert(relocation_directory != nullptr);
void* relocation_end = reinterpret_cast<uint8_t*>(relocation_directory) + relocation_size;
while (relocation_directory < relocation_end)
{
auto relocation_base = ::ImageRvaToVa(nt_headers, (void*)m_image_mapped.data(), relocation_directory->VirtualAddress, nullptr);
auto num_relocs = (relocation_directory->SizeOfBlock - 8) >> 1;
auto relocation_data = reinterpret_cast<PWORD>(relocation_directory + 1);
for (unsigned long i = 0; i < num_relocs; ++i, ++relocation_data)
{
if (process_relocation(image_base_delta, *relocation_data, (uint8_t*)relocation_base) == FALSE)
{
printf("failed to relocate!");
return;
}
}
relocation_directory = reinterpret_cast<PIMAGE_BASE_RELOCATION>(relocation_data);
}
}
template<typename T>
__forceinline T* ptr_add(void* base, uintptr_t offset)
{
return (T*)(uintptr_t)base + offset;
}
void pe_image::fix_imports(const std::function<uintptr_t(std::string_view)> get_module, const std::function<uintptr_t(const char*, const char*)> get_function)
{
ULONG size;
auto import_descriptors = static_cast<PIMAGE_IMPORT_DESCRIPTOR>(::ImageDirectoryEntryToData(m_image.data(), FALSE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size));
if (import_descriptors == nullptr)
{
printf("no imports!\n");
return;
}
for (; import_descriptors->Name; import_descriptors++)
{
IMAGE_THUNK_DATA* image_thunk_data;
const auto module_name = get_rva<char>(import_descriptors->Name);
const auto module_base = get_module(module_name);
assert(module_base != 0);
printf("processing module: %s [0x%I64X]\n", module_name, module_base);
if (import_descriptors->OriginalFirstThunk)
image_thunk_data = get_rva<IMAGE_THUNK_DATA>(import_descriptors->OriginalFirstThunk);
else
image_thunk_data = get_rva<IMAGE_THUNK_DATA>(import_descriptors->FirstThunk);
auto image_func_data = get_rva<IMAGE_THUNK_DATA64>(import_descriptors->FirstThunk);
assert(image_thunk_data != nullptr);
assert(image_func_data != nullptr);
for (; image_thunk_data->u1.AddressOfData; image_thunk_data++, image_func_data++)
{
uintptr_t function_address;
const auto ordinal = (image_thunk_data->u1.Ordinal & IMAGE_ORDINAL_FLAG64) != 0;
const auto image_import_by_name = get_rva<IMAGE_IMPORT_BY_NAME>(*(DWORD*)image_thunk_data);
const auto name_of_import = static_cast<char*>(image_import_by_name->Name);
function_address = get_function(module_name, name_of_import);
printf("function: %s [0x%I64X]\n", name_of_import, function_address);
assert(function_address != 0);
image_func_data->u1.Function = function_address;
}
}
}
void* pe_image::data()
{
return m_image_mapped.data();
}
size_t pe_image::header_size()
{
return m_nt_headers->OptionalHeader.SizeOfHeaders;
}
}

@ -0,0 +1,73 @@
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
!!!!!!!!!!!!!!!!!!!!!!!!!!! This code was created by not-wlan (wlan). all credit for this header and source file goes to him !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
#pragma once
#include <vector>
#define WIN32_NO_STATUS
#include <Windows.h>
#include <Winternl.h>
#undef WIN32_NO_STATUS
#include <ntstatus.h>
#include <functional>
#include <DbgHelp.h>
#include <variant>
#pragma comment(lib, "Dbghelp.lib")
namespace nasa
{
class pe_image
{
std::vector<uint8_t> m_image;
std::vector<uint8_t> m_image_mapped;
PIMAGE_DOS_HEADER m_dos_header = nullptr;
PIMAGE_NT_HEADERS64 m_nt_headers = nullptr;
PIMAGE_SECTION_HEADER m_section_header = nullptr;
public:
pe_image(std::vector<uint8_t>& image);
size_t size() const;
uintptr_t entry_point() const;
void map();
static bool process_relocation(size_t image_base_delta, uint16_t data, uint8_t* relocation_base);
void relocate(uintptr_t base) const;
template<typename T>
__forceinline T* get_rva(const unsigned long offset)
{
return (T*)::ImageRvaToVa(m_nt_headers, m_image.data(), offset, nullptr);
}
void fix_imports(const std::function<uintptr_t(std::string_view)> get_module, const std::function<uintptr_t(const char*, const char*)> get_function);
void* data();
size_t header_size();
};
}

@ -0,0 +1,88 @@
#pragma once
#include <windows.h>
#include <mutex>
#include <cstdint>
#include <map>
#include "../util/util.hpp"
#include "../loadup.hpp"
#include "../raw_driver.hpp"
#pragma pack ( push, 1 )
typedef struct _GIOMAP
{
unsigned long interface_type;
unsigned long bus;
std::uintptr_t physical_address;
unsigned long io_space;
unsigned long size;
} GIOMAP;
#pragma pack ( pop )
namespace nasa
{
inline std::string drv_key;
inline HANDLE drv_handle = NULL;
inline std::vector<std::pair<std::uintptr_t, std::uint32_t >> virtual_mappings;
inline bool load_drv()
{
const auto [result, key] =
driver::load(
raw_driver,
sizeof(raw_driver)
);
drv_key = key;
drv_handle = CreateFile(
"\\\\.\\GIO",
GENERIC_READ | GENERIC_WRITE,
NULL,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
return drv_handle;
}
inline bool unload_drv()
{
return CloseHandle(drv_handle) && driver::unload(drv_key);
}
inline std::uintptr_t map_phys(
std::uintptr_t addr,
std::size_t size
)
{
GIOMAP in_buffer = { 0, 0, addr, 0, size };
uintptr_t out_buffer[2] = { 0 };
unsigned long returned = 0;
DeviceIoControl(drv_handle, 0xC3502004, reinterpret_cast<LPVOID>(&in_buffer), sizeof(in_buffer),
reinterpret_cast<LPVOID>(out_buffer), sizeof(out_buffer), &returned, NULL);
virtual_mappings.push_back({ out_buffer[0], size });
return out_buffer[0];
}
inline bool unmap_phys(
std::uintptr_t addr,
std::size_t size
)
{
uintptr_t in_buffer = addr;
uintptr_t out_buffer[2] = { sizeof(out_buffer) };
unsigned long returned = NULL;
DeviceIoControl(drv_handle, 0xC3502008, reinterpret_cast<LPVOID>(&in_buffer), sizeof(in_buffer),
reinterpret_cast<LPVOID>(out_buffer), sizeof(out_buffer), &returned, NULL);
return out_buffer[0];
}
inline void unmap_all()
{
for (auto idx = 0u; idx < virtual_mappings.size(); ++idx)
unmap_phys(virtual_mappings[idx].first, virtual_mappings[idx].second);
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,190 @@
/*
MIT License
Copyright (c) 2020 xerox
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <Windows.h>
#include <map>
#include <atomic>
#include <memory>
#if _M_IX86
#define OFFSET_TO_ADDRESS 0x1
#elif _M_X64
#define OFFSET_TO_ADDRESS 0x2
#endif
namespace hook
{
static void write_to_readonly(void* addr, void* data, int size)
{
DWORD old_flags;
VirtualProtect((LPVOID)addr, size, PAGE_EXECUTE_READWRITE, &old_flags);
memcpy((void*)addr, data, size);
VirtualProtect((LPVOID)addr, size, old_flags, &old_flags);
}
class detour
{
public:
detour(void* addr_to_hook, void* jmp_to, bool enable = true)
: hook_addr(addr_to_hook), detour_addr(jmp_to), hook_installed(false)
{
//setup hook
memcpy(
jmp_code + OFFSET_TO_ADDRESS,
&jmp_to,
sizeof(jmp_to)
);
//save bytes
memcpy(
org_bytes,
hook_addr,
sizeof(org_bytes)
);
if(enable)
install();
}
void install()
{
if (hook_installed.load())
return;
// mapped page is already read/write
memcpy(hook_addr, jmp_code, sizeof(jmp_code));
hook_installed.exchange(true);
}
void uninstall()
{
if (!hook_installed.load())
return;
// mapped page is already read/write
memcpy(hook_addr, org_bytes, sizeof(org_bytes));
hook_installed.exchange(false);
}
~detour() { uninstall(); }
bool installed() { return hook_installed; }
void* hook_address() { return hook_addr; }
void* detour_address() { return detour_addr; }
private:
std::atomic<bool> hook_installed;
void *hook_addr, *detour_addr;
#if _M_IX86
/*
0: b8 ff ff ff ff mov eax, 0xffffffff
5: ff e0 jmp eax
*/
unsigned char jmp_code[7] = {
0xb8, 0x0, 0x0, 0x0, 0x0,
0xFF, 0xE0
};
#elif _M_X64
/*
0: 48 b8 ff ff ff ff ff ff ff ff movabs rax,0xffffffffffffffff
7: ff e0 jmp rax
*/
unsigned char jmp_code[12] = {
0x48, 0xb8,
0x0,
0x0,
0x0,
0x0,
0x0,
0x0,
0x0,
0x0,
0xff, 0xe0
};
#endif
std::uint8_t org_bytes[sizeof(jmp_code)];
};
static std::map<void*, std::unique_ptr<detour>> hooks{};
/*
Author: xerox
Date: 12/19/2019
Create Hook without needing to deal with objects
*/
static void make_hook(void* addr_to_hook, void* jmp_to_addr, bool enable = true)
{
if (!addr_to_hook)
return;
hooks.insert({
addr_to_hook,
std::make_unique<detour>(
addr_to_hook,
jmp_to_addr,
enable
)}
);
}
/*
Author: xerox
Date: 12/19/2019
Enable hook given the address to hook
*/
static void enable(void* addr)
{
if (!addr)
return;
hooks.at(addr)->install();
}
/*
Author: xerox
Date: 12/19/2019
Disable hook givent the address of the hook
*/
static void disable(void* addr)
{
if (!addr)
return;
hooks.at(addr)->uninstall();
}
/*
Author: xerox
Date: 12/19/2019
Remove hook completely from vector
*/
static void remove(void* addr)
{
if (!addr)
return;
hooks.at(addr)->~detour();
hooks.erase(addr);
}
}

@ -0,0 +1,355 @@
#pragma once
#include <Windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")
#if _DEBUG
#define DBG_ASSERT(...) assert(__VA_ARGS__)
#define DBG_PRINT(...) printf(__VA_ARGS__)
#else
#define DBG_ASSERT(...)
#define DBG_PRINT(...)
#endif
constexpr char piddb_lock_sig[] = "\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x4C\x8B\x8C\x24";
constexpr char piddb_lock_mask[] = "xxx????x????xxxx";
constexpr char piddb_table_sig[] = "\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x48\x8D\x1D\x00\x00\x00\x00\x48\x85\xC0\x0F";
constexpr char piddb_table_mask[] = "xxx????x????xxx????xxxx";
#define MM_COPY_MEMORY_PHYSICAL 0x1
#define MM_COPY_MEMORY_VIRTUAL 0x2
constexpr auto ntoskrnl_path = "C:\\Windows\\System32\\ntoskrnl.exe";
constexpr auto PAGE_SIZE = 0x1000;
constexpr auto SystemModuleInformation = 11;
constexpr auto SystemHandleInformation = 16;
constexpr auto SystemExtendedHandleInformation = 64;
typedef struct _SYSTEM_HANDLE
{
PVOID Object;
HANDLE UniqueProcessId;
HANDLE HandleValue;
ULONG GrantedAccess;
USHORT CreatorBackTraceIndex;
USHORT ObjectTypeIndex;
ULONG HandleAttributes;
ULONG Reserved;
} SYSTEM_HANDLE, * PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION_EX
{
ULONG_PTR HandleCount;
ULONG_PTR Reserved;
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION_EX, * PSYSTEM_HANDLE_INFORMATION_EX;
typedef struct _RTL_PROCESS_MODULE_INFORMATION
{
HANDLE Section;
PVOID MappedBase;
PVOID ImageBase;
ULONG ImageSize;
ULONG Flags;
USHORT LoadOrderIndex;
USHORT InitOrderIndex;
USHORT LoadCount;
USHORT OffsetToFileName;
UCHAR FullPathName[256];
} RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
typedef struct _RTL_PROCESS_MODULES
{
ULONG NumberOfModules;
RTL_PROCESS_MODULE_INFORMATION Modules[1];
} RTL_PROCESS_MODULES, * PRTL_PROCESS_MODULES;
typedef LARGE_INTEGER PHYSICAL_ADDRESS, * PPHYSICAL_ADDRESS;
typedef struct _MM_COPY_ADDRESS {
union {
PVOID VirtualAddress;
PHYSICAL_ADDRESS PhysicalAddress;
};
} MM_COPY_ADDRESS, * PMMCOPY_ADDRESS;
typedef struct PiDDBCacheEntry
{
LIST_ENTRY list;
UNICODE_STRING driver_name;
ULONG time_stamp;
NTSTATUS load_status;
char _0x0028[16]; // data from the shim engine, or uninitialized memory for custom drivers
}PIDCacheobj;
using ExAcquireResourceExclusiveLite = BOOLEAN(__stdcall*)(void*, bool);
using RtlLookupElementGenericTableAvl = PIDCacheobj * (__stdcall*) (void*, void*);
using RtlDeleteElementGenericTableAvl = bool(__stdcall*)(void*, void*);
using ExReleaseResourceLite = bool(__stdcall*)(void*);
typedef CCHAR KPROCESSOR_MODE;
typedef enum _MODE {
KernelMode,
UserMode,
MaximumMode
} MODE;
typedef enum _POOL_TYPE {
NonPagedPool,
NonPagedPoolExecute,
PagedPool,
NonPagedPoolMustSucceed,
DontUseThisType,
NonPagedPoolCacheAligned,
PagedPoolCacheAligned,
NonPagedPoolCacheAlignedMustS,
MaxPoolType,
NonPagedPoolBase,
NonPagedPoolBaseMustSucceed,
NonPagedPoolBaseCacheAligned,
NonPagedPoolBaseCacheAlignedMustS,
NonPagedPoolSession,
PagedPoolSession,
NonPagedPoolMustSucceedSession,
DontUseThisTypeSession,
NonPagedPoolCacheAlignedSession,
PagedPoolCacheAlignedSession,
NonPagedPoolCacheAlignedMustSSession,
NonPagedPoolNx,
NonPagedPoolNxCacheAligned,
NonPagedPoolSessionNx
} POOL_TYPE;
typedef enum _MEMORY_CACHING_TYPE {
MmNonCached,
MmCached,
MmWriteCombined,
MmHardwareCoherentCached,
MmNonCachedUnordered,
MmUSWCCached,
MmMaximumCacheType,
MmNotMapped
} MEMORY_CACHING_TYPE;
typedef struct _KAPC_STATE {
LIST_ENTRY ApcListHead[MaximumMode];
struct _KPROCESS* Process;
union {
UCHAR InProgressFlags;
struct {
BOOLEAN KernelApcInProgress : 1;
BOOLEAN SpecialApcInProgress : 1;
};
};
BOOLEAN KernelApcPending;
union {
BOOLEAN UserApcPendingAll;
struct {
BOOLEAN SpecialUserApcPending : 1;
BOOLEAN UserApcPending : 1;
};
};
} KAPC_STATE, * PKAPC_STATE, * PRKAPC_STATE;
using PEPROCESS = PVOID;
using ZwOpenProcess = NTSYSAPI NTSTATUS (__fastcall*)(
PHANDLE ProcessHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
CLIENT_ID* ClientId
);
using ZwAllocateVirtualMemory = NTSTATUS(__fastcall*)(
_In_ HANDLE ProcessHandle,
_Inout_ PVOID* BaseAddress,
_In_ ULONG_PTR ZeroBits,
_Inout_ PSIZE_T RegionSize,
_In_ ULONG AllocationType,
_In_ ULONG Protect
);
using MmCopyVirtualMemory = NTSTATUS (__fastcall*)(
IN PEPROCESS FromProcess,
IN PVOID FromAddress,
IN PEPROCESS ToProcess,
OUT PVOID ToAddress,
IN SIZE_T BufferSize,
IN KPROCESSOR_MODE PreviousMode,
OUT PSIZE_T NumberOfBytesCopied
);
using PsLookupProcessByProcessId = NTSTATUS (__fastcall*)(
HANDLE ProcessId,
PEPROCESS* Process
);
using MmCopyMemory = NTSTATUS(__stdcall*)(
PVOID,
MM_COPY_ADDRESS,
SIZE_T,
ULONG,
PSIZE_T
);
using MmGetVirtualForPhysical = PVOID(__fastcall*)(
__in PHYSICAL_ADDRESS PhysicalAddress
);
using MmGetPhysicalAddress = PVOID (__fastcall*)(
__in PVOID BaseAddress
);
using ExAllocatePool = PVOID (__fastcall*) (
POOL_TYPE PoolType,
SIZE_T NumberOfBytes
);
using IoAllocateMdl = PVOID(__fastcall*)(
__drv_aliasesMem PVOID VirtualAddress,
ULONG Length,
BOOLEAN SecondaryBuffer,
BOOLEAN ChargeQuota,
PVOID Irp
);
using MmBuildMdlForNonPagedPool = void (__fastcall*)(
PVOID MemoryDescriptorList
);
using MmMapLockedPagesSpecifyCache = PVOID (__fastcall*)(
PVOID MemoryDescriptorList,
KPROCESSOR_MODE AccessMode,
MEMORY_CACHING_TYPE CacheType,
PVOID RequestedAddress,
ULONG BugCheckOnFailure,
ULONG Priority
);
using KeUnstackDetachProcess = void (__fastcall*)(
PRKAPC_STATE ApcState
);
using KeStackAttachProcess = void (__fastcall*)(
PEPROCESS PROCESS,
PRKAPC_STATE ApcState
);
using ExFreePool = void* (__fastcall*)(
PVOID P
);
using ZwLockVirtualMemory = NTSTATUS (__fastcall*)(
IN HANDLE,
IN OUT PVOID,
IN OUT PULONG,
IN ULONG
);
typedef union _virt_addr_t
{
PVOID value;
struct
{
ULONG64 offset : 12;
ULONG64 pt_index : 9;
ULONG64 pd_index : 9;
ULONG64 pdpt_index : 9;
ULONG64 pml4_index : 9;
ULONG64 reserved : 16;
};
} virt_addr_t, *pvirt_addr_t;
static_assert(sizeof(virt_addr_t) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pml4e
{
ULONG64 value;
struct
{
ULONG64 Present : 1; // Must be 1, region invalid if 0.
ULONG64 ReadWrite : 1; // If 0, writes not allowed.
ULONG64 UserSupervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PDPT.
ULONG64 page_cache : 1; // Determines the memory type used to access PDPT.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1;
ULONG64 page_size : 1; // Must be 0 for PML4E.
ULONG64 Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PDPT of this PML4E.
ULONG64 Reserved : 4;
ULONG64 Ignored3 : 11;
ULONG64 ExecuteDisable : 1; // If 1, instruction fetches not allowed.
};
} pml4e, * ppml4e;
static_assert(sizeof(pml4e) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pdpte
{
ULONG64 value;
struct
{
ULONG64 Present : 1; // Must be 1, region invalid if 0.
ULONG64 ReadWrite : 1; // If 0, writes not allowed.
ULONG64 UserSupervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PD.
ULONG64 page_cache : 1; // Determines the memory type used to access PD.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1;
ULONG64 page_size : 1; // If 1, this entry maps a 1GB page.
ULONG64 Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PD of this PDPTE.
ULONG64 Reserved : 4;
ULONG64 Ignored3 : 11;
ULONG64 ExecuteDisable : 1; // If 1, instruction fetches not allowed.
};
} pdpte, * ppdpte;
static_assert(sizeof(pdpte) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pde
{
ULONG64 value;
struct
{
ULONG64 Present : 1; // Must be 1, region invalid if 0.
ULONG64 ReadWrite : 1; // If 0, writes not allowed.
ULONG64 UserSupervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access PT.
ULONG64 page_cache : 1; // Determines the memory type used to access PT.
ULONG64 Accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Ignored1 : 1;
ULONG64 page_size : 1; // If 1, this entry maps a 2MB page.
ULONG64 Ignored2 : 4;
ULONG64 pfn : 36; // The page frame number of the PT of this PDE.
ULONG64 Reserved : 4;
ULONG64 Ignored3 : 11;
ULONG64 ExecuteDisable : 1; // If 1, instruction fetches not allowed.
};
} pde, * ppde;
static_assert(sizeof(pde) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");
typedef union _pte
{
ULONG64 value;
struct
{
ULONG64 Present : 1; // Must be 1, region invalid if 0.
ULONG64 ReadWrite : 1; // If 0, writes not allowed.
ULONG64 UserSupervisor : 1; // If 0, user-mode accesses not allowed.
ULONG64 PageWriteThrough : 1; // Determines the memory type used to access the memory.
ULONG64 page_cache : 1; // Determines the memory type used to access the memory.
ULONG64 accessed : 1; // If 0, this entry has not been used for translation.
ULONG64 Dirty : 1; // If 0, the memory backing this page has not been written to.
ULONG64 PageAccessType : 1; // Determines the memory type used to access the memory.
ULONG64 Global : 1; // If 1 and the PGE bit of CR4 is set, translations are global.
ULONG64 Ignored2 : 3;
ULONG64 pfn : 36; // The page frame number of the backing physical page.
ULONG64 Reserved : 4;
ULONG64 Ignored3 : 7;
ULONG64 ProtectionKey : 4; // If the PKE bit of CR4 is set, determines the protection key.
ULONG64 ExecuteDisable : 1; // If 1, instruction fetches not allowed.
};
} pte, * ppte;
static_assert(sizeof(pte) == sizeof(PVOID), "Size mismatch, only 64-bit supported.");

@ -0,0 +1,403 @@
#pragma once
#include <Windows.h>
#include <cstdint>
#include <string_view>
#include <iterator>
#include <map>
#include <fstream>
#include <string>
#include <vector>
#include <tlhelp32.h>
#include <ntstatus.h>
#include <atomic>
#include <array>
#include <algorithm>
#include "nt.hpp"
namespace util
{
//--- ranges of physical memory
inline std::map<std::uintptr_t, std::size_t> pmem_ranges{};
//--- validates the address
inline bool is_valid(std::uintptr_t addr)
{
for (auto range : pmem_ranges)
if (addr >= range.first && addr <= range.first + range.second)
return true;
return false;
}
// Author: Remy Lebeau
// taken from here: https://stackoverflow.com/questions/48485364/read-reg-resource-list-memory-values-incorrect-value
inline const auto init_ranges = ([&]() -> bool
{
HKEY h_key;
DWORD type, size;
LPBYTE data;
RegOpenKeyEx(HKEY_LOCAL_MACHINE, "HARDWARE\\RESOURCEMAP\\System Resources\\Physical Memory", 0, KEY_READ, &h_key);
RegQueryValueEx(h_key, ".Translated", NULL, &type, NULL, &size); //get size
data = new BYTE[size];
RegQueryValueEx(h_key, ".Translated", NULL, &type, data, &size);
DWORD count = *(DWORD*)(data + 16);
auto pmi = data + 24;
for (int dwIndex = 0; dwIndex < count; dwIndex++)
{
pmem_ranges.emplace(*(uint64_t*)(pmi + 0), *(uint64_t*)(pmi + 8));
pmi += 20;
}
delete[] data;
RegCloseKey(h_key);
return true;
})();
inline PIMAGE_FILE_HEADER get_file_header(void* base_addr)
{
if (!base_addr || *(short*)base_addr != 0x5A4D)
return NULL;
PIMAGE_DOS_HEADER dos_headers =
reinterpret_cast<PIMAGE_DOS_HEADER>(base_addr);
PIMAGE_NT_HEADERS nt_headers =
reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<DWORD_PTR>(base_addr) + dos_headers->e_lfanew);
return &nt_headers->FileHeader;
}
inline DWORD get_pid(const char* proc_name)
{
PROCESSENTRY32 proc_info;
proc_info.dwSize = sizeof(proc_info);
HANDLE proc_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (proc_snapshot == INVALID_HANDLE_VALUE)
return NULL;
Process32First(proc_snapshot, &proc_info);
if (!strcmp(proc_info.szExeFile, proc_name))
{
CloseHandle(proc_snapshot);
return proc_info.th32ProcessID;
}
while (Process32Next(proc_snapshot, &proc_info))
{
if (!strcmp(proc_info.szExeFile, proc_name))
{
CloseHandle(proc_snapshot);
return proc_info.th32ProcessID;
}
}
CloseHandle(proc_snapshot);
return NULL;
}
// this was taken from wlan's drvmapper:
// https://github.com/not-wlan/drvmap/blob/98d93cc7b5ec17875f815a9cb94e6d137b4047ee/drvmap/util.cpp#L7
inline void open_binary_file(const std::string& file, std::vector<uint8_t>& data)
{
std::ifstream fstr(file, std::ios::binary);
fstr.unsetf(std::ios::skipws);
fstr.seekg(0, std::ios::end);
const auto file_size = fstr.tellg();
fstr.seekg(NULL, std::ios::beg);
data.reserve(static_cast<uint32_t>(file_size));
data.insert(data.begin(), std::istream_iterator<uint8_t>(fstr), std::istream_iterator<uint8_t>());
}
inline void* get_module_base(DWORD proc_id, const char* mod_name)
{
void* base_addr = 0;
HANDLE h_snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, proc_id);
if (h_snap != INVALID_HANDLE_VALUE)
{
MODULEENTRY32 mod_entry;
mod_entry.dwSize = sizeof(mod_entry);
if (Module32First(h_snap, &mod_entry))
{
do
{
if (!strcmp(mod_entry.szModule, mod_name))
{
base_addr = mod_entry.modBaseAddr;
break;
}
} while (Module32Next(h_snap, &mod_entry));
}
}
CloseHandle(h_snap);
return base_addr;
}
// get base address of kernel module
//
// taken from: https://github.com/z175/kdmapper/blob/master/kdmapper/utils.cpp#L30
inline std::uintptr_t get_module_base(const char* module_name)
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
NTSTATUS status =
NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation),
buffer,
buffer_size,
&buffer_size
);
while (status == STATUS_INFO_LENGTH_MISMATCH)
{
VirtualFree(buffer, NULL, MEM_RELEASE);
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
status = NtQuerySystemInformation(static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation), buffer, buffer_size, &buffer_size);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
{
const std::string current_module_name = std::string(reinterpret_cast<char*>(modules->Modules[idx].FullPathName) + modules->Modules[idx].OffsetToFileName);
if (!_stricmp(current_module_name.c_str(), module_name))
{
const uint64_t result = reinterpret_cast<uint64_t>(modules->Modules[idx].ImageBase);
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
// get base address of kernel module
//
// taken from: https://github.com/z175/kdmapper/blob/master/kdmapper/utils.cpp#L30
inline void* get_kernel_export(const char* module_name, const char* export_name, bool rva = false)
{
void* buffer = nullptr;
DWORD buffer_size = NULL;
NTSTATUS status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation),
buffer,
buffer_size,
&buffer_size
);
while (status == STATUS_INFO_LENGTH_MISMATCH)
{
VirtualFree(buffer, 0, MEM_RELEASE);
buffer = VirtualAlloc(nullptr, buffer_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
status = NtQuerySystemInformation(
static_cast<SYSTEM_INFORMATION_CLASS>(SystemModuleInformation),
buffer,
buffer_size,
&buffer_size
);
}
if (!NT_SUCCESS(status))
{
VirtualFree(buffer, 0, MEM_RELEASE);
return 0;
}
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
{
// find module and then load library it
const std::string current_module_name =
std::string(reinterpret_cast<char*>(
modules->Modules[idx].FullPathName) +
modules->Modules[idx].OffsetToFileName
);
if (!_stricmp(current_module_name.c_str(), module_name))
{
// had to shoot the tires off of "\\SystemRoot\\"
std::string full_path = reinterpret_cast<char*>(modules->Modules[idx].FullPathName);
full_path.replace(
full_path.find("\\SystemRoot\\"),
sizeof("\\SystemRoot\\") - 1,
std::string(getenv("SYSTEMROOT")).append("\\")
);
const auto module_base =
LoadLibraryEx(
full_path.c_str(),
NULL,
DONT_RESOLVE_DLL_REFERENCES
);
PIMAGE_DOS_HEADER p_idh;
PIMAGE_NT_HEADERS p_inh;
PIMAGE_EXPORT_DIRECTORY p_ied;
PDWORD addr, name;
PWORD ordinal;
p_idh = (PIMAGE_DOS_HEADER)module_base;
if (p_idh->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
p_inh = (PIMAGE_NT_HEADERS)((LPBYTE)module_base + p_idh->e_lfanew);
if (p_inh->Signature != IMAGE_NT_SIGNATURE)
return NULL;
if (p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0)
return NULL;
p_ied = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)module_base +
p_inh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
addr = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfFunctions);
name = (PDWORD)((LPBYTE)module_base + p_ied->AddressOfNames);
ordinal = (PWORD)((LPBYTE)module_base + p_ied->AddressOfNameOrdinals);
// find exported function
for (auto i = 0; i < p_ied->AddressOfFunctions; i++)
if (!strcmp(export_name, (char*)module_base + name[i]))
{
if (!rva)
{
auto result = (void*)((std::uintptr_t)modules->Modules[idx].ImageBase + addr[ordinal[i]]);
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
else
{
auto result = (void*)addr[ordinal[i]];
VirtualFree(buffer, NULL, MEM_RELEASE);
return result;
}
}
}
}
VirtualFree(buffer, NULL, MEM_RELEASE);
return NULL;
}
namespace memory
{
template<std::size_t pattern_length>
inline std::uintptr_t pattern_scan_kernel(const char(&signature)[pattern_length], const char(&mask)[pattern_length])
{
static const auto kernel_addr =
LoadLibraryEx(
"ntoskrnl.exe",
NULL,
DONT_RESOLVE_DLL_REFERENCES
);
static const auto p_idh = reinterpret_cast<PIMAGE_DOS_HEADER>(kernel_addr);
if (p_idh->e_magic != IMAGE_DOS_SIGNATURE)
return NULL;
static const auto p_inh = reinterpret_cast<PIMAGE_NT_HEADERS>((LPBYTE)kernel_addr + p_idh->e_lfanew);
if (p_inh->Signature != IMAGE_NT_SIGNATURE)
return NULL;
static auto current_section = reinterpret_cast<PIMAGE_SECTION_HEADER>(p_inh + 1);
static const auto first_section = current_section;
static const auto num_sec = p_inh->FileHeader.NumberOfSections;
static std::atomic<bool> ran_before = false;
//
// only run this once.
//
if (!ran_before.exchange(true))
for (; current_section < first_section + num_sec; ++current_section)
if (!strcmp(reinterpret_cast<char*>(current_section->Name), "PAGE"))
break;
static const auto page_section_begin =
reinterpret_cast<std::uint64_t>(kernel_addr) + current_section->VirtualAddress;
const auto pattern_view = std::string_view{
reinterpret_cast<char*>(page_section_begin),
current_section->SizeOfRawData
};
std::array<std::pair<char, char>, pattern_length - 1> pattern{};
for (std::size_t index = 0; index < pattern_length - 1; index++)
pattern[index] = { signature[index], mask[index] };
auto resultant_address = std::search(
pattern_view.cbegin(),
pattern_view.cend(),
pattern.cbegin(),
pattern.cend(),
[](char left, std::pair<char, char> right) -> bool {
return (right.second == '?' || left == right.first);
});
return resultant_address == pattern_view.cend() ? 0 : reinterpret_cast<std::uintptr_t>(resultant_address.operator->());
}
//
// be aware that this may not work for win8 or win7!
//
inline void* get_piddb_lock()
{
static const auto absolute_addr_instruction =
pattern_scan_kernel(
piddb_lock_sig,
piddb_lock_mask
);
static const auto ntoskrnl_in_my_process =
reinterpret_cast<std::uintptr_t>(GetModuleHandle("ntoskrnl.exe"));
if (!absolute_addr_instruction || !ntoskrnl_in_my_process)
return {};
const auto lea_rip_rva = *(PLONG)(absolute_addr_instruction + 3);
const auto real_rva = (absolute_addr_instruction + 7 + lea_rip_rva) - ntoskrnl_in_my_process;
static const auto kernel_base = util::get_module_base("ntoskrnl.exe");
if (!kernel_base)
return {};
return reinterpret_cast<void*>(kernel_base + real_rva);
}
//
// be aware that this may not work for win8 or win7!
//
inline void* get_piddb_table()
{
static const auto absolute_addr_instruction =
pattern_scan_kernel(
piddb_table_sig,
piddb_table_mask
);
static const auto ntoskrnl_in_my_process =
reinterpret_cast<std::uintptr_t>(GetModuleHandle("ntoskrnl.exe"));
if (!absolute_addr_instruction || !ntoskrnl_in_my_process)
return {};
const auto lea_rip_rva = *(PLONG)(absolute_addr_instruction + 3);
const auto real_rva = (absolute_addr_instruction + 7 + lea_rip_rva) - ntoskrnl_in_my_process;
static const auto kernel_base = util::get_module_base("ntoskrnl.exe");
if (!kernel_base)
return {};
return reinterpret_cast<void*>(kernel_base + real_rva);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Loading…
Cancel
Save