动态库注入app以及在非越狱手机使用

动态库注入app以及在非越狱手机使用

1. 动态库编写

动态库编写有多种方式,可以使用Xcode创建动态库,也可以通过tweak生成动态库

  • 对于越狱手机,可以直接编写tweak,将tweak打包成动态库
  • 对于非越狱手机,可以使用Xcode创建动态库,在Xcode中编写hook代码,生成动态库

第一种,在越狱手机上编写tweak,打包成动态库

1. 安装Xcode,这个就不多说了,在苹果官网下载安装即可
2. tweak环境的安装及编写请看这个

第一个tweak生成后,我们来hook一下之前新建的一个demo, 可以在我的github上面下载到。

@implementation FirstTweakViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor whiteColor];
    self.title = @"FirstTweakDemo";
}

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self showAlert];
    });
}

- (void)showAlert {
    UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提醒" message:@"这是原始弹窗" preferredStyle:(UIAlertControllerStyleAlert)];
    UIAlertAction * action = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:nil];
    [alert addAction:action];
    [self presentViewController:alert animated:YES completion:nil];
}

@end

demo的主要内容是在控制器的ViewDidAppear方法中延时两秒弹出一个弹窗,我们现在来一下这个逻辑,hook住弹出弹窗的方法,然后弹出一个actionSheet.

hook前的效果
在这里插入图片描述

3. 开始编写hook代码

回到我们生成的Tweak目录,发现四个文件

  • control
  • FirstTweak.plist
  • Makefile
  • Tweak.xm

hook的代码在Tweak.xm中编写,在xcode中打开Tweak.xm,logos语法参见Wiki. 编写如下代码

#import <UIKit/UIKit.h>

@interface FirstTweakViewController: UIViewController

- (void)showAlert;

@end
%hook FirstTweakViewController

- (void)showAlert {
	UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提醒" message:@"这是hook后的提醒" preferredStyle:(UIAlertControllerStyleActionSheet)];
    UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"Cancel" style:(UIAlertActionStyleCancel) handler:nil];
    UIAlertAction * confirm = [UIAlertAction actionWithTitle:@"Confirm" style:(UIAlertActionStyleDestructive) handler:nil];
    [alert addAction:cancel];
    [alert addAction:confirm];
    [self presentViewController:alert animated:YES completion:nil];
}

%end

检查无误后进行配置,打开FirstTweak.plist, 替换bundleId为我们想要hook的App的bundleId
NH.FirstTweakDemo

在这里插入图片描述
在这里插入图片描述

4. 安装tweak到手机

然后我们将22端口转发到22222

iproxy 22222 22

回到终端,cd到tweak目录, 连接我们的越狱手机

$ make package install

如果报错,缺少THEOS_DEVICE_IP,在终端输入如下

export THEOS_DEVICE_IP=localhost:22222

再次执行make package install
结果如下:

$ make package install
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk -find llvm-dsymutil 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "llvm-dsymutil", not a developer tool or in PATH
> Making all for tweak FirstTweak…
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk -find llvm-dsymutil 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "llvm-dsymutil", not a developer tool or in PATH
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk -find llvm-dsymutil 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "llvm-dsymutil", not a developer tool or in PATH
make[2]: Nothing to be done for `internal-library-compile'.
> Making stage for tweak FirstTweak…
xcrun: error: sh -c '/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -sdk /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk -find llvm-dsymutil 2> /dev/null' failed with exit code 17664: (null) (errno=No such file or directory)
xcrun: error: unable to find utility "llvm-dsymutil", not a developer tool or in PATH
dm.pl: building package `com.yourcompany.firsttweak:iphoneos-arm' in `./packages/com.yourcompany.firsttweak_0.0.1-3+debug_iphoneos-arm.deb'
==> Installing…
root@localhost's password:
Selecting previously unselected package com.yourcompany.firsttweak.
(Reading database ... 2140 files and directories currently installed.)
Preparing to unpack /tmp/_theos_install.deb ...
Unpacking com.yourcompany.firsttweak (0.0.1-3+debug) ...
Setting up com.yourcompany.firsttweak (0.0.1-3+debug) ...
install.exec "killall -9 SpringBoard"
root@localhost's password:

输入两次密码后,成功安装到手机。 SpringBoard重启后打开FirstTweakDemo,点击Push,进入FirstTweakViewController控制器,等待两秒后可以看到。

在这里插入图片描述

成功的hook住了!!!

那么问题来了,这是给越狱设备使用的,如何给未越狱设备使用呢?

5. 将tweak生成的动态库注入app中,实现非越狱设备安装

FirstTweak.dylib依赖替换

回到tweak目录,可以看到,有个.theos目录,找到.theos/_/Library/MobileSubstrate/DynamicLibraries/FirstTweak.dylib 文件,这是tweak打包后生成的动态库

在这里插入图片描述

用otool查看动态库的依赖情况

$ otool -l FirstTweak.dylib | grep name
  name /Library/MobileSubstrate/DynamicLibraries/FirstTweak.dylib (offset 24)
         name /usr/lib/libobjc.A.dylib (offset 24)
         name /System/Library/Frameworks/Foundation.framework/Foundation (offset 24)
         name /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (offset 24)
         name /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate (offset 24)
         name /usr/lib/libc++.1.dylib (offset 24)
         name /usr/lib/libSystem.B.dylib (offset 24)
         name /System/Library/Frameworks/UIKit.framework/UIKit (offset 24)

动态库依赖了CydiaSubstrate,这个是属于越狱设备的,非越狱设备上没有,需要修改依赖

这是依赖库 name /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate

至于这个依赖 name /Library/MobileSubstrate/DynamicLibraries/FirstTweak.dylib 不用管

修改如下

install_name_tool -change /Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate @executable_path/Frameworks/libsubstrate.dylib FirstTweak.dylib

再次查看依赖,发现已经修改成功

name /Library/MobileSubstrate/DynamicLibraries/FirstTweak.dylib (offset 24)
         name /usr/lib/libobjc.A.dylib (offset 24)
         name /System/Library/Frameworks/Foundation.framework/Foundation (offset 24)
         name /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation (offset 24)
         name @executable_path/Frameworks/libsubstrate.dylib (offset 24)
         name /usr/lib/libc++.1.dylib (offset 24)
         name /usr/lib/libSystem.B.dylib (offset 24)
         name /System/Library/Frameworks/UIKit.framework/UIKit (offset 24)

FirstTweak.dylib与libsubstrate.dylib签名

下一步,需要将libsubstrate.dylibFirstTweak.dylib签名

我们先来查看下电脑上有哪些可用的证书

$ security find-identity -v -p codesigning
$ 1) DFCF477C3F593AFEF3AB261E9FD641873600EAAD "Apple Development: daye  (12CD56GH78)"

找到之前用于签名FirstTweakDemo的证书,开始签名

$ codesign -fs 'Apple Development: daye  (12CD56GH78)' libsubstrate.dylib
$ codesign -fs 'Apple Development: daye  (12CD56GH78)' FirstTweak.dylib

签名成功后,将libsubstrate.dylibFirstTweak.dylib复制到FirstTweakDemo.app/Frameworks/

然后将FirstTweak.dylib注入到FirstTweakDemo

$ ./optool install -c load -p @executable_path/Frameworks/FirstTweak.dylib -t FirstTweakDemo.app/FirstTweakDemo
Found thin header...
Inserting a LC_LOAD_DYLIB command for architecture: arm64
Successfully inserted a LC_LOAD_DYLIB command for arm64
Writing executable to FiirstTweakDemo.app/FirstTweakDemo...

签名FirstTweak.app

生成entitlements.plist文件
将FiirstTweakDemo.app/下的 embedded.mobileprovision 拷贝一份

查看描述文件内容,寻找Entitlements节点,将Entitlements节点下的内容复制到根节点下

security cms -D -i embedded.mobileprovision > entitlements.plist

最终的entitlements.plist文件内容如下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.developer.team-identifier</key>
	<string>12CD56GH78</string>
	<key>get-task-allow</key>
	<true/>
	<key>keychain-access-groups</key>
	<array>
		<string>12CD56GH78.*</string>
		<string>com.apple.token</string>
	</array>
	<key>application-identifier</key>
	<string>12CD56GH78.*</string>
</dict>
</plist>

接着对FirstTweakDemo.app签名

codesign -fs 'Apple Development: daye  (12CD56GH78)' --no-strict --entitlements=entitlements.plist FirstTweakDemo.app

打包ipa包

zip -ry FirstTweakDemo.ipa Payload

现在就可以安装到未越狱的手机啦~~~

详细的信息参见install.sh

第二种,直接在Xcode中编写动态库

前面的tweak生成动态库方式,编写tweak稍显麻烦,我们直接在Xcode中编写代码。

利用Xcode,将目标APP拷贝到当前APP的目录替换现有APP,注入动态库到目标APP,重新签名,运行到设备。

1. 新建一个普通的iOS 工程, 名为InsertDemo

在这里插入图片描述

在这里插入图片描述

2. 新建一个target,选择dylib,名为InsertDemoDylib

在这里插入图片描述

3. 删除InsertDemoDylib类,新建InsertDemoDylib.xm与InsertDemoDylib.mm文件,工程结构如下

在这里插入图片描述

4. 在InsertDemoDylib.xm文件中编写hook代码,还是logos语法, 还是hook前面的FirstTweakDemo

#import <UIKit/UIKit.h>

@interface FirstTweakViewController: UIViewController

- (void)showAlert;

@end
%hook FirstTweakViewController

- (void)showAlert {
	UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提醒" message:@"这是hook后的提醒" preferredStyle:(UIAlertControllerStyleActionSheet)];
    UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"Cancel" style:(UIAlertActionStyleCancel) handler:nil];
    UIAlertAction * confirm = [UIAlertAction actionWithTitle:@"Confirm" style:(UIAlertActionStyleDestructive) handler:nil];
    [alert addAction:cancel];
    [alert addAction:confirm];
    [self presentViewController:alert animated:YES completion:nil];
}

%end

5. 使用theos生成InsertDemoDylib.mm的代码,这样才能让Xcode识别并编译InsertDemoDylib.mm

/opt/theos/bin/logos.pl InsertDemoDylib.xm > InsertDemoDylib.mm

InsertDemoDylib.mm 的内容如下

#line 1 "InsertDemoDylib/InsertDemoDylib.xm"


#if TARGET_OS_SIMULATOR
#error Do not support the simulator, please use the real iPhone Device.
#endif

#import <UIKit/UIKit.h>

@interface FirstTweakViewController: UIViewController

- (void)showAlert;

@end


#include <substrate.h>
#if defined(__clang__)
#if __has_feature(objc_arc)
#define _LOGOS_SELF_TYPE_NORMAL __unsafe_unretained
#define _LOGOS_SELF_TYPE_INIT __attribute__((ns_consumed))
#define _LOGOS_SELF_CONST const
#define _LOGOS_RETURN_RETAINED __attribute__((ns_returns_retained))
#else
#define _LOGOS_SELF_TYPE_NORMAL
#define _LOGOS_SELF_TYPE_INIT
#define _LOGOS_SELF_CONST
#define _LOGOS_RETURN_RETAINED
#endif
#else
#define _LOGOS_SELF_TYPE_NORMAL
#define _LOGOS_SELF_TYPE_INIT
#define _LOGOS_SELF_CONST
#define _LOGOS_RETURN_RETAINED
#endif

@class FirstTweakViewController; 
static void (*_logos_orig$_ungrouped$FirstTweakViewController$showAlert)(_LOGOS_SELF_TYPE_NORMAL FirstTweakViewController* _LOGOS_SELF_CONST, SEL); static void _logos_method$_ungrouped$FirstTweakViewController$showAlert(_LOGOS_SELF_TYPE_NORMAL FirstTweakViewController* _LOGOS_SELF_CONST, SEL); 

#line 15 "InsertDemoDylib/InsertDemoDylib.xm"


static void _logos_method$_ungrouped$FirstTweakViewController$showAlert(_LOGOS_SELF_TYPE_NORMAL FirstTweakViewController* _LOGOS_SELF_CONST __unused self, SEL __unused _cmd) {
    UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"提醒" message:@"这是hook后的提醒" preferredStyle:(UIAlertControllerStyleActionSheet)];
    UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"Cancel" style:(UIAlertActionStyleCancel) handler:nil];
    UIAlertAction * confirm = [UIAlertAction actionWithTitle:@"Confirm" style:(UIAlertActionStyleDestructive) handler:nil];
    [alert addAction:cancel];
    [alert addAction:confirm];
    [self presentViewController:alert animated:YES completion:nil];
}


static __attribute__((constructor)) void _logosLocalInit() {
{Class _logos_class$_ungrouped$FirstTweakViewController = objc_getClass("FirstTweakViewController"); MSHookMessageEx(_logos_class$_ungrouped$FirstTweakViewController, @selector(showAlert), (IMP)&_logos_method$_ungrouped$FirstTweakViewController$showAlert, (IMP*)&_logos_orig$_ungrouped$FirstTweakViewController$showAlert);} }
#line 27 "InsertDemoDylib/InsertDemoDylib.xm"

别忘了在工程中添加动态库依赖

在这里插入图片描述

别忘了把动态库的base SDK改为iOS,否则编译不通过

在这里插入图片描述

6. 编译InsertDemo,生成InsertDemoDylib.dylib

我们给工程的InsertDemoDylib target添加RunScript

在这里插入图片描述

dylib.sh的任务很简单,将InsertDemoDylib这个target下的所有 .xm后缀的文件生成对应的 .mm ,所用的工具即为 logos.pl

dylib.sh内容如下

TARGET_NAME=${PRODUCT_NAME}

echo "TARGET_NAME:${TARGET_NAME}"

function panic() # args: exitCode, message...
{
    local exitCode=$1
    set +e
    
    shift
    [[ "$@" == "" ]] || \
        echo "$@" >&2

    exit $exitCode
}
#预处理xm、x文件
function Processor()
{
    local logosProcessor="$1"
    local currentDirectory="$2"
    echo "currentDirectory:$currentDirectory"

    if [[ $currentDirectory =~ "Build/Products" ]] || [[ $currentDirectory =~ "Build/Intermediates" ]] || [[ $currentDirectory =~ "Index/DataStore" ]] || [[ $currentDirectory =~ "/LatestBuild/" ]]; then
        echo "???????"
        return
    fi
    for file in `ls "$currentDirectory"`;
    do
        echo "file:${file}"
        extension="${file#*.}"
        filename="${file##*/}"
        if [[ -d "$currentDirectory""$file" ]]; then
            Processor "$logosProcessor" "$currentDirectory""$file"
        elif [[ "$extension" == "xm" ]]; then
            echo "XMFile:${file}"
            if [[ ! -f "$currentDirectory/${file%.*}.mm" ]] || [[ `ls -l "$currentDirectory/${file%.*}.mm" | awk '{ print $5 }'` < 10 ]] || [[ `stat -f %c "$currentDirectory/$file"` > `stat -f %c "$currentDirectory/${file%.*}.mm"` ]]; then
                  echo "Logos Processor: $filename -> ${filename%.*}.mm..."
                  logosStdErr=$(("$logosProcessor" "$currentDirectory""$file" > "$currentDirectory""${file%.*}.mm") 2>&1) || \
                    panic $? "Failed Logos Processor: $logosStdErr"
            fi
        elif [[ "$extension" == "x" ]]; then
            if [[ ! -f "$currentDirectory/${file%.*}.m" ]] || [[ `ls -l "$currentDirectory/${file%.*}.m" | awk '{ print $5 }'` < 10 ]] || [[ `stat -f %c "$currentDirectory/$file"` > `stat -f %c "$currentDirectory/${file%.*}.m"` ]]; then
                  echo "Logos Processor: $filename -> ${filename%.*}.m..."
                logosStdErr=$(("$logosProcessor" "$currentDirectory/$file" > "$currentDirectory/${file%.*}.m") 2>&1) || \
                    panic $? "Failed Logos Processor: $logosStdErr"
            fi
        fi
    done
}
logosProcessor="/opt/theos/bin/logos.pl"
echo "Start to genarate xxx.mm"
Processor "$logosProcessor" "$PROJECT_DIR/${TARGET_NAME}/"

给工程APP添加RunScript

在这里插入图片描述

目标APP替换现有APP的主要过程如下

将现有APP下的描述文件拷贝出来

if [ -f "${BUILD_APP_PATH}/../embedded.mobileprovision" ]; then
      mv "${BUILD_APP_PATH}/../embedded.mobileprovision" "${BUILD_APP_PATH}"
fi

拷贝目标APP到当前APP的目录下做替换

cp -rf "${TARGET_APP_PATH}/" "${BUILD_APP_PATH}/"

拷贝描述文件到目标APP

if [ -f "${BUILD_APP_PATH}/../embedded.mobileprovision" ]; then
      mv "${BUILD_APP_PATH}/../embedded.mobileprovision" "${BUILD_APP_PATH}"
fi

将依赖的动态库与当前公吃过生成的动态库拷贝到APP的Frameworks目录下

cp -rf "${BUILT_PRODUCTS_DIR}/lib""${TARGET_NAME}""Dylib.dylib" "${TARGET_APP_FRAMEWORKS_PATH}"
cp -rf "${DYLIBS_TO_INJECT_PATH}" "${TARGET_APP_FRAMEWORKS_PATH}"

将当前工程生成的动态库注入到APP的二进制文件中

APP_BINARY=`plutil -convert xml1 -o - ${BUILD_APP_PATH}/Info.plist | grep -A1 Exec | tail -n1 | cut -f2 -d\> | cut -f1 -d\<`
"$OPTOOL" install -c load -p "@executable_path/Frameworks/lib""${TARGET_NAME}""Dylib.dylib" -t "${BUILD_APP_PATH}/${APP_BINARY}"

对APP的Frameworks目录下的所有动态库签名

code_sign "${TARGET_APP_FRAMEWORKS_PATH}"

Xcode将APP安装到设备
具体脚本信息请参见install.sh

7. 修改项目配置,安装APP到设备

编译当前项目,发现报错,找不到substrate.h

InsertDemoDylib/InsertDemoDylib.xm:16:10: fatal error: 'substrate.h' file not found
#include <substrate.h>
         ^~~~~~~~~~~~~
1 error generated.

这是因为我们没有引入substrate.h, 在header Search Path中引入下

在这里插入图片描述

再次编译,报错如下

Undefined symbols for architecture armv7:
  "_OBJC_CLASS_$_UIAlertAction", referenced from:
      objc-class-ref in InsertDemoDylib.o
  "_OBJC_CLASS_$_UIAlertController", referenced from:
      objc-class-ref in InsertDemoDylib.o
  "_MSHookMessageEx", referenced from:
      _logosLocalInit() in InsertDemoDylib.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

这是因为我们的代码依赖UIKit库,但是动态库的target并没有引入,引入即可

在这里插入图片描述

再次编译,报错如下

Undefined symbols for architecture arm64:
  "_MSHookMessageEx", referenced from:
      _logosLocalInit() in InsertDemoDylib.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

这是libsubstrate库没有引入的原因

在这里插入图片描述

再次编译,报错如下

ld: -weak_library and -bitcode_bundle (Xcode setting ENABLE_BITCODE=YES) cannot be used together

关闭bitcode即可

在这里插入图片描述

在这里插入图片描述

再次编译,不报错了哟~~哈哈

跑个真机试试, 哟,一次成功了!!!

在这里插入图片描述

采用Xcode这种方式更简单,不用编写Tweak, 而且可以给Xcode添加一个template,以后就不用一步步配置啦,直接从Xcode新建项目菜单新建一个工程就行了。
剩下的就是把想要Hook的app拖入TargetAPP目录中就可以了。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值