吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4079|回复: 14
上一主题 下一主题
收起左侧

[MacOS逆向] MacOS AirBuddy x86_64/arm dobby HOOK 通杀方案

  [复制链接]
跳转到指定楼层
楼主
Vvvvvoid 发表于 2024-1-14 22:00 回帖奖励
本帖最后由 Vvvvvoid 于 2024-1-16 09:52 编辑

前记

xcode 开发 dylib , 基于跨平台的 dobby HOOK 框架来构建跨平台的通杀补丁.
你妈再也不用担心你只能跑 Rosetta 了..  

开发环境:

  • xcode 15.2
  • dobby
  • insert_dylib
  • hopper | ida

项目搭建

  1. xcode 新建一个 MacOS > Library 项目

稍微做一些配置:
-- 关掉代码优化: Optimization Level -> None
这个东西开了的话 hook 或者 写内联汇编会出问题

-- 跨平台构建打开: Build Active Architecture Only > No
这个东西开了的话, m系列代码 编译出来的 x86/arm 都可以用,跨平台必备

  1. 项目中引入 dobby 动态库 < libdobby.dylib >, 并且 引入 dobby.h 头文件

  2. 编写 hook 代码

hook 代码

先根据 系统架构宏 写个判断  

#if defined(__arm64__) || defined(__aarch64__)
// 此处写 arm hook 代码

#elif defined(__x86_64__)
// 此处写 x86_64 hook 代码

#endif

+ (void) load {    
    NSString *appName = [[NSBundle mainBundle] bundleIdentifier];
    const char *myAppBundleName = [appName UTF8String];

    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:@"确认执行破解操作吗?"];
    [alert addButtonWithTitle:@"确认"];
    [alert addButtonWithTitle:@"取消"];
    NSInteger response = [alert runModal];

    if (response == NSAlertFirstButtonReturn) {
        // 用户选择了确认按钮
        // AirBuddy();
    } else {
        // 用户选择了取消按钮
        return;
    }
}

关键 hook 函数,见帖子 :
https://www.52pojie.cn/thread-1739112-1-1.html  

该帖子里已知,x86_64 下函数 0x100050480 中 r13+99h 来判断是否已注册
帖子中的 hook 框架 rd_router 只能在 x86下 使用, 我们替换成 dobby 写法
macos 逆向 ,我接触的不多, 原帖计算函数地址用到了 _dyld_get_image_vmaddr_slide 方法,
我配置 xcode dylib 直接启动 run with app 的话, 需要把_dyld_get_image_vmaddr_slide 删掉, 不知道为啥,..

#elif defined(__x86_64__)

int _0x100050480New() {
    NSLog(@"==== _0x100050480New called");
    __asm
    {  
        mov byte ptr[r13+99h], 0
    }
    NSLog(@"==== _0x100050480New call end");
    return _0x100050480Ori();
}

void AirBuddy() {
    NSLog(@"The current app running environment is __x86_64__");
    intptr_t _0x100050480 =  _dyld_get_image_vmaddr_slide() + 0x100050480;
    DobbyHook(_0x100050480, _0x100050480New, (void *)&_0x100050480Ori);
    NSLog(@"_0x100050480 >> %p",_0x100050480);
}
#endif

用同样的方法, 我们用 hopper 或者 ida 找到 arm 下的目标函数。

可以看到 , 函数地址为: 0x1000553b8
并且由 x20+99h 来判断是否已注册, hook 代码如下:
wzr 是一个特殊的寄存器,表示零寄存器,它的值始终为0。
通过将 wzr 寄存器的值存储到[x20, #0x99]内存地址处

#if defined(__arm64__) || defined(__aarch64__)

int _0x1000553b8New() {
    // r20 + 0x99 != 0x1
    NSLog(@"==== _0x1000553b8New called");
    __asm__ __volatile__(
       "strb wzr, [x20, #0x99]"
     );
    NSLog(@"==== _0x1000553b8New call end");
    return _0x1000553b8Ori();
}

void AirBuddy() {
    NSLog(@"The current app running environment is __arm64__");
    intptr_t _0x1000553b8 = _dyld_get_image_vmaddr_slide() + 0x1000553b8;
    DobbyHook(_0x1000553b8, _0x1000553b8New, (void *)&_0x1000553b8Ori);
    NSLog(@"_0x1000553b8 >> %p",_0x1000553b8);

}
#elif defined(__x86_64__)

build 注入

编译后, 会得到一个我们的 dylib 补丁
然后编写 shell 脚本,来注入  

current_path=$PWD
echo "当前路径:$current_path"

app_name="AirBuddy"

dylib_name="dylib_dobby_hook"
prefix="lib"

insert_dylib="${current_path}/../tools/insert_dylib"

# 我们的 release 补丁路径
BUILT_PRODUCTS_DIR="${current_path}/../Release"

app_bundle_path="/Applications/${app_name}.app/Contents/MacOS/"

cp -f "${insert_dylib}" "${app_bundle_path}/"   

app_bundle_framework="/Applications/${app_name}.app/Contents/Frameworks"
app_executable_path="${app_bundle_path}/${app_name}"
app_executable_backup_path="${app_executable_path}_Backup"

# 第一次注入的之后备份源文件
if [ ! -f "$app_executable_backup_path" ]; 
then
    cp "$app_executable_path" "$app_executable_backup_path"
fi

# 把补丁 与 补丁依赖的 dobby hook 框架都复制到目标程序下
cp -R "${BUILT_PRODUCTS_DIR}/${prefix}${dylib_name}.dylib" ${app_bundle_framework}
cp -R "${BUILT_PRODUCTS_DIR}/libdobby.dylib" ${app_bundle_framework}

# 用 insert_dylib 来向目标程序注入
"${app_bundle_path}/insert_dylib" --weak --all-yes "@rpath/${prefix}${dylib_name}.dylib" "$app_executable_backup_path" "$app_executable_path"

效果如下:

x86_64

arm

arm hook 的汇编代码怎么感觉看着有点奇怪 ??  

代码优化

基础代码已经完成, 为了兼容更多的 app 补丁, 我们对代码做一些重构优化。
使用适配器模式来扩展  

定义 Hack 接口

接口定义几个方法, 比如教研app名称/版本号,以及执行 hack

@protocol HackProtocol
- (NSString *)getAppName;
- (BOOL)checkVersion;
- (BOOL)hack;
@end

定义实现类(已当前 Airbuddy 为例)

#import "HackProtocol.h"

@interface AirBuddyHack : NSObject <HackProtocol>

@end

@implementation AirBuddyHack
- (NSString *)getAppName {
    return @"codes.rambo.AirBuddy";
}

- (BOOL)checkVersion {
    return YES;
}

- (BOOL)hack {
    [self hook];
    return YES;
}

#if defined(__arm64__) || defined(__aarch64__)
- (void)hook {
    ...doSomething
}
#elif defined(__x86_64__)

- (void)hook {
    ...doSomething
}
#endif
@end

定义一个全局的适配器工具类, 根据 appName 来获取对应的实现类,来执行 hack 操作

@implementation Constant

static void __attribute__ ((constructor)) initialize(void){
    NSLog(@"constant init");
}

+ (BOOL)isDebuggerAttached {
    BOOL isDebugging = NO;
        // 获取当前进程的信息
        NSProcessInfo *processInfo = [NSProcessInfo processInfo];
        // 获取进程的环境变量
        NSDictionary *environment = [processInfo environment];
        // 检查环境变量中是否有调试器相关的标志
        if (environment != nil) {
            // 根据环境变量中是否包含特定的调试器标志来判断是否处于调试模式
            if (environment[@"DYLD_INSERT_LIBRARIES"] ||
                environment[@"MallocStackLogging"] ||
                environment[@"NSZombieEnabled"] ||
                environment[@"__XDEBUGGER_PRESENT"] != nil) {
                isDebugging = YES;
            }
        }
    return isDebugging;
}

+ (intptr_t)getBaseAddr:(uint32_t)index{
    BOOL isDebugging = [Constant isDebuggerAttached];
    if(isDebugging){
        NSLog(@"The current app running with debugging");
        #if defined(__arm64__) || defined(__aarch64__)
        // 不知道为什么
        // arm 环境下,如果是调试模式, 计算地址不需要 + _dyld_get_image_vmaddr_slide,否则会出错
        return 0;
        #endif
    }
    return _dyld_get_image_vmaddr_slide(index);
}

+ (NSArray<Class> *)getAllHackClasses {
    NSMutableArray<Class> *hackClasses = [NSMutableArray array];

    int numClasses;
    Class *classes = NULL;
    numClasses = objc_getClassList(NULL, 0);

    if (numClasses > 0) {
        classes = (__unsafe_unretained Class *)malloc(sizeof(Class) * numClasses);
        numClasses = objc_getClassList(classes, numClasses);

        for (int i = 0; i < numClasses; i++) {
            Class class = classes[i];

            if (class_conformsToProtocol(class, @protocol(HackProtocol))) {
                [hackClasses addObject:class];
            }
        }
        free(classes);
    }
    return hackClasses;
}

+ (void)doHack:(NSString *)currentAppName {
    NSArray<Class> *personClasses = [Constant getAllHackClasses];

    for (Class class in personClasses) {
        id<HackProtocol> it = [[class alloc] init];
        NSString *appName = [it getAppName];
        if ([appName isEqualToString:currentAppName]) {
            // TODO 执行其他操作 ,比如 checkVersion
            [it hack];
            break;
        }
    }
}
@end

最后在 dylib 入口处传入 appName

+ (void) load {

    NSString *appName = [[NSBundle mainBundle] bundleIdentifier];
    NSAlert *alert = [[NSAlert alloc] init];
    [alert setMessageText:@"确认执行破解操作吗?"];
    [alert addButtonWithTitle:@"确认"];
    [alert addButtonWithTitle:@"取消"];
    NSInteger response = [alert runModal];
    if (response == NSAlertFirstButtonReturn) {
        [Constant doHack:appName];
    } else {
        return;
    }
}
@end

至此,代码重构优化结束,如果补丁要支持新的 app ,只需要添加一个 HackProtocol 实现类即可,
对别的地方的代码, 零入侵.

Ref

  1. [MacOS逆向] AirBuddy2 2.6.3 的dylib注入方案 (2) https://www.52pojie.cn/thread-1739112-1-1.html
  2. [C&C++ 原创] C++ 跨平台 内联汇编集成 (MacOS,Linux,Windows) https://www.52pojie.cn/thread-1653689-1-1.html
  3. jmpews/Dobby https://github.com/jmpews/Dobby

Release

项目已经打包 github,可以直接用 xcode 打开 :
https://github.com/marlkiller/dylib_dobby_hook  

目录:

  1. libs:  项目依赖的开源 dobby 库
  2. release:  build 后的成品
  3. script:  里面有个 hack.sh, 可以直接sudo sh 执行一键注入脚本
  4. tools: insert_dylib 开源注入工具



免费评分

参与人数 6威望 +1 吾爱币 +26 热心值 +5 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
来阻 + 1 + 1 用心讨论,共获提升!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Slimu + 2 + 1 用心讨论,共获提升!
杨辣子 + 1 + 1 谢谢@Thanks!
爱飞的猫 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
judong0x1 发表于 2024-1-16 09:55
Vvvvvoid 发表于 2024-1-16 09:53
Xcode Version 15.2 (15C500b)

低版本的 xcode 提示啥?

cannot be opened because it is in a future Xcode project file format. Adjust the project format using a compatible version of Xcode to allow it to be opened by this version of Xcode.
应该是你可以调整下兼容
推荐
爱飞的猫 发表于 2024-1-14 22:54
本帖最后由 爱飞的猫 于 2024-1-14 22:56 编辑

图好像挂了

不如说火狐报告图床的域名使用的证书已经被吊销了,错误码 SEC_ERROR_REVOKED_CERTIFICATE

3#
 楼主| Vvvvvoid 发表于 2024-1-14 23:01 |楼主
爱飞的猫 发表于 2024-1-14 22:54
[md]图好像挂了

不如说火狐报告图床的域名使用的证书已经被吊销了,错误码 `SEC_ERROR_REVOKED_CERTIF ...

好的, 换了个图床

免费评分

参与人数 1吾爱币 +1 收起 理由
爱飞的猫 + 1 其实也可以直接用论坛附件贴图的… 图床也行吧

查看全部评分

4#
Hmily 发表于 2024-1-15 00:07
确实,建议直接用论坛附件,比图床方便稳定。
5#
xixicoco 发表于 2024-1-15 02:19
很厉害的样子,多谢了
6#
我是流氓 发表于 2024-1-15 09:11
太厉害了吧,大佬
7#
justfavor 发表于 2024-1-15 10:57
感谢大佬,大老牛逼
8#
Bruce_HD 发表于 2024-1-15 12:12
来看看,来瞧瞧。
9#
风之子martin 发表于 2024-1-15 14:08
感谢楼主整理分享,有时间学习下
10#
judong0x1 发表于 2024-1-16 09:51
楼主用的什么版本的Xcode啊 我13.3打不开。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 00:00

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表