Files
PurrfectSnap/native/rust/src/modules/unary_call_hook.rs
particle-box 327efafbe5 fixes
2026-01-23 20:22:47 +05:30

124 lines
4.7 KiB
Rust

use std::ffi::{c_void, CStr};
use jni::{objects::{JByteArray, JMethodID, JValue}, signature::ReturnType};
use nix::libc;
use once_cell::sync::OnceCell;
use crate::{common::{self}, def_hook, dobby_hook, sig};
#[repr(C)]
#[derive(Copy, Clone)]
struct RefCountedSliceByteBuffer {
ref_counter: *mut c_void,
length: usize,
data: *mut u8
}
#[repr(C)]
struct GrpcByteBuffer {
reserved: *mut c_void,
type_: *mut c_void,
compression: *mut c_void,
slice_buffer: *mut RefCountedSliceByteBuffer
}
static NATIVE_LIB_ON_UNARY_CALL_METHOD: OnceCell<JMethodID> = OnceCell::new();
def_hook!(
unary_call,
*mut c_void,
|unk1: *mut c_void, uri: *const u8, grpc_byte_buffer: *mut *mut GrpcByteBuffer, unk4: *mut c_void, unk5: *mut c_void, unk6: *mut c_void| {
macro_rules! call_original {
() => {
unary_call_original.unwrap()(unk1, uri, grpc_byte_buffer, unk4, unk5, unk6)
};
}
// make a local copy of the slice buffer
let mut slice_buffer = *(**grpc_byte_buffer).slice_buffer;
if slice_buffer.ref_counter.is_null() {
return call_original!();
}
let java_vm = common::java_vm();
let mut env = java_vm.get_env().expect("Failed to get JNIEnv");
let slice_buffer_length = slice_buffer.length as usize;
let jni_buffer = env.new_byte_array(slice_buffer_length as i32).expect("Failed to create new byte array");
env.set_byte_array_region(&jni_buffer, 0, std::slice::from_raw_parts(slice_buffer.data as *const i8, slice_buffer_length)).expect("Failed to set byte array region");
let uri_str = CStr::from_ptr(uri).to_str().unwrap();
let native_request_data_object = env.call_method_unchecked(
common::native_lib_instance(),
NATIVE_LIB_ON_UNARY_CALL_METHOD.get().unwrap(),
ReturnType::Object,
&[
JValue::from(&env.new_string(uri_str).unwrap()).as_jni(),
JValue::from(&jni_buffer).as_jni()
]
).expect("Failed to call onNativeUnaryCall method").l().unwrap();
if native_request_data_object.is_null() {
return call_original!();
}
let is_canceled = env.get_field(&native_request_data_object, "canceled", "Z").expect("Failed to get canceled field").z().unwrap();
if is_canceled {
info!("canceled request for {}", uri_str);
return std::ptr::null_mut();
}
let new_buffer: JByteArray = env.get_field(&native_request_data_object, "buffer", "[B").expect("Failed to get buffer field").l().unwrap().into();
let new_buffer_length = env.get_array_length(&new_buffer).expect("Failed to get array length") as usize;
let mut new_buffer_data = Box::new(vec![0i8; new_buffer_length]);
env.get_byte_array_region(&new_buffer, 0, new_buffer_data.as_mut_slice()).expect("Failed to get byte array region");
let ref_counter_struct_size = (slice_buffer.data as usize) - (slice_buffer.ref_counter as usize);
//we need to allocate a new ref_counter struct and copy the old ref_counter and the new_buffer to it
let new_ref = {
let new_ref = libc::malloc(ref_counter_struct_size + new_buffer_length) as *mut c_void;
libc::memcpy(new_ref, slice_buffer.ref_counter, ref_counter_struct_size);
libc::memcpy(new_ref.offset(ref_counter_struct_size as isize), new_buffer_data.as_ptr() as *const c_void, new_buffer_length);
libc::free(slice_buffer.ref_counter);
new_ref
};
slice_buffer.ref_counter = new_ref;
slice_buffer.length = new_buffer_length;
slice_buffer.data = new_ref.offset(ref_counter_struct_size as isize) as *mut u8;
// update the grpc byte buffer
*(**grpc_byte_buffer).slice_buffer = slice_buffer;
debug!("unary_call {}", uri_str);
call_original!()
}
);
pub fn init() {
if let Some(signature) = sig::find_signature(
&common::CLIENT_MODULE,
"A8 03 1F F8 ?? ?? 00 94 ?? ?? ?? 91 ?? ?? ?? A9", -0x48,
"0A 90 00 F0 3F F9", -0x37
) {
dobby_hook!(signature as *mut c_void, unary_call);
common::attach_jni_env(|env| {
NATIVE_LIB_ON_UNARY_CALL_METHOD.set(
env.get_method_id(
env.get_object_class(common::native_lib_instance()).unwrap(),
"onNativeUnaryCall",
"(Ljava/lang/String;[B)Lme/eternal/purrfectsnap/nativelib/NativeRequestData;"
).expect("Failed to get onNativeUnaryCall method id")
).expect("unary call method already set");
});
} else {
error!("Can't find unaryCall signature");
}
}