Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve cross-platform support for platforms with greater r^x restrictions #28

Open
jam1garner opened this issue Dec 11, 2021 · 0 comments

Comments

@jam1garner
Copy link

jam1garner commented Dec 11, 2021

Motivation

Currently, this library uses a few abstractions which assume rwx abstractions exist. This model, however, is becoming increasingly less popular in embedded devices, with iOS and the Nintendo Switch both using doubly mapped memory for JIT, (one being rw- and another being either r-x or --x) rather than remapping existing memory, as this reduces the attack surface for ROP to escalate into arbitrary code execution. This approach is also becoming popular in other systems (another example being .NET 6 using this to harden their JIT against escalation of vulnerabilities)

In order to support more targets, it'd likely be better to abstract away the actual read/write accesses behind a Deref<Target = [u8]> implementation in order to allow target-specific access patterns.

My personal motivation here is possibly working with @Raytwo to add aarch64 support and hopefully eventually make use of it for providing a unified library for modding games on both PC and the Nintendo Switch. Since the Switch doesn't support rwx and has no direct equivalent to mmap*, these changes are necessary in order to make the library workable for our usecase.

*sorta

Implementation Proposal

From reading through the source I'm primarily seeing 2 things which rely on this assumption:

  1. region::protect_with_handle - Being used to rwx map existing r-x memory temporarily in order to enable patching existing code
    • Rework to use a function for getting a RAII guard for accessing the memory as rw-. The RwGuard<'a> would implement Deref<Target = [u8]> in order to deref to the rw- mapping for the memory. For platforms continuing to use the region library, this would simply Deref to the rwx memory, for other platforms it would Deref to the second rw- mapping which would have different addresses but maps to the same memory. This guard would also implement Drop to handle any locking/unlocking/icache clearing that would need to be done.
fn get_as_rw<'a, T>(addr: *const T, size: usize) -> RwGuard<'a>;

impl<'a> Deref for RwGuard<'a> {
    type Target = [u8];
}

impl<'a> DerefMut for RwGuard<'a> {}
  1. mmap - Being used to rwx map entirely new memory to a specific address
    • This one is easier, similar approach to the last one (return a struct which implements Deref<Target=[u8]>) but with less of a RAII guard style usage and more just "this is now a mapping that is owned by this type".
impl RwxMap {
    fn new(addr: *const T, size: usize) -> Result<Self, Error>;
    fn addr(&self) -> *const c_void;
    // ...
}
  • This may require a .lock() method to return a RwGuard or something similar, in order to properly lock/unlock/protect/unprotect memory
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant