吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2916|回复: 28
收起左侧

[MacOS逆向] Transmit for mac x86/arm dylib 补丁注入通杀

  [复制链接]
Vvvvvoid 发表于 2024-3-14 18:58
本帖最后由 Vvvvvoid 于 2024-3-17 16:22 编辑

关于

Transmit 是一种功能强大的FTP/SFTP/WebDAV客户端软件,是一个Mac OS X平台上设计的文件传输软件。
它由Panic(一家以软件工具为主的公司)开发和维护,是一款非常受欢迎且易于使用的软件,而且被广泛认为是Mac OS X平台上最好的文件传输客户端之一。

今天我们来基于 dylib 注入来搞一下。

环境/软件

分析

进入程序后, 右上角有使用弹框;  >>>> 还剩 x 天。立即购买!

1.png

全局搜索扫描目录, 发现是 PanicAppKit 程序下的引用的字符串

/Applications/Transmit.app/Contents/Frameworks/PanicAppKit.framework/Versions/A/PanicAppKit

// PCTitleBarTrialView
"Expired. Buy now! " = "已过期。立即购买! ";
"1 day left. Buy now! " = "还剩 1 天。立即购买! ";
"%lu days left. Buy now! " = "还剩 %lu 天。立即购买! ";

__cfstring:00000000000D6598                 dq offset aLuDaysLeftBuyN ; "%lu days left. Buy now! "
__cstring:00000000000B2205 aLuDaysLeftBuyN db '%lu days left. Buy now! ',0

但是查看不到有具体的方法引用, 这个时候偶然看到有一个注释 “PCTitleBarTrialView”, 猜测这几个字断都是该类下的;  

可以看到  PanicAppKit 中 export 导出了 “PCTitleBarTrialView” , 并且主程序中也 import 导入了该类
export.png
import.png

在主程序中搜索 “PCTitleBarTrialView”, 并且查看引用, 追踪到了这个函数 -[TRDocument updateCountdownView]

__text:00000001000DDED8 loc_1000DDED8:                          ; CODE XREF: -[TRDocument updateCountdownView]+1C↑j
__text:00000001000DDED8                                         ; -[TRDocument updateCountdownView]+21↑j
__text:00000001000DDED8                 mov     r13, cs:selRef_trialTitleBarViewController
__text:00000001000DDEDF                 mov     rdi, rbx        ; id
__text:00000001000DDEE2                 mov     rsi, r13        ; SEL
__text:00000001000DDEE5                 call    cs:_objc_msgSend_ptr
__text:00000001000DDEEB                 mov     rdi, rax        ; id
__text:00000001000DDEEE                 call    _objc_retainAutoreleasedReturnValue
__text:00000001000DDEF3                 mov     r12, rax
__text:00000001000DDEF6                 mov     rdi, rax        ; id
__text:00000001000DDEF9                 call    cs:_objc_release_ptr
__text:00000001000DDEFF                 test    r12, r12
__text:00000001000DDF02                 jnz     loc_1000DE072
__text:00000001000DDF08                 mov     rdi, cs:classRef_PCTitleBarTrialViewController ; Class

查看假码, 这里可以清晰的看到通过 TRTrialStatus 函数获取试用的剩余天数,之后调用 PCTitleBarTrialView 来显示;  

void __cdecl -[TRDocument updateCountdownView](TRDocument *self, SEL a2)
{
  unsigned int v3; // r15d
  int v4; // eax
  id v5; // rax
  id v6; // r12
  id v7; // rax
  id v8; // r12
  id v9; // rax
  id v10; // r12
  id v11; // rax
  id v12; // r13
  id v13; // rax
  id v14; // rax
  id v15; // r13
  id v16; // rax
  id v17; // r13
  id v18; // rax
  id v19; // r15
  void *v20; // rdi
  id v21; // rax
  id v22; // rbx
  id v23; // rax
  id v24; // r15
  id v25; // rax
  id v26; // r14
  id v27; // [rsp+0h] [rbp-30h]

  v3 = 0;
  v4 = TRTrialStatus(self, a2);
  if ( v4 == -5 || v4 == -1 || v4 != 9999 && (v3 = v4, v4 > 0) )
  {
    v5 = objc_msgSend(self, "trialTitleBarViewController");
    v6 = objc_retainAutoreleasedReturnValue(v5);
    objc_release(v6);
    if ( !v6 )
    {
      v7 = objc_alloc(&OBJC_CLASS___PCTitleBarTrialViewController);
      v8 = objc_msgSend(v7, "initWithTrialDaysLeft:", v3);
      objc_msgSend(self, "setTrialTitleBarViewController:", v8);
      objc_release(v8);
      v9 = objc_msgSend(self, "trialTitleBarViewController");
      v10 = objc_retainAutoreleasedReturnValue(v9);
      objc_msgSend(v10, "setWarningThreshold:", 5LL);
      objc_release(v10);
      v11 = objc_msgSend(self, "trialTitleBarViewController");
      v12 = objc_retainAutoreleasedReturnValue(v11);
      v13 = objc_msgSend(v12, "view");
      v27 = objc_retainAutoreleasedReturnValue(v13);
      objc_release(v12);
      v14 = objc_msgSend(NSApp, "delegate");
      v15 = objc_retainAutoreleasedReturnValue(v14);
      objc_msgSend(v27, "setTarget:", v15);
      objc_release(v15);
      objc_msgSend(v27, "setAction:", "showRegistrationWindow:");
      v16 = objc_msgSend(self, "window");
      v17 = objc_retainAutoreleasedReturnValue(v16);
      v18 = objc_msgSend(self, "trialTitleBarViewController");
      LODWORD(v10) = v3;
      v19 = objc_retainAutoreleasedReturnValue(v18);
      objc_msgSend(v17, "addTitlebarAccessoryViewController:", v19);
      v20 = v19;
      v3 = (unsigned int)v10;
      objc_release(v20);
      objc_release(v17);
      objc_release(v27);
    }
    v21 = objc_msgSend(self, "trialTitleBarViewController");
    v22 = objc_retainAutoreleasedReturnValue(v21);
    objc_msgSend(v22, "setDaysLeft:", v3);
    objc_release(v22);
  }
  else
  {
    v23 = objc_msgSend(self, "trialTitleBarViewController");
    v24 = objc_retainAutoreleasedReturnValue(v23);
    objc_release(v24);
    if ( v24 )
    {
      v25 = objc_msgSend(self, "trialTitleBarViewController");
      v26 = objc_retainAutoreleasedReturnValue(v25);
      objc_msgSend(v26, "removeFromParentViewController");
      objc_release(v26);
      objc_msgSend(self, "setTrialTitleBarViewController:", 0LL);
    }
  }
}

__text:00000001000DDEB3 call    _TRTrialStatus
__text:00000001000DDEB8 cmp     eax, 0FFFFFFFBh
__text:00000001000DDEBB jz      short loc_1000DDED8

这块有一个判断 如果 天数为 9999, 则不会执行, 我么通过修改 eax , 即 函数 TRTrialStatus 返回值来验证下

eax.png

9999的 16 进制为 0x270f, 修改 eax 后可以看到成功去除了 弹框显示, 这样我们就找到关键点了, 就可以打补丁了;
接下来我们来 hook TRTrialStatus 函数;  

int (*hook_TRTrialStatus_ori)(void);

int hook_TRTrialStatus(void){
    return 9999;
};

// 根据特征吗来获取函数地址,之后用 dobby 来 hook
#if defined(__arm64__) || defined(__aarch64__)
    NSString *searchMachineCode = @"F6 57 BD A9 F4 4F 01 A9 FD 7B 02 A9 FD 83 00 91 15 0C ?? ?? B5 72 ?? ?? A0 02 40 F9";
#elif defined(__x86_64__)
    NSString *searchMachineCode = @"55 48 89 E5 41 57 41 56 41 55 41 54 53 50 4C 8B ?? ?? ?? ?? ?? 49 8B 3E";
#endif

// 通常来讲, _dyld_get_image_header 获取base 地址的时候 默认获取第1个 image 即为主程序的, 
// 但是 Transmit 在debug的情况下的主程序会排在了后面呢...,所以这块用 imageName 来获取
int imageIndex = [MemoryUtils indexForImageWithName:@"Transmit"];
intptr_t _hook_TRTrialStatus = [MemoryUtils getPtrFromGlobalOffset:imageIndex targetFunctionOffset:(uintptr_t)globalOffset reduceOffset:(uintptr_t)fileOffset];    
DobbyHook((void *)_hook_TRTrialStatus, (void *)hook_TRTrialStatus, (void *)&hook_TRTrialStatus_ori);

之后打好补丁, 重启, 发现有一个检查更新的报错;  

error.png

全局扫描搜索, 发现是程序 Sparkle 中的,

sparkle.png

/Applications/Transmit.app/Contents/Frameworks/Sparkle.framework/Versions/B/Sparkle
000000000003b354         db         "Unable to Check For Updates", 0            ; DATA XREF=cfstring_Unable_to_Check_For_Updates

查看引用, 最终定位到了函数 SPUStandardUpdaterController startUpdater 中, 这里就不细看更新失败的原因了;  

startUpdater.png

这块因为是 objc 的原生函数, 可以直接用 method_exchangeImplementations 机制来 hook

-[SPUStandardUpdaterController startUpdater]:

- (void)hk_startUpdater {
    NSLog(@">>>>>> Swizzled hk_startUpdater method called");
    NSLog(@">>>>>> self.className : %@", self.className);

}

- (void)hk_terminateExpiredTrialTimerDidFire:(id)arg1  {
    NSLog(@">>>>>> Swizzled hk_terminateExpiredTrialTimerDidFire method called");
    NSLog(@">>>>>> self.className : %@", self.className);
}
[MemoryUtils hookInstanceMethod:
            objc_getClass("SPUStandardUpdaterController")
            originalSelector:NSSelectorFromString(@"startUpdater")
            swizzledClass:[self class]
            swizzledSelector:NSSelectorFromString(@"hk_startUpdater")

];
[MemoryUtils hookInstanceMethod:
            objc_getClass("TransmitDelegate")
            originalSelector:NSSelectorFromString(@"terminateExpiredTrialTimerDidFire:")
            swizzledClass:[self class]
            swizzledSelector:NSSelectorFromString(@"hk_terminateExpiredTrialTimerDidFire:")
];

之后打好补丁,重启, 拿下

功能基本拿下, 接下来乘热打铁, hook license

license1.png

由 license 窗口的 关键字 PCLicense 定位到了 showLicense 函数

license2.png

查看 showLicense 函数假码,下面有个关键地方

rax = [NSUserDefaults standardUserDefaults];
rax = [rax retain];
rbx = rax;
r15 = [[rax stringForKey:@"RegistrationUsername"] retain];
rax = [r14 licenseWindowController];
rax = [rax retain];
[rax setName:r15];
[rax release];
[r15 release];
[rbx release];
rax = [NSUserDefaults standardUserDefaults];
rax = [rax retain];
var_30 = [[rax stringForKey:@"RegistrationDate"] retain];
[rax release];
if (var_30 != 0x0) {
        r12 = objc_alloc_init(@class(NSDateFormatter));
        rax = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
        rax = [rax retain];
        [r12 setTimeZone:rax];
        [rax release];
        [r12 setDateFormat:@"yyyy-MM-dd HH:mm:ss Z"];
        rbx = [[r12 dateFromString:var_30] retain];
        rax = [r14 licenseWindowController];
        rax = [rax retain];
        [rax setRegistrationDate:rbx];
        [rax release];
        [rbx release];
        [r12 release];
}

得出结论, licnese 信息存在 standardUserDefaults文件中,
我们的 dylib 加载后直接写入即可

NSUserDefaults *defaults  = [NSUserDefaults standardUserDefaults];
[defaults setObject:@"marlkiller@xxx.xxx" forKey:@"RegistrationUsername"];
[defaults setObject:@"2099-04-11 13:30:45 GMT" forKey:@"RegistrationDate"];
[defaults synchronize];

license33.png

Quick Start

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

git clone https://github.com/marlkiller/dylib_dobby_hook.git 
cd script 
sudo sh auto_hack.sh

About ME

失业俩个月了。
北京/深圳/成都 JAVA 求职 QAQ

免费评分

参与人数 10威望 +1 吾爱币 +31 热心值 +9 收起 理由
笙若 + 1 + 1 谢谢@Thanks!
eijop252023 + 1 我很赞同!
allspark + 1 + 1 用心讨论,共获提升!
smile1110 + 3 + 1 棒了
Issacclark1 + 1 谢谢@Thanks!
zengsipei + 1 + 1 热心回复!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
抱薪风雪雾 + 1 + 1 谢谢@Thanks!
zhczf + 1 + 1 我很赞同!
qq87700906 + 2 + 1 谢谢@Thanks!

查看全部评分

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

geyihan 发表于 2024-3-14 20:01
我前端现在都干保安了

点评

干保安, 少走10年弯路呀  发表于 2024-3-14 20:09
kmboysky 发表于 2024-3-14 20:45
头像被屏蔽
5201813 发表于 2024-3-14 21:11
lduml 发表于 2024-3-15 11:26
感谢分享
抱薪风雪雾 发表于 2024-3-15 11:47
geyihan 发表于 2024-3-14 20:01
我前端现在都干保安了

没看懂,是种软件吗
yl060814 发表于 2024-3-15 12:55
感谢分享
cp8086 发表于 2024-3-15 15:09
学习了,mark一下
spd97 发表于 2024-3-15 16:48
大佬还是很牛的,吓的我都不敢离职了
 楼主| Vvvvvoid 发表于 2024-3-15 17:21
spd97 发表于 2024-3-15 16:48
大佬还是很牛的,吓的我都不敢离职了

裁员是很难避免的 T.T
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-22 21:40

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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