You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
428 lines
12 KiB
428 lines
12 KiB
/*
|
|
WARNING: utils.hpp must be the first file included...
|
|
this is because i use getenv and that requires _CRT_SECURE_NO_WARNINGS...
|
|
*/
|
|
|
|
#pragma once
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#pragma comment(lib, "ntdll.lib")
|
|
|
|
#include <Windows.h>
|
|
#include <winternl.h>
|
|
#include <ntstatus.h>
|
|
#include <tlhelp32.h>
|
|
|
|
#include <fstream>
|
|
#include <functional>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <map>
|
|
|
|
inline bool dbg_print = false;
|
|
#define DBG_PRINT(format, ...) \
|
|
if (dbg_print) std::printf(format, __VA_ARGS__ )
|
|
|
|
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;
|
|
|
|
#define SystemKernelVaShadowInformation (SYSTEM_INFORMATION_CLASS) 196
|
|
typedef struct _SYSTEM_KERNEL_VA_SHADOW_INFORMATION
|
|
{
|
|
struct
|
|
{
|
|
ULONG KvaShadowEnabled : 1;
|
|
ULONG KvaShadowUserGlobal : 1;
|
|
ULONG KvaShadowPcid : 1;
|
|
ULONG KvaShadowInvpcid : 1;
|
|
ULONG KvaShadowRequired : 1;
|
|
ULONG KvaShadowRequiredAvailable : 1;
|
|
ULONG InvalidPteBit : 6;
|
|
ULONG L1DataCacheFlushSupported : 1;
|
|
ULONG L1TerminalFaultMitigationPresent : 1;
|
|
ULONG Reserved : 18;
|
|
} KvaShadowFlags;
|
|
} SYSTEM_KERNEL_VA_SHADOW_INFORMATION, * PSYSTEM_KERNEL_VA_SHADOW_INFORMATION;
|
|
|
|
namespace utils
|
|
{
|
|
inline std::uintptr_t scan(std::uintptr_t base, std::uint32_t size, const char* pattern, const char* mask)
|
|
{
|
|
static const auto check_mask =
|
|
[&](const char* base, const char* pattern, const char* mask) -> bool
|
|
{
|
|
for (; *mask; ++base, ++pattern, ++mask)
|
|
if (*mask == 'x' && *base != *pattern)
|
|
return false;
|
|
return true;
|
|
};
|
|
|
|
size -= strlen(mask);
|
|
for (auto i = 0; i <= size; ++i)
|
|
{
|
|
void* addr = (void*)&(((char*)base)[i]);
|
|
if (check_mask((char*)addr, pattern, mask))
|
|
return reinterpret_cast<std::uintptr_t>(addr);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
inline void open_binary_file(const std::string& file, std::vector<uint8_t>& data)
|
|
{
|
|
std::ifstream fstr(file, std::ios::binary);
|
|
fstr.unsetf(std::ios::skipws);
|
|
fstr.seekg(0, std::ios::end);
|
|
|
|
const auto file_size = fstr.tellg();
|
|
|
|
fstr.seekg(NULL, std::ios::beg);
|
|
data.reserve(static_cast<uint32_t>(file_size));
|
|
data.insert(data.begin(), std::istream_iterator<uint8_t>(fstr), std::istream_iterator<uint8_t>());
|
|
}
|
|
|
|
inline std::uint32_t get_pid(const wchar_t* proc_name)
|
|
{
|
|
PROCESSENTRY32 proc_info;
|
|
proc_info.dwSize = sizeof(proc_info);
|
|
|
|
HANDLE proc_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
|
|
if (proc_snapshot == INVALID_HANDLE_VALUE)
|
|
return NULL;
|
|
|
|
Process32First(proc_snapshot, &proc_info);
|
|
if (!wcscmp(proc_info.szExeFile, proc_name))
|
|
{
|
|
CloseHandle(proc_snapshot);
|
|
return proc_info.th32ProcessID;
|
|
}
|
|
|
|
while (Process32Next(proc_snapshot, &proc_info))
|
|
{
|
|
if (!wcscmp(proc_info.szExeFile, proc_name))
|
|
{
|
|
CloseHandle(proc_snapshot);
|
|
return proc_info.th32ProcessID;
|
|
}
|
|
}
|
|
|
|
CloseHandle(proc_snapshot);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
namespace kmodule
|
|
{
|
|
using kmodule_callback_t = std::function<bool(PRTL_PROCESS_MODULE_INFORMATION, const char*)>;
|
|
inline void each_module(kmodule_callback_t callback)
|
|
{
|
|
void* buffer = nullptr;
|
|
DWORD buffer_size = NULL;
|
|
|
|
auto status = NtQuerySystemInformation(
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
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>(0xB),
|
|
buffer, buffer_size, &buffer_size);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
return;
|
|
}
|
|
|
|
const auto modules = static_cast<PRTL_PROCESS_MODULES>(buffer);
|
|
for (auto idx = 0u; idx < modules->NumberOfModules; ++idx)
|
|
{
|
|
auto full_path = std::string(
|
|
reinterpret_cast<char*>(
|
|
modules->Modules[idx].FullPathName));
|
|
|
|
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
|
full_path.replace(full_path.find("\\SystemRoot\\"),
|
|
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
|
|
|
else if (full_path.find("\\??\\") != std::string::npos)
|
|
full_path.replace(full_path.find("\\??\\"),
|
|
sizeof("\\??\\") - 1, "");
|
|
|
|
if (!callback(&modules->Modules[idx], full_path.c_str()))
|
|
{
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
return;
|
|
}
|
|
|
|
inline std::uintptr_t get_base(const char* module_name)
|
|
{
|
|
void* buffer = nullptr;
|
|
DWORD buffer_size = NULL;
|
|
|
|
auto status = NtQuerySystemInformation(
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
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>(0xB),
|
|
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 auto 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 auto result =
|
|
reinterpret_cast<std::uint64_t>(
|
|
modules->Modules[idx].ImageBase);
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
return NULL;
|
|
}
|
|
|
|
inline std::uintptr_t get_export(const char* module_name, const char* export_name)
|
|
{
|
|
void* buffer = nullptr;
|
|
DWORD buffer_size = NULL;
|
|
|
|
NTSTATUS status = NtQuerySystemInformation(
|
|
static_cast<SYSTEM_INFORMATION_CLASS>(0xB),
|
|
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>(0xB),
|
|
buffer,
|
|
buffer_size,
|
|
&buffer_size
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
VirtualFree(buffer, 0, MEM_RELEASE);
|
|
return NULL;
|
|
}
|
|
|
|
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))
|
|
{
|
|
auto full_path = std::string(
|
|
reinterpret_cast<char*>(
|
|
modules->Modules[idx].FullPathName));
|
|
|
|
if (full_path.find("\\SystemRoot\\") != std::string::npos)
|
|
full_path.replace(full_path.find("\\SystemRoot\\"),
|
|
sizeof("\\SystemRoot\\") - 1, std::string(getenv("SYSTEMROOT")).append("\\"));
|
|
|
|
else if (full_path.find("\\??\\") != std::string::npos)
|
|
full_path.replace(full_path.find("\\??\\"),
|
|
sizeof("\\??\\") - 1, "");
|
|
|
|
const auto module_base =
|
|
LoadLibraryExA(
|
|
full_path.c_str(),
|
|
NULL,
|
|
DONT_RESOLVE_DLL_REFERENCES
|
|
);
|
|
|
|
if (!module_base)
|
|
{
|
|
// free the RTL_PROCESS_MODULES buffer...
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
return NULL;
|
|
}
|
|
|
|
const auto image_base =
|
|
reinterpret_cast<std::uintptr_t>(
|
|
modules->Modules[idx].ImageBase);
|
|
|
|
// free the RTL_PROCESS_MODULES buffer...
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
|
|
const auto export_um_addr =
|
|
reinterpret_cast<std::uintptr_t>(
|
|
GetProcAddress(module_base, export_name));
|
|
|
|
if (!export_um_addr)
|
|
return NULL;
|
|
|
|
return (export_um_addr - reinterpret_cast<std::uintptr_t>(module_base)) + image_base;
|
|
}
|
|
}
|
|
|
|
VirtualFree(buffer, NULL, MEM_RELEASE);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
namespace pe
|
|
{
|
|
using section_callback_t = std::function<bool(PIMAGE_SECTION_HEADER, std::uintptr_t)>;
|
|
|
|
// returns an std::vector containing all of the bytes of the section
|
|
// and also the RVA from the image base to the beginning of the section...
|
|
inline std::pair<std::vector<std::uint8_t>, std::uint32_t> get_section(std::uintptr_t module_base, const char* section_name)
|
|
{
|
|
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
|
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
|
|
|
const auto section_header =
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
|
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
|
|
|
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
|
{
|
|
const auto _section_name =
|
|
reinterpret_cast<char*>(
|
|
section_header[idx].Name);
|
|
|
|
// sometimes section names are not null terminated...
|
|
if (!strncmp(_section_name, section_name, strlen(section_name) - 1))
|
|
{
|
|
const auto section_base =
|
|
reinterpret_cast<std::uint8_t*>(
|
|
module_base + section_header[idx].VirtualAddress);
|
|
|
|
const auto section_end =
|
|
reinterpret_cast<std::uint8_t*>(
|
|
section_base + section_header[idx].Misc.VirtualSize);
|
|
|
|
std::vector<std::uint8_t> section_bin(section_base, section_end);
|
|
return { section_bin, section_header[idx].VirtualAddress };
|
|
}
|
|
}
|
|
|
|
return { {}, {} };
|
|
}
|
|
|
|
inline void each_section(section_callback_t callback, std::uintptr_t module_base)
|
|
{
|
|
if (!module_base)
|
|
return;
|
|
|
|
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(
|
|
reinterpret_cast<PIMAGE_DOS_HEADER>(module_base)->e_lfanew + module_base);
|
|
|
|
const auto section_header =
|
|
reinterpret_cast<PIMAGE_SECTION_HEADER>(
|
|
reinterpret_cast<std::uintptr_t>(nt_headers) + sizeof(DWORD)
|
|
+ sizeof(IMAGE_FILE_HEADER) + nt_headers->FileHeader.SizeOfOptionalHeader);
|
|
|
|
for (auto idx = 0u; idx < nt_headers->FileHeader.NumberOfSections; ++idx)
|
|
{
|
|
const auto _section_name =
|
|
reinterpret_cast<char*>(
|
|
section_header[idx].Name);
|
|
|
|
// keep looping until the callback returns false...
|
|
if (!callback(§ion_header[idx], module_base))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace rop
|
|
{
|
|
// https://j00ru.vexillium.org/2011/06/smep-what-is-it-and-how-to-beat-it-on-windows/
|
|
// http://blog.ptsecurity.com/2012/09/bypassing-intel-smep-on-windows-8-x64.html?m=1
|
|
// just implimented the rop information from these posts...
|
|
inline std::uintptr_t find_kgadget(const char* sig, const char* mask)
|
|
{
|
|
std::uintptr_t result = 0u;
|
|
kmodule::each_module(
|
|
[&](auto kernel_image, const char* image_name) -> bool
|
|
{
|
|
utils::pe::each_section(
|
|
[&](auto section_header, std::uintptr_t image_base) -> bool
|
|
{
|
|
if (section_header->Characteristics & IMAGE_SCN_CNT_CODE &&
|
|
!(section_header->Characteristics & IMAGE_SCN_MEM_DISCARDABLE))
|
|
{
|
|
const auto rop_gadget =
|
|
utils::scan(image_base + section_header->VirtualAddress,
|
|
section_header->Misc.VirtualSize, sig, mask);
|
|
|
|
if (rop_gadget)
|
|
result = (rop_gadget - image_base) +
|
|
reinterpret_cast<std::uintptr_t>(kernel_image->ImageBase);
|
|
|
|
return !rop_gadget;
|
|
}
|
|
return true;
|
|
},
|
|
reinterpret_cast<std::uintptr_t>(
|
|
LoadLibraryExA(image_name,
|
|
NULL, DONT_RESOLVE_DLL_REFERENCES))
|
|
);
|
|
return !result;
|
|
}
|
|
);
|
|
return result;
|
|
}
|
|
}
|
|
} |