mirror of
https://github.com/decompals/wibo.git
synced 2025-10-16 07:05:11 +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.
|
- 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`).
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
80
common.h
80
common.h
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
129
dll/kernel32.cpp
129
dll/kernel32.cpp
@ -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,22 +2711,16 @@ namespace kernel32 {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto *info = wibo::moduleInfoFromHandle(hModule);
|
||||||
|
if (!info) {
|
||||||
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
std::string path;
|
std::string path;
|
||||||
if (wibo::isMainModule(hModule)) {
|
if (!info->resolvedPath.empty()) {
|
||||||
const auto exePath = files::pathFromWindows(wibo::argv[0]);
|
path = files::pathToWindows(info->resolvedPath);
|
||||||
const auto absPath = std::filesystem::absolute(exePath);
|
|
||||||
path = files::pathToWindows(absPath);
|
|
||||||
} else {
|
} else {
|
||||||
auto *info = wibo::moduleInfoFromHandle(hModule);
|
path = info->originalName;
|
||||||
if (!info) {
|
|
||||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!info->resolvedPath.empty()) {
|
|
||||||
path = files::pathToWindows(info->resolvedPath);
|
|
||||||
} else {
|
|
||||||
path = info->originalName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const size_t len = path.size();
|
const size_t len = path.size();
|
||||||
if (nSize == 0) {
|
if (nSize == 0) {
|
||||||
@ -2759,22 +2749,16 @@ namespace kernel32 {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto *info = wibo::moduleInfoFromHandle(hModule);
|
||||||
|
if (!info) {
|
||||||
|
wibo::lastError = ERROR_INVALID_PARAMETER;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
std::string path;
|
std::string path;
|
||||||
if (wibo::isMainModule(hModule)) {
|
if (!info->resolvedPath.empty()) {
|
||||||
const auto exePath = files::pathFromWindows(wibo::argv[0]);
|
path = files::pathToWindows(info->resolvedPath);
|
||||||
const auto absPath = std::filesystem::absolute(exePath);
|
|
||||||
path = files::pathToWindows(absPath);
|
|
||||||
} else {
|
} else {
|
||||||
auto *info = wibo::moduleInfoFromHandle(hModule);
|
path = info->originalName;
|
||||||
if (!info) {
|
|
||||||
wibo::lastError = ERROR_INVALID_PARAMETER;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (!info->resolvedPath.empty()) {
|
|
||||||
path = files::pathToWindows(info->resolvedPath);
|
|
||||||
} else {
|
|
||||||
path = info->originalName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (nSize == 0) {
|
if (nSize == 0) {
|
||||||
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
|
wibo::lastError = ERROR_INSUFFICIENT_BUFFER;
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
119
loader.cpp
119
loader.cpp
@ -52,12 +52,12 @@ struct PE32Header {
|
|||||||
uint32_t loaderFlags;
|
uint32_t loaderFlags;
|
||||||
uint32_t numberOfRvaAndSizes;
|
uint32_t numberOfRvaAndSizes;
|
||||||
PEImageDataDirectory exportTable;
|
PEImageDataDirectory exportTable;
|
||||||
PEImageDataDirectory importTable; // *
|
PEImageDataDirectory importTable; // *
|
||||||
PEImageDataDirectory resourceTable; // *
|
PEImageDataDirectory resourceTable; // *
|
||||||
PEImageDataDirectory exceptionTable;
|
PEImageDataDirectory exceptionTable;
|
||||||
PEImageDataDirectory certificateTable;
|
PEImageDataDirectory certificateTable;
|
||||||
PEImageDataDirectory baseRelocationTable; // *
|
PEImageDataDirectory baseRelocationTable; // *
|
||||||
PEImageDataDirectory debug; // *
|
PEImageDataDirectory debug; // *
|
||||||
PEImageDataDirectory architecture;
|
PEImageDataDirectory architecture;
|
||||||
PEImageDataDirectory globalPtr;
|
PEImageDataDirectory globalPtr;
|
||||||
PEImageDataDirectory tlsTable;
|
PEImageDataDirectory tlsTable;
|
||||||
@ -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 {
|
||||||
@ -299,7 +315,7 @@ bool wibo::Executable::loadPE(FILE *file, bool exec) {
|
|||||||
PEHintNameTableEntry *hintName = fromRVA<PEHintNameTableEntry>(lookup);
|
PEHintNameTableEntry *hintName = fromRVA<PEHintNameTableEntry>(lookup);
|
||||||
DEBUG_LOG(" Name: %s (IAT=%p)\n", hintName->name, addressTable);
|
DEBUG_LOG(" Name: %s (IAT=%p)\n", hintName->name, addressTable);
|
||||||
void *func = module ? resolveFuncByName(module, hintName->name)
|
void *func = module ? resolveFuncByName(module, hintName->name)
|
||||||
: resolveMissingImportByName(dllName, hintName->name);
|
: resolveMissingImportByName(dllName, hintName->name);
|
||||||
DEBUG_LOG(" -> %p\n", func);
|
DEBUG_LOG(" -> %p\n", func);
|
||||||
*addressTable = reinterpret_cast<uintptr_t>(func);
|
*addressTable = reinterpret_cast<uintptr_t>(func);
|
||||||
}
|
}
|
||||||
@ -309,28 +325,29 @@ 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) {
|
||||||
uint16_t ordinal = lookup & 0xFFFF;
|
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)
|
void *func = module ? resolveFuncByOrdinal(module, ordinal)
|
||||||
: resolveMissingImportByOrdinal(dllName, ordinal);
|
: resolveMissingImportByOrdinal(dllName, ordinal);
|
||||||
*addressTable = reinterpret_cast<uintptr_t>(func);
|
*addressTable = reinterpret_cast<uintptr_t>(func);
|
||||||
} else {
|
} else {
|
||||||
PEHintNameTableEntry *hintName = fromRVA<PEHintNameTableEntry>(lookup);
|
PEHintNameTableEntry *hintName = fromRVA<PEHintNameTableEntry>(lookup);
|
||||||
DEBUG_LOG(" Name: %s\n", hintName->name);
|
DEBUG_LOG(" Name: %s\n", hintName->name);
|
||||||
void *func = module ? resolveFuncByName(module, hintName->name)
|
void *func = module ? resolveFuncByName(module, hintName->name)
|
||||||
: resolveMissingImportByName(dllName, hintName->name);
|
: resolveMissingImportByName(dllName, hintName->name);
|
||||||
*addressTable = reinterpret_cast<uintptr_t>(func);
|
*addressTable = reinterpret_cast<uintptr_t>(func);
|
||||||
}
|
}
|
||||||
++lookupTable;
|
++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;
|
return true;
|
||||||
}
|
}
|
||||||
|
80
main.cpp
80
main.cpp
@ -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();
|
||||||
|
|
||||||
|
@ -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 ®,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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 ®, 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 ®, 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 ®, 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;
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user