前言:
本篇文章建立在上篇博客 :
iOS逆向 - 应用签名原理及重签名 (重签微信应用实战) 的基础知识之上的 , 不了解的同学欢迎去阅读 . 本篇会讲述如何利用 shell
脚本自动重签名和应用调试 .
如果篇幅不过长的话 , 我们再来玩一玩代码注入 .
另外 笔者使用的是 Xcode 11
, 所以 Xcode 新版本目前没发现有问题 . 一样玩 .
利用 Xcode 重签名和调试
这里之所以要用 Xcode
来做一次重签 , 有以下几个目的 :
- 节省自己创建描述文件 / 重签名应用的步骤 .
- 有了工程可以看得到部分打印.
- 方便我们自己添加代码运行 .
- ... 等等
利用 Xcode
重签的原理和上篇博客中基本一样 ( 因为我们也提到过 , Xcode
本身也就是利用 codeSign
来做的 ) , 因此 , 我这边快速过一遍 , 不过多讲述了 .
步骤 1 : 新建同名工程
之所以要用同名工程 , 因为是有一些坑的 .
( 不使用同名工程时 , 每次 build
会重新创建一个 Mach-O
, 也就是说此时你就算更换了包 , 但是这个包里有两个 Mach-O
, 而运行的还会是你自己的空工程 , 所以在我们已经下载到 ipa
看得到包里的 Mach-O
的情况下 , 我们就起和它一样的名字就好 )
ok , 新建工程 , 选择开发者 , 选择真机 , 直接 run
.
run
成功之后查看生产的 app
包
看完前面一篇博客的同学应该都清楚 , 这一步是为了把描述文件安装到手机里 .
步骤 2 : 替换 App 包
找到之前从pp助手里下载好的 已经砸过壳的微信应用包 , 直接替换.
步骤 3 : 删插件
这一步跟我们之前自己纯手动操作一样 .
-
打开
WeChat
, 显示包内容 . 找到PlugIns
文件夹, 直接删除 插件普通账号是签不了的 . -
找到
Watch
文件夹 , 因为这里也有插件 , 我们暂时不需要Watch
, 直接删掉 .
步骤 4 : 重签 FrameWork
进入 FrameWork
文件夹
利用 CodeSign
, 使用我们的证书进行重签名.
codesign -fs "复制的你自己的证书名字" 要重签的FrameWork名称
例如:
codesign -fs "iPhone Developer: ha ha (123456)" andromeda.framework
把 FrameWork
文件夹下所有的库全部重签.
注意 ⚠️:
这个时候 App 包里的Bundle ID 还是微信的 ,但是我们先不改 , 直接运行 .
重签成功
注意看原本我们的空工程 , 在手机上已经安装上了微信的包 ,
那么你再去查看微信包里的 Info.plist
里的 Bundle ID
, 已经被自动替换成我们的包名了 .
那么对比一下我们纯手写的重签 , 这个显然更加简单且实用 . 但是每次重签都这么搞一下也不太合适 , 当我们理解了重签原理之后 , 就没有必要每次这么搞了 .
而且我也不一定能保证我确实是同名工程 , 我希望我随便叫什么名字都可以重签 .
ok. 进入 Shell
部分 .
利用 Shell 脚本自动重签名
Shell 以及 Shell 脚本
为了统一大家前导知识的前提 , 我们还是先来介绍下 SHELL
脚本 , 已经熟悉的同学可以自行跳过 .
-
Shell
是一种特殊的交互式工具,它为用户提供了启动程序、管理文件系统中文件以及运行在系统上的进程的途径。 -
Shell
一般是指命令行工具。它允许你输入文本命令,然后解释命令,并在内核中执行。 -
Shell
脚本,也就是用各类命令预先放入到一个文本文件中,方便一次性执行的一个脚本文件。
简单来说 , 大家可以理解为我们平时在 终端
/ iTerm2
中写的命令的集合 . 当然 , 它就必不可少的设计到环境变量和权限等问题 .
在
MAC
系统中 , 默认使用的shell
是bash
. 我们使用iTerm
的时候 , 经常会配置另一个shell
叫zsh
. 当然 , 还有一些其他的shell
.我们需要知道的是
bash
的初始化文件是家目录下的.bash_profile
zsh
的初始化文件同样是家目录下的.zshrc
. 里面配置了一些环境变量.
写一个简单的 shell 脚本
例如我们经常有以下操作 :
mkdir test
cd test
touch 123.txt
cd ..
那么我们想将这些指令打包在一起 , 统一运行 . 类似于数据库中事务一样 .
输入命令 :
vi 123.sh
输入文件内容:
mkdir test
cd test
touch 123.txt
好 , 此时 我们已经生成了一个最简单的脚本 123.sh
.
那么如何执行呢 . 方法有很多种
-
- 使用
MAC
自带的bash
shell
:
bash 123.sh
- 使用
脚本执行完 , 我们的文件也创建好了 .
-
zsh
zsh 123.sh
-
source
source 123.sh
-
./
./123.sh
注意 :
source
执行完之后停留在子目录里 .
./
可能会报没有执行权限的问题 . +x
即可
区别
-
$source FileName
- 意思:在当前
shell
环境中读取并执行FileName
中的命令 - 特点:
- 命令可以强行让一个脚本去立即影响当前的环境(一般用于加载配置文件)。
- 命令会强制执行脚本中的全部命令,而忽略文件的权限。
- 意思:在当前
-
$bash FileName
/$zsh FileName
- 意思:重新建立一个子
shell
,在子shell
中执行脚本里面的句子。
- 意思:重新建立一个子
-
$./FileName
- 意思:读取并执行文件中的命令。但有一个前提,脚本文件需要有可执行权限。
shell 重签名
步骤 1. 新建工程
名称随意 , 新建完毕选择开发者 . 证书
运行一下空工程 ( 将描述文件安装到手机上 )
步骤 2. 准备要重签的包
- 在工程根目录下新建
APP
文件夹 , 也可以起其他名称 , 与Shell
脚本中的文件名对应起来就可以. - 将下载好的砸壳应用
ipa
放入APP
文件夹中.
步骤 3. 新建 Run Script Phase
可以看到他默认使用的是 sh
的 shell
.
当然 大家可以先随便写点 然后 build
一下看看有没有执行.
开始编写脚本 :
# ${SRCROOT} 它是工程文件所在的目录
TEMP_PATH="${SRCROOT}/Temp"
#资源文件夹,我们提前在工程目录下新建一个APP文件夹,里面放ipa包
ASSETS_PATH="${SRCROOT}/APP"
#目标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"
# 拿到解压的临时的APP的路径
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
# echo "路径是:$TEMP_APP_PATH"
#----------------------------------------
# 2. 将解压出来的.app拷贝进入工程下
# 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.个人证书没法签名Extention
rm -rf "$TARGET_APP_PATH/PlugIns"
rm -rf "$TARGET_APP_PATH/Watch"
#----------------------------------------
# 4. 更新info.plist文件 CFBundleIdentifier
# 设置:"Set : KEY Value" "目标文件路径"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"
#----------------------------------------
# 5. 给MachO文件上执行权限
# 拿到MachO文件的路径WeChat
APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
#上可执行权限
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
脚本中步骤都有详细注释说明. 那么我们来总结一下这个脚本做了什么.
其实跟我们手动重签的步骤是一模一样的.
1 在工程跟目录中找到
APP
文件夹. 找到里面的后缀为.ipa
的文件路径 , 创建并清空Temp
文件夹 . (以便后面存放解压ipa包的内容)2 将
ipa
解压到Temp
路径中 , 找到其中app
包的路径. ( Palyod 里的 .app 包) 并将解压出来的.app
包拷贝进入工程下 ( 其实就是替换掉工程 build 产生的.app 包)3 删除
PlugIns
和WatchAPP
.4 更新
info.plist
文件CFBundleIdentifier
5 给
MachO
文件上执行权限6 重签名第三方 FrameWorks
( shell
中添加 echo
可以进行打印输出 , 在 build
记录中 )
当然 , 你可以直接在 Run Script Phase
里写完这些 shell
, 也可以写到一个 app.sh
脚本文件中 , 然后在 Run Script Phase
里写一个 ./app.sh
即可 .
如果你遇到
相信你已经知道怎么解决了. 是的 chmod +x app.sh
重签成功 !
framework 代码注入
一般修改原始的程序,是利用代码注入的方式,注入代码就会选择利用 FrameWork
或者 Dylib
等三方库的方式注入。
当然 , 也可以直接修改 Mach-O
文件 , 这个涉及到汇编以及二进制 , 后面会考虑要不要继续解读 .
首先我们需要知道的是 Mach-O 文件拥有固定的格式 , 而对于 iOS
系统应用来说 , 需要通过 dyld
去读取 Mach-O
, 从而决定文件包需要加载哪些库 , 如何去加载等等 ,
后续我会专门讲一讲 Mach-O
文件 以及 dyld
链接流程 和 符号表的一些内容.
使用 MachOView
可视化预览 Mach-O
文件内容 ( otools 命令也可以 ) .
那么因此 , 我们可以思考 :
如果我可以修改 Mach-O
文件的 Load Commands
( 加载指令 , 就是告诉 dyld
如何去加载 ) , 往其中添加一条 , 添加一条指令让其去加载 我们自己生成的 framework
, 是否可行呢 .
答案是肯定的 .
步骤 1 : 工程准备
准备好已经重签完的工程 或者上述 准备好了 shell
和 ipa
的工程.
步骤 2 : 新建 framework
File - New - Target
选 Cocoa Touch Framework
, 我这里取名 LBHook
步骤 3 : framework 中新建一个类
在 .m
文件中写 load
方法 .
熟悉 load
方法机制的同学都知道 , load
方法是在 main
函数之前 , dyld
加载 _objc_init
时就会调用的. ( 不清楚的同学可以去阅读一下 iOS load方法调用机制解析 这篇文章 )
步骤 4 : cmd + B 编译一下
编译后打开包内容 , 查看 Frameworks 文件夹 , 可以看到我们的 framework 已经放进去了 .
但是没用啊 , 微信的 Mach-O
的 加载指令 load Commands
里并不会去加载我的这个库 .
步骤 5 : 修改 Mach-O
-
给当前
Mach-O
添加指令 让其加载我们的framework
. 这里要使用一个工具 yololib 提取码 : wxvv -
下载完毕后 , 将其复制到
usr/local/bin
中 , 这样环境变量就可以在任意路径下使用yololib
了. -
随便找一个
Mach-O
文件 ,go2shell
, 或者手动cd
到这个目录. -
yololib Wechat Frameworks/你的framework名称路径/你的framework
- 例 :
yololib Wechat Frameworks/LBHook.framework/LBHook
- 注意 这个
framework
路径是相当于前面macho
文件的路径 .
- 例 :
修改完毕后可以利用 MachOView 来看下我们的加载指令添加进去了没.
- 那么实验完毕可以添加指令 , 我们就来修改一下微信的原包里的
Mach-O
, 让其加载我们的framework
. - 打开我们
APP
路径下的ipa
包 , 改为zip
, 解压 ,Payload
里的包 , 显示包内容 , 找到Mach-O
. yololib Wechat Frameworks/LBHook.framework/LBHook
- 修改完毕后 重新压缩 使用
zip -ry Wechat.ipa Payload
( 其实不重新压缩也可以 跟我们shell
脚本里对应起来就好了 ) .
见证奇迹的时刻 :
cmd + r
运行 . 看控制台打印
思考题
那么既然我们可以注入代码 . 那我们可以做什么 ?
给个思路 :
可以注入代码 , 我们就可以
hook
微信的方法 . 上一篇文章我提到过view Debug
, 我们是可以看得到点击登录
/注册
的时候 , 调用的具体是什么方法的 . 又有黑魔法的存在 , 我们可以做的很多 . 大家自己去玩 .
补充
- 代码注入时修改 Mach-O 的
load commands
我们是使用手动注入的 . 其实我们在把yololib
环境变量配置好之后 , 完全可以在之前的 shell 脚本里做注入 .- 上面
shell
脚本中最后一行 加上 yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/HankHook.framework/HankHook"
- framework 的名称注意去修改就行
- 这样的话 , 我们做重签名和代码注入只需要 新建个空工程 运行 , 然后下载完微信的
ipa
, 根目录新建个APP
文件夹 , 放进去运行即可.
- 上面
dylib 代码注入
有些工具做代码注入的是 Library
就是 dylib
的库 , 其实它的原理和我们注入 framework
是一样的 , 只不过步骤上略有不同.
我们就使用 shell
简单带过一下.
另外 , iOS
现在已经不能添加 dylib
,只能从 macOS
添加
工程准备
同样是准备好已经重签完的工程 或者上述 准备好了 shell
和 ipa
的工程 .
新建库
File - New - Target
选 macOS
- Library
, 我这里取名 LBHookDylib
配置
- 打开
Build Settings
, 选择target
, 找到Base SDK
, 选择iOS
. - 找到
Code Sign Identity
, 选择iOS
的证书 - 选择
Build Phases
, 切换为主工程的target
, 新建Copy Files
- 添加
Destination
选择Frameworks
-
shell
脚本中最后一行多加一句yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libLBHookDylib.dylib"
-
运行 , 代码注入成功 .