Frida 在设计之初主要是用于应用的逆向工程,使用方法通常是在越狱环境下动态注入,在非越狱环境下使用 FridaGadget 时,因为其写死了 LC_ID_DYLIB
的 Install Name
为 @rpath/Framework/FridaGadget.dylib
,致使直接使用时必须在工程中建立一个名为 Framework
的实体目录。对于正向开发而言,须要对这类动态库进行环境控制,即仅在 Debug 模式下引入,通常方法为将动态库打成 Pod,而后基于 CocoaPods 的语法去作环境控制。ios
对于常规的 Cocoa Touch Dynamic Framework,其 Install Name 通常为 @rpath/SomeLib.framework/SomeLib
的形式,显然 FridaGadget 的 Install Name 与其不符,为了将 FridaGadget 打成 Pod,咱们不得不将其封装成 Framework,以享受 CocoaPods 的自动签名功能,这就要求修改 FridaGadget 的 Install Name 为正确格式,并将其包装成标准的 Framework 格式。git
其次,FridaGadget 为了保证对启动流程的控制,默认阻塞了进程,这会致使应用必须在 Frida Console Attach 后才会继续运行,这很适用于逆向工程,但对于正向开发者和测试者十分不利,更为麻烦的是,FridaGadget.config 彷佛在非越狱环境下不生效,从而致使咱们没法修改 FridaGadget 的默认行为,这个问题彷佛只有修改源码并从新编译才能解决。github
对于包装成 Framework 的问题,其实没必要从新编译 FridaGadget,只须要用 MachOView 打开动态库文件,找到每一个架构的 LC_ID_DYLIB
并修改 Name
字段: macos
这里的注意点是,Name 的存储方法比较巧妙,是将其直接存储在 LC_ID_DYLIB
这一条 Load Command 的尾部,经过第三个字段 String Offset 指定基于当前 Load Command 的偏移量,这一条指令的总长度为 72,字符串从第 24 个字节开始存储,所以字符串的总长度不能超过 47 (须要额外一位来存储\0
),MachOView 提供了直接修改字符串值的方法,双击 Name 的 Data 行便可修改,图中所示的就是修改后的值,字符串和二进制互转可使用这个网站。npm
一个 Framework 须要包含 dylib、Info.plist 和 modulemap 等信息,不然 Xcode 在编译时会报错(特别是缺乏 Info.plist),这里有个取巧的方案是建立一个名为 FridaGadget 的 Cocoa Touch Framework 工程,Build 后将二进制替换为真正的 FridaGadget,即完成了 FridaGadget.framework 的包装。ruby
这里注意一个签名问题,FridaGadget.dylib须要先用本身的开发者帐号从新签名一次,不然bash
若是直接使用动态库,须要在 Xcode 中手动配置一个 Copy File 的 Build Phase 来实现对动态库的签名和拷贝,为了将这个过程自动化,咱们能够将 FridaGadget.framework 打成 Pod 库,一方面 Pod 会为咱们自动生成工程配置,另外一方面也能够基于 Podfile 的 configurations
实现环境控制。架构
CocoaPods 包含动态库的方式很简单,只须要声明一个 vendored_frameworks
:测试
s.vendored_frameworks = "FridaGadget.framework"
复制代码
对于声明了 vendored_frameworks
的 Pod 库,CocoaPods 会自动为其建立 Pods-ProjectName-frameworks.sh 的脚本,并添加一个 Embed Pods Framework 的 Build Phase 来完成动态库的签名,这里有个坑咱们须要先手动重签名依次 FridaGadget.dylib,不然可能会致使 Pods 脚本中签名失败,重签名的方法可参考 Frida Doc。网站
随后,将 Pod 库部署到企业私有环境或是公库下,便可经过 Podfile 直接引入 FridaGadget:
# 注意我尚未将其上传到公库,所以你没法直接这么引入
pod 'FridaGadget', :configurations => ['Debug']
复制代码
这种方式完美解决了 FridaGadget 的环境控制问题,可是启动时自动阻塞进程不是咱们想要的(毕竟是正向开发环境,只是偶尔使用 Frida 来调试和自动化测试),这个问题在于 Frida 默认采用了阻塞方式,要修改能够经过在工程中建立一个 FridaGadget.config 来修改 on_load 策略,但非越狱环境 FridaGadget 下彷佛读不到 Main Bundle 中的 config 文件,所以咱们只剩下一条路:修改源码。
首先下载 Frida 源码,具体方式请参考 Build Frida,先完成 clone 便可,随后咱们须要定位到 on_load
相关逻辑的代码,全工程搜索 on_load
首先会找到一堆看起来不像是人写的 C 代码,这些代码是由 Vala Language 自动生成的,所以核心代码都在 Vala 里,随后咱们找到 gadget.vala,从中搜索 on_load
可找到默认等待的逻辑,将 WAIT 修改成 RESUME 便可:
public LoadBehavior on_load {
get;
set;
default = LoadBehavior.RESUME;
}
public enum LoadBehavior {
RESUME,
WAIT
}
复制代码
很显然,将 default 改成 LoadBehavior.RESUME
便可。
既然要从新编译,咱们能够直接指定 Install Name 为 Framework 须要的形式,这个配置位于 Makefile.macos.mk
的第 36四、371 行,直接修改便可:
build/.core-macos-stamp-%: build/%/lib/pkgconfig/frida-core-1.0.pc
@if [ -z "$$MAC_CERTID" ]; then echo "MAC_CERTID not set, see https://github.com/frida/frida#macos-and-ios"; exit 1; fi
. build/frida-meson-env-macos-$(build_arch).rc \
&& $$CODESIGN -f -s "$$MAC_CERTID" -i "re.frida.Server" build/$*/bin/frida-server \
## Install Name
&& $$INSTALL_NAME_TOOL -id @rpath/../FridaGadget.framework/FridaGadget build/$*/lib/frida-gadget.dylib \
&& $$CODESIGN -f -s "$$MAC_CERTID" build/$*/lib/frida-gadget.dylib
@touch $@
build/.core-ios-stamp-%: build/%/lib/pkgconfig/frida-core-1.0.pc
@if [ -z "$$IOS_CERTID" ]; then echo "IOS_CERTID not set, see https://github.com/frida/frida#macos-and-ios"; exit 1; fi
. build/frida-meson-env-macos-$(build_arch).rc \
&& $$CODESIGN -f -s "$$IOS_CERTID" --entitlements frida-core/server/frida-server.xcent build/$*/bin/frida-server \
## Install Name
&& $$INSTALL_NAME_TOOL -id @rpath/FridaGadget.framework/FridaGadget build/$*/lib/frida-gadget.dylib \
&& $$CODESIGN -f -s "$$IOS_CERTID" build/$*/lib/frida-gadget.dylib
@touch $@
复制代码
Frida 的编译相对比较简单,参考 官方文档 便可,这里说几个编译过程当中遇到的坑。
make core-ios
再执行 make gadget-ios
;随后再按照相同的步骤,将 FridaGadget 封装成 Framework,再打包成 Pod 库,再结合环境控制,便可完美的将 FridaGadget 用于正向开发过程当中,环境控制也强有力的保证了 FridaGadget 不会被打包到正式环境。