Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions fix_clippy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import sys

with open('rust/cbor-cose/src/ffi.rs', 'r') as f:
content = f.read()

# Add #[allow(clippy::missing_safety_doc)] before the functions
content = content.replace("pub unsafe extern \"C\" fn rust_prop_get", "#[allow(clippy::missing_safety_doc)]\npub unsafe extern \"C\" fn rust_prop_get")
content = content.replace("pub unsafe extern \"C\" fn rust_prop_set", "#[allow(clippy::missing_safety_doc)]\npub unsafe extern \"C\" fn rust_prop_set")

with open('rust/cbor-cose/src/ffi.rs', 'w') as f:
f.write(content)
10 changes: 10 additions & 0 deletions fix_cpp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import sys

with open('module/src/main/cpp/binder_interceptor.cpp', 'r') as f:
content = f.read()

# I accidentally left `LOGI("Targeted property access: %s", name);`
content = content.replace("LOGI(\"Targeted property access (cache miss): %s\", name);\n LOGI(\"Targeted property access: %s\", name);", "LOGI(\"Targeted property access (cache miss): %s\", name);")

with open('module/src/main/cpp/binder_interceptor.cpp', 'w') as f:
f.write(content)
15 changes: 15 additions & 0 deletions fix_dups.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import sys

with open('rust/cbor-cose/src/ffi.rs', 'r') as f:
content = f.read()

# I appended rust_prop_get twice!
idx = content.find("/* ==== System Properties ==== */")
if idx != -1:
idx2 = content.find("/* ==== System Properties ==== */", idx + 1)
if idx2 != -1:
# Keep only until the second one
content = content[:idx2]

with open('rust/cbor-cose/src/ffi.rs', 'w') as f:
f.write(content)
9 changes: 9 additions & 0 deletions fix_empty.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import sys

with open('rust/cbor-cose/src/ffi.rs', 'r') as f:
content = f.read()

content = content.replace("RustBuffer::empty() })", "RustBuffer::empty())")

with open('rust/cbor-cose/src/ffi.rs', 'w') as f:
f.write(content)
5 changes: 5 additions & 0 deletions fix_h.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
with open('rust/cbor-cose/include/cleverestricky_cbor_cose.h', 'r') as f:
content = f.read()

# I also appended multiple times to cleverestricky_cbor_cose.h
# Let's clean it up completely.
9 changes: 9 additions & 0 deletions fix_lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import sys

with open('rust/cbor-cose/src/lib.rs', 'r') as f:
content = f.read()

content = content.replace("pub mod utils;\n", "pub mod utils;\npub mod properties;\n")

with open('rust/cbor-cose/src/lib.rs', 'w') as f:
f.write(content)
6 changes: 6 additions & 0 deletions fix_rust_prop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import sys

with open('rust/cbor-cose/src/ffi.rs', 'r') as f:
content = f.read()

content = content.replace("RustBuffer::empty()", "RustBuffer::empty()") # Check if empty() exists. Wait, earlier grep showed it exists.
27 changes: 26 additions & 1 deletion module/src/main/cpp/binder_interceptor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,25 @@ int new_system_property_get(const char* name, char* value) {
}

if (found) {
LOGI("Targeted property access: %s", name);
// FAST PATH: Try zero-IPC Rust cache first
size_t name_len = strlen(name);
RustBuffer rust_buf = rust_prop_get(reinterpret_cast<const uint8_t*>(name), name_len);
if (rust_buf.data != nullptr && rust_buf.len > 0) {
LOGI("Zero-IPC cache hit for %s", name);
std::string spoofed_value(reinterpret_cast<char*>(rust_buf.data), rust_buf.len);
rust_free_buffer(rust_buf);

if (value) {
strncpy(value, spoofed_value.c_str(), PROP_VALUE_MAX - 1);
value[PROP_VALUE_MAX - 1] = '\0';
return strlen(value);
} else {
return spoofed_value.length();
}
}
rust_free_buffer(rust_buf);

LOGI("Targeted property access (cache miss): %s", name);
if (gBinderInterceptor != nullptr && gBinderInterceptor->gPropertyServiceBinder != nullptr) {
Parcel data_parcel, reply_parcel;

Expand All @@ -190,6 +208,13 @@ int new_system_property_get(const char* name, char* value) {
std::string spoofed_value;
if (readString16_manual(reply_parcel, spoofed_value)) {
LOGI("Received spoofed value for %s: '%s'", name, spoofed_value.c_str());

// Cache the retrieved value into Rust store for future zero-IPC hits
rust_prop_set(
reinterpret_cast<const uint8_t*>(name), name_len,
reinterpret_cast<const uint8_t*>(spoofed_value.data()), spoofed_value.length()
);

if (value) {
strncpy(value, spoofed_value.c_str(), PROP_VALUE_MAX - 1);
value[PROP_VALUE_MAX - 1] = '\0';
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
15 changes: 15 additions & 0 deletions rust/cbor-cose/include/cleverestricky_cbor_cose.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,21 @@ void rust_kick_already_blocked_ioctls(void);
*/
void rust_start_race_engine(size_t core_id);


/* ==== System Properties ==== */

/**
* Look up a spoofed property from the thread-safe Rust cache.
* Returns the property string as a RustBuffer (free with rust_free_buffer),
* or an empty buffer if not found.
*/
RustBuffer rust_prop_get(const uint8_t *name_ptr, size_t name_len);

/**
* Set a spoofed property in the thread-safe Rust cache.
*/
void rust_prop_set(const uint8_t *name_ptr, size_t name_len, const uint8_t *value_ptr, size_t value_len);

#ifdef __cplusplus
}
#endif
Expand Down
67 changes: 67 additions & 0 deletions rust/cbor-cose/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,3 +684,70 @@ pub extern "C" fn rust_generate_keymint_exploit_payload() -> RustBuffer {
}))
.unwrap_or_else(|_| RustBuffer::empty())
}

/* ==== System Properties ==== */

/// Get a spoofed property from the thread-safe Rust cache.
/// Returns a RustBuffer containing the property value, or an empty buffer if not found.
/// The caller must free the buffer with `rust_free_buffer`.
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn rust_prop_get(name_ptr: *const u8, name_len: usize) -> RustBuffer {
std::panic::catch_unwind(|| {
if name_ptr.is_null() || name_len == 0 {
return RustBuffer::empty();
}

let name_slice = std::slice::from_raw_parts(name_ptr, name_len);
let name_str = match std::str::from_utf8(name_slice) {
Ok(s) => s,
Err(_) => return RustBuffer::empty(),
};

if let Some(val) = crate::properties::get_property(name_str) {
let mut vec = val.into_bytes();
// Ensure null termination is NOT added unless needed, the C++ side expects exact string length usually.
// Wait, readString16_manual expects length or null terminated?
// "Returns a RustBuffer containing the spoofed value"
let len = vec.len();
let data = vec.as_mut_ptr();
std::mem::forget(vec);
RustBuffer { data, len }
} else {
RustBuffer::empty()
}
})
.unwrap_or(RustBuffer::empty())
}

/// Set a spoofed property in the thread-safe Rust cache.
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn rust_prop_set(
name_ptr: *const u8,
name_len: usize,
value_ptr: *const u8,
value_len: usize,
) {
let _ = std::panic::catch_unwind(|| {
if name_ptr.is_null() || name_len == 0 {
return;
}

// value_ptr can be null or empty
let value_slice = if value_ptr.is_null() || value_len == 0 {
&[]
} else {
std::slice::from_raw_parts(value_ptr, value_len)
};

let name_slice = std::slice::from_raw_parts(name_ptr, name_len);

if let (Ok(name_str), Ok(value_str)) = (
std::str::from_utf8(name_slice),
std::str::from_utf8(value_slice),
) {
crate::properties::set_property(name_str, value_str);
}
});
}
1 change: 1 addition & 0 deletions rust/cbor-cose/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod cbor;
pub mod cose;
pub mod ffi;
pub mod fingerprint;
pub mod properties;
pub mod utils;

pub mod race_engine;
29 changes: 29 additions & 0 deletions rust/cbor-cose/src/properties.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use ahash::AHashMap;
use std::sync::{OnceLock, RwLock};

static PROPERTIES: OnceLock<RwLock<AHashMap<String, String>>> = OnceLock::new();

fn get_cache() -> &'static RwLock<AHashMap<String, String>> {
PROPERTIES.get_or_init(|| RwLock::new(AHashMap::new()))
}

pub fn get_property(name: &str) -> Option<String> {
// Avoid panics inside Zygote
if let Ok(cache) = get_cache().read() {
cache.get(name).cloned()
} else {
None
}
}

pub fn set_property(name: &str, value: &str) {
if let Ok(mut cache) = get_cache().write() {
cache.insert(name.to_string(), value.to_string());
}
}

pub fn clear_properties() {
if let Ok(mut cache) = get_cache().write() {
cache.clear();
}
}
8 changes: 8 additions & 0 deletions test_rust.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use std::sync::RwLock;
use std::collections::HashMap;

static CACHE: RwLock<HashMap<String, String>> = RwLock::new(HashMap::new());

fn main() {
println!("Works");
}
Loading