mirror of
https://github.com/nbaertsch/COMegon
synced 2026-08-09 12:57:42 +00:00
93 lines
3.9 KiB
Zig
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", .{});
|
|
}
|