吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9117|回复: 18
上一主题 下一主题
收起左侧

[MacOS逆向] Downie分析与暴破

  [复制链接]
跳转到指定楼层
楼主
TLHorse 发表于 2022-2-28 15:16 回帖奖励

这将是寒假中最后一次发帖,从明天开始我就要学习了:D

Downie,全能的网络视频下载软件。在论坛中搜索Downie,一大把一大把的TNT破解出现在眼前。但是求人不如求己,因此本文主要介绍Downie暴破,技术含量不是很大,包含中变量的tweak,和动态库之间交互的分析。

软件分析

打开Downie,在一系列的初始化后,会弹出试用期限窗口,提醒试用倒计时(14天)。操作可知,在试用期限中,软件的所有功能都可以正常使用。点开菜单栏中的Downie 4,可以看到几个关于注册的按钮。


通过我们观察到的元素,可以分析出如下三种逆向方案:

  1. 分析算法写注册机:现在软件都是联网注册,分析算法没啥用;
  2. 修改剩余天数为定值:分析非常简单;
  3. 修改全局已注册变量:也很简单,需同时去除校对验证函数。

本文就介绍二三两种方法吧。

代码分析

拖进Hopper,对于这种应用程序的启动弹窗来说,懂Apple开发的都知道,可以从macOS应用程序的生命周期入手。搜索appDidFinishLaunching可以找到如下符号:

s6Downie13XUAppDelegateC29applicationDidFinishLaunchingyy10Foundation12NotificationVF

函数会在程序启动完成后被调用。在loc_100037546中可以找到一些有关license的代码:

call imp___stubs__$s9Licensing11CMLicensingCMa
mov  r13, rax
call imp___stubs__$s9Licensing11CMLicensingC6sharedACvgZ
mov  rbx, rax
mov  rax, qword [_$s9Licensing11CMLicensingC8delegateAA0B8Delegate_pSgvpWvd_100118910]
mov  qword [rbp+var_30], r14
mov  r12, qword [rax]
lea  r14, qword [rbx+r12]
lea  rsi, qword [rbp+var_E8]
mov  edx, 0x1
mov  rdi, r14
xor  ecx, ecx
call imp___stubs__swift_beginAccess
lea  rax, qword [_$s6Downie13XUAppDelegateC9Licensing011CMLicensingC0AAWP]

随便找一个call跟踪进去,可以看到外部代码,在注释中可看到如下路径:

; in @executable_path/../Frameworks/Licensing.framework/Versions/A/Licensing

我们发现,Downie对license的验证流程原来写到了Licensing附加库中。把它拖到Hopper中,转而分析Licensing!

破解一:修改剩余天数

观察代码风格,很明显Licensing是Swift写的。搜索days这样的字眼,在搜索结果中找getter,很明显可以找到一个Swift整型变量:s9Licensing11CMLicensingC21numberOfDaysRemainingSivg(符号),返回一个int值。在汇编中,把返回寄存器rax改为定值。

mov rax, 0xff
ret


嘿嘿,剩余天数从14天一下飙升到255天。在系统设置里怎么改日期,都不会变!朋友们,破解已经成功!

其实有一个遗憾,每次启动都会弹出这个窗口。我总想把它去掉,但找了许多有关的函数和windowDidLoad方法,很奇怪都破不了,好失望啊。。。

破解二(完美):修改全局注册变量

抱着试试看的心态,搜了islicensed关键词,没想到还真有

s9Licensing11CMLicensingC10isLicensedSbvg

赶快把rax改成0x1,启动调试,仍然未注册(节省空间,此处不贴图)。这说明软件获取注册状态调用的不是这个函数。
到底是哪个环节出了问题?瞟一眼伪代码:

int _$s9Licensing11CMLicensingC10isLicensedSbvg() {
    [sub_11600() release];
    [rdx release];
    r14 = [rcx activated];
    [rcx release];
    rax = r14 != 0x0 ? 0x1 : 0x0;
    return rax;
}

莫名其妙发现了一个名为activated的selector,无交叉引用,无来源。很有可能是外部符号。看看Downie还有什么库:还有Paddle没用呢!(我这破解思维也是没谁了,看到哪个库就搞哪个)

把Paddle拽到Hopper分析,搜索activated,得到结果:

/* @Class PADProduct */
-(char)activated {
    rax = [self licenseController];
    rax = (*(*rax + 0x58))(rax);
    return rax;
}

很标准的类方法,返回char类型,就是它了。打开汇编,rax改为1:

mov rax, 1
ret


很可恶,Downie直接崩溃!但这是个好消息,证明了我们的tweak是成功的,只是被某个检验流程检测到了。别慌,接着找找同类的关键词:

开发者小心思真多!两个verify函数,第一个指向第二个,我们看看0x95c5的代码:

/* @class PADProduct */
-(void)verifyActivationDetailsWithCompletion:(void *)arg2 {
    r13 = self;
    r14 = [arg2 retain];
    rax = [r13 licenseController];
    if (((*(*rax + 0x50))(rax) == 0x0) && ([r13 activated] == 0x0)) {
            r15 = [[PADErrorFactory errorWithCode:0xffffffffffffff94] retain];
            if (r14 != 0x0) {
                    var_88 = *__NSConcreteStackBlock;
                    *(&var_88 + 0x8) = 0xffffffffc2000000;
                    *(&var_88 + 0x10) = ___52-[PADProduct verifyActivationDetailsWithCompletion:]_block_invoke;
                    *(&var_88 + 0x18) = ___block_descriptor_48_ea8_32bs40s_e5_v8?0l;
                    *(&var_88 + 0x20) = [r14 retain];
                    *(&var_88 + 0x28) = [r15 retain];
                    dispatch_async(*__dispatch_main_q, &var_88);
                    [*(&var_88 + 0x28) release];
                    [*(&var_88 + 0x20) release];
            }
            else {
                    [r13 sendErrorToPaddleDelegateIfResponsive:r15];
            }
            rdi = r15;
    }
    else {
            rax = [NSDate date];
            rax = [rax retain];
            [r13 setLastVerifyDate:rax];
            [rax release];
            [r13 saveProductFile];
            r12 = [r13 determineActivationService];
            r15 = [r13 licenseController];
            var_58 = *__NSConcreteStackBlock;
            *(&var_58 + 0x8) = 0xffffffffc2000000;
            *(&var_58 + 0x10) = ___52-[PADProduct verifyActivationDetailsWithCompletion:]_block_invoke.209;
            *(&var_58 + 0x18) = ___block_descriptor_48_ea8_32s40bs_e28_v32?0q8q16"NSDictionary"24l;
            *(&var_58 + 0x20) = r13;
            *(&var_58 + 0x28) = [r14 retain];
            (*(*r15 + 0x68))(r15, r12, &var_58);
            rdi = var_30;
    }
    [rdi release];
    [r14 release];
    return;
}

观察到PADErrorFactory类,检测到问题会调用这个类的函数。怪不得程序会崩溃!转到汇编,赶紧ret掉。

ret


这波破解顺理成章,基本没怎么调试,完美暴破!

从破解的过程,可以梳理出Downie的业务逻辑结构如下:

禁用取消激活功能

破解完后我们发现一个小问题,即点击取消激活后Downie会先弹出对话框,确定后app崩溃。


与其分析算法不如直接去掉这个功能。我们大致可推测功能应该在Licensing中。搜索deactivate关键词可搜到好多函数,但我们选择下面这个:

void _$s9Licensing11CMLicensingC17deactivateLicenseyyF(int arg0, int arg1, int arg2, int arg3, int arg4) {
    rdx = arg2;
    [sub_11600() release];
    [rdx release];
    r14 = [arg3 activated];
    [arg3 release];
    if (r14 == 0x0) goto loc_10ba3;

loc_10830:
    var_30 = [objc_allocWithZone(@class(NSAlert)) init];
    if (*qword_28418 != 0xffffffffffffffff) {
            swift_once(qword_28418, sub_a6b0, rdx);
    }
    sub_1ca70();

// ............

为啥选的是这个?因为这个函数里有NSAlert的初始化。如果想去掉,ret即可。

总结

可以看到Downie的静态库还真不少。这次破解主要是以静态分析为主,但要调试的话,也没问题。我破解速度并不是很快,总是不断试错。本人逆向能力也需要提升啊。

免费评分

参与人数 10威望 +1 吾爱币 +29 热心值 +10 收起 理由
touchmii + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
songwr + 1 + 1 鼓励转贴优秀软件安全工具和文档!
guyuchao3 + 1 + 1 我很赞同!
BarryAllen829 + 1 热心回复!
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
sharkyc + 1 用心讨论,共获提升!
沙鱼 + 2 + 1 谢谢@Thanks!
某些人 + 1 + 1 谢谢@Thanks!
uoyleef + 1 + 1 谢谢@Thanks!
Monitor + 2 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

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

推荐
Vvvvvoid 发表于 2024-4-3 18:23
这样可以 nop 掉弹框; (我也搞不懂为啥 在 hook_viewDidLoad 里必须要 get 一次 windows)

[Objective-C] 纯文本查看 复制代码
- (void) hook_viewDidLoad {
    NSLog(@">>>>>> called hook_viewDidLoad");
    [self valueForKey:@"window"];
    return ;
}
- (void) hook_windowDidLoad {
    NSLog(@">>>>>> called hook_windowDidLoad");
    NSWindow *window = [self valueForKey:@"window"];
    NSRect frame = NSMakeRect(0, 0, 0, 0);
    [window setFrame:frame display:YES];
    return ;
}


[MemoryUtils hookInstanceMethod:
     objc_getClass("Licensing.CMLicensingWindowController")
               originalSelector:NSSelectorFromString(@"windowDidLoad")
                  swizzledClass:[self class]
               swizzledSelector:NSSelectorFromString(@"hook_windowDidLoad")
];
//  viewDidLoad
[MemoryUtils hookInstanceMethod:
     objc_getClass("Licensing.CMLicensingViewController")
               originalSelector:NSSelectorFromString(@"viewDidLoad")
                  swizzledClass:[self class]
               swizzledSelector:NSSelectorFromString(@"hook_viewDidLoad")
];
推荐
 楼主| TLHorse 发表于 2022-3-1 19:53 |楼主
se34218 发表于 2022-3-1 09:01
mac用过这个,这种破解方法要是能在后续升级版本一直使用就好了

有个可行的解决方法是写一个patcher实现自动化注入,每次更新完就运行一次。但是因为太麻烦我又懒所以没做
沙发
whnet1618 发表于 2022-2-28 15:58
3#
vans4u1 发表于 2022-3-1 00:30
帅,思路清晰。用Paddle 还能解决不少别的类似软件。
4#
se34218 发表于 2022-3-1 09:01
mac用过这个,这种破解方法要是能在后续升级版本一直使用就好了
头像被屏蔽
5#
wbzb 发表于 2022-3-1 09:12
提示: 作者被禁止或删除 内容自动屏蔽
6#
zhangheng8687 发表于 2022-3-1 10:07
吾爱因你而美,感谢分享
8#
codetimer 发表于 2022-3-2 08:20
唉 早看到就好了 我再pdd7.99 买了一个
9#
failsray 发表于 2022-3-2 08:55
牛人 膜拜大佬
10#
我今天是大佬 发表于 2022-3-2 14:05
厉害了, 可以整一个
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-22 10:55

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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