iOS 代码注入和HOOK

2021-04-20  本文已影响0人  木扬音

在中我们知道了可以通过重签名的方式将他人的IPA包安装在自己手机上,这篇文章我们来了解一下

准备工作

资料地址密码qbt2

首先我们使用上一篇iOS应用重签名中Xcode重签名的方式,来对微信8.0.2进行重签名,连接手机运行程序后,我们修改ViewController中添加代码会发现不起作用,是因为整个App(Mach-O)被替换了

Mach-O 简单分析

我们将上一步的可执行程序拖进MachOView中,进行分析,会得到一个可视化的Mach-O文件

Mach-O文件

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

image.png

通过上面的分析,我们理论上是可以通过增加一个动态库来实现代码注入

动态库代码注入

添加动态库

首先我们在重签名的项目中新建一个动态库,并添加一个load方法

创建动态库
然后Build一下,我们在微信的可执行程序的Frameworks中会找到我们刚新增的动态库
image.png

我们在运行一下程序,会发现load方法的代码并没执行。然后我们再用MachOView重新分析WeChat,会发现在Load Commands并没有刚刚新建的动态库,那么下面我要做就是将新建的动态库添加到Load Commands

yololib修改Mach-O文件注入
./yololib WeChat Frameworks/HankHook.framework/HankHook
yololib修改Mach-O文件

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


Mach-O

重新运行就可以了


注入成功

Dylib代码注入

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

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

在当前工程里添加动态库


添加动态库

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


重写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的注册、登录

我们新建一个项目,通过上面的方式进行重签名和注入后,运行程序,在调试界面我们可以发现对应的登录和注册的ActionTarget

调试WeChat

或者我们也可以通过响应链找到对应的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,里面有两个对象命名很像账号和密码的东西


WCAccountMainLoginViewController

搜索 WCAccountTextFieldItem


WCAccountTextFieldItem

搜索 WCBaseTextFieldItem,在里面找到了WCUITextField


WCBaseTextFieldItem

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

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();
    
}
上一篇下一篇

猜你喜欢

热点阅读