在中我们知道了可以通过重签名的方式将他人的IPA包安装在自己手机上,这篇文章我们来了解一下
准备工作
资料地址密码qbt2
- 微信8.0.2IPA包
- MachOView
- yololib
- class-dump
首先我们使用上一篇iOS应用重签名中Xcode重签名的方式,来对微信8.0.2进行重签名,连接手机运行程序后,我们修改ViewController
中添加代码会发现不起作用,是因为整个App(Mach-O)被替换了
Mach-O 简单分析
我们将上一步的可执行程序拖进MachOView
中,进行分析,会得到一个可视化的Mach-O文件

我们知道在iOS中,可执行文件时通过dyld加载到内存中的,在Mach-O文件中,Load Commands
会告诉dyld需要加载那些动态库

通过上面的分析,我们理论上是可以通过增加一个动态库
来实现代码注入
动态库代码注入
添加动态库
首先我们在重签名的项目中新建一个动态库,并添加一个load方法

然后Build一下,我们在微信的可执行程序的
Frameworks
中会找到我们刚新增的动态库
我们在运行一下程序,会发现load方法的代码并没执行。然后我们再用MachOView
重新分析WeChat,会发现在Load Commands
并没有刚刚新建的动态库,那么下面我要做就是将新建的动态库添加到Load Commands
中
yololib修改Mach-O文件注入
./yololib WeChat Frameworks/HankHook.framework/HankHook

我们再用MachOView重新分析WeChat,会发现我们新加的动态库已经添加进了Mach-O文件中

重新运行就可以了

Dylib代码注入
我们新建一个工程,使用iOS应用重签名中Xcode重签名的方式对WeChat进行重签名后,我们新建一个macOS的动态库

修改macOS动态库的
Base SDK
为iOS,签名改为iOS Developer

在当前工程里添加动态库

同样的,我们在动态库中重写load方法

脚本执行
更新原来Xcode中的脚本重新运行就可以了
# ${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文件的路径
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
#代码注入
./yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/libHankHook.dylib"
HOOK微信
hook WeChat的注册、登录
我们新建一个项目,通过上面的方式进行重签名和注入后,运行程序,在调试界面我们可以发现对应的登录和注册的Action
和Target

或者我们也可以通过响应链找到对应的view

然后我们在新加的动态库中添加下面代码
#import "InjectCode.h"
#import <objc/runtime.h>
@implementation InjectCode
+ ( void)load {
Method oldMethod = class_getInstanceMethod(objc_getClass( "WCAccountLoginControlLogic"), @selector(onFirstViewRegister));
Method newMethod = class_getInstanceMethod( self, @selector(my_register));
method_exchangeImplementations(oldMethod, newMethod);
}
- ( void)my_register {
NSLog( @"想注册吗?想注册先打钱!");
}
@end
重新运行后,会打印出my_register
中的信息
窃取登录密码
class-dump静态分析Mach-O
终端命令
./class-dump -A WeChat
./class-dump -H WeChat -o ./headers/
通过class-dump分析后,我们会得到OC的类列表和方法列表,然后我们用sublime工具进行全局查找对应的类
搜索 WCAccountMainLoginViewController,里面有两个对象命名很像账号和密码的东西

搜索 WCAccountTextFieldItem

搜索 WCBaseTextFieldItem,在里面找到了WCUITextField

接下来我们来验证一下WCUITextField,在登录框中输入账号和密码,然后打开调试界面,通过valueForkey
找到登录密码

代码窃取登录密码
方法交换
+(void)load{
//原始的Method
Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
//添加新方法!
class_addMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext), new_onNext, "v@:");
//交换
method_exchangeImplementations(onNext, class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(new_onNext)));
}
//新的IMP
void new_onNext(id self,SEL _cmd){
UITextField * pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"密码是:%@",pwd.text);
//调用回原来的逻辑!!
//调用原来的方法!
[self performSelector:@selector(new_onNext)];
//objc_msgSend();
}
方法替换
+(void)load{
//原始的Method
Method onNext = class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext));
old_onNext = class_replaceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext), new_onNext, "v@:");
}
//原来的IMP
IMP (*old_onNext)(id self,SEL _cmd);
//新的IMP
void new_onNext(id self,SEL _cmd){
UITextField * pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"密码是:%@",pwd.text);
//调用回原来的逻辑!!
//调用原来的方法!
old_onNext(self,_cmd);
//objc_msgSend();
}
getter和setter
+(void)load{
//原始的Method
old_onNext = method_getImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)));
method_setImplementation(class_getInstanceMethod(objc_getClass("WCAccountMainLoginViewController"), @selector(onNext)), new_onNext);
}
//原来的IMP
IMP (*old_onNext)(id self,SEL _cmd);
//新的IMP
void new_onNext(id self,SEL _cmd){
UITextField * pwd = (UITextField *)[[self valueForKey:@"_textFieldUserPwdItem"] valueForKey:@"m_textField"];
NSLog(@"密码是:%@",pwd.text);
//调用回原来的逻辑!!
//调用原来的方法!
old_onNext(self,_cmd);
//objc_msgSend();
}
网友评论