diff --git a/Hauyne.Injector/linux/arch/aarch64.zig b/Hauyne.Injector/linux/arch/aarch64.zig index 1516990..897616c 100644 --- a/Hauyne.Injector/linux/arch/aarch64.zig +++ b/Hauyne.Injector/linux/arch/aarch64.zig @@ -14,6 +14,7 @@ pub const UserRegsStruct = extern struct { }; pub const SYS_mmap: u64 = 222; +pub const SYS_mprotect: u64 = 226; pub const syscall_insn_size: u64 = 0; pub const idle_syscalls = [_]i64{ @@ -50,6 +51,13 @@ pub fn setupMmapRegs(regs: *UserRegsStruct, scratch_size: u64, prot: u64, flags: regs.regs[5] = 0; } +pub fn setupMprotectRegs(regs: *UserRegsStruct, addr: u64, len: u64, prot: u64) void { + regs.regs[8] = SYS_mprotect; + regs.regs[0] = addr; + regs.regs[1] = len; + regs.regs[2] = prot; +} + pub fn setupShimRegs(regs: *UserRegsStruct, shim_addr: usize) void { regs.pc = shim_addr; } diff --git a/Hauyne.Injector/linux/arch/x86_64.zig b/Hauyne.Injector/linux/arch/x86_64.zig index f77b817..abf0ff9 100644 --- a/Hauyne.Injector/linux/arch/x86_64.zig +++ b/Hauyne.Injector/linux/arch/x86_64.zig @@ -37,6 +37,7 @@ pub const UserRegsStruct = extern struct { }; pub const SYS_mmap: u64 = 9; +pub const SYS_mprotect: u64 = 10; pub const syscall_insn_size: u64 = 2; pub const idle_syscalls = [_]i64{ @@ -75,6 +76,15 @@ pub fn setupMmapRegs(regs: *UserRegsStruct, scratch_size: u64, prot: u64, flags: regs.orig_rax = @bitCast(@as(i64, -1)); } +pub fn setupMprotectRegs(regs: *UserRegsStruct, addr: u64, len: u64, prot: u64) void { + regs.rip -= 2; + regs.rax = SYS_mprotect; + regs.rdi = addr; + regs.rsi = len; + regs.rdx = prot; + regs.orig_rax = @bitCast(@as(i64, -1)); +} + pub fn setupShimRegs(regs: *UserRegsStruct, shim_addr: usize) void { regs.rip = shim_addr; regs.orig_rax = @bitCast(@as(i64, -1)); diff --git a/Hauyne.Injector/linux/linux.zig b/Hauyne.Injector/linux/linux.zig index e0f3533..77fcf34 100644 --- a/Hauyne.Injector/linux/linux.zig +++ b/Hauyne.Injector/linux/linux.zig @@ -146,6 +146,8 @@ pub fn inject( var page = shim.buildScratchPage(so_path, payload_path, type_name, method_name, dlopen_addr, dlsym_addr, pthread_create_addr, pthread_detach_addr, scratch, self_pid); try ptrace_mod.writeMemory(victim, scratch, &page); + try bootstrapMprotect(victim, saved, scratch + shim.CodePageOff, shim.ScratchSize - shim.CodePageOff, PROT_READ | PROT_EXEC); + try runVictimShim(victim, saved, scratch + shim.VictimShimOff); try ptrace_mod.setRegs(victim, saved); @@ -161,7 +163,7 @@ pub fn inject( fn bootstrapMmap(pid: i32, saved: UserRegsStruct) !usize { var regs = saved; - arch.setupMmapRegs(®s, shim.ScratchSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS); + arch.setupMmapRegs(®s, shim.ScratchSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS); try ptrace_mod.setRegs(pid, regs); try continueAndWait(pid, ptrace_mod.PTRACE_SYSCALL, "mmap-enter"); @@ -175,6 +177,20 @@ fn bootstrapMmap(pid: i32, saved: UserRegsStruct) !usize { return @intCast(ret); } +fn bootstrapMprotect(pid: i32, saved: UserRegsStruct, addr: usize, len: usize, prot: u64) !void { + var regs = saved; + arch.setupMprotectRegs(®s, @intCast(addr), @intCast(len), prot); + + try ptrace_mod.setRegs(pid, regs); + try continueAndWait(pid, ptrace_mod.PTRACE_SYSCALL, "mprotect-enter"); + try continueAndWait(pid, ptrace_mod.PTRACE_SYSCALL, "mprotect-exit"); + + const after = try ptrace_mod.getRegs(pid); + const ret: i64 = @bitCast(arch.getSyscallResult(after)); + if (ret < 0 and ret > -4096) + return error.MprotectInTargetFailed; +} + fn runVictimShim(pid: i32, saved: UserRegsStruct, shim_addr: usize) !void { var regs = saved; arch.setupShimRegs(®s, shim_addr); diff --git a/Hauyne.Injector/linux/shim.zig b/Hauyne.Injector/linux/shim.zig index 4db32ec..aa52521 100644 --- a/Hauyne.Injector/linux/shim.zig +++ b/Hauyne.Injector/linux/shim.zig @@ -6,12 +6,17 @@ const std = @import("std"); -pub const ScratchSize: usize = 0x2000; // 8 KiB (two pages) -pub const PathOffset: usize = 0x40; // bootstrap .so path (1984) -pub const PayloadOffset: usize = 0x800; // payload triple, NUL-separated (4096) -pub const SymbolOffset: usize = 0x1800; // "hauyne_start\0" (256) -pub const VictimShimOff: usize = 0x1900; // pthread_create + pthread_detach (256) -pub const PayloadShimOff: usize = 0x1A00; // dlopen + dlsym + hauyne_start (1536) +pub const ScratchSize: usize = 0x2000; // 8 KiB (two 4K pages) + +// P1 RW: data region, pthread_handle +pub const PathOffset: usize = 0x40; // bootstrap .so path (1984) +pub const PayloadOffset: usize = 0x800; // payload triple, NUL-separated (2048) + +// P2 RX: read-only data + executable shims +pub const CodePageOff: usize = 0x1000; // page boundary for mprotect +pub const SymbolOffset: usize = 0x1800; // "hauyne_start\0" (256) +pub const VictimShimOff: usize = 0x1900; // pthread_create + pthread_detach (256) +pub const PayloadShimOff: usize = 0x1A00; // dlopen + dlsym + hauyne_start (1536) const arch = @import("arch.zig").emitter;