frida / frida-rust

Frida Rust bindings
Other
188 stars 53 forks source link

Returning a released pointer from RangeDetails::with_address #120

Closed mkravchik closed 10 months ago

mkravchik commented 11 months ago

Hi, I consistently encountered this in the code I'm debugging. There is a call to RangeDetails::with_address. The returned value is complete nonsense. When I debug it, I can see that when the save_range_details_by_address is called, it is passed a pointer to a details struct with correct data. However when we get back to with_address, the pointer points to garbage. I can only assume that the native Gum has freed this object and don't see anything preventing it from doing so.

pub fn with_address(address: u64) -> Option<RangeDetails<'a>> {
    let mut context = SaveRangeDetailsByAddressContext {
        address,
        details: core::ptr::null_mut(),
    };
    unsafe {
        gum_sys::gum_process_enumerate_ranges(
            gum_sys::_GumPageProtection_GUM_PAGE_NO_ACCESS as u32,
            Some(save_range_details_by_address),
            &mut context as *mut _ as *mut c_void,
        );
    }

    if !context.details.is_null() {
        Some(RangeDetails::from_raw(context.details))
    } else {
        None
    }
}
s1341 commented 10 months ago

Nice catch!

The upstream code is (for linux) https://github.com/frida/frida-gum/blob/a50dae75a25cb7fe95ed09883527dbd6777f28be/gum/backend-linux/gumprocess-linux.c#L1398.

I will attempt a fix now.

s1341 commented 10 months ago

@mkravchik can you try this fix and confirm it is working?

s1341 commented 10 months ago

Oh wait. I'm dumb... This doesn't fix at all...

s1341 commented 10 months ago

Ok. Try now

mkravchik commented 10 months ago

I tried it, and it looks good. In the meantime (until the version is out) I've found the following workaround useful:

/// Get the start and end of the memory region containing the given address
/// Uses RangeDetails::enumerate_with_prot as RangeDetails::with_address seems to have a bug
/// Returns (start, end)
fn range_for_address(address: usize) -> (usize, usize) {
    let mut start = 0; 
    let mut end = 0; 
    RangeDetails::enumerate_with_prot(PageProtection::NoAccess, &mut |range: &RangeDetails| {
        let range_start = range.memory_range().base_address().0 as usize;
        let range_end = range_start + range.memory_range().size();
        if range_start <= address && range_end >= address {
            start = range_start;
            end = range_end;
            // I want to stop iteration here
            return false;
        }
        true
    });

    if start == 0 {
        log::error!("range_for_address: no range found for address {:#x}", address);
    }
    (start, end)
}
s1341 commented 10 months ago

I'll wait till the next frida version to release.