use super::sys::*;
use crate::CoreError;
use crate::android::dex;
use std::os::raw::{c_char, c_void};
const AM_DESCRIPTOR: &[u8] = b"android.app.IActivityManager\0";
const ICP_DESCRIPTOR: &[u8] = b"android.content.IContentProvider\0";
const ACTIVITY_SERVICE: &[u8] = b"activity\0";
const CALL_TRANSACTION: u32 = 21;
const ATTRIBUTION_START_SIZE: i32 = 56;
const BUNDLE_LENGTH: i32 = 56;
const BUNDLE_KEY: &str = "binder";
unsafe extern "C" fn hf_on_create(_: *mut c_void) -> *mut c_void {
std::ptr::null_mut()
}
unsafe extern "C" fn hf_on_destroy(_: *mut c_void) {}
unsafe extern "C" fn hf_on_transact(
_: *mut AIBinder,
_: u32,
_: *const AParcel,
_: *mut AParcel,
) -> BinderStatus {
STATUS_UNKNOWN_TRANSACTION
}
fn skip_pattern_matcher(r: &ParcelReader<'_>) -> Result<(), CoreError> {
r.skip_string16()?;
r.read_i32()?;
r.skip_int_array()
}
fn skip_path_permission(r: &ParcelReader<'_>) -> Result<(), CoreError> {
skip_pattern_matcher(r)?;
r.skip_string16()?;
r.skip_string16().map(|_| ())
}
fn skip_shared_library_info(r: &ParcelReader<'_>) -> Result<(), CoreError> {
r.skip_string8()?;
r.skip_string8()?;
if r.read_i32()? == 1 {
r.skip_string8_array()?;
}
r.skip_string8()?;
r.read_int64()?;
r.read_i32()?;
if r.skip_string16()? {
r.skip_string8()?;
r.read_int64()?;
}
let dep = r.read_i32()?;
if dep >= 0 {
for _ in 0..dep {
r.skip_value()?;
}
}
let deps = r.read_i32()?;
if deps >= 0 {
for _ in 0..deps {
if r.read_i32()? == 1 {
r.skip_string8()?;
r.read_int64()?;
}
}
}
r.read_i32()?;
Ok(())
}
fn skip_package_item_info(r: &ParcelReader<'_>) -> Result<(), CoreError> {
r.skip_string8()?;
r.skip_string8()?;
r.read_i32()?;
r.skip_char_sequence()?;
r.read_i32()?;
r.read_i32()?;
r.skip_bundle()?;
r.read_i32()?;
r.read_i32().map(|_| ())
}
fn walk_application_info(r: &ParcelReader<'_>) -> Result<i32, CoreError> {
if r.read_i32()? != 0 {
return Err(CoreError::binder(-1, "handoff:squashed_application_info"));
}
skip_package_item_info(r)?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_i32s(7)?;
if r.read_i32()? == 1 {
r.read_int64()?;
r.read_int64()?;
}
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8_array()?;
r.skip_string8_array()?;
r.skip_string8_array()?;
r.skip_sparsearray()?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8()?;
r.read_i32()?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8_array()?;
r.skip_string8_array()?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8_array()?;
r.skip_typed_object_array(skip_shared_library_info)?;
r.skip_string8()?;
r.skip_string8()?;
r.skip_string8()?;
let uid = r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_int64()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.skip_string8()?;
r.skip_string8()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.skip_string8()?;
r.skip_string8_array()?;
r.read_i32()?;
r.skip_string8()?;
r.skip_string8()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.skip_string8()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_i32()?;
r.read_int64()?;
let names = r.read_i32()?;
if names > 0 {
for _ in 0..names {
r.skip_string16()?;
r.skip_string16()?;
}
}
r.read_i32()?;
let certs = r.read_i32()?;
if certs >= 0 {
for _ in 0..certs {
r.skip_string16()?;
}
}
Ok(uid)
}
#[allow(clippy::type_complexity)]
fn walk_holder(
r: &ParcelReader<'_>,
) -> Result<(Option<OwnedBinder>, Option<OwnedBinder>, i32, bool, bool), CoreError> {
skip_package_item_info(r)?;
let uid = walk_application_info(r)?;
r.skip_string8()?; r.skip_string8()?; r.skip_string8_array()?; r.read_i32()?; r.read_i32()?; r.read_i32()?; r.read_i32()?; r.skip_string8()?; r.skip_string8()?; r.skip_string8()?; r.read_i32()?; r.read_i32()?; r.skip_typed_object_array(skip_pattern_matcher)?; r.skip_typed_object_array(skip_path_permission)?; r.read_i32()?; r.read_i32()?; r.read_i32()?; r.read_i32()?; let provider = r.read_strong_binder()?;
let connection = r.read_strong_binder()?;
let no_release_needed = r.read_i32()? != 0;
let m_local = r.read_i32()? != 0;
Ok((provider, connection, uid, no_release_needed, m_local))
}
pub struct ProviderHandle {
pub provider: OwnedBinder,
pub connection: OwnedBinder,
pub uid: i32,
pub no_release_needed: bool,
}
pub struct Handoff {
_lib: DlHandle,
vt: Vtable,
_am_class: *mut AIBinder_Class,
_icp_class: *mut AIBinder_Class,
service: OwnedBinder,
get_code: u32,
remove_code: u32,
}
unsafe impl Send for Handoff {}
impl Handoff {
pub fn open() -> Result<Self, CoreError> {
let handle = unsafe {
libc::dlopen(
LIBBINDER_PATH.as_ptr() as *const c_char,
libc::RTLD_NOW | libc::RTLD_LOCAL,
)
};
if handle.is_null() {
return Err(CoreError::binder(-1, "dlopen:libbinder_ndk.so"));
}
let lib = DlHandle;
let vt = load_vtable(handle)?;
let am_class = unsafe {
(vt.class_define)(
AM_DESCRIPTOR.as_ptr() as *const c_char,
hf_on_create,
hf_on_destroy,
hf_on_transact,
)
};
if am_class.is_null() {
return Err(CoreError::binder(-1, "AIBinder_Class_define:AM"));
}
let icp_class = unsafe {
(vt.class_define)(
ICP_DESCRIPTOR.as_ptr() as *const c_char,
hf_on_create,
hf_on_destroy,
hf_on_transact,
)
};
if icp_class.is_null() {
return Err(CoreError::binder(
-1,
"AIBinder_Class_define:IContentProvider",
));
}
let raw = unsafe { (vt.get_service)(ACTIVITY_SERVICE.as_ptr() as *const c_char) };
if raw.is_null() {
return Err(CoreError::binder(-1, "AServiceManager_getService:activity"));
}
unsafe { (vt.associate_class)(raw, am_class) };
let service = OwnedBinder {
ptr: raw,
dec_strong: vt.dec_strong,
};
let (get_code, remove_code) = dex::resolve_handoff_codes()
.ok_or_else(|| CoreError::binder(-1, "handoff:tx_code_resolution:dex_parse_failed"))?;
Ok(Self {
_lib: lib,
vt,
_am_class: am_class,
_icp_class: icp_class,
service,
get_code,
remove_code,
})
}
pub fn acquire_provider(
&self,
authority: &str,
user_id: i32,
) -> Result<ProviderHandle, CoreError> {
let out = transact_write(&self.vt, self.service.ptr, self.get_code, |w| {
w.write_string(Some(authority))?;
w.write_i32(user_id)?;
w.write_strong_binder(std::ptr::null_mut())?;
w.write_string(Some(authority))
})?;
let r = ParcelReader::owned(&self.vt, &out);
let ex = r.read_i32()?;
if ex != EX_NONE {
return Err(CoreError::binder(
ex,
"getContentProviderExternal:exception",
));
}
if r.read_i32()? != 1 {
return Err(CoreError::binder(
-1,
"getContentProviderExternal:no_holder",
));
}
let (provider, connection, uid, no_release_needed, _m_local) = walk_holder(&r)?;
let provider = provider
.ok_or_else(|| CoreError::binder(-1, "getContentProviderExternal:null_provider"))?;
unsafe { (self.vt.associate_class)(provider.ptr, self._icp_class) };
let connection = connection.unwrap_or_else(|| OwnedBinder {
ptr: std::ptr::null_mut(),
dec_strong: self.vt.dec_strong,
});
Ok(ProviderHandle {
provider,
connection,
uid,
no_release_needed,
})
}
pub fn send_binder(
&self,
handle: &ProviderHandle,
authority: &str,
method: &str,
arg: &str,
service_binder: *mut c_void,
) -> Result<(), CoreError> {
let euid = unsafe { libc::geteuid() } as i32;
let out = transact_write(&self.vt, handle.provider.ptr, CALL_TRANSACTION, |w| {
w.write_i32(ATTRIBUTION_START_SIZE)?;
w.write_i32(-1)?; w.write_i32(euid)?; w.write_string(None)?; w.write_string(None)?; w.write_strong_binder(std::ptr::null_mut())?; w.write_i32(-1)?; w.write_i32(-1)?; w.write_string(Some(authority))?;
w.write_string(Some(method))?;
w.write_string(Some(arg))?;
w.write_i32(BUNDLE_LENGTH)?;
w.write_i32(BUNDLE_MAGIC)?;
w.write_i32(1)?;
w.write_string(Some(BUNDLE_KEY))?;
w.write_i32(VAL_IBINDER)?;
w.write_strong_binder(service_binder)
})?;
let r = ParcelReader::owned(&self.vt, &out);
let ex = r.read_i32()?;
if ex != EX_NONE {
return Err(CoreError::binder(ex, "call:exception"));
}
Ok(())
}
pub fn remove_provider(&self, authority: &str, user_id: i32) -> Result<(), CoreError> {
let out = transact_write(&self.vt, self.service.ptr, self.remove_code, |w| {
w.write_string(Some(authority))?;
w.write_strong_binder(std::ptr::null_mut())?;
w.write_i32(user_id)
})?;
let r = ParcelReader::owned(&self.vt, &out);
let ex = r.read_i32()?;
if ex != EX_NONE {
return Err(CoreError::binder(
ex,
"removeContentProviderExternalAsUser:exception",
));
}
Ok(())
}
pub fn handoff(
&self,
authority: &str,
user_id: i32,
method: &str,
arg: &str,
service_binder: *mut c_void,
) -> Result<i32, CoreError> {
let handle = self.acquire_provider(authority, user_id)?;
let result = self.send_binder(&handle, authority, method, arg, service_binder);
if !handle.no_release_needed {
let _ = self.remove_provider(authority, user_id);
}
result?;
Ok(handle.uid)
}
}