Fix WriteFormattedLogMessage: stdcall with va_list, not cdecl variadic

Ghidra disasm confirms RET 0xC (callee cleans 3 args). Third arg is a
va_list pointer, not the variadic args directly. For %s, vsprintf reads
*(char**)va_list -- previous code passed &name_buf causing vsprintf to
interpret "Munj" (0x6A6E754D) as a char* and crash at strlen.

Fix: pass &name_ptr (pointer to the char*) and remove caller stack
cleanup. Added detailed research to RESEARCH.md.
This commit is contained in:
MarcelineVQ
2026-03-03 14:53:32 -08:00
parent aa6ea3c558
commit 11030e0566
3 changed files with 296 additions and 7 deletions
+9 -6
View File
@@ -53,7 +53,7 @@ fn setupPathRedirect() void {
const pid = GetCurrentProcessId();
const path = std.fmt.bufPrint(&g_path_buf, "Logs\\WoWCombatLog_{d:0>4}{d:0>2}{d:0>2}_{d:0>2}{d:0>2}{d:0>2}_{d}.txt", .{
st.wYear, st.wMonth, st.wDay,
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond,
pid,
}) catch {
@@ -162,18 +162,21 @@ fn maybeWriteSessionMarker() void {
name_local[0..len], @intFromPtr(name), len,
});
const args = [3]u32{ combat_handle, @intFromPtr(@as([*:0]const u8, "COMBATLOG_SESSION,%s")), @intFromPtr(&name_local) };
// WriteFormattedLogMessage — __stdcall(handle, fmt, va_list).
// RET 0xC: callee cleans 12 bytes (3 args), no caller cleanup needed.
// arg3 (va_list) is a pointer to the variadic args on the stack.
// For %s, vsprintf reads *(char**)va_list, so va_list must point to a char*.
const name_ptr: u32 = @intFromPtr(&name_local);
const args = [3]u32{ combat_handle, @intFromPtr(@as([*:0]const u8, "COMBATLOG_SESSION: %s")), @intFromPtr(&name_ptr) };
asm volatile (
\\ push 8(%[a])
\\ push 4(%[a])
\\ push (%[a])
\\ call *%[func]
\\ add $12, %%esp
:
: [a] "r" (&args),
[func] "r" (@as(u32, o.FN_WRITE_FMT_LOG_MSG)),
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true }
);
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
g_session_marker_written = true;
con.fmt("[combatlog] session: {s}\n", .{name_local[0..len]});
@@ -196,7 +199,7 @@ fn enableChatLoggingDetour(lua_state: u32, index: u32) callconv(fc) u32 {
// compiler knowing. This barrier forces Zig to push/pop ESI/EDI/EBX in the
// prologue/epilogue and avoid using them for intermediates — guaranteeing
// they're correctly restored on return to the Lua VM.
asm volatile ("" : : : .{ .esi = true, .edi = true, .ebx = true });
asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true });
const result = enable_logging_hook.callOriginal(.{ lua_state, index });
// index 1 = combat log