首页
论坛
课程
招聘
[原创]【MacOS破解】破解市场屏保区Top1的软件
2021-12-16 17:05 41935

[原创]【MacOS破解】破解市场屏保区Top1的软件

2021-12-16 17:05
41935


PS:Mac区的帖子太少了。


我最近下载了一个屏保软件,据说Mac屏保软件中排行Top 1的,最重要素材真的很好,

可惜需要付费才能下载更多素材。

如图,(支持正版,本文章仅供学习交流使用)点击下载会弹出购买窗口,

因为我已经破解完成没办法打开购买窗口,所以不能截图了。


界面功能比较简单,一个付费、一个素材库列表,未购买下载素材会提示付费。

所需工具:

1. HopperDisassember

2. Frida

3. class-dump

分析篇:

整体思路:

为了保证软件响应速度,一般来讲会员状态都会写入在本地,假如我们能找到软件写用户状态的地址,就可以完成复购校验了。

UI分析:

支付窗口有两个按钮,新用户购买和老用户恢复购买,这两个都可以作为我们的切入点,我选择的是”恢复购买“作为切入点,找到软件的二进制文件,所在路径:

/Applications/Live Wallpaper.app/Contents/MacOS/Live Wallpaper

预先通过class-dump -H Live Wallpaper  生成头文件,以备不时之需。

拖入到HopperDisassember中,用采用字符串搜索,"恢复购买“,发现找不到,猜测该软件采用了本地语言包的方式,进入到资源目录:/Applications/Live Wallpaper.app/Contents/Resources,

本地有一个文件:zh-Hans.lproj,通过名字判断出这是汉化包,进入到页面搜索:find ./ -name '*.strings' -print|xargs grep '恢复购买'

汉化包连类名都我们标记出来。

恢复的英文是:restore,这个函数应该是恢复购买的逻辑了,点击进去


查看反汇编代码:

 -[SiShiPurchaseWindowController restorePurchaseAction]:
000000010002650c         push       rbp                                         ; Objective C Implementation defined at 0x1001137f0 (instance method), Begin of try block, DATA XREF=0x1001137f0
000000010002650d         mov        rbp, rsp
0000000100026510         push       r15
0000000100026512         push       r14
0000000100026514         push       r13
0000000100026516         push       r12
0000000100026518         push       rbx
0000000100026519         sub        rsp, 0x58
// 调用 indicatorView方法,不重要
000000010002651d         mov        r14, rdi
0000000100026520         mov        rsi, qword [0x100143648]                    ; argument "selector" for method _objc_msgSend, @selector(indicatorView)
0000000100026527         mov        r15, qword [_objc_msgSend_1000f4360]        ; _objc_msgSend_1000f4360
000000010002652e         call       r15                                         ; Jumps to 0x100174ec0 (_objc_msgSend), _objc_msgSend
0000000100026531         mov        rdi, rax                                    ; argument "instance" for method imp___stubs__objc_retainAutoreleasedReturnValue
0000000100026534         call       imp___stubs__objc_retainAutoreleasedReturnValue ; objc_retainAutoreleasedReturnValue
//调用 setHidden方法,不重要
0000000100026539         mov        rbx, rax
000000010002653c         mov        rsi, qword [0x100142210]                    ; argument "selector" for method _objc_msgSend, @selector(setHidden:)
0000000100026543         mov        rdi, rax                                    ; argument "instance" for method _objc_msgSend
0000000100026546         xor        edx, edx
0000000100026548         call       r15                                         ; Jumps to 0x100174ec0 (_objc_msgSend), _objc_msgSend
000000010002654b         mov        rdi, rbx                                    ; argument "instance" for method _objc_release
000000010002654e         call       qword [_objc_release_1000f4368]             ; _objc_release, _objc_release_1000f4368,_objc_release
0000000100026554         lea        rdi, qword [rbp+var_30]                     ; argument "addr" for method imp___stubs__objc_initWeak
0000000100026558         mov        rsi, r14                                    ; argument "value" for method imp___stubs__objc_initWeak
000000010002655b         call       imp___stubs__objc_initWeak                  ; objc_initWeak
//划重点,SiShiPurchaseHelper这个类
0000000100026560         mov        rdi, qword [objc_cls_ref_SiShiPurchaseHelper] ; argument "instance" for method _objc_msgSend, objc_cls_ref_SiShiPurchaseHelper
0000000100026567         mov        rsi, qword [0x100141dd8]                    ; argument "selector" for method _objc_msgSend, @selector(sharedInstance)
000000010002656e         call       r15                                         ; End of try block started at 0x10002650c, Begin of try block (catch block at 0x10002664c), Jumps to 0x100174ec0 (_objc_msgSend), _objc_msgSend
0000000100026571         mov        rdi, rax                                    ; End of try block started at 0x10002656e, Begin of try block, argument "instance" for method imp___stubs__objc_retainAutoreleasedReturnValue
0000000100026574         call       imp___stubs__objc_retainAutoreleasedReturnValue ; objc_retainAutoreleasedReturnValue
0000000100026579         mov        r15, rax
000000010002657c         mov        rax, qword [__NSConcreteStackBlock_1000f41b8] ; __NSConcreteStackBlock_1000f41b8
0000000100026583         lea        r14, qword [rbp+var_60]
0000000100026587         mov        qword [r14-0x20], rax
000000010002658b         mov        r13d, 0xc2000000
0000000100026591         mov        qword [r14-0x18], r13
0000000100026595         lea        rax, qword [sub_100026660]                  ; sub_100026660
000000010002659c         mov        qword [r14-0x10], rax
00000001000265a0         lea        rax, qword [0x1000f4e28]                    ; 0x1000f4e28
00000001000265a7         mov        qword [r14-8], rax
00000001000265ab         lea        r12, qword [rbp+var_30]
00000001000265af         mov        rdi, r14                                    ; argument "dest" for method imp___stubs__objc_copyWeak
00000001000265b2         mov        rsi, r12                                    ; argument "src" for method imp___stubs__objc_copyWeak
00000001000265b5         call       imp___stubs__objc_copyWeak                  ; objc_copyWeak
00000001000265ba         lea        rbx, qword [rbp+var_38]
00000001000265be         mov        rax, qword [__NSConcreteStackBlock_1000f41b8] ; __NSConcreteStackBlock_1000f41b8
00000001000265c5         mov        qword [rbx-0x20], rax
00000001000265c9         mov        qword [rbx-0x18], r13
00000001000265cd         lea        rax, qword [sub_100026691]                  ; sub_100026691
00000001000265d4         mov        qword [rbx-0x10], rax
00000001000265d8         lea        rax, qword [0x1000f4ef8]                    ; 0x1000f4ef8
00000001000265df         mov        qword [rbx-8], rax
00000001000265e3         mov        rdi, rbx                                    ; argument "dest" for method imp___stubs__objc_copyWeak
00000001000265e6         mov        rsi, r12                                    ; argument "src" for method imp___stubs__objc_copyWeak
00000001000265e9         call       imp___stubs__objc_copyWeak                  ; objc_copyWeak
//重点是这个方法 startRestore
00000001000265ee         mov        rsi, qword [0x100143720]                    ; argument "selector" for method _objc_msgSend, @selector(startRestore:failedBlock:)
00000001000265f5         lea        rdx, qword [rbp+var_80]                     ; End of try block started at 0x100026571, Begin of try block (catch block at 0x100026637)
00000001000265f9         lea        rcx, qword [rbp+var_58]
00000001000265fd         mov        rdi, r15                                    ; argument "instance" for method _objc_msgSend
0000000100026600         call       qword [_objc_msgSend_1000f4360]             ; _objc_msgSend, _objc_msgSend_1000f4360,_objc_msgSend
0000000100026606         mov        rdi, r15                                    ; End of try block started at 0x1000265f5, Begin of try block, argument "instance" for method _objc_release
0000000100026609         call       qword [_objc_release_1000f4368]             ; _objc_release, _objc_release_1000f4368,_objc_release
000000010002660f         mov        rdi, rbx                                    ; argument "instance" for method imp___stubs__objc_destroyWeak
0000000100026612         call       imp___stubs__objc_destroyWeak               ; objc_destroyWeak
0000000100026617         mov        rdi, r14                                    ; argument "instance" for method imp___stubs__objc_destroyWeak
000000010002661a         call       imp___stubs__objc_destroyWeak               ; objc_destroyWeak
000000010002661f         lea        rdi, qword [rbp+var_30]                     ; argument "instance" for method imp___stubs__objc_destroyWeak
0000000100026623         call       imp___stubs__objc_destroyWeak               ; objc_destroyWeak
0000000100026628         add        rsp, 0x58
000000010002662c         pop        rbx
000000010002662d         pop        r12
000000010002662f         pop        r13
0000000100026631         pop        r14
0000000100026633         pop        r15
0000000100026635         pop        rbp
0000000100026636         ret

通过汇编代码,知道重点在SiShiPurchaseHelper:startRestore 这个方法中,二话不说,进入到代码区,上面的汇编代码晦涩难懂,HopperDisassmber可以给我们生成伪代码,位置如下图:

这样就直观多了:

/* @class SiShiPurchaseHelper */
-(void)startRestore:(void *)arg2 failedBlock:(void *)arg3 {
    r12 = [arg3 retain];
    rbx = [arg2 retain];
    [self setStartPurchase:0x1];
    [self setCompeletedBlock:rbx];
    [rbx release];
    [self setFailedBlock:r12];
    [r12 release];
    rax = [SKPaymentQueue defaultQueue];
    rax = [rax retain];
    [rax restoreCompletedTransactions];
    [rax release];
    return;
}

这里发现代码没办法跟进去了,通过查阅资料,SKPaymentQueue是一个APL在Mac下支持的lib库用于桌面的支付操作,所以一定有一个Delegate回调方法用于处理支付的校验,怎么找呢?

我们回到刚才生成的头文件文件夹下,执行Linux命令: ls *SK*,意思是查找所有包含SK字符串的头文件名:

OSSPlainTextAKSKPairCredentialProvider.h
SKPaymentTransactionObserver-Protocol.h
SKProductsRequestDelegate-Protocol.h
SKRequestDelegate-Protocol.h

返回的内容如上,很明显,SKPaymentTransactionObserver-Protocol.h就是实现的协议了。

#import "NSObject-Protocol.h"

@class NSArray, NSError, SKPayment, SKPaymentQueue, SKProduct;

@protocol SKPaymentTransactionObserver- (void)paymentQueue:(SKPaymentQueue *)arg1 updatedTransactions:(NSArray *)arg2;

@optional
- (void)paymentQueue:(SKPaymentQueue *)arg1 didRevokeEntitlementsForProductIdentifiers:(
- (void)paymentQueueDidChangeStorefront:(SKPaymentQueue *)arg1;
- (BOOL)paymentQueue:(SKPaymentQueue *)arg1 shouldAddStorePayment:(SKPayment *)arg2 forP
- (void)paymentQueue:(SKPaymentQueue *)arg1 updatedDownloads:(NSArray *)arg2;
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)arg1;
- (void)paymentQueue:(SKPaymentQueue *)arg1 restoreCompletedTransactionsFailedWithError:
- (void)paymentQueue:(SKPaymentQueue *)arg1 removedTransactions:(NSArray *)arg2;
@end它实现了几个回调方法,不管它,在Hopper中搜索 paymentQueue,并生成伪代码:
/* @class SiShiPurchaseHelper */
-(void)paymentQueue:(void *)arg2 updatedTransactions:(void *)arg3 {
    rbx = self;
    rax = [arg3 retain];
    var_150 = intrinsic_movaps(var_150, 0x0);
    *(int128_t *)(&var_150 + 0x10) = intrinsic_movaps(*(int128_t *)(&var_150 + 0x10), 0x0);
    *(int128_t *)(&var_150 + 0x20) = intrinsic_movaps(*(int128_t *)(&var_150 + 0x20), 0x0);
    *(int128_t *)(&var_150 + 0x30) = intrinsic_movaps(*(int128_t *)(&var_150 + 0x30), 0x0);
    var_B8 = rax;
    rax = [rax countByEnumeratingWithState:&var_150 objects:&var_B0 count:0x10];
    var_D8 = rax;
    if (rax != 0x0) {
            var_100 = **(&var_150 + 0x10);
            var_C0 = rbx;
            do {
                    r12 = 0x0;
                    do {
                            if (*var_140 != var_100) {
                                    objc_enumerationMutation(var_B8);
                            }
                            r14 = *(var_148 + r12 * 0x8);
                            rax = [r14 transactionState];
                            if (rax != 0x3) {
                                    if (rax != 0x2) {
                                            if (rax == 0x1) {
                                                    // 关键位置
                                                    [rbx completeTransaction:r14];
                                            }
                                    }
                                    else {
                                            rax = [r14 error];
                                            rax = [rax retain];
                                            r14 = [rax code];
                                            [rax release];
                                            if (r14 == 0x2) {
                                                    rbx = var_C0;
                                                    [rbx purchaseFailedWithError:0x0];
                                            }
                                            else {
                                                    rbx = var_C0;
                                                    [rbx purchaseFailedWithError:[[[[NSBundle mainBundle] retain] localizedStringForKey:@"Unlock failed" value:@"" table:0x0] retain]];
                                                    [rax release];
                                                    [rax release];
                                            }
                                    }
                            }
                            else {
                                    [rbx completeTransaction:r14];
                            }
                            r12 = r12 + 0x1;
                    } while (r12 < var_D8);
                    rax = [var_B8 countByEnumeratingWithState:&var_150 objects:&var_B0 count:0x10];
                    var_D8 = rax;
            } while (rax != 0x0);
    }
    var_30 = **___stack_chk_guard;
    [var_B8 release];
    if (**___stack_chk_guard != var_30) {
            __stack_chk_fail();
    }
    return;
}

它实现了几个回调方法,不管它,在Hopper中搜索 completeTransaction,并生成伪代码:

/* @class SiShiPurchaseHelper */
-(void)completeTransaction:(void *)arg2 {
    r14 = self;
    rax = [arg2 retain];
    r15 = rax;
    rax = [rax payment];
    rax = [rax retain];
    r12 = rax;
    rax = [rax productIdentifier];
    rax = [rax retain];
    [rax release];
    [r12 release];
    if (rax != 0x0) {
            [r14 setCurrentTransaction:r15];
            //关键方法:
            [r14 purchaseSuccess];
            [r14 bornWenYuShan];
    }
    [r15 release];
    return;
}

找到两个疑似关键方法:

[r14 purchaseSuccess];
[r14 bornWenYuShan];

经过阅读代码,找到最终设置用户身份的函数bornWenYuShan

/* @class SiShiPurchaseHelper */
-(void)bornWenYuShan {
    [self setIsVip:0x1];
    rax = [NSUserDefaults standardUserDefaults];
    rax = [rax retain];
    [rax setBool:0x1 forKey:@"kSiShiIsVipString"];
    [rax release];
    rbx = [[NSNotificationCenter defaultCenter] retain];
    [rbx postNotificationName:*0x1000f52b0 object:0x0];
    [rbx release];
    return;
}

了解APL开发的童鞋都知道,[NSUserDefaults standardUserDefaults]是保存用户信息存储的接口。

[rax setBool:0x1 forKey:@"kSiShiIsVipString"];

写入值为YES。

经过以上分析,只要能写入这个代码重启软件应该就可以实现付费破解了。

整理思路:

如果我们要实现 completeTransaction 的调用,前面要修改多个if校验的逻辑,实在是麻烦,有没有简单有效的办法呢?

既然找到了 [SiShiPurchaseHelper bornWenYuShan]关键函数,那我们在点击”恢复购买“的按钮时直接执行它不就可以了,省去了多处的修改也易于操作和验证。

回到函数[SiShiPurchaseHelper startRestore:failedBlock:]位置:

掐头去尾只保留开始的入栈和出栈部分,也就是:

0000000100012910         push       rbp                                         ; Objective C Implementation defined at 0x10010f4d0 (instance method), DATA XREF=0x10010f4d0
0000000100012911         mov        rbp, rsp
0000000100012914         push       r15
0000000100012916         push       r14
0000000100012918         push       r13
000000010001291a         push       r12
000000010001291c         push       rbx
000000010001291d         push       rax

......中间的代码全部NOP掉

00000001000129bd         pop        rbx
00000001000129be         pop        r12
00000001000129c0         pop        r13
00000001000129c2         pop        r14
00000001000129c4         pop        r15
00000001000129c6         pop        rbp
00000001000129c7         jmp        rax

NOP后,在第九行编写代码片,上面都加了注释便于理解

// r14 = self
000000010001291e         mov        r14, rdi
// rbx = _objc_msgSend_1000f4360
0000000100012921         mov        rbx, qword [_objc_msgSend_1000f4360]    
// msgSend函数的第一个参数:rsi = bornWenYuShan    
0000000100012928         mov        rsi, qword [0x100142d48]    
// msgSend函数的第二个参数:rdi = r14                
000000010001292f         mov        rdi, r14                                    
0000000100012932         call       rbx   
//等于执行 msgSend(self,bornWenYuShan)

最终代码如图:

调用Hopper生成可执行文件覆盖源文件/Applications/Live Wallpaper.app/Contents/MacOS/Live Wallpaper 即可。

最后我们退出软件后发现打不开了?是代码改的有问题吗?并不是。

因为APL的所有软件必须签名,我们导出的的二进制文件必须要重新签名才可以执行,进入到/Applications/目录

执行以下命令进行签名即可:

sudo codesign --sign - --force --deep ./Live Wallpaper.app

有关签名的内容大家可自行Google脑补。

效果:首次安装后,在支付窗口点击“恢复购买” 重开软件,发现购买窗口没有了,随便选一个素材点击即可直接下载使用。

开发者不易,支持正版!




【看雪培训】目录重大更新!《安卓高级研修班》2022年春季班开始招生!

最后于 2021-12-22 10:46 被Mr.梵高编辑 ,原因: 修改部分文字错误
收藏
点赞7
打赏
分享
最新回复 (8)
雪    币: 162
活跃值: 活跃值 (65)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
wyfe 活跃值 2021-12-16 20:49
2
0
Mac区的破解文章少,学习一下
雪    币: 1558
活跃值: 活跃值 (1009)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zylrocket 活跃值 2021-12-17 01:23
3
0
看下私信呀,哥们!
雪    币: 415
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
micro_zhou 活跃值 2021-12-17 09:48
4
0
虽然看不懂,但是觉得很厉害!
雪    币: 648
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
OnlyThen 活跃值 2021-12-21 17:17
5
0
太欺负人了,10几块的软件也要搞一下
雪    币: 661
活跃值: 活跃值 (682)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
qyc 活跃值 4 2021-12-22 11:36
6
0

兄地,简单的问题,被你复杂化了

大道至简 

下载附件自行 otool 注入并 codesign 签名,支持M1

最后于 2021-12-22 14:34 被qyc编辑 ,原因: 增加附件
上传的附件:
雪    币: 213
活跃值: 活跃值 (226)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mr.梵高 活跃值 2021-12-23 14:00
7
0
qyc 兄地,简简单单的问题,被你复杂化了大道至简&nbsp;下载附件自行&nbsp;otool 注入并&nbsp;codesign&nbsp;签名,支持M1
哈哈,我主要是学习为主
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
chenxia0 活跃值 2021-12-27 09:54
8
0
app 名字叫啥?
雪    币: 272
活跃值: 活跃值 (202)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
o冰柠檬o 活跃值 2022-3-9 17:43
9
0
楼主能出一个动态调试的教程吗?
游客
登录 | 注册 方可回帖
返回