mirror of
https://github.com/decompals/wibo.git
synced 2025-10-15 14:45:12 +00:00
Refactor main module resolution & HMODULE handle behavior
This commit is contained in:
parent
4a2ba45620
commit
c17953b318
@ -7,7 +7,7 @@
|
||||
- Sample fixtures for exercising the loader live in `test/`; keep new repros small and self-contained.
|
||||
|
||||
## 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.
|
||||
- `./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`).
|
||||
|
@ -7,11 +7,11 @@ Don't run this on any untrusted executables, I implore you. (Or probably just do
|
||||
## Building
|
||||
|
||||
```sh
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Debug
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
|
||||
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
|
||||
|
||||
|
80
common.h
80
common.h
@ -70,6 +70,7 @@ typedef unsigned char BYTE;
|
||||
#define ERROR_RESOURCE_NAME_NOT_FOUND 1814
|
||||
#define ERROR_RESOURCE_LANG_NOT_FOUND 1815
|
||||
#define ERROR_MOD_NOT_FOUND 126
|
||||
#define ERROR_PROC_NOT_FOUND 127
|
||||
#define ERROR_NEGATIVE_SEEK 131
|
||||
#define ERROR_BAD_EXE_FORMAT 193
|
||||
#define ERROR_ALREADY_EXISTS 183
|
||||
@ -102,6 +103,7 @@ namespace wibo {
|
||||
extern uint32_t lastError;
|
||||
extern char **argv;
|
||||
extern int argc;
|
||||
extern std::filesystem::path guestExecutablePath;
|
||||
extern std::string executableName;
|
||||
extern std::string commandLine;
|
||||
extern std::vector<uint16_t> commandLineW;
|
||||
@ -125,16 +127,16 @@ namespace wibo {
|
||||
void setDllDirectoryOverride(const std::filesystem::path &path);
|
||||
void clearDllDirectoryOverride();
|
||||
std::optional<std::filesystem::path> dllDirectoryOverride();
|
||||
HMODULE findLoadedModule(const char *name);
|
||||
ModuleInfo *findLoadedModule(const char *name);
|
||||
void registerOnExitTable(void *table);
|
||||
void addOnExitFunction(void *table, void (*func)());
|
||||
void executeOnExitTable(void *table);
|
||||
void runPendingOnExit(ModuleInfo &info);
|
||||
|
||||
HMODULE loadModule(const char *name);
|
||||
void freeModule(HMODULE module);
|
||||
void *resolveFuncByName(HMODULE module, const char *funcName);
|
||||
void *resolveFuncByOrdinal(HMODULE module, uint16_t ordinal);
|
||||
ModuleInfo *loadModule(const char *name);
|
||||
void freeModule(ModuleInfo *info);
|
||||
void *resolveFuncByName(ModuleInfo *info, const char *funcName);
|
||||
void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal);
|
||||
void *resolveMissingImportByName(const char *dllName, const char *funcName);
|
||||
void *resolveMissingImportByOrdinal(const char *dllName, uint16_t ordinal);
|
||||
|
||||
@ -172,51 +174,63 @@ namespace wibo {
|
||||
};
|
||||
|
||||
struct Executable {
|
||||
Executable();
|
||||
Executable() = default;
|
||||
~Executable();
|
||||
bool loadPE(FILE *file, bool exec);
|
||||
bool resolveImports();
|
||||
|
||||
void *imageBuffer;
|
||||
size_t imageSize;
|
||||
void *entryPoint;
|
||||
void *rsrcBase;
|
||||
uint32_t rsrcSize;
|
||||
uintptr_t preferredImageBase;
|
||||
intptr_t relocationDelta;
|
||||
uint32_t exportDirectoryRVA;
|
||||
uint32_t exportDirectorySize;
|
||||
uint32_t relocationDirectoryRVA;
|
||||
uint32_t relocationDirectorySize;
|
||||
void *imageBase = nullptr;
|
||||
size_t imageSize = 0;
|
||||
void *entryPoint = nullptr;
|
||||
void *rsrcBase = nullptr;
|
||||
uint32_t rsrcSize = 0;
|
||||
uintptr_t preferredImageBase = 0;
|
||||
intptr_t relocationDelta = 0;
|
||||
uint32_t exportDirectoryRVA = 0;
|
||||
uint32_t exportDirectorySize = 0;
|
||||
uint32_t relocationDirectoryRVA = 0;
|
||||
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,
|
||||
const ResourceIdentifier &name,
|
||||
std::optional<uint16_t> language,
|
||||
ResourceLocation &out) const;
|
||||
bool findResource(const ResourceIdentifier &type, const ResourceIdentifier &name,
|
||||
std::optional<uint16_t> language, ResourceLocation &out) const;
|
||||
|
||||
template <typename T>
|
||||
T *fromRVA(uint32_t rva) const {
|
||||
return (T *) (rva + (uint8_t *) imageBuffer);
|
||||
T *fromRVA(uintptr_t rva) const {
|
||||
return (T *) (rva + (uint8_t *) imageBase);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T *fromRVA(T *rva) const {
|
||||
return fromRVA<T>((uint32_t) rva);
|
||||
return fromRVA<T>((uintptr_t) rva);
|
||||
}
|
||||
};
|
||||
|
||||
extern ModuleInfo *mainModule;
|
||||
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;
|
||||
// Normalized module name
|
||||
std::string normalizedName;
|
||||
// Full path to the loaded module
|
||||
std::filesystem::path resolvedPath;
|
||||
// Pointer to the built-in module, nullptr if loaded from file
|
||||
const wibo::Module *module = nullptr;
|
||||
// Loaded PE executable
|
||||
std::unique_ptr<wibo::Executable> executable;
|
||||
void *entryPoint = nullptr;
|
||||
void *imageBase = nullptr;
|
||||
size_t imageSize = 0;
|
||||
// Reference count, or UINT_MAX for built-in modules
|
||||
unsigned int refCount = 0;
|
||||
bool dataFile = false;
|
||||
bool processAttachCalled = false;
|
||||
bool processAttachSucceeded = false;
|
||||
bool dontResolveReferences = false;
|
||||
uint32_t exportOrdinalBase = 0;
|
||||
std::vector<void *> exportsByOrdinal;
|
||||
std::unordered_map<std::string, uint16_t> exportNameToOrdinal;
|
||||
@ -224,14 +238,16 @@ namespace wibo {
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
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
|
||||
|
@ -522,7 +522,7 @@ namespace advapi32 {
|
||||
return FALSE;
|
||||
}
|
||||
auto *stats = reinterpret_cast<TokenStatisticsData *>(TokenInformation);
|
||||
memset(stats, 0, required);
|
||||
*stats = {};
|
||||
stats->tokenType = 1; // TokenPrimary
|
||||
stats->impersonationLevel = 0; // SecurityAnonymous
|
||||
stats->tokenId.LowPart = 1;
|
||||
|
@ -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); }
|
||||
|
||||
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 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;
|
||||
if (strcmp(name, "strncmp") == 0)
|
||||
return (void *)crt::strncmp;
|
||||
if (strcmp(name, "strcpy") == 0)
|
||||
return (void *)crt::strcpy;
|
||||
if (strcmp(name, "malloc") == 0)
|
||||
return (void *)crt::malloc;
|
||||
if (strcmp(name, "calloc") == 0)
|
||||
|
129
dll/kernel32.cpp
129
dll/kernel32.cpp
@ -2685,17 +2685,13 @@ namespace kernel32 {
|
||||
|
||||
HMODULE WIN_FUNC GetModuleHandleA(LPCSTR lpModuleName) {
|
||||
DEBUG_LOG("GetModuleHandleA(%s)\n", lpModuleName);
|
||||
|
||||
if (!lpModuleName) {
|
||||
// If lpModuleName is NULL, GetModuleHandle returns a handle to the file
|
||||
// used to create the calling process (.exe file).
|
||||
// This handle needs to equal the actual image buffer, from which data can be read.
|
||||
return wibo::mainModule->imageBuffer;
|
||||
const auto* module = wibo::findLoadedModule(lpModuleName);
|
||||
if (!module) {
|
||||
wibo::lastError = ERROR_MOD_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HMODULE module = wibo::findLoadedModule(lpModuleName);
|
||||
wibo::lastError = module ? ERROR_SUCCESS : ERROR_MOD_NOT_FOUND;
|
||||
return module;
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
return module->handle;
|
||||
}
|
||||
|
||||
HMODULE WIN_FUNC GetModuleHandleW(LPCWSTR lpModuleName) {
|
||||
@ -2715,22 +2711,16 @@ namespace kernel32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto *info = wibo::moduleInfoFromHandle(hModule);
|
||||
if (!info) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
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);
|
||||
if (!info->resolvedPath.empty()) {
|
||||
path = files::pathToWindows(info->resolvedPath);
|
||||
} else {
|
||||
auto *info = wibo::moduleInfoFromHandle(hModule);
|
||||
if (!info) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return 0;
|
||||
}
|
||||
if (!info->resolvedPath.empty()) {
|
||||
path = files::pathToWindows(info->resolvedPath);
|
||||
} else {
|
||||
path = info->originalName;
|
||||
}
|
||||
path = info->originalName;
|
||||
}
|
||||
const size_t len = path.size();
|
||||
if (nSize == 0) {
|
||||
@ -2759,22 +2749,16 @@ namespace kernel32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto *info = wibo::moduleInfoFromHandle(hModule);
|
||||
if (!info) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
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);
|
||||
if (!info->resolvedPath.empty()) {
|
||||
path = files::pathToWindows(info->resolvedPath);
|
||||
} else {
|
||||
auto *info = wibo::moduleInfoFromHandle(hModule);
|
||||
if (!info) {
|
||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||
return 0;
|
||||
}
|
||||
if (!info->resolvedPath.empty()) {
|
||||
path = files::pathToWindows(info->resolvedPath);
|
||||
} else {
|
||||
path = info->originalName;
|
||||
}
|
||||
path = info->originalName;
|
||||
}
|
||||
if (nSize == 0) {
|
||||
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
|
||||
@ -2802,18 +2786,9 @@ namespace kernel32 {
|
||||
return copyLen;
|
||||
}
|
||||
|
||||
static wibo::Executable *module_executable_for_resource(void *hModule) {
|
||||
if (!hModule) {
|
||||
hModule = GetModuleHandleA(nullptr);
|
||||
}
|
||||
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);
|
||||
static void *findResourceInternal(HMODULE hModule, const wibo::ResourceIdentifier &type,
|
||||
const wibo::ResourceIdentifier &name, std::optional<uint16_t> language) {
|
||||
auto *exe = wibo::executableFromModule(hModule);
|
||||
if (!exe) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return nullptr;
|
||||
@ -2825,41 +2800,41 @@ namespace kernel32 {
|
||||
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);
|
||||
auto type = wibo::resourceIdentifierFromAnsi(lpType);
|
||||
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);
|
||||
auto type = wibo::resourceIdentifierFromAnsi(lpType);
|
||||
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);
|
||||
auto type = wibo::resourceIdentifierFromWide(lpType);
|
||||
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);
|
||||
auto type = wibo::resourceIdentifierFromWide(lpType);
|
||||
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);
|
||||
if (!res) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
auto *exe = module_executable_for_resource(hModule);
|
||||
auto *exe = wibo::executableFromModule(hModule);
|
||||
if (!exe || !exe->rsrcBase) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return nullptr;
|
||||
@ -2901,13 +2876,13 @@ namespace kernel32 {
|
||||
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);
|
||||
if (!res) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return 0;
|
||||
}
|
||||
auto *exe = module_executable_for_resource(hModule);
|
||||
auto *exe = wibo::executableFromModule(hModule);
|
||||
if (!exe || !exe->rsrcBase) {
|
||||
wibo::lastError = ERROR_RESOURCE_DATA_NOT_FOUND;
|
||||
return 0;
|
||||
@ -2922,7 +2897,13 @@ namespace kernel32 {
|
||||
|
||||
HMODULE WIN_FUNC LoadLibraryA(LPCSTR 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) {
|
||||
@ -2943,7 +2924,12 @@ namespace kernel32 {
|
||||
|
||||
BOOL WIN_FUNC FreeLibrary(HMODULE 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;
|
||||
}
|
||||
|
||||
@ -3664,15 +3650,26 @@ namespace kernel32 {
|
||||
|
||||
FARPROC WIN_FUNC GetProcAddress(HMODULE hModule, LPCSTR lpProcName) {
|
||||
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);
|
||||
if (proc & ~0xFFFF) {
|
||||
DEBUG_LOG("GetProcAddress(%p, %s) ", hModule, lpProcName);
|
||||
result = wibo::resolveFuncByName(hModule, lpProcName);
|
||||
result = wibo::resolveFuncByName(info, lpProcName);
|
||||
} else {
|
||||
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);
|
||||
if (!result) {
|
||||
wibo::lastError = ERROR_PROC_NOT_FOUND;
|
||||
} else {
|
||||
wibo::lastError = ERROR_SUCCESS;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -169,8 +169,8 @@ namespace msvcrt {
|
||||
std::string converted = files::hostPathListToWindows(value);
|
||||
std::string result = converted.empty() ? value : converted;
|
||||
std::string exeDir;
|
||||
if (wibo::argv && wibo::argv[0]) {
|
||||
std::filesystem::path exePath = std::filesystem::absolute(std::filesystem::path(wibo::argv[0])).parent_path();
|
||||
if (!wibo::guestExecutablePath.empty()) {
|
||||
auto exePath = wibo::guestExecutablePath.parent_path();
|
||||
if (!exePath.empty()) {
|
||||
exeDir = files::pathToWindows(exePath);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ BOOL WIN_FUNC EnumProcessModules(HANDLE hProcess, HMODULE *lphModule, DWORD cb,
|
||||
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;
|
||||
if (lpcbNeeded) {
|
||||
*lpcbNeeded = required;
|
||||
|
119
loader.cpp
119
loader.cpp
@ -52,12 +52,12 @@ struct PE32Header {
|
||||
uint32_t loaderFlags;
|
||||
uint32_t numberOfRvaAndSizes;
|
||||
PEImageDataDirectory exportTable;
|
||||
PEImageDataDirectory importTable; // *
|
||||
PEImageDataDirectory importTable; // *
|
||||
PEImageDataDirectory resourceTable; // *
|
||||
PEImageDataDirectory exceptionTable;
|
||||
PEImageDataDirectory certificateTable;
|
||||
PEImageDataDirectory baseRelocationTable; // *
|
||||
PEImageDataDirectory debug; // *
|
||||
PEImageDataDirectory debug; // *
|
||||
PEImageDataDirectory architecture;
|
||||
PEImageDataDirectory globalPtr;
|
||||
PEImageDataDirectory tlsTable;
|
||||
@ -122,24 +122,10 @@ uint32_t read32(FILE *file) {
|
||||
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() {
|
||||
if (imageBuffer) {
|
||||
munmap(imageBuffer, imageSize);
|
||||
if (imageBase) {
|
||||
munmap(imageBase, imageSize);
|
||||
imageBase = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +153,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
|
||||
PE32Header 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)
|
||||
return false;
|
||||
|
||||
@ -181,6 +167,13 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
exportDirectorySize = header32.exportTable.size;
|
||||
relocationDirectoryRVA = header32.baseRelocationTable.virtualAddress;
|
||||
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
|
||||
imageSize = header32.sizeOfImage;
|
||||
@ -188,17 +181,17 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
if (exec)
|
||||
prot |= PROT_EXEC;
|
||||
void *preferredBase = (void *)(uintptr_t)header32.imageBase;
|
||||
imageBuffer = mmap(preferredBase, header32.sizeOfImage, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (imageBuffer == MAP_FAILED) {
|
||||
imageBuffer = mmap(nullptr, header32.sizeOfImage, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
imageBase = mmap(preferredBase, header32.sizeOfImage, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (imageBase == MAP_FAILED) {
|
||||
imageBase = mmap(nullptr, header32.sizeOfImage, prot, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
}
|
||||
if (imageBuffer == MAP_FAILED) {
|
||||
if (imageBase == MAP_FAILED) {
|
||||
perror("Image mapping failed!");
|
||||
imageBuffer = nullptr;
|
||||
imageBase = nullptr;
|
||||
return false;
|
||||
}
|
||||
relocationDelta = (intptr_t)((uintptr_t)imageBuffer - (uintptr_t)header32.imageBase);
|
||||
memset(imageBuffer, 0, header32.sizeOfImage);
|
||||
relocationDelta = (intptr_t)((uintptr_t)imageBase - (uintptr_t)header32.imageBase);
|
||||
memset(imageBase, 0, header32.sizeOfImage);
|
||||
|
||||
// Read the sections
|
||||
fseek(file, offsetToPE + sizeof header + header.sizeOfOptionalHeader, SEEK_SET);
|
||||
@ -210,9 +203,10 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
char name[9];
|
||||
memcpy(name, section.name, 8);
|
||||
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) {
|
||||
// Grab this data
|
||||
long savePos = ftell(file);
|
||||
@ -230,8 +224,8 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
if (exec && relocationDelta != 0) {
|
||||
if (relocationDirectoryRVA == 0 || relocationDirectorySize == 0) {
|
||||
DEBUG_LOG("Relocation required but no relocation directory present\n");
|
||||
munmap(imageBuffer, imageSize);
|
||||
imageBuffer = nullptr;
|
||||
munmap(imageBase, imageSize);
|
||||
imageBase = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -239,7 +233,8 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
uint8_t *relocEnd = relocCursor + relocationDirectorySize;
|
||||
while (relocCursor < relocEnd) {
|
||||
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;
|
||||
}
|
||||
if (block->sizeOfBlock == sizeof(PEBaseRelocationBlock)) {
|
||||
@ -253,7 +248,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
uint16_t offset = entry & 0x0FFF;
|
||||
if (type == IMAGE_REL_BASED_ABSOLUTE)
|
||||
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) {
|
||||
case IMAGE_REL_BASED_HIGHLOW: {
|
||||
auto *addr = reinterpret_cast<uint32_t *>(target);
|
||||
@ -269,13 +264,34 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!exec) {
|
||||
// No need to resolve imports
|
||||
entryPoint = header32.addressOfEntryPoint ? fromRVA<void>(header32.addressOfEntryPoint) : nullptr;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Handle imports
|
||||
PEImportDirectoryEntry *dir = fromRVA<PEImportDirectoryEntry>(header32.importTable.virtualAddress);
|
||||
PEImportDirectoryEntry *dir = fromRVA<PEImportDirectoryEntry>(importDirectoryRVA);
|
||||
if (!dir) {
|
||||
importsResolved = true;
|
||||
importsResolving = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
while (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 *addressTable = fromRVA(dir->importAddressTable);
|
||||
|
||||
HMODULE module = loadModule(dllName);
|
||||
ModuleInfo *module = loadModule(dllName);
|
||||
while (*lookupTable) {
|
||||
uint32_t lookup = *lookupTable;
|
||||
if (lookup & 0x80000000) {
|
||||
// Import by ordinal
|
||||
uint16_t ordinal = lookup & 0xFFFF;
|
||||
DEBUG_LOG(" Ordinal: %d\n", ordinal);
|
||||
void *func = module ? resolveFuncByOrdinal(module, ordinal)
|
||||
: resolveMissingImportByOrdinal(dllName, ordinal);
|
||||
void *func =
|
||||
module ? resolveFuncByOrdinal(module, ordinal) : resolveMissingImportByOrdinal(dllName, ordinal);
|
||||
DEBUG_LOG(" -> %p\n", func);
|
||||
*addressTable = reinterpret_cast<uintptr_t>(func);
|
||||
} else {
|
||||
@ -299,7 +315,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
PEHintNameTableEntry *hintName = fromRVA<PEHintNameTableEntry>(lookup);
|
||||
DEBUG_LOG(" Name: %s (IAT=%p)\n", hintName->name, addressTable);
|
||||
void *func = module ? resolveFuncByName(module, hintName->name)
|
||||
: resolveMissingImportByName(dllName, hintName->name);
|
||||
: resolveMissingImportByName(dllName, hintName->name);
|
||||
DEBUG_LOG(" -> %p\n", func);
|
||||
*addressTable = reinterpret_cast<uintptr_t>(func);
|
||||
}
|
||||
@ -309,28 +325,29 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
||||
++dir;
|
||||
}
|
||||
|
||||
if (header32.delayImportDescriptor.virtualAddress) {
|
||||
DEBUG_LOG("Processing delay import table at RVA %x\n", header32.delayImportDescriptor.virtualAddress);
|
||||
PEDelayImportDescriptor *delay = fromRVA<PEDelayImportDescriptor>(header32.delayImportDescriptor.virtualAddress);
|
||||
while (delay->name) {
|
||||
// TODO: actual delay loading from __delayLoadHelper2
|
||||
if (delayImportDirectoryRVA) {
|
||||
DEBUG_LOG("Processing delay import table at RVA %x\n", delayImportDirectoryRVA);
|
||||
PEDelayImportDescriptor *delay = fromRVA<PEDelayImportDescriptor>(delayImportDirectoryRVA);
|
||||
while (delay && delay->name) {
|
||||
char *dllName = fromRVA<char>(delay->name);
|
||||
DEBUG_LOG("Delay DLL Name: %s\n", dllName);
|
||||
uint32_t *lookupTable = fromRVA<uint32_t>(delay->importNameTable);
|
||||
uint32_t *addressTable = fromRVA<uint32_t>(delay->importAddressTable);
|
||||
HMODULE module = loadModule(dllName);
|
||||
ModuleInfo *module = loadModule(dllName);
|
||||
while (*lookupTable) {
|
||||
uint32_t lookup = *lookupTable;
|
||||
if (lookup & 0x80000000) {
|
||||
uint16_t ordinal = lookup & 0xFFFF;
|
||||
DEBUG_LOG(" Ordinal: %d (IAT=%p)\n", ordinal, addressTable);
|
||||
DEBUG_LOG(" Ordinal: %d (IAT=%p)\n", ordinal, addressTable);
|
||||
void *func = module ? resolveFuncByOrdinal(module, ordinal)
|
||||
: resolveMissingImportByOrdinal(dllName, ordinal);
|
||||
: resolveMissingImportByOrdinal(dllName, ordinal);
|
||||
*addressTable = reinterpret_cast<uintptr_t>(func);
|
||||
} else {
|
||||
PEHintNameTableEntry *hintName = fromRVA<PEHintNameTableEntry>(lookup);
|
||||
DEBUG_LOG(" Name: %s\n", hintName->name);
|
||||
void *func = module ? resolveFuncByName(module, hintName->name)
|
||||
: resolveMissingImportByName(dllName, hintName->name);
|
||||
: resolveMissingImportByName(dllName, hintName->name);
|
||||
*addressTable = reinterpret_cast<uintptr_t>(func);
|
||||
}
|
||||
++lookupTable;
|
||||
@ -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;
|
||||
}
|
||||
|
80
main.cpp
80
main.cpp
@ -1,26 +1,28 @@
|
||||
#include "common.h"
|
||||
#include "files.h"
|
||||
#include "processes.h"
|
||||
#include "strutil.h"
|
||||
#include <asm/ldt.h>
|
||||
#include <charconv>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <stdarg.h>
|
||||
#include <system_error>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <system_error>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
uint32_t wibo::lastError = 0;
|
||||
char** wibo::argv;
|
||||
char **wibo::argv;
|
||||
int wibo::argc;
|
||||
std::filesystem::path wibo::guestExecutablePath;
|
||||
std::string wibo::executableName;
|
||||
std::string wibo::commandLine;
|
||||
std::vector<uint16_t> wibo::commandLineW;
|
||||
wibo::Executable *wibo::mainModule = 0;
|
||||
wibo::ModuleInfo *wibo::mainModule = nullptr;
|
||||
bool wibo::debugEnabled = false;
|
||||
unsigned int wibo::debugIndent = 0;
|
||||
uint16_t wibo::tibSelector = 0;
|
||||
@ -109,7 +111,7 @@ static void printHelp(const char *argv0) {
|
||||
* @param buffer The buffer to read into.
|
||||
* @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);
|
||||
if (fd == -1) {
|
||||
perror("Failed to open /proc/self/maps");
|
||||
@ -187,7 +189,8 @@ static void blockUpper2GB() {
|
||||
holdingMapStart = std::max(holdingMapStart, FILL_MEMORY_ABOVE);
|
||||
|
||||
// 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) {
|
||||
perror("Failed to create holding map");
|
||||
@ -288,13 +291,13 @@ int main(int argc, char **argv) {
|
||||
// Create TIB
|
||||
memset(&tib, 0, sizeof(tib));
|
||||
tib.tib = &tib;
|
||||
tib.peb = (PEB*)calloc(sizeof(PEB), 1);
|
||||
tib.peb->ProcessParameters = (RTL_USER_PROCESS_PARAMETERS*)calloc(sizeof(RTL_USER_PROCESS_PARAMETERS), 1);
|
||||
tib.peb = (PEB *)calloc(sizeof(PEB), 1);
|
||||
tib.peb->ProcessParameters = (RTL_USER_PROCESS_PARAMETERS *)calloc(sizeof(RTL_USER_PROCESS_PARAMETERS), 1);
|
||||
|
||||
struct user_desc tibDesc;
|
||||
memset(&tibDesc, 0, sizeof tibDesc);
|
||||
tibDesc.entry_number = 0;
|
||||
tibDesc.base_addr = (unsigned int) &tib;
|
||||
tibDesc.base_addr = (unsigned int)&tib;
|
||||
tibDesc.limit = 0x1000;
|
||||
tibDesc.seg_32bit = 1;
|
||||
tibDesc.contents = 0; // hopefully this is ok
|
||||
@ -312,12 +315,25 @@ int main(int argc, char **argv) {
|
||||
char **guestArgv = argv + 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
|
||||
std::string cmdLine;
|
||||
for (int i = 0; i < guestArgc; ++i) {
|
||||
std::string arg;
|
||||
if (i == 0) {
|
||||
arg = files::pathToWindows(std::filesystem::absolute(guestArgv[0]));
|
||||
arg = files::pathToWindows(resolvedGuestPath);
|
||||
} else {
|
||||
cmdLine += ' ';
|
||||
arg = guestArgv[i];
|
||||
@ -326,7 +342,7 @@ int main(int argc, char **argv) {
|
||||
if (needQuotes)
|
||||
cmdLine += '"';
|
||||
int backslashes = 0;
|
||||
for (const char *p = arg.c_str(); ; p++) {
|
||||
for (const char *p = arg.c_str();; p++) {
|
||||
char c = *p;
|
||||
if (c == '\\') {
|
||||
backslashes++;
|
||||
@ -356,32 +372,50 @@ int main(int argc, char **argv) {
|
||||
wibo::commandLineW = stringToWideString(wibo::commandLine.c_str());
|
||||
DEBUG_LOG("Command line: %s\n", wibo::commandLine.c_str());
|
||||
|
||||
wibo::guestExecutablePath = resolvedGuestPath;
|
||||
wibo::executableName = executablePath;
|
||||
wibo::argv = guestArgv;
|
||||
wibo::argc = guestArgc;
|
||||
|
||||
wibo::initializeModuleRegistry();
|
||||
|
||||
wibo::Executable exec;
|
||||
wibo::mainModule = &exec;
|
||||
|
||||
char* pe_path = guestArgv[0];
|
||||
FILE *f = fopen(pe_path, "rb");
|
||||
FILE *f = fopen(resolvedGuestPath.c_str(), "rb");
|
||||
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());
|
||||
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);
|
||||
|
||||
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
|
||||
asm(
|
||||
"movw %0, %%fs; call *%1"
|
||||
:
|
||||
: "r"(wibo::tibSelector), "r"(exec.entryPoint)
|
||||
);
|
||||
asm("movw %0, %%fs; call *%1" : : "r"(wibo::tibSelector), "r"(entryPoint));
|
||||
DEBUG_LOG("We came back\n");
|
||||
wibo::shutdownModuleRegistry();
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <climits>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@ -253,10 +252,10 @@ std::vector<std::filesystem::path> collectSearchDirectories(ModuleRegistry ®,
|
||||
}
|
||||
};
|
||||
|
||||
if (wibo::argv && wibo::argc > 0 && wibo::argv[0]) {
|
||||
std::filesystem::path mainBinary = std::filesystem::absolute(wibo::argv[0]);
|
||||
if (mainBinary.has_parent_path()) {
|
||||
addDirectory(mainBinary.parent_path());
|
||||
if (!wibo::guestExecutablePath.empty()) {
|
||||
auto parent = wibo::guestExecutablePath.parent_path();
|
||||
if (!parent.empty()) {
|
||||
addDirectory(parent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -369,6 +368,7 @@ void registerBuiltinModule(ModuleRegistry ®, const wibo::Module *module) {
|
||||
return;
|
||||
}
|
||||
ModulePtr entry = std::make_unique<wibo::ModuleInfo>();
|
||||
entry->handle = entry.get();
|
||||
entry->module = module;
|
||||
entry->refCount = UINT_MAX;
|
||||
entry->originalName = module->names[0] ? module->names[0] : "";
|
||||
@ -406,24 +406,24 @@ void registerBuiltinModule(ModuleRegistry ®, const wibo::Module *module) {
|
||||
}
|
||||
|
||||
void callDllMain(wibo::ModuleInfo &info, DWORD reason) {
|
||||
if (!info.entryPoint || info.module) {
|
||||
if (!info.executable) {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto invokeWithGuestTIB = [&](DWORD callReason) -> BOOL {
|
||||
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;
|
||||
asm volatile("mov %%fs, %0" : "=r"(previousSegment));
|
||||
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");
|
||||
return result;
|
||||
};
|
||||
@ -455,20 +455,13 @@ wibo::ModuleInfo *moduleFromAddress(ModuleRegistry ®, void *addr) {
|
||||
return nullptr;
|
||||
for (auto &pair : reg.modulesByKey) {
|
||||
wibo::ModuleInfo *info = pair.second.get();
|
||||
if (!info)
|
||||
if (!info || !info->executable)
|
||||
continue;
|
||||
uint8_t *base = nullptr;
|
||||
size_t size = 0;
|
||||
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;
|
||||
}
|
||||
const auto *base = static_cast<const uint8_t *>(info->executable->imageBase);
|
||||
size_t size = info->executable->imageSize;
|
||||
if (!base || size == 0)
|
||||
continue;
|
||||
auto *ptr = static_cast<uint8_t *>(addr);
|
||||
const auto *ptr = static_cast<const uint8_t *>(addr);
|
||||
if (ptr >= base && ptr < base + size) {
|
||||
return info;
|
||||
}
|
||||
@ -530,6 +523,68 @@ namespace wibo {
|
||||
|
||||
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() {
|
||||
auto reg = registry();
|
||||
for (auto &pair : reg->modulesByKey) {
|
||||
@ -549,7 +604,12 @@ void shutdownModuleRegistry() {
|
||||
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) {
|
||||
auto canonical = files::canonicalPath(path);
|
||||
@ -598,7 +658,7 @@ void addOnExitFunction(void *table, void (*func)()) {
|
||||
|
||||
void runPendingOnExit(ModuleInfo &info) {
|
||||
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) {
|
||||
fn();
|
||||
}
|
||||
@ -623,9 +683,9 @@ void executeOnExitTable(void *table) {
|
||||
}
|
||||
}
|
||||
|
||||
HMODULE findLoadedModule(const char *name) {
|
||||
if (!name) {
|
||||
return nullptr;
|
||||
ModuleInfo *findLoadedModule(const char *name) {
|
||||
if (!name || *name == '\0') {
|
||||
return wibo::mainModule;
|
||||
}
|
||||
auto reg = registry();
|
||||
ParsedModuleName parsed = parseModuleName(name);
|
||||
@ -637,8 +697,8 @@ HMODULE findLoadedModule(const char *name) {
|
||||
return info;
|
||||
}
|
||||
|
||||
HMODULE loadModule(const char *dllName) {
|
||||
if (!dllName) {
|
||||
ModuleInfo *loadModule(const char *dllName) {
|
||||
if (!dllName || *dllName == '\0') {
|
||||
lastError = ERROR_INVALID_PARAMETER;
|
||||
return nullptr;
|
||||
}
|
||||
@ -680,22 +740,19 @@ HMODULE loadModule(const char *dllName) {
|
||||
fclose(file);
|
||||
|
||||
ModulePtr info = std::make_unique<ModuleInfo>();
|
||||
info->handle = info.get();
|
||||
info->module = nullptr;
|
||||
info->originalName = requested;
|
||||
info->normalizedName = normalizedBaseKey(parsed);
|
||||
info->resolvedPath = files::canonicalPath(path);
|
||||
info->executable = std::move(executable);
|
||||
info->entryPoint = info->executable->entryPoint;
|
||||
info->imageBase = info->executable->imageBuffer;
|
||||
info->imageSize = info->executable->imageSize;
|
||||
info->refCount = 1;
|
||||
info->dataFile = false;
|
||||
info->dontResolveReferences = false;
|
||||
|
||||
ModuleInfo *raw = info.get();
|
||||
reg->modulesByKey[key] = std::move(info);
|
||||
registerExternalModuleAliases(*reg, requested, raw->resolvedPath, raw);
|
||||
ensureExportsInitialized(*raw);
|
||||
raw->executable->resolveImports();
|
||||
callDllMain(*raw, DLL_PROCESS_ATTACH);
|
||||
return raw;
|
||||
};
|
||||
@ -765,12 +822,8 @@ HMODULE loadModule(const char *dllName) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void freeModule(HMODULE module) {
|
||||
if (!module) {
|
||||
return;
|
||||
}
|
||||
void freeModule(ModuleInfo *info) {
|
||||
auto reg = registry();
|
||||
ModuleInfo *info = moduleInfoFromHandle(module);
|
||||
if (!info || info->refCount == UINT_MAX) {
|
||||
return;
|
||||
}
|
||||
@ -801,8 +854,7 @@ void freeModule(HMODULE module) {
|
||||
}
|
||||
}
|
||||
|
||||
void *resolveFuncByName(HMODULE module, const char *funcName) {
|
||||
ModuleInfo *info = moduleInfoFromHandle(module);
|
||||
void *resolveFuncByName(ModuleInfo *info, const char *funcName) {
|
||||
if (!info) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -816,14 +868,13 @@ void *resolveFuncByName(HMODULE module, const char *funcName) {
|
||||
if (!info->module) {
|
||||
auto it = info->exportNameToOrdinal.find(funcName);
|
||||
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));
|
||||
}
|
||||
|
||||
void *resolveFuncByOrdinal(HMODULE module, uint16_t ordinal) {
|
||||
ModuleInfo *info = moduleInfoFromHandle(module);
|
||||
void *resolveFuncByOrdinal(ModuleInfo *info, uint16_t ordinal) {
|
||||
if (!info) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -862,9 +913,6 @@ void *resolveMissingImportByOrdinal(const char *dllName, uint16_t ordinal) {
|
||||
}
|
||||
|
||||
Executable *executableFromModule(HMODULE module) {
|
||||
if (isMainModule(module)) {
|
||||
return mainModule;
|
||||
}
|
||||
ModuleInfo *info = moduleInfoFromHandle(module);
|
||||
if (!info) {
|
||||
return nullptr;
|
||||
|
@ -1,12 +1,14 @@
|
||||
#include <windows.h>
|
||||
|
||||
static int attached = 0;
|
||||
static HMODULE observedMainModule = NULL;
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
|
||||
(void) hinstDLL;
|
||||
(void) lpReserved;
|
||||
if (fdwReason == DLL_PROCESS_ATTACH) {
|
||||
attached = 1;
|
||||
observedMainModule = GetModuleHandleW(NULL);
|
||||
} else if (fdwReason == DLL_PROCESS_DETACH) {
|
||||
attached = 2;
|
||||
}
|
||||
@ -20,3 +22,7 @@ __declspec(dllexport) int __stdcall add_numbers(int a, int b) {
|
||||
__declspec(dllexport) int __stdcall was_attached(void) {
|
||||
return attached;
|
||||
}
|
||||
|
||||
__declspec(dllexport) HMODULE __stdcall observed_main_module(void) {
|
||||
return observedMainModule;
|
||||
}
|
||||
|
@ -7,23 +7,32 @@
|
||||
int main(void) {
|
||||
typedef int(__stdcall *add_numbers_fn)(int, int);
|
||||
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");
|
||||
TEST_CHECK_MSG(mod != NULL, "LoadLibraryA failed: %lu", (unsigned long)GetLastError());
|
||||
|
||||
FARPROC raw_add_numbers = GetProcAddress(mod, "add_numbers@8");
|
||||
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_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;
|
||||
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 attached = was_attached();
|
||||
HMODULE observed_main_module = observed_main();
|
||||
|
||||
TEST_CHECK_EQ(42, sum);
|
||||
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());
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user