SpringBoardServices 为例。

方法1

从越狱的ios设备中提取 SpringBoardServices.tbd 文件,也可以在网上找别人提取过的,也可以自己对着 SpringBoardServices 二进制文件符号表手写(。

tbd 全称是 text-based stub libraries 用于记录链接动态库的必要信息,包括动态库导出的符号、架构信息、依赖信息、链接路径等。

放在 PrivateFrameworks/SpringBoardServices.framework 文件夹中。

build.rs 中加入:

1
2
3
fn main() {
    println!("cargo::rustc-link-search=framework=PrivateFrameworks");
}

目录结构:

1
2
3
4
5
6
7
├── PrivateFrameworks
│   └── SpringBoardServices.framework
│       └── SpringBoardServices.tbd
├── src
│   └── main.rs
├── build.rs
├── Cargo.toml

然后就可以正常写 binding 代码了,例如:

1
2
3
4
5
6
7
#[allow(non_snake_case)]
#[link(name = "SpringBoardServices", kind = "framework")]
extern "C" {
    fn SBSCopyDisplayIdentifierForProcessID(p1: i32) -> CFStringRef;
    fn SBSCopyLocalizedApplicationNameForDisplayIdentifier(p1: CFStringRef) -> CFStringRef;
    fn SBSCopyIconImagePNGDataForDisplayIdentifier(p1: CFStringRef) -> CFDataRef;
}

方法2

使用 dlopen、dlsym 动态加载符号

dlopen 以指定模式打开指定的动态链接库文件,并返回一个句柄给 dlsym,dlsym 根据动态链接库操作句柄与符号,返回符号对应的地址

这里偷懒使用 https://github.com/nagisa/rust_libloading 库,它做了一层使用起来比较方便并且安全的包装。

例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
let lib = Library::new(
    "/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices",
)?;

#[allow(non_snake_case)]
let SBSCopyDisplayIdentifierForProcessID: Symbol<extern "C" fn(i32) -> CFStringRef> =
    lib.get(b"SBSCopyDisplayIdentifierForProcessID")?;

#[allow(non_snake_case)]
let SBSCopyLocalizedApplicationNameForDisplayIdentifier: Symbol<
    extern "C" fn(CFStringRef) -> CFStringRef,
> = lib.get(b"SBSCopyLocalizedApplicationNameForDisplayIdentifier")?;

#[allow(non_snake_case)]
let SBSCopyIconImagePNGDataForDisplayIdentifier: Symbol<
    extern "C" fn(CFStringRef) -> CFDataRef,
> = lib.get(b"SBSCopyIconImagePNGDataForDisplayIdentifier")?;

区别

方法1是编译时链接的,方法2是运行时去调用

另外别忘了签名对应权限