飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 12683|回复: 27

[macOS] Moom 分析记录之一(爆破篇 )- tree_fly

  [复制链接]
  • TA的每日心情
    开心
    2019-3-17 22:44
  • 签到天数: 132 天

    [LV.7]常住居民III

    发表于 2016-10-22 00:56:54 | 显示全部楼层 |阅读模式
    本帖最后由 tree_fly 于 2016-10-22 20:47 编辑

    Moom 分析记录之一 爆破篇
             by   tree_fly/P.Y.G


    Moom 小工具挺实用,测试地址:https://www.chinapyg.com/thread-87211-1-1.html

    今天来简单的分析这款软件,首先看下未注册时状态:
    2016-10-21_22-17-07_01.png
    这里有三点需要关注:
    • 主程序的右上角有100次的试用标记:Demo: 100 Mooms remainning
    • 注册窗口内主体文本是:this license could be yours
    • 菜单栏有Buy字样
    基于这些信息,我们启动Hopper调试工具,载入app后常规查询字符串。

    2016-10-21_22-32-53.png

    点击 Demo: %lu Mooms remaining此行,查看字符串调用信息,注意一些细节,比如此处有2处调用:
    2016-10-21_22-39-01.png

    双击右侧边栏中第一个地址:0x100016b67,并于此处下断点:
    2016-10-21_22-46-39.png

    接下来,启动hopper的调试器,开始动态分析app。
    2016-10-21_22-49-55.png
    运行程序,尝试使用功能,直到中断在(RIP指向)刚才设置的断点处。
    中间可能出现这样的画面,忽略,继续运行即可。
    2016-10-21_23-04-33.png

    调试器的左下角已显示目前的调用栈内容,除此之外,也可以用lldb的bt命令来显示:

    2016-10-21_23-18-38.png

    [Plain Text] 纯文本查看 复制代码
    bt 
    * thread #1: tid = 0x527df, 0x0000000100016b67 Moom`-[PMMainController updateReminderButtonWithIncrementedUseCounter:applyRefractoryPeriod:showLicense:showAlert:] + 1042, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
      * frame #0: 0x0000000100016b67 Moom`[b][color=#0000ff]-[PMMainController updateReminderButtonWithIncrementedUseCounter:applyRefractoryPeriod:showLicense:showAlert:][/color][/b] + 1042
        frame #1: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
        frame #2: 0x00007fff9cebb4e7 AppKit`-[NSApplication(NSResponder) sendAction:to:from:] + 456
        frame #3: 0x00007fff9ca0b245 AppKit`-[NSControl sendAction:to:] + 86
        frame #4: 0x00007fff9ca0b16d AppKit`__26-[NSCell _sendActionFrom:]_block_invoke + 136
        frame #5: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
        frame #6: 0x00007fff9ca0b0c5 AppKit`-[NSCell _sendActionFrom:] + 128
        frame #7: 0x00007fff9ca4d92a AppKit`-[NSButtonCell _sendActionFrom:] + 98
        frame #8: 0x00007fffb3d8303d libsystem_trace.dylib`_os_activity_initiate + 61
        frame #9: 0x00007fff9ca09a58 AppKit`-[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 2481
        frame #10: 0x00007fff9ca4d667 AppKit`-[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 785
        frame #11: 0x00007fff9ca084c8 AppKit`-[NSControl mouseDown:] + 832
        frame #12: 0x00007fff9d01b73d AppKit`-[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 6341
        frame #13: 0x00007fff9d017f8c AppKit`-[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 1942
        frame #14: 0x00007fff9d01742a AppKit`-[NSWindow(NSEventRouting) sendEvent:] + 541
        frame #15: 0x00007fff9ceb7bf5 AppKit`-[NSApplication(NSEvent) sendEvent:] + 1145
        frame #16: 0x0000000100022412 Moom`-[PMApplication sendEvent:] + 630
        frame #17: 0x00007fff9c79e009 AppKit`-[NSApplication run] + 1002
        frame #18: 0x00007fff9c768a8a AppKit`NSApplicationMain + 1237
        frame #19: 0x00000001000012b8 Moom`start + 52


    根据堆栈信息, frame #0 以下的都没有什么好分析的,重点分析PMMainController updateReminder...,
    粗略分析发现:
    [Objective-C] 纯文本查看 复制代码
    char -[PMMainController updateReminderButtonWithIncrementedUseCounter:applyRefractoryPeriod:showLicense:showAlert:]
    
        if ([color=#ff0000][b]_PMHasValidCertificate(var_29, var_38, sign_extend_64(r14), var_40, r13) != 0x0[/b][/color]) {
                r15->numberOfTrialRunsLeft = 0xffffffffffffffff;
                [r15->licenseReminderButton setHidden:0x1];
                rbx = r15->licenseBottomStatusField;
                var_60 = @selector(mainBundle);
                var_68 = @selector(localizedStringForKey:value:table:);
                rcx = @"";
                r8 = 0x0;
                rdx = [[NSBundle mainBundle] localizedStringForKey:@"Thank You" value:rcx table:r8];
                [rbx setStringValue:rdx];
                if (var_50 != 0x0) {
                        r8 = 0x0
                        ;
                        var_54 = r12;
                        r12 = [NSFileManager defaultManager];
                        rbx = [[[[@"~/Library/Preferences" stringByExpandingTildeInPath] stringByAppendingString:@"/.byhost.spo", rcx, r8] stringByAppendingString:@"tli", rcx, r8] stringByAppendingString:@"ght.mdquery", rcx, r8];
                        rdx = rbx;
                        if ([r12 fileExistsAtPath:rdx, rcx, r8] != 0x0) {
                                rcx = 0x0;
                                rdx = rbx;
                                [r12 removeItemAtPath:rdx error:rcx];
                        }
                        rbx = r15->licenseMainStatusField;
                        rax = var_40;
                        if ([var_40 length] != 0x0) {
                                r13 = rax;
                        }
                        [rbx setStringValue:*r13];
                        rbx = r15->licenseTopStatusField;
                        rax = [NSBundle mainBundle];
                        rax = [rax localizedStringForKey:@"Moom - Licensed to" value:@"" table:0x0];
                        [rbx setStringValue:rax];
                        rax = [r15->licenseBuyButton superview];
                        [rax setHidden:0x1];
                }
        }
        else {
    
                if (r13 != 0x0) {
                        r14 = r13;
                        r13 = r15->licenseMainStatusField;
                        rax = _objc_msgSend(@class(NSBundle), var_60, rdx);
                        rax = _objc_msgSend(rax, var_68, @"This license could be yours", @"", 0x0);
                        rdi = r13;
                        r13 = r14;
                        _objc_msgSend(rdi, r12, rax);
                        _objc_msgSend(r15->licenseTopStatusField, r12, @"Moom");
                        rax = [r15->licenseBuyButton superview];
                        [rax setHidden:0x0];
                }
        }
        
    
    


    发现一个关键跳转,取决于函数_PMHasValidCertificate返回值。
    继续分析:
    2016-10-21_23-35-12.png

    首先,第一个要注意的函数:
    [Objective-C] 纯文本查看 复制代码
    int [b][color=#0000ff]_PMSimulateExpiredDemo[/color][/b]() {
        rax = [[NSUserDefaults standardUserDefaults] boolForKey:@"PMDebugSimulateExpiredManyTricksDemo"];
        return rax;
    }

    检测试用是否过期,那么简单修改返回值为0(False)是否就可以了呢?想想要操作100次,还是算了。
    第二个关键的函数:_PMValidPersistentCertificateDictionary
    字面意是检测本地证书是否有效,如果有效,读取用户名和邮箱地址等信息,否则就是试用情况了。
    那么先来看看是如何处理试用100次的问题。
    [Objective-C] 纯文本查看 复制代码
    loc_10000257d:
        r14 = [[[[@"~/Library/Preferences" stringByExpandingTildeInPath] 
            stringByAppendingString:@"/.byhost.spo"] stringByAppendingString:@"tli"]
             stringByAppendingString:@"ght.mdquery"];
        rax = [NSDictionary dictionaryWithContentsOfFile:r14];
        rax = [rax objectForKey:@"c"];
        rax = [rax integerValue];
        r12 = 0x0;
        if (rax >= 0x0) { r12 = rax;}
        if (var_34 != 0x0) {
                r12 = r12 + 0x1;
                rax = [NSNumber numberWithInteger:r12];
                rax = [NSDictionary dictionaryWithObject:rax forKey:@"c"];
                [rax writeToFile:r14 atomically:0x1];
        }
        goto loc_10000265f;

    很显然是读和写本地配置文件,简单提取一下文件的路径:
    ~/Library/Preferences/.byhost.spotlight.mdquery

    动态调试验证,在地址00000001000025cc处下断点,中断后,输入lldb命令:
    po $rax
    可读出路径信息:
    2016-10-22_00-48-41.png

    文件内容是什么?
    [XML] 纯文本查看 复制代码
    Last login: Fri Oct 21 21:57:44 on ttys000
    ➜  ~ cat ~/Library/Preferences/.byhost.spotlight.mdquery
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
            <key>c</key>
            <integer>2</integer>
    </dict>
    </plist>
    &#10140;  ~ 
    这里的c就是count的意思喽,2是已使用的次数。


    就目前已获取的信息,可以采用“清零递增值”的方法来固定使用次数,达到无次数限制。
    如何patch呢?这里有一种方法:
    2016-10-22_00-07-54.png

    [Asm] 纯文本查看 复制代码
    inc r12
    //改成
    mov r12,r12


    如果手动修改了配置文件,使得c>=100,会如何?
    2016-10-22_00-20-35.png

    app弹窗提醒了!

    综上,所以修改计步器初始值0,递增值0,并补一刀设置检测过期函数返回值为0,即可爆破成功!
    2016-10-21_23-40-35.png

    将修改后的主程序,替换原主程序,重新打开App。
    2016-10-22_18-54-05.png

    弹窗提醒出错,看来有程序自校验,根据字符串搜索定位到关键位置:

    2016-10-22_18-59-11.png

    继续搜索applicationDidFinishLaunching方法,

    2016-10-22_19-03-54.png

    分析这几段代码,可以发现这个关键函数:_PMCheckBundleSignature
    2016-10-22_19-04-33.png

    他的伪代码是这样的:
    2016-10-22_19-06-30.png

    这里调用了这些相关函数:
    [C] 纯文本查看 复制代码
    SecStaticCodeCreateWithPath()
    SecStaticCodeCheckValidity();
    SecCodeCopySelf() 
    SecRequirementCreateWithString()
    SecCodeCheckValidity()



    可以不用处理这些子函数,直接修改_PMCheckBundleSignature的返回值为 1 即可。
    再次测试弹窗消失。

    以上是Moom的爆破方案,先写到这里。
    下一篇将从分析Moom的注册证书采用的RSA、SHA1加密等细节以及伪造数据和如何HOOK关键函数优雅破解等讲起,欢迎阅读哦~




    评分

    参与人数 7威望 +72 飘云币 +104 收起 理由
    pxps + 4 赞一个!
    speedboy + 4 + 4 PYG有你更精彩!
    EvilNing + 4 + 4 很给力!
    0xcb + 8 + 8 赞一个!
    NoNameX2016 + 8 + 8 赞一个!
    qq939595 + 4 PYG有你更精彩!
    [PYG]版务督察 + 40 + 80 飞树博士V587

    查看全部评分

    本帖被以下淘专辑推荐:

    PYG19周年生日快乐!
  • TA的每日心情
    开心
    2023-6-28 17:31
  • 签到天数: 34 天

    [LV.5]常住居民I

    发表于 2016-10-22 05:50:16 | 显示全部楼层
    不懂啊,这是啥?
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2016-6-16 14:07
  • 签到天数: 10 天

    [LV.3]偶尔看看II

    发表于 2016-10-22 08:30:51 | 显示全部楼层
    不错!精华了!
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2016-10-22 08:38:43 | 显示全部楼层
    赞。
    如果inc变成Dec,应该也可以吧。这样与100就成南辕北辙了。
    不过用mov感觉更简单,更安全!

    点评

    dec会变成负数。取决于作者用了有符号还是无符号来处理返回值!  详情 回复 发表于 2016-10-22 08:40
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    2016-6-16 14:07
  • 签到天数: 10 天

    [LV.3]偶尔看看II

    发表于 2016-10-22 08:40:25 | 显示全部楼层
    [PYG]版务督察 发表于 2016-10-22 08:38
    赞。
    如果inc变成Dec,应该也可以吧。这样与100就成南辕北辙了。
    不过用mov感觉更简单,更安全!

    dec会变成负数。取决于作者用了有符号还是无符号来处理返回值!

    点评

    对。 所以还是Mov的用法更安全!  详情 回复 发表于 2016-10-22 09:05
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2016-10-22 09:05:52 | 显示全部楼层
    飘云 发表于 2016-10-22 08:40
    dec会变成负数。取决于作者用了有符号还是无符号来处理返回值!

    对。
    所以还是Mov的用法更安全!
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    6 天前
  • 签到天数: 1234 天

    [LV.10]以坛为家III

    发表于 2016-10-22 10:29:23 | 显示全部楼层
    支持飞树大神!
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    奋斗
    2021-9-29 15:05
  • 签到天数: 114 天

    [LV.6]常住居民II

    发表于 2016-10-23 00:21:02 | 显示全部楼层
    赞,还是伪代码读着舒服
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

  • TA的每日心情
    开心
    3 天前
  • 签到天数: 1266 天

    [LV.10]以坛为家III

    发表于 2016-10-23 08:53:12 | 显示全部楼层
    区待下一篇文章、、、
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    该用户从未签到

    发表于 2016-10-23 21:25:18 | 显示全部楼层
    谢谢分享,学习了!!!
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

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