mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: implement Warden API stub dispatch via Unicorn UC_HOOK_CODE
Previously hookAPI() allocated a stub address and registered a C++ handler
but never stored the handler or wrote any executable code to the stub
region, meaning any Warden module call to a Windows API would execute zeros
and crash or silently return garbage.
Changes:
- Store ApiHookEntry {argCount, handler} per stub address in apiHandlers_
- Write RET (0xC3) to stub memory as a safe fallback
- Register UC_HOOK_CODE over the API stub address range during initialize()
- hookCode() now detects stub addresses, reads args from the emulated stack,
dispatches to the C++ handler, then simulates stdcall epilogue by setting
EAX/ESP/EIP so Unicorn returns cleanly to the caller
- Convert static-local nextStubAddr to instance member nextApiStubAddr_
so re-initialization resets the allocator correctly
- Known arg counts for all 7 registered Windows APIs (VirtualAlloc,
VirtualFree, GetTickCount, Sleep, GetCurrentThreadId,
GetCurrentProcessId, ReadProcessMemory)
This commit is contained in:
parent
b29d76bbc8
commit
005b1fcb54
2 changed files with 92 additions and 16 deletions
|
|
@ -147,9 +147,18 @@ private:
|
||||||
uint32_t heapSize_; // Heap size
|
uint32_t heapSize_; // Heap size
|
||||||
uint32_t apiStubBase_; // API stub base address
|
uint32_t apiStubBase_; // API stub base address
|
||||||
|
|
||||||
// API hooks: DLL name -> Function name -> Handler
|
// API hooks: DLL name -> Function name -> stub address
|
||||||
std::map<std::string, std::map<std::string, uint32_t>> apiAddresses_;
|
std::map<std::string, std::map<std::string, uint32_t>> apiAddresses_;
|
||||||
|
|
||||||
|
// API stub dispatch: stub address -> {argCount, handler}
|
||||||
|
struct ApiHookEntry {
|
||||||
|
int argCount;
|
||||||
|
std::function<uint32_t(WardenEmulator&, const std::vector<uint32_t>&)> handler;
|
||||||
|
};
|
||||||
|
std::map<uint32_t, ApiHookEntry> apiHandlers_;
|
||||||
|
uint32_t nextApiStubAddr_; // tracks next free stub slot (replaces static local)
|
||||||
|
bool apiCodeHookRegistered_; // true once UC_HOOK_CODE for stub range is added
|
||||||
|
|
||||||
// Memory allocation tracking
|
// Memory allocation tracking
|
||||||
std::map<uint32_t, size_t> allocations_;
|
std::map<uint32_t, size_t> allocations_;
|
||||||
std::map<uint32_t, size_t> freeBlocks_; // free-list keyed by base address
|
std::map<uint32_t, size_t> freeBlocks_; // free-list keyed by base address
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,8 @@ WardenEmulator::WardenEmulator()
|
||||||
, heapBase_(HEAP_BASE)
|
, heapBase_(HEAP_BASE)
|
||||||
, heapSize_(HEAP_SIZE)
|
, heapSize_(HEAP_SIZE)
|
||||||
, apiStubBase_(API_STUB_BASE)
|
, apiStubBase_(API_STUB_BASE)
|
||||||
|
, nextApiStubAddr_(API_STUB_BASE)
|
||||||
|
, apiCodeHookRegistered_(false)
|
||||||
, nextHeapAddr_(HEAP_BASE)
|
, nextHeapAddr_(HEAP_BASE)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -51,8 +53,11 @@ bool WardenEmulator::initialize(const void* moduleCode, size_t moduleSize, uint3
|
||||||
allocations_.clear();
|
allocations_.clear();
|
||||||
freeBlocks_.clear();
|
freeBlocks_.clear();
|
||||||
apiAddresses_.clear();
|
apiAddresses_.clear();
|
||||||
|
apiHandlers_.clear();
|
||||||
hooks_.clear();
|
hooks_.clear();
|
||||||
nextHeapAddr_ = heapBase_;
|
nextHeapAddr_ = heapBase_;
|
||||||
|
nextApiStubAddr_ = apiStubBase_;
|
||||||
|
apiCodeHookRegistered_ = false;
|
||||||
|
|
||||||
{
|
{
|
||||||
char addrBuf[32];
|
char addrBuf[32];
|
||||||
|
|
@ -149,6 +154,13 @@ bool WardenEmulator::initialize(const void* moduleCode, size_t moduleSize, uint3
|
||||||
uc_hook_add(uc_, &hh, UC_HOOK_MEM_INVALID, (void*)hookMemInvalid, this, 1, 0);
|
uc_hook_add(uc_, &hh, UC_HOOK_MEM_INVALID, (void*)hookMemInvalid, this, 1, 0);
|
||||||
hooks_.push_back(hh);
|
hooks_.push_back(hh);
|
||||||
|
|
||||||
|
// Add code hook over the API stub area so Windows API calls are intercepted
|
||||||
|
uc_hook apiHook;
|
||||||
|
uc_hook_add(uc_, &apiHook, UC_HOOK_CODE, (void*)hookCode, this,
|
||||||
|
API_STUB_BASE, API_STUB_BASE + 0x10000 - 1);
|
||||||
|
hooks_.push_back(apiHook);
|
||||||
|
apiCodeHookRegistered_ = true;
|
||||||
|
|
||||||
{
|
{
|
||||||
char sBuf[128];
|
char sBuf[128];
|
||||||
std::snprintf(sBuf, sizeof(sBuf), "WardenEmulator: Emulator initialized Stack: 0x%X-0x%X Heap: 0x%X-0x%X",
|
std::snprintf(sBuf, sizeof(sBuf), "WardenEmulator: Emulator initialized Stack: 0x%X-0x%X Heap: 0x%X-0x%X",
|
||||||
|
|
@ -161,23 +173,45 @@ bool WardenEmulator::initialize(const void* moduleCode, size_t moduleSize, uint3
|
||||||
|
|
||||||
uint32_t WardenEmulator::hookAPI(const std::string& dllName,
|
uint32_t WardenEmulator::hookAPI(const std::string& dllName,
|
||||||
const std::string& functionName,
|
const std::string& functionName,
|
||||||
[[maybe_unused]] std::function<uint32_t(WardenEmulator&, const std::vector<uint32_t>&)> handler) {
|
std::function<uint32_t(WardenEmulator&, const std::vector<uint32_t>&)> handler) {
|
||||||
// Allocate address for this API stub
|
// Allocate address for this API stub (16 bytes each)
|
||||||
static uint32_t nextStubAddr = API_STUB_BASE;
|
uint32_t stubAddr = nextApiStubAddr_;
|
||||||
uint32_t stubAddr = nextStubAddr;
|
nextApiStubAddr_ += 16;
|
||||||
nextStubAddr += 16; // Space for stub code
|
|
||||||
|
|
||||||
// Store mapping
|
// Store address mapping for IAT patching
|
||||||
apiAddresses_[dllName][functionName] = stubAddr;
|
apiAddresses_[dllName][functionName] = stubAddr;
|
||||||
|
|
||||||
{
|
// Determine stdcall arg count from known Windows APIs so the hook can
|
||||||
char hBuf[32];
|
// clean up the stack correctly (RETN N convention).
|
||||||
std::snprintf(hBuf, sizeof(hBuf), "0x%X", stubAddr);
|
static const std::pair<const char*, int> knownArgCounts[] = {
|
||||||
LOG_DEBUG("WardenEmulator: Hooked ", dllName, "!", functionName, " at ", hBuf);
|
{"VirtualAlloc", 4},
|
||||||
|
{"VirtualFree", 3},
|
||||||
|
{"GetTickCount", 0},
|
||||||
|
{"Sleep", 1},
|
||||||
|
{"GetCurrentThreadId", 0},
|
||||||
|
{"GetCurrentProcessId", 0},
|
||||||
|
{"ReadProcessMemory", 5},
|
||||||
|
};
|
||||||
|
int argCount = 0;
|
||||||
|
for (const auto& [name, cnt] : knownArgCounts) {
|
||||||
|
if (functionName == name) { argCount = cnt; break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Write stub code that triggers a hook callback
|
// Store the handler so hookCode() can dispatch to it
|
||||||
// For now, just return the address for IAT patching
|
apiHandlers_[stubAddr] = { argCount, std::move(handler) };
|
||||||
|
|
||||||
|
// Write a RET (0xC3) at the stub address as a safe fallback in case
|
||||||
|
// the code hook fires after EIP has already advanced past our intercept.
|
||||||
|
if (uc_) {
|
||||||
|
static const uint8_t retInstr = 0xC3;
|
||||||
|
uc_mem_write(uc_, stubAddr, &retInstr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
char hBuf[64];
|
||||||
|
std::snprintf(hBuf, sizeof(hBuf), "0x%X (argCount=%d)", stubAddr, argCount);
|
||||||
|
LOG_DEBUG("WardenEmulator: Hooked ", dllName, "!", functionName, " at ", hBuf);
|
||||||
|
}
|
||||||
|
|
||||||
return stubAddr;
|
return stubAddr;
|
||||||
}
|
}
|
||||||
|
|
@ -503,8 +537,40 @@ uint32_t WardenEmulator::apiReadProcessMemory(WardenEmulator& emu, const std::ve
|
||||||
// Unicorn Callbacks
|
// Unicorn Callbacks
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
void WardenEmulator::hookCode([[maybe_unused]] uc_engine* uc, uint64_t address, [[maybe_unused]] uint32_t size, [[maybe_unused]] void* userData) {
|
void WardenEmulator::hookCode(uc_engine* uc, uint64_t address, [[maybe_unused]] uint32_t size, void* userData) {
|
||||||
(void)address; // Trace disabled by default to avoid log spam
|
auto* self = static_cast<WardenEmulator*>(userData);
|
||||||
|
if (!self) return;
|
||||||
|
|
||||||
|
auto it = self->apiHandlers_.find(static_cast<uint32_t>(address));
|
||||||
|
if (it == self->apiHandlers_.end()) return; // not an API stub — trace disabled to avoid spam
|
||||||
|
|
||||||
|
const ApiHookEntry& entry = it->second;
|
||||||
|
|
||||||
|
// Read stack: [ESP+0] = return address, [ESP+4..] = stdcall args
|
||||||
|
uint32_t esp = 0;
|
||||||
|
uc_reg_read(uc, UC_X86_REG_ESP, &esp);
|
||||||
|
|
||||||
|
uint32_t retAddr = 0;
|
||||||
|
uc_mem_read(uc, esp, &retAddr, 4);
|
||||||
|
|
||||||
|
std::vector<uint32_t> args(static_cast<size_t>(entry.argCount));
|
||||||
|
for (int i = 0; i < entry.argCount; ++i) {
|
||||||
|
uint32_t val = 0;
|
||||||
|
uc_mem_read(uc, esp + 4 + static_cast<uint32_t>(i) * 4, &val, 4);
|
||||||
|
args[static_cast<size_t>(i)] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch to the C++ handler
|
||||||
|
uint32_t retVal = 0;
|
||||||
|
if (entry.handler) {
|
||||||
|
retVal = entry.handler(*self, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate stdcall epilogue: pop return address + args
|
||||||
|
uint32_t newEsp = esp + 4 + static_cast<uint32_t>(entry.argCount) * 4;
|
||||||
|
uc_reg_write(uc, UC_X86_REG_EAX, &retVal);
|
||||||
|
uc_reg_write(uc, UC_X86_REG_ESP, &newEsp);
|
||||||
|
uc_reg_write(uc, UC_X86_REG_EIP, &retAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WardenEmulator::hookMemInvalid([[maybe_unused]] uc_engine* uc, int type, uint64_t address, int size, [[maybe_unused]] int64_t value, [[maybe_unused]] void* userData) {
|
void WardenEmulator::hookMemInvalid([[maybe_unused]] uc_engine* uc, int type, uint64_t address, int size, [[maybe_unused]] int64_t value, [[maybe_unused]] void* userData) {
|
||||||
|
|
@ -533,7 +599,8 @@ WardenEmulator::WardenEmulator()
|
||||||
: uc_(nullptr), moduleBase_(0), moduleSize_(0)
|
: uc_(nullptr), moduleBase_(0), moduleSize_(0)
|
||||||
, stackBase_(0), stackSize_(0)
|
, stackBase_(0), stackSize_(0)
|
||||||
, heapBase_(0), heapSize_(0)
|
, heapBase_(0), heapSize_(0)
|
||||||
, apiStubBase_(0), nextHeapAddr_(0) {}
|
, apiStubBase_(0), nextApiStubAddr_(0), apiCodeHookRegistered_(false)
|
||||||
|
, nextHeapAddr_(0) {}
|
||||||
WardenEmulator::~WardenEmulator() {}
|
WardenEmulator::~WardenEmulator() {}
|
||||||
bool WardenEmulator::initialize(const void*, size_t, uint32_t) { return false; }
|
bool WardenEmulator::initialize(const void*, size_t, uint32_t) { return false; }
|
||||||
uint32_t WardenEmulator::hookAPI(const std::string&, const std::string&,
|
uint32_t WardenEmulator::hookAPI(const std::string&, const std::string&,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue