Refactor main module resolution & HMODULE handle behavior

This commit is contained in:
Luke Street 2025-09-29 13:50:27 -06:00
parent 4a2ba45620
commit c17953b318
13 changed files with 355 additions and 224 deletions

View File

@ -7,7 +7,7 @@
- Sample fixtures for exercising the loader live in `test/`; keep new repros small and self-contained. - Sample fixtures for exercising the loader live in `test/`; keep new repros small and self-contained.
## Build, Test, and Development Commands ## Build, Test, and Development Commands
- `cmake -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON` configures a 32-bit toolchain; ensure multilib packages are present. - `cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON` configures a 32-bit toolchain; ensure multilib packages are present.
- `cmake --build build --target wibo` compiles the shim; switch to `-DCMAKE_BUILD_TYPE=Release` for optimised binaries. - `cmake --build build --target wibo` compiles the shim; switch to `-DCMAKE_BUILD_TYPE=Release` for optimised binaries.
- `./build/wibo /path/to/program.exe` runs a Windows binary. Use `WIBO_DEBUG=1` (or `--debug`/`-D`) for verbose logging. Use `--chdir`/`-C` to set the working directory. - `./build/wibo /path/to/program.exe` runs a Windows binary. Use `WIBO_DEBUG=1` (or `--debug`/`-D`) for verbose logging. Use `--chdir`/`-C` to set the working directory.
- `cmake -B build -DBUILD_TESTING=ON` + `ctest --test-dir build --output-on-failure` runs the self-checking WinAPI fixtures (requires `i686-w64-mingw32-gcc` and `i686-w64-mingw32-windres`). - `cmake -B build -DBUILD_TESTING=ON` + `ctest --test-dir build --output-on-failure` runs the self-checking WinAPI fixtures (requires `i686-w64-mingw32-gcc` and `i686-w64-mingw32-windres`).

View File

@ -7,11 +7,11 @@ Don't run this on any untrusted executables, I implore you. (Or probably just do
## Building ## Building
```sh ```sh
cmake -B build -DCMAKE_BUILD_TYPE=Debug cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build --target wibo cmake --build build --target wibo
``` ```
`cmake -B build -DCMAKE_BUILD_TYPE=Release` to produce an optimized binary instead. Set `-DCMAKE_BUILD_TYPE=Release` to produce an optimized binary instead.
## Running ## Running

View File

@ -70,6 +70,7 @@ typedef unsigned char BYTE;
#define ERROR_RESOURCE_NAME_NOT_FOUND 1814 #define ERROR_RESOURCE_NAME_NOT_FOUND 1814
#define ERROR_RESOURCE_LANG_NOT_FOUND 1815 #define ERROR_RESOURCE_LANG_NOT_FOUND 1815
#define ERROR_MOD_NOT_FOUND 126 #define ERROR_MOD_NOT_FOUND 126
#define ERROR_PROC_NOT_FOUND 127
#define ERROR_NEGATIVE_SEEK 131 #define ERROR_NEGATIVE_SEEK 131
#define ERROR_BAD_EXE_FORMAT 193 #define ERROR_BAD_EXE_FORMAT 193
#define ERROR_ALREADY_EXISTS 183 #define ERROR_ALREADY_EXISTS 183
@ -102,6 +103,7 @@ namespace wibo {
extern uint32_t lastError; extern uint32_t lastError;
extern char **argv; extern char **argv;
extern int argc; extern int argc;
extern std::filesystem::path guestExecutablePath;
extern std::string executableName; extern std::string executableName;
extern std::string commandLine; extern std::string commandLine;
extern std::vector<uint16_t> commandLineW; extern std::vector<uint16_t> commandLineW;
@ -125,16 +127,16 @@ namespace wibo {
void setDllDirectoryOverride(const std::filesystem::path &path); void setDllDirectoryOverride(const std::filesystem::path &path);
void clearDllDirectoryOverride(); void clearDllDirectoryOverride();
std::optional<std::filesystem::path> dllDirectoryOverride(); std::optional<std::filesystem::path> dllDirectoryOverride();
HMODULE findLoadedModule(const char *name); ModuleInfo *findLoadedModule(const char *name);
void registerOnExitTable(void *table); void registerOnExitTable(void *table);
void addOnExitFunction(void *table, void (*func)()); void addOnExitFunction(void *table, void (*func)());
void executeOnExitTable(void *table); void executeOnExitTable(void *table);
void runPendingOnExit(ModuleInfo &info); void runPendingOnExit(ModuleInfo &info);
HMODULE loadModule(const char *name); ModuleInfo *loadModule(const char *name);
void freeModule(HMODULE module); void freeModule(ModuleInfo *info);
void *resolveFuncByName(HMODULE module, const char *funcName); void *resolveFuncByName(ModuleInfo *info, const char *funcName);
void *resolveFuncByOrdinal(HMODULE module, uint16_t ordinal); void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal);
void *resolveMissingImportByName(const char *dllName, const char *funcName); void *resolveMissingImportByName(const char *dllName, const char *funcName);
void *resolveMissingImportByOrdinal(const char *dllName, uint16_t ordinal); void *resolveMissingImportByOrdinal(const char *dllName, uint16_t ordinal);
@ -172,51 +174,63 @@ namespace wibo {
}; };
struct Executable { struct Executable {
Executable(); Executable() = default;
~Executable(); ~Executable();
bool loadPE(FILE *file, bool exec); bool loadPE(FILE *file, bool exec);
bool resolveImports();
void *imageBuffer; void *imageBase = nullptr;
size_t imageSize; size_t imageSize = 0;
void *entryPoint; void *entryPoint = nullptr;
void *rsrcBase; void *rsrcBase = nullptr;
uint32_t rsrcSize; uint32_t rsrcSize = 0;
uintptr_t preferredImageBase; uintptr_t preferredImageBase = 0;
intptr_t relocationDelta; intptr_t relocationDelta = 0;
uint32_t exportDirectoryRVA; uint32_t exportDirectoryRVA = 0;
uint32_t exportDirectorySize; uint32_t exportDirectorySize = 0;
uint32_t relocationDirectoryRVA; uint32_t relocationDirectoryRVA = 0;
uint32_t relocationDirectorySize; uint32_t relocationDirectorySize = 0;
uint32_t importDirectoryRVA = 0;
uint32_t importDirectorySize = 0;
uint32_t delayImportDirectoryRVA = 0;
uint32_t delayImportDirectorySize = 0;
bool execMapped = false;
bool importsResolved = false;
bool importsResolving = false;
bool findResource(const ResourceIdentifier &type, bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name,
const ResourceIdentifier &name, std::optional<uint16_t> language, ResourceLocation &out) const;
std::optional<uint16_t> language,
ResourceLocation &out) const;
template <typename T> template <typename T>
T *fromRVA(uint32_t rva) const { T *fromRVA(uintptr_t rva) const {
return (T *) (rva + (uint8_t *) imageBuffer); return (T *) (rva + (uint8_t *) imageBase);
} }
template <typename T> template <typename T>
T *fromRVA(T *rva) const { T *fromRVA(T *rva) const {
return fromRVA<T>((uint32_t) rva); return fromRVA<T>((uintptr_t) rva);
} }
}; };
extern ModuleInfo *mainModule;
struct ModuleInfo { struct ModuleInfo {
// Windows-style handle to the module. For the main module, this is the image base.
// For other modules, this is a pointer to the ModuleInfo structure.
HMODULE handle;
// Original name used to load the module
std::string originalName; std::string originalName;
// Normalized module name
std::string normalizedName; std::string normalizedName;
// Full path to the loaded module
std::filesystem::path resolvedPath; std::filesystem::path resolvedPath;
// Pointer to the built-in module, nullptr if loaded from file
const wibo::Module *module = nullptr; const wibo::Module *module = nullptr;
// Loaded PE executable
std::unique_ptr<wibo::Executable> executable; std::unique_ptr<wibo::Executable> executable;
void *entryPoint = nullptr; // Reference count, or UINT_MAX for built-in modules
void *imageBase = nullptr;
size_t imageSize = 0;
unsigned int refCount = 0; unsigned int refCount = 0;
bool dataFile = false;
bool processAttachCalled = false; bool processAttachCalled = false;
bool processAttachSucceeded = false; bool processAttachSucceeded = false;
bool dontResolveReferences = false;
uint32_t exportOrdinalBase = 0; uint32_t exportOrdinalBase = 0;
std::vector<void *> exportsByOrdinal; std::vector<void *> exportsByOrdinal;
std::unordered_map<std::string, uint16_t> exportNameToOrdinal; std::unordered_map<std::string, uint16_t> exportNameToOrdinal;
@ -224,14 +238,16 @@ namespace wibo {
std::vector<void *> onExitFunctions; std::vector<void *> onExitFunctions;
}; };
extern Executable *mainModule; ModuleInfo *registerProcessModule(std::unique_ptr<Executable> executable, std::filesystem::path resolvedPath,
std::string originalName);
Executable *executableFromModule(HMODULE module); Executable *executableFromModule(HMODULE module);
/** /**
* HMODULE will be `nullptr` or `mainModule->imageBuffer` if it's the main module, * HMODULE will be `nullptr` or `mainModule->imageBase` if it's the main module,
* otherwise it will be a pointer to a `wibo::ModuleInfo`. * otherwise it will be a pointer to a `wibo::ModuleInfo`.
*/ */
inline bool isMainModule(HMODULE hModule) { inline bool isMainModule(HMODULE hModule) {
return hModule == nullptr || hModule == mainModule->imageBuffer; return hModule == nullptr || hModule == reinterpret_cast<HMODULE>(mainModule) ||
(mainModule && mainModule->executable && hModule == mainModule->executable->imageBase);
} }
} // namespace wibo } // namespace wibo

View File

@ -522,7 +522,7 @@ namespace advapi32 {
return FALSE; return FALSE;
} }
auto *stats = reinterpret_cast<TokenStatisticsData *>(TokenInformation); auto *stats = reinterpret_cast<TokenStatisticsData *>(TokenInformation);
memset(stats, 0, required); *stats = {};
stats->tokenType = 1; // TokenPrimary stats->tokenType = 1; // TokenPrimary
stats->impersonationLevel = 0; // SecurityAnonymous stats->impersonationLevel = 0; // SecurityAnonymous
stats->tokenId.LowPart = 1; stats->tokenId.LowPart = 1;

View File

@ -122,6 +122,8 @@ int WIN_ENTRY strcmp(const char *lhs, const char *rhs) { return ::strcmp(lhs, rh
int WIN_ENTRY strncmp(const char *lhs, const char *rhs, size_t count) { return ::strncmp(lhs, rhs, count); } int WIN_ENTRY strncmp(const char *lhs, const char *rhs, size_t count) { return ::strncmp(lhs, rhs, count); }
char *WIN_ENTRY strcpy(char *dest, const char *src) { return ::strcpy(dest, src); }
void *WIN_ENTRY malloc(size_t size) { return ::malloc(size); } void *WIN_ENTRY malloc(size_t size) { return ::malloc(size); }
void *WIN_ENTRY calloc(size_t count, size_t size) { return ::calloc(count, size); } void *WIN_ENTRY calloc(size_t count, size_t size) { return ::calloc(count, size); }
@ -262,6 +264,8 @@ static void *resolveByName(const char *name) {
return (void *)crt::strcmp; return (void *)crt::strcmp;
if (strcmp(name, "strncmp") == 0) if (strcmp(name, "strncmp") == 0)
return (void *)crt::strncmp; return (void *)crt::strncmp;
if (strcmp(name, "strcpy") == 0)
return (void *)crt::strcpy;
if (strcmp(name, "malloc") == 0) if (strcmp(name, "malloc") == 0)
return (void *)crt::malloc; return (void *)crt::malloc;
if (strcmp(name, "calloc") == 0) if (strcmp(name, "calloc") == 0)

View File

@ -2685,17 +2685,13 @@ namespace kernel32 {
HMODULE WIN_FUNC GetModuleHandleA(LPCSTR lpModuleName) { HMODULE WIN_FUNC GetModuleHandleA(LPCSTR lpModuleName) {
DEBUG_LOG("GetModuleHandleA(%s)\n", lpModuleName); DEBUG_LOG("GetModuleHandleA(%s)\n", lpModuleName);
const auto* module = wibo::findLoadedModule(lpModuleName);
if (!lpModuleName) { if (!module) {
// If lpModuleName is NULL, GetModuleHandle returns a handle to the file wibo::lastError = ERROR_MOD_NOT_FOUND;
// used to create the calling process (.exe file). return nullptr;
// This handle needs to equal the actual image buffer, from which data can be read.
return wibo::mainModule->imageBuffer;
} }
wibo::lastError = ERROR_SUCCESS;
HMODULE module = wibo::findLoadedModule(lpModuleName); return module->handle;
wibo::lastError = module ? ERROR_SUCCESS : ERROR_MOD_NOT_FOUND;
return module;
} }
HMODULE WIN_FUNC GetModuleHandleW(LPCWSTR lpModuleName) { HMODULE WIN_FUNC GetModuleHandleW(LPCWSTR lpModuleName) {
@ -2715,23 +2711,17 @@ namespace kernel32 {
return 0; return 0;
} }
std::string path;
if (wibo::isMainModule(hModule)) {
const auto exePath = files::pathFromWindows(wibo::argv[0]);
const auto absPath = std::filesystem::absolute(exePath);
path = files::pathToWindows(absPath);
} else {
auto *info = wibo::moduleInfoFromHandle(hModule); auto *info = wibo::moduleInfoFromHandle(hModule);
if (!info) { if (!info) {
wibo::lastError = ERROR_INVALID_PARAMETER; wibo::lastError = ERROR_INVALID_PARAMETER;
return 0; return 0;
} }
std::string path;
if (!info->resolvedPath.empty()) { if (!info->resolvedPath.empty()) {
path = files::pathToWindows(info->resolvedPath); path = files::pathToWindows(info->resolvedPath);
} else { } else {
path = info->originalName; path = info->originalName;
} }
}
const size_t len = path.size(); const size_t len = path.size();
if (nSize == 0) { if (nSize == 0) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER; wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
@ -2759,23 +2749,17 @@ namespace kernel32 {
return 0; return 0;
} }
std::string path;
if (wibo::isMainModule(hModule)) {
const auto exePath = files::pathFromWindows(wibo::argv[0]);
const auto absPath = std::filesystem::absolute(exePath);
path = files::pathToWindows(absPath);
} else {
auto *info = wibo::moduleInfoFromHandle(hModule); auto *info = wibo::moduleInfoFromHandle(hModule);
if (!info) { if (!info) {
wibo::lastError = ERROR_INVALID_PARAMETER; wibo::lastError = ERROR_INVALID_PARAMETER;
return 0; return 0;
} }
std::string path;
if (!info->resolvedPath.empty()) { if (!info->resolvedPath.empty()) {
path = files::pathToWindows(info->resolvedPath); path = files::pathToWindows(info->resolvedPath);
} else { } else {
path = info->originalName; path = info->originalName;
} }
}
if (nSize == 0) { if (nSize == 0) {
wibo::lastError = ERROR_INSUFFICIENT_BUFFER; wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
return 0; return 0;
@ -2802,18 +2786,9 @@ namespace kernel32 {
return copyLen; return copyLen;
} }
static wibo::Executable *module_executable_for_resource(void *hModule) { static void *findResourceInternal(HMODULE hModule, const wibo::ResourceIdentifier &type,
if (!hModule) { const wibo::ResourceIdentifier &name, std::optional<uint16_t> language) {
hModule = GetModuleHandleA(nullptr); auto *exe = wibo::executableFromModule(hModule);
}
return wibo::executableFromModule((HMODULE) hModule);
}
static void *find_resource_internal(void *hModule,
const wibo::ResourceIdentifier &type,
const wibo::ResourceIdentifier &name,
std::optional<uint16_t> language) {
auto *exe = module_executable_for_resource(hModule);
if (!exe) { if (!exe) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return nullptr; return nullptr;
@ -2825,41 +2800,41 @@ namespace kernel32 {
return const_cast<void *>(loc.dataEntry); return const_cast<void *>(loc.dataEntry);
} }
void *WIN_FUNC FindResourceA(void *hModule, const char *lpName, const char *lpType) { void *WIN_FUNC FindResourceA(HMODULE hModule, const char *lpName, const char *lpType) {
DEBUG_LOG("FindResourceA %p %p %p\n", hModule, lpName, lpType); DEBUG_LOG("FindResourceA %p %p %p\n", hModule, lpName, lpType);
auto type = wibo::resourceIdentifierFromAnsi(lpType); auto type = wibo::resourceIdentifierFromAnsi(lpType);
auto name = wibo::resourceIdentifierFromAnsi(lpName); auto name = wibo::resourceIdentifierFromAnsi(lpName);
return find_resource_internal(hModule, type, name, std::nullopt); return findResourceInternal(hModule, type, name, std::nullopt);
} }
void *WIN_FUNC FindResourceExA(void *hModule, const char *lpType, const char *lpName, uint16_t wLanguage) { void *WIN_FUNC FindResourceExA(HMODULE hModule, const char *lpType, const char *lpName, uint16_t wLanguage) {
DEBUG_LOG("FindResourceExA %p %p %p %u\n", hModule, lpName, lpType, wLanguage); DEBUG_LOG("FindResourceExA %p %p %p %u\n", hModule, lpName, lpType, wLanguage);
auto type = wibo::resourceIdentifierFromAnsi(lpType); auto type = wibo::resourceIdentifierFromAnsi(lpType);
auto name = wibo::resourceIdentifierFromAnsi(lpName); auto name = wibo::resourceIdentifierFromAnsi(lpName);
return find_resource_internal(hModule, type, name, wLanguage); return findResourceInternal(hModule, type, name, wLanguage);
} }
void *WIN_FUNC FindResourceW(void *hModule, const uint16_t *lpName, const uint16_t *lpType) { void *WIN_FUNC FindResourceW(HMODULE hModule, const uint16_t *lpName, const uint16_t *lpType) {
DEBUG_LOG("FindResourceW %p\n", hModule); DEBUG_LOG("FindResourceW %p\n", hModule);
auto type = wibo::resourceIdentifierFromWide(lpType); auto type = wibo::resourceIdentifierFromWide(lpType);
auto name = wibo::resourceIdentifierFromWide(lpName); auto name = wibo::resourceIdentifierFromWide(lpName);
return find_resource_internal(hModule, type, name, std::nullopt); return findResourceInternal(hModule, type, name, std::nullopt);
} }
void *WIN_FUNC FindResourceExW(void *hModule, const uint16_t *lpType, const uint16_t *lpName, uint16_t wLanguage) { void *WIN_FUNC FindResourceExW(HMODULE hModule, const uint16_t *lpType, const uint16_t *lpName, uint16_t wLanguage) {
DEBUG_LOG("FindResourceExW %p %u\n", hModule, wLanguage); DEBUG_LOG("FindResourceExW %p %u\n", hModule, wLanguage);
auto type = wibo::resourceIdentifierFromWide(lpType); auto type = wibo::resourceIdentifierFromWide(lpType);
auto name = wibo::resourceIdentifierFromWide(lpName); auto name = wibo::resourceIdentifierFromWide(lpName);
return find_resource_internal(hModule, type, name, wLanguage); return findResourceInternal(hModule, type, name, wLanguage);
} }
void* WIN_FUNC LoadResource(void* hModule, void* res) { void *WIN_FUNC LoadResource(HMODULE hModule, void *res) {
DEBUG_LOG("LoadResource %p %p\n", hModule, res); DEBUG_LOG("LoadResource %p %p\n", hModule, res);
if (!res) { if (!res) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return nullptr; return nullptr;
} }
auto *exe = module_executable_for_resource(hModule); auto *exe = wibo::executableFromModule(hModule);
if (!exe || !exe->rsrcBase) { if (!exe || !exe->rsrcBase) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return nullptr; return nullptr;
@ -2901,13 +2876,13 @@ namespace kernel32 {
return res; return res;
} }
unsigned int WIN_FUNC SizeofResource(void* hModule, void* res) { unsigned int WIN_FUNC SizeofResource(HMODULE hModule, void* res) {
DEBUG_LOG("SizeofResource %p %p\n", hModule, res); DEBUG_LOG("SizeofResource %p %p\n", hModule, res);
if (!res) { if (!res) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return 0; return 0;
} }
auto *exe = module_executable_for_resource(hModule); auto *exe = wibo::executableFromModule(hModule);
if (!exe || !exe->rsrcBase) { if (!exe || !exe->rsrcBase) {
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND; wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
return 0; return 0;
@ -2922,7 +2897,13 @@ namespace kernel32 {
HMODULE WIN_FUNC LoadLibraryA(LPCSTR lpLibFileName) { HMODULE WIN_FUNC LoadLibraryA(LPCSTR lpLibFileName) {
DEBUG_LOG("LoadLibraryA(%s)\n", lpLibFileName); DEBUG_LOG("LoadLibraryA(%s)\n", lpLibFileName);
return wibo::loadModule(lpLibFileName); const auto *info = wibo::loadModule(lpLibFileName);
if (!info) {
// loadModule already sets lastError
return nullptr;
}
wibo::lastError = ERROR_SUCCESS;
return info->handle;
} }
HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName) { HMODULE WIN_FUNC LoadLibraryW(LPCWSTR lpLibFileName) {
@ -2943,7 +2924,12 @@ namespace kernel32 {
BOOL WIN_FUNC FreeLibrary(HMODULE hLibModule) { BOOL WIN_FUNC FreeLibrary(HMODULE hLibModule) {
DEBUG_LOG("FreeLibrary(%p)\n", hLibModule); DEBUG_LOG("FreeLibrary(%p)\n", hLibModule);
wibo::freeModule(hLibModule); auto *info = wibo::moduleInfoFromHandle(hLibModule);
if (!info) {
wibo::lastError = ERROR_INVALID_HANDLE;
return FALSE;
}
wibo::freeModule(info);
return TRUE; return TRUE;
} }
@ -3664,15 +3650,26 @@ namespace kernel32 {
FARPROC WIN_FUNC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) { FARPROC WIN_FUNC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) {
FARPROC result; FARPROC result;
const auto info = wibo::moduleInfoFromHandle(hModule);
if (!info) {
DEBUG_LOG("GetProcAddress: invalid module handle %p\n", hModule);
wibo::lastError = ERROR_INVALID_HANDLE;
return nullptr;
}
const auto proc = reinterpret_cast<uintptr_t>(lpProcName); const auto proc = reinterpret_cast<uintptr_t>(lpProcName);
if (proc & ~0xFFFF) { if (proc & ~0xFFFF) {
DEBUG_LOG("GetProcAddress(%p, %s) ", hModule, lpProcName); DEBUG_LOG("GetProcAddress(%p, %s) ", hModule, lpProcName);
result = wibo::resolveFuncByName(hModule, lpProcName); result = wibo::resolveFuncByName(info, lpProcName);
} else { } else {
DEBUG_LOG("GetProcAddress(%p, %u) ", hModule, proc); DEBUG_LOG("GetProcAddress(%p, %u) ", hModule, proc);
result = wibo::resolveFuncByOrdinal(hModule, static_cast<uint16_t>(proc)); result = wibo::resolveFuncByOrdinal(info, static_cast<uint16_t>(proc));
} }
DEBUG_LOG("-> %p\n", result); DEBUG_LOG("-> %p\n", result);
if (!result) {
wibo::lastError = ERROR_PROC_NOT_FOUND;
} else {
wibo::lastError = ERROR_SUCCESS;
}
return result; return result;
} }

View File

@ -169,8 +169,8 @@ namespace msvcrt {
std::string converted = files::hostPathListToWindows(value); std::string converted = files::hostPathListToWindows(value);
std::string result = converted.empty() ? value : converted; std::string result = converted.empty() ? value : converted;
std::string exeDir; std::string exeDir;
if (wibo::argv && wibo::argv[0]) { if (!wibo::guestExecutablePath.empty()) {
std::filesystem::path exePath = std::filesystem::absolute(std::filesystem::path(wibo::argv[0])).parent_path(); auto exePath = wibo::guestExecutablePath.parent_path();
if (!exePath.empty()) { if (!exePath.empty()) {
exeDir = files::pathToWindows(exePath); exeDir = files::pathToWindows(exePath);
} }

View File

@ -17,7 +17,7 @@ BOOL WIN_FUNC EnumProcessModules(HANDLE hProcess, HMODULE *lphModule, DWORD cb,
return FALSE; return FALSE;
} }
HMODULE currentModule = wibo::mainModule ? reinterpret_cast<HMODULE>(wibo::mainModule->imageBuffer) : nullptr; HMODULE currentModule = wibo::mainModule ? wibo::mainModule->handle : nullptr;
DWORD required = currentModule ? sizeof(HMODULE) : 0; DWORD required = currentModule ? sizeof(HMODULE) : 0;
if (lpcbNeeded) { if (lpcbNeeded) {
*lpcbNeeded = required; *lpcbNeeded = required;

View File

@ -122,24 +122,10 @@ uint32_t read32(FILE *file) {
return v; return v;
} }
wibo::Executable::Executable() {
imageBuffer = nullptr;
imageSize = 0;
entryPoint = nullptr;
rsrcBase = 0;
rsrcSize = 0;
preferredImageBase = 0;
relocationDelta = 0;
exportDirectoryRVA = 0;
exportDirectorySize = 0;
relocationDirectoryRVA = 0;
relocationDirectorySize = 0;
}
wibo::Executable::~Executable() { wibo::Executable::~Executable() {
if (imageBuffer) { if (imageBase) {
munmap(imageBuffer, imageSize); munmap(imageBase, imageSize);
imageBase = nullptr;
} }
} }
@ -167,7 +153,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
PE32Header header32; PE32Header header32;
memset(&header32, 0, sizeof header32); memset(&header32, 0, sizeof header32);
fread(&header32, std::min(sizeof(header32), (size_t) header.sizeOfOptionalHeader), 1, file); fread(&header32, std::min(sizeof(header32), (size_t)header.sizeOfOptionalHeader), 1, file);
if (header32.magic != 0x10B) if (header32.magic != 0x10B)
return false; return false;
@ -181,6 +167,13 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
exportDirectorySize = header32.exportTable.size; exportDirectorySize = header32.exportTable.size;
relocationDirectoryRVA = header32.baseRelocationTable.virtualAddress; relocationDirectoryRVA = header32.baseRelocationTable.virtualAddress;
relocationDirectorySize = header32.baseRelocationTable.size; relocationDirectorySize = header32.baseRelocationTable.size;
importDirectoryRVA = header32.importTable.virtualAddress;
importDirectorySize = header32.importTable.size;
delayImportDirectoryRVA = header32.delayImportDescriptor.virtualAddress;
delayImportDirectorySize = header32.delayImportDescriptor.size;
execMapped = exec;
importsResolved = false;
importsResolving = false;
// Build buffer // Build buffer
imageSize = header32.sizeOfImage; imageSize = header32.sizeOfImage;
@ -188,17 +181,17 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
if (exec) if (exec)
prot |= PROT_EXEC; prot |= PROT_EXEC;
void *preferredBase = (void *)(uintptr_t)header32.imageBase; void *preferredBase = (void *)(uintptr_t)header32.imageBase;
imageBuffer = mmap(preferredBase, header32.sizeOfImage, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); imageBase = mmap(preferredBase, header32.sizeOfImage, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (imageBuffer == MAP_FAILED) { if (imageBase == MAP_FAILED) {
imageBuffer = mmap(nullptr, header32.sizeOfImage, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); imageBase = mmap(nullptr, header32.sizeOfImage, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
} }
if (imageBuffer == MAP_FAILED) { if (imageBase == MAP_FAILED) {
perror("Image mapping failed!"); perror("Image mapping failed!");
imageBuffer = nullptr; imageBase = nullptr;
return false; return false;
} }
relocationDelta = (intptr_t)((uintptr_t)imageBuffer - (uintptr_t)header32.imageBase); relocationDelta = (intptr_t)((uintptr_t)imageBase - (uintptr_t)header32.imageBase);
memset(imageBuffer, 0, header32.sizeOfImage); memset(imageBase, 0, header32.sizeOfImage);
// Read the sections // Read the sections
fseek(file, offsetToPE + sizeof header + header.sizeOfOptionalHeader, SEEK_SET); fseek(file, offsetToPE + sizeof header + header.sizeOfOptionalHeader, SEEK_SET);
@ -210,9 +203,10 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
char name[9]; char name[9];
memcpy(name, section.name, 8); memcpy(name, section.name, 8);
name[8] = 0; name[8] = 0;
DEBUG_LOG("Section %d: name=%s addr=%x size=%x (raw=%x) ptr=%x\n", i, name, section.virtualAddress, section.virtualSize, section.sizeOfRawData, section.pointerToRawData); DEBUG_LOG("Section %d: name=%s addr=%x size=%x (raw=%x) ptr=%x\n", i, name, section.virtualAddress,
section.virtualSize, section.sizeOfRawData, section.pointerToRawData);
void *sectionBase = (void *) ((uintptr_t) imageBuffer + section.virtualAddress); void *sectionBase = (void *)((uintptr_t)imageBase + section.virtualAddress);
if (section.pointerToRawData > 0 && section.sizeOfRawData > 0) { if (section.pointerToRawData > 0 && section.sizeOfRawData > 0) {
// Grab this data // Grab this data
long savePos = ftell(file); long savePos = ftell(file);
@ -230,8 +224,8 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
if (exec && relocationDelta != 0) { if (exec && relocationDelta != 0) {
if (relocationDirectoryRVA == 0 || relocationDirectorySize == 0) { if (relocationDirectoryRVA == 0 || relocationDirectorySize == 0) {
DEBUG_LOG("Relocation required but no relocation directory present\n"); DEBUG_LOG("Relocation required but no relocation directory present\n");
munmap(imageBuffer, imageSize); munmap(imageBase, imageSize);
imageBuffer = nullptr; imageBase = nullptr;
return false; return false;
} }
@ -239,7 +233,8 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
uint8_t *relocEnd = relocCursor + relocationDirectorySize; uint8_t *relocEnd = relocCursor + relocationDirectorySize;
while (relocCursor < relocEnd) { while (relocCursor < relocEnd) {
auto *block = reinterpret_cast<PEBaseRelocationBlock *>(relocCursor); auto *block = reinterpret_cast<PEBaseRelocationBlock *>(relocCursor);
if (block->sizeOfBlock < sizeof(PEBaseRelocationBlock) || block->sizeOfBlock > static_cast<uint32_t>(relocEnd - relocCursor)) { if (block->sizeOfBlock < sizeof(PEBaseRelocationBlock) ||
block->sizeOfBlock > static_cast<uint32_t>(relocEnd - relocCursor)) {
break; break;
} }
if (block->sizeOfBlock == sizeof(PEBaseRelocationBlock)) { if (block->sizeOfBlock == sizeof(PEBaseRelocationBlock)) {
@ -253,7 +248,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
uint16_t offset = entry & 0x0FFF; uint16_t offset = entry & 0x0FFF;
if (type == IMAGE_REL_BASED_ABSOLUTE) if (type == IMAGE_REL_BASED_ABSOLUTE)
continue; continue;
uintptr_t target = reinterpret_cast<uintptr_t>(imageBuffer) + block->virtualAddress + offset; uintptr_t target = reinterpret_cast<uintptr_t>(imageBase) + block->virtualAddress + offset;
switch (type) { switch (type) {
case IMAGE_REL_BASED_HIGHLOW: { case IMAGE_REL_BASED_HIGHLOW: {
auto *addr = reinterpret_cast<uint32_t *>(target); auto *addr = reinterpret_cast<uint32_t *>(target);
@ -269,13 +264,34 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
} }
} }
if (!exec) { entryPoint = header32.addressOfEntryPoint ? fromRVA<void>(header32.addressOfEntryPoint) : nullptr;
// No need to resolve imports
return true;
}
bool wibo::Executable::resolveImports() {
if (importsResolved || !execMapped) {
importsResolved = true;
importsResolving = false;
return true;
}
if (importsResolving) {
return true;
}
importsResolving = true;
if (!importDirectoryRVA) {
importsResolved = true;
importsResolving = false;
return true; return true;
} }
// Handle imports PEImportDirectoryEntry *dir = fromRVA<PEImportDirectoryEntry>(importDirectoryRVA);
PEImportDirectoryEntry *dir = fromRVA<PEImportDirectoryEntry>(header32.importTable.virtualAddress); if (!dir) {
importsResolved = true;
importsResolving = false;
return true;
}
while (dir->name) { while (dir->name) {
char *dllName = fromRVA(dir->name); char *dllName = fromRVA(dir->name);
@ -283,15 +299,15 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
uint32_t *lookupTable = fromRVA(dir->importLookupTable); uint32_t *lookupTable = fromRVA(dir->importLookupTable);
uint32_t *addressTable = fromRVA(dir->importAddressTable); uint32_t *addressTable = fromRVA(dir->importAddressTable);
HMODULE module = loadModule(dllName); ModuleInfo *module = loadModule(dllName);
while (*lookupTable) { while (*lookupTable) {
uint32_t lookup = *lookupTable; uint32_t lookup = *lookupTable;
if (lookup & 0x80000000) { if (lookup & 0x80000000) {
// Import by ordinal // Import by ordinal
uint16_t ordinal = lookup & 0xFFFF; uint16_t ordinal = lookup & 0xFFFF;
DEBUG_LOG(" Ordinal: %d\n", ordinal); DEBUG_LOG(" Ordinal: %d\n", ordinal);
void *func = module ? resolveFuncByOrdinal(module, ordinal) void *func =
: resolveMissingImportByOrdinal(dllName, ordinal); module ? resolveFuncByOrdinal(module, ordinal) : resolveMissingImportByOrdinal(dllName, ordinal);
DEBUG_LOG(" -> %p\n", func); DEBUG_LOG(" -> %p\n", func);
*addressTable = reinterpret_cast<uintptr_t>(func); *addressTable = reinterpret_cast<uintptr_t>(func);
} else { } else {
@ -309,15 +325,16 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
++dir; ++dir;
} }
if (header32.delayImportDescriptor.virtualAddress) { // TODO: actual delay loading from __delayLoadHelper2
DEBUG_LOG("Processing delay import table at RVA %x\n", header32.delayImportDescriptor.virtualAddress); if (delayImportDirectoryRVA) {
PEDelayImportDescriptor *delay = fromRVA<PEDelayImportDescriptor>(header32.delayImportDescriptor.virtualAddress); DEBUG_LOG("Processing delay import table at RVA %x\n", delayImportDirectoryRVA);
while (delay->name) { PEDelayImportDescriptor *delay = fromRVA<PEDelayImportDescriptor>(delayImportDirectoryRVA);
while (delay && delay->name) {
char *dllName = fromRVA<char>(delay->name); char *dllName = fromRVA<char>(delay->name);
DEBUG_LOG("Delay DLL Name: %s\n", dllName); DEBUG_LOG("Delay DLL Name: %s\n", dllName);
uint32_t *lookupTable = fromRVA<uint32_t>(delay->importNameTable); uint32_t *lookupTable = fromRVA<uint32_t>(delay->importNameTable);
uint32_t *addressTable = fromRVA<uint32_t>(delay->importAddressTable); uint32_t *addressTable = fromRVA<uint32_t>(delay->importAddressTable);
HMODULE module = loadModule(dllName); ModuleInfo *module = loadModule(dllName);
while (*lookupTable) { while (*lookupTable) {
uint32_t lookup = *lookupTable; uint32_t lookup = *lookupTable;
if (lookup & 0x80000000) { if (lookup & 0x80000000) {
@ -346,7 +363,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
} }
} }
entryPoint = header32.addressOfEntryPoint ? fromRVA<void>(header32.addressOfEntryPoint) : nullptr; importsResolved = true;
importsResolving = false;
return true; return true;
} }

View File

@ -1,26 +1,28 @@
#include "common.h" #include "common.h"
#include "files.h" #include "files.h"
#include "processes.h"
#include "strutil.h" #include "strutil.h"
#include <asm/ldt.h> #include <asm/ldt.h>
#include <charconv> #include <charconv>
#include <cstdarg>
#include <cstring> #include <cstring>
#include <fcntl.h> #include <fcntl.h>
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
#include <stdarg.h>
#include <system_error>
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/syscall.h> #include <sys/syscall.h>
#include <system_error>
#include <unistd.h> #include <unistd.h>
#include <vector> #include <vector>
uint32_t wibo::lastError = 0; uint32_t wibo::lastError = 0;
char** wibo::argv; char **wibo::argv;
int wibo::argc; int wibo::argc;
std::filesystem::path wibo::guestExecutablePath;
std::string wibo::executableName; std::string wibo::executableName;
std::string wibo::commandLine; std::string wibo::commandLine;
std::vector<uint16_t> wibo::commandLineW; std::vector<uint16_t> wibo::commandLineW;
wibo::Executable *wibo::mainModule = 0; wibo::ModuleInfo *wibo::mainModule = nullptr;
bool wibo::debugEnabled = false; bool wibo::debugEnabled = false;
unsigned int wibo::debugIndent = 0; unsigned int wibo::debugIndent = 0;
uint16_t wibo::tibSelector = 0; uint16_t wibo::tibSelector = 0;
@ -109,7 +111,7 @@ static void printHelp(const char *argv0) {
* @param buffer The buffer to read into. * @param buffer The buffer to read into.
* @return The number of bytes read. * @return The number of bytes read.
*/ */
static size_t readMaps(char* buffer) { static size_t readMaps(char *buffer) {
int fd = open("/proc/self/maps", O_RDONLY); int fd = open("/proc/self/maps", O_RDONLY);
if (fd == -1) { if (fd == -1) {
perror("Failed to open /proc/self/maps"); perror("Failed to open /proc/self/maps");
@ -187,7 +189,8 @@ static void blockUpper2GB() {
holdingMapStart = std::max(holdingMapStart, FILL_MEMORY_ABOVE); holdingMapStart = std::max(holdingMapStart, FILL_MEMORY_ABOVE);
// DEBUG_LOG("Mapping %08x-%08x\n", holdingMapStart, holdingMapEnd); // DEBUG_LOG("Mapping %08x-%08x\n", holdingMapStart, holdingMapEnd);
void* holdingMap = mmap((void*) holdingMapStart, holdingMapEnd - holdingMapStart, PROT_READ | PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0); void *holdingMap = mmap((void *)holdingMapStart, holdingMapEnd - holdingMapStart, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, -1, 0);
if (holdingMap == MAP_FAILED) { if (holdingMap == MAP_FAILED) {
perror("Failed to create holding map"); perror("Failed to create holding map");
@ -288,13 +291,13 @@ int main(int argc, char **argv) {
// Create TIB // Create TIB
memset(&tib, 0, sizeof(tib)); memset(&tib, 0, sizeof(tib));
tib.tib = &tib; tib.tib = &tib;
tib.peb = (PEB*)calloc(sizeof(PEB), 1); tib.peb = (PEB *)calloc(sizeof(PEB), 1);
tib.peb->ProcessParameters = (RTL_USER_PROCESS_PARAMETERS*)calloc(sizeof(RTL_USER_PROCESS_PARAMETERS), 1); tib.peb->ProcessParameters = (RTL_USER_PROCESS_PARAMETERS *)calloc(sizeof(RTL_USER_PROCESS_PARAMETERS), 1);
struct user_desc tibDesc; struct user_desc tibDesc;
memset(&tibDesc, 0, sizeof tibDesc); memset(&tibDesc, 0, sizeof tibDesc);
tibDesc.entry_number = 0; tibDesc.entry_number = 0;
tibDesc.base_addr = (unsigned int) &tib; tibDesc.base_addr = (unsigned int)&tib;
tibDesc.limit = 0x1000; tibDesc.limit = 0x1000;
tibDesc.seg_32bit = 1; tibDesc.seg_32bit = 1;
tibDesc.contents = 0; // hopefully this is ok tibDesc.contents = 0; // hopefully this is ok
@ -312,12 +315,25 @@ int main(int argc, char **argv) {
char **guestArgv = argv + programIndex; char **guestArgv = argv + programIndex;
int guestArgc = argc - programIndex; int guestArgc = argc - programIndex;
const char *pePath = guestArgv[0];
if (!pePath || pePath[0] == '\0') {
fprintf(stderr, "No guest binary specified\n");
return 1;
}
std::string originalName = pePath;
std::filesystem::path resolvedGuestPath = processes::resolveExecutable(originalName, true).value_or({});
if (resolvedGuestPath.empty()) {
fprintf(stderr, "Failed to resolve path to guest binary %s\n", originalName.c_str());
return 1;
}
// Build a command line // Build a command line
std::string cmdLine; std::string cmdLine;
for (int i = 0; i < guestArgc; ++i) { for (int i = 0; i < guestArgc; ++i) {
std::string arg; std::string arg;
if (i == 0) { if (i == 0) {
arg = files::pathToWindows(std::filesystem::absolute(guestArgv[0])); arg = files::pathToWindows(resolvedGuestPath);
} else { } else {
cmdLine += ' '; cmdLine += ' ';
arg = guestArgv[i]; arg = guestArgv[i];
@ -326,7 +342,7 @@ int main(int argc, char **argv) {
if (needQuotes) if (needQuotes)
cmdLine += '"'; cmdLine += '"';
int backslashes = 0; int backslashes = 0;
for (const char *p = arg.c_str(); ; p++) { for (const char *p = arg.c_str();; p++) {
char c = *p; char c = *p;
if (c == '\\') { if (c == '\\') {
backslashes++; backslashes++;
@ -356,32 +372,50 @@ int main(int argc, char **argv) {
wibo::commandLineW = stringToWideString(wibo::commandLine.c_str()); wibo::commandLineW = stringToWideString(wibo::commandLine.c_str());
DEBUG_LOG("Command line: %s\n", wibo::commandLine.c_str()); DEBUG_LOG("Command line: %s\n", wibo::commandLine.c_str());
wibo::guestExecutablePath = resolvedGuestPath;
wibo::executableName = executablePath; wibo::executableName = executablePath;
wibo::argv = guestArgv; wibo::argv = guestArgv;
wibo::argc = guestArgc; wibo::argc = guestArgc;
wibo::initializeModuleRegistry(); wibo::initializeModuleRegistry();
wibo::Executable exec; FILE *f = fopen(resolvedGuestPath.c_str(), "rb");
wibo::mainModule = &exec;
char* pe_path = guestArgv[0];
FILE *f = fopen(pe_path, "rb");
if (!f) { if (!f) {
std::string mesg = std::string("Failed to open file ") + pe_path; std::string mesg = std::string("Failed to open file ") + resolvedGuestPath.string();
perror(mesg.c_str()); perror(mesg.c_str());
return 1; return 1;
} }
exec.loadPE(f, true); auto executable = std::make_unique<wibo::Executable>();
if (!executable->loadPE(f, true)) {
fclose(f);
fprintf(stderr, "Failed to load PE image %s\n", resolvedGuestPath.c_str());
return 1;
}
fclose(f); fclose(f);
const auto entryPoint = executable->entryPoint;
if (!entryPoint) {
fprintf(stderr, "Executable %s has no entry point\n", resolvedGuestPath.c_str());
return 1;
}
wibo::mainModule =
wibo::registerProcessModule(std::move(executable), std::move(resolvedGuestPath), std::move(originalName));
if (!wibo::mainModule || !wibo::mainModule->executable) {
fprintf(stderr, "Failed to register process module\n");
return 1;
}
DEBUG_LOG("Registered main module %s at %p\n", wibo::mainModule->normalizedName.c_str(),
wibo::mainModule->executable->imageBase);
if (!wibo::mainModule->executable->resolveImports()) {
fprintf(stderr, "Failed to resolve imports for main module\n");
return 1;
}
// Invoke the damn thing // Invoke the damn thing
asm( asm("movw %0, %%fs; call *%1" : : "r"(wibo::tibSelector), "r"(entryPoint));
"movw %0, %%fs; call *%1"
:
: "r"(wibo::tibSelector), "r"(exec.entryPoint)
);
DEBUG_LOG("We came back\n"); DEBUG_LOG("We came back\n");
wibo::shutdownModuleRegistry(); wibo::shutdownModuleRegistry();

View File

@ -4,7 +4,6 @@
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cerrno>
#include <climits> #include <climits>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
@ -253,10 +252,10 @@ std::vector<std::filesystem::path> collectSearchDirectories(ModuleRegistry &reg,
} }
}; };
if (wibo::argv && wibo::argc > 0 && wibo::argv[0]) { if (!wibo::guestExecutablePath.empty()) {
std::filesystem::path mainBinary = std::filesystem::absolute(wibo::argv[0]); auto parent = wibo::guestExecutablePath.parent_path();
if (mainBinary.has_parent_path()) { if (!parent.empty()) {
addDirectory(mainBinary.parent_path()); addDirectory(parent);
} }
} }
@ -369,6 +368,7 @@ void registerBuiltinModule(ModuleRegistry &reg, const wibo::Module *module) {
return; return;
} }
ModulePtr entry = std::make_unique<wibo::ModuleInfo>(); ModulePtr entry = std::make_unique<wibo::ModuleInfo>();
entry->handle = entry.get();
entry->module = module; entry->module = module;
entry->refCount = UINT_MAX; entry->refCount = UINT_MAX;
entry->originalName = module->names[0] ? module->names[0] : ""; entry->originalName = module->names[0] ? module->names[0] : "";
@ -406,24 +406,24 @@ void registerBuiltinModule(ModuleRegistry &reg, const wibo::Module *module) {
} }
void callDllMain(wibo::ModuleInfo &info, DWORD reason) { void callDllMain(wibo::ModuleInfo &info, DWORD reason) {
if (!info.entryPoint || info.module) { if (!info.executable) {
return; return;
} }
using DllMainFunc = BOOL(WIN_FUNC *)(HMODULE, DWORD, LPVOID); using DllMainFunc = BOOL(WIN_FUNC *)(HMODULE, DWORD, LPVOID);
auto dllMain = reinterpret_cast<DllMainFunc>(info.entryPoint); auto dllMain = reinterpret_cast<DllMainFunc>(info.executable->entryPoint);
if (!dllMain) { if (!dllMain) {
return; return;
} }
auto invokeWithGuestTIB = [&](DWORD callReason) -> BOOL { auto invokeWithGuestTIB = [&](DWORD callReason) -> BOOL {
if (!wibo::tibSelector) { if (!wibo::tibSelector) {
return dllMain(reinterpret_cast<HMODULE>(info.imageBase), callReason, nullptr); return dllMain(reinterpret_cast<HMODULE>(info.executable->imageBase), callReason, nullptr);
} }
uint16_t previousSegment = 0; uint16_t previousSegment = 0;
asm volatile("mov %%fs, %0" : "=r"(previousSegment)); asm volatile("mov %%fs, %0" : "=r"(previousSegment));
asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory"); asm volatile("movw %0, %%fs" : : "r"(wibo::tibSelector) : "memory");
BOOL result = dllMain(reinterpret_cast<HMODULE>(info.imageBase), callReason, nullptr); BOOL result = dllMain(reinterpret_cast<HMODULE>(info.executable->imageBase), callReason, nullptr);
asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory"); asm volatile("movw %0, %%fs" : : "r"(previousSegment) : "memory");
return result; return result;
}; };
@ -455,20 +455,13 @@ wibo::ModuleInfo *moduleFromAddress(ModuleRegistry &reg, void *addr) {
return nullptr; return nullptr;
for (auto &pair : reg.modulesByKey) { for (auto &pair : reg.modulesByKey) {
wibo::ModuleInfo *info = pair.second.get(); wibo::ModuleInfo *info = pair.second.get();
if (!info) if (!info || !info->executable)
continue; continue;
uint8_t *base = nullptr; const auto *base = static_cast<const uint8_t *>(info->executable->imageBase);
size_t size = 0; size_t size = info->executable->imageSize;
if (info->imageBase && info->imageSize) {
base = static_cast<uint8_t *>(info->imageBase);
size = info->imageSize;
} else if (info->executable) {
base = static_cast<uint8_t *>(info->executable->imageBuffer);
size = info->executable->imageSize;
}
if (!base || size == 0) if (!base || size == 0)
continue; continue;
auto *ptr = static_cast<uint8_t *>(addr); const auto *ptr = static_cast<const uint8_t *>(addr);
if (ptr >= base && ptr < base + size) { if (ptr >= base && ptr < base + size) {
return info; return info;
} }
@ -530,6 +523,68 @@ namespace wibo {
void initializeModuleRegistry() { registry(); } void initializeModuleRegistry() { registry(); }
ModuleInfo *registerProcessModule(std::unique_ptr<Executable> executable, std::filesystem::path resolvedPath,
std::string originalName) {
if (!executable) {
return nullptr;
}
if (originalName.empty() && !resolvedPath.empty()) {
originalName = resolvedPath.filename().string();
}
ParsedModuleName parsed = parseModuleName(originalName);
std::string normalizedName = normalizedBaseKey(parsed);
ModulePtr info = std::make_unique<ModuleInfo>();
info->handle = executable->imageBase; // Use image base as handle for main module
info->module = nullptr;
info->originalName = std::move(originalName);
info->normalizedName = std::move(normalizedName);
info->resolvedPath = std::move(resolvedPath);
info->executable = std::move(executable);
info->refCount = UINT_MAX;
ModuleInfo *raw = info.get();
std::string storageKey;
if (!raw->resolvedPath.empty()) {
storageKey = storageKeyForPath(raw->resolvedPath);
} else if (!raw->normalizedName.empty()) {
storageKey = storageKeyForBuiltin(raw->normalizedName);
}
if (storageKey.empty()) {
storageKey = normalizeAlias(raw->originalName);
}
auto reg = registry();
reg->modulesByKey[storageKey] = std::move(info);
if (!raw->resolvedPath.empty()) {
registerExternalModuleAliases(*reg, raw->originalName, raw->resolvedPath, raw);
} else {
registerAlias(*reg, normalizeAlias(raw->originalName), raw);
std::string baseAlias = normalizedBaseKey(parsed);
if (baseAlias != raw->originalName) {
registerAlias(*reg, baseAlias, raw);
}
}
ensureExportsInitialized(*raw);
auto pinAlias = [&](const std::string &alias) {
if (!alias.empty()) {
reg->pinnedAliases.insert(alias);
}
};
reg->pinnedModules.insert(raw);
pinAlias(storageKey);
pinAlias(normalizeAlias(raw->originalName));
pinAlias(normalizedName);
return raw;
}
void shutdownModuleRegistry() { void shutdownModuleRegistry() {
auto reg = registry(); auto reg = registry();
for (auto &pair : reg->modulesByKey) { for (auto &pair : reg->modulesByKey) {
@ -549,7 +604,12 @@ void shutdownModuleRegistry() {
reg->onExitTables.clear(); reg->onExitTables.clear();
} }
ModuleInfo *moduleInfoFromHandle(HMODULE module) { return static_cast<ModuleInfo *>(module); } ModuleInfo *moduleInfoFromHandle(HMODULE module) {
if (isMainModule(module)) {
return wibo::mainModule;
}
return static_cast<ModuleInfo *>(module);
}
void setDllDirectoryOverride(const std::filesystem::path &path) { void setDllDirectoryOverride(const std::filesystem::path &path) {
auto canonical = files::canonicalPath(path); auto canonical = files::canonicalPath(path);
@ -598,7 +658,7 @@ void addOnExitFunction(void *table, void (*func)()) {
void runPendingOnExit(ModuleInfo &info) { void runPendingOnExit(ModuleInfo &info) {
for (auto it = info.onExitFunctions.rbegin(); it != info.onExitFunctions.rend(); ++it) { for (auto it = info.onExitFunctions.rbegin(); it != info.onExitFunctions.rend(); ++it) {
auto fn = reinterpret_cast<void (*)(void)>(*it); auto fn = reinterpret_cast<void (*)()>(*it);
if (fn) { if (fn) {
fn(); fn();
} }
@ -623,9 +683,9 @@ void executeOnExitTable(void *table) {
} }
} }
HMODULE findLoadedModule(const char *name) { ModuleInfo *findLoadedModule(const char *name) {
if (!name) { if (!name || *name == '\0') {
return nullptr; return wibo::mainModule;
} }
auto reg = registry(); auto reg = registry();
ParsedModuleName parsed = parseModuleName(name); ParsedModuleName parsed = parseModuleName(name);
@ -637,8 +697,8 @@ HMODULE findLoadedModule(const char *name) {
return info; return info;
} }
HMODULE loadModule(const char *dllName) { ModuleInfo *loadModule(const char *dllName) {
if (!dllName) { if (!dllName || *dllName == '\0') {
lastError = ERROR_INVALID_PARAMETER; lastError = ERROR_INVALID_PARAMETER;
return nullptr; return nullptr;
} }
@ -680,22 +740,19 @@ HMODULE loadModule(const char *dllName) {
fclose(file); fclose(file);
ModulePtr info = std::make_unique<ModuleInfo>(); ModulePtr info = std::make_unique<ModuleInfo>();
info->handle = info.get();
info->module = nullptr; info->module = nullptr;
info->originalName = requested; info->originalName = requested;
info->normalizedName = normalizedBaseKey(parsed); info->normalizedName = normalizedBaseKey(parsed);
info->resolvedPath = files::canonicalPath(path); info->resolvedPath = files::canonicalPath(path);
info->executable = std::move(executable); info->executable = std::move(executable);
info->entryPoint = info->executable->entryPoint;
info->imageBase = info->executable->imageBuffer;
info->imageSize = info->executable->imageSize;
info->refCount = 1; info->refCount = 1;
info->dataFile = false;
info->dontResolveReferences = false;
ModuleInfo *raw = info.get(); ModuleInfo *raw = info.get();
reg->modulesByKey[key] = std::move(info); reg->modulesByKey[key] = std::move(info);
registerExternalModuleAliases(*reg, requested, raw->resolvedPath, raw); registerExternalModuleAliases(*reg, requested, raw->resolvedPath, raw);
ensureExportsInitialized(*raw); ensureExportsInitialized(*raw);
raw->executable->resolveImports();
callDllMain(*raw, DLL_PROCESS_ATTACH); callDllMain(*raw, DLL_PROCESS_ATTACH);
return raw; return raw;
}; };
@ -765,12 +822,8 @@ HMODULE loadModule(const char *dllName) {
return nullptr; return nullptr;
} }
void freeModule(HMODULE module) { void freeModule(ModuleInfo *info) {
if (!module) {
return;
}
auto reg = registry(); auto reg = registry();
ModuleInfo *info = moduleInfoFromHandle(module);
if (!info || info->refCount == UINT_MAX) { if (!info || info->refCount == UINT_MAX) {
return; return;
} }
@ -801,8 +854,7 @@ void freeModule(HMODULE module) {
} }
} }
void *resolveFuncByName(HMODULE module, const char *funcName) { void *resolveFuncByName(ModuleInfo *info, const char *funcName) {
ModuleInfo *info = moduleInfoFromHandle(module);
if (!info) { if (!info) {
return nullptr; return nullptr;
} }
@ -816,14 +868,13 @@ void *resolveFuncByName(HMODULE module, const char *funcName) {
if (!info->module) { if (!info->module) {
auto it = info->exportNameToOrdinal.find(funcName); auto it = info->exportNameToOrdinal.find(funcName);
if (it != info->exportNameToOrdinal.end()) { if (it != info->exportNameToOrdinal.end()) {
return resolveFuncByOrdinal(module, it->second); return resolveFuncByOrdinal(info, it->second);
} }
} }
return reinterpret_cast<void *>(resolveMissingFuncName(info->originalName.c_str(), funcName)); return reinterpret_cast<void *>(resolveMissingFuncName(info->originalName.c_str(), funcName));
} }
void *resolveFuncByOrdinal(HMODULE module, uint16_t ordinal) { void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) {
ModuleInfo *info = moduleInfoFromHandle(module);
if (!info) { if (!info) {
return nullptr; return nullptr;
} }
@ -862,9 +913,6 @@ void *resolveMissingImportByOrdinal(const char *dllName, uint16_t ordinal) {
} }
Executable *executableFromModule(HMODULE module) { Executable *executableFromModule(HMODULE module) {
if (isMainModule(module)) {
return mainModule;
}
ModuleInfo *info = moduleInfoFromHandle(module); ModuleInfo *info = moduleInfoFromHandle(module);
if (!info) { if (!info) {
return nullptr; return nullptr;

View File

@ -1,12 +1,14 @@
#include <windows.h> #include <windows.h>
static int attached = 0; static int attached = 0;
static HMODULE observedMainModule = NULL;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
(void) hinstDLL; (void) hinstDLL;
(void) lpReserved; (void) lpReserved;
if (fdwReason == DLL_PROCESS_ATTACH) { if (fdwReason == DLL_PROCESS_ATTACH) {
attached = 1; attached = 1;
observedMainModule = GetModuleHandleW(NULL);
} else if (fdwReason == DLL_PROCESS_DETACH) { } else if (fdwReason == DLL_PROCESS_DETACH) {
attached = 2; attached = 2;
} }
@ -20,3 +22,7 @@ __declspec(dllexport) int __stdcall add_numbers(int a, int b) {
__declspec(dllexport) int __stdcall was_attached(void) { __declspec(dllexport) int __stdcall was_attached(void) {
return attached; return attached;
} }
__declspec(dllexport) HMODULE __stdcall observed_main_module(void) {
return observedMainModule;
}

View File

@ -7,23 +7,32 @@
int main(void) { int main(void) {
typedef int(__stdcall *add_numbers_fn)(int, int); typedef int(__stdcall *add_numbers_fn)(int, int);
typedef int(__stdcall *was_attached_fn)(void); typedef int(__stdcall *was_attached_fn)(void);
typedef HMODULE(__stdcall *observed_main_module_fn)(void);
HMODULE initial_main = GetModuleHandleW(NULL);
TEST_CHECK(initial_main != NULL);
HMODULE mod = LoadLibraryA("external_exports.dll"); HMODULE mod = LoadLibraryA("external_exports.dll");
TEST_CHECK_MSG(mod != NULL, "LoadLibraryA failed: %lu", (unsigned long)GetLastError()); TEST_CHECK_MSG(mod != NULL, "LoadLibraryA failed: %lu", (unsigned long)GetLastError());
FARPROC raw_add_numbers = GetProcAddress(mod, "add_numbers@8"); FARPROC raw_add_numbers = GetProcAddress(mod, "add_numbers@8");
FARPROC raw_was_attached = GetProcAddress(mod, "was_attached@0"); FARPROC raw_was_attached = GetProcAddress(mod, "was_attached@0");
FARPROC raw_observed_main = GetProcAddress(mod, "observed_main_module@0");
TEST_CHECK_MSG(raw_add_numbers != NULL, "GetProcAddress(add_numbers@8) failed: %lu", (unsigned long)GetLastError()); TEST_CHECK_MSG(raw_add_numbers != NULL, "GetProcAddress(add_numbers@8) failed: %lu", (unsigned long)GetLastError());
TEST_CHECK_MSG(raw_was_attached != NULL, "GetProcAddress(was_attached@0) failed: %lu", (unsigned long)GetLastError()); TEST_CHECK_MSG(raw_was_attached != NULL, "GetProcAddress(was_attached@0) failed: %lu", (unsigned long)GetLastError());
TEST_CHECK_MSG(raw_observed_main != NULL, "GetProcAddress(observed_main_module@0) failed: %lu", (unsigned long)GetLastError());
add_numbers_fn add_numbers = (add_numbers_fn)(uintptr_t)raw_add_numbers; add_numbers_fn add_numbers = (add_numbers_fn)(uintptr_t)raw_add_numbers;
was_attached_fn was_attached = (was_attached_fn)(uintptr_t)raw_was_attached; was_attached_fn was_attached = (was_attached_fn)(uintptr_t)raw_was_attached;
observed_main_module_fn observed_main = (observed_main_module_fn)(uintptr_t)raw_observed_main;
int sum = add_numbers(2, 40); int sum = add_numbers(2, 40);
int attached = was_attached(); int attached = was_attached();
HMODULE observed_main_module = observed_main();
TEST_CHECK_EQ(42, sum); TEST_CHECK_EQ(42, sum);
TEST_CHECK_EQ(1, attached); TEST_CHECK_EQ(1, attached);
TEST_CHECK_EQ(initial_main, observed_main_module);
TEST_CHECK_MSG(FreeLibrary(mod) != 0, "FreeLibrary failed: %lu", (unsigned long)GetLastError()); TEST_CHECK_MSG(FreeLibrary(mod) != 0, "FreeLibrary failed: %lu", (unsigned long)GetLastError());