From fb17999e733ebe920eb33d3a1e5763a8242622ce Mon Sep 17 00:00:00 2001 From: Airi Sezaki Date: Mon, 11 May 2026 23:00:36 +0300 Subject: [PATCH] cry about it --- .github/workflows/test.yml | 37 +++++++++++++++++++++----------- Hauyne.Injector/injector.zig | 36 +++++++++++++------------------ Hauyne.Injector/linux/victim.zig | 2 +- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4d8f481..2b648fb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,36 +44,45 @@ jobs: - name: Build victim run: dotnet build Hauyne.Victim -c Release -p:TargetFramework=net${{ matrix.dotnet }} --nologo - - name: Test + - name: Configure ptrace run: | echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope ulimit -c unlimited echo '/tmp/core.%p' | sudo tee /proc/sys/kernel/core_pattern + - name: Start victim + run: | rm -f /tmp/Hauyne.txt /tmp/hauyne-victim.pid - dotnet exec Hauyne.Victim/bin/Release/net${{ matrix.dotnet }}/Hauyne.Victim.dll & sleep 3 + echo "VICTIM_PID=$(cat /tmp/hauyne-victim.pid)" >> $GITHUB_ENV + echo "Victim PID: $(cat /tmp/hauyne-victim.pid) (net${{ matrix.dotnet }})" - VICTIM_PID=$(cat /tmp/hauyne-victim.pid) - echo "Victim PID: $VICTIM_PID (net${{ matrix.dotnet }})" - - grep -q libhostfxr /proc/$VICTIM_PID/maps - + - name: Inject payload + run: | HAUYNE_DEBUG=1 ./bin/Hauyne.Injector "$VICTIM_PID" "$PWD/bin/net${{ matrix.dotnet }}/Hauyne.Payload.dll" + sleep 2 - sleep 3 - + - name: Verify payload loaded + run: | if [ ! -f /tmp/Hauyne.txt ]; then - echo "FUCK! Bootstrap log:" + echo "Hauyne.txt missing. Bootstrap:" find bin -name 'hauyne.log' -exec cat {} \; 2>/dev/null || true exit 1 fi echo "PASS: $(cat /tmp/Hauyne.txt)" - echo "Waiting 10s to ensure victim survived" - sleep 10 + - name: Verify failure report + run: | + if ./bin/Hauyne.Injector "$VICTIM_PID" --type Moonyware.NativeEntry 2>&1; then + echo "Injector should've failed" + exit 1 + fi + echo "PASS: bad type correctly reported failure" + - name: Verify victim survived + run: | + sleep 10 if kill -0 $VICTIM_PID 2>/dev/null; then echo "Victim still alive" else @@ -81,7 +90,9 @@ jobs: exit 1 fi - kill $VICTIM_PID 2>/dev/null || true + - name: Cleanup + if: always() + run: kill $VICTIM_PID 2>/dev/null || true - name: Backtrace if: failure() diff --git a/Hauyne.Injector/injector.zig b/Hauyne.Injector/injector.zig index 96d8107..0d50646 100644 --- a/Hauyne.Injector/injector.zig +++ b/Hauyne.Injector/injector.zig @@ -88,31 +88,26 @@ pub fn main(init: std.process.Init) u8 { return 1; }; - if (is_windows) { + const payload_ok = if (is_windows) blk: { const windows = @import("windows.zig"); - const payload_ok = windows.inject(allocator, @intCast(pid), bootstrap_path, payload_path, type_name, method_name) catch |err| { - std.debug.print("Injection failed: {}\n", .{err}); - return 1; - }; - if (!payload_ok) { - std.debug.print("Injected into PID {d}, but payload failed to load\n", .{pid}); - printLastLogLine(allocator, bootstrap_path); - return 1; - } - } else if (builtin.os.tag == .linux) { + break :blk windows.inject(allocator, @intCast(pid), bootstrap_path, payload_path, type_name, method_name); + } else if (builtin.os.tag == .linux) blk: { const linux = @import("linux/linux.zig"); - const payload_ok = linux.inject(io, allocator, @intCast(pid), bootstrap_path, payload_path, type_name, method_name) catch |err| { - std.debug.print("Injection failed: {}\n", .{err}); - return 1; - }; - if (!payload_ok) { - std.debug.print("Injected into PID {d}, but payload failed to load\n", .{pid}); - printLastLogLine(allocator, bootstrap_path); - return 1; - } + break :blk linux.inject(io, allocator, @intCast(pid), bootstrap_path, payload_path, type_name, method_name); } else { std.debug.print("Unsupported platform\n", .{}); return 1; + }; + + const ok = payload_ok catch |err| { + std.debug.print("Injection failed: {}\n", .{err}); + return 1; + }; + + if (!ok) { + std.debug.print("Injected into PID {d}, but payload failed to load\n", .{pid}); + printLastLogLine(allocator, bootstrap_path); + return 1; } println(allocator, "Injected into PID {}\n", .{pid}); @@ -361,7 +356,6 @@ fn isDotNetProcessLinux(io: std.Io, allocator: std.mem.Allocator, pid: u32, inac return false; } - fn isDotNetProcessWindows(pid: u32) bool { const windows = std.os.windows; diff --git a/Hauyne.Injector/linux/victim.zig b/Hauyne.Injector/linux/victim.zig index 429c5cb..dacb33e 100644 --- a/Hauyne.Injector/linux/victim.zig +++ b/Hauyne.Injector/linux/victim.zig @@ -5,6 +5,7 @@ // Mozilla Public License, v. 2.0. const std = @import("std"); +const procfs = @import("procfs.zig"); const arch = switch (@import("builtin").cpu.arch) { .x86_64 => @import("arch/x86_64.zig"), @@ -31,7 +32,6 @@ pub fn pickVictimThread(io: std.Io, allocator: std.mem.Allocator, tgid: i32) !i3 const syscall_path = std.fmt.allocPrint(allocator, "/proc/{d}/task/{d}/syscall", .{ tgid, tid }) catch continue; defer allocator.free(syscall_path); - const procfs = @import("procfs.zig"); const syscall_text = procfs.readFileAlloc(allocator, syscall_path) catch continue; const trimmed = std.mem.trimEnd(u8, syscall_text, "\n\r \t");