mmap and mprotect interception
This commit is contained in:
@@ -363,10 +363,9 @@ test "nolibc_nopie_readlink" {
|
|||||||
test "nolibc_pie_readlink" {
|
test "nolibc_pie_readlink" {
|
||||||
try testReadlink("nolibc_pie_readlink");
|
try testReadlink("nolibc_pie_readlink");
|
||||||
}
|
}
|
||||||
// BUG: This one just outputs the path to the flicker executable
|
test "libc_pie_readlink" {
|
||||||
// test "libc_pie_readlink" {
|
try testReadlink("libc_pie_readlink");
|
||||||
// try testReadlink("libc_pie_readlink");
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
test "nolibc_nopie_clone_raw" {
|
test "nolibc_nopie_clone_raw" {
|
||||||
try testHelper(
|
try testHelper(
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const linux = std.os.linux;
|
const linux = std.os.linux;
|
||||||
|
const posix = std.posix;
|
||||||
const Patcher = @import("Patcher.zig");
|
const Patcher = @import("Patcher.zig");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
|
const page_size = std.heap.pageSize();
|
||||||
|
|
||||||
/// Represents the stack layout pushed by `syscallEntry` before calling the handler.
|
/// Represents the stack layout pushed by `syscallEntry` before calling the handler.
|
||||||
pub const SavedContext = extern struct {
|
pub const SavedContext = extern struct {
|
||||||
padding: u64, // Result of `sub $8, %rsp` for alignment
|
padding: u64, // Result of `sub $8, %rsp` for alignment
|
||||||
@@ -80,30 +83,75 @@ export fn syscall_handler(ctx: *SavedContext) callconv(.c) void {
|
|||||||
: .{ .memory = true });
|
: .{ .memory = true });
|
||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
.execve, .execveat => |s| {
|
.mmap => {
|
||||||
// TODO: option to persist across new processes
|
// mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)
|
||||||
std.debug.print("syscall {} called\n", .{s});
|
|
||||||
|
const prot: u32 = @intCast(ctx.rdx);
|
||||||
|
// Execute the syscall first to get the address (rax)
|
||||||
|
ctx.rax = executeSyscall(ctx);
|
||||||
|
const addr = ctx.rax;
|
||||||
|
const len = ctx.rsi;
|
||||||
|
|
||||||
|
const is_error = @as(i64, @bitCast(ctx.rax)) < 0;
|
||||||
|
if (is_error) return;
|
||||||
|
if ((prot & posix.PROT.EXEC) == 0) return;
|
||||||
|
if (len <= 0) return;
|
||||||
|
|
||||||
|
// mmap addresses are always page aligned
|
||||||
|
const ptr = @as([*]align(page_size) u8, @ptrFromInt(addr));
|
||||||
|
// Check if we can patch it
|
||||||
|
Patcher.patchRegion(ptr[0..len]) catch |err| {
|
||||||
|
std.log.warn("JIT Patching failed: {}", .{err});
|
||||||
|
};
|
||||||
|
|
||||||
|
// patchRegion leaves it as RW. We need to restore to requested prot.
|
||||||
|
_ = linux.syscall3(.mprotect, addr, len, prot);
|
||||||
|
return;
|
||||||
},
|
},
|
||||||
.prctl, .arch_prctl, .set_tid_address => |s| {
|
.mprotect => {
|
||||||
|
// mprotect(void *addr, size_t len, int prot)
|
||||||
|
// TODO: cleanup trampolines, when removing X
|
||||||
|
const prot: u32 = @intCast(ctx.rdx);
|
||||||
|
if ((prot & posix.PROT.EXEC) != 0) {
|
||||||
|
const addr = ctx.rdi;
|
||||||
|
const len = ctx.rsi;
|
||||||
|
// mprotect requires addr to be page aligned.
|
||||||
|
if (len > 0 and std.mem.isAligned(addr, page_size)) {
|
||||||
|
const ptr = @as([*]align(page_size) u8, @ptrFromInt(addr));
|
||||||
|
Patcher.patchRegion(ptr[0..len]) catch |err| {
|
||||||
|
std.log.warn("mprotect Patching failed: {}", .{err});
|
||||||
|
};
|
||||||
|
// patchRegion leaves it R|W.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.rax = executeSyscall(ctx);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
.execve, .execveat => {
|
||||||
|
// TODO: option to persist across new processes
|
||||||
|
ctx.rax = executeSyscall(ctx);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
.prctl, .arch_prctl, .set_tid_address => {
|
||||||
// TODO: what do we need to handle from these?
|
// TODO: what do we need to handle from these?
|
||||||
// process name
|
// process name
|
||||||
// fs base(gs?)
|
// fs base(gs?)
|
||||||
// thread id pointers
|
// thread id pointers
|
||||||
std.debug.print("syscall {} called\n", .{s});
|
ctx.rax = executeSyscall(ctx);
|
||||||
},
|
return;
|
||||||
.mmap, .mprotect => {
|
|
||||||
// TODO: JIT support
|
|
||||||
// TODO: cleanup
|
|
||||||
},
|
},
|
||||||
.munmap, .mremap => {
|
.munmap, .mremap => {
|
||||||
// TODO: cleanup
|
// TODO: cleanup
|
||||||
|
ctx.rax = executeSyscall(ctx);
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
// Write result back to the saved RAX so it is restored to the application.
|
||||||
|
ctx.rax = executeSyscall(ctx);
|
||||||
|
return;
|
||||||
},
|
},
|
||||||
|
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
|
unreachable;
|
||||||
// Write result back to the saved RAX so it is restored to the application.
|
|
||||||
ctx.rax = executeSyscall(ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn executeSyscall(ctx: *SavedContext) u64 {
|
inline fn executeSyscall(ctx: *SavedContext) u64 {
|
||||||
|
|||||||
Reference in New Issue
Block a user