Vvvvvoid 发表于 2024-3-14 18:58

Transmit for mac x86/arm dylib 补丁注入通杀

本帖最后由 Vvvvvoid 于 2024-3-17 16:22 编辑

## 关于

- Transmit (https://www.panic.com/transmit/#download)

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

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



## 环境/软件
- MacOS arm/x8
- IDA/Hopper
- xcode
    - 注入工具: https://github.com/tyilo/insert_dylib
    - hook 框架: https://github.com/jmpews/Dobby

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



全局搜索扫描目录, 发现是 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 导入了该类



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

```
__text:00000001000DDED8 loc_1000DDED8:                        ; CODE XREF: -+1C↑j
__text:00000001000DDED8                                       ; -+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 *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; //

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 返回值来验证下



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 = ;
intptr_t _hook_TRTrialStatus = ;   
DobbyHook((void *)_hook_TRTrialStatus, (void *)hook_TRTrialStatus, (void *)&hook_TRTrialStatus_ori);
```

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



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



```
/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 中, 这里就不细看更新失败的原因了;



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

```
-:

- (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:
            swizzledSelector:NSSelectorFromString(@"hk_startUpdater")

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



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

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




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




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

```
rax = ;
rax = ;
rbx = rax;
r15 = [ retain];
rax = ;
rax = ;
;
;
;
;
rax = ;
rax = ;
var_30 = [ retain];
;
if (var_30 != 0x0) {
      r12 = objc_alloc_init(@class(NSDateFormatter));
      rax = ;
      rax = ;
      ;
      ;
      ;
      rbx = [ retain];
      rax = ;
      rax = ;
      ;
      ;
      ;
      ;
}

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

```
NSUserDefaults *defaults= ;
;
;
;
```



## 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

geyihan 发表于 2024-3-14 20:01

我前端现在都干保安了

cdwbdq 发表于 2024-3-14 20:41

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

干保安, 少走10年弯路;www{:1_923:}

kmboysky 发表于 2024-3-14 20:45

感谢楼主分享

Jin87 发表于 2024-3-14 20:45

感谢分享!

5201813 发表于 2024-3-14 21:11

为什么不支持WINDOWS,非得让我用chfsgui

lduml 发表于 2024-3-15 11:26

感谢分享{:1_921:}

抱薪风雪雾 发表于 2024-3-15 11:47

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

没看懂,是种软件吗

yl060814 发表于 2024-3-15 12:55

感谢分享

cp8086 发表于 2024-3-15 15:09

学习了,mark一下
页: [1] 2 3
查看完整版本: Transmit for mac x86/arm dylib 补丁注入通杀