321 lines
8.5 KiB
C++
321 lines
8.5 KiB
C++
/***
|
|
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: UtilProcessMap.NT.cpp
|
|
Date: 2022-1-24
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "ProcessMap.NT.hpp"
|
|
#include "ProcessMap.hpp"
|
|
|
|
namespace Aurora::Process
|
|
{
|
|
static AuThreadPrimitives::MutexUnique_t gMutex;
|
|
static AuBST<AuUInt, AuString> gPathCache;
|
|
static AuBST<AuUInt, AuString> gModNameCache;
|
|
|
|
AuString ModuleToSomething(HMODULE handle, bool path)
|
|
{
|
|
try
|
|
{
|
|
std::wstring file;
|
|
|
|
if (!handle)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (gMutex)
|
|
{
|
|
AU_LOCK_GUARD(gMutex);
|
|
if (path)
|
|
{
|
|
auto itr = gPathCache.find(reinterpret_cast<AuUInt>(handle));
|
|
if (itr != gPathCache.end()) return itr->second;
|
|
}
|
|
else
|
|
{
|
|
auto itr = gModNameCache.find(reinterpret_cast<AuUInt>(handle));
|
|
if (itr != gModNameCache.end()) return itr->second;
|
|
}
|
|
}
|
|
|
|
if (!AuTryResize(file, 16 * 1024))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
auto length = GetModuleFileNameW(handle, &file[0], DWORD(file.size()));
|
|
if (!length)
|
|
{
|
|
return {};
|
|
}
|
|
|
|
file.resize(length);
|
|
|
|
if (!path)
|
|
{
|
|
auto idx = file.find_last_of('\\');
|
|
if (idx != std::wstring::npos)
|
|
{
|
|
file = file.substr(idx + 1);
|
|
}
|
|
}
|
|
|
|
auto ret = Locale::ConvertFromWChar(file.c_str(), file.length());
|
|
|
|
if (gMutex)
|
|
{
|
|
AU_LOCK_GUARD(gMutex);
|
|
if (path)
|
|
{
|
|
AuTryInsert(gPathCache, AuMakePair(reinterpret_cast<AuUInt>(handle), ret));
|
|
}
|
|
else
|
|
{
|
|
AuTryInsert(gModNameCache, AuMakePair(reinterpret_cast<AuUInt>(handle), ret));
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
catch (...)
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
AuString ModuleToName(HMODULE handle)
|
|
{
|
|
return ModuleToSomething(handle, false);
|
|
}
|
|
|
|
AuString ModuleToPath(HMODULE handle)
|
|
{
|
|
return ModuleToSomething(handle, true);
|
|
}
|
|
|
|
static AuUInt GetModuleBaseAddressFromPathGrossYesThisIsCached(HMODULE mod, const AuString &path)
|
|
{
|
|
if (path.empty())
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
auto dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(mod);
|
|
auto nt = &(reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<AuUInt>(mod) + dosHeader->e_lfanew)->OptionalHeader.ImageBase);
|
|
|
|
auto offset = static_cast<AuUInt32>(reinterpret_cast<const AuUInt8 *>(nt) - reinterpret_cast<const AuUInt8 *>(mod));
|
|
using Value_t = AuRemovePointer_t<decltype(nt)>;
|
|
Value_t value {};
|
|
|
|
auto handle = CreateFileW(Locale::ConvertFromUTF8(path).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
DWORD bytesRead {};
|
|
|
|
if (!SetFilePointer(handle, offset, NULL, 0))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (!ReadFile(handle, &value, sizeof(Value_t), &bytesRead, NULL))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
if (bytesRead != sizeof(Value_t))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
CloseHandle(handle);
|
|
return value;
|
|
}
|
|
|
|
static void FetchModuleSections(Sections §ions, AuUInt offset, HMODULE mod)
|
|
{
|
|
auto dosHeader = reinterpret_cast<PIMAGE_DOS_HEADER>(mod);
|
|
auto nt = reinterpret_cast<PIMAGE_NT_HEADERS>(reinterpret_cast<AuUInt>(mod) + dosHeader->e_lfanew);
|
|
|
|
auto pSection = IMAGE_FIRST_SECTION(nt);
|
|
auto pSectionCur = pSection;
|
|
|
|
auto imageBase = nt->OptionalHeader.ImageBase;
|
|
|
|
for (auto i = 0; i < nt->FileHeader.NumberOfSections; i++)
|
|
{
|
|
auto cur = pSectionCur++;
|
|
Section seg;
|
|
|
|
seg.origVa = cur->VirtualAddress + offset;
|
|
seg.baseVa = cur->VirtualAddress + imageBase;
|
|
seg.size = cur->SizeOfRawData;
|
|
seg.fsOff = cur->PointerToRawData;
|
|
|
|
seg.pt.readable = cur->Characteristics & (IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE);
|
|
seg.pt.NX = !(cur->Characteristics & IMAGE_SCN_MEM_EXECUTE);
|
|
seg.pt.writable = cur->Characteristics & IMAGE_SCN_MEM_WRITE;
|
|
|
|
seg.name = AuString(cur->Name, cur->Name + strnlen(reinterpret_cast<const char *>(cur->Name), sizeof(cur->Name)));
|
|
|
|
AuTryInsert(sections, seg);
|
|
}
|
|
}
|
|
|
|
static AuSPtr<PublicModule> HandleToPublicModule(HMODULE h)
|
|
{
|
|
auto pub = AuMakeShared<PublicModule>();
|
|
if (!pub) return {};
|
|
pub->moduleMeta = AuMakeShared<ModuleMeta>();
|
|
if (!pub->moduleMeta) return {};
|
|
|
|
pub->moduleMeta->moduleBase = AuUInt(h);
|
|
pub->moduleMeta->moduleName = ModuleToName(h);
|
|
pub->moduleMeta->modulePath = ModuleToPath(h);
|
|
pub->moduleMeta->origVa = GetModuleBaseAddressFromPathGrossYesThisIsCached(h, pub->moduleMeta->modulePath);
|
|
FetchModuleSections(pub->sections, pub->moduleMeta->origVa, h);
|
|
return pub;
|
|
}
|
|
|
|
void InvaildateModule(HMODULE hmod)
|
|
{
|
|
if (!gMutex)
|
|
{
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// acquire gmutex
|
|
{
|
|
AU_LOCK_GUARD(gMutex);
|
|
auto itr1 = gPathCache.find(reinterpret_cast<AuUInt>(hmod));
|
|
if (itr1 != gPathCache.end()) gPathCache.erase(itr1);
|
|
|
|
auto itr2 = gModNameCache.find(reinterpret_cast<AuUInt>(hmod));
|
|
if (itr2 != gModNameCache.end()) gModNameCache.erase(itr2);
|
|
}
|
|
|
|
// release lock
|
|
{
|
|
RemoveModuleCache({"", reinterpret_cast<AuUInt>(hmod)});
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
bool MakeAwarePtr(AuUInt pointer)
|
|
{
|
|
HMODULE handle;
|
|
if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCWSTR>(pointer), &handle))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
MakeAware(handle);
|
|
return true;
|
|
}
|
|
|
|
AuOptional<Section> LookupArbitrarySection(AuUInt address)
|
|
{
|
|
MEMORY_BASIC_INFORMATION info;
|
|
Section section;
|
|
|
|
if (!VirtualQuery((LPCVOID)address, &info, sizeof(info)))
|
|
{
|
|
return {};
|
|
}
|
|
|
|
section.baseVa = reinterpret_cast<AuUInt>(info.BaseAddress);
|
|
section.size = info.RegionSize;
|
|
section.pt.NX = (info.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ)) == 0;
|
|
section.pt.writable = (info.Protect & (PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) != 0;
|
|
section.pt.readable = (!section.pt.NX) && ((info.Protect & (PAGE_NOACCESS)) != 0);
|
|
section.pt.acSanity = info.Protect == PAGE_EXECUTE_WRITECOPY;
|
|
|
|
if (info.Type & (MEM_MAPPED | MEM_IMAGE))
|
|
{
|
|
section.name = kSectionNameFile;
|
|
}
|
|
else if (info.Type & (MEM_PRIVATE))
|
|
{
|
|
section.name = kSectionNameHeap;
|
|
}
|
|
|
|
return section;
|
|
}
|
|
|
|
void MakeAware(HMODULE hmod)
|
|
{
|
|
ModuleBasePair handle {"", reinterpret_cast<AuUInt>(hmod)};
|
|
|
|
if (!gMutex)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (hmod == INVALID_HANDLE_VALUE)
|
|
{
|
|
SysPushErrorArg();
|
|
return;
|
|
}
|
|
|
|
if (!hmod)
|
|
{
|
|
SysPushErrorArg();
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (IsInModuleCache(handle))
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto ptr = HandleToPublicModule(hmod);
|
|
if (!ptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
InsertModuleCache(handle, ptr);
|
|
}
|
|
catch (...)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
PublicModule GetExecutableRoot()
|
|
{
|
|
try
|
|
{
|
|
auto handle = GetModuleHandleW(NULL);
|
|
MakeAware(handle);
|
|
return GetFromModuleCache(reinterpret_cast<AuUInt>(handle));
|
|
}
|
|
catch (...)
|
|
{
|
|
return {};
|
|
}
|
|
}
|
|
|
|
void InitProcessMapNt()
|
|
{
|
|
gMutex = AuThreadPrimitives::MutexUnique();
|
|
}
|
|
|
|
void DeinitProcessMapNt()
|
|
{
|
|
gMutex.reset();
|
|
}
|
|
} |