Mac 下的软件绕过激活之iMazing


(nzt) #1

0x00前言

iMazing是一款Mac下管理手机的工具。
本次过程使用工具:Hopper,文本编辑器。
对于那些不熟悉Mac下软件逆向的同学,作者画了一张脑图给你们参考。

image.png2448x1682 342 KB

0x01初步查看软件

从官网下载试用版,发现试用版和正式版的区别挺多的。
首先先判断这个软件的字符串标签都是从哪里来的,我们把系统语言设置为中文,这个软件打开发现他是中文,然后再将系统语言设置为英文,这个软件打开就是英文。这说明这个软件的语言是适配系统语言的,但是一般适配系统语言的字符串标签要么存在Resources目录,要么存到二进制文件里边去,iMazing这个软件他就写到了Resources目录。

image.png1764x1096 214 KB

en.lproj是存放英语字符串的文件夹,zh.lproj是存放中文字符串的文件夹。在非英语语言的文件夹下一般只有两个strings文件
image.png1764x1096 112 KB

在en.lproj文件夹下有一个readme.txt告诉了我们GenericLabels.strings这个文件存放的是可以复用的字符串标签,那么猜测一下iMazingLabels.strings存储的是不可复用字符串标签。
image.png1504x1048 112 KB

这个时候我们既然知道了他的中文字符串在哪里,我们就可以检索他的中文字符串来找一找这个软件的一个关键的突破点。
image.jpg2166x1632 805 KB

我们找到了这个关键字iMazingNotActivated_Title这个应该表示的是未激活窗口的一个标题。

0x01开始反编译

void -[MessageViewController displayRegistrationAlert](void * self, void * _cmd) {
    r15 = self;
    r12 = *_objc_msgSend;
    r14 = [[self mainWindowController] window];
    if (r14 != 0x0) {
            r13 = (r12)((r12)(@class(NSAlert), @selector(new)), @selector(autorelease));
            (r12)(r13, @selector(setMessageText:), (r12)((r12)(@class(LocalizationManager), @selector(sharedInstance)), @selector(localizedStringForClass:key:), (r12)(r15, @selector(class)), @"iMazingNotActivated_Title"));
            (r12)(r13, @selector(setInformativeText:), (r12)((r12)(@class(LocalizationManager), @selector(sharedInstance)), @selector(localizedStringForClass:key:), (r12)(r15, @selector(class)), @"iMazingNotActivated_Info"));
            (r12)(r13, @selector(setAlertStyle:), 0x0);
            (r12)(r13, @selector(addButtonWithTitle:), (r12)((r12)(@class(LocalizationManager), @selector(sharedInstance)), @selector(localizedStringForClass:key:), (r12)(r15, @selector(class)), @"OK_UserReply"));
            (r12)(r13, @selector(beginSheetModalForWindow:completionHandler:), r14, ^ {/* block implemented at sub_1001102ae */ } });
            (r12)(**_NSApp, @selector(runModalForWindow:), r14);
    }
    return;
}

发现这个方法只是单纯的构造了一个弹窗,并没什么用。。
那再找一个字符串好了。。

image.png1672x44 22.7 KB

这回设定为Deactivate字符串。
找到了这个deactivateLicense:
最终追踪到了
ApplicationDelegate validateMenuItem:
菜单项这里

char -[ApplicationDelegate validateMenuItem:](void * self, void * _cmd, void * arg2) {
    r15 = _cmd;
    r12 = self;
    r14 = [arg2 retain];
    rbx = [[**_NSApp modalWindow] retain];
    [rbx release];
    if (rbx == 0x0) {
            var_38 = r15;
            rbx = [[r12 mainWindowController] retain];
            r15 = [rbx exiting];
            [rbx release];
            if (r15 != 0x0) {
                    rbx = 0x0;
            }
            else {
                    r15 = *_objc_msgSend;
                    rbx = [[r12 mainWindowController] retain];
                    rdx = [r14 action];
                    rax = [rbx respondsToSelector:rdx];
                    var_30 = r14;
                    [rbx release];
                    rbx = r15;
                    r15 = [[r12 mainWindowController] retain];
                    if (rax != 0x0) {
                            r13 = rbx;
                            rdx = var_38;
                            rbx = [r15 respondsToSelector:rdx];
                            [r15 release];
                            if (rbx != 0x0) {
                                    r14 = [(r13)(r12, @selector(mainWindowController), rdx) retain];
                                    rbx = (r13)(r14, @selector(validateMenuItem:), var_30);
                                    rdi = r14;
                                    r14 = var_30;
                                    [rdi release];
                            }
                            else {
                                    rbx = 0x1;
                                    r14 = var_30;
                            }
                    }
                    else {
                            r14 = [(rbx)(r15, @selector(currentContentViewController), rdx) retain];
                            rbx = (rbx)(r14, @selector(respondsToSelector:), (rbx)(var_30, @selector(action), rdx));
                            [r14 release];
                            [r15 release];
                            if (rbx != 0x0) {
                                    r14 = [[r12 mainWindowController] retain];
                                    rbx = [[r14 currentContentViewController] retain];
                                    rdx = var_38;
                                    r15 = [rbx respondsToSelector:rdx];
                                    [rbx release];
                                    [r14 release];
                                    if (r15 != 0x0) {
                                            r14 = [[r12 mainWindowController] retain];
                                            r15 = [[r14 currentContentViewController] retain];
                                            rbx = [r15 validateMenuItem:var_30];
                                            [r15 release];
                                            rdi = r14;
                                            r14 = var_30;
                                            [rdi release];
                                    }
                                    else {
                                            rbx = 0x1;
                                            r14 = var_30;
                                    }
                            }
                            else {
                                    r14 = var_30;
                                    rbx = 0x1;
                                    if (((((((([r14 action] != @selector(showHelp:)) && ([r14 action] != @selector(showAboutWindow:))) && ([r14 action] != @selector(showAlternateAboutWindow:))) && ([r14 action] != @selector(showPreferencesWindow:))) && ([r14 action] != @selector(showSendFeedbackOrBugReport:))) && ([r14 action] != @selector(socialVisitFacebook:))) && ([r14 action] != @selector(socialVisitTwitter:))) && ([r14 action] != @selector(socialVisitGoogle:))) {
                                            if ([r14 action] != @selector(socialShareFacebook:)) {
                                                    if ([r14 action] != @selector(socialShareTwitter:)) {
                                                            if ([r14 action] != @selector(socialShareGoogle:)) {
                                                                    if ([r14 action] != @selector(showBuyScreen:)) {
                                                                            if ([r14 action] != @selector(openRetrieveLicensePage:)) {
                                                                                    rbx = [r14 action] == @selector(deactivateLicense:) ? 0x1 : 0x0;
                                                                            }
                                                                    }
                                                            }
                                                    }
                                            }
                                    }
                            }
                    }
            }
    }
    else {
            rbx = 0x0;
    }
    [r14 release];
    rax = sign_extend_64(rbx);
    return rax;
}
这时候我们看到它经过了很多的判断才调用了(deactivateLicense:)这个方法,我们看一下这个方法是那个类的,双击方法名即可。

void -[ApplicationDelegate deactivateLicense:](void * self, void * _cmd, void * arg2) {
    rbx = [[Activation sharedInstance] retain];
    [rbx Pv8iwIVja9yAv];
    [rbx release];
    return;
}

sharedInstance是 单例模式不用管,重点看一下Pv8iwIVja9yAv这个方法,这个方法来自Activation类

void -[Activation Pv8iwIVja9yAv](void * self, void * _cmd) {
    rax = *_NSApp;
    [*rax Pv8iwIVja9yAv];
    [self checkActivation];
    return;
}

调用的checkActivation方法好像是检查是否激活的。

void -[Activation checkActivation](void * self, void * _cmd) {
    r14 = self;
    if ([self checkIfIsActivated] != 0x0) {
            [IronSource reportConversionToIronSource:@"purchase"];
    }
    else {
            rbx = [[Preferences sharedInstance] retain];
            r15 = [rbx firstLaunch];
            [rbx release];
            if (r15 == 0x0) {
                    intrinsic_movsd(xmm0, *qword);
                    [r14 performSelector:@selector(showDialog) withObject:0x0 afterDelay:r8];
            }
    }
    return;
}

好像我们只要将Activation checkIfIsActivated这个方法让他为 YES 就可以成功绕过激活。

char -[Activation checkIfIsActivated](void * self, void * _cmd) {
    r13 = sub_100024440(0x0);
    if (r13 != 0x0) {
            r14 = sub_100027860();
            if (r14 != 0x0) {
                    r15 = [CFDictionaryGetValue(r14, *0x100241aa0) retain];
                    if (r15 != 0x0) {
                            r12 = [[Preferences sharedInstance] retain];
                            [r12 setRegistrationEmail:r15];
                            [r12 release];
                    }
                    CFRelease(r14);
                    [r15 release];
            }
    }
    rbx = [[DDNAConfig sharedInstance] retain];
    [rbx setAppRegistered:sign_extend_64(r13)];
    [rbx release];
    rax = sign_extend_64(r13);
    return rax;
}

直接把Activation checkIfIsActivated这个方法
mov rax, 0x1
ret

image.png3360x2100 1.71 MB

保存测试一下。
image.png1764x1096 130 KB

打开后发现果然没有让我激活了。但是会弹一个窗。
image.png1064x536 64.8 KB

一开始我以为,这个弹窗的原因是我们修改了它的二进制文件,它签名校验失败了,但是并不影响正常使用。
后来经过@ChiChou 的指正弹窗是 sparkle 框架,用来实现自动更新的。
可以patch /Frameworks/DevMateKit.framework/DevMateKit 这个文件里的 -[DM_SUUpdater checkIfConfiguredProperly] 方法。
来阻止弹窗。
image.png944x178 185 KB

image.jpg2048x1236 308 KB

成功绕过激活。