Files
nbaertsch-COMegon/test_comegon_invoke.zig
2026-06-21 13:46:13 -04:00

93 lines
3.9 KiB
Zig

// COMegon invoke test — exercises proxy mode (single synchronous call with return value).
// Build: zig build-exe test_comegon_invoke.zig -target x86_64-windows -O ReleaseSmall
const std = @import("std");
const print = std.debug.print;
const comegon = @import("comegon.zig");
extern "kernel32" fn GetTickCount64() callconv(.c) u64;
fn resolve(dll: [*:0]const u8, name: [*:0]const u8) usize {
const GetModuleHandleA = @extern(*const fn (?[*:0]const u8) callconv(.c) ?*anyopaque, .{ .name = "GetModuleHandleA", .library_name = "kernel32" });
const GetProcAddress = @extern(*const fn (?*anyopaque, [*:0]const u8) callconv(.c) ?*const fn () callconv(.c) isize, .{ .name = "GetProcAddress", .library_name = "kernel32" });
const LoadLibraryA = @extern(*const fn ([*:0]const u8) callconv(.c) ?*anyopaque, .{ .name = "LoadLibraryA", .library_name = "kernel32" });
var mod = GetModuleHandleA(dll);
if (mod == null) mod = LoadLibraryA(dll);
const proc = GetProcAddress(mod, name);
return if (proc) |p| @intFromPtr(p) else 0;
}
pub fn main() void {
print("[*] COMegon Invoke (Proxy Mode) Test\n", .{});
var ctx = comegon.init(.{}) catch |e| {
print("[-] Init failed: {}\n", .{e});
return;
};
defer ctx.deinit();
print("[+] COMegon initialized\n", .{});
// Test 1: GetCurrentProcessId — returns a known nonzero u32
{
const fn_getpid = resolve("kernel32.dll", "GetCurrentProcessId");
if (fn_getpid == 0) {
print("[-] Failed to resolve GetCurrentProcessId\n", .{});
return;
}
print("[+] invoke(GetCurrentProcessId)...\n", .{});
const pid = ctx.invoke(fn_getpid, &[_]usize{});
const real_pid = @extern(*const fn () callconv(.c) u32, .{ .name = "GetCurrentProcessId", .library_name = "kernel32" })();
print("[+] Returned PID: {d} (expected {d})\n", .{ @as(u32, @truncate(pid)), real_pid });
if (@as(u32, @truncate(pid)) == real_pid) {
print("[+] PASS: Return value matches\n", .{});
} else {
print("[-] FAIL: Return value mismatch\n", .{});
}
}
// Test 2: GetTickCount64 — returns a u64, verifies full 8-byte capture
{
const fn_tick = resolve("kernel32.dll", "GetTickCount64");
if (fn_tick == 0) {
print("[-] Failed to resolve GetTickCount64\n", .{});
return;
}
const before = GetTickCount64();
print("[+] invoke(GetTickCount64)...\n", .{});
const tick = ctx.invoke(fn_tick, &[_]usize{});
const after = GetTickCount64();
print("[+] Returned tick: {d} (window: {d}..{d})\n", .{ tick, before, after });
if (tick >= before and tick <= after) {
print("[+] PASS: Tick within expected range\n", .{});
} else {
print("[-] FAIL: Tick out of range\n", .{});
}
}
// Test 3: MsgWaitForMultipleObjectsEx with timeout — verifies parameter passing + timing
{
const fn_msgwait = resolve("user32.dll", "MsgWaitForMultipleObjectsEx");
if (fn_msgwait == 0) {
print("[-] Failed to resolve MsgWaitForMultipleObjectsEx\n", .{});
return;
}
const wait_ms: usize = 500;
print("[+] invoke(MsgWaitForMultipleObjectsEx, timeout={d}ms)...\n", .{wait_ms});
const start = GetTickCount64();
const result = ctx.invoke(fn_msgwait, &[_]usize{ 0, 0, wait_ms, 0x04, 0 });
const elapsed = GetTickCount64() - start;
// WAIT_TIMEOUT = 258
print("[+] Returned: {d} (WAIT_TIMEOUT=258), elapsed={d}ms\n", .{ @as(u32, @truncate(result)), elapsed });
if (@as(u32, @truncate(result)) == 258 and elapsed >= 450) {
print("[+] PASS: Correct timeout behavior\n", .{});
} else {
print("[-] FAIL: Unexpected result or timing\n", .{});
}
}
print("[+] All invoke tests complete\n", .{});
}