用expect脚本实现Xcode对越狱设备的动态库注入

1,381 阅读6分钟

ssh远程控制

如果我们想远程登录或者控制一台机器,可以在被操控的设备上安装ssh服务。无论是本地设备使用命令行还是可视化工具都需要预先登录到远程设备中,登录过程需要输入用户名和密码。我们还可以使用scp命令来实现本地设备和远程设备之间的文件拷贝。很多操作系统比如Linux、macOS都内置了ssh服务和客户端相关的功能。要想具体了解ssh可以参考下面几个非常有用的链接:

因为iOS系统并没有内置ssh服务,所以对于一台越狱设备来说要想被远程控制则需要安装这个服务。安装的方法就是从cydia这个应用中搜索openssh,然后安装即可。你也可以参考下面两篇教程:

当我们通过各种方法开发一些工具程序时,往往都需要将这些程序复制到越狱设备中去。从工具的开发到打包一般都会借助脚本来实现,而最后的远程文件传输部分因为需要登录所以需要手动输入用户和密码而无法进行自动化处理,即使是在脚本中使用输入重定向也是无用。这个时候我们可以使用expect来解决这个问题。

expect 脚本

expect是一个免费的编程工具语言,用来实现自动和交互式任务进行通信,而无需人的干预。expect需要Tcl编程语言的支持,要在系统上运行expect必须首先安装Tcl。

一个好消息就是macOS系统内置了expect。

你可以从利用expect命令实现Shell自动化交互的方法详解这篇中获取详细的expect脚本的使用方法。

聪明的你是否想到了什么?

我们先暂停一下关于shell和expect相关的东西。

越狱设备的动态库注入

熟悉越狱开发的同学都知道只要动态库放到越狱设备的/Library/MobileSubstrate/DynamicLibraries/目录中并配备一个同名的plist文件就可以实现任意应用程序启动时加载这些动态库的能力。其原理在Cydia的基石:MobileSubstrate有说明。

你可以借助一些第三方工具来创建一个Tweak程序,也可以在Xcode直接创建动态库。方法如下:

  1. 选择菜单:File -> New -> Project... -> iOS -> Cocoa Touch Static Library

  2. 系统默认创建的是静态库。创建成功后进入项目的Build Settings中,搜索"Mach-O Type"并将这个选项中的文件类型改为:Dynamic Library。然后再搜索"Executable Extension"并将这个选项中的内容改为dylib即可。

  3. 建立并添加一个和动态库同名的plist文件(File->new->File...->Property List)。编辑这个plist文件,这个plist文件用于过滤加载动态库的应用程序列表,里面具体的格式和内容定义请参考Tweak相关的教程。

  4. 构建出动态库的真机运行版本。

  5. 将生成的动态库文件和对应的plist文件一同拷贝到越狱设备的**/Library/MobileSubstrate/DynamicLibraries/**目录下,然后运行相关的应用。

上面第5步的拷贝操作,可以使用scp命令完成,这需要登录到越狱设备上。也可以通过一些可视化界面工具来完成拷贝。

你是否希望第5步的操作通过脚本来自动完成,而无需人工的干预呢?为了解决这个问题你是否又想到了借助expect脚本来完成上述的功能!这就是下面要介绍的方法。

在Xcode 上引入expect脚本

我们在Xcode中添加expect脚本来实现无交互的远程文件的拷贝的能力,从而快速的安装和运行Tweak插件。这个脚本不能添加到Build Phases中,因为动态库的签名是最后执行的一步操作,是在Build Phases中的指定的脚本执行后才进行。因此只能将脚本添加到Scheme 中的Post actions中,在这里添加脚本的好处在于可以在构建完成后执行特定的脚本。具体的处理如下:

脚本

  1. 在项目工程中选择Edit Scheme...
  2. 选择Build -> Post-actions。
  3. 添加一个Run Script
  4. 将Shell 改为: /usr/bin/expect
  5. Provide build settings from 中选择你的项目
  6. 在脚本输入框中写入:
#设置几个环境变量,这几个环境变量其实就是用于指定plist文件和动态库文件的路径和名称。
#您的应用中的路径可能和demo中的路径不一致,这里需要保证路径变量的正确设置。
set prjpath $env(PROJECT_DIR)
set prdname $env(PRODUCT_NAME)
set libprefix $env(EXECUTABLE_PREFIX)
set libpath $env(TARGET_BUILD_DIR)
set libname $env(EXECUTABLE_NAME)
#越狱设备的IP地址
set devip "172.19.212.21"  

#拷贝动态库到设备对应的目录下去。这里使用了expect脚本的spawn来执运行远程文件拷贝scp命令
spawn scp $libpath/$libname root@$devip:/Library/MobileSubstrate/DynamicLibraries/
#在用户密码输入处添加expect命令来指定输入的密码,并设定30秒的超时。
expect {
"*assword" {set timeout 30; send "alpine\r";}
"yes/no" {send "yes\r"; exp_continue;}
}
expect eof

#拷贝配置文件到设备对应的目录下去,注意这里的配置文件的路径。
spawn scp $prjpath/$libprefix$prdname.plist root@$devip:/Library/MobileSubstrate/DynamicLibraries/
expect {
"*assword" {set timeout 30; send "alpine\r";}
"yes/no" {send "yes\r"; exp_continue;}
}

expect eof

这段脚本很容易弄懂,就是开始设置环境变量,然后执行2个文件的远程拷贝,并指定特定的远程登录密码。这里要记住的就是要将devip改为你越狱设备上的IP地址,同时要注意plist文件的存放路径。

  1. 将工程的active scheme设置为真机模式:Generic iOS Device
  2. 执行构建操作。因为添加了脚本,所以整个动态库的构建以及拷贝工作都会自动完成,并且整个过程不需要进行任何联机操作。

工程示例

为了更好的演示通过expect脚本直接实现动态库的注入处理,我在github中建立了一个项目:

github.com/youngsoft/Y…

这个项目实现的功能就是简单的通过动态库的注入方法,在相机应用的拍照界面按钮上植入一个笑脸的图标。因为这个动态库只在相机应用启动时才加载所以在对应的plist文件中只添加了相机应用的bundleid。

plist文件过滤加载的应用程序

当你将工程下载后,记得将上面介绍的expect脚本处将IP地址改为某台越狱手机的IP地址,并且确保越狱手机安装了ssh以及MobileSubstrate。然后在编译示例工程后打开相机应用。最终相机运行的效果如下:

866F5C9D151EEE4EA0B915D593995A15.jpg


本教程的越狱开发方法,只用于个人学习之用,不能用于商业目的,出现问题概不负责!!!