mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-03 20:03:50 +00:00
feat: implement Warden API binding / IAT patching for module imports
Complete the last major Warden stub — the import table parser that resolves Windows API calls in loaded modules. This is the critical missing piece for strict servers like Warmane. Implementation: - Parse Warden module import table from decompressed data (after relocation entries): alternating libraryName\0 / functionName\0 pairs, terminated by null library name - For each import, look up the emulator's pre-registered stub address (VirtualAlloc, GetTickCount, ReadProcessMemory, etc.) - Auto-stub unrecognized APIs with a no-op returning 0 — prevents module crashes on unimplemented Windows functions - Patch each IAT slot (sequential dwords at module image base) with the resolved stub address - Add WardenEmulator::getAPIAddress() public accessor for IAT lookups - Fix initialization order: bindAPIs() now runs inside initializeModule() after emulator setup but before entry point call The full Warden pipeline is now: RC4 decrypt → RSA verify → zlib decompress → parse executable → relocate → create emulator → register API hooks → bind imports (IAT patch) → call entry point → extract exported functions (packetHandler, tick, generateRC4Keys, unload).
This commit is contained in:
parent
248d131af7
commit
88d047d2fb
3 changed files with 104 additions and 54 deletions
|
|
@ -138,6 +138,10 @@ public:
|
||||||
*/
|
*/
|
||||||
std::vector<uint8_t> readData(uint32_t address, size_t size);
|
std::vector<uint8_t> readData(uint32_t address, size_t size);
|
||||||
|
|
||||||
|
// Look up an already-registered API stub address by DLL and function name.
|
||||||
|
// Returns 0 if not found. Used by WardenModule::bindAPIs() for IAT patching.
|
||||||
|
uint32_t getAPIAddress(const std::string& dllName, const std::string& funcName) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uc_engine* uc_; // Unicorn engine instance
|
uc_engine* uc_; // Unicorn engine instance
|
||||||
uint32_t moduleBase_; // Module base address
|
uint32_t moduleBase_; // Module base address
|
||||||
|
|
|
||||||
|
|
@ -216,6 +216,13 @@ uint32_t WardenEmulator::hookAPI(const std::string& dllName,
|
||||||
return stubAddr;
|
return stubAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t WardenEmulator::getAPIAddress(const std::string& dllName, const std::string& funcName) const {
|
||||||
|
auto libIt = apiAddresses_.find(dllName);
|
||||||
|
if (libIt == apiAddresses_.end()) return 0;
|
||||||
|
auto funcIt = libIt->second.find(funcName);
|
||||||
|
return (funcIt != libIt->second.end()) ? funcIt->second : 0;
|
||||||
|
}
|
||||||
|
|
||||||
void WardenEmulator::setupCommonAPIHooks() {
|
void WardenEmulator::setupCommonAPIHooks() {
|
||||||
LOG_INFO("WardenEmulator: Setting up common Windows API hooks...");
|
LOG_INFO("WardenEmulator: Setting up common Windows API hooks...");
|
||||||
|
|
||||||
|
|
@ -614,6 +621,7 @@ bool WardenEmulator::freeMemory(uint32_t) { return false; }
|
||||||
uint32_t WardenEmulator::getRegister(int) { return 0; }
|
uint32_t WardenEmulator::getRegister(int) { return 0; }
|
||||||
void WardenEmulator::setRegister(int, uint32_t) {}
|
void WardenEmulator::setRegister(int, uint32_t) {}
|
||||||
void WardenEmulator::setupCommonAPIHooks() {}
|
void WardenEmulator::setupCommonAPIHooks() {}
|
||||||
|
uint32_t WardenEmulator::getAPIAddress(const std::string&, const std::string&) const { return 0; }
|
||||||
uint32_t WardenEmulator::writeData(const void*, size_t) { return 0; }
|
uint32_t WardenEmulator::writeData(const void*, size_t) { return 0; }
|
||||||
std::vector<uint8_t> WardenEmulator::readData(uint32_t, size_t) { return {}; }
|
std::vector<uint8_t> WardenEmulator::readData(uint32_t, size_t) { return {}; }
|
||||||
void WardenEmulator::hookCode(uc_engine*, uint64_t, uint32_t, void*) {}
|
void WardenEmulator::hookCode(uc_engine*, uint64_t, uint32_t, void*) {}
|
||||||
|
|
|
||||||
|
|
@ -115,13 +115,10 @@ bool WardenModule::load(const std::vector<uint8_t>& moduleData,
|
||||||
LOG_ERROR("WardenModule: Address relocations failed; continuing with unrelocated image");
|
LOG_ERROR("WardenModule: Address relocations failed; continuing with unrelocated image");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 7: Bind APIs
|
// Step 7+8: Initialize module (creates emulator) then bind APIs (patches IAT).
|
||||||
if (!bindAPIs()) {
|
// API binding must happen after emulator setup (needs stub addresses) but before
|
||||||
LOG_ERROR("WardenModule: API binding failed!");
|
// the module entry point is called (needs resolved imports). Both are handled
|
||||||
// Note: Currently returns true (stub) on both Windows and Linux
|
// inside initializeModule().
|
||||||
}
|
|
||||||
|
|
||||||
// Step 8: Initialize module
|
|
||||||
if (!initializeModule()) {
|
if (!initializeModule()) {
|
||||||
LOG_ERROR("WardenModule: Module initialization failed; continuing with stub callbacks");
|
LOG_ERROR("WardenModule: Module initialization failed; continuing with stub callbacks");
|
||||||
}
|
}
|
||||||
|
|
@ -780,64 +777,99 @@ bool WardenModule::bindAPIs() {
|
||||||
|
|
||||||
LOG_INFO("WardenModule: Binding Windows APIs for module...");
|
LOG_INFO("WardenModule: Binding Windows APIs for module...");
|
||||||
|
|
||||||
// Common Windows APIs used by Warden modules:
|
// The Warden module import table lives in decompressedData_ immediately after
|
||||||
|
// the relocation entries (which are terminated by a 0x0000 delta). Format:
|
||||||
//
|
//
|
||||||
// kernel32.dll:
|
// Repeated library blocks until null library name:
|
||||||
// - VirtualAlloc, VirtualFree, VirtualProtect
|
// string libraryName\0
|
||||||
// - GetTickCount, GetCurrentThreadId, GetCurrentProcessId
|
// Repeated function entries until null function name:
|
||||||
// - Sleep, SwitchToThread
|
// string functionName\0
|
||||||
// - CreateThread, ExitThread
|
|
||||||
// - GetModuleHandleA, GetProcAddress
|
|
||||||
// - ReadProcessMemory, WriteProcessMemory
|
|
||||||
//
|
//
|
||||||
// user32.dll:
|
// Each imported function corresponds to a sequential IAT slot at the start
|
||||||
// - GetForegroundWindow, GetWindowTextA
|
// of the module image (first N dwords). We patch each with the emulator's
|
||||||
//
|
// stub address so calls into Windows APIs land on our Unicorn hooks.
|
||||||
// ntdll.dll:
|
|
||||||
// - NtQueryInformationProcess, NtQuerySystemInformation
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
if (relocDataOffset_ == 0 || relocDataOffset_ >= decompressedData_.size()) {
|
||||||
// On Windows: Use GetProcAddress to resolve imports
|
LOG_WARNING("WardenModule: No relocation/import data — skipping API binding");
|
||||||
LOG_INFO("WardenModule: Platform: Windows - using GetProcAddress");
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
|
// Skip past relocation entries (delta-encoded uint16 pairs, 0x0000 terminated)
|
||||||
HMODULE user32 = GetModuleHandleA("user32.dll");
|
size_t pos = relocDataOffset_;
|
||||||
HMODULE ntdll = GetModuleHandleA("ntdll.dll");
|
while (pos + 2 <= decompressedData_.size()) {
|
||||||
|
uint16_t delta = decompressedData_[pos] | (decompressedData_[pos + 1] << 8);
|
||||||
|
pos += 2;
|
||||||
|
if (delta == 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!kernel32 || !user32 || !ntdll) {
|
if (pos >= decompressedData_.size()) {
|
||||||
LOG_ERROR("WardenModule: Failed to get module handles");
|
LOG_INFO("WardenModule: No import data after relocations");
|
||||||
return false;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse import table
|
||||||
|
uint32_t iatSlotIndex = 0;
|
||||||
|
int totalImports = 0;
|
||||||
|
int resolvedImports = 0;
|
||||||
|
|
||||||
|
auto readString = [&](size_t& p) -> std::string {
|
||||||
|
std::string s;
|
||||||
|
while (p < decompressedData_.size() && decompressedData_[p] != 0) {
|
||||||
|
s.push_back(static_cast<char>(decompressedData_[p]));
|
||||||
|
p++;
|
||||||
}
|
}
|
||||||
|
if (p < decompressedData_.size()) p++; // skip null terminator
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: Parse module's import table
|
while (pos < decompressedData_.size()) {
|
||||||
// - Find import directory in PE headers
|
std::string libraryName = readString(pos);
|
||||||
// - For each imported DLL:
|
if (libraryName.empty()) break; // null library name = end of imports
|
||||||
// - For each imported function:
|
|
||||||
// - Resolve address using GetProcAddress
|
|
||||||
// - Write address to Import Address Table (IAT)
|
|
||||||
|
|
||||||
LOG_WARNING("WardenModule: Windows API binding is STUB (needs PE import table parsing)");
|
// Read functions for this library
|
||||||
LOG_INFO("WardenModule: Would parse PE headers and patch IAT with resolved addresses");
|
while (pos < decompressedData_.size()) {
|
||||||
|
std::string functionName = readString(pos);
|
||||||
|
if (functionName.empty()) break; // null function name = next library
|
||||||
|
|
||||||
#else
|
totalImports++;
|
||||||
// On Linux: Cannot directly execute Windows code
|
|
||||||
// Options:
|
|
||||||
// 1. Use Wine to provide Windows API compatibility
|
|
||||||
// 2. Implement Windows API stubs (limited functionality)
|
|
||||||
// 3. Use binfmt_misc + Wine (transparent Windows executable support)
|
|
||||||
|
|
||||||
LOG_WARNING("WardenModule: Platform: Linux - Windows module execution NOT supported");
|
// Look up the emulator's stub address for this API
|
||||||
LOG_INFO("WardenModule: Options:");
|
uint32_t resolvedAddr = 0;
|
||||||
LOG_INFO("WardenModule: 1. Run wowee under Wine (provides Windows API layer)");
|
#ifdef HAVE_UNICORN
|
||||||
LOG_INFO("WardenModule: 2. Use a Windows VM");
|
if (emulator_) {
|
||||||
LOG_INFO("WardenModule: 3. Implement Windows API stubs (limited, complex)");
|
// Check if this API was pre-registered in setupCommonAPIHooks()
|
||||||
|
resolvedAddr = emulator_->getAPIAddress(libraryName, functionName);
|
||||||
|
if (resolvedAddr == 0) {
|
||||||
|
// Not pre-registered — create a no-op stub that returns 0.
|
||||||
|
// Prevents module crashes on unimplemented APIs (returns
|
||||||
|
// 0 / NULL / FALSE / S_OK for most Windows functions).
|
||||||
|
resolvedAddr = emulator_->hookAPI(libraryName, functionName,
|
||||||
|
[](WardenEmulator&, const std::vector<uint32_t>&) -> uint32_t {
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
LOG_DEBUG("WardenModule: Auto-stubbed ", libraryName, "!", functionName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// For now, we'll return true to continue the loading pipeline
|
// Patch IAT slot in module image
|
||||||
// Real execution would fail, but this allows testing the infrastructure
|
if (resolvedAddr != 0) {
|
||||||
LOG_WARNING("WardenModule: Skipping API binding (Linux platform limitation)");
|
uint32_t iatOffset = iatSlotIndex * 4;
|
||||||
#endif
|
if (iatOffset + 4 <= moduleSize_) {
|
||||||
|
uint8_t* slot = static_cast<uint8_t*>(moduleMemory_) + iatOffset;
|
||||||
|
std::memcpy(slot, &resolvedAddr, 4);
|
||||||
|
resolvedImports++;
|
||||||
|
LOG_DEBUG("WardenModule: IAT[", iatSlotIndex, "] = ", libraryName,
|
||||||
|
"!", functionName, " → 0x", std::hex, resolvedAddr, std::dec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iatSlotIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true; // Return true to continue (stub implementation)
|
LOG_INFO("WardenModule: Bound ", resolvedImports, "/", totalImports,
|
||||||
|
" API imports (", iatSlotIndex, " IAT slots patched)");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WardenModule::initializeModule() {
|
bool WardenModule::initializeModule() {
|
||||||
|
|
@ -952,9 +984,15 @@ bool WardenModule::initializeModule() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup Windows API hooks
|
// Setup Windows API hooks (VirtualAlloc, GetTickCount, ReadProcessMemory, etc.)
|
||||||
emulator_->setupCommonAPIHooks();
|
emulator_->setupCommonAPIHooks();
|
||||||
|
|
||||||
|
// Bind module imports: parse the import table from decompressed data and
|
||||||
|
// patch each IAT slot with the emulator's stub address. Must happen after
|
||||||
|
// setupCommonAPIHooks() (which registers the stubs) and before calling the
|
||||||
|
// module entry point (which uses the resolved imports).
|
||||||
|
bindAPIs();
|
||||||
|
|
||||||
{
|
{
|
||||||
char addrBuf[32];
|
char addrBuf[32];
|
||||||
std::snprintf(addrBuf, sizeof(addrBuf), "0x%X", moduleBase_);
|
std::snprintf(addrBuf, sizeof(addrBuf), "0x%X", moduleBase_);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue