Proxyman for mac lldb 调试 dylib 注入
本帖最后由 Vvvvvoid 于 2024-4-12 18:08 编辑## 关于
> Proxyman (Version 5.1.1 (50101)) (https://proxyman.io/)
Proxyman是一款专为macOS设计的现代本地Web调试代{过}{滤}理工具,它不仅支持macOS平台,还能无缝地与iOS和Android设备进行集成。作为一个网络调试工具,Proxyman的设计旨在提供高性能、直观且功能丰富的解决方案,使开发者能够深入网络层进行调试和问题解决。
## 环境/软件
- MacOS
- IDA/Hopper
- xcode
-注入工具: https://github.com/tyilo/insert_dylib
## 分析
免费版有诸多限制; 比如 maping 映射规则只能有一个;
从关键字入手 "您已达到免费版可用规则的上";
全局定位到主程序下 (/Applications/Proxyman.app/Contents/Resources/zh-Hans.lproj/Localizable.strings):
```
/* No comment provided by engineer. */
"You have reached the limit of free rules\n(Limit at %d)" = "您已达到免费版可用规则的上限\\n(限制在 %d 处)";
```
主程序 bin 拖入 hopper 中搜索
```
aYouHaveReached_100325010: // aYouHaveReached
0000000100325010 db "You have reached the limit of free rules\n(Limit at %d)", 0
```
x 查看引用链
只要一个引用,进入函数
```
0000000100135cd0 lea rsi, qword ; 0x100325030
```
然后 F5, 查看假码,看不出来啥..
在 hopper中 往上看看, 找到函数首
```
sub_100135551:
0000000100135551 cmp qword , 0x2 ; CODE XREF=sub_100134540+685, sub_100134540+694
0000000100135556 jne loc_1001358a2
000000010013555c test rax, rax
000000010013555f jne loc_1001358a2
```
用 lldb 调试看看
lldb "/Applications/Proxyman.app"
载入后, r 运行;
程序启动后 ctrl + c ,暂停 , 之后下断点 br s -a 0x100135551
然后反复复现创建rule规则, 发现断不下来... 有点奇怪;
我们我们在 lldb 中 dis -a 0x100135551 , lldb 的查看汇编代码似乎比 hopper 的高级呢,找到了真实的函数首;
```
(lldb) dis -a 0x100135551
Proxyman`___lldb_unnamed_symbol9700:
Proxyman <+0>: pushq%rbp
Proxyman <+1>: movq %rsp, %rbp
Proxyman <+4>: pushq%r15
Proxyman <+6>: pushq%r14
Proxyman <+8>: pushq%r13
Proxyman <+10>: pushq%r12
Proxyman <+12>: pushq%rbx
```
接着下一个断点 br s -a 0x1001324b0
```
(lldb) br s -a 0x1001324b0
Breakpoint 1: address = 0x00000001001324b0
```
然后继续创建rule规则, 成功断下来了;
bt 查看堆栈
```
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
* frame #0: 0x00000001001324b0 Proxyman`___lldb_unnamed_symbol9700 // sub_1001324b0 弹框 alert 需要充值
frame #1: 0x00000001001adefb Proxyman`___lldb_unnamed_symbol12544 + 171 //call sub_1001324b0
frame #2: 0x00000001001ad9ef Proxyman`___lldb_unnamed_symbol12534 + 79 // call sub_1001ade50
frame #3: 0x0000000102fb0e6c ProxymanCore`ProxymanCore.runOnMainThread(() -> ()) -> () + 140
frame #4: 0x00000001001ad313 Proxyman`___lldb_unnamed_symbol12530 + 595 // thread
frame #5: 0x0000000100054ae3 Proxyman`___lldb_unnamed_symbol4628 + 99 // call sub_1001ad0c0 cmp 01 , // 需要 ret 0
frame #6: 0x00000001000534ea Proxyman`___lldb_unnamed_symbol4620 + 74
frame #7: 0x00007ff80aaff2b6 AppKit`- + 337
frame #8: 0x00007ff80aaff12b AppKit`- + 86
frame #9: 0x00007ff80aaff05d AppKit`__26-_block_invoke + 131
frame #10: 0x00007ff80aafef66 AppKit`- + 171
frame #11: 0x00007ff80aafeeae AppKit`- + 96
frame #12: 0x00007ff80aafbdd2 AppKit`NSControlTrackMouse + 1823
frame #13: 0x00007ff80aafb68f AppKit`- + 125
frame #14: 0x00007ff80aafb556 AppKit`- + 666
frame #15: 0x00007ff80aafa94b AppKit`- + 666
frame #16: 0x00007ff80aaf92f3 AppKit`- + 4582
frame #17: 0x00007ff80aa720ce AppKit`- + 404
frame #18: 0x00007ff80aa71d1f AppKit`- + 345
frame #19: 0x00007ff80b2212b6 AppKit`- + 346
frame #20: 0x00007ff80addc5c2 AppKit`- + 65
frame #21: 0x00007ff80a90402a AppKit`- + 640
frame #22: 0x00007ff80a8d7ff1 AppKit`NSApplicationMain + 816
frame #23: 0x0000000100067e79 Proxyman`___lldb_unnamed_symbol5090 + 9
frame #24: 0x00007ff806e44366 dyld`start + 1942
```
前面几个 堆栈没看出啥;
看 #5: 0x0000000100054ae3
简单分析一下; 第 11 行 调用 sub_1001ad0c0 函数, rax == 0 的话会show 一个 windoow; 盲猜是配置 rule 详情的 window
```
0000000100054ade call sub_1001ad0c0 ; sub_1001ad0c0
0000000100054ae3 test al, 0x1
0000000100054ae5 je loc_100054af6
```
我们在 0x100054ae3 下一个断点看看; br s -a 0x100054ae3; 然后回到程序,创建规则; 这个时候弹框了, 先不要管;按确定;然后 断下来了;
lldb 查看当前寄存器 al 为 1, 我们修改为 0 看看,然后 c 继续;
```
(lldb) register read al
al = 0x01
(lldb) register write al 0
(lldb) register read al
al = 0x00
(lldb)
```
发现已经成功出现了创建 rule 的页面了; 但是此刻弹框依然存在;
至此,我们可以得出结论 sub_1001ad0c0 有俩个作用
1. 函数里会判断是否弹框 (具体逻辑逻辑)
2. 函数返回值会判断是否创建 rule (根据返回值 ==0 触发)
接着 我们进入函数 sub_1001ad0c0 里看看
qword_10044b170, qword_100466550 这俩个地址 是程序的常量,逻辑判断始终成立;不用管;
函数的返回值取决于 第 14 行
rax = sub_10001af90(&var_D0, &var_128);
int sub_10001af90(int arg0, int arg1) {
(*(*0x1003bc2d8 + 0x20))(arg1, arg0, 0x1003bc2e0);
rax = arg1;
return rax;
}
sub_10001af90 这个函数的代码看着有点抽象, 就不继续看了,盲猜他是搞弹框 alert 的;
我们直接 hook ret 0 看看;
这里采用 dylib 注入来 hook ;
注意不要注入主程序, 可能会导致主程序 签名与 heler 不一致; 而启动不了;
我们去FrameWoork 下挑一个来注入 /Applications/Proxyman.app/Contents/Frameworks/HexFiend.framework/Versions/A/HexFiend
```
DobbyHook((void *)_sub_0x10001af90, ret0, nil);
```
注入后 启动程序,成功注册 (居然 license 给了 Setapp..)
运行了一段时间; 发现还需要处理下remainingDays 这个 函数,有概率程序会报错, 我们把调用的地方写入 mov rax 1 即可;
因为 mov rax 1 比 原生的 call xxx 的机器码长, 所以改成 push 1,pop rax;
call imp___stubs__$s12ProxymanCore7LicenseV13remainingDaysSiSgvg >>>>> push 1,pop rax
```
function $s12ProxymanCore7LicenseV13remainingDaysSiSgvg {
rax = (*pointer to ProxymanCore.License.remainingDays.getter : Swift.Int?)();
return rax;
}
```
//push 1,pop rax
uint8_t ret1Day = {0x6A, 0x01, 0x58, 0x90, 0x90};
DobbyCodePatch((void *)_remainingDaysCode, ret1Day,5);
至此,结束, x64/arm 通杀特征码如下:
```
#if defined(__arm64__) || defined(__aarch64__)
NSString *sub_0x10001af90Code = @"F4 4F BE A9 FD 7B 01 A9 FD 43 00 91 F3 03 01 AA E1 03 00 AA .. .. 00 90 42 C0 20 91 48 80 5F F8 08 11 40 F9 E0 03 13 AA 00 01 3F D6 E0 03 13 AA FD 7B 41 A9 F4 4F C2 A8 C0 03 5F D6";
NSString *remainingDaysCode = @".. .. .. 94 F4 03 00 AA F5 03 01 AA E0 83 01 91 .. .. .. 94 28 00 80 52 89 FE 7F D3 BF 02 00 72 34 01 88 1A";
#elif defined(__x86_64__)
NSString *sub_0x10001af90Code = @"55 48 89 E5 53 50 48 89 F3 48 89 FE 48 8D .. .. .. .. .. 48 8B 42 F8 48 89 DF FF 50 20 48 89 D8 48 83 C4 08 5B 5D C3";
NSString *remainingDaysCode =@"E8 .. .. .. .. 49 89 C6 41 89 D7 48 8D BD C8 FE FF FF E8 .. .. .. .. 41 B4 01 41 F6 C7 01 75 ..";
DobbyHook((void *)_sub_0x10001af90, ret0, nil);
NSLog(@">>>>>> Before %s",.UTF8String);
#if defined(__arm64__) || defined(__aarch64__)
// nop:0x1F, 0x20, 0x03, 0xD5
// mov x0,#1
uint8_t ret1Day = {0x20, 0x00, 0x80, 0xD2};
DobbyCodePatch((void *)_remainingDaysCode, ret1Day,4);
#elif defined(__x86_64__)
//push 1,pop rax
uint8_t ret1Day = {0x6A, 0x01, 0x58, 0x90, 0x90};
DobbyCodePatch((void *)_remainingDaysCode, ret1Day,5);
#endif
NSLog(@">>>>>> After %s",.UTF8String);
#endif
```
## 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 的工作好难呀 , 加上逆向 Surge 搞的我头大, 搞不明白;
我还是找个软柿子捏捏把...
-------------------------------------
Translated Report (Full Report Below)
-------------------------------------
Process: Proxyman
Path: /Applications/Proxyman.app/Contents/MacOS/Proxyman
Identifier: com.proxyman.NSProxy
Version: 5.1.0 (50100)
Code Type: ARM-64 (Native)
Parent Process: launchd
User ID: 501
Date/Time: 2024-04-15 17:13:02.9576 +0800
OS Version: macOS 13.5 (22G74)
Report Version: 12
Anonymous UUID: E265310A-937D-ED2A-E258-FF75A2013911
Time Awake Since Boot: 1100 seconds
System Integrity Protection: disabled
Crashed Thread: 0Dispatch queue: com.apple.main-thread
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: Namespace SIGNAL, Code 6 Abort trap: 6
Terminating Process: Proxyman
Application Specific Information:
abort() called
Application Specific Backtrace 0:
0 CoreFoundation 0x000000019487b154 __exceptionPreprocess + 176
1 libobjc.A.dylib 0x000000019439a4d4 objc_exception_throw + 60
2 CoreFoundation 0x00000001948a81b4 CFArrayApply + 0
3 libdylib_dobby_hook.dylib 0x00000001008cf5dc - + 260
4 libdylib_dobby_hook.dylib 0x00000001008d3204 + + 696
5 libdylib_dobby_hook.dylib 0x00000001008cf9a0 + + 180
6 libobjc.A.dylib 0x000000019438c460 load_images + 828
7 dyld 0x00000001943d9880 _ZN5dyld412RuntimeState14notifyObjCInitEPKNS_6LoaderE + 164
8 dyld 0x00000001943e4c04 _ZNK5dyld46Loader23runInitializersBottomUpERNS_12RuntimeStateERN5dyld35ArrayIPKS0_EE + 208
9 dyld 0x00000001943e4bec _ZNK5dyld46Loader23runInitializersBottomUpERNS_12RuntimeStateERN5dyld35ArrayIPKS0_EE + 184
10dyld 0x00000001943e4bec _ZNK5dyld46Loader23runInitializersBottomUpERNS_12RuntimeStateERN5dyld35ArrayIPKS0_EE + 184
11dyld 0x00000001943e8264 _ZZNK5dyld46Loader38runInitializersBottomUpPlusUpwardLinksERNS_12RuntimeStateEENK3$_1clEv + 112
12dyld 0x00000001943e4d90 _ZNK5dyld46Loader38runInitializersBottomUpPlusUpwardLinksERNS_12RuntimeStateE + 304
13dyld 0x0000000194408984 _ZN5dyld44APIs25runAllInitializersForMainEv + 468
14dyld 0x00000001943cd2d0 _ZN5dyld4L7prepareERNS_4APIsEPKN5dyld313MachOAnalyzerE + 3480
15dyld 0x00000001943cbe18 start + 1964
Vvvvvoid 发表于 2024-4-12 18:12
下的最新版 version 5.1.1,
特征吗应该通杀后续几个版本吧
哥 我上次试了 结果破解完显示
Termination Reason: Namespace DYLD, Code 1 Library missing
Library not loaded: @loader_path/../Frameworks/HexFiend.framework/Versions/A/HexFiend
Referenced from: <C86FB29A-C114-39D0-AACC-4206EB1610BB> /Applications/Proxyman.app/Contents/MacOS/Proxyman
Reason: tried: '/Applications/Proxyman.app/Contents/MacOS/../Frameworks/HexFiend.framework/Versions/A/HexFiend' (code signature in <1B9C64E6-31B9-34E0-9402-50DA49C1ADF7> '/Applications/Proxyman.app/Contents/Frameworks/HexFiend.framework/Versions/A/HexFiend' not valid for use in process: Trying to load an unsigned library)
(terminated at launch; ignore backtrace) 你这家伙大有赶超我的实力,后起之秀,可怕可怕~@Vvvvvoid smile1110 发表于 2024-4-10 13:37
你这家伙大有赶超我的实力,后起之秀,可怕可怕~@Vvvvvoid
最近很闲,正好有空学习😄 最近很闲,正好有空学习 1:注意不要注入主程序, 可能会导致主程序 签名与 heler 不一致; 而启动不了;
我们去FrameWoork 下挑一个来注入 /Applications/Proxyman.app/Contents/Frameworks/HexFiend.framework/Versions/A/HexFiend
2:因为 mov rax 1 比 原生的 call xxx 的机器码长, 所以改成 push 1,pop rax;
牛,学到这两点!{:1_921:} 学习 学习 学习了666666 哈哈 我们总部在长沙 有机会来玩 思路很赞,学习一下 请问是什么版本的proxyman?