Skip to content
微生 edited this page Oct 27, 2024 · 2 revisions

Welcome to the rnidbg wiki!

创建一个VM

const PID: u32 = 2667; // 系统调用getpid()的返回值
const PPID: u32 = 2427; // getppid()

let emulator = AndroidEmulator::create_arm64(PID, PPID, "com.tencent.mobileqq:MSF", data);
let memory = emulator.memory();

设置一个system prop handler

let mut libc = Box::new(Libc::new());
libc.set_system_property_service(Rc::new(Box::new(move |name| match name {
    "ro.build.version.sdk" => Some(device_info.build_version.clone()),
    "persist.sys.timezone" => Some("Asia/Shanghai".to_string()),
    "gsm.version.baseband" => Some("MOLY.LR12A.R3.MP.V79.P10".into()),
     _ => {
        if option_env!("FUQIULUO_DEBUG") == Some("1") {
            info!("unresolved property: {}", name)
        }
        panic!("name={}", name)
    }
})));
memory.add_hook_listeners(libc);

目前的libc只能监听并劫持部分调用,大部分调用使用从安卓环境提取的so文件转译执行实现,后续将移除该so文件!

初始化文件系统

let system_base_path = emulator.get_base_path().to_string();
let file_system = emulator.get_file_system();

file_system.set_file_resolver(Box::new(move |_, path, flags, mode| {
    if path == "/data/user/0/com.tencent.mobileqq/files/5463306EE50FE3AA/6FAcBa17D93747A5" {
        if flags == OFlag::O_RDONLY {
            if let Ok(file) = File::open(format!("{}/6FAcBa17D93747A5", base_path)) {
                return Some(FileIO::File(LinuxFileIO::new_with_file(
                    file,
                    path,
                    flags.bits(),
                    12345,
                    StMode::APP_FILE
                ))); // 返回真实环境的文件
            }
            return None;
        } else if flags == (OFlag::O_WRONLY | OFlag::O_CREAT | OFlag::O_TRUNC) {
            let file = File::create(format!("{}/6FAcBa17D93747A5", base_path)).unwrap();
            return Some(FileIO::File(LinuxFileIO::new_with_file(
                file,
                path,
                flags.bits(),
                12345,
                StMode::APP_FILE
            ))); // 根据flags要求,创建文件
        } else {
            if option_env!("FUQIULUO_DEBUG") == Some("1") {
                info!("unresolved file_resolver: path={}, flags={:?}, mode={}", path, flags, mode);
            }
            panic!("unsupported flags: {:?}", flags);
        }
    }
    if path == "/sdcard/Android/.android_lq" {
        return None // 文件不存在返回None
    }
      
    if path == "/sdcard/Android/" {
        return Some(FileIO::Direction(Direction::new(VecDeque::new(), path))) // 返回空文件夹
    }

    if path == "/proc/self/cmdline" || path == format!("/proc/{}/cmdline", PID) {
        return Some(FileIO::Bytes(ByteArrayFileIO::new(b"com.tencent.mobileqq:MSF\0".to_vec(), path.to_string(), 12345, flags.bits(), StMode::APP_FILE))) // 内存数据假装一个文件
    }

    if path == "/data/app/~~vbcRLwPxS0GyVfqT-nCYrQ==/com.tencent.mobileqq-xJKJPVp9lorkCgR_w5zhyA==/lib/arm64" {
        let mut files = VecDeque::new();
        files.push_back(DirectionEntry::new(true, "libfekit.so"));
        files.push_back(DirectionEntry::new(true, "libwtecdh.so"));
        files.push_back(DirectionEntry::new(true, "libmmkv.so"));
        files.push_back(DirectionEntry::new(true, "libQSec.so"));
        files.push_back(DirectionEntry::new(true, "libqimei.so"));
        return Some(FileIO::Direction(Direction::new(files, path)))
    } // 返回带有子文件和子目录的文件夹

    if option_env!("FUQIULUO_DEBUG") == Some("1") {
        info!("unresolved file_resolver: path={}, flags={:?}, mode={}", path, flags, mode);
        panic!("unresolved file_resolver: path={}, flags={:?}, mode={}", path, flags, mode);
    } else {
        return None
    }
}));

初始化类白名单

let vm = emulator.get_dalvik_vm();
vm.set_class_resolver(ClassResolver::new(vec![
    "com/tencent/mobileqq/qsec/qsecprotocol/ByteData",
    "com/tencent/mobileqq/qsec/qsecdandelionsdk/Dandelion",
    "com/tencent/mobileqq/qsec/qseccodec/SecCipher",
    "com/tencent/mobileqq/qsec/qsecurity/QSec",
    "com/tencent/mobileqq/sign/QQSecuritySign$SignResult",
    "com/tencent/mobileqq/sign/QQSecuritySign",
    "com/tencent/mobileqq/channel/ChannelManager",
]));

初始化安卓JNI调用处理器

pub struct MyJni {
}

impl<T: Clone> Jni<T> for MyJni {
    fn resolve_method(
        &self,
        vm: &mut DalvikVM64<T>,
        class: &Rc<DvmClass>,
        name: &str,
        signature: &str,
        is_static: bool,
    ) -> bool {
        if class.name == "java/lang/String" {
            if name == "<init>" && signature == "([BLjava/lang/String;)V" && !is_static {
                return true;
            }
            if name == "hashCode" && signature == "()I" && !is_static {
                return true;
            }
        }
        return true // 判断方法是否存在,不存在将会抛出异常给模拟调用中的二进制文件
    }

    fn resolve_filed(
        &self,
        vm: &mut DalvikVM64<T>,
        class: &Rc<DvmClass>,
        name: &str,
        signature: &str,
        is_static: bool,
    ) -> bool {
         if class.name == "android/os/Build$VERSION"
            && name == "SDK_INT"
            && signature == "I"
            && is_static
        {
            return true;
        }
        return true
    }

    fn call_method_v(
        &self,
        vm: &mut DalvikVM64<T>,
        acc: MethodAcc,
        class: &Rc<DvmClass>,
        method: &DvmMethod,
        instance: Option<&mut DvmObject>,
        args: &mut VaList<T>,
    ) -> JniValue {
        if acc.contains(MethodAcc::CONSTRUCTOR) {
            if class.name == "java/lang/String" {
                if method.name == "<init>" && method.signature == "([BLjava/lang/String;)V" {
                    let result = String::from_utf8(args.get::<Vec<u8>>()).unwrap();
                    let charset = args.get::<String>();
                    return DvmObject::String(result).into();
                }
            }
        }

        panic!("?")
    }
    
     fn get_field_value(
        &self,
        vm: &mut DalvikVM64<T>,
        class: &Rc<DvmClass>,
        field: &DvmField,
        instance: Option<&mut DvmObject>,
    ) -> JniValue {
        // do something
    }

    fn set_field_value(
        &self,
        vm: &mut DalvikVM64<T>,
        class: &Rc<DvmClass>,
        field: &DvmField,
        instance: Option<&mut DvmObject>,
        value: JniValue,
    ) {
        // do something
    }
}

let mut jni = Box::new(MyJni::new());
vm.set_jni(jni);

加载二进制文件

 let dm = vm
    .load_library(
        emulator.clone(),
        &format!("{}/libfql.so", base_path),
        true,
    )
    .map_err(|e| eprintln!("failed to load_library: {}", e))
    .unwrap();

构建一个对象

let context = vm
    .resolve_class_unchecked("android/content/Context")
    .1
    .new_simple_instance(vm);

调用非静态方法

 let dtn = vm
    .resolve_class_unchecked("com/baidu/XXX/dt/Dtn")
    .1
    .new_simple_instance(vm);
 dtn.call_method(
    emulator,
    vm,
    "initContext",
    "(Landroid/content/Context;Ljava/lang/String;)V",
    vec![
        JniValue::Object(context.clone()),
        "/data/user/0/com.baidu.map/files/5463306EE50FE3AA".into(),
    ],
);

移除对象引用

 let qq_security_sign = vm
    .resolve_class_unchecked("com/tencent/mobileqq/sign/QQSecuritySign")
    .1
    .new_simple_instance(vm);
if let DvmObject::ObjectRef(ref_id) = qq_security_sign {
    vm.remove_global_ref(ref_id);
}