iOS应用安全—— IPAPatch免越狱调试、修改第三方App

简介

IPAPatch可以实现免越狱调试、修改第三方APP,其原理主要是基于砸壳的APP进行修改和签名,并安装在真机设备上,类似于重打包。

项目地址:https://github.com/Naituw/IPAPatch

IPAPatch基于Xcode实现,整个过程在 Xcode 中进行,就像在编写自己的 App,此外:

  • 支持链接第三方 Framework
  • 在 Xcode 中可以直接断点进行调试,可以用 lldb 命令(如 po),输出运行时信息
  • 可以使用 Xcode 的调试功能查看 View Hierarchy、Memory Graph 等信息
  • 修改过的 App 可以与原始 App 共存,并自动修改名字以作区分

实现原理

主要的自动化过程在 patch.sh 这个脚本里,Xcode 会在把代码编译成 Framework 后执行这个脚本:

  1. 解压 IPA 文件
  2. 用 IPA 文件的内容,替换掉 Xcode 生成的 .app 的内容
  3. 通过 OPTOOL,将代码生成的 Framework 及其他外部 Framework,注入到二进制文件中
  4. 对这些文件进行重新签名 完成后,Xcode 会自动将修改过的 .app 安装到 iPhone 上

patch.sh的注释中写的很明确:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# ---------------------------------------------------
# 0. Prepare Working Enviroment

rm -rf "$TEMP_PATH" || true
mkdir -p "$TEMP_PATH" || true

DUMMY_DISPLAY_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleDisplayName" "${SRCROOT}/$TARGET_NAME/Info.plist")
echo "DUMMY_DISPLAY_NAME: $DUMMY_DISPLAY_NAME"

TARGET_BUNDLE_ID="$PRODUCT_BUNDLE_IDENTIFIER"
echo "TARGET_BUNDLE_ID: $TARGET_BUNDLE_ID"


# ---------------------------------------------------
# 1. Extract Target IPA

unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app; echo "$1")
echo "TEMP_APP_PATH: $TEMP_APP_PATH"




# ---------------------------------------------------
# 2. Overwrite DummyApp IPA with Target App Contents

TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
echo "TARGET_APP_PATH: $TARGET_APP_PATH"

rm -rf "$TARGET_APP_PATH" || true
mkdir -p "$TARGET_APP_PATH" || true
cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH/"




# ---------------------------------------------------
# 3. Inject the Executable We Wrote and Built (IPAPatch.framework)

APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
OPTOOL="${SRCROOT}/Tools/optool"

mkdir "$TARGET_APP_PATH/Dylibs"
cp "$BUILT_PRODUCTS_DIR/IPAPatch.framework/IPAPatch" "$TARGET_APP_PATH/Dylibs/IPAPatch"
for file in `ls -1 $TARGET_APP_PATH/Dylibs`; do
echo -n ' '
echo "Install Load: $file -> @executable_path/Dylibs/$file"
$OPTOOL install -c load -p "@executable_path/Dylibs/$file" -t $TARGET_APP_PATH/$APP_BINARY >& /dev/null
done

chmod +x "$TARGET_APP_PATH/$APP_BINARY"




# ---------------------------------------------------
# 4. Inject External Frameworks if Exists

TARGET_APP_FRAMEWORKS_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app/Frameworks"

echo "Injecting Frameworks from $FRAMEWORKS_TO_INJECT_PATH"
for file in `ls -1 ${FRAMEWORKS_TO_INJECT_PATH}`; do
extension="${file##*.}"
echo "$file 's extension is $extension"

if [ "$extension" != "framework" ]
then
continue
fi

filename="${file%.*}"

cp "$FRAMEWORKS_TO_INJECT_PATH/$file/$filename" "$TARGET_APP_PATH/Dylibs/$filename"

echo -n ' '
echo "Install Load: $file -> @executable_path/Dylibs/$filename"
$OPTOOL install -c load -p "@executable_path/Dylibs/$filename" -t $TARGET_APP_PATH/$APP_BINARY >& /dev/null
done




# ---------------------------------------------------
# 5. Remove Plugins (AppExtensions), To Simplify the Signing Process

rm -rf "$TARGET_APP_PATH/PlugIns" || true




# ---------------------------------------------------
# 7. Update Info.plist for Target App

TARGET_DISPLAY_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleDisplayName" "$TARGET_APP_PATH/Info.plist")
TARGET_DISPLAY_NAME="$DUMMY_DISPLAY_NAME$TARGET_DISPLAY_NAME"

/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleDisplayName $TARGET_DISPLAY_NAME" "$TARGET_APP_PATH/Info.plist"



# ---------------------------------------------------
# 8. Code Sign All The Things

for DYLIB in "$TARGET_APP_PATH/Dylibs/"*
do
FILENAME=$(basename $DYLIB)
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$DYLIB"
done

if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ]; then
for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
do
FILENAME=$(basename $FRAMEWORK)
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
done
fi

/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp=none "$TARGET_APP_PATH/$APP_BINARY"




# ---------------------------------------------------
# 9. Install
#
# Nothing To Do, Xcode Will Automatically Install the DummyApp We Overwrited

使用方法

  1. Clone或下载IPAPatch项目到本地

  2. 准备砸过壳的IPA文件
    可以在 http://www.iphonecake.com 上找
    The IPA file you use need to be decrypted, you can get

  3. 替换项目中的IPA
    将IPAPatch/Assets/app.ipa替换为自己的ipa,名字必须为app.ipa
  4. 准备External Frameworks (非必须)
    目录在IPAPatch/Assets/Frameworks. Frameworks会自动链接
    例如:IPAPatch/Assets/Frameworks/RevealServer.framework
    下图是通过 Reveal,拿到需要集成的 Framework 文件
    upload successful
    将 RevealServer.framework 放置在 Assets/Frameworks/RevealServer.framework
    upload successful
  5. 配置Build Settings
    使用xcode打开IPAPatch.xcodeproj,在Project Editor中选择IPAPatch-DummyApp,默认名称为”💊”, 可以自由修改。
    修改Bundle Identifier,并配置codesing
    upload successful
  6. 编译运行
    编译后点击手机上的图标运行app,会提示证书问题,去设置-通用-设备管理中信任证书即可。