Hello,
> From reading about Goanna, I realized that it should be possible to
> override noexec by avoiding using the kernel mmap implementation for
> mapping executables when they are being executed; the mmaper is the
> one that disallows it, by checking if the file being mmap'ed is on an
> executable mount, as far as i know. libsigsegv
> [
http://www.gnu.org/software/libsigsegv/] might be helpful if
> implementing this.
It should be possible to bypass the noexec limitation on mmap by
replacing file-backed mappings with copies of the expected contents
into anonymous mappings. This might be quite inefficient and some
corner cases [about file-backed mappings] are not easy to simulate
(c.f. chapter 49 "Memory Mappings" from the great "Linux Programming
Interface" book
http://man7.org/tlpi/)
In a PRoot extension, this could be achieved this way:
// pseudo code, no error checking!
// extension event: SYSCALL_ENTER_*
hook_mmap_enter(tracee)
{
prot = peek_reg(tracee, CURRENT, SYSARG_3);
flags = peek_reg(tracee, CURRENT, SYSARG_4);
if ((flags & MAP_ANONYMOUS) != 0)
return; // not a file-backed mmap
prot |= PROT_WRITE;
prot &= ~PROT_EXEC;
flags |= MAP_ANONYMOUS;
poke_reg(tracee, SYSARG_3, prot);
poke_reg(tracee, SYSARG_4, flags);
poke_reg(tracee, SYSARG_5, -1);
}
This modifies the mmap parameters before this syscall is handled by
the kernel. For instance:
- mmap(NULL, 183055, PROT_READ|PROT_EXEC, MAP_PRIVATE, 3, 0)
+ mmap(NULL, 183055, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
Then, once this syscall is completed by the kernel, such an extension
could copy the expected content to the expected address:
// pseudo code, no error checking!
// extension event: SYSCALL_EXIT_*
hook_mmap_exit(tracee)
{
remote_address = peek_reg(tracee, CURRENT, SYSARG_RESULT);
remote_size = peek_reg(tracee, ORIGINAL, SYSARG_2);
remote_fd = peek_reg(tracee, ORIGINAL, SYSARG_5);
local_fd = open("/proc/%d/fd/%d", tracee->pid, remote_fd);
local_address = mmap(NULL, remote_size, PROT_READ,
MAP_PRIVATE, local_fd, 0);
write_data(tracee, remote_address, local_address, remote_size);
}
> also, I realized that I wouldn't care to use the fuse implementation,
> but just to use something which is compatible with fuse, so all the
> existing fuse filesystem implementations can be used transparently. I
> would guess the best way to do this is to reimplement libfuse, but
> emulating /dev/fuse and other fuse interfaces may be needed too.
I don't know what would be easier: writing libfuse-like interface or
writing a "/dev/fuse"-like interface.
Regards,
Cédric.
PS: for information, the project umview (similar to PRoot in some
aspects) has a user-mode dev/loop interface. Although, I don't
know if this could help.