Skip to content

Commit

Permalink
Hooks for NOCANCEL functions. (#1204)
Browse files Browse the repository at this point in the history
* Hooks for NOCANCEL functions.

* Add to changelog.
  • Loading branch information
meowjesty authored Mar 21, 2023
1 parent a721e01 commit 9693530
Show file tree
Hide file tree
Showing 3 changed files with 178 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog.d/1203.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add version of hooked functions.
138 changes: 138 additions & 0 deletions mirrord/layer/src/file/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,19 @@ pub(crate) unsafe extern "C" fn openat_detour(
.unwrap_or_bypass_with(|_| FN_OPENAT(fd, raw_path, open_flags))
}

#[hook_guard_fn]
pub(crate) unsafe extern "C" fn _openat_nocancel_detour(
fd: RawFd,
raw_path: *const c_char,
open_flags: c_int,
) -> RawFd {
let rawish_path = (!raw_path.is_null()).then(|| CStr::from_ptr(raw_path));
let open_options: OpenOptionsInternal = OpenOptionsInternalExt::from_flags(open_flags);

openat(fd, rawish_path, open_options)
.unwrap_or_bypass_with(|_| FN__OPENAT_NOCANCEL(fd, raw_path, open_flags))
}

/// Hook for getdents64, for Go's `os.ReadDir` on Linux.
#[cfg(target_os = "linux")]
#[hook_guard_fn]
Expand Down Expand Up @@ -283,6 +296,31 @@ pub(crate) unsafe extern "C" fn read_detour(
.unwrap_or_bypass_with(|_| FN_READ(fd, out_buffer, count))
}

#[hook_guard_fn]
pub(crate) unsafe extern "C" fn _read_nocancel_detour(
fd: RawFd,
out_buffer: *mut c_void,
count: size_t,
) -> ssize_t {
read(fd, count as u64)
.map(|read_file| {
let ReadFileResponse { bytes, read_amount } = read_file;

// There is no distinction between reading 0 bytes or if we hit EOF, but we only copy to
// buffer if we have something to copy.
if read_amount > 0 {
let read_ptr = bytes.as_ptr();
let out_buffer = out_buffer.cast();
ptr::copy(read_ptr, out_buffer, read_amount as usize);
}

// WARN: Must be careful when it comes to `EOF`, incorrect handling may appear as the
// `read` call being repeated.
ssize_t::try_from(read_amount).unwrap()
})
.unwrap_or_bypass_with(|_| FN__READ_NOCANCEL(fd, out_buffer, count))
}

/// Hook for `libc::fread`.
///
/// Reads `element_size * number_of_elements` bytes into `out_buffer`, only for `*mut FILE`s that
Expand Down Expand Up @@ -386,6 +424,32 @@ pub(crate) unsafe extern "C" fn pread_detour(
.unwrap_or_bypass_with(|_| FN_PREAD(fd, out_buffer, amount_to_read, offset))
}

#[hook_guard_fn]
pub(crate) unsafe extern "C" fn _pread_nocancel_detour(
fd: RawFd,
out_buffer: *mut c_void,
amount_to_read: size_t,
offset: off_t,
) -> ssize_t {
pread(fd, amount_to_read as u64, offset as u64)
.map(|read_file| {
let ReadFileResponse { bytes, read_amount } = read_file;
let fixed_read = (amount_to_read as u64).min(read_amount);

// There is no distinction between reading 0 bytes or if we hit EOF, but we only
// copy to buffer if we have something to copy.
//
// Callers can check for EOF by using `ferror`.
if read_amount > 0 {
let bytes_slice = &bytes[0..fixed_read as usize];

ptr::copy(bytes_slice.as_ptr().cast(), out_buffer, bytes_slice.len());
}
fixed_read as ssize_t
})
.unwrap_or_bypass_with(|_| FN__PREAD_NOCANCEL(fd, out_buffer, amount_to_read, offset))
}

#[hook_guard_fn]
pub(crate) unsafe extern "C" fn pwrite_detour(
fd: RawFd,
Expand All @@ -404,6 +468,24 @@ pub(crate) unsafe extern "C" fn pwrite_detour(
.unwrap_or_bypass_with(|_| FN_PWRITE(fd, in_buffer, amount_to_write, offset))
}

#[hook_guard_fn]
pub(crate) unsafe extern "C" fn _pwrite_nocancel_detour(
fd: RawFd,
in_buffer: *const c_void,
amount_to_write: size_t,
offset: off_t,
) -> ssize_t {
// Convert the given buffer into a u8 slice, upto the amount to write.
let casted_in_buffer: &[u8] = slice::from_raw_parts(in_buffer.cast(), amount_to_write);

pwrite(fd, casted_in_buffer, offset as u64)
.map(|write_response| {
let WriteFileResponse { written_amount } = write_response;
written_amount as ssize_t
})
.unwrap_or_bypass_with(|_| FN__PWRITE_NOCANCEL(fd, in_buffer, amount_to_write, offset))
}

/// Hook for `libc::lseek`.
///
/// **Bypassed** by `fd`s that are not managed by us (not found in `OPEN_FILES`).
Expand Down Expand Up @@ -440,6 +522,20 @@ pub(crate) unsafe extern "C" fn write_detour(
write_logic(fd, buffer, count)
}

#[hook_guard_fn]
pub(crate) unsafe extern "C" fn _write_nocancel_detour(
fd: RawFd,
buffer: *const c_void,
count: size_t,
) -> ssize_t {
// WARN: Be veeery careful here, you cannot construct the `Vec` directly, as the buffer
// allocation is handled on the C side.
let write_bytes =
(!buffer.is_null()).then(|| slice::from_raw_parts(buffer as *const u8, count).to_vec());

write(fd, write_bytes).unwrap_or_bypass_with(|_| FN__WRITE_NOCANCEL(fd, buffer, count))
}

/// Implementation of access_detour, used in access_detour and faccessat_detour
unsafe fn access_logic(raw_path: *const c_char, mode: c_int) -> c_int {
let rawish_path = (!raw_path.is_null()).then(|| CStr::from_ptr(raw_path));
Expand Down Expand Up @@ -694,11 +790,27 @@ unsafe extern "C" fn fstatat_detour(
/// Convenience function to setup file hooks (`x_detour`) with `frida_gum`.
pub(crate) unsafe fn enable_file_hooks(hook_manager: &mut HookManager) {
replace!(hook_manager, "open", open_detour, FnOpen, FN_OPEN);

replace!(hook_manager, "openat", openat_detour, FnOpenat, FN_OPENAT);
replace!(
hook_manager,
"_openat$NOCANCEL",
_openat_nocancel_detour,
Fn_openat_nocancel,
FN__OPENAT_NOCANCEL
);

replace!(hook_manager, "fopen", fopen_detour, FnFopen, FN_FOPEN);
replace!(hook_manager, "fdopen", fdopen_detour, FnFdopen, FN_FDOPEN);

replace!(hook_manager, "read", read_detour, FnRead, FN_READ);
replace!(
hook_manager,
"_read$NOCANCEL",
_read_nocancel_detour,
Fn_read_nocancel,
FN__READ_NOCANCEL
);

replace!(
hook_manager,
Expand All @@ -709,13 +821,39 @@ pub(crate) unsafe fn enable_file_hooks(hook_manager: &mut HookManager) {
);
replace!(hook_manager, "fread", fread_detour, FnFread, FN_FREAD);
replace!(hook_manager, "fgets", fgets_detour, FnFgets, FN_FGETS);

replace!(hook_manager, "pread", pread_detour, FnPread, FN_PREAD);
replace!(
hook_manager,
"_pread$NOCANCEL",
_pread_nocancel_detour,
Fn_pread_nocancel,
FN__PREAD_NOCANCEL
);

replace!(hook_manager, "ferror", ferror_detour, FnFerror, FN_FERROR);
replace!(hook_manager, "fclose", fclose_detour, FnFclose, FN_FCLOSE);
replace!(hook_manager, "fileno", fileno_detour, FnFileno, FN_FILENO);
replace!(hook_manager, "lseek", lseek_detour, FnLseek, FN_LSEEK);

replace!(hook_manager, "write", write_detour, FnWrite, FN_WRITE);
replace!(
hook_manager,
"_write$NOCANCEL",
_write_nocancel_detour,
Fn_write_nocancel,
FN__WRITE_NOCANCEL
);

replace!(hook_manager, "pwrite", pwrite_detour, FnPwrite, FN_PWRITE);
replace!(
hook_manager,
"_pwrite$NOCANCEL",
_pwrite_nocancel_detour,
Fn_pwrite_nocancel,
FN__PWRITE_NOCANCEL
);

replace!(hook_manager, "access", access_detour, FnAccess, FN_ACCESS);
replace!(
hook_manager,
Expand Down
39 changes: 39 additions & 0 deletions mirrord/layer/src/socket/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ pub(crate) unsafe extern "C" fn connect_detour(
.unwrap_or_bypass_with(|_| FN_CONNECT(sockfd, raw_address, address_length))
}

/// Hook for `_connect$NOCANCEL` (for macos, see
/// [this](https://opensource.apple.com/source/xnu/xnu-4570.41.2/libsyscall/Platforms/MacOSX/x86_64/syscall.map.auto.html)).
#[hook_guard_fn]
pub(super) unsafe extern "C" fn _connect_nocancel_detour(
sockfd: RawFd,
raw_address: *const sockaddr,
address_length: socklen_t,
) -> c_int {
connect(sockfd, raw_address, address_length)
.map(From::from)
.unwrap_or_bypass_with(|_| FN__CONNECT_NOCANCEL(sockfd, raw_address, address_length))
}

#[hook_guard_fn]
pub(super) unsafe extern "C" fn getpeername_detour(
sockfd: RawFd,
Expand Down Expand Up @@ -145,6 +158,18 @@ pub(super) unsafe extern "C" fn uv__accept4_detour(
accept4_detour(sockfd, address, address_len, flags)
}

/// Hook for `_accept$NOCANCEL` (for macos, see
/// [this](https://opensource.apple.com/source/xnu/xnu-4570.41.2/libsyscall/Platforms/MacOSX/x86_64/syscall.map.auto.html)).
#[hook_guard_fn]
pub(super) unsafe extern "C" fn _accept_nocancel_detour(
sockfd: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> c_int {
let accept_result = FN__ACCEPT_NOCANCEL(sockfd, address, address_len);
accept(sockfd, address, address_len, accept_result).unwrap_or_bypass(accept_result)
}

/// <https://github.com/metalbear-co/mirrord/issues/184>
#[hook_fn]
pub(super) unsafe extern "C" fn fcntl_detour(fd: c_int, cmd: c_int, mut arg: ...) -> c_int {
Expand Down Expand Up @@ -304,6 +329,13 @@ pub(crate) unsafe fn enable_socket_hooks(hook_manager: &mut HookManager, enabled
FnConnect,
FN_CONNECT
);
replace!(
hook_manager,
"_connect$NOCANCEL",
_connect_nocancel_detour,
Fn_connect_nocancel,
FN__CONNECT_NOCANCEL
);

replace!(hook_manager, "fcntl", fcntl_detour, FnFcntl, FN_FCNTL);
replace!(hook_manager, "dup", dup_detour, FnDup, FN_DUP);
Expand Down Expand Up @@ -356,6 +388,13 @@ pub(crate) unsafe fn enable_socket_hooks(hook_manager: &mut HookManager, enabled
}

replace!(hook_manager, "accept", accept_detour, FnAccept, FN_ACCEPT);
replace!(
hook_manager,
"_accept$NOCANCEL",
_accept_nocancel_detour,
Fn_accept_nocancel,
FN__ACCEPT_NOCANCEL
);

if enabled_remote_dns {
replace!(
Expand Down

0 comments on commit 9693530

Please sign in to comment.