代碼一般是編譯成動(dòng)態(tài)庫或者靜態(tài)庫注入到應(yīng)用程序中的
1. 動(dòng)態(tài)庫
庫是一種共享程序代碼的方式,從本質(zhì)上來說是就是一種可執(zhí)行代碼的二進(jìn)制形式。在正向開發(fā)中常常將程序的所用的部分功能編譯成庫的形式,只保留頭文件供開發(fā)者調(diào)用。庫分為動(dòng)態(tài)庫和靜態(tài)庫。
靜態(tài)庫:
- 靜態(tài)庫在編譯鏈接階段會(huì)被加入可執(zhí)行文件中
- 靜態(tài)庫的存在形式有.a和.framework
- 靜態(tài)庫由一個(gè)或多個(gè)object文件組成,可以將一個(gè)靜態(tài)庫拆解成多個(gè)object文件
- 靜態(tài)庫鏈接時(shí)會(huì)直接鏈接到目標(biāo)文件,并作為它的一部分存在
- .a 是一個(gè)純二進(jìn)制文件,.framework 中除了有二進(jìn)制文件之外還有資源文件, .a 要有 .h 文件以及資源文件配合, .framework 文件可以直接使用??偟膩碚f,.a + .h + sourceFile = .framework。所以創(chuàng)建靜態(tài)庫最好還是用.framework的形式
動(dòng)態(tài)庫:
- 動(dòng)態(tài)庫在可執(zhí)行文件裝載或運(yùn)行時(shí)由操作系統(tǒng)加載,iOS中由lldb加載
- 動(dòng)態(tài)庫的存在形式有.dylib、.framework以及鏈接符號(hào).tdb
- 動(dòng)態(tài)庫的格式和普通的二進(jìn)制文件沒有區(qū)別
- 動(dòng)態(tài)庫的好處是可以只保留一份文件和內(nèi)存空間,從而能夠被多個(gè)進(jìn)程使用,例如系統(tǒng)的動(dòng)態(tài)庫
- 在修改動(dòng)態(tài)庫的功能時(shí),只需要替換動(dòng)態(tài)庫,不需要修改依賴的主文件
- 可以減小可執(zhí)行文件的體積,不需要鏈接到目標(biāo)文件
蘋果系統(tǒng)專屬的framework 是共享的(如UIKit), 但是我們自己使用 Cocoa Touch Framework 制作的動(dòng)態(tài)庫是放到 app bundle 中,運(yùn)行在沙盒中的
2. framework注入
以微信注入為例
2.1 先看下微信的Mach-O文件

微信的太長(zhǎng)了,不截全了
2.2 動(dòng)態(tài)庫注入的本質(zhì)
本質(zhì)就是往Load Commands中添加了一個(gè)LC_LOAD_DYLIB或LC_LOAD_WEAK_DYLIB,其實(shí)只是添加了一個(gè)路徑而已,framework還需要我們拷貝到WeChat的Frameworks里面。這里的往微信包里面插入framework,破壞了二進(jìn)制文件的簽名,需要重新進(jìn)行簽名。
- 這里我們可以直接往微信包里面拷貝打包好的framework,也可以利用Xcode自動(dòng)自動(dòng)幫我們注入(
編譯->運(yùn)行重簽名腳本,替換了我們當(dāng)前工程的.app包) - 添加LC_LOAD_DYLIB(framework)的路徑可以利用工具,yololib工具,需要替換成自己的framework路徑文件
yololib WeChat Frameworks/GGHook.framework/GGHook
其中GGHook為GGHook.framework中的可執(zhí)行文件

再用MachOView看下,

剩下的就是重簽名微信的Frameworks,再給微信Mach-O文件重簽名,打包就可以安裝了。
上面的步驟可以利用腳本來做
# ${SRCROOT} 它是工程文件所在的目錄
TEMP_PATH="${SRCROOT}/Temp"
#資源文件夾,我們提前在工程目錄下新建一個(gè)APP文件夾,里面放ipa包
ASSETS_PATH="${SRCROOT}/APP"
#目標(biāo)ipa包路徑
TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
#清空Temp文件夾
rm -rf "${SRCROOT}/Temp"
mkdir -p "${SRCROOT}/Temp"
#----------------------------------------
# 1. 解壓IPA到Temp下
unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
# 拿到解壓的臨時(shí)的APP的路徑
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
# echo "路徑是:$TEMP_APP_PATH"
#----------------------------------------
# 2. 將解壓出來的.app拷貝進(jìn)入工程下
# BUILT_PRODUCTS_DIR 工程生成的APP包的路徑
# TARGET_NAME target名稱
TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
echo "app路徑:$TARGET_APP_PATH"
rm -rf "$TARGET_APP_PATH"
mkdir -p "$TARGET_APP_PATH"
cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"
#----------------------------------------
# 3. 刪除extension和WatchAPP.個(gè)人證書沒法簽名Extention
rm -rf "$TARGET_APP_PATH/PlugIns"
rm -rf "$TARGET_APP_PATH/Watch"
#----------------------------------------
# 4. 更新info.plist文件 CFBundleIdentifier
# 設(shè)置:"Set : KEY Value" "目標(biāo)文件路徑"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
#----------------------------------------
# 5. 給MachO文件上執(zhí)行權(quán)限
# 拿到MachO文件的路徑
APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
#上可執(zhí)行權(quán)限
chmod +x "$TARGET_APP_PATH/$APP_BINARY"
#----------------------------------------
# 6. 重簽名第三方 FrameWorks
TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
then
for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
do
#簽名
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
done
fi
#注入
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/GGHook.framework/GGHook"
新建個(gè)工程,工程目錄下創(chuàng)建個(gè)APP文件夾,把微信ipa丟進(jìn)去,然后新建個(gè)Framework,


3. dylib注入
insert_dylib工具
insert_dylib --weak --all-yes Frameworks/GGHook.framework/GGHook WeChat
--weak,即使動(dòng)態(tài)庫找不到也不會(huì)報(bào)錯(cuò)
--all-yes,后面所有的選擇都為yes
3.1 先創(chuàng)建個(gè)Dylib

3.2 把這個(gè)Base SDK改成iOS

3.3 把Mac Developer改成iOS Developer

3.4 Dylib需要添加依賴文件




還是用上面的腳本,最后的注入代碼改成這個(gè)
yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libGGHook.dylib"

4. 總結(jié)
Frameworks注入跟Dylib注入道理都是一樣的,都是需要framework和dylib拷貝到Frameworks文件里面,然后給可執(zhí)行文件Mach-O添加加載路徑,剩下的都是重簽名運(yùn)行。