mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
VirtualQuery rework
This commit is contained in:
parent
d8150e33b9
commit
a9581423a8
@ -251,6 +251,17 @@ if(BUILD_TESTING)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_virtualalloc.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_virtualquery.exe
|
||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||
-I${CMAKE_CURRENT_SOURCE_DIR}/test
|
||||
-o test_virtualquery.exe
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_virtualquery.c
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_virtualquery.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test/test_assert.h)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${WIBO_TEST_BIN_DIR}/test_rtl.exe
|
||||
COMMAND ${WIBO_MINGW_CC} -Wall -Wextra -O2
|
||||
@ -299,6 +310,7 @@ if(BUILD_TESTING)
|
||||
${WIBO_TEST_BIN_DIR}/test_overlapped_io.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_time.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_virtualalloc.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_virtualquery.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_rtl.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_ntquery.exe
|
||||
${WIBO_TEST_BIN_DIR}/test_sysdir.exe)
|
||||
@ -361,6 +373,12 @@ if(BUILD_TESTING)
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
|
||||
add_test(NAME wibo.test_virtualquery
|
||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_virtualquery.exe)
|
||||
set_tests_properties(wibo.test_virtualquery PROPERTIES
|
||||
WORKING_DIRECTORY ${WIBO_TEST_BIN_DIR}
|
||||
DEPENDS wibo.build_fixtures)
|
||||
|
||||
add_test(NAME wibo.test_rtl
|
||||
COMMAND $<TARGET_FILE:wibo> ${WIBO_TEST_BIN_DIR}/test_rtl.exe)
|
||||
set_tests_properties(wibo.test_rtl PROPERTIES
|
||||
|
47
common.h
47
common.h
@ -78,6 +78,44 @@ using LPWSTR = uint16_t *;
|
||||
using LPCWSTR = const uint16_t *;
|
||||
using LPCWCH = const uint16_t *;
|
||||
using WCHAR = uint16_t;
|
||||
|
||||
// Page protection constants
|
||||
constexpr DWORD PAGE_NOACCESS = 0x01;
|
||||
constexpr DWORD PAGE_READONLY = 0x02;
|
||||
constexpr DWORD PAGE_READWRITE = 0x04;
|
||||
constexpr DWORD PAGE_WRITECOPY = 0x08;
|
||||
constexpr DWORD PAGE_EXECUTE = 0x10;
|
||||
constexpr DWORD PAGE_EXECUTE_READ = 0x20;
|
||||
constexpr DWORD PAGE_EXECUTE_READWRITE = 0x40;
|
||||
constexpr DWORD PAGE_EXECUTE_WRITECOPY = 0x80;
|
||||
constexpr DWORD PAGE_GUARD = 0x100;
|
||||
constexpr DWORD PAGE_NOCACHE = 0x200;
|
||||
constexpr DWORD PAGE_WRITECOMBINE = 0x400;
|
||||
|
||||
// Allocation type and memory state constants
|
||||
constexpr DWORD MEM_COMMIT = 0x00001000;
|
||||
constexpr DWORD MEM_RESERVE = 0x00002000;
|
||||
constexpr DWORD MEM_DECOMMIT = 0x00004000;
|
||||
constexpr DWORD MEM_RELEASE = 0x00008000;
|
||||
constexpr DWORD MEM_FREE = 0x00010000;
|
||||
constexpr DWORD MEM_PRIVATE = 0x00020000;
|
||||
constexpr DWORD MEM_MAPPED = 0x00040000;
|
||||
constexpr DWORD MEM_RESET = 0x00080000;
|
||||
constexpr DWORD MEM_TOP_DOWN = 0x00100000;
|
||||
constexpr DWORD MEM_WRITE_WATCH = 0x00200000;
|
||||
constexpr DWORD MEM_PHYSICAL = 0x00400000;
|
||||
constexpr DWORD MEM_RESET_UNDO = 0x01000000;
|
||||
constexpr DWORD MEM_LARGE_PAGES = 0x20000000;
|
||||
constexpr DWORD MEM_COALESCE_PLACEHOLDERS = 0x00000001;
|
||||
constexpr DWORD MEM_PRESERVE_PLACEHOLDER = 0x00000002;
|
||||
constexpr DWORD MEM_IMAGE = 0x01000000;
|
||||
|
||||
// File mapping access flags
|
||||
constexpr DWORD FILE_MAP_COPY = 0x00000001;
|
||||
constexpr DWORD FILE_MAP_WRITE = 0x00000002;
|
||||
constexpr DWORD FILE_MAP_READ = 0x00000004;
|
||||
constexpr DWORD FILE_MAP_EXECUTE = 0x00000020;
|
||||
constexpr DWORD FILE_MAP_ALL_ACCESS = 0x000f001f;
|
||||
using LPCH = char *;
|
||||
using LPWCH = uint16_t *;
|
||||
using BOOL = int;
|
||||
@ -241,6 +279,13 @@ struct Executable {
|
||||
bool loadPE(FILE *file, bool exec);
|
||||
bool resolveImports();
|
||||
|
||||
struct SectionInfo {
|
||||
uintptr_t base = 0;
|
||||
size_t size = 0;
|
||||
DWORD protect = PAGE_NOACCESS;
|
||||
DWORD characteristics = 0;
|
||||
};
|
||||
|
||||
void *imageBase = nullptr;
|
||||
size_t imageSize = 0;
|
||||
void *entryPoint = nullptr;
|
||||
@ -259,6 +304,7 @@ struct Executable {
|
||||
bool execMapped = false;
|
||||
bool importsResolved = false;
|
||||
bool importsResolving = false;
|
||||
std::vector<SectionInfo> sections;
|
||||
|
||||
bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name, std::optional<uint16_t> language,
|
||||
ResourceLocation &out) const;
|
||||
@ -296,6 +342,7 @@ struct ModuleInfo {
|
||||
ModuleInfo *registerProcessModule(std::unique_ptr<Executable> executable, std::filesystem::path resolvedPath,
|
||||
std::string originalName);
|
||||
Executable *executableFromModule(HMODULE module);
|
||||
ModuleInfo *moduleInfoFromAddress(void *addr);
|
||||
|
||||
/**
|
||||
* HMODULE will be `nullptr` or `mainModule->imageBase` if it's the main module,
|
||||
|
@ -15,13 +15,13 @@
|
||||
#include <mutex>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t kVirtualAllocationGranularity = 64 * 1024;
|
||||
constexpr uintptr_t kProcessAddressLimit = 0x80000000;
|
||||
|
||||
struct MappingObject {
|
||||
int fd = -1;
|
||||
@ -33,12 +33,18 @@ struct MappingObject {
|
||||
};
|
||||
|
||||
struct ViewInfo {
|
||||
void *mapBase = nullptr;
|
||||
size_t mapLength = 0;
|
||||
uintptr_t viewBase = 0;
|
||||
size_t viewLength = 0;
|
||||
uintptr_t allocationBase = 0;
|
||||
size_t allocationLength = 0;
|
||||
MappingObject *owner = nullptr;
|
||||
DWORD protect = PAGE_NOACCESS;
|
||||
DWORD allocationProtect = PAGE_NOACCESS;
|
||||
DWORD type = MEM_PRIVATE;
|
||||
};
|
||||
|
||||
std::unordered_map<void *, ViewInfo> g_viewInfo;
|
||||
std::map<uintptr_t, ViewInfo> g_viewInfo;
|
||||
std::mutex g_viewInfoMutex;
|
||||
|
||||
void closeMappingIfPossible(MappingObject *mapping) {
|
||||
if (!mapping) {
|
||||
@ -157,6 +163,69 @@ bool rangeWithinRegion(const VirtualAllocation ®ion, uintptr_t start, size_t
|
||||
return (start + length) <= regionEnd(region);
|
||||
}
|
||||
|
||||
DWORD desiredAccessToProtect(DWORD desiredAccess, DWORD mappingProtect) {
|
||||
DWORD access = desiredAccess;
|
||||
if ((access & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS) {
|
||||
access |= FILE_MAP_READ | FILE_MAP_WRITE;
|
||||
}
|
||||
bool wantExecute = (access & FILE_MAP_EXECUTE) != 0;
|
||||
bool wantWrite = (access & FILE_MAP_WRITE) != 0;
|
||||
bool wantCopy = (access & FILE_MAP_COPY) != 0;
|
||||
bool wantRead = (access & (FILE_MAP_READ | FILE_MAP_WRITE | FILE_MAP_COPY)) != 0;
|
||||
if (wantCopy) {
|
||||
wantWrite = true;
|
||||
}
|
||||
const bool supportsWrite =
|
||||
mappingProtect == PAGE_READWRITE || mappingProtect == PAGE_EXECUTE_READWRITE || mappingProtect == PAGE_WRITECOPY ||
|
||||
mappingProtect == PAGE_EXECUTE_WRITECOPY;
|
||||
const bool supportsCopy = mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY;
|
||||
|
||||
if (wantCopy && !supportsCopy) {
|
||||
wantCopy = false;
|
||||
}
|
||||
if (wantWrite && !supportsWrite) {
|
||||
if (supportsCopy) {
|
||||
wantCopy = true;
|
||||
wantWrite = false;
|
||||
} else {
|
||||
wantWrite = false;
|
||||
}
|
||||
}
|
||||
if (!wantRead && (mappingProtect == PAGE_READONLY || mappingProtect == PAGE_EXECUTE_READ ||
|
||||
mappingProtect == PAGE_WRITECOPY || mappingProtect == PAGE_EXECUTE_WRITECOPY)) {
|
||||
wantRead = true;
|
||||
}
|
||||
|
||||
DWORD protect = PAGE_NOACCESS;
|
||||
if (wantCopy && supportsCopy) {
|
||||
protect = wantExecute ? PAGE_EXECUTE_WRITECOPY : PAGE_WRITECOPY;
|
||||
} else if (wantExecute) {
|
||||
if (wantWrite) {
|
||||
protect = PAGE_EXECUTE_READWRITE;
|
||||
} else if (wantRead) {
|
||||
protect = PAGE_EXECUTE_READ;
|
||||
} else {
|
||||
protect = PAGE_EXECUTE;
|
||||
}
|
||||
} else {
|
||||
if (wantWrite) {
|
||||
protect = PAGE_READWRITE;
|
||||
} else if (wantRead) {
|
||||
protect = PAGE_READONLY;
|
||||
}
|
||||
}
|
||||
if ((mappingProtect & PAGE_NOCACHE) != 0) {
|
||||
protect |= PAGE_NOCACHE;
|
||||
}
|
||||
if ((mappingProtect & PAGE_GUARD) != 0) {
|
||||
protect |= PAGE_GUARD;
|
||||
}
|
||||
if ((mappingProtect & PAGE_WRITECOMBINE) != 0) {
|
||||
protect |= PAGE_WRITECOMBINE;
|
||||
}
|
||||
return protect;
|
||||
}
|
||||
|
||||
void markCommitted(VirtualAllocation ®ion, uintptr_t start, size_t length, DWORD protect) {
|
||||
if (length == 0) {
|
||||
return;
|
||||
@ -181,6 +250,174 @@ void markDecommitted(VirtualAllocation ®ion, uintptr_t start, size_t length)
|
||||
}
|
||||
}
|
||||
|
||||
bool moduleRegionForAddress(uintptr_t pageBase, MEMORY_BASIC_INFORMATION &info) {
|
||||
if (pageBase == 0) {
|
||||
return false;
|
||||
}
|
||||
wibo::ModuleInfo *module = wibo::moduleInfoFromAddress(reinterpret_cast<void *>(pageBase));
|
||||
if (!module || !module->executable) {
|
||||
return false;
|
||||
}
|
||||
const auto §ions = module->executable->sections;
|
||||
if (sections.empty()) {
|
||||
return false;
|
||||
}
|
||||
size_t matchIndex = sections.size();
|
||||
for (size_t i = 0; i < sections.size(); ++i) {
|
||||
const auto §ion = sections[i];
|
||||
if (pageBase >= section.base && pageBase < section.base + section.size) {
|
||||
matchIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchIndex == sections.size()) {
|
||||
return false;
|
||||
}
|
||||
uintptr_t blockStart = sections[matchIndex].base;
|
||||
uintptr_t blockEnd = sections[matchIndex].base + sections[matchIndex].size;
|
||||
DWORD blockProtect = sections[matchIndex].protect;
|
||||
for (size_t prev = matchIndex; prev > 0; ) {
|
||||
--prev;
|
||||
const auto §ion = sections[prev];
|
||||
if (section.base + section.size != blockStart) {
|
||||
break;
|
||||
}
|
||||
if (section.protect != blockProtect) {
|
||||
break;
|
||||
}
|
||||
blockStart = section.base;
|
||||
}
|
||||
for (size_t next = matchIndex + 1; next < sections.size(); ++next) {
|
||||
const auto §ion = sections[next];
|
||||
if (section.base != blockEnd) {
|
||||
break;
|
||||
}
|
||||
if (section.protect != blockProtect) {
|
||||
break;
|
||||
}
|
||||
blockEnd = section.base + section.size;
|
||||
}
|
||||
info.BaseAddress = reinterpret_cast<void *>(blockStart);
|
||||
info.AllocationBase = module->executable->imageBase;
|
||||
info.AllocationProtect = blockProtect;
|
||||
info.RegionSize = blockEnd > blockStart ? blockEnd - blockStart : 0;
|
||||
info.State = MEM_COMMIT;
|
||||
info.Protect = blockProtect;
|
||||
info.Type = MEM_IMAGE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mappedViewRegionForAddress(uintptr_t request, uintptr_t pageBase, MEMORY_BASIC_INFORMATION &info) {
|
||||
std::lock_guard<std::mutex> guard(g_viewInfoMutex);
|
||||
if (g_viewInfo.empty()) {
|
||||
return false;
|
||||
}
|
||||
const size_t pageSize = systemPageSize();
|
||||
for (const auto &entry : g_viewInfo) {
|
||||
const ViewInfo &view = entry.second;
|
||||
if (view.viewLength == 0) {
|
||||
continue;
|
||||
}
|
||||
uintptr_t allocationStart = view.allocationBase;
|
||||
uintptr_t allocationEnd = allocationStart + view.allocationLength;
|
||||
if (pageBase < allocationStart || pageBase >= allocationEnd) {
|
||||
continue;
|
||||
}
|
||||
uintptr_t viewStart = view.viewBase;
|
||||
uintptr_t viewEnd = view.viewBase + view.viewLength;
|
||||
if (request != 0 && (request < viewStart || request >= viewEnd)) {
|
||||
continue;
|
||||
}
|
||||
uintptr_t blockStart = viewStart;
|
||||
uintptr_t blockEnd = alignUp(viewEnd, pageSize);
|
||||
info.BaseAddress = reinterpret_cast<void *>(blockStart);
|
||||
info.AllocationBase = reinterpret_cast<void *>(view.viewBase);
|
||||
info.AllocationProtect = view.allocationProtect;
|
||||
info.RegionSize = blockEnd > blockStart ? blockEnd - blockStart : 0;
|
||||
info.State = MEM_COMMIT;
|
||||
info.Protect = view.protect;
|
||||
info.Type = view.type;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool virtualAllocationRegionForAddress(uintptr_t pageBase, MEMORY_BASIC_INFORMATION &info) {
|
||||
const size_t pageSize = systemPageSize();
|
||||
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
|
||||
VirtualAllocation *region = lookupRegion(pageBase);
|
||||
if (!region) {
|
||||
uintptr_t regionStart = pageBase;
|
||||
uintptr_t regionEnd = regionStart;
|
||||
auto next = g_virtualAllocations.lower_bound(pageBase);
|
||||
if (next != g_virtualAllocations.end()) {
|
||||
regionEnd = next->second.base;
|
||||
} else {
|
||||
regionEnd = kProcessAddressLimit;
|
||||
}
|
||||
if (regionEnd <= regionStart) {
|
||||
regionEnd = regionStart + pageSize;
|
||||
}
|
||||
lock.unlock();
|
||||
info.BaseAddress = reinterpret_cast<void *>(regionStart);
|
||||
info.AllocationBase = nullptr;
|
||||
info.AllocationProtect = 0;
|
||||
info.RegionSize = regionEnd - regionStart;
|
||||
info.State = MEM_FREE;
|
||||
info.Protect = PAGE_NOACCESS;
|
||||
info.Type = 0;
|
||||
return true;
|
||||
}
|
||||
const uintptr_t regionLimit = region->base + region->size;
|
||||
const size_t pageIndex = (pageBase - region->base) / pageSize;
|
||||
if (pageIndex >= region->pageProtect.size()) {
|
||||
return false;
|
||||
}
|
||||
const DWORD pageProtect = region->pageProtect[pageIndex];
|
||||
const bool committed = pageProtect != 0;
|
||||
uintptr_t blockStart = pageBase;
|
||||
uintptr_t blockEnd = pageBase + pageSize;
|
||||
while (blockStart > region->base) {
|
||||
size_t idx = (blockStart - region->base) / pageSize - 1;
|
||||
DWORD protect = region->pageProtect[idx];
|
||||
bool pageCommitted = protect != 0;
|
||||
if (pageCommitted != committed) {
|
||||
break;
|
||||
}
|
||||
if (committed && protect != pageProtect) {
|
||||
break;
|
||||
}
|
||||
blockStart -= pageSize;
|
||||
}
|
||||
while (blockEnd < regionLimit) {
|
||||
size_t idx = (blockEnd - region->base) / pageSize;
|
||||
if (idx >= region->pageProtect.size()) {
|
||||
break;
|
||||
}
|
||||
DWORD protect = region->pageProtect[idx];
|
||||
bool pageCommitted = protect != 0;
|
||||
if (pageCommitted != committed) {
|
||||
break;
|
||||
}
|
||||
if (committed && protect != pageProtect) {
|
||||
break;
|
||||
}
|
||||
blockEnd += pageSize;
|
||||
}
|
||||
uintptr_t allocationBase = region->base;
|
||||
DWORD allocationProtect = region->allocationProtect != 0 ? region->allocationProtect : PAGE_NOACCESS;
|
||||
DWORD finalProtect = committed ? pageProtect : PAGE_NOACCESS;
|
||||
lock.unlock();
|
||||
info.BaseAddress = reinterpret_cast<void *>(blockStart);
|
||||
info.AllocationBase = reinterpret_cast<void *>(allocationBase);
|
||||
info.AllocationProtect = allocationProtect;
|
||||
info.RegionSize = blockEnd - blockStart;
|
||||
info.State = committed ? MEM_COMMIT : MEM_RESERVE;
|
||||
info.Protect = finalProtect;
|
||||
info.Type = MEM_PRIVATE;
|
||||
return true;
|
||||
}
|
||||
|
||||
void *alignedReserve(size_t length, int prot, int flags) {
|
||||
const size_t granularity = kVirtualAllocationGranularity;
|
||||
const size_t request = length + granularity;
|
||||
@ -335,11 +572,14 @@ static LPVOID mapViewOfFileInternal(MappingObject *mapping, DWORD dwDesiredAcces
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int prot = PROT_READ;
|
||||
bool wantWrite = (dwDesiredAccess & FILE_MAP_WRITE) != 0;
|
||||
bool wantExecute = (dwDesiredAccess & FILE_MAP_EXECUTE) != 0;
|
||||
bool wantCopy = (dwDesiredAccess & FILE_MAP_COPY) != 0;
|
||||
|
||||
bool wantAllAccess = (dwDesiredAccess & FILE_MAP_ALL_ACCESS) == FILE_MAP_ALL_ACCESS;
|
||||
if (wantAllAccess) {
|
||||
wantWrite = true;
|
||||
}
|
||||
int prot = PROT_READ;
|
||||
if (mapping->protect == PAGE_READWRITE) {
|
||||
if (wantWrite || wantCopy) {
|
||||
prot |= PROT_WRITE;
|
||||
@ -358,7 +598,7 @@ static LPVOID mapViewOfFileInternal(MappingObject *mapping, DWORD dwDesiredAcces
|
||||
}
|
||||
|
||||
int flags = (mapping->anonymous ? MAP_ANONYMOUS : 0) | (wantCopy ? MAP_PRIVATE : MAP_SHARED);
|
||||
const size_t pageSize = static_cast<size_t>(sysconf(_SC_PAGESIZE));
|
||||
const size_t pageSize = systemPageSize();
|
||||
off_t alignedOffset = mapping->anonymous ? 0 : static_cast<off_t>(offset & ~static_cast<uint64_t>(pageSize - 1));
|
||||
size_t offsetDelta = static_cast<size_t>(offset - static_cast<uint64_t>(alignedOffset));
|
||||
uint64_t requestedLength = length + offsetDelta;
|
||||
@ -415,7 +655,24 @@ static LPVOID mapViewOfFileInternal(MappingObject *mapping, DWORD dwDesiredAcces
|
||||
wibo::lastError = ERROR_INVALID_ADDRESS;
|
||||
return nullptr;
|
||||
}
|
||||
g_viewInfo[viewPtr] = ViewInfo{mapBase, mapLength, mapping};
|
||||
uintptr_t viewLength = static_cast<uintptr_t>(length);
|
||||
uintptr_t alignedViewLength = alignUp(viewLength, pageSize);
|
||||
if (alignedViewLength == std::numeric_limits<uintptr_t>::max()) {
|
||||
alignedViewLength = viewLength;
|
||||
}
|
||||
ViewInfo view{};
|
||||
view.viewBase = reinterpret_cast<uintptr_t>(viewPtr);
|
||||
view.viewLength = static_cast<size_t>(alignedViewLength);
|
||||
view.allocationBase = reinterpret_cast<uintptr_t>(mapBase);
|
||||
view.allocationLength = mapLength;
|
||||
view.owner = mapping;
|
||||
view.protect = desiredAccessToProtect(dwDesiredAccess, mapping->protect);
|
||||
view.allocationProtect = mapping->protect;
|
||||
view.type = MEM_MAPPED;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(g_viewInfoMutex);
|
||||
g_viewInfo[view.viewBase] = view;
|
||||
}
|
||||
mapping->refCount++;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return viewPtr;
|
||||
@ -453,15 +710,18 @@ LPVOID WIN_FUNC MapViewOfFileEx(HANDLE hFileMappingObject, DWORD dwDesiredAccess
|
||||
|
||||
BOOL WIN_FUNC UnmapViewOfFile(LPCVOID lpBaseAddress) {
|
||||
DEBUG_LOG("UnmapViewOfFile(%p)\n", lpBaseAddress);
|
||||
auto it = g_viewInfo.find(const_cast<void *>(lpBaseAddress));
|
||||
std::unique_lock<std::mutex> lock(g_viewInfoMutex);
|
||||
auto it = g_viewInfo.find(reinterpret_cast<uintptr_t>(lpBaseAddress));
|
||||
if (it == g_viewInfo.end()) {
|
||||
lock.unlock();
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return FALSE;
|
||||
}
|
||||
ViewInfo info = it->second;
|
||||
g_viewInfo.erase(it);
|
||||
if (info.mapBase && info.mapLength) {
|
||||
munmap(info.mapBase, info.mapLength);
|
||||
lock.unlock();
|
||||
if (info.allocationLength != 0) {
|
||||
munmap(reinterpret_cast<void *>(info.allocationBase), info.allocationLength);
|
||||
}
|
||||
if (info.owner && info.owner->refCount > 0) {
|
||||
info.owner->refCount--;
|
||||
@ -823,7 +1083,7 @@ BOOL WIN_FUNC VirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect
|
||||
|
||||
SIZE_T WIN_FUNC VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) {
|
||||
DEBUG_LOG("VirtualQuery(%p, %p, %zu)\n", lpAddress, lpBuffer, dwLength);
|
||||
if (!lpBuffer || dwLength < sizeof(MEMORY_BASIC_INFORMATION) || !lpAddress) {
|
||||
if (!lpBuffer || dwLength < sizeof(MEMORY_BASIC_INFORMATION)) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
DEBUG_LOG("-> ERROR_INVALID_PARAMETER\n");
|
||||
return 0;
|
||||
@ -831,53 +1091,34 @@ SIZE_T WIN_FUNC VirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuff
|
||||
|
||||
std::memset(lpBuffer, 0, sizeof(MEMORY_BASIC_INFORMATION));
|
||||
const size_t pageSize = systemPageSize();
|
||||
uintptr_t request = reinterpret_cast<uintptr_t>(lpAddress);
|
||||
uintptr_t request = lpAddress ? reinterpret_cast<uintptr_t>(lpAddress) : 0;
|
||||
uintptr_t pageBase = alignDown(request, pageSize);
|
||||
|
||||
std::unique_lock<std::mutex> lock(g_virtualAllocMutex);
|
||||
VirtualAllocation *region = lookupRegion(pageBase);
|
||||
if (!region) {
|
||||
wibo::lastError = ERROR_INVALID_ADDRESS;
|
||||
DEBUG_LOG("-> ERROR_INVALID_ADDRESS\n");
|
||||
if (pageBase >= kProcessAddressLimit) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
DEBUG_LOG("-> ERROR_INVALID_PARAMETER (beyond address space)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t pageIndex = (pageBase - region->base) / pageSize;
|
||||
if (pageIndex >= region->pageProtect.size()) {
|
||||
wibo::lastError = ERROR_INVALID_ADDRESS;
|
||||
DEBUG_LOG("-> ERROR_INVALID_ADDRESS\n");
|
||||
return 0;
|
||||
MEMORY_BASIC_INFORMATION info{};
|
||||
if (moduleRegionForAddress(pageBase, info)) {
|
||||
*lpBuffer = info;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return sizeof(MEMORY_BASIC_INFORMATION);
|
||||
}
|
||||
const bool committed = region->pageProtect[pageIndex] != 0;
|
||||
uintptr_t blockStart = pageBase;
|
||||
uintptr_t blockEnd = pageBase + pageSize;
|
||||
while (blockStart > region->base) {
|
||||
size_t idx = (blockStart - region->base) / pageSize - 1;
|
||||
bool pageCommitted = region->pageProtect[idx] != 0;
|
||||
if (pageCommitted != committed) {
|
||||
break;
|
||||
}
|
||||
blockStart -= pageSize;
|
||||
if (mappedViewRegionForAddress(request, pageBase, info)) {
|
||||
*lpBuffer = info;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return sizeof(MEMORY_BASIC_INFORMATION);
|
||||
}
|
||||
while (blockEnd < region->base + region->size) {
|
||||
size_t idx = (blockEnd - region->base) / pageSize;
|
||||
bool pageCommitted = region->pageProtect[idx] != 0;
|
||||
if (pageCommitted != committed) {
|
||||
break;
|
||||
}
|
||||
blockEnd += pageSize;
|
||||
if (virtualAllocationRegionForAddress(pageBase, info)) {
|
||||
*lpBuffer = info;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return sizeof(MEMORY_BASIC_INFORMATION);
|
||||
}
|
||||
|
||||
lpBuffer->BaseAddress = reinterpret_cast<PVOID>(blockStart);
|
||||
lpBuffer->AllocationBase = reinterpret_cast<PVOID>(region->base);
|
||||
lpBuffer->AllocationProtect = region->allocationProtect != 0 ? region->allocationProtect : PAGE_NOACCESS;
|
||||
lpBuffer->RegionSize = blockEnd - blockStart;
|
||||
lpBuffer->State = committed ? MEM_COMMIT : MEM_RESERVE;
|
||||
lpBuffer->Protect = committed ? region->pageProtect[pageIndex] : 0;
|
||||
lpBuffer->Type = MEM_PRIVATE;
|
||||
lock.unlock();
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return sizeof(MEMORY_BASIC_INFORMATION);
|
||||
wibo::lastError = ERROR_INVALID_ADDRESS;
|
||||
DEBUG_LOG("-> ERROR_INVALID_ADDRESS\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL WIN_FUNC GetProcessWorkingSetSize(HANDLE hProcess, PSIZE_T lpMinimumWorkingSetSize,
|
||||
|
@ -3,36 +3,6 @@
|
||||
#include "common.h"
|
||||
#include "minwinbase.h"
|
||||
|
||||
// Allocation type flags
|
||||
constexpr DWORD MEM_COMMIT = 0x00001000;
|
||||
constexpr DWORD MEM_RESERVE = 0x00002000;
|
||||
constexpr DWORD MEM_DECOMMIT = 0x00004000;
|
||||
constexpr DWORD MEM_RELEASE = 0x00008000;
|
||||
constexpr DWORD MEM_RESET = 0x00080000;
|
||||
constexpr DWORD MEM_RESET_UNDO = 0x01000000;
|
||||
constexpr DWORD MEM_TOP_DOWN = 0x00100000;
|
||||
constexpr DWORD MEM_WRITE_WATCH = 0x00200000;
|
||||
constexpr DWORD MEM_PHYSICAL = 0x00400000;
|
||||
constexpr DWORD MEM_PRIVATE = 0x00020000;
|
||||
constexpr DWORD MEM_LARGE_PAGES = 0x20000000;
|
||||
constexpr DWORD MEM_COALESCE_PLACEHOLDERS = 0x00000001;
|
||||
constexpr DWORD MEM_PRESERVE_PLACEHOLDER = 0x00000002;
|
||||
|
||||
// Page protection constants
|
||||
constexpr DWORD PAGE_NOACCESS = 0x01;
|
||||
constexpr DWORD PAGE_READONLY = 0x02;
|
||||
constexpr DWORD PAGE_READWRITE = 0x04;
|
||||
constexpr DWORD PAGE_WRITECOPY = 0x08;
|
||||
constexpr DWORD PAGE_EXECUTE = 0x10;
|
||||
constexpr DWORD PAGE_EXECUTE_READ = 0x20;
|
||||
constexpr DWORD PAGE_EXECUTE_READWRITE = 0x40;
|
||||
constexpr DWORD PAGE_EXECUTE_WRITECOPY = 0x80;
|
||||
|
||||
constexpr DWORD FILE_MAP_COPY = 0x00000001;
|
||||
constexpr DWORD FILE_MAP_WRITE = 0x00000002;
|
||||
constexpr DWORD FILE_MAP_READ = 0x00000004;
|
||||
constexpr DWORD FILE_MAP_EXECUTE = 0x00000020;
|
||||
|
||||
struct MEMORY_BASIC_INFORMATION {
|
||||
PVOID BaseAddress;
|
||||
PVOID AllocationBase;
|
||||
|
@ -178,18 +178,6 @@ NTSTATUS WIN_FUNC NtReadFile(HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE Ap
|
||||
return status;
|
||||
}
|
||||
|
||||
#define PAGE_NOACCESS 0x1
|
||||
#define PAGE_READONLY 0x2
|
||||
#define PAGE_READWRITE 0x4
|
||||
#define PAGE_WRITECOPY 0x8
|
||||
#define PAGE_EXECUTE 0x10
|
||||
#define PAGE_EXECUTE_READ 0x20
|
||||
#define PAGE_EXECUTE_READWRITE 0x40
|
||||
#define PAGE_EXECUTE_WRITECOPY 0x80
|
||||
#define PAGE_GUARD 0x100
|
||||
#define PAGE_NOCACHE 0x200
|
||||
#define PAGE_WRITECOMBINE 0x400
|
||||
|
||||
NTSTATUS WIN_FUNC NtAllocateVirtualMemory(HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits,
|
||||
PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect) {
|
||||
DEBUG_LOG("NtAllocateVirtualMemory(%p, %p, %lu, %p, %lu, %lu) ", ProcessHandle, BaseAddress, ZeroBits, RegionSize,
|
||||
|
85
loader.cpp
85
loader.cpp
@ -114,6 +114,61 @@ struct PEBaseRelocationBlock {
|
||||
constexpr uint16_t IMAGE_REL_BASED_ABSOLUTE = 0;
|
||||
constexpr uint16_t IMAGE_REL_BASED_HIGHLOW = 3;
|
||||
|
||||
constexpr uint32_t IMAGE_SCN_MEM_EXECUTE = 0x20000000;
|
||||
constexpr uint32_t IMAGE_SCN_MEM_READ = 0x40000000;
|
||||
constexpr uint32_t IMAGE_SCN_MEM_WRITE = 0x80000000;
|
||||
constexpr uint32_t IMAGE_SCN_MEM_NOT_CACHED = 0x04000000;
|
||||
|
||||
static uintptr_t alignDown(uintptr_t value, size_t alignment) {
|
||||
if (alignment == 0) {
|
||||
return value;
|
||||
}
|
||||
return value - (value % alignment);
|
||||
}
|
||||
|
||||
static uintptr_t alignUp(uintptr_t value, size_t alignment) {
|
||||
if (alignment == 0) {
|
||||
return value;
|
||||
}
|
||||
const uintptr_t remainder = value % alignment;
|
||||
if (remainder == 0) {
|
||||
return value;
|
||||
}
|
||||
if (value > std::numeric_limits<uintptr_t>::max() - (alignment - remainder)) {
|
||||
return std::numeric_limits<uintptr_t>::max();
|
||||
}
|
||||
return value + (alignment - remainder);
|
||||
}
|
||||
|
||||
static DWORD sectionProtectFromCharacteristics(uint32_t characteristics) {
|
||||
const bool executable = (characteristics & IMAGE_SCN_MEM_EXECUTE) != 0;
|
||||
bool readable = (characteristics & IMAGE_SCN_MEM_READ) != 0;
|
||||
const bool writable = (characteristics & IMAGE_SCN_MEM_WRITE) != 0;
|
||||
if (!readable && !writable && !executable) {
|
||||
readable = true;
|
||||
}
|
||||
DWORD protect = PAGE_NOACCESS;
|
||||
if (executable) {
|
||||
if (writable) {
|
||||
protect = PAGE_EXECUTE_READWRITE;
|
||||
} else if (readable) {
|
||||
protect = PAGE_EXECUTE_READ;
|
||||
} else {
|
||||
protect = PAGE_EXECUTE;
|
||||
}
|
||||
} else {
|
||||
if (writable) {
|
||||
protect = PAGE_READWRITE;
|
||||
} else if (readable) {
|
||||
protect = PAGE_READONLY;
|
||||
}
|
||||
}
|
||||
if ((characteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0) {
|
||||
protect |= PAGE_NOCACHE;
|
||||
}
|
||||
return protect;
|
||||
}
|
||||
|
||||
uint16_t read16(FILE *file) {
|
||||
uint16_t v = 0;
|
||||
fread(&v, 2, 1, file);
|
||||
@ -164,6 +219,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
|
||||
long pageSize = sysconf(_SC_PAGE_SIZE);
|
||||
DEBUG_LOG("Page size: %x\n", (unsigned int)pageSize);
|
||||
const size_t pageSizeValue = pageSize > 0 ? static_cast<size_t>(pageSize) : static_cast<size_t>(4096);
|
||||
|
||||
preferredImageBase = header32.imageBase;
|
||||
exportDirectoryRVA = header32.exportTable.virtualAddress;
|
||||
@ -195,6 +251,16 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
}
|
||||
relocationDelta = (intptr_t)((uintptr_t)imageBase - (uintptr_t)header32.imageBase);
|
||||
memset(imageBase, 0, header32.sizeOfImage);
|
||||
sections.clear();
|
||||
uintptr_t imageBaseAddr = reinterpret_cast<uintptr_t>(imageBase);
|
||||
uintptr_t headerSpan = alignUp(static_cast<uintptr_t>(header32.sizeOfHeaders), pageSizeValue);
|
||||
if (headerSpan != 0) {
|
||||
Executable::SectionInfo headerInfo{};
|
||||
headerInfo.base = imageBaseAddr;
|
||||
headerInfo.size = static_cast<size_t>(headerSpan);
|
||||
headerInfo.protect = PAGE_READONLY;
|
||||
sections.push_back(headerInfo);
|
||||
}
|
||||
|
||||
// Read the sections
|
||||
fseek(file, offsetToPE + sizeof header + header.sizeOfOptionalHeader, SEEK_SET);
|
||||
@ -222,7 +288,26 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
rsrcBase = sectionBase;
|
||||
rsrcSize = std::max(section.virtualSize, section.sizeOfRawData);
|
||||
}
|
||||
|
||||
size_t sectionSpan = std::max(section.virtualSize, section.sizeOfRawData);
|
||||
if (sectionSpan != 0) {
|
||||
uintptr_t sectionStart = alignDown(imageBaseAddr + static_cast<uintptr_t>(section.virtualAddress), pageSizeValue);
|
||||
uintptr_t sectionEnd = alignUp(imageBaseAddr + static_cast<uintptr_t>(section.virtualAddress) +
|
||||
static_cast<uintptr_t>(sectionSpan),
|
||||
pageSizeValue);
|
||||
if (sectionEnd > sectionStart) {
|
||||
Executable::SectionInfo sectionInfo{};
|
||||
sectionInfo.base = sectionStart;
|
||||
sectionInfo.size = static_cast<size_t>(sectionEnd - sectionStart);
|
||||
sectionInfo.protect = sectionProtectFromCharacteristics(section.characteristics);
|
||||
sectionInfo.characteristics = section.characteristics;
|
||||
sections.push_back(sectionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(sections.begin(), sections.end(), [](const SectionInfo &lhs, const SectionInfo &rhs) {
|
||||
return lhs.base < rhs.base;
|
||||
});
|
||||
|
||||
if (exec && relocationDelta != 0) {
|
||||
if (relocationDirectoryRVA == 0 || relocationDirectorySize == 0) {
|
||||
|
@ -711,6 +711,14 @@ std::optional<std::filesystem::path> dllDirectoryOverride() {
|
||||
return reg->dllDirectory;
|
||||
}
|
||||
|
||||
ModuleInfo *moduleInfoFromAddress(void *addr) {
|
||||
if (!addr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto reg = registry();
|
||||
return moduleFromAddress(*reg, addr);
|
||||
}
|
||||
|
||||
void registerOnExitTable(void *table) {
|
||||
if (!table)
|
||||
return;
|
||||
|
104
test/test_virtualquery.c
Normal file
104
test/test_virtualquery.c
Normal file
@ -0,0 +1,104 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "test_assert.h"
|
||||
|
||||
static SIZE_T query_page_size(void) {
|
||||
SYSTEM_INFO info;
|
||||
GetSystemInfo(&info);
|
||||
return info.dwPageSize;
|
||||
}
|
||||
|
||||
static void test_null_address(void) {
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
SIZE_T got = VirtualQuery(NULL, &mbi, sizeof(mbi));
|
||||
TEST_CHECK_EQ(sizeof(mbi), got);
|
||||
TEST_CHECK(mbi.BaseAddress == (PVOID)0);
|
||||
TEST_CHECK(mbi.AllocationBase == NULL);
|
||||
TEST_CHECK_EQ(0u, mbi.AllocationProtect);
|
||||
TEST_CHECK_EQ(MEM_FREE, mbi.State);
|
||||
TEST_CHECK_EQ(PAGE_NOACCESS, mbi.Protect);
|
||||
TEST_CHECK_EQ(0u, mbi.Type);
|
||||
TEST_CHECK(mbi.RegionSize >= query_page_size());
|
||||
}
|
||||
|
||||
static void test_module_region(void) {
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
void *address = (void *)&test_module_region;
|
||||
SIZE_T got = VirtualQuery(address, &mbi, sizeof(mbi));
|
||||
TEST_CHECK_EQ(sizeof(mbi), got);
|
||||
TEST_CHECK_EQ(MEM_COMMIT, mbi.State);
|
||||
TEST_CHECK_EQ(MEM_IMAGE, mbi.Type);
|
||||
TEST_CHECK(mbi.RegionSize >= query_page_size());
|
||||
HMODULE module = GetModuleHandleA(NULL);
|
||||
TEST_CHECK(module != NULL);
|
||||
TEST_CHECK((HMODULE)mbi.AllocationBase == module);
|
||||
TEST_CHECK(mbi.AllocationProtect != 0);
|
||||
TEST_CHECK((mbi.Protect & (PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)) != 0);
|
||||
TEST_CHECK((uintptr_t)address >= (uintptr_t)mbi.BaseAddress);
|
||||
TEST_CHECK((uintptr_t)address < (uintptr_t)mbi.BaseAddress + mbi.RegionSize);
|
||||
}
|
||||
|
||||
static void test_anonymous_mapping(void) {
|
||||
const SIZE_T page = query_page_size();
|
||||
HANDLE mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)page, NULL);
|
||||
TEST_CHECK(mapping != NULL);
|
||||
uint8_t *view = (uint8_t *)MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, page);
|
||||
TEST_CHECK(view != NULL);
|
||||
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
SIZE_T got = VirtualQuery(view, &mbi, sizeof(mbi));
|
||||
TEST_CHECK_EQ(sizeof(mbi), got);
|
||||
TEST_CHECK_EQ(MEM_COMMIT, mbi.State);
|
||||
TEST_CHECK_EQ(MEM_MAPPED, mbi.Type);
|
||||
TEST_CHECK_EQ(PAGE_READWRITE, mbi.AllocationProtect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE));
|
||||
TEST_CHECK_EQ(PAGE_READWRITE, mbi.Protect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE));
|
||||
TEST_CHECK(mbi.RegionSize >= page);
|
||||
TEST_CHECK((uint8_t *)mbi.BaseAddress == view);
|
||||
TEST_CHECK((uint8_t *)mbi.AllocationBase == view);
|
||||
|
||||
TEST_CHECK(UnmapViewOfFile(view));
|
||||
TEST_CHECK(CloseHandle(mapping));
|
||||
}
|
||||
|
||||
static void test_file_mapping(void) {
|
||||
const SIZE_T page = query_page_size();
|
||||
HANDLE file = CreateFileA("test_virtualquery.tmp", GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
|
||||
TEST_CHECK(file != INVALID_HANDLE_VALUE);
|
||||
|
||||
DWORD newPos = SetFilePointer(file, (LONG)page, NULL, FILE_BEGIN);
|
||||
TEST_CHECK(newPos != INVALID_SET_FILE_POINTER);
|
||||
TEST_CHECK(SetEndOfFile(file));
|
||||
TEST_CHECK(SetFilePointer(file, 0, NULL, FILE_BEGIN) != INVALID_SET_FILE_POINTER);
|
||||
|
||||
HANDLE mapping = CreateFileMappingA(file, NULL, PAGE_READONLY, 0, 0, NULL);
|
||||
TEST_CHECK(mapping != NULL);
|
||||
const uint8_t *view = (const uint8_t *)MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
|
||||
TEST_CHECK(view != NULL);
|
||||
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
SIZE_T got = VirtualQuery(view, &mbi, sizeof(mbi));
|
||||
TEST_CHECK_EQ(sizeof(mbi), got);
|
||||
TEST_CHECK_EQ(MEM_COMMIT, mbi.State);
|
||||
TEST_CHECK_EQ(MEM_MAPPED, mbi.Type);
|
||||
TEST_CHECK_EQ(PAGE_READONLY, mbi.AllocationProtect & (PAGE_READONLY | PAGE_EXECUTE_READ));
|
||||
TEST_CHECK_EQ(PAGE_READONLY, mbi.Protect & (PAGE_READONLY | PAGE_EXECUTE_READ));
|
||||
TEST_CHECK(mbi.RegionSize >= page);
|
||||
TEST_CHECK((const uint8_t *)mbi.BaseAddress == view);
|
||||
TEST_CHECK((const uint8_t *)mbi.AllocationBase == view);
|
||||
|
||||
TEST_CHECK(UnmapViewOfFile(view));
|
||||
TEST_CHECK(CloseHandle(mapping));
|
||||
TEST_CHECK(CloseHandle(file));
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_null_address();
|
||||
test_module_region();
|
||||
test_anonymous_mapping();
|
||||
test_file_mapping();
|
||||
return 0;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user