[TOC]
- 越狱手机,安装Cydia,用于获取越狱工具
- OpenSSH(Cydia)——ssh连接越狱手机
- Cycript(Cydia)——找出应用Documents的路径
- Mac-Apple Configurator 2
- Mac-Command Line Tools
- yololib
- class-dump
- dumpdecrypted
- Xcode
- 苹果开发者证书或企业证书
-
在越狱手机上通过appstore安装需要逆向操作的应用
-
打包Mac电脑,打开Apple Configurator 2,点击菜单栏-账号 登录越狱手机上登录的apple账号
-
点击添加-App,选择刚安装好的应用-添加
-
点击添加后,会将该App重新添加到手机上,由于手机上已经安装了该app,此时会提示是否替换该app,先不点击任何按钮,打开一个Finder,(快捷键shift+command+G),进入 '~/Library/Group Containers/K36BKF7T3D.group.com.apple.configurator/Library/Caches/Assets/TemporaryItems/MobileApps/', 就可以找到需要的资源的.ipa文件,
⚠️ 注意:要拷贝出去,过一会可能会消失哦 -
将ipa后缀修改为zip后缀后解压,进入Payload文件夹,右击.app文件,显示包内容即可显示ipa包内的所有文件
从Appstrore下载的应用是经过苹果加密的ipa包,获取appstore的包之后需要解密之后才可以做自己的修改并签名安装到手机上。解密的主要操作为解密ipa包中的可执行文件(Mach-O),俗称砸壳
-
打开已经越狱的iPhone手机(具体越狱方法这里就不介绍了),进入Cydia,安装OpenSSH、Cycript这两款软件。
-
打开Mac电脑终端,连接iPhone,用ssh进入连上的iPhone(确保iPhone和Mac在同一个网段)
-
获取砸壳应用的Bundle id:输入命令 ps -e,记录下此时的bundle路径
-
用Cycript找出微信的Documents的路径,输入命令cycript -p WeChat,记录下此时的Document路径
-
编译dumpdecrypted,用dumpdecrypted来为微信二进制文件(WeChat)砸壳,确保我们从Github上下载了最新的dumpdecrypted源码,打开Mac电脑终端,进入dumpdecrypted源码的目录,编译dumpdecrypted.dylib,命令如下:
这样我们可以看到dumpdecrypted目录下生成了一个dumpdecrypted.dylib的文件。
-
scp拷贝dumpdecrypted.dylib到iPhone上,目标路径为第4步中获取到的Document路径,这里我们用到scp命令。
scp 源文件路径 目标文件路径 。具体如下:
-
开始砸壳,dumpdecrypted.dylib的具体用法是:DYLD_INSERT_LIBRARIES=/PathFrom/dumpdecrypted.dylib /PathTo
这样就代表砸壳成功了,当前目录下会生成砸壳后的文件,即WeChat.decrypted。在Mac终端上,同样用scp命令把WeChat.decrypted文件拷贝到电脑上,接下来我们要正式的dump微信的可执行文件了。
-
验证是否砸壳成功,cryptid=0即为砸壳成功
$ otool -l 应用名.decrypted | grep crypt
-
当应用内存在其它Plugins时,需要一一对Plugin中的可执行文件进行砸壳,按照以上步骤重复操作,此处不再赘述。
创建hook framework
获取yololib最新源码并编译,将Product中生成的yololib可执行文件复制到 /usr/local/bin路径下
加入以下脚本代码
# Type a script or drag a script file from your workspace to insert its path.
# ${SRCROOT} 为工程文件所在的目录
TEMP_PATH="${SRCROOT}/Temp"
#资源文件夹,放三方APP的
ASSETS_PATH="${SRCROOT}/APP"
#ipa包路径
TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
#新建Temp文件夹
rm -rf "$TEMP_PATH"
mkdir -p "$TEMP_PATH"
# --------------------------------------
# 1. 解压IPA 到Temp下
unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
# 拿到解压的临时APP的路径
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
# 这里显示打印一下 TEMP_APP_PATH变量
echo "TEMP_APP_PATH: $TEMP_APP_PATH"
# -------------------------------------
# 2. 把解压出来的.app拷贝进去
#BUILT_PRODUCTS_DIR 工程生成的APP包路径
#TARGET_NAME target名称
TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
echo "TARGET_APP_PATH: $TARGET_APP_PATH"
rm -rf "$TARGET_APP_PATH"
mkdir -p "$TARGET_APP_PATH"
cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH/"
# -------------------------------------
# 3. 为了是重签过程简化,移走extension和watchAPP. 此外个人免费的证书没办法签extension
echo "Removing AppExtensions"
#rm -rf "$TARGET_APP_PATH/PlugIns" //付费开发者证书时可保留extension
rm -rf "$TARGET_APP_PATH/Watch"
# -------------------------------------
# 4. 更新 Info.plist 里的BundleId
# 设置 "Set :KEY Value" "目标文件路径.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
# 5.给可执行文件上权限
#添加ipa二进制的执行权限,否则xcode会告知无法运行
#这个操作是要找到第三方app包里的可执行文件名称,因为info.plist的 'Executable file' key对应的是可执行文件的名称
#我们grep 一下,然后取最后一行, 然后以cut 命令分割,取出想要的关键信息。存到APP_BINARY变量里
APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
#这个为二进制文件加上可执行权限 +X
chmod +x "$TARGET_APP_PATH/$APP_BINARY"
#动态注入的framework,需修改为对应注入framework名称
INJECT_FRAMEWORK_PATH="Frameworks/WeHook.framework/WeHook"
yololib "$TARGET_APP_PATH/$APP_BINARY" "$INJECT_FRAMEWORK_PATH"
echo "inject success"
# -------------------------------------
# 6. 重签第三方app Frameworks下已存在的动态库
TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
then
#遍历出所有动态库的路径
for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
do
echo "🍺🍺🍺🍺🍺🍺FRAMEWORK : $FRAMEWORK"
#签名
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
done
fi
# 7. 重签第三方app下已存在的plugins
TARGET_APP_PLUGINS_PATH="$TARGET_APP_PATH/PlugIns"
if [ -d "$TARGET_APP_PLUGINS_PATH" ];
then
#遍历出所有plugins的路径
for PLUGINS in "$TARGET_APP_PLUGINS_PATH/"*
do
echo "🍺🍺🍺🍺🍺🍺PLUGINS : $PLUGINS"
# 1.给可执行文件上权限
#添加ipa二进制的执行权限,否则xcode会告知无法运行
#这个操作是要找到第三方app包里的可执行文件名称,因为info.plist的 'Executable file' key对应的是可执行文件的名称
#我们grep 一下,然后取最后一行, 然后以cut 命令分割,取出想要的关键信息。存到APP_BINARY变量里
APP_BINARY=`plutil -convert xml1 -o - $PLUGINS/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
# 这个为二进制文件加上可执行权限 +X
chmod +x "$PLUGINS/$APP_BINARY"
# -------------------------------------
# 2. 更新 Info.plist 里的BundleId
# 设置 "Set :KEY Value" "目标文件路径.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER.$APP_BINARY" "$PLUGINS/Info.plist"
# 3.签名
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$PLUGINS"
done
fi
从Github上下载最新的class-dump源代码,然后用Xcode编译即可生成class-dump(这里比较简单,笔者就不详细说明了)。
使用class-dump命令,把刚刚砸壳后的WeChat.decrypted,导出其中的头文件。./class-dump -s -S -H ./WeChat.decrypted -o ./WeChatHeader
根据dump出来的头文件分析需要hook的功能,编写hook代码,示例如下:
-
修改某ViewController中的某方法
#import "MoreTableHook.h" #import <objc/runtime.h> @implementation MoreTableHook + (void)load { NSLog(@"hook load"); Class origin_class = NSClassFromString(@"MoreTableViewController"); Class swizzed_class = [self class]; SEL origin_selector = NSSelectorFromString(@"numberOfSectionsInTableView:"); SEL swizzed_selector = NSSelectorFromString(@"new_numberOfSectionsInTableView:"); Method origin_method = class_getInstanceMethod(origin_class, origin_selector); Method swizzed_method = class_getInstanceMethod(swizzed_class, swizzed_selector); BOOL add_method = class_addMethod(origin_class, swizzed_selector, method_getImplementation(swizzed_method), method_getTypeEncoding(swizzed_method)); if (!add_method) { return; } swizzed_method = class_getInstanceMethod(origin_class, swizzed_selector); if (!swizzed_method) { return; } BOOL did_add_method = class_addMethod(origin_class, origin_selector, method_getImplementation(swizzed_method), method_getTypeEncoding(swizzed_method)); if (did_add_method) { class_replaceMethod(origin_class, swizzed_selector, method_getImplementation(origin_method), method_getTypeEncoding(origin_method)); }else{ method_exchangeImplementations(origin_method, swizzed_method); } } - (long long)numberOfSectionsInTableView:(id)arg1; { [self numberOfSectionsInTableView:arg1]; return 1; } @end
-
修改某系统类的某方法——创建category类hook原Method
#import "UILabel+HookSetText.h" #import <objc/runtime.h> @implementation UILabel (HookSetText) + (void)load { Class clazz = [self class]; SEL setTextSelector = @selector(setText:); SEL new_setTextSelector = @selector(new_setText:); Method setTextMethod = class_getInstanceMethod(clazz, setTextSelector); Method new_setTextMethod = class_getInstanceMethod(clazz, new_setTextSelector); BOOL did_add_method = class_addMethod(clazz, setTextSelector, method_getImplementation(new_setTextMethod), method_getTypeEncoding(new_setTextMethod)); if (did_add_method) { class_replaceMethod(clazz, new_setTextSelector, method_getImplementation(setTextMethod), method_getTypeEncoding(setTextMethod)); }else{ method_exchangeImplementations(setTextMethod, new_setTextMethod); } } - (void)new_setText:(NSString *)text { if ([text isEqualToString:@"微信"]) { [self new_setText:@"new_微信"]; }else { [self new_setText:text]; } } @end
添加编译脚本实现自动注入Framework及重签名中的脚本文件已经支持XCode编译完成重签名(包括对Framework、plugin的处理)
使用本地证书对ipa进行重签名
未完待续。。。